Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(273)

Side by Side Diff: content/browser/renderer_host/media/desktop_capture_device_ash.cc

Issue 93583003: Reland 237634 and 237645. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebase at 237726. Created 7 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 //
5 #include "content/browser/renderer_host/media/desktop_capture_device_ash.h"
6
7 #include "base/logging.h"
8 #include "base/timer/timer.h"
9 #include "cc/output/copy_output_request.h"
10 #include "cc/output/copy_output_result.h"
11 #include "content/browser/aura/image_transport_factory.h"
12 #include "content/browser/renderer_host/media/video_capture_device_impl.h"
13 #include "content/common/gpu/client/gl_helper.h"
14 #include "content/public/browser/browser_thread.h"
15 #include "media/base/video_util.h"
16 #include "media/video/capture/video_capture_types.h"
17 #include "third_party/skia/include/core/SkBitmap.h"
18 #include "ui/aura/window.h"
19 #include "ui/aura/window_observer.h"
20 #include "ui/compositor/compositor.h"
21 #include "ui/compositor/dip_util.h"
22 #include "ui/compositor/layer.h"
23 #include "ui/gfx/screen.h"
24
25 namespace content {
26
27 namespace {
28
29 class DesktopVideoCaptureMachine
30 : public VideoCaptureMachine,
31 public aura::WindowObserver,
32 public ui::CompositorObserver,
33 public base::SupportsWeakPtr<DesktopVideoCaptureMachine> {
34 public:
35 DesktopVideoCaptureMachine(const DesktopMediaID& source);
36 virtual ~DesktopVideoCaptureMachine();
37
38 // VideoCaptureFrameSource overrides.
39 virtual bool Start(
40 const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy) OVERRIDE;
41 virtual void Stop() OVERRIDE;
42
43 // Implements aura::WindowObserver.
44 virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE;
45
46 // Implements ui::CompositorObserver.
47 virtual void OnCompositingDidCommit(ui::Compositor* compositor) OVERRIDE {}
48 virtual void OnCompositingStarted(ui::Compositor* compositor,
49 base::TimeTicks start_time) OVERRIDE {}
50 virtual void OnCompositingEnded(ui::Compositor* compositor) OVERRIDE;
51 virtual void OnCompositingAborted(ui::Compositor* compositor) OVERRIDE {}
52 virtual void OnCompositingLockStateChanged(
53 ui::Compositor* compositor) OVERRIDE {}
54 virtual void OnUpdateVSyncParameters(ui::Compositor* compositor,
55 base::TimeTicks timebase,
56 base::TimeDelta interval) OVERRIDE {}
57
58 private:
59 // Captures a frame.
60 // |dirty| is false for timer polls and true for compositor updates.
61 void Capture(bool dirty);
62
63 // Response callback for cc::Layer::RequestCopyOfOutput().
64 void DidCopyOutput(
65 scoped_refptr<media::VideoFrame> video_frame,
66 base::Time start_time,
67 const ThreadSafeCaptureOracle::CaptureFrameCallback& capture_frame_cb,
68 scoped_ptr<cc::CopyOutputResult> result);
69
70 // The window associated with the desktop.
71 aura::Window* desktop_window_;
72
73 // The layer associated with the desktop.
74 ui::Layer* desktop_layer_;
75
76 // The timer that kicks off period captures.
77 base::Timer timer_;
78
79 // The desktop id.
80 DesktopMediaID desktop_id_;
81
82 // Makes all the decisions about which frames to copy, and how.
83 scoped_refptr<ThreadSafeCaptureOracle> oracle_proxy_;
84
85 // YUV readback pipeline.
86 scoped_ptr<content::ReadbackYUVInterface> yuv_readback_pipeline_;
87
88 DISALLOW_COPY_AND_ASSIGN(DesktopVideoCaptureMachine);
89 };
90
91 DesktopVideoCaptureMachine::DesktopVideoCaptureMachine(
92 const DesktopMediaID& source)
93 : desktop_window_(NULL),
94 desktop_layer_(NULL),
95 timer_(true, true),
96 desktop_id_(source) {}
97
98 DesktopVideoCaptureMachine::~DesktopVideoCaptureMachine() {}
99
100 bool DesktopVideoCaptureMachine::Start(
101 const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy) {
102 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
103
104 // TODO(hshi): get the correct display specified by |desktop_id_|.
105 const gfx::Display& primary_display =
106 gfx::Screen::GetNativeScreen()->GetPrimaryDisplay();
107 desktop_window_ = gfx::Screen::GetNativeScreen()->GetWindowAtScreenPoint(
108 primary_display.bounds().CenterPoint())->GetRootWindow();
109 if (!desktop_window_)
110 return false;
111
112 // If the desktop layer is already destroyed then return failure.
113 desktop_layer_ = desktop_window_->layer();
114 if (!desktop_layer_)
115 return false;
116
117 DCHECK(oracle_proxy.get());
118 oracle_proxy_ = oracle_proxy;
119
120 // Start observing window events.
121 desktop_window_->AddObserver(this);
122
123 // Start observing compositor updates.
124 ui::Compositor* compositor = desktop_layer_->GetCompositor();
125 if (!compositor)
126 return false;
127
128 compositor->AddObserver(this);
129
130 // Starts timer.
131 timer_.Start(FROM_HERE, oracle_proxy_->capture_period(),
132 base::Bind(&DesktopVideoCaptureMachine::Capture, AsWeakPtr(),
133 false));
134
135 started_ = true;
136 return true;
137 }
138
139 void DesktopVideoCaptureMachine::Stop() {
140 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
141
142 // Stop observing window events.
143 if (desktop_window_)
144 desktop_window_->RemoveObserver(this);
145
146 // Stop observing compositor updates.
147 if (desktop_layer_) {
148 ui::Compositor* compositor = desktop_layer_->GetCompositor();
149 if (compositor)
150 compositor->RemoveObserver(this);
151 }
152
153 // Stop timer.
154 timer_.Stop();
155
156 started_ = false;
157 }
158
159 void DesktopVideoCaptureMachine::Capture(bool dirty) {
160 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
161
162 // Do not capture if the desktop layer is already destroyed.
163 if (!desktop_layer_)
164 return;
165
166 scoped_refptr<media::VideoFrame> frame;
167 ThreadSafeCaptureOracle::CaptureFrameCallback capture_frame_cb;
168
169 const base::Time start_time = base::Time::Now();
170 const VideoCaptureOracle::Event event =
171 dirty ? VideoCaptureOracle::kCompositorUpdate
172 : VideoCaptureOracle::kTimerPoll;
173 if (oracle_proxy_->ObserveEventAndDecideCapture(
174 event, start_time, &frame, &capture_frame_cb)) {
175 scoped_ptr<cc::CopyOutputRequest> request =
176 cc::CopyOutputRequest::CreateRequest(
177 base::Bind(&DesktopVideoCaptureMachine::DidCopyOutput,
178 AsWeakPtr(), frame, start_time, capture_frame_cb));
179 gfx::Rect desktop_size = ui::ConvertRectToPixel(
180 desktop_layer_, desktop_layer_->bounds());
181 request->set_area(desktop_size);
182 desktop_layer_->RequestCopyOfOutput(request.Pass());
183 }
184 }
185
186 static void CopyOutputFinishedForVideo(
187 base::Time start_time,
188 const ThreadSafeCaptureOracle::CaptureFrameCallback& capture_frame_cb,
189 scoped_ptr<cc::SingleReleaseCallback> release_callback,
190 bool result) {
191 release_callback->Run(0, false);
192 capture_frame_cb.Run(start_time, result);
193 }
194
195 void DesktopVideoCaptureMachine::DidCopyOutput(
196 scoped_refptr<media::VideoFrame> video_frame,
197 base::Time start_time,
198 const ThreadSafeCaptureOracle::CaptureFrameCallback& capture_frame_cb,
199 scoped_ptr<cc::CopyOutputResult> result) {
200 if (result->IsEmpty() || result->size().IsEmpty())
201 return;
202
203 // Compute the dest size we want after the letterboxing resize. Make the
204 // coordinates and sizes even because we letterbox in YUV space
205 // (see CopyRGBToVideoFrame). They need to be even for the UV samples to
206 // line up correctly.
207 // The video frame's coded_size() and the result's size() are both physical
208 // pixels.
209 gfx::Rect region_in_frame =
210 media::ComputeLetterboxRegion(gfx::Rect(video_frame->coded_size()),
211 result->size());
212 region_in_frame = gfx::Rect(region_in_frame.x() & ~1,
213 region_in_frame.y() & ~1,
214 region_in_frame.width() & ~1,
215 region_in_frame.height() & ~1);
216 if (region_in_frame.IsEmpty())
217 return;
218
219 ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
220 GLHelper* gl_helper = factory->GetGLHelper();
221 if (!gl_helper)
222 return;
223
224 cc::TextureMailbox texture_mailbox;
225 scoped_ptr<cc::SingleReleaseCallback> release_callback;
226 result->TakeTexture(&texture_mailbox, &release_callback);
227 DCHECK(texture_mailbox.IsTexture());
228 if (!texture_mailbox.IsTexture())
229 return;
230
231 gfx::Rect result_rect(result->size());
232 if (!yuv_readback_pipeline_ ||
233 yuv_readback_pipeline_->scaler()->SrcSize() != result_rect.size() ||
234 yuv_readback_pipeline_->scaler()->SrcSubrect() != result_rect ||
235 yuv_readback_pipeline_->scaler()->DstSize() != region_in_frame.size()) {
236 yuv_readback_pipeline_.reset(
237 gl_helper->CreateReadbackPipelineYUV(GLHelper::SCALER_QUALITY_FAST,
238 result_rect.size(),
239 result_rect,
240 video_frame->coded_size(),
241 region_in_frame,
242 true,
243 true));
244 }
245 yuv_readback_pipeline_->ReadbackYUV(
246 texture_mailbox.name(), texture_mailbox.sync_point(), video_frame.get(),
247 base::Bind(&CopyOutputFinishedForVideo, start_time, capture_frame_cb,
248 base::Passed(&release_callback)));
249 }
250
251 void DesktopVideoCaptureMachine::OnWindowDestroyed(aura::Window* window) {
252 DCHECK(desktop_window_ && window == desktop_window_);
253 desktop_window_ = NULL;
254 desktop_layer_ = NULL;
255
256 // Post task to stop capture on UI thread.
257 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
258 &DesktopVideoCaptureMachine::Stop, AsWeakPtr()));
259 }
260
261 void DesktopVideoCaptureMachine::OnCompositingEnded(
262 ui::Compositor* compositor) {
263 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
264 &DesktopVideoCaptureMachine::Capture, AsWeakPtr(), true));
265 }
266
267 } // namespace
268
269 DesktopCaptureDeviceAsh::DesktopCaptureDeviceAsh(
270 const DesktopMediaID& source)
271 : impl_(new VideoCaptureDeviceImpl(scoped_ptr<VideoCaptureMachine>(
272 new DesktopVideoCaptureMachine(source)))) {}
273
274 DesktopCaptureDeviceAsh::~DesktopCaptureDeviceAsh() {
275 DVLOG(2) << "DesktopCaptureDeviceAsh@" << this << " destroying.";
276 }
277
278 // static
279 media::VideoCaptureDevice* DesktopCaptureDeviceAsh::Create(
280 const DesktopMediaID& source) {
281 // This implementation only supports screen capture.
282 if (source.type != DesktopMediaID::TYPE_SCREEN)
283 return NULL;
284
285 return new DesktopCaptureDeviceAsh(source);
286 }
287
288 void DesktopCaptureDeviceAsh::AllocateAndStart(
289 const media::VideoCaptureParams& params,
290 scoped_ptr<Client> client) {
291 DVLOG(1) << "Allocating " << params.requested_format.frame_size.ToString();
292 impl_->AllocateAndStart(params, client.Pass());
293 }
294
295 void DesktopCaptureDeviceAsh::StopAndDeAllocate() {
296 impl_->StopAndDeAllocate();
297 }
298
299 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698