Ignore non-active fullscreen windows for shelf state.
[chromium-blink-merge.git] / content / browser / renderer_host / media / video_capture_manager.cc
blobff71016dc1e09567774fedc2a34c2b4c5d4b0bc8
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/message_loop/message_loop.h"
13 #include "base/stl_util.h"
14 #include "base/threading/sequenced_worker_pool.h"
15 #include "content/browser/renderer_host/media/video_capture_controller.h"
16 #include "content/browser/renderer_host/media/video_capture_controller_event_handler.h"
17 #include "content/browser/renderer_host/media/web_contents_video_capture_device.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "content/public/common/content_switches.h"
20 #include "content/public/common/desktop_media_id.h"
21 #include "content/public/common/media_stream_request.h"
22 #include "media/base/media_switches.h"
23 #include "media/base/scoped_histogram_timer.h"
24 #include "media/video/capture/fake_video_capture_device.h"
25 #include "media/video/capture/file_video_capture_device.h"
26 #include "media/video/capture/video_capture_device.h"
28 #if defined(ENABLE_SCREEN_CAPTURE)
29 #include "content/browser/renderer_host/media/desktop_capture_device.h"
30 #if defined(OS_CHROMEOS)
31 #include "content/browser/renderer_host/media/desktop_capture_device_aura.h"
32 #endif
33 #endif
35 namespace content {
37 VideoCaptureManager::DeviceEntry::DeviceEntry(
38 MediaStreamType stream_type,
39 const std::string& id,
40 scoped_ptr<VideoCaptureController> controller)
41 : stream_type(stream_type),
42 id(id),
43 video_capture_controller(controller.Pass()) {}
45 VideoCaptureManager::DeviceEntry::~DeviceEntry() {}
47 VideoCaptureManager::VideoCaptureManager()
48 : listener_(NULL),
49 new_capture_session_id_(1),
50 artificial_device_source_for_testing_ (DISABLED) {
53 VideoCaptureManager::~VideoCaptureManager() {
54 DCHECK(devices_.empty());
57 void VideoCaptureManager::Register(MediaStreamProviderListener* listener,
58 base::MessageLoopProxy* device_thread_loop) {
59 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
60 DCHECK(!listener_);
61 DCHECK(!device_loop_.get());
62 listener_ = listener;
63 device_loop_ = device_thread_loop;
66 void VideoCaptureManager::Unregister() {
67 DCHECK(listener_);
68 listener_ = NULL;
71 void VideoCaptureManager::EnumerateDevices(MediaStreamType stream_type) {
72 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
73 DVLOG(1) << "VideoCaptureManager::EnumerateDevices, type " << stream_type;
74 DCHECK(listener_);
75 base::PostTaskAndReplyWithResult(
76 device_loop_, FROM_HERE,
77 base::Bind(&VideoCaptureManager::GetAvailableDevicesOnDeviceThread, this,
78 stream_type),
79 base::Bind(&VideoCaptureManager::OnDevicesEnumerated, this, stream_type));
82 int VideoCaptureManager::Open(const StreamDeviceInfo& device_info) {
83 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
84 DCHECK(listener_);
86 // Generate a new id for the session being opened.
87 const int capture_session_id = new_capture_session_id_++;
89 DCHECK(sessions_.find(capture_session_id) == sessions_.end());
90 DVLOG(1) << "VideoCaptureManager::Open, id " << capture_session_id;
92 // We just save the stream info for processing later.
93 sessions_[capture_session_id] = device_info.device;
95 // Notify our listener asynchronously; this ensures that we return
96 // |capture_session_id| to the caller of this function before using that same
97 // id in a listener event.
98 base::MessageLoop::current()->PostTask(FROM_HERE,
99 base::Bind(&VideoCaptureManager::OnOpened, this,
100 device_info.device.type, capture_session_id));
101 return capture_session_id;
104 void VideoCaptureManager::Close(int capture_session_id) {
105 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
106 DCHECK(listener_);
107 DVLOG(1) << "VideoCaptureManager::Close, id " << capture_session_id;
109 std::map<int, MediaStreamDevice>::iterator session_it =
110 sessions_.find(capture_session_id);
111 if (session_it == sessions_.end()) {
112 NOTREACHED();
113 return;
116 DeviceEntry* const existing_device = GetDeviceEntryForMediaStreamDevice(
117 session_it->second);
118 if (existing_device) {
119 // Remove any client that is still using the session. This is safe to call
120 // even if there are no clients using the session.
121 existing_device->video_capture_controller->StopSession(capture_session_id);
123 // StopSession() may have removed the last client, so we might need to
124 // close the device.
125 DestroyDeviceEntryIfNoClients(existing_device);
128 // Notify listeners asynchronously, and forget the session.
129 base::MessageLoop::current()->PostTask(FROM_HERE,
130 base::Bind(&VideoCaptureManager::OnClosed, this, session_it->second.type,
131 capture_session_id));
132 sessions_.erase(session_it);
135 void VideoCaptureManager::UseFakeDevice() {
136 if (CommandLine::ForCurrentProcess()->HasSwitch(
137 switches::kUseFileForFakeVideoCapture)) {
138 artificial_device_source_for_testing_ = Y4M_FILE;
139 } else {
140 artificial_device_source_for_testing_ = TEST_PATTERN;
144 void VideoCaptureManager::DoStartDeviceOnDeviceThread(
145 DeviceEntry* entry,
146 const media::VideoCaptureParams& params,
147 scoped_ptr<media::VideoCaptureDevice::Client> device_client) {
148 SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StartDeviceTime");
149 DCHECK(IsOnDeviceThread());
151 scoped_ptr<media::VideoCaptureDevice> video_capture_device;
152 switch (entry->stream_type) {
153 case MEDIA_DEVICE_VIDEO_CAPTURE: {
154 // We look up the device id from the renderer in our local enumeration
155 // since the renderer does not have all the information that might be
156 // held in the browser-side VideoCaptureDevice::Name structure.
157 media::VideoCaptureDevice::Name* found =
158 video_capture_devices_.FindById(entry->id);
159 if (found) {
160 switch (artificial_device_source_for_testing_) {
161 case DISABLED:
162 video_capture_device.reset(
163 media::VideoCaptureDevice::Create(*found));
164 break;
165 case TEST_PATTERN:
166 video_capture_device.reset(
167 media::FakeVideoCaptureDevice::Create(*found));
168 break;
169 case Y4M_FILE:
170 video_capture_device.reset(
171 media::FileVideoCaptureDevice::Create(*found));
172 break;
175 break;
177 case MEDIA_TAB_VIDEO_CAPTURE: {
178 video_capture_device.reset(
179 WebContentsVideoCaptureDevice::Create(entry->id));
180 break;
182 case MEDIA_DESKTOP_VIDEO_CAPTURE: {
183 #if defined(ENABLE_SCREEN_CAPTURE)
184 DesktopMediaID id = DesktopMediaID::Parse(entry->id);
185 if (id.type != DesktopMediaID::TYPE_NONE) {
186 #if defined(OS_CHROMEOS)
187 // TODO(hshi): enable this path for Ash windows in metro mode.
188 video_capture_device.reset(DesktopCaptureDeviceAura::Create(id));
189 #else
190 video_capture_device = DesktopCaptureDevice::Create(id);
191 #endif
193 #endif // defined(ENABLE_SCREEN_CAPTURE)
194 break;
196 default: {
197 NOTIMPLEMENTED();
198 break;
202 if (!video_capture_device) {
203 device_client->OnError();
204 return;
207 video_capture_device->AllocateAndStart(params, device_client.Pass());
208 entry->video_capture_device = video_capture_device.Pass();
211 void VideoCaptureManager::StartCaptureForClient(
212 media::VideoCaptureSessionId session_id,
213 const media::VideoCaptureParams& params,
214 base::ProcessHandle client_render_process,
215 VideoCaptureControllerID client_id,
216 VideoCaptureControllerEventHandler* client_handler,
217 const DoneCB& done_cb) {
218 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
219 DVLOG(1) << "VideoCaptureManager::StartCaptureForClient, "
220 << params.requested_format.frame_size.ToString() << ", "
221 << params.requested_format.frame_rate << ", #" << session_id << ")";
223 DeviceEntry* entry = GetOrCreateDeviceEntry(session_id);
224 if (!entry) {
225 done_cb.Run(base::WeakPtr<VideoCaptureController>());
226 return;
229 DCHECK(entry->video_capture_controller);
231 // First client starts the device.
232 if (entry->video_capture_controller->GetClientCount() == 0) {
233 DVLOG(1) << "VideoCaptureManager starting device (type = "
234 << entry->stream_type << ", id = " << entry->id << ")";
236 device_loop_->PostTask(
237 FROM_HERE,
238 base::Bind(
239 &VideoCaptureManager::DoStartDeviceOnDeviceThread,
240 this,
241 entry,
242 params,
243 base::Passed(entry->video_capture_controller->NewDeviceClient())));
245 // Run the callback first, as AddClient() may trigger OnFrameInfo().
246 done_cb.Run(entry->video_capture_controller->GetWeakPtr());
247 entry->video_capture_controller->AddClient(
248 client_id, client_handler, client_render_process, session_id, params);
251 void VideoCaptureManager::StopCaptureForClient(
252 VideoCaptureController* controller,
253 VideoCaptureControllerID client_id,
254 VideoCaptureControllerEventHandler* client_handler) {
255 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
256 DCHECK(controller);
257 DCHECK(client_handler);
259 DeviceEntry* entry = GetDeviceEntryForController(controller);
260 if (!entry) {
261 NOTREACHED();
262 return;
265 // Detach client from controller.
266 int session_id = controller->RemoveClient(client_id, client_handler);
267 DVLOG(1) << "VideoCaptureManager::StopCaptureForClient, session_id = "
268 << session_id;
270 // If controller has no more clients, delete controller and device.
271 DestroyDeviceEntryIfNoClients(entry);
274 void VideoCaptureManager::DoStopDeviceOnDeviceThread(DeviceEntry* entry) {
275 SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StopDeviceTime");
276 DCHECK(IsOnDeviceThread());
277 if (entry->video_capture_device) {
278 entry->video_capture_device->StopAndDeAllocate();
280 entry->video_capture_device.reset();
283 void VideoCaptureManager::OnOpened(MediaStreamType stream_type,
284 int capture_session_id) {
285 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
286 if (!listener_) {
287 // Listener has been removed.
288 return;
290 listener_->Opened(stream_type, capture_session_id);
293 void VideoCaptureManager::OnClosed(MediaStreamType stream_type,
294 int capture_session_id) {
295 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
296 if (!listener_) {
297 // Listener has been removed.
298 return;
300 listener_->Closed(stream_type, capture_session_id);
303 void VideoCaptureManager::OnDevicesEnumerated(
304 MediaStreamType stream_type,
305 const media::VideoCaptureDevice::Names& device_names) {
306 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
308 if (!listener_) {
309 // Listener has been removed.
310 return;
313 // Transform from VCD::Name to StreamDeviceInfo.
314 StreamDeviceInfoArray devices;
315 for (media::VideoCaptureDevice::Names::const_iterator it =
316 device_names.begin(); it != device_names.end(); ++it) {
317 devices.push_back(StreamDeviceInfo(
318 stream_type, it->GetNameAndModel(), it->id()));
321 listener_->DevicesEnumerated(stream_type, devices);
324 bool VideoCaptureManager::IsOnDeviceThread() const {
325 return device_loop_->BelongsToCurrentThread();
328 media::VideoCaptureDevice::Names
329 VideoCaptureManager::GetAvailableDevicesOnDeviceThread(
330 MediaStreamType stream_type) {
331 SCOPED_UMA_HISTOGRAM_TIMER(
332 "Media.VideoCaptureManager.GetAvailableDevicesTime");
333 DCHECK(IsOnDeviceThread());
334 media::VideoCaptureDevice::Names result;
336 switch (stream_type) {
337 case MEDIA_DEVICE_VIDEO_CAPTURE:
338 // Cache the latest enumeration of video capture devices.
339 // We'll refer to this list again in OnOpen to avoid having to
340 // enumerate the devices again.
341 switch (artificial_device_source_for_testing_) {
342 case DISABLED:
343 media::VideoCaptureDevice::GetDeviceNames(&result);
344 break;
345 case TEST_PATTERN:
346 media::FakeVideoCaptureDevice::GetDeviceNames(&result);
347 break;
348 case Y4M_FILE:
349 media::FileVideoCaptureDevice::GetDeviceNames(&result);
350 break;
353 // TODO(nick): The correctness of device start depends on this cache being
354 // maintained, but it seems a little odd to keep a cache here. Can we
355 // eliminate it?
356 video_capture_devices_ = result;
357 break;
359 case MEDIA_DESKTOP_VIDEO_CAPTURE:
360 // Do nothing.
361 break;
363 default:
364 NOTREACHED();
365 break;
367 return result;
370 VideoCaptureManager::DeviceEntry*
371 VideoCaptureManager::GetDeviceEntryForMediaStreamDevice(
372 const MediaStreamDevice& device_info) {
373 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
375 for (DeviceEntries::iterator it = devices_.begin();
376 it != devices_.end(); ++it) {
377 DeviceEntry* device = *it;
378 if (device_info.type == device->stream_type &&
379 device_info.id == device->id) {
380 return device;
383 return NULL;
386 VideoCaptureManager::DeviceEntry*
387 VideoCaptureManager::GetDeviceEntryForController(
388 const VideoCaptureController* controller) {
389 // Look up |controller| in |devices_|.
390 for (DeviceEntries::iterator it = devices_.begin();
391 it != devices_.end(); ++it) {
392 if ((*it)->video_capture_controller.get() == controller) {
393 return *it;
396 return NULL;
399 void VideoCaptureManager::DestroyDeviceEntryIfNoClients(DeviceEntry* entry) {
400 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
401 // Removal of the last client stops the device.
402 if (entry->video_capture_controller->GetClientCount() == 0) {
403 DVLOG(1) << "VideoCaptureManager stopping device (type = "
404 << entry->stream_type << ", id = " << entry->id << ")";
406 // The DeviceEntry is removed from |devices_| immediately. The controller is
407 // deleted immediately, and the device is freed asynchronously. After this
408 // point, subsequent requests to open this same device ID will create a new
409 // DeviceEntry, VideoCaptureController, and VideoCaptureDevice.
410 devices_.erase(entry);
411 entry->video_capture_controller.reset();
412 device_loop_->PostTask(
413 FROM_HERE,
414 base::Bind(&VideoCaptureManager::DoStopDeviceOnDeviceThread, this,
415 base::Owned(entry)));
419 VideoCaptureManager::DeviceEntry* VideoCaptureManager::GetOrCreateDeviceEntry(
420 int capture_session_id) {
421 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
423 std::map<int, MediaStreamDevice>::iterator session_it =
424 sessions_.find(capture_session_id);
425 if (session_it == sessions_.end()) {
426 return NULL;
428 const MediaStreamDevice& device_info = session_it->second;
430 // Check if another session has already opened this device. If so, just
431 // use that opened device.
432 DeviceEntry* const existing_device =
433 GetDeviceEntryForMediaStreamDevice(device_info);
434 if (existing_device) {
435 DCHECK_EQ(device_info.type, existing_device->stream_type);
436 return existing_device;
439 scoped_ptr<VideoCaptureController> video_capture_controller(
440 new VideoCaptureController());
441 DeviceEntry* new_device = new DeviceEntry(device_info.type,
442 device_info.id,
443 video_capture_controller.Pass());
444 devices_.insert(new_device);
445 return new_device;
448 } // namespace content