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