Save errno for logging before potentially overwriting it.
[chromium-blink-merge.git] / content / browser / renderer_host / media / video_capture_manager.cc
blob919684d310d6b6407e7d1176a0814c205577476e
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.
5 #include "content/browser/renderer_host/media/video_capture_manager.h"
7 #include <set>
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/base/scoped_histogram_timer.h"
21 #include "media/video/capture/fake_video_capture_device.h"
22 #include "media/video/capture/video_capture_device.h"
24 #if defined(ENABLE_SCREEN_CAPTURE)
25 #include "content/browser/renderer_host/media/screen_capture_device.h"
26 #endif
28 namespace content {
30 // Starting id for the first capture session.
31 // VideoCaptureManager::kStartOpenSessionId is used as default id without
32 // explicitly calling open device.
33 enum { kFirstSessionId = VideoCaptureManager::kStartOpenSessionId + 1 };
35 struct VideoCaptureManager::Controller {
36 Controller(
37 VideoCaptureController* vc_controller,
38 VideoCaptureControllerEventHandler* handler)
39 : controller(vc_controller),
40 ready_to_delete(false) {
41 handlers.push_front(handler);
43 ~Controller() {}
45 scoped_refptr<VideoCaptureController> controller;
46 bool ready_to_delete;
47 Handlers handlers;
50 VideoCaptureManager::VideoCaptureManager()
51 : listener_(NULL),
52 new_capture_session_id_(kFirstSessionId),
53 use_fake_device_(false) {
56 VideoCaptureManager::~VideoCaptureManager() {
57 DCHECK(devices_.empty());
58 DCHECK(controllers_.empty());
61 void VideoCaptureManager::Register(MediaStreamProviderListener* listener,
62 base::MessageLoopProxy* device_thread_loop) {
63 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
64 DCHECK(!listener_);
65 DCHECK(!device_loop_.get());
66 listener_ = listener;
67 device_loop_ = device_thread_loop;
70 void VideoCaptureManager::Unregister() {
71 DCHECK(listener_);
72 listener_ = NULL;
75 void VideoCaptureManager::EnumerateDevices(MediaStreamType stream_type) {
76 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
77 DCHECK(listener_);
78 device_loop_->PostTask(
79 FROM_HERE,
80 base::Bind(&VideoCaptureManager::OnEnumerateDevices, this, stream_type));
83 int VideoCaptureManager::Open(const StreamDeviceInfo& device) {
84 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
85 DCHECK(listener_);
87 // Generate a new id for this device.
88 int video_capture_session_id = new_capture_session_id_++;
90 device_loop_->PostTask(
91 FROM_HERE,
92 base::Bind(&VideoCaptureManager::OnOpen, this, video_capture_session_id,
93 device));
95 return video_capture_session_id;
98 void VideoCaptureManager::Close(int capture_session_id) {
99 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
100 DCHECK(listener_);
101 DVLOG(1) << "VideoCaptureManager::Close, id " << capture_session_id;
102 device_loop_->PostTask(
103 FROM_HERE,
104 base::Bind(&VideoCaptureManager::OnClose, this, capture_session_id));
107 void VideoCaptureManager::Start(
108 const media::VideoCaptureParams& capture_params,
109 media::VideoCaptureDevice::EventHandler* video_capture_receiver) {
110 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
111 device_loop_->PostTask(
112 FROM_HERE,
113 base::Bind(&VideoCaptureManager::OnStart, this, capture_params,
114 video_capture_receiver));
117 void VideoCaptureManager::Stop(
118 const media::VideoCaptureSessionId& capture_session_id,
119 base::Closure stopped_cb) {
120 DVLOG(1) << "VideoCaptureManager::Stop, id " << capture_session_id;
121 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
122 device_loop_->PostTask(
123 FROM_HERE,
124 base::Bind(&VideoCaptureManager::OnStop, this, capture_session_id,
125 stopped_cb));
128 void VideoCaptureManager::UseFakeDevice() {
129 use_fake_device_ = true;
132 void VideoCaptureManager::OnEnumerateDevices(MediaStreamType stream_type) {
133 SCOPED_UMA_HISTOGRAM_TIMER(
134 "Media.VideoCaptureManager.OnEnumerateDevicesTime");
135 DCHECK(IsOnDeviceThread());
137 media::VideoCaptureDevice::Names device_names;
138 GetAvailableDevices(stream_type, &device_names);
140 scoped_ptr<StreamDeviceInfoArray> devices(new StreamDeviceInfoArray());
141 for (media::VideoCaptureDevice::Names::iterator it =
142 device_names.begin(); it != device_names.end(); ++it) {
143 bool opened = DeviceOpened(*it);
144 devices->push_back(StreamDeviceInfo(
145 stream_type, it->device_name, it->unique_id, opened));
148 PostOnDevicesEnumerated(stream_type, devices.Pass());
151 void VideoCaptureManager::OnOpen(int capture_session_id,
152 const StreamDeviceInfo& device) {
153 SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.OnOpenTime");
154 DCHECK(IsOnDeviceThread());
155 DCHECK(devices_.find(capture_session_id) == devices_.end());
156 DVLOG(1) << "VideoCaptureManager::OnOpen, id " << capture_session_id;
158 // Check if another session has already opened this device. If so, just
159 // use that opened device.
160 media::VideoCaptureDevice* video_capture_device = GetOpenedDevice(device);
161 if (video_capture_device) {
162 DeviceEntry& new_entry = devices_[capture_session_id];
163 new_entry.stream_type = device.device.type;
164 new_entry.capture_device = video_capture_device;
165 PostOnOpened(device.device.type, capture_session_id);
166 return;
169 // Open the device.
170 media::VideoCaptureDevice::Name vc_device_name;
171 vc_device_name.device_name = device.device.name;
172 vc_device_name.unique_id = device.device.id;
174 if (use_fake_device_) {
175 video_capture_device =
176 media::FakeVideoCaptureDevice::Create(vc_device_name);
177 } else {
178 switch (device.device.type) {
179 case MEDIA_DEVICE_VIDEO_CAPTURE: {
180 video_capture_device =
181 media::VideoCaptureDevice::Create(vc_device_name);
182 break;
184 case MEDIA_TAB_VIDEO_CAPTURE: {
185 video_capture_device = WebContentsVideoCaptureDevice::Create(
186 vc_device_name.unique_id);
187 break;
189 case MEDIA_SCREEN_VIDEO_CAPTURE: {
190 #if defined(ENABLE_SCREEN_CAPTURE)
191 scoped_refptr<base::SequencedWorkerPool> blocking_pool =
192 BrowserThread::GetBlockingPool();
193 video_capture_device = new ScreenCaptureDevice(
194 blocking_pool->GetSequencedTaskRunner(
195 blocking_pool->GetSequenceToken()));
196 #endif // defined(ENABLE_SCREEN_CAPTURE)
197 break;
199 default: {
200 NOTIMPLEMENTED();
201 break;
205 if (!video_capture_device) {
206 PostOnError(capture_session_id, kDeviceNotAvailable);
207 return;
210 DeviceEntry& new_entry = devices_[capture_session_id];
211 new_entry.stream_type = device.device.type;
212 new_entry.capture_device = video_capture_device;
213 PostOnOpened(device.device.type, capture_session_id);
216 void VideoCaptureManager::OnClose(int capture_session_id) {
217 SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.OnCloseTime");
218 DCHECK(IsOnDeviceThread());
219 DVLOG(1) << "VideoCaptureManager::OnClose, id " << capture_session_id;
221 VideoCaptureDevices::iterator device_it = devices_.find(capture_session_id);
222 if (device_it == devices_.end()) {
223 return;
225 const DeviceEntry removed_entry = device_it->second;
226 devices_.erase(device_it);
228 Controllers::iterator cit = controllers_.find(removed_entry.capture_device);
229 if (cit != controllers_.end()) {
230 BrowserThread::PostTask(
231 BrowserThread::IO, FROM_HERE,
232 base::Bind(&VideoCaptureController::StopSession,
233 cit->second->controller, capture_session_id));
236 if (!DeviceInUse(removed_entry.capture_device)) {
237 // No other users of this device, deallocate (if not done already) and
238 // delete the device. No need to take care of the controller, that is done
239 // by |OnStop|.
240 removed_entry.capture_device->DeAllocate();
241 Controllers::iterator cit = controllers_.find(removed_entry.capture_device);
242 if (cit != controllers_.end()) {
243 delete cit->second;
244 controllers_.erase(cit);
246 delete removed_entry.capture_device;
249 PostOnClosed(removed_entry.stream_type, capture_session_id);
252 void VideoCaptureManager::OnStart(
253 const media::VideoCaptureParams capture_params,
254 media::VideoCaptureDevice::EventHandler* video_capture_receiver) {
255 SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.OnStartTime");
256 DCHECK(IsOnDeviceThread());
257 DCHECK(video_capture_receiver != NULL);
258 DVLOG(1) << "VideoCaptureManager::OnStart, (" << capture_params.width
259 << ", " << capture_params.height
260 << ", " << capture_params.frame_per_second
261 << ", " << capture_params.session_id
262 << ")";
264 media::VideoCaptureDevice* video_capture_device =
265 GetDeviceInternal(capture_params.session_id);
266 if (!video_capture_device) {
267 // Invalid session id.
268 video_capture_receiver->OnError();
269 return;
271 Controllers::iterator cit = controllers_.find(video_capture_device);
272 if (cit != controllers_.end()) {
273 cit->second->ready_to_delete = false;
276 // Possible errors are signaled to video_capture_receiver by
277 // video_capture_device. video_capture_receiver to perform actions.
278 video_capture_device->Allocate(capture_params.width, capture_params.height,
279 capture_params.frame_per_second,
280 video_capture_receiver);
281 video_capture_device->Start();
284 void VideoCaptureManager::OnStop(
285 const media::VideoCaptureSessionId capture_session_id,
286 base::Closure stopped_cb) {
287 SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.OnStopTime");
288 DCHECK(IsOnDeviceThread());
289 DVLOG(1) << "VideoCaptureManager::OnStop, id " << capture_session_id;
291 VideoCaptureDevices::iterator it = devices_.find(capture_session_id);
292 if (it != devices_.end()) {
293 media::VideoCaptureDevice* video_capture_device = it->second.capture_device;
294 // Possible errors are signaled to video_capture_receiver by
295 // video_capture_device. video_capture_receiver to perform actions.
296 video_capture_device->Stop();
297 video_capture_device->DeAllocate();
298 Controllers::iterator cit = controllers_.find(video_capture_device);
299 if (cit != controllers_.end()) {
300 cit->second->ready_to_delete = true;
301 if (cit->second->handlers.empty()) {
302 delete cit->second;
303 controllers_.erase(cit);
308 if (!stopped_cb.is_null())
309 stopped_cb.Run();
311 if (capture_session_id == kStartOpenSessionId) {
312 // This device was opened from Start(), not Open(). Close it!
313 OnClose(capture_session_id);
317 void VideoCaptureManager::OnOpened(MediaStreamType stream_type,
318 int capture_session_id) {
319 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
320 if (!listener_) {
321 // Listener has been removed.
322 return;
324 listener_->Opened(stream_type, capture_session_id);
327 void VideoCaptureManager::OnClosed(MediaStreamType stream_type,
328 int capture_session_id) {
329 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
330 if (!listener_) {
331 // Listener has been removed.
332 return;
334 listener_->Closed(stream_type, capture_session_id);
337 void VideoCaptureManager::OnDevicesEnumerated(
338 MediaStreamType stream_type,
339 scoped_ptr<StreamDeviceInfoArray> devices) {
340 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
341 if (!listener_) {
342 // Listener has been removed.
343 return;
345 listener_->DevicesEnumerated(stream_type, *devices);
348 void VideoCaptureManager::OnError(MediaStreamType stream_type,
349 int capture_session_id,
350 MediaStreamProviderError error) {
351 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
352 if (!listener_) {
353 // Listener has been removed.
354 return;
356 listener_->Error(stream_type, capture_session_id, error);
359 void VideoCaptureManager::PostOnOpened(
360 MediaStreamType stream_type, int capture_session_id) {
361 DCHECK(IsOnDeviceThread());
362 BrowserThread::PostTask(BrowserThread::IO,
363 FROM_HERE,
364 base::Bind(&VideoCaptureManager::OnOpened, this,
365 stream_type, capture_session_id));
368 void VideoCaptureManager::PostOnClosed(
369 MediaStreamType stream_type, int capture_session_id) {
370 DCHECK(IsOnDeviceThread());
371 BrowserThread::PostTask(BrowserThread::IO,
372 FROM_HERE,
373 base::Bind(&VideoCaptureManager::OnClosed, this,
374 stream_type, capture_session_id));
377 void VideoCaptureManager::PostOnDevicesEnumerated(
378 MediaStreamType stream_type,
379 scoped_ptr<StreamDeviceInfoArray> devices) {
380 DCHECK(IsOnDeviceThread());
381 BrowserThread::PostTask(
382 BrowserThread::IO, FROM_HERE,
383 base::Bind(&VideoCaptureManager::OnDevicesEnumerated,
384 this, stream_type, base::Passed(&devices)));
387 void VideoCaptureManager::PostOnError(int capture_session_id,
388 MediaStreamProviderError error) {
389 DCHECK(IsOnDeviceThread());
390 MediaStreamType stream_type = MEDIA_DEVICE_VIDEO_CAPTURE;
391 VideoCaptureDevices::const_iterator it = devices_.find(capture_session_id);
392 if (it != devices_.end())
393 stream_type = it->second.stream_type;
394 BrowserThread::PostTask(BrowserThread::IO,
395 FROM_HERE,
396 base::Bind(&VideoCaptureManager::OnError, this,
397 stream_type, capture_session_id, error));
400 bool VideoCaptureManager::IsOnDeviceThread() const {
401 return device_loop_->BelongsToCurrentThread();
404 void VideoCaptureManager::GetAvailableDevices(
405 MediaStreamType stream_type,
406 media::VideoCaptureDevice::Names* device_names) {
407 DCHECK(IsOnDeviceThread());
409 switch (stream_type) {
410 case MEDIA_DEVICE_VIDEO_CAPTURE:
411 if (!use_fake_device_) {
412 media::VideoCaptureDevice::GetDeviceNames(device_names);
413 } else {
414 media::FakeVideoCaptureDevice::GetDeviceNames(device_names);
416 break;
418 case MEDIA_SCREEN_VIDEO_CAPTURE:
419 device_names->clear();
420 break;
422 default:
423 NOTREACHED();
424 break;
428 bool VideoCaptureManager::DeviceOpened(
429 const media::VideoCaptureDevice::Name& device_name) {
430 DCHECK(IsOnDeviceThread());
432 for (VideoCaptureDevices::iterator it = devices_.begin();
433 it != devices_.end(); ++it) {
434 if (device_name.unique_id ==
435 it->second.capture_device->device_name().unique_id) {
436 // We've found the device!
437 return true;
440 return false;
443 media::VideoCaptureDevice* VideoCaptureManager::GetOpenedDevice(
444 const StreamDeviceInfo& device_info) {
445 DCHECK(IsOnDeviceThread());
447 for (VideoCaptureDevices::iterator it = devices_.begin();
448 it != devices_.end(); it++) {
449 if (device_info.device.id ==
450 it->second.capture_device->device_name().unique_id) {
451 return it->second.capture_device;
454 return NULL;
457 bool VideoCaptureManager::DeviceInUse(
458 const media::VideoCaptureDevice* video_capture_device) {
459 DCHECK(IsOnDeviceThread());
461 for (VideoCaptureDevices::iterator it = devices_.begin();
462 it != devices_.end(); ++it) {
463 if (video_capture_device == it->second.capture_device) {
464 // We've found the device!
465 return true;
468 return false;
471 void VideoCaptureManager::AddController(
472 const media::VideoCaptureParams& capture_params,
473 VideoCaptureControllerEventHandler* handler,
474 base::Callback<void(VideoCaptureController*)> added_cb) {
475 DCHECK(handler);
476 device_loop_->PostTask(
477 FROM_HERE,
478 base::Bind(&VideoCaptureManager::DoAddControllerOnDeviceThread,
479 this, capture_params, handler, added_cb));
482 void VideoCaptureManager::DoAddControllerOnDeviceThread(
483 const media::VideoCaptureParams capture_params,
484 VideoCaptureControllerEventHandler* handler,
485 base::Callback<void(VideoCaptureController*)> added_cb) {
486 DCHECK(IsOnDeviceThread());
488 media::VideoCaptureDevice* video_capture_device =
489 GetDeviceInternal(capture_params.session_id);
490 scoped_refptr<VideoCaptureController> controller;
491 if (video_capture_device) {
492 Controllers::iterator cit = controllers_.find(video_capture_device);
493 if (cit == controllers_.end()) {
494 controller = new VideoCaptureController(this);
495 controllers_[video_capture_device] =
496 new Controller(controller.get(), handler);
497 } else {
498 controllers_[video_capture_device]->handlers.push_front(handler);
499 controller = controllers_[video_capture_device]->controller;
502 added_cb.Run(controller.get());
505 void VideoCaptureManager::RemoveController(
506 VideoCaptureController* controller,
507 VideoCaptureControllerEventHandler* handler) {
508 DCHECK(handler);
509 device_loop_->PostTask(
510 FROM_HERE,
511 base::Bind(&VideoCaptureManager::DoRemoveControllerOnDeviceThread, this,
512 make_scoped_refptr(controller), handler));
515 void VideoCaptureManager::DoRemoveControllerOnDeviceThread(
516 VideoCaptureController* controller,
517 VideoCaptureControllerEventHandler* handler) {
518 DCHECK(IsOnDeviceThread());
520 for (Controllers::iterator cit = controllers_.begin();
521 cit != controllers_.end(); ++cit) {
522 if (controller == cit->second->controller.get()) {
523 Handlers& handlers = cit->second->handlers;
524 for (Handlers::iterator hit = handlers.begin();
525 hit != handlers.end(); ++hit) {
526 if ((*hit) == handler) {
527 handlers.erase(hit);
528 break;
531 if (handlers.empty() && cit->second->ready_to_delete) {
532 delete cit->second;
533 controllers_.erase(cit);
535 return;
540 media::VideoCaptureDevice* VideoCaptureManager::GetDeviceInternal(
541 int capture_session_id) {
542 DCHECK(IsOnDeviceThread());
543 VideoCaptureDevices::iterator dit = devices_.find(capture_session_id);
544 if (dit != devices_.end()) {
545 return dit->second.capture_device;
548 // Solution for not using MediaStreamManager.
549 // This session id won't be returned by Open().
550 if (capture_session_id == kStartOpenSessionId) {
551 media::VideoCaptureDevice::Names device_names;
552 GetAvailableDevices(MEDIA_DEVICE_VIDEO_CAPTURE, &device_names);
553 if (device_names.empty()) {
554 // No devices available.
555 return NULL;
557 StreamDeviceInfo device(MEDIA_DEVICE_VIDEO_CAPTURE,
558 device_names.front().device_name,
559 device_names.front().unique_id, false);
561 // Call OnOpen to open using the first device in the list.
562 OnOpen(capture_session_id, device);
564 VideoCaptureDevices::iterator dit = devices_.find(capture_session_id);
565 if (dit != devices_.end()) {
566 return dit->second.capture_device;
569 return NULL;
572 } // namespace content