1 // Copyright 2014 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/media/capture/content_video_capture_device_core.h"
7 #include "base/basictypes.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/string_number_conversions.h"
19 #include "base/strings/stringprintf.h"
20 #include "base/synchronization/lock.h"
21 #include "base/threading/thread.h"
22 #include "base/threading/thread_checker.h"
23 #include "base/time/time.h"
24 #include "content/public/browser/browser_thread.h"
25 #include "media/base/bind_to_current_loop.h"
26 #include "media/base/video_frame.h"
27 #include "media/base/video_util.h"
28 #include "media/video/capture/video_capture_types.h"
29 #include "ui/gfx/rect.h"
35 void DeleteCaptureMachineOnUIThread(
36 scoped_ptr<VideoCaptureMachine> capture_machine) {
37 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
39 capture_machine.reset();
44 ThreadSafeCaptureOracle::ThreadSafeCaptureOracle(
45 scoped_ptr<media::VideoCaptureDevice::Client> client,
46 scoped_ptr<VideoCaptureOracle> oracle,
47 const media::VideoCaptureParams& params)
48 : client_(client.Pass()),
49 oracle_(oracle.Pass()),
51 capture_size_updated_(false) {
52 switch (params_.requested_format.pixel_format) {
53 case media::PIXEL_FORMAT_I420:
54 video_frame_format_ = media::VideoFrame::I420;
56 case media::PIXEL_FORMAT_TEXTURE:
57 video_frame_format_ = media::VideoFrame::NATIVE_TEXTURE;
60 LOG(FATAL) << "Unexpected pixel_format "
61 << params_.requested_format.pixel_format;
65 ThreadSafeCaptureOracle::~ThreadSafeCaptureOracle() {}
67 bool ThreadSafeCaptureOracle::ObserveEventAndDecideCapture(
68 VideoCaptureOracle::Event event,
69 const gfx::Rect& damage_rect,
70 base::TimeTicks event_time,
71 scoped_refptr<media::VideoFrame>* storage,
72 CaptureFrameCallback* callback) {
73 base::AutoLock guard(lock_);
76 return false; // Capture is stopped.
78 // Always round up the coded size to multiple of 16 pixels.
79 // See http://crbug.com/402151.
80 const gfx::Size visible_size = params_.requested_format.frame_size;
81 const gfx::Size coded_size((visible_size.width() + 15) & ~15,
82 (visible_size.height() + 15) & ~15);
84 scoped_refptr<media::VideoCaptureDevice::Client::Buffer> output_buffer =
85 client_->ReserveOutputBuffer(video_frame_format_, coded_size);
86 const bool should_capture =
87 oracle_->ObserveEventAndDecideCapture(event, damage_rect, event_time);
88 const bool content_is_dirty =
89 (event == VideoCaptureOracle::kCompositorUpdate ||
90 event == VideoCaptureOracle::kSoftwarePaint);
91 const char* event_name =
92 (event == VideoCaptureOracle::kTimerPoll ? "poll" :
93 (event == VideoCaptureOracle::kCompositorUpdate ? "gpu" :
96 // Consider the various reasons not to initiate a capture.
97 if (should_capture && !output_buffer.get()) {
98 TRACE_EVENT_INSTANT1("mirroring",
100 TRACE_EVENT_SCOPE_THREAD,
104 } else if (!should_capture && output_buffer.get()) {
105 if (content_is_dirty) {
106 // This is a normal and acceptable way to drop a frame. We've hit our
107 // capture rate limit: for example, the content is animating at 60fps but
108 // we're capturing at 30fps.
109 TRACE_EVENT_INSTANT1("mirroring", "FpsRateLimited",
110 TRACE_EVENT_SCOPE_THREAD,
111 "trigger", event_name);
114 } else if (!should_capture && !output_buffer.get()) {
115 // We decided not to capture, but we wouldn't have been able to if we wanted
116 // to because no output buffer was available.
117 TRACE_EVENT_INSTANT1("mirroring", "NearlyPipelineLimited",
118 TRACE_EVENT_SCOPE_THREAD,
119 "trigger", event_name);
122 int frame_number = oracle_->RecordCapture();
123 TRACE_EVENT_ASYNC_BEGIN2("mirroring", "Capture", output_buffer.get(),
124 "frame_number", frame_number,
125 "trigger", event_name);
126 // NATIVE_TEXTURE frames wrap a texture mailbox, which we don't have at the
127 // moment. We do not construct those frames.
128 if (video_frame_format_ != media::VideoFrame::NATIVE_TEXTURE) {
129 *storage = media::VideoFrame::WrapExternalPackedMemory(
132 gfx::Rect(visible_size),
134 static_cast<uint8*>(output_buffer->data()),
135 output_buffer->size(),
136 base::SharedMemory::NULLHandle(),
140 *callback = base::Bind(&ThreadSafeCaptureOracle::DidCaptureFrame,
147 gfx::Size ThreadSafeCaptureOracle::GetCaptureSize() const {
148 base::AutoLock guard(lock_);
149 return params_.requested_format.frame_size;
152 void ThreadSafeCaptureOracle::UpdateCaptureSize(const gfx::Size& source_size) {
153 base::AutoLock guard(lock_);
155 // If this is the first call to UpdateCaptureSize(), or the receiver supports
156 // variable resolution, then determine the capture size by treating the
157 // requested width and height as maxima.
158 if (!capture_size_updated_ || params_.resolution_change_policy ==
159 media::RESOLUTION_POLICY_DYNAMIC_WITHIN_LIMIT) {
160 // The capture resolution should not exceed the source frame size.
161 // In other words it should downscale the image but not upscale it.
162 if (source_size.width() > params_.requested_format.frame_size.width() ||
163 source_size.height() > params_.requested_format.frame_size.height()) {
164 gfx::Rect capture_rect = media::ComputeLetterboxRegion(
165 gfx::Rect(params_.requested_format.frame_size), source_size);
166 params_.requested_format.frame_size.SetSize(
167 MakeEven(capture_rect.width()), MakeEven(capture_rect.height()));
169 params_.requested_format.frame_size.SetSize(
170 MakeEven(source_size.width()), MakeEven(source_size.height()));
172 capture_size_updated_ = true;
176 void ThreadSafeCaptureOracle::Stop() {
177 base::AutoLock guard(lock_);
181 void ThreadSafeCaptureOracle::ReportError(const std::string& reason) {
182 base::AutoLock guard(lock_);
184 client_->OnError(reason);
187 void ThreadSafeCaptureOracle::DidCaptureFrame(
189 const scoped_refptr<media::VideoCaptureDevice::Client::Buffer>& buffer,
190 const scoped_refptr<media::VideoFrame>& frame,
191 base::TimeTicks timestamp,
193 base::AutoLock guard(lock_);
194 TRACE_EVENT_ASYNC_END2("mirroring", "Capture", buffer.get(),
196 "timestamp", timestamp.ToInternalValue());
199 return; // Capture is stopped.
202 if (oracle_->CompleteCapture(frame_number, ×tamp)) {
203 media::VideoCaptureFormat format = params_.requested_format;
204 format.frame_size = frame->coded_size();
205 client_->OnIncomingCapturedVideoFrame(buffer, format, frame, timestamp);
210 void ContentVideoCaptureDeviceCore::AllocateAndStart(
211 const media::VideoCaptureParams& params,
212 scoped_ptr<media::VideoCaptureDevice::Client> client) {
213 DCHECK(thread_checker_.CalledOnValidThread());
215 if (state_ != kIdle) {
216 DVLOG(1) << "Allocate() invoked when not in state Idle.";
220 if (params.requested_format.frame_rate <= 0) {
221 std::string error_msg("Invalid frame_rate: ");
222 error_msg += base::DoubleToString(params.requested_format.frame_rate);
223 DVLOG(1) << error_msg;
224 client->OnError(error_msg);
228 if (params.requested_format.pixel_format != media::PIXEL_FORMAT_I420 &&
229 params.requested_format.pixel_format != media::PIXEL_FORMAT_TEXTURE) {
230 std::string error_msg = base::StringPrintf(
231 "unsupported format: %d", params.requested_format.pixel_format);
232 DVLOG(1) << error_msg;
233 client->OnError(error_msg);
237 if (params.requested_format.frame_size.width() < kMinFrameWidth ||
238 params.requested_format.frame_size.height() < kMinFrameHeight) {
239 std::string error_msg =
240 "invalid frame size: " + params.requested_format.frame_size.ToString();
241 DVLOG(1) << error_msg;
242 client->OnError(error_msg);
246 media::VideoCaptureParams new_params = params;
247 // Frame dimensions must each be an even integer since the client wants (or
248 // will convert to) YUV420.
249 new_params.requested_format.frame_size.SetSize(
250 MakeEven(params.requested_format.frame_size.width()),
251 MakeEven(params.requested_format.frame_size.height()));
253 base::TimeDelta capture_period = base::TimeDelta::FromMicroseconds(
254 1000000.0 / params.requested_format.frame_rate + 0.5);
256 scoped_ptr<VideoCaptureOracle> oracle(
257 new VideoCaptureOracle(capture_period,
258 kAcceleratedSubscriberIsSupported));
260 new ThreadSafeCaptureOracle(client.Pass(), oracle.Pass(), new_params);
262 // Starts the capture machine asynchronously.
263 BrowserThread::PostTaskAndReplyWithResult(
266 base::Bind(&VideoCaptureMachine::Start,
267 base::Unretained(capture_machine_.get()),
270 base::Bind(&ContentVideoCaptureDeviceCore::CaptureStarted, AsWeakPtr()));
272 TransitionStateTo(kCapturing);
275 void ContentVideoCaptureDeviceCore::StopAndDeAllocate() {
276 DCHECK(thread_checker_.CalledOnValidThread());
278 if (state_ != kCapturing)
281 oracle_proxy_->Stop();
282 oracle_proxy_ = NULL;
284 TransitionStateTo(kIdle);
286 // Stops the capture machine asynchronously.
287 BrowserThread::PostTask(
288 BrowserThread::UI, FROM_HERE, base::Bind(
289 &VideoCaptureMachine::Stop,
290 base::Unretained(capture_machine_.get()),
291 base::Bind(&base::DoNothing)));
294 void ContentVideoCaptureDeviceCore::CaptureStarted(bool success) {
295 DCHECK(thread_checker_.CalledOnValidThread());
297 std::string reason("Failed to start capture machine.");
303 ContentVideoCaptureDeviceCore::ContentVideoCaptureDeviceCore(
304 scoped_ptr<VideoCaptureMachine> capture_machine)
306 capture_machine_(capture_machine.Pass()) {
307 DCHECK(capture_machine_.get());
310 ContentVideoCaptureDeviceCore::~ContentVideoCaptureDeviceCore() {
311 DCHECK(thread_checker_.CalledOnValidThread());
312 DCHECK_NE(state_, kCapturing);
313 // If capture_machine is not NULL, then we need to return to the UI thread to
314 // safely stop the capture machine.
315 if (capture_machine_) {
316 VideoCaptureMachine* capture_machine_ptr = capture_machine_.get();
317 BrowserThread::PostTask(
318 BrowserThread::UI, FROM_HERE,
319 base::Bind(&VideoCaptureMachine::Stop,
320 base::Unretained(capture_machine_ptr),
321 base::Bind(&DeleteCaptureMachineOnUIThread,
322 base::Passed(&capture_machine_))));
324 DVLOG(1) << "ContentVideoCaptureDeviceCore@" << this << " destroying.";
327 void ContentVideoCaptureDeviceCore::TransitionStateTo(State next_state) {
328 DCHECK(thread_checker_.CalledOnValidThread());
331 static const char* kStateNames[] = {
332 "Idle", "Allocated", "Capturing", "Error"
334 DVLOG(1) << "State change: " << kStateNames[state_]
335 << " --> " << kStateNames[next_state];
341 void ContentVideoCaptureDeviceCore::Error(const std::string& reason) {
342 DCHECK(thread_checker_.CalledOnValidThread());
347 if (oracle_proxy_.get())
348 oracle_proxy_->ReportError(reason);
351 TransitionStateTo(kError);
354 } // namespace content