Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / content / browser / renderer_host / media / video_capture_manager.cc
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/renderer_host/media/video_capture_controller.h"
17 #include "content/browser/renderer_host/media/video_capture_controller_event_handler.h"
18 #include "content/browser/renderer_host/media/web_contents_video_capture_device.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/renderer_host/media/desktop_capture_device.h"
31 #if defined(USE_AURA)
32 #include "content/browser/renderer_host/media/desktop_capture_device_aura.h"
33 #endif
34 #endif
35
36 namespace content {
37
38 VideoCaptureManager::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
46 VideoCaptureManager::DeviceEntry::~DeviceEntry() {}
47
48 VideoCaptureManager::DeviceInfo::DeviceInfo() {}
49
50 VideoCaptureManager::DeviceInfo::DeviceInfo(
51     const media::VideoCaptureDevice::Name& name,
52     const media::VideoCaptureFormats& supported_formats)
53     : name(name),
54       supported_formats(supported_formats) {}
55
56 VideoCaptureManager::DeviceInfo::~DeviceInfo() {}
57
58 VideoCaptureManager::VideoCaptureManager()
59     : listener_(NULL),
60       new_capture_session_id_(1),
61       artificial_device_source_for_testing_(DISABLED) {
62 }
63
64 VideoCaptureManager::~VideoCaptureManager() {
65   DCHECK(devices_.empty());
66 }
67
68 void 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
78 void VideoCaptureManager::Unregister() {
79   DCHECK(listener_);
80   listener_ = NULL;
81 }
82
83 void 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
95 int 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
118 void 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
149 void 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
158 void 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
225 void 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
265 void 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
289 bool 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
309 bool 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
332 void 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
341 void 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
352 void 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
363 void 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
385 bool VideoCaptureManager::IsOnDeviceThread() const {
386   return device_task_runner_->BelongsToCurrentThread();
387 }
388
389 VideoCaptureManager::DeviceInfos
390 VideoCaptureManager::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
467 VideoCaptureManager::DeviceEntry*
468 VideoCaptureManager::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
483 VideoCaptureManager::DeviceEntry*
484 VideoCaptureManager::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
496 void 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
516 VideoCaptureManager::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
545 VideoCaptureManager::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