video_capture_manager.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
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/stl_util.h"
13#include "base/threading/sequenced_worker_pool.h"
14#include "content/browser/renderer_host/media/video_capture_controller.h"
15#include "content/browser/renderer_host/media/video_capture_controller_event_handler.h"
16#include "content/browser/renderer_host/media/web_contents_video_capture_device.h"
17#include "content/public/browser/browser_thread.h"
18#include "content/public/common/content_switches.h"
19#include "content/public/common/media_stream_request.h"
20#include "media/video/capture/fake_video_capture_device.h"
21#include "media/video/capture/screen/screen_capture_device.h"
22#include "media/video/capture/video_capture_device.h"
23
24namespace content {
25
26// Starting id for the first capture session.
27// VideoCaptureManager::kStartOpenSessionId is used as default id without
28// explicitly calling open device.
29enum { kFirstSessionId = VideoCaptureManager::kStartOpenSessionId + 1 };
30
31struct VideoCaptureManager::Controller {
32  Controller(
33      VideoCaptureController* vc_controller,
34      VideoCaptureControllerEventHandler* handler)
35      : controller(vc_controller),
36        ready_to_delete(false) {
37    handlers.push_front(handler);
38  }
39  ~Controller() {}
40
41  scoped_refptr<VideoCaptureController> controller;
42  bool ready_to_delete;
43  Handlers handlers;
44};
45
46VideoCaptureManager::VideoCaptureManager()
47    : listener_(NULL),
48      new_capture_session_id_(kFirstSessionId),
49      use_fake_device_(false) {
50}
51
52VideoCaptureManager::~VideoCaptureManager() {
53  DCHECK(devices_.empty());
54  DCHECK(controllers_.empty());
55}
56
57void VideoCaptureManager::Register(MediaStreamProviderListener* listener,
58                                   base::MessageLoopProxy* device_thread_loop) {
59  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
60  DCHECK(!listener_);
61  DCHECK(!device_loop_);
62  listener_ = listener;
63  device_loop_ = device_thread_loop;
64}
65
66void VideoCaptureManager::Unregister() {
67  DCHECK(listener_);
68  listener_ = NULL;
69}
70
71void VideoCaptureManager::EnumerateDevices(MediaStreamType stream_type) {
72  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
73  DCHECK(listener_);
74  device_loop_->PostTask(
75      FROM_HERE,
76      base::Bind(&VideoCaptureManager::OnEnumerateDevices, this, stream_type));
77}
78
79int VideoCaptureManager::Open(const StreamDeviceInfo& device) {
80  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
81  DCHECK(listener_);
82
83  // Generate a new id for this device.
84  int video_capture_session_id = new_capture_session_id_++;
85
86  device_loop_->PostTask(
87      FROM_HERE,
88      base::Bind(&VideoCaptureManager::OnOpen, this, video_capture_session_id,
89                 device));
90
91  return video_capture_session_id;
92}
93
94void VideoCaptureManager::Close(int capture_session_id) {
95  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
96  DCHECK(listener_);
97  DVLOG(1) << "VideoCaptureManager::Close, id " << capture_session_id;
98  device_loop_->PostTask(
99      FROM_HERE,
100      base::Bind(&VideoCaptureManager::OnClose, this, capture_session_id));
101}
102
103void VideoCaptureManager::Start(
104    const media::VideoCaptureParams& capture_params,
105    media::VideoCaptureDevice::EventHandler* video_capture_receiver) {
106  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
107  device_loop_->PostTask(
108      FROM_HERE,
109      base::Bind(&VideoCaptureManager::OnStart, this, capture_params,
110                 video_capture_receiver));
111}
112
113void VideoCaptureManager::Stop(
114    const media::VideoCaptureSessionId& capture_session_id,
115    base::Closure stopped_cb) {
116  DVLOG(1) << "VideoCaptureManager::Stop, id " << capture_session_id;
117  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
118  device_loop_->PostTask(
119      FROM_HERE,
120      base::Bind(&VideoCaptureManager::OnStop, this, capture_session_id,
121                 stopped_cb));
122}
123
124void VideoCaptureManager::UseFakeDevice() {
125  use_fake_device_ = true;
126}
127
128void VideoCaptureManager::OnEnumerateDevices(MediaStreamType stream_type) {
129  DCHECK(IsOnDeviceThread());
130
131  media::VideoCaptureDevice::Names device_names;
132  GetAvailableDevices(stream_type, &device_names);
133
134  scoped_ptr<StreamDeviceInfoArray> devices(new StreamDeviceInfoArray());
135  for (media::VideoCaptureDevice::Names::iterator it =
136           device_names.begin(); it != device_names.end(); ++it) {
137    bool opened = DeviceOpened(*it);
138    devices->push_back(StreamDeviceInfo(
139        stream_type, it->device_name, it->unique_id, opened));
140  }
141
142  PostOnDevicesEnumerated(stream_type, devices.Pass());
143}
144
145void VideoCaptureManager::OnOpen(int capture_session_id,
146                                 const StreamDeviceInfo& device) {
147  DCHECK(IsOnDeviceThread());
148  DCHECK(devices_.find(capture_session_id) == devices_.end());
149  DVLOG(1) << "VideoCaptureManager::OnOpen, id " << capture_session_id;
150
151  // Check if another session has already opened this device. If so, just
152  // use that opened device.
153  media::VideoCaptureDevice* video_capture_device = GetOpenedDevice(device);
154  if (video_capture_device) {
155    DeviceEntry& new_entry = devices_[capture_session_id];
156    new_entry.stream_type = device.device.type;
157    new_entry.capture_device = video_capture_device;
158    PostOnOpened(device.device.type, capture_session_id);
159    return;
160  }
161
162  // Open the device.
163  media::VideoCaptureDevice::Name vc_device_name;
164  vc_device_name.device_name = device.device.name;
165  vc_device_name.unique_id = device.device.id;
166
167  if (use_fake_device_) {
168    video_capture_device =
169        media::FakeVideoCaptureDevice::Create(vc_device_name);
170  } else {
171    switch (device.device.type) {
172      case MEDIA_DEVICE_VIDEO_CAPTURE: {
173        video_capture_device =
174            media::VideoCaptureDevice::Create(vc_device_name);
175        break;
176      }
177      case MEDIA_TAB_VIDEO_CAPTURE: {
178        video_capture_device = WebContentsVideoCaptureDevice::Create(
179            vc_device_name.unique_id);
180        break;
181      }
182      case MEDIA_SCREEN_VIDEO_CAPTURE: {
183#if (defined(OS_LINUX) && defined(USE_X11)) || \
184    defined(OS_MACOSX) || defined(OS_WIN)
185        scoped_refptr<base::SequencedWorkerPool> blocking_pool =
186            BrowserThread::GetBlockingPool();
187        video_capture_device = new media::ScreenCaptureDevice(
188            blocking_pool->GetSequencedTaskRunner(
189                blocking_pool->GetSequenceToken()));
190#endif  // defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_WIN)
191        break;
192      }
193      default: {
194        NOTIMPLEMENTED();
195        break;
196      }
197    }
198  }
199  if (!video_capture_device) {
200    PostOnError(capture_session_id, kDeviceNotAvailable);
201    return;
202  }
203
204  DeviceEntry& new_entry = devices_[capture_session_id];
205  new_entry.stream_type = device.device.type;
206  new_entry.capture_device = video_capture_device;
207  PostOnOpened(device.device.type, capture_session_id);
208}
209
210void VideoCaptureManager::OnClose(int capture_session_id) {
211  DCHECK(IsOnDeviceThread());
212  DVLOG(1) << "VideoCaptureManager::OnClose, id " << capture_session_id;
213
214  VideoCaptureDevices::iterator device_it = devices_.find(capture_session_id);
215  if (device_it == devices_.end()) {
216    return;
217  }
218  const DeviceEntry removed_entry = device_it->second;
219  devices_.erase(device_it);
220
221  Controllers::iterator cit = controllers_.find(removed_entry.capture_device);
222  if (cit != controllers_.end()) {
223    BrowserThread::PostTask(
224        BrowserThread::IO, FROM_HERE,
225        base::Bind(&VideoCaptureController::StopSession,
226                   cit->second->controller, capture_session_id));
227  }
228
229  if (!DeviceInUse(removed_entry.capture_device)) {
230    // No other users of this device, deallocate (if not done already) and
231    // delete the device. No need to take care of the controller, that is done
232    // by |OnStop|.
233    removed_entry.capture_device->DeAllocate();
234    Controllers::iterator cit = controllers_.find(removed_entry.capture_device);
235    if (cit != controllers_.end()) {
236      delete cit->second;
237      controllers_.erase(cit);
238    }
239    delete removed_entry.capture_device;
240  }
241
242  PostOnClosed(removed_entry.stream_type, capture_session_id);
243}
244
245void VideoCaptureManager::OnStart(
246    const media::VideoCaptureParams capture_params,
247    media::VideoCaptureDevice::EventHandler* video_capture_receiver) {
248  DCHECK(IsOnDeviceThread());
249  DCHECK(video_capture_receiver != NULL);
250  DVLOG(1) << "VideoCaptureManager::OnStart, (" << capture_params.width
251           << ", " << capture_params.height
252           << ", " << capture_params.frame_per_second
253           << ", " << capture_params.session_id
254           << ")";
255
256  media::VideoCaptureDevice* video_capture_device =
257      GetDeviceInternal(capture_params.session_id);
258  if (!video_capture_device) {
259    // Invalid session id.
260    video_capture_receiver->OnError();
261    return;
262  }
263  Controllers::iterator cit = controllers_.find(video_capture_device);
264  if (cit != controllers_.end()) {
265    cit->second->ready_to_delete = false;
266  }
267
268  // Possible errors are signaled to video_capture_receiver by
269  // video_capture_device. video_capture_receiver to perform actions.
270  video_capture_device->Allocate(capture_params.width, capture_params.height,
271                                 capture_params.frame_per_second,
272                                 video_capture_receiver);
273  video_capture_device->Start();
274}
275
276void VideoCaptureManager::OnStop(
277    const media::VideoCaptureSessionId capture_session_id,
278    base::Closure stopped_cb) {
279  DCHECK(IsOnDeviceThread());
280  DVLOG(1) << "VideoCaptureManager::OnStop, id " << capture_session_id;
281
282  VideoCaptureDevices::iterator it = devices_.find(capture_session_id);
283  if (it != devices_.end()) {
284    media::VideoCaptureDevice* video_capture_device = it->second.capture_device;
285    // Possible errors are signaled to video_capture_receiver by
286    // video_capture_device. video_capture_receiver to perform actions.
287    video_capture_device->Stop();
288    video_capture_device->DeAllocate();
289    Controllers::iterator cit = controllers_.find(video_capture_device);
290    if (cit != controllers_.end()) {
291      cit->second->ready_to_delete = true;
292      if (cit->second->handlers.empty()) {
293        delete cit->second;
294        controllers_.erase(cit);
295      }
296    }
297  }
298
299  if (!stopped_cb.is_null())
300    stopped_cb.Run();
301
302  if (capture_session_id == kStartOpenSessionId) {
303    // This device was opened from Start(), not Open(). Close it!
304    OnClose(capture_session_id);
305  }
306}
307
308void VideoCaptureManager::OnOpened(MediaStreamType stream_type,
309                                   int capture_session_id) {
310  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
311  if (!listener_) {
312    // Listener has been removed.
313    return;
314  }
315  listener_->Opened(stream_type, capture_session_id);
316}
317
318void VideoCaptureManager::OnClosed(MediaStreamType stream_type,
319                                   int capture_session_id) {
320  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
321  if (!listener_) {
322    // Listener has been removed.
323    return;
324  }
325  listener_->Closed(stream_type, capture_session_id);
326}
327
328void VideoCaptureManager::OnDevicesEnumerated(
329    MediaStreamType stream_type,
330    scoped_ptr<StreamDeviceInfoArray> devices) {
331  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
332  if (!listener_) {
333    // Listener has been removed.
334    return;
335  }
336  listener_->DevicesEnumerated(stream_type, *devices);
337}
338
339void VideoCaptureManager::OnError(MediaStreamType stream_type,
340                                  int capture_session_id,
341                                  MediaStreamProviderError error) {
342  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
343  if (!listener_) {
344    // Listener has been removed.
345    return;
346  }
347  listener_->Error(stream_type, capture_session_id, error);
348}
349
350void VideoCaptureManager::PostOnOpened(
351    MediaStreamType stream_type, int capture_session_id) {
352  DCHECK(IsOnDeviceThread());
353  BrowserThread::PostTask(BrowserThread::IO,
354                          FROM_HERE,
355                          base::Bind(&VideoCaptureManager::OnOpened, this,
356                                     stream_type, capture_session_id));
357}
358
359void VideoCaptureManager::PostOnClosed(
360    MediaStreamType stream_type, int capture_session_id) {
361  DCHECK(IsOnDeviceThread());
362  BrowserThread::PostTask(BrowserThread::IO,
363                          FROM_HERE,
364                          base::Bind(&VideoCaptureManager::OnClosed, this,
365                                     stream_type, capture_session_id));
366}
367
368void VideoCaptureManager::PostOnDevicesEnumerated(
369    MediaStreamType stream_type,
370    scoped_ptr<StreamDeviceInfoArray> devices) {
371  DCHECK(IsOnDeviceThread());
372  BrowserThread::PostTask(
373      BrowserThread::IO, FROM_HERE,
374      base::Bind(&VideoCaptureManager::OnDevicesEnumerated,
375                 this, stream_type, base::Passed(&devices)));
376}
377
378void VideoCaptureManager::PostOnError(int capture_session_id,
379                                      MediaStreamProviderError error) {
380  DCHECK(IsOnDeviceThread());
381  MediaStreamType stream_type = MEDIA_DEVICE_VIDEO_CAPTURE;
382  VideoCaptureDevices::const_iterator it = devices_.find(capture_session_id);
383  if (it != devices_.end())
384    stream_type = it->second.stream_type;
385  BrowserThread::PostTask(BrowserThread::IO,
386                          FROM_HERE,
387                          base::Bind(&VideoCaptureManager::OnError, this,
388                                     stream_type, capture_session_id, error));
389}
390
391bool VideoCaptureManager::IsOnDeviceThread() const {
392  return device_loop_->BelongsToCurrentThread();
393}
394
395void VideoCaptureManager::GetAvailableDevices(
396    MediaStreamType stream_type,
397    media::VideoCaptureDevice::Names* device_names) {
398  DCHECK(IsOnDeviceThread());
399
400  switch (stream_type) {
401    case MEDIA_DEVICE_VIDEO_CAPTURE:
402      if (!use_fake_device_) {
403        media::VideoCaptureDevice::GetDeviceNames(device_names);
404      } else {
405        media::FakeVideoCaptureDevice::GetDeviceNames(device_names);
406      }
407      break;
408
409    case MEDIA_SCREEN_VIDEO_CAPTURE:
410      device_names->clear();
411      break;
412
413    default:
414      NOTREACHED();
415      break;
416  }
417}
418
419bool VideoCaptureManager::DeviceOpened(
420    const media::VideoCaptureDevice::Name& device_name) {
421  DCHECK(IsOnDeviceThread());
422
423  for (VideoCaptureDevices::iterator it = devices_.begin();
424       it != devices_.end(); ++it) {
425    if (device_name.unique_id ==
426            it->second.capture_device->device_name().unique_id) {
427      // We've found the device!
428      return true;
429    }
430  }
431  return false;
432}
433
434media::VideoCaptureDevice* VideoCaptureManager::GetOpenedDevice(
435    const StreamDeviceInfo& device_info) {
436  DCHECK(IsOnDeviceThread());
437
438  for (VideoCaptureDevices::iterator it = devices_.begin();
439       it != devices_.end(); it++) {
440    if (device_info.device.id ==
441            it->second.capture_device->device_name().unique_id) {
442      return it->second.capture_device;
443    }
444  }
445  return NULL;
446}
447
448bool VideoCaptureManager::DeviceInUse(
449    const media::VideoCaptureDevice* video_capture_device) {
450  DCHECK(IsOnDeviceThread());
451
452  for (VideoCaptureDevices::iterator it = devices_.begin();
453       it != devices_.end(); ++it) {
454    if (video_capture_device == it->second.capture_device) {
455      // We've found the device!
456      return true;
457    }
458  }
459  return false;
460}
461
462void VideoCaptureManager::AddController(
463    const media::VideoCaptureParams& capture_params,
464    VideoCaptureControllerEventHandler* handler,
465    base::Callback<void(VideoCaptureController*)> added_cb) {
466  DCHECK(handler);
467  device_loop_->PostTask(
468      FROM_HERE,
469      base::Bind(&VideoCaptureManager::DoAddControllerOnDeviceThread,
470                 this, capture_params, handler, added_cb));
471}
472
473void VideoCaptureManager::DoAddControllerOnDeviceThread(
474    const media::VideoCaptureParams capture_params,
475    VideoCaptureControllerEventHandler* handler,
476    base::Callback<void(VideoCaptureController*)> added_cb) {
477  DCHECK(IsOnDeviceThread());
478
479  media::VideoCaptureDevice* video_capture_device =
480      GetDeviceInternal(capture_params.session_id);
481  scoped_refptr<VideoCaptureController> controller;
482  if (video_capture_device) {
483    Controllers::iterator cit = controllers_.find(video_capture_device);
484    if (cit == controllers_.end()) {
485      controller = new VideoCaptureController(this);
486      controllers_[video_capture_device] = new Controller(controller, handler);
487    } else {
488      controllers_[video_capture_device]->handlers.push_front(handler);
489      controller = controllers_[video_capture_device]->controller;
490    }
491  }
492  added_cb.Run(controller);
493}
494
495void VideoCaptureManager::RemoveController(
496    VideoCaptureController* controller,
497    VideoCaptureControllerEventHandler* handler) {
498  DCHECK(handler);
499  device_loop_->PostTask(
500      FROM_HERE,
501      base::Bind(&VideoCaptureManager::DoRemoveControllerOnDeviceThread, this,
502                 make_scoped_refptr(controller), handler));
503}
504
505void VideoCaptureManager::DoRemoveControllerOnDeviceThread(
506    VideoCaptureController* controller,
507    VideoCaptureControllerEventHandler* handler) {
508  DCHECK(IsOnDeviceThread());
509
510  for (Controllers::iterator cit = controllers_.begin();
511       cit != controllers_.end(); ++cit) {
512    if (controller == cit->second->controller) {
513      Handlers& handlers = cit->second->handlers;
514      for (Handlers::iterator hit = handlers.begin();
515           hit != handlers.end(); ++hit) {
516        if ((*hit) == handler) {
517          handlers.erase(hit);
518          break;
519        }
520      }
521      if (handlers.empty() && cit->second->ready_to_delete) {
522        delete cit->second;
523        controllers_.erase(cit);
524      }
525      return;
526    }
527  }
528}
529
530media::VideoCaptureDevice* VideoCaptureManager::GetDeviceInternal(
531    int capture_session_id) {
532  DCHECK(IsOnDeviceThread());
533  VideoCaptureDevices::iterator dit = devices_.find(capture_session_id);
534  if (dit != devices_.end()) {
535    return dit->second.capture_device;
536  }
537
538  // Solution for not using MediaStreamManager.
539  // This session id won't be returned by Open().
540  if (capture_session_id == kStartOpenSessionId) {
541    media::VideoCaptureDevice::Names device_names;
542    GetAvailableDevices(MEDIA_DEVICE_VIDEO_CAPTURE, &device_names);
543    if (device_names.empty()) {
544      // No devices available.
545      return NULL;
546    }
547    StreamDeviceInfo device(MEDIA_DEVICE_VIDEO_CAPTURE,
548                            device_names.front().device_name,
549                            device_names.front().unique_id, false);
550
551    // Call OnOpen to open using the first device in the list.
552    OnOpen(capture_session_id, device);
553
554    VideoCaptureDevices::iterator dit = devices_.find(capture_session_id);
555    if (dit != devices_.end()) {
556      return dit->second.capture_device;
557    }
558  }
559  return NULL;
560}
561
562}  // namespace content
563