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

Side by Side Diff: content/renderer/media/video_capture_impl.cc

Issue 6902166: Add VideoCaptureImpl (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: coding style Created 9 years, 7 months 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
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 // Copyright (c) 2011 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/renderer/media/video_capture_impl.h"
6
7 #include "base/memory/singleton.h"
8 #include "content/common/child_process.h"
9 #include "content/common/child_thread.h"
10 #include "content/common/video_capture_messages.h"
11 #include "content/common/view_messages.h"
12 #include "media/base/message_loop_factory_impl.h"
13
14 namespace {
15
16 // VideoCaptureMessageFilterCreator is to be used as a singleton so we can get
17 // access to a shared VideoCaptureMessageFilter.
18 // Example usage:
19 // VideoCaptureMessageFilter* filter =
20 // VideoCaptureMessageFilterCreator::SharedFilter();
21
22 class VideoCaptureMessageFilterCreator {
23 public:
24 VideoCaptureMessageFilterCreator() {
25 int routing_id;
26 ChildThread::current()->Send(
27 new ViewHostMsg_GenerateRoutingID(&routing_id));
28 filter_ = new VideoCaptureMessageFilter(routing_id);
29 filter_->AddFilter();
30 }
31
32 static VideoCaptureMessageFilter* SharedFilter() {
33 return GetInstance()->filter_.get();
34 }
35
36 static VideoCaptureMessageFilterCreator* GetInstance() {
37 return Singleton<VideoCaptureMessageFilterCreator>::get();
38 }
39
40 private:
41 scoped_refptr<VideoCaptureMessageFilter> filter_;
42 };
43
44 } // namespace
45
46 VideoCaptureImplManager::VideoCaptureImplManager() {
47 ml_factory_.reset(new media::MessageLoopFactoryImpl());
48 ml_proxy_ = ml_factory_->GetMessageLoopProxy("VC manager");
49 }
50
51 // static
52 media::VideoCapture* VideoCaptureImplManager::AddDevice(
53 media::VideoCaptureSessionId id,
54 media::VideoCapture::EventHandler* handler) {
55 DCHECK(handler);
56 VideoCaptureImplManager* manager = GetInstance();
57
58 base::AutoLock auto_lock(manager->lock_);
59 Devices::iterator it = manager->devices_.find(id);
60 if (it == manager->devices_.end()) {
61 VideoCaptureImpl* vc =
62 new VideoCaptureImpl(id, manager->ml_proxy_,
63 VideoCaptureMessageFilterCreator::SharedFilter());
64 if (vc) {
scherkus (not reviewing) 2011/05/14 23:33:42 not sure how valuable this check is chrome regist
wjia(left Chromium) 2011/05/17 04:00:25 removed the checking. What if some module has a ba
65 manager->devices_[id] = Device(vc, handler);
66 vc->Init();
67 }
68 return vc;
69 }
70
71 manager->devices_[id].clients.push_front(handler);
72 return it->second.vc;
73 }
74
75 // static
76 void VideoCaptureImplManager::RemoveDevice(
77 media::VideoCaptureSessionId id,
78 media::VideoCapture::EventHandler* handler) {
79 DCHECK(handler);
80 VideoCaptureImplManager* manager = GetInstance();
81
82 base::AutoLock auto_lock(manager->lock_);
83 Devices::iterator it = manager->devices_.find(id);
84 if (it == manager->devices_.end())
scherkus (not reviewing) 2011/05/14 23:33:42 is it valid to call RemoveDevice w/ invalid id? i
wjia(left Chromium) 2011/05/17 04:00:25 The idea is to allow anyone to call RemoveDevice w
85 return;
86
87 size_t size = it->second.clients.size();
88 it->second.clients.remove(handler);
89
90 if (size == it->second.clients.size() || size > 1)
scherkus (not reviewing) 2011/05/14 23:33:42 is it valid to remove pass in an invalid handler p
wjia(left Chromium) 2011/05/17 04:00:25 same explanation as above.
91 return;
92
93 manager->devices_[id].vc->DeInit(NewRunnableMethod(manager,
94 &VideoCaptureImplManager::FreeDevice, manager->devices_[id].vc));
scherkus (not reviewing) 2011/05/14 23:33:42 are you transferring ownership of vc to this task
wjia(left Chromium) 2011/05/17 04:00:25 yes, because |vc| has to be removed from MessageFi
95 manager->devices_.erase(id);
96 return;
97 }
98
99 // static
100 VideoCaptureImplManager* VideoCaptureImplManager::GetInstance() {
101 return Singleton<VideoCaptureImplManager>::get();
102 }
103
104 void VideoCaptureImplManager::FreeDevice(VideoCaptureImpl* vc) {
105 delete vc;
scherkus (not reviewing) 2011/05/14 23:33:42 looks like this method only gets called on the IO
wjia(left Chromium) 2011/05/17 04:00:25 VideoCaptureImplManager::AddDevice() can be called
106 }
107
108 VideoCaptureImpl::VideoCaptureImpl(
109 const media::VideoCaptureSessionId id,
110 scoped_refptr<base::MessageLoopProxy> ml_proxy,
111 VideoCaptureMessageFilter* filter)
112 : VideoCapture(),
113 filter_(filter),
114 session_id_(id),
115 ml_proxy_(ml_proxy),
116 device_id_(0),
117 width_(0),
118 height_(0),
119 frame_rate_(0),
120 video_type_(media::VideoFrame::I420),
scherkus (not reviewing) 2011/05/14 23:33:42 not initializing new_width_ / new_height_
wjia(left Chromium) 2011/05/17 04:00:25 Done.
121 state_(kStopped) {
122 DCHECK(filter);
123 }
124
125 VideoCaptureImpl::~VideoCaptureImpl() {
scherkus (not reviewing) 2011/05/14 23:33:42 nit: collapse {} onto one line if it's <80chars
wjia(left Chromium) 2011/05/17 04:00:25 Done.
126 }
127
128 void VideoCaptureImpl::Init() {
129 AddDelegateOnIOThread();
130 }
131
132 void VideoCaptureImpl::DeInit(Task* task) {
133 StopDevice();
scherkus (not reviewing) 2011/05/14 23:33:42 hrmmmm... so again I'm not 100% on which thread is
wjia(left Chromium) 2011/05/17 04:00:25 You are right. Thanks for pointing this out. I nee
134 RemoveDelegateOnIOThread(task);
135 }
136
137 void VideoCaptureImpl::StartCapture(
138 media::VideoCapture::EventHandler* handler,
139 const VideoCaptureCapability& capability) {
140 DCHECK_EQ(capability.raw_type, media::VideoFrame::I420);
141
142 if (!ml_proxy_->BelongsToCurrentThread()) {
143 ml_proxy_->PostTask(FROM_HERE,
144 NewRunnableMethod(this, &VideoCaptureImpl::StartCapture, handler,
145 capability));
146 return;
147 }
148
149 if (capability.resolution_fixed && master_clients_.size() &&
150 (capability.width != width_ || capability.height != height_)) {
151 // Can't have 2 master clients with different resolutions.
152 handler->OnError(this, 1);
153 return;
154 }
155
156 clients_[handler] = capability;
157 if (capability.resolution_fixed) {
158 master_clients_.push_back(handler);
159 if (master_clients_.size() > 1)
160 return;
161 }
162
163 if (state_ == kStarted) {
164 // Take the resolution of master client.
165 if (capability.resolution_fixed &&
166 (capability.width != width_ || capability.height != height_)) {
167 new_width_ = capability.width;
168 new_height_ = capability.height;
169 DLOG(INFO) << "StartCapture: Got master client with new resolution "
170 "during started, try to restart.";
171 StopDevice();
172 }
173 handler->OnStarted(this);
174 return;
175 }
176
177 if (state_ == kStopping) {
178 if (capability.resolution_fixed || !pending_start()) {
179 new_width_ = capability.width;
180 new_height_ = capability.height;
181 DLOG(INFO) << "StartCapture: Got new resolution, already in stopping.";
182 }
183 handler->OnStarted(this);
184 return;
185 }
186
187 DCHECK_EQ(clients_.size(), 1ul);
188 video_type_ = capability.raw_type;
189 new_width_ = 0;
190 new_height_ = 0;
191 width_ = capability.width;
192 height_ = capability.height;
193
194 StartCaptureInternal();
195 }
196
197 void VideoCaptureImpl::StopCapture(media::VideoCapture::EventHandler* handler) {
198 if (!ml_proxy_->BelongsToCurrentThread()) {
199 ml_proxy_->PostTask(FROM_HERE,
200 NewRunnableMethod(this, &VideoCaptureImpl::StopCapture, handler));
201 return;
202 }
203
204 if (clients_.find(handler) == clients_.end())
205 return;
206
207 handler->OnStopped(this);
208 clients_.erase(handler);
209 master_clients_.remove(handler);
210
211 // Still have at least one master client.
212 if (master_clients_.size() > 0)
213 return;
214
215 // TODO(wjia): Is it really needed to handle resolution change for non-master
216 // clients, except no client case?
217 if (clients_.size() > 0) {
218 DLOG(INFO) << "StopCapture: No master client.";
219 int maxw = 0;
220 int maxh = 0;
221 for (ClientInfo::iterator it = clients_.begin();
222 it != clients_.end(); it++) {
223 if (it->second.width > maxw && it->second.height > maxh) {
224 maxw = it->second.width;
225 maxh = it->second.height;
226 }
227 }
228
229 if (state_ == kStarted) {
230 // Only handle resolution reduction.
231 if (maxw < width_ && maxh < height_) {
232 new_width_ = maxw;
233 new_height_ = maxh;
234 DLOG(INFO) << "StopCapture: New smaller resolution, stopping ...";
235 StopDevice();
236 }
237 return;
238 }
239
240 if (state_ == kStopping) {
241 new_width_ = maxw;
242 new_height_ = maxh;
243 DLOG(INFO) << "StopCapture: New resolution, during stopping.";
244 return;
245 }
246 } else {
247 new_width_ = width_ = 0;
248 new_height_ = height_ = 0;
249 DLOG(INFO) << "StopCapture: No more client, stopping ...";
250 StopDevice();
251 }
252 }
253
254 void VideoCaptureImpl::OnBufferReceived(TransportDIB::Handle handle,
255 base::Time timestamp) {
256 if (!ml_proxy_->BelongsToCurrentThread()) {
257 ml_proxy_->PostTask(FROM_HERE,
258 NewRunnableMethod(this, &VideoCaptureImpl::OnBufferReceived,
259 handle, timestamp));
260 return;
261 }
262
263 if (state_ != kStarted) {
264 filter_->Send(
265 new VideoCaptureHostMsg_BufferReady(0, device_id_, handle));
266 return;
267 }
268
269 media::VideoCapture::VideoFrameBuffer* buffer;
270 CachedDIB::iterator it;
271 for (it = cached_dibs_.begin(); it != cached_dibs_.end(); it++) {
272 if ((*it)->dib->handle() == handle)
273 break;
274 }
275 if (it == cached_dibs_.end()) {
276 TransportDIB* dib = TransportDIB::Map(handle);
277 buffer = new VideoFrameBuffer;
scherkus (not reviewing) 2011/05/14 23:33:42 add ()
wjia(left Chromium) 2011/05/17 04:00:25 Done.
278 buffer->memory_pointer = dib->memory();
279 buffer->buffer_size = dib->size();
280 buffer->width = width_;
281 buffer->height = height_;
282
283 DIBBuffer* dib_buffer = new DIBBuffer(dib, buffer);
284 cached_dibs_.push_back(dib_buffer);
285 } else {
286 buffer = (*it)->mapped_memory;
287 }
288
289 // TODO(wjia): handle buffer sharing with downstream modules.
290 for (ClientInfo::iterator it = clients_.begin(); it != clients_.end(); it++) {
291 it->first->OnBufferReady(this, buffer);
292 }
293
294 filter_->Send(
295 new VideoCaptureHostMsg_BufferReady(0, device_id_, handle));
296 }
297
298 void VideoCaptureImpl::OnStateChanged(
299 const media::VideoCapture::State& state) {
300 if (!ml_proxy_->BelongsToCurrentThread()) {
301 ml_proxy_->PostTask(FROM_HERE,
302 NewRunnableMethod(this, &VideoCaptureImpl::OnStateChanged, state));
303 return;
304 }
305
306 switch (state) {
307 case media::VideoCapture::kStarted:
308 for (ClientInfo::iterator it = clients_.begin();
309 it != clients_.end(); it++) {
310 it->first->OnStarted(this);
311 }
312 break;
313 case media::VideoCapture::kStopped:
314 state_ = kStopped;
315 DLOG(INFO) << "OnStateChanged: stopped!, device_id = " << device_id_;
316 if (pending_start())
317 RestartCapture();
318 break;
319 case media::VideoCapture::kPaused:
320 for (ClientInfo::iterator it = clients_.begin();
321 it != clients_.end(); it++) {
322 it->first->OnPaused(this);
323 }
324 break;
325 case media::VideoCapture::kError:
326 for (ClientInfo::iterator it = clients_.begin();
327 it != clients_.end(); it++) {
328 // TODO(wjia): browser process would send error code.
329 it->first->OnError(this, 1);
330 }
331 break;
332 default:
333 break;
334 }
335 }
336
337 void VideoCaptureImpl::OnDeviceInfoReceived(
338 const media::VideoCaptureParams& device_info) {
339 if (!ml_proxy_->BelongsToCurrentThread()) {
340 ml_proxy_->PostTask(FROM_HERE,
341 NewRunnableMethod(this, &VideoCaptureImpl::OnDeviceInfoReceived,
342 device_info));
343 return;
344 }
345
346 for (ClientInfo::iterator it = clients_.begin(); it != clients_.end(); it++) {
347 it->first->OnDeviceInfoReceived(this, device_info);
348 }
349 }
350
351 void VideoCaptureImpl::StopDevice() {
352 if (!ml_proxy_->BelongsToCurrentThread()) {
scherkus (not reviewing) 2011/05/14 23:33:42 the idea behind using the proxy is that you don't
353 ml_proxy_->PostTask(FROM_HERE,
354 NewRunnableMethod(this, &VideoCaptureImpl::StopDevice));
355 return;
356 }
357
358 if (state_ == kStarted) {
359 state_ = kStopping;
360 filter_->Send(new VideoCaptureHostMsg_Stop(0, device_id_));
361 width_ = height_ = 0;
362 }
363 }
364
365 void VideoCaptureImpl::RestartCapture() {
366 DCHECK(ml_proxy_->BelongsToCurrentThread());
367 DCHECK_EQ(state_, kStopped);
368
369 width_ = new_width_;
370 height_ = new_height_;
371 new_width_ = 0;
372 new_height_ = 0;
373
374 DLOG(INFO) << "RestartCapture, " << width_ << ", " << height_;
375 StartCaptureInternal();
376 }
377
378 void VideoCaptureImpl::StartCaptureInternal() {
379 DCHECK(ml_proxy_->BelongsToCurrentThread());
380
381 media::VideoCaptureParams params;
382 params.width = width_;
383 params.height = height_;
384 params.session_id = session_id_;
385
386 int try_times = 100;
387 bool result = false;
388 for (int i = 0; i < try_times; i++) {
scherkus (not reviewing) 2011/05/14 23:33:42 why do we have to try 100 times?
wjia(left Chromium) 2011/05/17 04:00:25 I just pick up this number for timeout. The reason
389 if (device_id_ &&
390 filter_->Send(new VideoCaptureHostMsg_Start(0, device_id_, params))) {
391 result = true;
392 break;
393 }
394
395 base::PlatformThread::Sleep(1000);
scherkus (not reviewing) 2011/05/14 23:33:42 ???
wjia(left Chromium) 2011/05/17 04:00:25 When either |channel_| or |device_id_| is not read
396 }
397
398 if (!result) {
399 state_ = kStopped;
400 width_ = height_ = 0;
401 for (ClientInfo::iterator it = clients_.begin(); it != clients_.end();
402 it++) {
403 it->first->OnError(this, 2);
404 }
405 } else {
406 state_ = kStarted;
407 for (ClientInfo::iterator it = clients_.begin(); it != clients_.end();
408 it++) {
409 it->first->OnStarted(this);
410 }
411 }
412 }
413
414 void VideoCaptureImpl::AddDelegateOnIOThread() {
415 DCHECK(ChildProcess::current()) << "Must be in the renderer";
416 base::MessageLoopProxy* message_loop_proxy =
417 ChildProcess::current()->io_message_loop_proxy();
418 DCHECK(message_loop_proxy);
419
420 if (!message_loop_proxy->BelongsToCurrentThread()) {
scherkus (not reviewing) 2011/05/14 23:33:42 considering you only have one caller of AddDelegat
wjia(left Chromium) 2011/05/17 04:00:25 Done.
421 message_loop_proxy->PostTask(FROM_HERE,
422 NewRunnableMethod(this, &VideoCaptureImpl::AddDelegateOnIOThread));
423 return;
424 }
425
426 device_id_ = filter_->AddDelegate(this);
scherkus (not reviewing) 2011/05/14 23:33:42 I think there's a race between StartCaptureInterna
wjia(left Chromium) 2011/05/17 04:00:25 The |device_id_| can be only modified on IO thread
427 }
428
429 void VideoCaptureImpl::RemoveDelegateOnIOThread(Task* task) {
430 DCHECK(ChildProcess::current()) << "Must be in the renderer";
431 base::MessageLoopProxy* message_loop_proxy =
432 ChildProcess::current()->io_message_loop_proxy();
433 DCHECK(message_loop_proxy);
434
435 if (!message_loop_proxy->BelongsToCurrentThread()) {
scherkus (not reviewing) 2011/05/14 23:33:42 ditto
wjia(left Chromium) 2011/05/17 04:00:25 Done.
436 message_loop_proxy->PostTask(FROM_HERE,
437 NewRunnableMethod(this, &VideoCaptureImpl::RemoveDelegateOnIOThread,
438 task));
439 return;
440 }
441
442 filter_->RemoveDelegate(this);
443 media::AutoTaskRunner auto_runner(task);
444 }
OLDNEW
« content/renderer/media/video_capture_impl.h ('K') | « content/renderer/media/video_capture_impl.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698