video_capture_manager.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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/command_line.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/media_switches.h"
24#include "media/base/scoped_histogram_timer.h"
25#include "media/video/capture/fake_video_capture_device.h"
26#include "media/video/capture/file_video_capture_device.h"
27#include "media/video/capture/video_capture_device.h"
28
29#if defined(ENABLE_SCREEN_CAPTURE)
30#include "content/browser/media/capture/desktop_capture_device.h"
31#if defined(USE_AURA)
32#include "content/browser/media/capture/desktop_capture_device_aura.h"
33#endif
34#endif
35
36namespace content {
37
38VideoCaptureManager::DeviceEntry::DeviceEntry(
39    MediaStreamType stream_type,
40    const std::string& id,
41    scoped_ptr<VideoCaptureController> controller)
42    : stream_type(stream_type),
43      id(id),
44      video_capture_controller(controller.Pass()) {}
45
46VideoCaptureManager::DeviceEntry::~DeviceEntry() {}
47
48VideoCaptureManager::DeviceInfo::DeviceInfo() {}
49
50VideoCaptureManager::DeviceInfo::DeviceInfo(
51    const media::VideoCaptureDevice::Name& name,
52    const media::VideoCaptureFormats& supported_formats)
53    : name(name),
54      supported_formats(supported_formats) {}
55
56VideoCaptureManager::DeviceInfo::~DeviceInfo() {}
57
58VideoCaptureManager::VideoCaptureManager()
59    : listener_(NULL),
60      new_capture_session_id_(1),
61      artificial_device_source_for_testing_(DISABLED) {
62}
63
64VideoCaptureManager::~VideoCaptureManager() {
65  DCHECK(devices_.empty());
66}
67
68void VideoCaptureManager::Register(
69    MediaStreamProviderListener* listener,
70    const scoped_refptr<base::SingleThreadTaskRunner>& device_task_runner) {
71  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
72  DCHECK(!listener_);
73  DCHECK(!device_task_runner_.get());
74  listener_ = listener;
75  device_task_runner_ = device_task_runner;
76}
77
78void VideoCaptureManager::Unregister() {
79  DCHECK(listener_);
80  listener_ = NULL;
81}
82
83void VideoCaptureManager::EnumerateDevices(MediaStreamType stream_type) {
84  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
85  DVLOG(1) << "VideoCaptureManager::EnumerateDevices, type " << stream_type;
86  DCHECK(listener_);
87  base::PostTaskAndReplyWithResult(
88      device_task_runner_, FROM_HERE,
89      base::Bind(&VideoCaptureManager::GetAvailableDevicesInfoOnDeviceThread,
90                 this, stream_type, devices_info_cache_),
91      base::Bind(&VideoCaptureManager::OnDevicesInfoEnumerated, this,
92                 stream_type));
93}
94
95int VideoCaptureManager::Open(const StreamDeviceInfo& device_info) {
96  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
97  DCHECK(listener_);
98
99  // Generate a new id for the session being opened.
100  const media::VideoCaptureSessionId capture_session_id =
101      new_capture_session_id_++;
102
103  DCHECK(sessions_.find(capture_session_id) == sessions_.end());
104  DVLOG(1) << "VideoCaptureManager::Open, id " << capture_session_id;
105
106  // We just save the stream info for processing later.
107  sessions_[capture_session_id] = device_info.device;
108
109  // Notify our listener asynchronously; this ensures that we return
110  // |capture_session_id| to the caller of this function before using that same
111  // id in a listener event.
112  base::MessageLoop::current()->PostTask(FROM_HERE,
113      base::Bind(&VideoCaptureManager::OnOpened, this,
114                 device_info.device.type, capture_session_id));
115  return capture_session_id;
116}
117
118void VideoCaptureManager::Close(int capture_session_id) {
119  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
120  DCHECK(listener_);
121  DVLOG(1) << "VideoCaptureManager::Close, id " << capture_session_id;
122
123  std::map<media::VideoCaptureSessionId, MediaStreamDevice>::iterator
124      session_it = sessions_.find(capture_session_id);
125  if (session_it == sessions_.end()) {
126    NOTREACHED();
127    return;
128  }
129
130  DeviceEntry* const existing_device = GetDeviceEntryForMediaStreamDevice(
131      session_it->second);
132  if (existing_device) {
133    // Remove any client that is still using the session. This is safe to call
134    // even if there are no clients using the session.
135    existing_device->video_capture_controller->StopSession(capture_session_id);
136
137    // StopSession() may have removed the last client, so we might need to
138    // close the device.
139    DestroyDeviceEntryIfNoClients(existing_device);
140  }
141
142  // Notify listeners asynchronously, and forget the session.
143  base::MessageLoop::current()->PostTask(FROM_HERE,
144      base::Bind(&VideoCaptureManager::OnClosed, this, session_it->second.type,
145                 capture_session_id));
146  sessions_.erase(session_it);
147}
148
149void VideoCaptureManager::UseFakeDevice() {
150  if (CommandLine::ForCurrentProcess()->HasSwitch(
151          switches::kUseFileForFakeVideoCapture)) {
152    artificial_device_source_for_testing_ = Y4M_FILE;
153  } else {
154    artificial_device_source_for_testing_ = TEST_PATTERN;
155  }
156}
157
158void VideoCaptureManager::DoStartDeviceOnDeviceThread(
159    DeviceEntry* entry,
160    const media::VideoCaptureParams& params,
161    scoped_ptr<media::VideoCaptureDevice::Client> device_client) {
162  SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StartDeviceTime");
163  DCHECK(IsOnDeviceThread());
164
165  scoped_ptr<media::VideoCaptureDevice> video_capture_device;
166  switch (entry->stream_type) {
167    case MEDIA_DEVICE_VIDEO_CAPTURE: {
168      // We look up the device id from the renderer in our local enumeration
169      // since the renderer does not have all the information that might be
170      // held in the browser-side VideoCaptureDevice::Name structure.
171      DeviceInfo* found = FindDeviceInfoById(entry->id, devices_info_cache_);
172      if (found) {
173        switch (artificial_device_source_for_testing_) {
174          case DISABLED:
175            video_capture_device.reset(
176                media::VideoCaptureDevice::Create(found->name));
177            break;
178          case TEST_PATTERN:
179            video_capture_device.reset(
180                media::FakeVideoCaptureDevice::Create(found->name));
181            break;
182          case Y4M_FILE:
183            video_capture_device.reset(
184                media::FileVideoCaptureDevice::Create(found->name));
185            break;
186        }
187      }
188      break;
189    }
190    case MEDIA_TAB_VIDEO_CAPTURE: {
191      video_capture_device.reset(
192          WebContentsVideoCaptureDevice::Create(entry->id));
193      break;
194    }
195    case MEDIA_DESKTOP_VIDEO_CAPTURE: {
196#if defined(ENABLE_SCREEN_CAPTURE)
197      DesktopMediaID id = DesktopMediaID::Parse(entry->id);
198#if defined(USE_AURA)
199      if (id.type == DesktopMediaID::TYPE_AURA_WINDOW) {
200        video_capture_device.reset(DesktopCaptureDeviceAura::Create(id));
201      } else
202#endif
203      if (id.type != DesktopMediaID::TYPE_NONE &&
204          id.type != DesktopMediaID::TYPE_AURA_WINDOW) {
205        video_capture_device = DesktopCaptureDevice::Create(id);
206      }
207#endif  // defined(ENABLE_SCREEN_CAPTURE)
208      break;
209    }
210    default: {
211      NOTIMPLEMENTED();
212      break;
213    }
214  }
215
216  if (!video_capture_device) {
217    device_client->OnError("Could not create capture device");
218    return;
219  }
220
221  video_capture_device->AllocateAndStart(params, device_client.Pass());
222  entry->video_capture_device = video_capture_device.Pass();
223}
224
225void VideoCaptureManager::StartCaptureForClient(
226    media::VideoCaptureSessionId session_id,
227    const media::VideoCaptureParams& params,
228    base::ProcessHandle client_render_process,
229    VideoCaptureControllerID client_id,
230    VideoCaptureControllerEventHandler* client_handler,
231    const DoneCB& done_cb) {
232  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
233  DVLOG(1) << "VideoCaptureManager::StartCaptureForClient, "
234           << params.requested_format.frame_size.ToString() << ", "
235           << params.requested_format.frame_rate << ", #" << session_id << ")";
236
237  DeviceEntry* entry = GetOrCreateDeviceEntry(session_id);
238  if (!entry) {
239    done_cb.Run(base::WeakPtr<VideoCaptureController>());
240    return;
241  }
242
243  DCHECK(entry->video_capture_controller);
244
245  // First client starts the device.
246  if (entry->video_capture_controller->GetClientCount() == 0) {
247    DVLOG(1) << "VideoCaptureManager starting device (type = "
248             << entry->stream_type << ", id = " << entry->id << ")";
249
250    device_task_runner_->PostTask(
251        FROM_HERE,
252        base::Bind(
253            &VideoCaptureManager::DoStartDeviceOnDeviceThread,
254            this,
255            entry,
256            params,
257            base::Passed(entry->video_capture_controller->NewDeviceClient())));
258  }
259  // Run the callback first, as AddClient() may trigger OnFrameInfo().
260  done_cb.Run(entry->video_capture_controller->GetWeakPtr());
261  entry->video_capture_controller->AddClient(
262      client_id, client_handler, client_render_process, session_id, params);
263}
264
265void VideoCaptureManager::StopCaptureForClient(
266    VideoCaptureController* controller,
267    VideoCaptureControllerID client_id,
268    VideoCaptureControllerEventHandler* client_handler) {
269  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
270  DCHECK(controller);
271  DCHECK(client_handler);
272
273  DeviceEntry* entry = GetDeviceEntryForController(controller);
274  if (!entry) {
275    NOTREACHED();
276    return;
277  }
278
279  // Detach client from controller.
280  media::VideoCaptureSessionId session_id =
281      controller->RemoveClient(client_id, client_handler);
282  DVLOG(1) << "VideoCaptureManager::StopCaptureForClient, session_id = "
283           << session_id;
284
285  // If controller has no more clients, delete controller and device.
286  DestroyDeviceEntryIfNoClients(entry);
287}
288
289bool VideoCaptureManager::GetDeviceSupportedFormats(
290    media::VideoCaptureSessionId capture_session_id,
291    media::VideoCaptureFormats* supported_formats) {
292  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
293  DCHECK(supported_formats->empty());
294
295  std::map<media::VideoCaptureSessionId, MediaStreamDevice>::iterator it =
296      sessions_.find(capture_session_id);
297  if (it == sessions_.end())
298    return false;
299  DVLOG(1) << "GetDeviceSupportedFormats for device: " << it->second.name;
300
301  // Return all available formats of the device, regardless its started state.
302  DeviceInfo* existing_device =
303      FindDeviceInfoById(it->second.id, devices_info_cache_);
304  if (existing_device)
305    *supported_formats = existing_device->supported_formats;
306  return true;
307}
308
309bool VideoCaptureManager::GetDeviceFormatsInUse(
310    media::VideoCaptureSessionId capture_session_id,
311    media::VideoCaptureFormats* formats_in_use) {
312  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
313  DCHECK(formats_in_use->empty());
314
315  std::map<media::VideoCaptureSessionId, MediaStreamDevice>::iterator it =
316      sessions_.find(capture_session_id);
317  if (it == sessions_.end())
318    return false;
319  DVLOG(1) << "GetDeviceFormatsInUse for device: " << it->second.name;
320
321  // Return the currently in-use format(s) of the device, if it's started.
322  DeviceEntry* device_in_use =
323      GetDeviceEntryForMediaStreamDevice(it->second);
324  if (device_in_use) {
325    // Currently only one format-in-use is supported at the VCC level.
326    formats_in_use->push_back(
327        device_in_use->video_capture_controller->GetVideoCaptureFormat());
328  }
329  return true;
330}
331
332void VideoCaptureManager::DoStopDeviceOnDeviceThread(DeviceEntry* entry) {
333  SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StopDeviceTime");
334  DCHECK(IsOnDeviceThread());
335  if (entry->video_capture_device) {
336    entry->video_capture_device->StopAndDeAllocate();
337  }
338  entry->video_capture_device.reset();
339}
340
341void VideoCaptureManager::OnOpened(
342    MediaStreamType stream_type,
343    media::VideoCaptureSessionId capture_session_id) {
344  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
345  if (!listener_) {
346    // Listener has been removed.
347    return;
348  }
349  listener_->Opened(stream_type, capture_session_id);
350}
351
352void VideoCaptureManager::OnClosed(
353    MediaStreamType stream_type,
354    media::VideoCaptureSessionId capture_session_id) {
355  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
356  if (!listener_) {
357    // Listener has been removed.
358    return;
359  }
360  listener_->Closed(stream_type, capture_session_id);
361}
362
363void VideoCaptureManager::OnDevicesInfoEnumerated(
364    MediaStreamType stream_type,
365    const DeviceInfos& new_devices_info_cache) {
366  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
367
368  if (!listener_) {
369    // Listener has been removed.
370    return;
371  }
372  devices_info_cache_ = new_devices_info_cache;
373
374  // Walk the |devices_info_cache_| and transform from VCD::Name to
375  // StreamDeviceInfo for return purposes.
376  StreamDeviceInfoArray devices;
377  for (DeviceInfos::const_iterator it = devices_info_cache_.begin();
378       it != devices_info_cache_.end(); ++it) {
379    devices.push_back(StreamDeviceInfo(
380        stream_type, it->name.GetNameAndModel(), it->name.id()));
381  }
382  listener_->DevicesEnumerated(stream_type, devices);
383}
384
385bool VideoCaptureManager::IsOnDeviceThread() const {
386  return device_task_runner_->BelongsToCurrentThread();
387}
388
389VideoCaptureManager::DeviceInfos
390VideoCaptureManager::GetAvailableDevicesInfoOnDeviceThread(
391    MediaStreamType stream_type,
392    const DeviceInfos& old_device_info_cache) {
393  SCOPED_UMA_HISTOGRAM_TIMER(
394      "Media.VideoCaptureManager.GetAvailableDevicesInfoOnDeviceThreadTime");
395  DCHECK(IsOnDeviceThread());
396  media::VideoCaptureDevice::Names names_snapshot;
397  switch (stream_type) {
398    case MEDIA_DEVICE_VIDEO_CAPTURE:
399      // Cache the latest enumeration of video capture devices.
400      // We'll refer to this list again in OnOpen to avoid having to
401      // enumerate the devices again.
402      switch (artificial_device_source_for_testing_) {
403        case DISABLED:
404          media::VideoCaptureDevice::GetDeviceNames(&names_snapshot);
405          break;
406        case TEST_PATTERN:
407          media::FakeVideoCaptureDevice::GetDeviceNames(&names_snapshot);
408          break;
409        case Y4M_FILE:
410          media::FileVideoCaptureDevice::GetDeviceNames(&names_snapshot);
411          break;
412      }
413      break;
414
415    case MEDIA_DESKTOP_VIDEO_CAPTURE:
416      // Do nothing.
417      break;
418
419    default:
420      NOTREACHED();
421      break;
422  }
423
424  // Construct |new_devices_info_cache| with the cached devices that are still
425  // present in the system, and remove their names from |names_snapshot|, so we
426  // keep there the truly new devices.
427  DeviceInfos new_devices_info_cache;
428  for (DeviceInfos::const_iterator it_device_info =
429           old_device_info_cache.begin();
430       it_device_info != old_device_info_cache.end(); ++it_device_info) {
431     for (media::VideoCaptureDevice::Names::iterator it =
432              names_snapshot.begin();
433          it != names_snapshot.end(); ++it) {
434      if (it_device_info->name.id() == it->id()) {
435        new_devices_info_cache.push_back(*it_device_info);
436        names_snapshot.erase(it);
437        break;
438      }
439    }
440  }
441
442  // Get the supported capture formats for the new devices in |names_snapshot|.
443  for (media::VideoCaptureDevice::Names::const_iterator it =
444           names_snapshot.begin();
445       it != names_snapshot.end(); ++it) {
446    media::VideoCaptureFormats supported_formats;
447    DeviceInfo device_info(*it, media::VideoCaptureFormats());
448    switch (artificial_device_source_for_testing_) {
449      case DISABLED:
450        media::VideoCaptureDevice::GetDeviceSupportedFormats(
451            *it, &(device_info.supported_formats));
452        break;
453      case TEST_PATTERN:
454        media::FakeVideoCaptureDevice::GetDeviceSupportedFormats(
455            *it, &(device_info.supported_formats));
456        break;
457      case Y4M_FILE:
458        media::FileVideoCaptureDevice::GetDeviceSupportedFormats(
459            *it, &(device_info.supported_formats));
460        break;
461    }
462    new_devices_info_cache.push_back(device_info);
463  }
464  return new_devices_info_cache;
465}
466
467VideoCaptureManager::DeviceEntry*
468VideoCaptureManager::GetDeviceEntryForMediaStreamDevice(
469    const MediaStreamDevice& device_info) {
470  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
471
472  for (DeviceEntries::iterator it = devices_.begin();
473       it != devices_.end(); ++it) {
474    DeviceEntry* device = *it;
475    if (device_info.type == device->stream_type &&
476        device_info.id == device->id) {
477      return device;
478    }
479  }
480  return NULL;
481}
482
483VideoCaptureManager::DeviceEntry*
484VideoCaptureManager::GetDeviceEntryForController(
485    const VideoCaptureController* controller) {
486  // Look up |controller| in |devices_|.
487  for (DeviceEntries::iterator it = devices_.begin();
488       it != devices_.end(); ++it) {
489    if ((*it)->video_capture_controller.get() == controller) {
490      return *it;
491    }
492  }
493  return NULL;
494}
495
496void VideoCaptureManager::DestroyDeviceEntryIfNoClients(DeviceEntry* entry) {
497  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
498  // Removal of the last client stops the device.
499  if (entry->video_capture_controller->GetClientCount() == 0) {
500    DVLOG(1) << "VideoCaptureManager stopping device (type = "
501             << entry->stream_type << ", id = " << entry->id << ")";
502
503    // The DeviceEntry is removed from |devices_| immediately. The controller is
504    // deleted immediately, and the device is freed asynchronously. After this
505    // point, subsequent requests to open this same device ID will create a new
506    // DeviceEntry, VideoCaptureController, and VideoCaptureDevice.
507    devices_.erase(entry);
508    entry->video_capture_controller.reset();
509    device_task_runner_->PostTask(
510        FROM_HERE,
511        base::Bind(&VideoCaptureManager::DoStopDeviceOnDeviceThread, this,
512                   base::Owned(entry)));
513  }
514}
515
516VideoCaptureManager::DeviceEntry* VideoCaptureManager::GetOrCreateDeviceEntry(
517    media::VideoCaptureSessionId capture_session_id) {
518  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
519
520  std::map<media::VideoCaptureSessionId, MediaStreamDevice>::iterator
521      session_it = sessions_.find(capture_session_id);
522  if (session_it == sessions_.end()) {
523    return NULL;
524  }
525  const MediaStreamDevice& device_info = session_it->second;
526
527  // Check if another session has already opened this device. If so, just
528  // use that opened device.
529  DeviceEntry* const existing_device =
530      GetDeviceEntryForMediaStreamDevice(device_info);
531  if (existing_device) {
532    DCHECK_EQ(device_info.type, existing_device->stream_type);
533    return existing_device;
534  }
535
536  scoped_ptr<VideoCaptureController> video_capture_controller(
537      new VideoCaptureController());
538  DeviceEntry* new_device = new DeviceEntry(device_info.type,
539                                            device_info.id,
540                                            video_capture_controller.Pass());
541  devices_.insert(new_device);
542  return new_device;
543}
544
545VideoCaptureManager::DeviceInfo* VideoCaptureManager::FindDeviceInfoById(
546    const std::string& id,
547    DeviceInfos& device_vector) {
548  for (DeviceInfos::iterator it = device_vector.begin();
549       it != device_vector.end(); ++it) {
550    if (it->name.id() == id)
551      return &(*it);
552  }
553  return NULL;
554}
555
556}  // namespace content
557