Revert 268405 "Make sure that ScratchBuffer::Allocate() always r..."
[chromium-blink-merge.git] / content / browser / renderer_host / media / video_capture_manager.cc
blob6568407bdfdf5042ebdc69c83106492945788bb9
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/logging.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/stl_util.h"
13 #include "base/task_runner_util.h"
14 #include "base/threading/sequenced_worker_pool.h"
15 #include "content/browser/media/capture/web_contents_video_capture_device.h"
16 #include "content/browser/renderer_host/media/video_capture_controller.h"
17 #include "content/browser/renderer_host/media/video_capture_controller_event_handler.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "content/public/browser/desktop_media_id.h"
20 #include "content/public/common/content_switches.h"
21 #include "content/public/common/media_stream_request.h"
22 #include "media/base/scoped_histogram_timer.h"
23 #include "media/video/capture/video_capture_device.h"
24 #include "media/video/capture/video_capture_device_factory.h"
26 #if defined(ENABLE_SCREEN_CAPTURE)
27 #include "content/browser/media/capture/desktop_capture_device.h"
28 #if defined(USE_AURA)
29 #include "content/browser/media/capture/desktop_capture_device_aura.h"
30 #endif
31 #endif
33 namespace {
35 // Compares two VideoCaptureFormat by checking smallest frame_size area, then
36 // by _largest_ frame_rate. Used to order a VideoCaptureFormats vector so that
37 // the first entry for a given resolution has the largest frame rate, as needed
38 // by the ConsolidateCaptureFormats() method.
39 bool IsCaptureFormatSmaller(const media::VideoCaptureFormat& format1,
40 const media::VideoCaptureFormat& format2) {
41 if (format1.frame_size.GetArea() == format2.frame_size.GetArea())
42 return format1.frame_rate > format2.frame_rate;
43 return format1.frame_size.GetArea() < format2.frame_size.GetArea();
46 bool IsCaptureFormatSizeEqual(const media::VideoCaptureFormat& format1,
47 const media::VideoCaptureFormat& format2) {
48 return format1.frame_size.GetArea() == format2.frame_size.GetArea();
51 // This function receives a list of capture formats, removes duplicated
52 // resolutions while keeping the highest frame rate for each, and forcing I420
53 // pixel format.
54 void ConsolidateCaptureFormats(media::VideoCaptureFormats* formats) {
55 if (formats->empty())
56 return;
57 std::sort(formats->begin(), formats->end(), IsCaptureFormatSmaller);
58 // Due to the ordering imposed, the largest frame_rate is kept while removing
59 // duplicated resolutions.
60 media::VideoCaptureFormats::iterator last =
61 std::unique(formats->begin(), formats->end(), IsCaptureFormatSizeEqual);
62 formats->erase(last, formats->end());
63 // Mark all formats as I420, since this is what the renderer side will get
64 // anyhow: the actual pixel format is decided at the device level.
65 for (media::VideoCaptureFormats::iterator it = formats->begin();
66 it != formats->end(); ++it) {
67 it->pixel_format = media::PIXEL_FORMAT_I420;
71 } // namespace
73 namespace content {
75 VideoCaptureManager::DeviceEntry::DeviceEntry(
76 MediaStreamType stream_type,
77 const std::string& id,
78 scoped_ptr<VideoCaptureController> controller)
79 : stream_type(stream_type),
80 id(id),
81 video_capture_controller(controller.Pass()) {}
83 VideoCaptureManager::DeviceEntry::~DeviceEntry() {}
85 VideoCaptureManager::DeviceInfo::DeviceInfo() {}
87 VideoCaptureManager::DeviceInfo::DeviceInfo(
88 const media::VideoCaptureDevice::Name& name,
89 const media::VideoCaptureFormats& supported_formats)
90 : name(name),
91 supported_formats(supported_formats) {}
93 VideoCaptureManager::DeviceInfo::~DeviceInfo() {}
95 VideoCaptureManager::VideoCaptureManager(
96 scoped_ptr<media::VideoCaptureDeviceFactory> factory)
97 : listener_(NULL),
98 new_capture_session_id_(1),
99 video_capture_device_factory_(factory.Pass()) {
102 VideoCaptureManager::~VideoCaptureManager() {
103 DCHECK(devices_.empty());
106 void VideoCaptureManager::Register(
107 MediaStreamProviderListener* listener,
108 const scoped_refptr<base::SingleThreadTaskRunner>& device_task_runner) {
109 DCHECK_CURRENTLY_ON(BrowserThread::IO);
110 DCHECK(!listener_);
111 DCHECK(!device_task_runner_.get());
112 listener_ = listener;
113 device_task_runner_ = device_task_runner;
116 void VideoCaptureManager::Unregister() {
117 DCHECK(listener_);
118 listener_ = NULL;
121 void VideoCaptureManager::EnumerateDevices(MediaStreamType stream_type) {
122 DCHECK_CURRENTLY_ON(BrowserThread::IO);
123 DVLOG(1) << "VideoCaptureManager::EnumerateDevices, type " << stream_type;
124 DCHECK(listener_);
125 base::PostTaskAndReplyWithResult(
126 device_task_runner_, FROM_HERE,
127 base::Bind(&VideoCaptureManager::GetAvailableDevicesInfoOnDeviceThread,
128 this, stream_type, devices_info_cache_),
129 base::Bind(&VideoCaptureManager::OnDevicesInfoEnumerated, this,
130 stream_type));
133 int VideoCaptureManager::Open(const StreamDeviceInfo& device_info) {
134 DCHECK_CURRENTLY_ON(BrowserThread::IO);
135 DCHECK(listener_);
137 // Generate a new id for the session being opened.
138 const media::VideoCaptureSessionId capture_session_id =
139 new_capture_session_id_++;
141 DCHECK(sessions_.find(capture_session_id) == sessions_.end());
142 DVLOG(1) << "VideoCaptureManager::Open, id " << capture_session_id;
144 // We just save the stream info for processing later.
145 sessions_[capture_session_id] = device_info.device;
147 // Notify our listener asynchronously; this ensures that we return
148 // |capture_session_id| to the caller of this function before using that same
149 // id in a listener event.
150 base::MessageLoop::current()->PostTask(FROM_HERE,
151 base::Bind(&VideoCaptureManager::OnOpened, this,
152 device_info.device.type, capture_session_id));
153 return capture_session_id;
156 void VideoCaptureManager::Close(int capture_session_id) {
157 DCHECK_CURRENTLY_ON(BrowserThread::IO);
158 DCHECK(listener_);
159 DVLOG(1) << "VideoCaptureManager::Close, id " << capture_session_id;
161 SessionMap::iterator session_it = sessions_.find(capture_session_id);
162 if (session_it == sessions_.end()) {
163 NOTREACHED();
164 return;
167 DeviceEntry* const existing_device = GetDeviceEntryForMediaStreamDevice(
168 session_it->second);
169 if (existing_device) {
170 // Remove any client that is still using the session. This is safe to call
171 // even if there are no clients using the session.
172 existing_device->video_capture_controller->StopSession(capture_session_id);
174 // StopSession() may have removed the last client, so we might need to
175 // close the device.
176 DestroyDeviceEntryIfNoClients(existing_device);
179 // Notify listeners asynchronously, and forget the session.
180 base::MessageLoop::current()->PostTask(FROM_HERE,
181 base::Bind(&VideoCaptureManager::OnClosed, this, session_it->second.type,
182 capture_session_id));
183 sessions_.erase(session_it);
186 void VideoCaptureManager::DoStartDeviceOnDeviceThread(
187 media::VideoCaptureSessionId session_id,
188 DeviceEntry* entry,
189 const media::VideoCaptureParams& params,
190 scoped_ptr<media::VideoCaptureDevice::Client> device_client) {
191 SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StartDeviceTime");
192 DCHECK(IsOnDeviceThread());
194 scoped_ptr<media::VideoCaptureDevice> video_capture_device;
195 switch (entry->stream_type) {
196 case MEDIA_DEVICE_VIDEO_CAPTURE: {
197 // We look up the device id from the renderer in our local enumeration
198 // since the renderer does not have all the information that might be
199 // held in the browser-side VideoCaptureDevice::Name structure.
200 DeviceInfo* found = FindDeviceInfoById(entry->id, devices_info_cache_);
201 if (found) {
202 video_capture_device =
203 video_capture_device_factory_->Create(found->name);
205 break;
207 case MEDIA_TAB_VIDEO_CAPTURE: {
208 video_capture_device.reset(
209 WebContentsVideoCaptureDevice::Create(entry->id));
210 break;
212 case MEDIA_DESKTOP_VIDEO_CAPTURE: {
213 #if defined(ENABLE_SCREEN_CAPTURE)
214 DesktopMediaID id = DesktopMediaID::Parse(entry->id);
215 #if defined(USE_AURA)
216 if (id.type == DesktopMediaID::TYPE_AURA_WINDOW) {
217 video_capture_device.reset(DesktopCaptureDeviceAura::Create(id));
218 } else
219 #endif
220 if (id.type != DesktopMediaID::TYPE_NONE &&
221 id.type != DesktopMediaID::TYPE_AURA_WINDOW) {
222 video_capture_device = DesktopCaptureDevice::Create(id);
223 if (notification_window_ids_.find(session_id) !=
224 notification_window_ids_.end()) {
225 static_cast<DesktopCaptureDevice*>(video_capture_device.get())
226 ->SetNotificationWindowId(notification_window_ids_[session_id]);
229 #endif // defined(ENABLE_SCREEN_CAPTURE)
230 break;
232 default: {
233 NOTIMPLEMENTED();
234 break;
238 if (!video_capture_device) {
239 device_client->OnError("Could not create capture device");
240 return;
243 video_capture_device->AllocateAndStart(params, device_client.Pass());
244 entry->video_capture_device = video_capture_device.Pass();
247 void VideoCaptureManager::StartCaptureForClient(
248 media::VideoCaptureSessionId session_id,
249 const media::VideoCaptureParams& params,
250 base::ProcessHandle client_render_process,
251 VideoCaptureControllerID client_id,
252 VideoCaptureControllerEventHandler* client_handler,
253 const DoneCB& done_cb) {
254 DCHECK_CURRENTLY_ON(BrowserThread::IO);
255 DVLOG(1) << "VideoCaptureManager::StartCaptureForClient, "
256 << params.requested_format.frame_size.ToString() << ", "
257 << params.requested_format.frame_rate << ", #" << session_id << ")";
259 DeviceEntry* entry = GetOrCreateDeviceEntry(session_id);
260 if (!entry) {
261 done_cb.Run(base::WeakPtr<VideoCaptureController>());
262 return;
265 DCHECK(entry->video_capture_controller);
267 // First client starts the device.
268 if (entry->video_capture_controller->GetClientCount() == 0) {
269 DVLOG(1) << "VideoCaptureManager starting device (type = "
270 << entry->stream_type << ", id = " << entry->id << ")";
272 device_task_runner_->PostTask(
273 FROM_HERE,
274 base::Bind(
275 &VideoCaptureManager::DoStartDeviceOnDeviceThread,
276 this,
277 session_id,
278 entry,
279 params,
280 base::Passed(entry->video_capture_controller->NewDeviceClient())));
282 // Run the callback first, as AddClient() may trigger OnFrameInfo().
283 done_cb.Run(entry->video_capture_controller->GetWeakPtr());
284 entry->video_capture_controller->AddClient(
285 client_id, client_handler, client_render_process, session_id, params);
288 void VideoCaptureManager::StopCaptureForClient(
289 VideoCaptureController* controller,
290 VideoCaptureControllerID client_id,
291 VideoCaptureControllerEventHandler* client_handler,
292 bool aborted_due_to_error) {
293 DCHECK_CURRENTLY_ON(BrowserThread::IO);
294 DCHECK(controller);
295 DCHECK(client_handler);
297 DeviceEntry* entry = GetDeviceEntryForController(controller);
298 if (!entry) {
299 NOTREACHED();
300 return;
302 if (aborted_due_to_error) {
303 SessionMap::iterator it;
304 for (it = sessions_.begin(); it != sessions_.end(); ++it) {
305 if (it->second.type == entry->stream_type &&
306 it->second.id == entry->id) {
307 listener_->Aborted(it->second.type, it->first);
308 break;
313 // Detach client from controller.
314 media::VideoCaptureSessionId session_id =
315 controller->RemoveClient(client_id, client_handler);
316 DVLOG(1) << "VideoCaptureManager::StopCaptureForClient, session_id = "
317 << session_id;
319 // If controller has no more clients, delete controller and device.
320 DestroyDeviceEntryIfNoClients(entry);
323 bool VideoCaptureManager::GetDeviceSupportedFormats(
324 media::VideoCaptureSessionId capture_session_id,
325 media::VideoCaptureFormats* supported_formats) {
326 DCHECK_CURRENTLY_ON(BrowserThread::IO);
327 DCHECK(supported_formats->empty());
329 SessionMap::iterator it = sessions_.find(capture_session_id);
330 if (it == sessions_.end())
331 return false;
332 DVLOG(1) << "GetDeviceSupportedFormats for device: " << it->second.name;
334 // Return all available formats of the device, regardless its started state.
335 DeviceInfo* existing_device =
336 FindDeviceInfoById(it->second.id, devices_info_cache_);
337 if (existing_device)
338 *supported_formats = existing_device->supported_formats;
339 return true;
342 bool VideoCaptureManager::GetDeviceFormatsInUse(
343 media::VideoCaptureSessionId capture_session_id,
344 media::VideoCaptureFormats* formats_in_use) {
345 DCHECK_CURRENTLY_ON(BrowserThread::IO);
346 DCHECK(formats_in_use->empty());
348 SessionMap::iterator it = sessions_.find(capture_session_id);
349 if (it == sessions_.end())
350 return false;
351 DVLOG(1) << "GetDeviceFormatsInUse for device: " << it->second.name;
353 // Return the currently in-use format(s) of the device, if it's started.
354 DeviceEntry* device_in_use =
355 GetDeviceEntryForMediaStreamDevice(it->second);
356 if (device_in_use) {
357 // Currently only one format-in-use is supported at the VCC level.
358 formats_in_use->push_back(
359 device_in_use->video_capture_controller->GetVideoCaptureFormat());
361 return true;
364 void VideoCaptureManager::SetDesktopCaptureWindowId(
365 media::VideoCaptureSessionId session_id,
366 gfx::NativeViewId window_id) {
367 DCHECK_CURRENTLY_ON(BrowserThread::IO);
368 SessionMap::iterator session_it = sessions_.find(session_id);
369 if (session_it == sessions_.end()) {
370 device_task_runner_->PostTask(
371 FROM_HERE,
372 base::Bind(
373 &VideoCaptureManager::SaveDesktopCaptureWindowIdOnDeviceThread,
374 this,
375 session_id,
376 window_id));
377 return;
380 DeviceEntry* const existing_device =
381 GetDeviceEntryForMediaStreamDevice(session_it->second);
382 if (!existing_device)
383 return;
385 DCHECK_EQ(MEDIA_DESKTOP_VIDEO_CAPTURE, existing_device->stream_type);
386 DesktopMediaID id = DesktopMediaID::Parse(existing_device->id);
387 if (id.type == DesktopMediaID::TYPE_NONE ||
388 id.type == DesktopMediaID::TYPE_AURA_WINDOW) {
389 return;
392 device_task_runner_->PostTask(
393 FROM_HERE,
394 base::Bind(&VideoCaptureManager::SetDesktopCaptureWindowIdOnDeviceThread,
395 this,
396 existing_device,
397 window_id));
400 void VideoCaptureManager::DoStopDeviceOnDeviceThread(DeviceEntry* entry) {
401 SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StopDeviceTime");
402 DCHECK(IsOnDeviceThread());
403 if (entry->video_capture_device) {
404 entry->video_capture_device->StopAndDeAllocate();
406 entry->video_capture_device.reset();
409 void VideoCaptureManager::OnOpened(
410 MediaStreamType stream_type,
411 media::VideoCaptureSessionId capture_session_id) {
412 DCHECK_CURRENTLY_ON(BrowserThread::IO);
413 if (!listener_) {
414 // Listener has been removed.
415 return;
417 listener_->Opened(stream_type, capture_session_id);
420 void VideoCaptureManager::OnClosed(
421 MediaStreamType stream_type,
422 media::VideoCaptureSessionId capture_session_id) {
423 DCHECK_CURRENTLY_ON(BrowserThread::IO);
424 if (!listener_) {
425 // Listener has been removed.
426 return;
428 listener_->Closed(stream_type, capture_session_id);
431 void VideoCaptureManager::OnDevicesInfoEnumerated(
432 MediaStreamType stream_type,
433 const DeviceInfos& new_devices_info_cache) {
434 DCHECK_CURRENTLY_ON(BrowserThread::IO);
436 if (!listener_) {
437 // Listener has been removed.
438 return;
440 devices_info_cache_ = new_devices_info_cache;
442 // Walk the |devices_info_cache_| and transform from VCD::Name to
443 // StreamDeviceInfo for return purposes.
444 StreamDeviceInfoArray devices;
445 for (DeviceInfos::const_iterator it = devices_info_cache_.begin();
446 it != devices_info_cache_.end(); ++it) {
447 devices.push_back(StreamDeviceInfo(
448 stream_type, it->name.GetNameAndModel(), it->name.id()));
450 listener_->DevicesEnumerated(stream_type, devices);
453 bool VideoCaptureManager::IsOnDeviceThread() const {
454 return device_task_runner_->BelongsToCurrentThread();
457 VideoCaptureManager::DeviceInfos
458 VideoCaptureManager::GetAvailableDevicesInfoOnDeviceThread(
459 MediaStreamType stream_type,
460 const DeviceInfos& old_device_info_cache) {
461 SCOPED_UMA_HISTOGRAM_TIMER(
462 "Media.VideoCaptureManager.GetAvailableDevicesInfoOnDeviceThreadTime");
463 DCHECK(IsOnDeviceThread());
464 media::VideoCaptureDevice::Names names_snapshot;
465 switch (stream_type) {
466 case MEDIA_DEVICE_VIDEO_CAPTURE:
467 // Cache the latest enumeration of video capture devices.
468 // We'll refer to this list again in OnOpen to avoid having to
469 // enumerate the devices again.
470 video_capture_device_factory_->GetDeviceNames(&names_snapshot);
471 break;
472 case MEDIA_DESKTOP_VIDEO_CAPTURE:
473 // Do nothing.
474 break;
475 default:
476 NOTREACHED();
477 break;
480 // Construct |new_devices_info_cache| with the cached devices that are still
481 // present in the system, and remove their names from |names_snapshot|, so we
482 // keep there the truly new devices.
483 DeviceInfos new_devices_info_cache;
484 for (DeviceInfos::const_iterator it_device_info =
485 old_device_info_cache.begin();
486 it_device_info != old_device_info_cache.end(); ++it_device_info) {
487 for (media::VideoCaptureDevice::Names::iterator it =
488 names_snapshot.begin();
489 it != names_snapshot.end(); ++it) {
490 if (it_device_info->name.id() == it->id()) {
491 new_devices_info_cache.push_back(*it_device_info);
492 names_snapshot.erase(it);
493 break;
498 // Get the supported capture formats for the new devices in |names_snapshot|.
499 for (media::VideoCaptureDevice::Names::const_iterator it =
500 names_snapshot.begin();
501 it != names_snapshot.end(); ++it) {
502 media::VideoCaptureFormats supported_formats;
503 DeviceInfo device_info(*it, media::VideoCaptureFormats());
504 video_capture_device_factory_->GetDeviceSupportedFormats(
505 *it, &(device_info.supported_formats));
506 ConsolidateCaptureFormats(&device_info.supported_formats);
507 new_devices_info_cache.push_back(device_info);
509 return new_devices_info_cache;
512 VideoCaptureManager::DeviceEntry*
513 VideoCaptureManager::GetDeviceEntryForMediaStreamDevice(
514 const MediaStreamDevice& device_info) {
515 DCHECK_CURRENTLY_ON(BrowserThread::IO);
517 for (DeviceEntries::iterator it = devices_.begin();
518 it != devices_.end(); ++it) {
519 DeviceEntry* device = *it;
520 if (device_info.type == device->stream_type &&
521 device_info.id == device->id) {
522 return device;
525 return NULL;
528 VideoCaptureManager::DeviceEntry*
529 VideoCaptureManager::GetDeviceEntryForController(
530 const VideoCaptureController* controller) const {
531 // Look up |controller| in |devices_|.
532 for (DeviceEntries::const_iterator it = devices_.begin();
533 it != devices_.end(); ++it) {
534 if ((*it)->video_capture_controller.get() == controller) {
535 return *it;
538 return NULL;
541 void VideoCaptureManager::DestroyDeviceEntryIfNoClients(DeviceEntry* entry) {
542 DCHECK_CURRENTLY_ON(BrowserThread::IO);
543 // Removal of the last client stops the device.
544 if (entry->video_capture_controller->GetClientCount() == 0) {
545 DVLOG(1) << "VideoCaptureManager stopping device (type = "
546 << entry->stream_type << ", id = " << entry->id << ")";
548 // The DeviceEntry is removed from |devices_| immediately. The controller is
549 // deleted immediately, and the device is freed asynchronously. After this
550 // point, subsequent requests to open this same device ID will create a new
551 // DeviceEntry, VideoCaptureController, and VideoCaptureDevice.
552 devices_.erase(entry);
553 entry->video_capture_controller.reset();
554 device_task_runner_->PostTask(
555 FROM_HERE,
556 base::Bind(&VideoCaptureManager::DoStopDeviceOnDeviceThread, this,
557 base::Owned(entry)));
561 VideoCaptureManager::DeviceEntry* VideoCaptureManager::GetOrCreateDeviceEntry(
562 media::VideoCaptureSessionId capture_session_id) {
563 DCHECK_CURRENTLY_ON(BrowserThread::IO);
565 SessionMap::iterator session_it = sessions_.find(capture_session_id);
566 if (session_it == sessions_.end()) {
567 return NULL;
569 const MediaStreamDevice& device_info = session_it->second;
571 // Check if another session has already opened this device. If so, just
572 // use that opened device.
573 DeviceEntry* const existing_device =
574 GetDeviceEntryForMediaStreamDevice(device_info);
575 if (existing_device) {
576 DCHECK_EQ(device_info.type, existing_device->stream_type);
577 return existing_device;
580 scoped_ptr<VideoCaptureController> video_capture_controller(
581 new VideoCaptureController());
582 DeviceEntry* new_device = new DeviceEntry(device_info.type,
583 device_info.id,
584 video_capture_controller.Pass());
585 devices_.insert(new_device);
586 return new_device;
589 VideoCaptureManager::DeviceInfo* VideoCaptureManager::FindDeviceInfoById(
590 const std::string& id,
591 DeviceInfos& device_vector) {
592 for (DeviceInfos::iterator it = device_vector.begin();
593 it != device_vector.end(); ++it) {
594 if (it->name.id() == id)
595 return &(*it);
597 return NULL;
600 void VideoCaptureManager::SetDesktopCaptureWindowIdOnDeviceThread(
601 DeviceEntry* entry,
602 gfx::NativeViewId window_id) {
603 DCHECK(IsOnDeviceThread());
604 DCHECK(entry->stream_type == MEDIA_DESKTOP_VIDEO_CAPTURE);
605 #if defined(ENABLE_SCREEN_CAPTURE)
606 DesktopCaptureDevice* device =
607 static_cast<DesktopCaptureDevice*>(entry->video_capture_device.get());
608 device->SetNotificationWindowId(window_id);
609 #endif
612 void VideoCaptureManager::SaveDesktopCaptureWindowIdOnDeviceThread(
613 media::VideoCaptureSessionId session_id,
614 gfx::NativeViewId window_id) {
615 DCHECK(IsOnDeviceThread());
616 DCHECK(notification_window_ids_.find(session_id) ==
617 notification_window_ids_.end());
618 notification_window_ids_[session_id] = window_id;
621 } // namespace content