Ignore non-active fullscreen windows for shelf state.
[chromium-blink-merge.git] / content / browser / renderer_host / media / video_capture_device_impl.cc
blob9b37b296c4f3aa43ebc21158b2193a505935ef89
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.
5 #include "content/browser/renderer_host/media/video_capture_device_impl.h"
7 #include "base/basictypes.h"
8 #include "base/bind.h"
9 #include "base/callback_forward.h"
10 #include "base/callback_helpers.h"
11 #include "base/debug/trace_event.h"
12 #include "base/logging.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/memory/weak_ptr.h"
15 #include "base/message_loop/message_loop_proxy.h"
16 #include "base/metrics/histogram.h"
17 #include "base/sequenced_task_runner.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/synchronization/lock.h"
20 #include "base/threading/thread.h"
21 #include "base/threading/thread_checker.h"
22 #include "base/time/time.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "media/base/bind_to_loop.h"
25 #include "media/base/video_frame.h"
26 #include "media/base/video_util.h"
27 #include "media/video/capture/video_capture_types.h"
28 #include "ui/gfx/rect.h"
30 namespace content {
32 namespace {
34 void DeleteCaptureMachineOnUIThread(
35 scoped_ptr<VideoCaptureMachine> capture_machine) {
36 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
37 if (capture_machine) {
38 capture_machine->Stop();
39 capture_machine.reset();
43 } // namespace
45 ThreadSafeCaptureOracle::ThreadSafeCaptureOracle(
46 scoped_ptr<media::VideoCaptureDevice::Client> client,
47 scoped_ptr<VideoCaptureOracle> oracle,
48 const media::VideoCaptureParams& params)
49 : client_(client.Pass()),
50 oracle_(oracle.Pass()),
51 params_(params),
52 capture_size_updated_(false) {
53 // Frame dimensions must each be an even integer since the client wants (or
54 // will convert to) YUV420.
55 capture_size_ = gfx::Size(
56 MakeEven(params.requested_format.frame_size.width()),
57 MakeEven(params.requested_format.frame_size.height()));
58 frame_rate_ = params.requested_format.frame_rate;
61 ThreadSafeCaptureOracle::~ThreadSafeCaptureOracle() {}
63 bool ThreadSafeCaptureOracle::ObserveEventAndDecideCapture(
64 VideoCaptureOracle::Event event,
65 base::Time event_time,
66 scoped_refptr<media::VideoFrame>* storage,
67 CaptureFrameCallback* callback) {
68 base::AutoLock guard(lock_);
70 if (!client_)
71 return false; // Capture is stopped.
73 scoped_refptr<media::VideoCaptureDevice::Client::Buffer> output_buffer =
74 client_->ReserveOutputBuffer(media::VideoFrame::I420, capture_size_);
75 const bool should_capture =
76 oracle_->ObserveEventAndDecideCapture(event, event_time);
77 const bool content_is_dirty =
78 (event == VideoCaptureOracle::kCompositorUpdate ||
79 event == VideoCaptureOracle::kSoftwarePaint);
80 const char* event_name =
81 (event == VideoCaptureOracle::kTimerPoll ? "poll" :
82 (event == VideoCaptureOracle::kCompositorUpdate ? "gpu" :
83 "paint"));
85 // Consider the various reasons not to initiate a capture.
86 if (should_capture && !output_buffer) {
87 TRACE_EVENT_INSTANT1("mirroring",
88 "EncodeLimited",
89 TRACE_EVENT_SCOPE_THREAD,
90 "trigger",
91 event_name);
92 return false;
93 } else if (!should_capture && output_buffer) {
94 if (content_is_dirty) {
95 // This is a normal and acceptable way to drop a frame. We've hit our
96 // capture rate limit: for example, the content is animating at 60fps but
97 // we're capturing at 30fps.
98 TRACE_EVENT_INSTANT1("mirroring", "FpsRateLimited",
99 TRACE_EVENT_SCOPE_THREAD,
100 "trigger", event_name);
102 return false;
103 } else if (!should_capture && !output_buffer) {
104 // We decided not to capture, but we wouldn't have been able to if we wanted
105 // to because no output buffer was available.
106 TRACE_EVENT_INSTANT1("mirroring", "NearlyEncodeLimited",
107 TRACE_EVENT_SCOPE_THREAD,
108 "trigger", event_name);
109 return false;
111 int frame_number = oracle_->RecordCapture();
112 TRACE_EVENT_ASYNC_BEGIN2("mirroring", "Capture", output_buffer.get(),
113 "frame_number", frame_number,
114 "trigger", event_name);
115 *callback = base::Bind(&ThreadSafeCaptureOracle::DidCaptureFrame,
116 this,
117 output_buffer,
118 frame_number);
119 *storage = media::VideoFrame::WrapExternalPackedMemory(
120 media::VideoFrame::I420,
121 capture_size_,
122 gfx::Rect(capture_size_),
123 capture_size_,
124 static_cast<uint8*>(output_buffer->data()),
125 output_buffer->size(),
126 base::SharedMemory::NULLHandle(),
127 base::TimeDelta(),
128 base::Closure());
129 return true;
132 void ThreadSafeCaptureOracle::UpdateCaptureSize(const gfx::Size& source_size) {
133 base::AutoLock guard(lock_);
135 // If this is the first call to UpdateCaptureSize(), or the receiver supports
136 // variable resolution, then determine the capture size by treating the
137 // requested width and height as maxima.
138 if (!capture_size_updated_ || params_.allow_resolution_change) {
139 // The capture resolution should not exceed the source frame size.
140 // In other words it should downscale the image but not upscale it.
141 if (source_size.width() > params_.requested_format.frame_size.width() ||
142 source_size.height() > params_.requested_format.frame_size.height()) {
143 gfx::Rect capture_rect = media::ComputeLetterboxRegion(
144 gfx::Rect(params_.requested_format.frame_size), source_size);
145 capture_size_ = gfx::Size(MakeEven(capture_rect.width()),
146 MakeEven(capture_rect.height()));
147 } else {
148 capture_size_ = gfx::Size(MakeEven(source_size.width()),
149 MakeEven(source_size.height()));
151 capture_size_updated_ = true;
155 void ThreadSafeCaptureOracle::Stop() {
156 base::AutoLock guard(lock_);
157 client_.reset();
160 void ThreadSafeCaptureOracle::ReportError() {
161 base::AutoLock guard(lock_);
162 if (client_)
163 client_->OnError();
166 void ThreadSafeCaptureOracle::DidCaptureFrame(
167 scoped_refptr<media::VideoCaptureDevice::Client::Buffer> buffer,
168 int frame_number,
169 base::Time timestamp,
170 bool success) {
171 base::AutoLock guard(lock_);
172 TRACE_EVENT_ASYNC_END2("mirroring", "Capture", buffer.get(),
173 "success", success,
174 "timestamp", timestamp.ToInternalValue());
176 if (!client_)
177 return; // Capture is stopped.
179 if (success) {
180 if (oracle_->CompleteCapture(frame_number, timestamp)) {
181 client_->OnIncomingCapturedBuffer(buffer,
182 media::VideoFrame::I420,
183 capture_size_,
184 timestamp,
185 frame_rate_);
190 void VideoCaptureDeviceImpl::AllocateAndStart(
191 const media::VideoCaptureParams& params,
192 scoped_ptr<media::VideoCaptureDevice::Client> client) {
193 DCHECK(thread_checker_.CalledOnValidThread());
195 if (state_ != kIdle) {
196 DVLOG(1) << "Allocate() invoked when not in state Idle.";
197 return;
200 if (params.requested_format.frame_rate <= 0) {
201 DVLOG(1) << "invalid frame_rate: " << params.requested_format.frame_rate;
202 client->OnError();
203 return;
206 if (params.requested_format.frame_size.width() < kMinFrameWidth ||
207 params.requested_format.frame_size.height() < kMinFrameHeight) {
208 DVLOG(1) << "invalid frame size: "
209 << params.requested_format.frame_size.ToString();
210 client->OnError();
211 return;
214 base::TimeDelta capture_period = base::TimeDelta::FromMicroseconds(
215 1000000.0 / params.requested_format.frame_rate + 0.5);
217 scoped_ptr<VideoCaptureOracle> oracle(
218 new VideoCaptureOracle(capture_period,
219 kAcceleratedSubscriberIsSupported));
220 oracle_proxy_ =
221 new ThreadSafeCaptureOracle(client.Pass(), oracle.Pass(), params);
223 // Starts the capture machine asynchronously.
224 BrowserThread::PostTaskAndReplyWithResult(
225 BrowserThread::UI, FROM_HERE,
226 base::Bind(&VideoCaptureMachine::Start,
227 base::Unretained(capture_machine_.get()),
228 oracle_proxy_),
229 base::Bind(&VideoCaptureDeviceImpl::CaptureStarted,
230 AsWeakPtr()));
232 TransitionStateTo(kCapturing);
235 void VideoCaptureDeviceImpl::StopAndDeAllocate() {
236 DCHECK(thread_checker_.CalledOnValidThread());
238 if (state_ != kCapturing)
239 return;
241 oracle_proxy_->Stop();
242 oracle_proxy_ = NULL;
244 TransitionStateTo(kIdle);
246 // Stops the capture machine asynchronously.
247 BrowserThread::PostTask(
248 BrowserThread::UI, FROM_HERE, base::Bind(
249 &VideoCaptureMachine::Stop,
250 base::Unretained(capture_machine_.get())));
253 void VideoCaptureDeviceImpl::CaptureStarted(bool success) {
254 DCHECK(thread_checker_.CalledOnValidThread());
255 if (!success) {
256 DVLOG(1) << "Failed to start capture machine.";
257 Error();
261 VideoCaptureDeviceImpl::VideoCaptureDeviceImpl(
262 scoped_ptr<VideoCaptureMachine> capture_machine)
263 : state_(kIdle),
264 capture_machine_(capture_machine.Pass()) {}
266 VideoCaptureDeviceImpl::~VideoCaptureDeviceImpl() {
267 // If capture_machine is not NULL, then we need to return to the UI thread to
268 // safely stop the capture machine.
269 if (capture_machine_) {
270 BrowserThread::PostTask(
271 BrowserThread::UI, FROM_HERE, base::Bind(
272 &DeleteCaptureMachineOnUIThread, base::Passed(&capture_machine_)));
274 DVLOG(1) << "VideoCaptureDeviceImpl@" << this << " destroying.";
277 void VideoCaptureDeviceImpl::TransitionStateTo(State next_state) {
278 DCHECK(thread_checker_.CalledOnValidThread());
280 #ifndef NDEBUG
281 static const char* kStateNames[] = {
282 "Idle", "Allocated", "Capturing", "Error"
284 DVLOG(1) << "State change: " << kStateNames[state_]
285 << " --> " << kStateNames[next_state];
286 #endif
288 state_ = next_state;
291 void VideoCaptureDeviceImpl::Error() {
292 DCHECK(thread_checker_.CalledOnValidThread());
294 if (state_ == kIdle)
295 return;
297 if (oracle_proxy_)
298 oracle_proxy_->ReportError();
300 StopAndDeAllocate();
301 TransitionStateTo(kError);
304 } // namespace content