1 // Copyright (c) 2012 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_manager.h"
6
7 #include <set>
8
9 #include "base/bind.h"
10 #include "base/bind_helpers.h"
11 #include "base/logging.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/stl_util.h"
14 #include "base/task_runner_util.h"
15 #include "base/threading/sequenced_worker_pool.h"
16 #include "content/browser/media/capture/web_contents_video_capture_device.h"
17 #include "content/browser/renderer_host/media/video_capture_controller.h"
18 #include "content/browser/renderer_host/media/video_capture_controller_event_handler.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "content/public/browser/desktop_media_id.h"
21 #include "content/public/common/content_switches.h"
22 #include "content/public/common/media_stream_request.h"
23 #include "media/base/bind_to_current_loop.h"
24 #include "media/base/scoped_histogram_timer.h"
25 #include "media/video/capture/video_capture_device.h"
26 #include "media/video/capture/video_capture_device_factory.h"
27
28 #if defined(ENABLE_SCREEN_CAPTURE)
29 #include "content/browser/media/capture/desktop_capture_device.h"
30 #if defined(USE_AURA)
31 #include "content/browser/media/capture/desktop_capture_device_aura.h"
32 #endif
33 #endif
34
35 namespace {
36
37 // Compares two VideoCaptureFormat by checking smallest frame_size area, then
38 // by _largest_ frame_rate. Used to order a VideoCaptureFormats vector so that
39 // the first entry for a given resolution has the largest frame rate, as needed
40 // by the ConsolidateCaptureFormats() method.
IsCaptureFormatSmaller(const media::VideoCaptureFormat & format1,const media::VideoCaptureFormat & format2)41 bool IsCaptureFormatSmaller(const media::VideoCaptureFormat& format1,
42 const media::VideoCaptureFormat& format2) {
43 if (format1.frame_size.GetArea() == format2.frame_size.GetArea())
44 return format1.frame_rate > format2.frame_rate;
45 return format1.frame_size.GetArea() < format2.frame_size.GetArea();
46 }
47
IsCaptureFormatSizeEqual(const media::VideoCaptureFormat & format1,const media::VideoCaptureFormat & format2)48 bool IsCaptureFormatSizeEqual(const media::VideoCaptureFormat& format1,
49 const media::VideoCaptureFormat& format2) {
50 return format1.frame_size.GetArea() == format2.frame_size.GetArea();
51 }
52
53 // This function receives a list of capture formats, removes duplicated
54 // resolutions while keeping the highest frame rate for each, and forcing I420
55 // pixel format.
ConsolidateCaptureFormats(media::VideoCaptureFormats * formats)56 void ConsolidateCaptureFormats(media::VideoCaptureFormats* formats) {
57 if (formats->empty())
58 return;
59 std::sort(formats->begin(), formats->end(), IsCaptureFormatSmaller);
60 // Due to the ordering imposed, the largest frame_rate is kept while removing
61 // duplicated resolutions.
62 media::VideoCaptureFormats::iterator last =
63 std::unique(formats->begin(), formats->end(), IsCaptureFormatSizeEqual);
64 formats->erase(last, formats->end());
65 // Mark all formats as I420, since this is what the renderer side will get
66 // anyhow: the actual pixel format is decided at the device level.
67 for (media::VideoCaptureFormats::iterator it = formats->begin();
68 it != formats->end(); ++it) {
69 it->pixel_format = media::PIXEL_FORMAT_I420;
70 }
71 }
72
73 // The maximum number of buffers in the capture pipeline. See
74 // VideoCaptureController ctor comments for more details.
75 const int kMaxNumberOfBuffers = 3;
76 const int kMaxNumberOfBuffersForTabCapture = 5;
77
78 // Used for logging capture events.
79 // Elements in this enum should not be deleted or rearranged; the only
80 // permitted operation is to add new elements before NUM_VIDEO_CAPTURE_EVENT.
81 enum VideoCaptureEvent {
82 VIDEO_CAPTURE_EVENT_START_CAPTURE = 0,
83 VIDEO_CAPTURE_EVENT_STOP_CAPTURE_NORMAL = 1,
84 VIDEO_CAPTURE_EVENT_STOP_CAPTURE_DUE_TO_ERROR = 2,
85 NUM_VIDEO_CAPTURE_EVENT
86 };
87
LogVideoCaptureEvent(VideoCaptureEvent event)88 void LogVideoCaptureEvent(VideoCaptureEvent event) {
89 UMA_HISTOGRAM_ENUMERATION("Media.VideoCaptureManager.Event",
90 event,
91 NUM_VIDEO_CAPTURE_EVENT);
92 }
93
94 } // namespace
95
96 namespace content {
97
DeviceEntry(MediaStreamType stream_type,const std::string & id,scoped_ptr<VideoCaptureController> controller)98 VideoCaptureManager::DeviceEntry::DeviceEntry(
99 MediaStreamType stream_type,
100 const std::string& id,
101 scoped_ptr<VideoCaptureController> controller)
102 : stream_type(stream_type),
103 id(id),
104 video_capture_controller(controller.Pass()) {}
105
~DeviceEntry()106 VideoCaptureManager::DeviceEntry::~DeviceEntry() {}
107
DeviceInfo()108 VideoCaptureManager::DeviceInfo::DeviceInfo() {}
109
DeviceInfo(const media::VideoCaptureDevice::Name & name,const media::VideoCaptureFormats & supported_formats)110 VideoCaptureManager::DeviceInfo::DeviceInfo(
111 const media::VideoCaptureDevice::Name& name,
112 const media::VideoCaptureFormats& supported_formats)
113 : name(name),
114 supported_formats(supported_formats) {}
115
~DeviceInfo()116 VideoCaptureManager::DeviceInfo::~DeviceInfo() {}
117
VideoCaptureManager(scoped_ptr<media::VideoCaptureDeviceFactory> factory)118 VideoCaptureManager::VideoCaptureManager(
119 scoped_ptr<media::VideoCaptureDeviceFactory> factory)
120 : listener_(NULL),
121 new_capture_session_id_(1),
122 video_capture_device_factory_(factory.Pass()) {
123 }
124
~VideoCaptureManager()125 VideoCaptureManager::~VideoCaptureManager() {
126 DCHECK(devices_.empty());
127 }
128
Register(MediaStreamProviderListener * listener,const scoped_refptr<base::SingleThreadTaskRunner> & device_task_runner)129 void VideoCaptureManager::Register(
130 MediaStreamProviderListener* listener,
131 const scoped_refptr<base::SingleThreadTaskRunner>& device_task_runner) {
132 DCHECK_CURRENTLY_ON(BrowserThread::IO);
133 DCHECK(!listener_);
134 DCHECK(!device_task_runner_.get());
135 listener_ = listener;
136 device_task_runner_ = device_task_runner;
137 }
138
Unregister()139 void VideoCaptureManager::Unregister() {
140 DCHECK(listener_);
141 listener_ = NULL;
142 }
143
EnumerateDevices(MediaStreamType stream_type)144 void VideoCaptureManager::EnumerateDevices(MediaStreamType stream_type) {
145 DCHECK_CURRENTLY_ON(BrowserThread::IO);
146 DVLOG(1) << "VideoCaptureManager::EnumerateDevices, type " << stream_type;
147 DCHECK(listener_);
148 DCHECK_EQ(stream_type, MEDIA_DEVICE_VIDEO_CAPTURE);
149
150 // Bind a callback to ConsolidateDevicesInfoOnDeviceThread() with an argument
151 // for another callback to OnDevicesInfoEnumerated() to be run in the current
152 // loop, i.e. IO loop. Pass a timer for UMA histogram collection.
153 base::Callback<void(scoped_ptr<media::VideoCaptureDevice::Names>)>
154 devices_enumerated_callback =
155 base::Bind(&VideoCaptureManager::ConsolidateDevicesInfoOnDeviceThread,
156 this,
157 media::BindToCurrentLoop(base::Bind(
158 &VideoCaptureManager::OnDevicesInfoEnumerated,
159 this,
160 stream_type,
161 base::Owned(new base::ElapsedTimer()))),
162 stream_type,
163 devices_info_cache_);
164 // OK to use base::Unretained() since we own the VCDFactory and |this| is
165 // bound in |devices_enumerated_callback|.
166 device_task_runner_->PostTask(FROM_HERE,
167 base::Bind(&media::VideoCaptureDeviceFactory::EnumerateDeviceNames,
168 base::Unretained(video_capture_device_factory_.get()),
169 devices_enumerated_callback));
170 }
171
Open(const StreamDeviceInfo & device_info)172 int VideoCaptureManager::Open(const StreamDeviceInfo& device_info) {
173 DCHECK_CURRENTLY_ON(BrowserThread::IO);
174 DCHECK(listener_);
175
176 // Generate a new id for the session being opened.
177 const media::VideoCaptureSessionId capture_session_id =
178 new_capture_session_id_++;
179
180 DCHECK(sessions_.find(capture_session_id) == sessions_.end());
181 DVLOG(1) << "VideoCaptureManager::Open, id " << capture_session_id;
182
183 // We just save the stream info for processing later.
184 sessions_[capture_session_id] = device_info.device;
185
186 // Notify our listener asynchronously; this ensures that we return
187 // |capture_session_id| to the caller of this function before using that same
188 // id in a listener event.
189 base::MessageLoop::current()->PostTask(FROM_HERE,
190 base::Bind(&VideoCaptureManager::OnOpened, this,
191 device_info.device.type, capture_session_id));
192 return capture_session_id;
193 }
194
Close(int capture_session_id)195 void VideoCaptureManager::Close(int capture_session_id) {
196 DCHECK_CURRENTLY_ON(BrowserThread::IO);
197 DCHECK(listener_);
198 DVLOG(1) << "VideoCaptureManager::Close, id " << capture_session_id;
199
200 SessionMap::iterator session_it = sessions_.find(capture_session_id);
201 if (session_it == sessions_.end()) {
202 NOTREACHED();
203 return;
204 }
205
206 DeviceEntry* const existing_device = GetDeviceEntryForMediaStreamDevice(
207 session_it->second);
208 if (existing_device) {
209 // Remove any client that is still using the session. This is safe to call
210 // even if there are no clients using the session.
211 existing_device->video_capture_controller->StopSession(capture_session_id);
212
213 // StopSession() may have removed the last client, so we might need to
214 // close the device.
215 DestroyDeviceEntryIfNoClients(existing_device);
216 }
217
218 // Notify listeners asynchronously, and forget the session.
219 base::MessageLoop::current()->PostTask(FROM_HERE,
220 base::Bind(&VideoCaptureManager::OnClosed, this, session_it->second.type,
221 capture_session_id));
222 sessions_.erase(session_it);
223 }
224
DoStartDeviceOnDeviceThread(media::VideoCaptureSessionId session_id,DeviceEntry * entry,const media::VideoCaptureParams & params,scoped_ptr<media::VideoCaptureDevice::Client> device_client)225 void VideoCaptureManager::DoStartDeviceOnDeviceThread(
226 media::VideoCaptureSessionId session_id,
227 DeviceEntry* entry,
228 const media::VideoCaptureParams& params,
229 scoped_ptr<media::VideoCaptureDevice::Client> device_client) {
230 SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StartDeviceTime");
231 DCHECK(IsOnDeviceThread());
232
233 scoped_ptr<media::VideoCaptureDevice> video_capture_device;
234 switch (entry->stream_type) {
235 case MEDIA_DEVICE_VIDEO_CAPTURE: {
236 // We look up the device id from the renderer in our local enumeration
237 // since the renderer does not have all the information that might be
238 // held in the browser-side VideoCaptureDevice::Name structure.
239 DeviceInfo* found = FindDeviceInfoById(entry->id, devices_info_cache_);
240 if (found) {
241 video_capture_device =
242 video_capture_device_factory_->Create(found->name);
243 }
244 break;
245 }
246 case MEDIA_TAB_VIDEO_CAPTURE: {
247 video_capture_device.reset(
248 WebContentsVideoCaptureDevice::Create(entry->id));
249 break;
250 }
251 case MEDIA_DESKTOP_VIDEO_CAPTURE: {
252 #if defined(ENABLE_SCREEN_CAPTURE)
253 DesktopMediaID id = DesktopMediaID::Parse(entry->id);
254 #if defined(USE_AURA)
255 if (id.type == DesktopMediaID::TYPE_AURA_WINDOW) {
256 video_capture_device.reset(DesktopCaptureDeviceAura::Create(id));
257 } else
258 #endif
259 if (id.type != DesktopMediaID::TYPE_NONE &&
260 id.type != DesktopMediaID::TYPE_AURA_WINDOW) {
261 video_capture_device = DesktopCaptureDevice::Create(id);
262 if (notification_window_ids_.find(session_id) !=
263 notification_window_ids_.end()) {
264 static_cast<DesktopCaptureDevice*>(video_capture_device.get())
265 ->SetNotificationWindowId(notification_window_ids_[session_id]);
266 VLOG(2) << "Screen capture notification window passed for session "
267 << session_id;
268 }
269 }
270 #endif // defined(ENABLE_SCREEN_CAPTURE)
271 break;
272 }
273 default: {
274 NOTIMPLEMENTED();
275 break;
276 }
277 }
278
279 if (!video_capture_device) {
280 device_client->OnError("Could not create capture device");
281 return;
282 }
283
284 video_capture_device->AllocateAndStart(params, device_client.Pass());
285 entry->video_capture_device = video_capture_device.Pass();
286 }
287
StartCaptureForClient(media::VideoCaptureSessionId session_id,const media::VideoCaptureParams & params,base::ProcessHandle client_render_process,VideoCaptureControllerID client_id,VideoCaptureControllerEventHandler * client_handler,const DoneCB & done_cb)288 void VideoCaptureManager::StartCaptureForClient(
289 media::VideoCaptureSessionId session_id,
290 const media::VideoCaptureParams& params,
291 base::ProcessHandle client_render_process,
292 VideoCaptureControllerID client_id,
293 VideoCaptureControllerEventHandler* client_handler,
294 const DoneCB& done_cb) {
295 DCHECK_CURRENTLY_ON(BrowserThread::IO);
296 DVLOG(1) << "VideoCaptureManager::StartCaptureForClient, "
297 << params.requested_format.frame_size.ToString() << ", "
298 << params.requested_format.frame_rate << ", #" << session_id << ")";
299
300 DeviceEntry* entry = GetOrCreateDeviceEntry(session_id);
301 if (!entry) {
302 done_cb.Run(base::WeakPtr<VideoCaptureController>());
303 return;
304 }
305
306 DCHECK(entry->video_capture_controller);
307
308 LogVideoCaptureEvent(VIDEO_CAPTURE_EVENT_START_CAPTURE);
309
310 // First client starts the device.
311 if (entry->video_capture_controller->GetActiveClientCount() == 0) {
312 DVLOG(1) << "VideoCaptureManager starting device (type = "
313 << entry->stream_type << ", id = " << entry->id << ")";
314
315 device_task_runner_->PostTask(
316 FROM_HERE,
317 base::Bind(
318 &VideoCaptureManager::DoStartDeviceOnDeviceThread,
319 this,
320 session_id,
321 entry,
322 params,
323 base::Passed(entry->video_capture_controller->NewDeviceClient())));
324 }
325 // Run the callback first, as AddClient() may trigger OnFrameInfo().
326 done_cb.Run(entry->video_capture_controller->GetWeakPtr());
327 entry->video_capture_controller->AddClient(
328 client_id, client_handler, client_render_process, session_id, params);
329 }
330
StopCaptureForClient(VideoCaptureController * controller,VideoCaptureControllerID client_id,VideoCaptureControllerEventHandler * client_handler,bool aborted_due_to_error)331 void VideoCaptureManager::StopCaptureForClient(
332 VideoCaptureController* controller,
333 VideoCaptureControllerID client_id,
334 VideoCaptureControllerEventHandler* client_handler,
335 bool aborted_due_to_error) {
336 DCHECK_CURRENTLY_ON(BrowserThread::IO);
337 DCHECK(controller);
338 DCHECK(client_handler);
339
340 LogVideoCaptureEvent(aborted_due_to_error ?
341 VIDEO_CAPTURE_EVENT_STOP_CAPTURE_DUE_TO_ERROR :
342 VIDEO_CAPTURE_EVENT_STOP_CAPTURE_NORMAL);
343
344 DeviceEntry* entry = GetDeviceEntryForController(controller);
345 if (!entry) {
346 NOTREACHED();
347 return;
348 }
349 if (aborted_due_to_error) {
350 SessionMap::iterator it;
351 for (it = sessions_.begin(); it != sessions_.end(); ++it) {
352 if (it->second.type == entry->stream_type &&
353 it->second.id == entry->id) {
354 listener_->Aborted(it->second.type, it->first);
355 break;
356 }
357 }
358 }
359
360 // Detach client from controller.
361 media::VideoCaptureSessionId session_id =
362 controller->RemoveClient(client_id, client_handler);
363 DVLOG(1) << "VideoCaptureManager::StopCaptureForClient, session_id = "
364 << session_id;
365
366 // If controller has no more clients, delete controller and device.
367 DestroyDeviceEntryIfNoClients(entry);
368 }
369
PauseCaptureForClient(VideoCaptureController * controller,VideoCaptureControllerID client_id,VideoCaptureControllerEventHandler * client_handler)370 void VideoCaptureManager::PauseCaptureForClient(
371 VideoCaptureController* controller,
372 VideoCaptureControllerID client_id,
373 VideoCaptureControllerEventHandler* client_handler) {
374 DCHECK_CURRENTLY_ON(BrowserThread::IO);
375 DCHECK(controller);
376 DCHECK(client_handler);
377 DeviceEntry* entry = GetDeviceEntryForController(controller);
378 if (!entry) {
379 NOTREACHED();
380 return;
381 }
382
383 // We only pause the MEDIA_DEVICE_VIDEO_CAPTURE entry to release camera to
384 // system.
385 if (entry->stream_type != MEDIA_DEVICE_VIDEO_CAPTURE)
386 return;
387
388 controller->PauseOrResumeClient(client_id, client_handler, true);
389 if (controller->GetActiveClientCount() != 0)
390 return;
391
392 // There is no more client, release the camera.
393 device_task_runner_->PostTask(
394 FROM_HERE,
395 base::Bind(&VideoCaptureManager::DoStopDeviceOnDeviceThread, this,
396 base::Unretained(entry)));
397 }
398
ResumeCaptureForClient(media::VideoCaptureSessionId session_id,const media::VideoCaptureParams & params,VideoCaptureController * controller,VideoCaptureControllerID client_id,VideoCaptureControllerEventHandler * client_handler)399 void VideoCaptureManager::ResumeCaptureForClient(
400 media::VideoCaptureSessionId session_id,
401 const media::VideoCaptureParams& params,
402 VideoCaptureController* controller,
403 VideoCaptureControllerID client_id,
404 VideoCaptureControllerEventHandler* client_handler) {
405 DCHECK_CURRENTLY_ON(BrowserThread::IO);
406 DCHECK(controller);
407 DCHECK(client_handler);
408
409 DeviceEntry* entry = GetDeviceEntryForController(controller);
410 if (!entry) {
411 NOTREACHED();
412 return;
413 }
414
415 // We only pause/resume the MEDIA_DEVICE_VIDEO_CAPTURE entry.
416 if (entry->stream_type != MEDIA_DEVICE_VIDEO_CAPTURE)
417 return;
418
419 controller->PauseOrResumeClient(client_id, client_handler, false);
420 if (controller->GetActiveClientCount() != 1)
421 return;
422
423 // This is first active client, allocate the camera.
424 device_task_runner_->PostTask(
425 FROM_HERE,
426 base::Bind(
427 &VideoCaptureManager::DoStartDeviceOnDeviceThread,
428 this,
429 session_id,
430 entry,
431 params,
432 base::Passed(entry->video_capture_controller->NewDeviceClient())));
433 }
434
GetDeviceSupportedFormats(media::VideoCaptureSessionId capture_session_id,media::VideoCaptureFormats * supported_formats)435 bool VideoCaptureManager::GetDeviceSupportedFormats(
436 media::VideoCaptureSessionId capture_session_id,
437 media::VideoCaptureFormats* supported_formats) {
438 DCHECK_CURRENTLY_ON(BrowserThread::IO);
439 DCHECK(supported_formats->empty());
440
441 SessionMap::iterator it = sessions_.find(capture_session_id);
442 if (it == sessions_.end())
443 return false;
444 DVLOG(1) << "GetDeviceSupportedFormats for device: " << it->second.name;
445
446 // Return all available formats of the device, regardless its started state.
447 DeviceInfo* existing_device =
448 FindDeviceInfoById(it->second.id, devices_info_cache_);
449 if (existing_device)
450 *supported_formats = existing_device->supported_formats;
451 return true;
452 }
453
GetDeviceFormatsInUse(media::VideoCaptureSessionId capture_session_id,media::VideoCaptureFormats * formats_in_use)454 bool VideoCaptureManager::GetDeviceFormatsInUse(
455 media::VideoCaptureSessionId capture_session_id,
456 media::VideoCaptureFormats* formats_in_use) {
457 DCHECK_CURRENTLY_ON(BrowserThread::IO);
458 DCHECK(formats_in_use->empty());
459
460 SessionMap::iterator it = sessions_.find(capture_session_id);
461 if (it == sessions_.end())
462 return false;
463 DVLOG(1) << "GetDeviceFormatsInUse for device: " << it->second.name;
464
465 // Return the currently in-use format(s) of the device, if it's started.
466 DeviceEntry* device_in_use =
467 GetDeviceEntryForMediaStreamDevice(it->second);
468 if (device_in_use) {
469 // Currently only one format-in-use is supported at the VCC level.
470 formats_in_use->push_back(
471 device_in_use->video_capture_controller->GetVideoCaptureFormat());
472 }
473 return true;
474 }
475
SetDesktopCaptureWindowId(media::VideoCaptureSessionId session_id,gfx::NativeViewId window_id)476 void VideoCaptureManager::SetDesktopCaptureWindowId(
477 media::VideoCaptureSessionId session_id,
478 gfx::NativeViewId window_id) {
479 DCHECK_CURRENTLY_ON(BrowserThread::IO);
480 VLOG(2) << "SetDesktopCaptureWindowId called for session " << session_id;
481
482 SessionMap::iterator session_it = sessions_.find(session_id);
483 if (session_it == sessions_.end()) {
484 VLOG(2) << "Session not found, will save the notification window.";
485 device_task_runner_->PostTask(
486 FROM_HERE,
487 base::Bind(
488 &VideoCaptureManager::SaveDesktopCaptureWindowIdOnDeviceThread,
489 this,
490 session_id,
491 window_id));
492 return;
493 }
494
495 DeviceEntry* const existing_device =
496 GetDeviceEntryForMediaStreamDevice(session_it->second);
497 if (!existing_device) {
498 VLOG(2) << "Failed to find an existing device.";
499 return;
500 }
501
502 DCHECK_EQ(MEDIA_DESKTOP_VIDEO_CAPTURE, existing_device->stream_type);
503 DesktopMediaID id = DesktopMediaID::Parse(existing_device->id);
504 if (id.type == DesktopMediaID::TYPE_NONE ||
505 id.type == DesktopMediaID::TYPE_AURA_WINDOW) {
506 VLOG(2) << "Video capture device type mismatch.";
507 return;
508 }
509
510 device_task_runner_->PostTask(
511 FROM_HERE,
512 base::Bind(&VideoCaptureManager::SetDesktopCaptureWindowIdOnDeviceThread,
513 this,
514 existing_device,
515 window_id));
516 }
517
DoStopDeviceOnDeviceThread(DeviceEntry * entry)518 void VideoCaptureManager::DoStopDeviceOnDeviceThread(DeviceEntry* entry) {
519 SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StopDeviceTime");
520 DCHECK(IsOnDeviceThread());
521 if (entry->video_capture_device) {
522 entry->video_capture_device->StopAndDeAllocate();
523 }
524 entry->video_capture_device.reset();
525 }
526
OnOpened(MediaStreamType stream_type,media::VideoCaptureSessionId capture_session_id)527 void VideoCaptureManager::OnOpened(
528 MediaStreamType stream_type,
529 media::VideoCaptureSessionId capture_session_id) {
530 DCHECK_CURRENTLY_ON(BrowserThread::IO);
531 if (!listener_) {
532 // Listener has been removed.
533 return;
534 }
535 listener_->Opened(stream_type, capture_session_id);
536 }
537
OnClosed(MediaStreamType stream_type,media::VideoCaptureSessionId capture_session_id)538 void VideoCaptureManager::OnClosed(
539 MediaStreamType stream_type,
540 media::VideoCaptureSessionId capture_session_id) {
541 DCHECK_CURRENTLY_ON(BrowserThread::IO);
542 if (!listener_) {
543 // Listener has been removed.
544 return;
545 }
546 listener_->Closed(stream_type, capture_session_id);
547 }
548
OnDevicesInfoEnumerated(MediaStreamType stream_type,base::ElapsedTimer * timer,const DeviceInfos & new_devices_info_cache)549 void VideoCaptureManager::OnDevicesInfoEnumerated(
550 MediaStreamType stream_type,
551 base::ElapsedTimer* timer,
552 const DeviceInfos& new_devices_info_cache) {
553 DCHECK_CURRENTLY_ON(BrowserThread::IO);
554 UMA_HISTOGRAM_TIMES(
555 "Media.VideoCaptureManager.GetAvailableDevicesInfoOnDeviceThreadTime",
556 timer->Elapsed());
557 if (!listener_) {
558 // Listener has been removed.
559 return;
560 }
561 devices_info_cache_ = new_devices_info_cache;
562
563 // Walk the |devices_info_cache_| and transform from VCD::Name to
564 // StreamDeviceInfo for return purposes.
565 StreamDeviceInfoArray devices;
566 for (DeviceInfos::const_iterator it = devices_info_cache_.begin();
567 it != devices_info_cache_.end(); ++it) {
568 devices.push_back(StreamDeviceInfo(
569 stream_type, it->name.GetNameAndModel(), it->name.id()));
570 }
571 listener_->DevicesEnumerated(stream_type, devices);
572 }
573
IsOnDeviceThread() const574 bool VideoCaptureManager::IsOnDeviceThread() const {
575 return device_task_runner_->BelongsToCurrentThread();
576 }
577
ConsolidateDevicesInfoOnDeviceThread(base::Callback<void (const DeviceInfos &)> on_devices_enumerated_callback,MediaStreamType stream_type,const DeviceInfos & old_device_info_cache,scoped_ptr<media::VideoCaptureDevice::Names> names_snapshot)578 void VideoCaptureManager::ConsolidateDevicesInfoOnDeviceThread(
579 base::Callback<void(const DeviceInfos&)> on_devices_enumerated_callback,
580 MediaStreamType stream_type,
581 const DeviceInfos& old_device_info_cache,
582 scoped_ptr<media::VideoCaptureDevice::Names> names_snapshot) {
583 DCHECK(IsOnDeviceThread());
584 // Construct |new_devices_info_cache| with the cached devices that are still
585 // present in the system, and remove their names from |names_snapshot|, so we
586 // keep there the truly new devices.
587 DeviceInfos new_devices_info_cache;
588 for (DeviceInfos::const_iterator it_device_info =
589 old_device_info_cache.begin();
590 it_device_info != old_device_info_cache.end(); ++it_device_info) {
591 for (media::VideoCaptureDevice::Names::iterator it =
592 names_snapshot->begin();
593 it != names_snapshot->end(); ++it) {
594 if (it_device_info->name.id() == it->id()) {
595 new_devices_info_cache.push_back(*it_device_info);
596 names_snapshot->erase(it);
597 break;
598 }
599 }
600 }
601
602 // Get the supported capture formats for the new devices in |names_snapshot|.
603 for (media::VideoCaptureDevice::Names::const_iterator it =
604 names_snapshot->begin();
605 it != names_snapshot->end(); ++it) {
606 media::VideoCaptureFormats supported_formats;
607 DeviceInfo device_info(*it, media::VideoCaptureFormats());
608 video_capture_device_factory_->GetDeviceSupportedFormats(
609 *it, &(device_info.supported_formats));
610 ConsolidateCaptureFormats(&device_info.supported_formats);
611 new_devices_info_cache.push_back(device_info);
612 }
613
614 on_devices_enumerated_callback.Run(new_devices_info_cache);
615 }
616
617 VideoCaptureManager::DeviceEntry*
GetDeviceEntryForMediaStreamDevice(const MediaStreamDevice & device_info)618 VideoCaptureManager::GetDeviceEntryForMediaStreamDevice(
619 const MediaStreamDevice& device_info) {
620 DCHECK_CURRENTLY_ON(BrowserThread::IO);
621
622 for (DeviceEntries::iterator it = devices_.begin();
623 it != devices_.end(); ++it) {
624 DeviceEntry* device = *it;
625 if (device_info.type == device->stream_type &&
626 device_info.id == device->id) {
627 return device;
628 }
629 }
630 return NULL;
631 }
632
633 VideoCaptureManager::DeviceEntry*
GetDeviceEntryForController(const VideoCaptureController * controller) const634 VideoCaptureManager::GetDeviceEntryForController(
635 const VideoCaptureController* controller) const {
636 // Look up |controller| in |devices_|.
637 for (DeviceEntries::const_iterator it = devices_.begin();
638 it != devices_.end(); ++it) {
639 if ((*it)->video_capture_controller.get() == controller) {
640 return *it;
641 }
642 }
643 return NULL;
644 }
645
DestroyDeviceEntryIfNoClients(DeviceEntry * entry)646 void VideoCaptureManager::DestroyDeviceEntryIfNoClients(DeviceEntry* entry) {
647 DCHECK_CURRENTLY_ON(BrowserThread::IO);
648 // Removal of the last client stops the device.
649 if (entry->video_capture_controller->GetClientCount() == 0) {
650 DVLOG(1) << "VideoCaptureManager stopping device (type = "
651 << entry->stream_type << ", id = " << entry->id << ")";
652
653 // The DeviceEntry is removed from |devices_| immediately. The controller is
654 // deleted immediately, and the device is freed asynchronously. After this
655 // point, subsequent requests to open this same device ID will create a new
656 // DeviceEntry, VideoCaptureController, and VideoCaptureDevice.
657 devices_.erase(entry);
658 entry->video_capture_controller.reset();
659 device_task_runner_->PostTask(
660 FROM_HERE,
661 base::Bind(&VideoCaptureManager::DoStopDeviceOnDeviceThread, this,
662 base::Owned(entry)));
663 }
664 }
665
GetOrCreateDeviceEntry(media::VideoCaptureSessionId capture_session_id)666 VideoCaptureManager::DeviceEntry* VideoCaptureManager::GetOrCreateDeviceEntry(
667 media::VideoCaptureSessionId capture_session_id) {
668 DCHECK_CURRENTLY_ON(BrowserThread::IO);
669
670 SessionMap::iterator session_it = sessions_.find(capture_session_id);
671 if (session_it == sessions_.end()) {
672 return NULL;
673 }
674 const MediaStreamDevice& device_info = session_it->second;
675
676 // Check if another session has already opened this device. If so, just
677 // use that opened device.
678 DeviceEntry* const existing_device =
679 GetDeviceEntryForMediaStreamDevice(device_info);
680 if (existing_device) {
681 DCHECK_EQ(device_info.type, existing_device->stream_type);
682 return existing_device;
683 }
684
685 const int max_buffers = device_info.type == MEDIA_TAB_VIDEO_CAPTURE ?
686 kMaxNumberOfBuffersForTabCapture : kMaxNumberOfBuffers;
687 scoped_ptr<VideoCaptureController> video_capture_controller(
688 new VideoCaptureController(max_buffers));
689 DeviceEntry* new_device = new DeviceEntry(device_info.type,
690 device_info.id,
691 video_capture_controller.Pass());
692 devices_.insert(new_device);
693 return new_device;
694 }
695
FindDeviceInfoById(const std::string & id,DeviceInfos & device_vector)696 VideoCaptureManager::DeviceInfo* VideoCaptureManager::FindDeviceInfoById(
697 const std::string& id,
698 DeviceInfos& device_vector) {
699 for (DeviceInfos::iterator it = device_vector.begin();
700 it != device_vector.end(); ++it) {
701 if (it->name.id() == id)
702 return &(*it);
703 }
704 return NULL;
705 }
706
SetDesktopCaptureWindowIdOnDeviceThread(DeviceEntry * entry,gfx::NativeViewId window_id)707 void VideoCaptureManager::SetDesktopCaptureWindowIdOnDeviceThread(
708 DeviceEntry* entry,
709 gfx::NativeViewId window_id) {
710 DCHECK(IsOnDeviceThread());
711 DCHECK(entry->stream_type == MEDIA_DESKTOP_VIDEO_CAPTURE);
712 #if defined(ENABLE_SCREEN_CAPTURE)
713 DesktopCaptureDevice* device =
714 static_cast<DesktopCaptureDevice*>(entry->video_capture_device.get());
715 device->SetNotificationWindowId(window_id);
716 VLOG(2) << "Screen capture notification window passed on device thread.";
717 #endif
718 }
719
SaveDesktopCaptureWindowIdOnDeviceThread(media::VideoCaptureSessionId session_id,gfx::NativeViewId window_id)720 void VideoCaptureManager::SaveDesktopCaptureWindowIdOnDeviceThread(
721 media::VideoCaptureSessionId session_id,
722 gfx::NativeViewId window_id) {
723 DCHECK(IsOnDeviceThread());
724 DCHECK(notification_window_ids_.find(session_id) ==
725 notification_window_ids_.end());
726 notification_window_ids_[session_id] = window_id;
727 VLOG(2) << "Screen capture notification window saved for session "
728 << session_id << " on device thread.";
729 }
730
731 } // namespace content
732