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

Side by Side Diff: content/browser/renderer_host/media/video_capture_device_impl.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/video_capture_device_impl.h"
6
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/video/capture/video_capture_types.h"
27 #include "ui/gfx/rect.h"
28
29 namespace content {
30
31 namespace {
32
33 void DeleteCaptureMachineOnUIThread(
34 scoped_ptr<VideoCaptureMachine> capture_machine) {
35 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
36 if (capture_machine) {
37 capture_machine->Stop();
38 capture_machine.reset();
39 }
40 }
41
42 } // namespace
43
44 ThreadSafeCaptureOracle::ThreadSafeCaptureOracle(
45 scoped_ptr<media::VideoCaptureDevice::Client> client,
46 scoped_ptr<VideoCaptureOracle> oracle,
47 const gfx::Size& capture_size,
48 int frame_rate)
49 : client_(client.Pass()),
50 oracle_(oracle.Pass()),
51 capture_size_(capture_size),
52 frame_rate_(frame_rate) {}
53
54 ThreadSafeCaptureOracle::~ThreadSafeCaptureOracle() {}
55
56 bool ThreadSafeCaptureOracle::ObserveEventAndDecideCapture(
57 VideoCaptureOracle::Event event,
58 base::Time event_time,
59 scoped_refptr<media::VideoFrame>* storage,
60 CaptureFrameCallback* callback) {
61 base::AutoLock guard(lock_);
62
63 if (!client_)
64 return false; // Capture is stopped.
65
66 scoped_refptr<media::VideoCaptureDevice::Client::Buffer> output_buffer =
67 client_->ReserveOutputBuffer(media::VideoFrame::I420, capture_size_);
68 const bool should_capture =
69 oracle_->ObserveEventAndDecideCapture(event, event_time);
70 const bool content_is_dirty =
71 (event == VideoCaptureOracle::kCompositorUpdate ||
72 event == VideoCaptureOracle::kSoftwarePaint);
73 const char* event_name =
74 (event == VideoCaptureOracle::kTimerPoll ? "poll" :
75 (event == VideoCaptureOracle::kCompositorUpdate ? "gpu" :
76 "paint"));
77
78 // Consider the various reasons not to initiate a capture.
79 if (should_capture && !output_buffer) {
80 TRACE_EVENT_INSTANT1("mirroring",
81 "EncodeLimited",
82 TRACE_EVENT_SCOPE_THREAD,
83 "trigger",
84 event_name);
85 return false;
86 } else if (!should_capture && output_buffer) {
87 if (content_is_dirty) {
88 // This is a normal and acceptable way to drop a frame. We've hit our
89 // capture rate limit: for example, the content is animating at 60fps but
90 // we're capturing at 30fps.
91 TRACE_EVENT_INSTANT1("mirroring", "FpsRateLimited",
92 TRACE_EVENT_SCOPE_THREAD,
93 "trigger", event_name);
94 }
95 return false;
96 } else if (!should_capture && !output_buffer) {
97 // We decided not to capture, but we wouldn't have been able to if we wanted
98 // to because no output buffer was available.
99 TRACE_EVENT_INSTANT1("mirroring", "NearlyEncodeLimited",
100 TRACE_EVENT_SCOPE_THREAD,
101 "trigger", event_name);
102 return false;
103 }
104 int frame_number = oracle_->RecordCapture();
105 TRACE_EVENT_ASYNC_BEGIN2("mirroring", "Capture", output_buffer.get(),
106 "frame_number", frame_number,
107 "trigger", event_name);
108 *callback = base::Bind(&ThreadSafeCaptureOracle::DidCaptureFrame,
109 this,
110 output_buffer,
111 frame_number);
112 *storage = media::VideoFrame::WrapExternalPackedMemory(
113 media::VideoFrame::I420,
114 capture_size_,
115 gfx::Rect(capture_size_),
116 capture_size_,
117 static_cast<uint8*>(output_buffer->data()),
118 output_buffer->size(),
119 base::SharedMemory::NULLHandle(),
120 base::TimeDelta(),
121 base::Closure());
122 return true;
123 }
124
125 void ThreadSafeCaptureOracle::Stop() {
126 base::AutoLock guard(lock_);
127 client_.reset();
128 }
129
130 void ThreadSafeCaptureOracle::ReportError() {
131 base::AutoLock guard(lock_);
132 if (client_)
133 client_->OnError();
134 }
135
136 void ThreadSafeCaptureOracle::DidCaptureFrame(
137 scoped_refptr<media::VideoCaptureDevice::Client::Buffer> buffer,
138 int frame_number,
139 base::Time timestamp,
140 bool success) {
141 base::AutoLock guard(lock_);
142 TRACE_EVENT_ASYNC_END2("mirroring", "Capture", buffer.get(),
143 "success", success,
144 "timestamp", timestamp.ToInternalValue());
145
146 if (!client_)
147 return; // Capture is stopped.
148
149 if (success) {
150 if (oracle_->CompleteCapture(frame_number, timestamp)) {
151 client_->OnIncomingCapturedBuffer(buffer,
152 media::VideoFrame::I420,
153 capture_size_,
154 timestamp,
155 frame_rate_);
156 }
157 }
158 }
159
160 void VideoCaptureDeviceImpl::AllocateAndStart(
161 const media::VideoCaptureParams& params,
162 scoped_ptr<media::VideoCaptureDevice::Client> client) {
163 DCHECK(thread_checker_.CalledOnValidThread());
164
165 if (state_ != kIdle) {
166 DVLOG(1) << "Allocate() invoked when not in state Idle.";
167 return;
168 }
169
170 if (params.requested_format.frame_rate <= 0) {
171 DVLOG(1) << "invalid frame_rate: " << params.requested_format.frame_rate;
172 client->OnError();
173 return;
174 }
175
176 // Frame dimensions must each be a positive, even integer, since the client
177 // wants (or will convert to) YUV420.
178 gfx::Size frame_size(MakeEven(params.requested_format.frame_size.width()),
179 MakeEven(params.requested_format.frame_size.height()));
180 if (frame_size.width() < kMinFrameWidth ||
181 frame_size.height() < kMinFrameHeight) {
182 DVLOG(1) << "invalid frame size: " << frame_size.ToString();
183 client->OnError();
184 return;
185 }
186
187 base::TimeDelta capture_period = base::TimeDelta::FromMicroseconds(
188 1000000.0 / params.requested_format.frame_rate + 0.5);
189
190 scoped_ptr<VideoCaptureOracle> oracle(
191 new VideoCaptureOracle(capture_period,
192 kAcceleratedSubscriberIsSupported));
193 oracle_proxy_ =
194 new ThreadSafeCaptureOracle(client.Pass(),
195 oracle.Pass(),
196 frame_size,
197 params.requested_format.frame_rate);
198
199 // Starts the capture machine asynchronously.
200 BrowserThread::PostTaskAndReplyWithResult(
201 BrowserThread::UI, FROM_HERE,
202 base::Bind(&VideoCaptureMachine::Start,
203 base::Unretained(capture_machine_.get()),
204 oracle_proxy_),
205 base::Bind(&VideoCaptureDeviceImpl::CaptureStarted,
206 AsWeakPtr()));
207
208 TransitionStateTo(kCapturing);
209 }
210
211 void VideoCaptureDeviceImpl::StopAndDeAllocate() {
212 DCHECK(thread_checker_.CalledOnValidThread());
213
214 if (state_ != kCapturing)
215 return;
216
217 oracle_proxy_->Stop();
218 oracle_proxy_ = NULL;
219
220 TransitionStateTo(kIdle);
221
222 // Stops the capture machine asynchronously.
223 BrowserThread::PostTask(
224 BrowserThread::UI, FROM_HERE, base::Bind(
225 &VideoCaptureMachine::Stop,
226 base::Unretained(capture_machine_.get())));
227 }
228
229 void VideoCaptureDeviceImpl::CaptureStarted(bool success) {
230 DCHECK(thread_checker_.CalledOnValidThread());
231 if (!success) {
232 DVLOG(1) << "Failed to start capture machine.";
233 Error();
234 }
235 }
236
237 VideoCaptureDeviceImpl::VideoCaptureDeviceImpl(
238 scoped_ptr<VideoCaptureMachine> capture_machine)
239 : state_(kIdle),
240 capture_machine_(capture_machine.Pass()) {}
241
242 VideoCaptureDeviceImpl::~VideoCaptureDeviceImpl() {
243 // If capture_machine is not NULL, then we need to return to the UI thread to
244 // safely stop the capture machine.
245 if (capture_machine_) {
246 BrowserThread::PostTask(
247 BrowserThread::UI, FROM_HERE, base::Bind(
248 &DeleteCaptureMachineOnUIThread, base::Passed(&capture_machine_)));
249 }
250 DVLOG(1) << "VideoCaptureDeviceImpl@" << this << " destroying.";
251 }
252
253 void VideoCaptureDeviceImpl::TransitionStateTo(State next_state) {
254 DCHECK(thread_checker_.CalledOnValidThread());
255
256 #ifndef NDEBUG
257 static const char* kStateNames[] = {
258 "Idle", "Allocated", "Capturing", "Error"
259 };
260 DVLOG(1) << "State change: " << kStateNames[state_]
261 << " --> " << kStateNames[next_state];
262 #endif
263
264 state_ = next_state;
265 }
266
267 void VideoCaptureDeviceImpl::Error() {
268 DCHECK(thread_checker_.CalledOnValidThread());
269
270 if (state_ == kIdle)
271 return;
272
273 if (oracle_proxy_)
274 oracle_proxy_->ReportError();
275
276 StopAndDeAllocate();
277 TransitionStateTo(kError);
278 }
279
280 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698