Chromium Code Reviews| Index: content/browser/media_stream/video_capture_manager.cc |
| =================================================================== |
| --- content/browser/media_stream/video_capture_manager.cc (revision 0) |
| +++ content/browser/media_stream/video_capture_manager.cc (revision 0) |
| @@ -0,0 +1,442 @@ |
| +// Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "content/browser/media_stream/video_capture_manager.h" |
| + |
| +#include "base/memory/scoped_ptr.h" |
| +#include "content/browser/browser_thread.h" |
| +#include "media/video/capture/fake_video_capture_device.h" |
| +#include "media/video/capture/video_capture_device.h" |
| + |
| +namespace media_stream { |
| + |
| +// Starting id for the first capture session |
| +enum { kFirstSessionId = 2}; |
| + |
| +static ::base::LazyInstance<VideoCaptureManager> g_video_capture_manager( |
| + base::LINKER_INITIALIZED); |
| + |
| +bool VideoCaptureManager::use_fake_device_ = false; |
|
John Knottenbelt
2011/05/11 11:36:35
Question: Can you make use_fake_device_ non-static
mflodman1
2011/05/15 20:24:00
Done.
|
| + |
| +VideoCaptureManager* VideoCaptureManager::Get() { |
| + return g_video_capture_manager.Pointer(); |
| +} |
| + |
| +void VideoCaptureManager::CreateTestManager() { |
|
John Knottenbelt
2011/05/11 11:36:35
Suggestion: Remove this method and make UseFakeDev
mflodman1
2011/05/15 20:24:00
Done.
|
| + VideoCaptureManager* vcm = g_video_capture_manager.Pointer(); |
| + vcm->UseFakeDevice(); |
| +} |
| + |
| +VideoCaptureManager::VideoCaptureManager() |
| + : vc_device_thread_("VideoCaptureManagerThread"), |
| + listener_(NULL), |
| + new_capture_session_id_(kFirstSessionId), |
| + devices_() { |
| + vc_device_thread_.Start(); |
| +} |
| + |
| +VideoCaptureManager::~VideoCaptureManager() { |
| + vc_device_thread_.Stop(); |
| + devices_.clear(); |
|
John Knottenbelt
2011/05/11 11:36:35
devices_ will be cleared by virtue of its destruct
mflodman1
2011/05/15 20:24:00
Done.
|
| +} |
| + |
| +bool VideoCaptureManager::Register(MediaStreamType service_type, |
| + MediaStreamProviderListener* listener) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| + if (service_type != kVideoCapture) { |
|
John Knottenbelt
2011/05/11 11:36:35
Nit (here, and elsewhere): Single line body should
mflodman1
2011/05/15 20:24:00
Earlier code review (for VideoCaptureDeviceLinux)
|
| + // Not a valid service type |
| + return false; |
| + } |
| + if (listener_) { |
|
John Knottenbelt
2011/05/11 11:36:35
Question (here, and elsewhere): Is it a logic erro
mflodman1
2011/05/15 20:24:00
Done.
|
| + // Already registered, unregister first. |
| + return false; |
| + } |
| + listener_ = listener; |
| + return true; |
| +} |
| + |
| +void VideoCaptureManager::Unregister(MediaStreamType service_type, |
| + MediaStreamProviderListener* listener) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| + if (service_type != kVideoCapture) { |
| + return; |
| + } |
| + listener_ = NULL; |
| + return; |
|
John Knottenbelt
2011/05/11 11:36:35
Nit (here and elsewhere): Unnecessary final return
mflodman1
2011/05/15 20:24:00
Done.
|
| +} |
| + |
| +void VideoCaptureManager::EnumerateDevices(MediaStreamType service_type) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| + if (listener_ == NULL) { |
| + return; |
| + } |
| + if (service_type != kVideoCapture) { |
| + PostOnError(-1, kInvalidMediaStreamType); |
|
John Knottenbelt
2011/05/11 11:36:35
Suggestion: Introduce a constant, e.g. kInvalidMed
mflodman1
2011/05/15 20:24:00
servive_type removed, but invalid id added as retu
|
| + return; |
| + } |
| + |
| + vc_device_thread_.message_loop()->PostTask( |
| + FROM_HERE, |
| + NewRunnableMethod(this, |
| + &VideoCaptureManager::OnEnumerateDevices)); |
| + return; |
| +} |
| + |
| +MediaCaptureSessionId VideoCaptureManager::Open( |
| + MediaStreamType service_type, const MediaCaptureDeviceInfo& device) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| + if (listener_ == NULL) { |
| + return -1; |
| + } |
| + if (service_type != kVideoCapture) { |
| + PostOnError(-1, kInvalidMediaStreamType); |
| + return -1; |
| + } |
| + |
| + // Generate a new id for this device |
| + MediaCaptureSessionId video_capture_session_id = new_capture_session_id_++; |
| + |
| + vc_device_thread_.message_loop()->PostTask( |
| + FROM_HERE, |
| + NewRunnableMethod(this, |
| + &VideoCaptureManager::OnOpen, |
| + video_capture_session_id, |
| + device)); |
| + |
| + return video_capture_session_id; |
| +} |
| + |
| +void VideoCaptureManager::Close(MediaStreamType service_type, |
| + MediaCaptureSessionId capture_session_id) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| + if (listener_ == NULL) { |
| + return; |
| + } |
| + if (service_type != kVideoCapture) { |
| + PostOnError(-1, kInvalidMediaStreamType); |
| + return; |
| + } |
| + |
| + vc_device_thread_.message_loop()->PostTask( |
| + FROM_HERE, |
| + NewRunnableMethod(this, |
| + &VideoCaptureManager::OnClose, |
| + capture_session_id)); |
| + return; |
| +} |
| + |
| +void VideoCaptureManager::Start( |
| + const media::VideoCaptureParams& capture_params, |
| + media::VideoCaptureDevice::EventHandler* video_capture_receiver) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| + |
| + vc_device_thread_.message_loop()->PostTask( |
| + FROM_HERE, |
| + NewRunnableMethod(this, |
| + &VideoCaptureManager::OnStart, |
| + capture_params, |
| + video_capture_receiver)); |
| +} |
| + |
| +void VideoCaptureManager::Stop( |
| + const media::VideoCaptureSessionId capture_session_id, Task* stopped_task) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| + |
| + vc_device_thread_.message_loop()->PostTask( |
| + FROM_HERE, |
| + NewRunnableMethod(this, |
| + &VideoCaptureManager::OnStop, |
| + capture_session_id, |
| + stopped_task)); |
| +} |
| + |
| +void VideoCaptureManager::OnEnumerateDevices() { |
| + DCHECK(IsOnCaptureDeviceThread()); |
| + |
| + scoped_ptr<media::VideoCaptureDevice::Names> device_names( |
| + new media::VideoCaptureDevice::Names()); |
| + GetAvailableDevices(device_names.get()); |
| + |
| + MediaCaptureDevices devices; |
| + for (media::VideoCaptureDevice::Names::iterator it = |
| + device_names.get()->begin(); it != device_names.get()->end(); ++it) { |
| + bool opened = DeviceOpened(*it); |
| + devices.push_back(MediaCaptureDeviceInfo(kVideoCapture, it->device_name, |
| + it->unique_id, opened)); |
| + } |
| + |
| + // Let the listener know the result |
| + PostOnDevicesEnumerated(devices); |
| + |
| + // Clean-up |
| + devices.clear(); |
| + device_names.get()->clear(); |
| + |
| + return; |
| +} |
| + |
| +void VideoCaptureManager::OnOpen(MediaCaptureSessionId capture_session_id, |
| + const MediaCaptureDeviceInfo device) { |
| + DCHECK(IsOnCaptureDeviceThread()); |
| + if (devices_.find(capture_session_id) != devices_.end()) { |
|
John Knottenbelt
2011/05/11 11:36:35
Question: Is it appropriate to add more DCHECKs in
mflodman1
2011/05/15 20:24:00
Yes, changed.
|
| + // id already exists! |
| + PostOnError(capture_session_id, kInvalidSession); |
| + return; |
| + } |
| + |
| + // Check if another session has already opened this device, only one user per |
| + // device is supported. |
| + if (DeviceOpened(device) == true) { |
|
John Knottenbelt
2011/05/11 11:36:35
Nit: Remove == true
mflodman1
2011/05/15 20:24:00
Done.
|
| + PostOnError(capture_session_id, kDeviceAlreadyInUse); |
| + return; |
| + } |
| + |
| + // Open the device |
| + media::VideoCaptureDevice::Name vc_device_name; |
| + vc_device_name.device_name = device.name; |
| + vc_device_name.unique_id = device.device_id; |
| + |
| + media::VideoCaptureDevice* video_capture_device = NULL; |
| + if (!use_fake_device_) { |
| + video_capture_device = media::VideoCaptureDevice::Create(vc_device_name); |
| + } else { |
| + video_capture_device = |
| + media::FakeVideoCaptureDevice::Create(vc_device_name); |
| + } |
| + if (video_capture_device == NULL) { |
| + PostOnError(capture_session_id, kDeviceNotAvailable); |
| + return; |
| + } |
| + |
| + devices_[capture_session_id] = video_capture_device; |
| + PostOnOpened(capture_session_id); |
| + return; |
| +} |
| + |
| +void VideoCaptureManager::OnClose( |
| + MediaCaptureSessionId capture_session_id) { |
| + DCHECK(IsOnCaptureDeviceThread()); |
| + |
| + VideoCaptureDevices::iterator it = devices_.find(capture_session_id); |
| + if (it != devices_.end()) { |
| + // Deallocate (if not done already) and delete the device |
| + media::VideoCaptureDevice* video_capture_device = it->second; |
| + video_capture_device->DeAllocate(); |
| + delete video_capture_device; |
| + devices_.erase(it); |
| + } |
| + |
| + PostOnClosed(capture_session_id); |
| + return; |
| +} |
| + |
| +void VideoCaptureManager::OnStart( |
| + const media::VideoCaptureParams capture_params, |
| + media::VideoCaptureDevice::EventHandler* video_capture_receiver) { |
| + DCHECK(IsOnCaptureDeviceThread()); |
| + |
| + // Solution for not using MediaStreamManager |
| + // This session id won't be returned by Open() |
| + if (capture_params.session_id == kStartOpenSessionId) { |
|
John Knottenbelt
2011/05/11 11:36:35
Question: This seems like a convenience feature. I
mflodman1
2011/05/15 20:24:00
It is used to be able to open capture devices in C
|
| + // Start() is called without using Open(), we need to open a device |
| + scoped_ptr<media::VideoCaptureDevice::Names> device_names( |
| + new media::VideoCaptureDevice::Names()); |
| + GetAvailableDevices(device_names.get()); |
| + |
| + MediaCaptureDeviceInfo device(kVideoCapture, |
| + device_names.get()->front().device_name, |
| + device_names.get()->front().unique_id, false); |
| + |
| + // Call OnOpen to open using the first device in the list |
| + OnOpen(capture_params.session_id, device); |
| + } |
| + |
| + VideoCaptureDevices::iterator it = devices_.find(capture_params.session_id); |
| + if (it == devices_.end()) { |
| + // Invalid session id |
| + video_capture_receiver->OnError(); |
| + return; |
| + } |
| + media::VideoCaptureDevice* video_capture_device = it->second; |
| + |
| + // Possible errors are signaled to video_capture_receiver by |
| + // video_capture_device. video_capture_receiver to perform actions. |
| + video_capture_device->Allocate(capture_params.width, capture_params.height, |
| + capture_params.frame_per_second, |
| + video_capture_receiver); |
| + video_capture_device->Start(); |
| + return; |
| +} |
| + |
| +void VideoCaptureManager::OnStop( |
| + const media::VideoCaptureSessionId capture_session_id, |
| + Task* stopped_task) { |
| + DCHECK(IsOnCaptureDeviceThread()); |
| + |
| + VideoCaptureDevices::iterator it = devices_.find(capture_session_id); |
| + if (it != devices_.end()) { |
| + media::VideoCaptureDevice* video_capture_device = it->second; |
| + // Possible errors are signaled to video_capture_receiver by |
| + // video_capture_device. video_capture_receiver to perform actions. |
| + video_capture_device->Stop(); |
| + video_capture_device->DeAllocate(); |
| + } |
| + |
| + if (stopped_task) { |
| + stopped_task->Run(); |
| + delete stopped_task; |
| + } |
| + |
| + if (capture_session_id == kStartOpenSessionId) { |
| + // This device was opened from Start(), not Open(). Close it! |
| + OnClose(capture_session_id); |
| + } |
| + return; |
| +} |
| + |
| +void VideoCaptureManager::OnOpened( |
| + MediaCaptureSessionId capture_session_id) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| + if (listener_ == NULL) { |
| + // Listener has been removed |
| + return; |
| + } |
| + listener_->Opened(kVideoCapture, capture_session_id); |
| + return; |
| +} |
| + |
| +void VideoCaptureManager::OnClosed( |
| + MediaCaptureSessionId capture_session_id) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| + if (listener_ == NULL) { |
| + // Listener has been removed |
| + return; |
| + } |
| + listener_->Closed(kVideoCapture, capture_session_id); |
| + return; |
| +} |
| + |
| +void VideoCaptureManager::OnDevicesEnumerated( |
| + const MediaCaptureDevices& devices) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| + if (listener_ == NULL) { |
| + // Listener has been removed |
| + return; |
| + } |
| + listener_->DevicesEnumerated(kVideoCapture, devices); |
| + return; |
| +} |
| + |
| +void VideoCaptureManager::OnError(MediaCaptureSessionId capture_session_id, |
| + MediaStreamProviderError error) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| + if (listener_ == NULL) { |
| + // Listener has been removed |
| + return; |
| + } |
| + listener_->Error(kVideoCapture, capture_session_id, error); |
| + return; |
| +} |
| + |
| +void VideoCaptureManager::PostOnOpened( |
| + MediaCaptureSessionId capture_session_id) { |
| + DCHECK(IsOnCaptureDeviceThread()); |
| + BrowserThread::PostTask(BrowserThread::IO, |
| + FROM_HERE, |
| + NewRunnableMethod(this, |
| + &VideoCaptureManager::OnOpened, |
| + capture_session_id)); |
| + return; |
| +} |
| + |
| +void VideoCaptureManager::PostOnClosed( |
| + MediaCaptureSessionId capture_session_id) { |
| + DCHECK(IsOnCaptureDeviceThread()); |
| + BrowserThread::PostTask(BrowserThread::IO, |
| + FROM_HERE, |
| + NewRunnableMethod(this, |
| + &VideoCaptureManager::OnClosed, |
| + capture_session_id)); |
| + return; |
| +} |
| + |
| +void VideoCaptureManager::PostOnDevicesEnumerated(MediaCaptureDevices devices) { |
| + DCHECK(IsOnCaptureDeviceThread()); |
| + |
| + BrowserThread::PostTask(BrowserThread::IO, |
| + FROM_HERE, |
| + NewRunnableMethod( |
| + this, |
| + &VideoCaptureManager::OnDevicesEnumerated, |
| + devices)); |
| + return; |
| +} |
| + |
| +void VideoCaptureManager::PostOnError(MediaCaptureSessionId capture_session_id, |
| + MediaStreamProviderError error) { |
| + // Don't check thread here, can be called from both IO thread and device |
| + // thread. |
| + BrowserThread::PostTask(BrowserThread::IO, |
| + FROM_HERE, |
| + NewRunnableMethod(this, |
| + &VideoCaptureManager::OnError, |
| + capture_session_id, |
| + error)); |
|
John Knottenbelt
2011/05/11 11:36:35
Nit: indentation
mflodman1
2011/05/15 20:24:00
Done.
|
| + return; |
| +} |
| + |
| +bool VideoCaptureManager::IsOnCaptureDeviceThread() const { |
| + return MessageLoop::current() == vc_device_thread_.message_loop(); |
| +} |
| + |
| +void VideoCaptureManager::GetAvailableDevices( |
| + media::VideoCaptureDevice::Names* device_names) { |
| + DCHECK(IsOnCaptureDeviceThread()); |
| + |
| + if (!use_fake_device_) { |
| + media::VideoCaptureDevice::GetDeviceNames(device_names); |
| + } else { |
| + media::FakeVideoCaptureDevice::GetDeviceNames(device_names); |
| + } |
| + return; |
| +} |
| + |
| +bool VideoCaptureManager::DeviceOpened( |
| + const media::VideoCaptureDevice::Name& device_name) { |
| + DCHECK(IsOnCaptureDeviceThread()); |
| + |
| + for (VideoCaptureDevices::iterator it = devices_.begin(); |
| + it != devices_.end(); |
| + ++it) { |
| + if (device_name.unique_id.compare(it->second->device_name().unique_id) == |
|
John Knottenbelt
2011/05/11 11:36:35
I think we can use a directy comparison (==) inste
mflodman1
2011/05/15 20:24:00
Done.
|
| + 0) { |
| + // We've found the device! |
| + return true; |
| + } |
| + } |
| + return false; |
| +} |
| + |
| +bool VideoCaptureManager::DeviceOpened( |
|
John Knottenbelt
2011/05/11 11:36:35
Add DCHECK to ensure/document what thread this met
mflodman1
2011/05/15 20:24:00
Done.
|
| + const MediaCaptureDeviceInfo& device_info) { |
| + for (VideoCaptureDevices::iterator it = devices_.begin(); |
| + it != devices_.end(); |
| + it++) { |
| + if (device_info.device_id.compare(it->second->device_name().unique_id) == |
| + 0) { |
| + return true; |
| + } |
| + } |
| + return false; |
| +} |
| + |
| +void VideoCaptureManager::UseFakeDevice() { |
| + use_fake_device_ = true; |
| +} |
| + |
| +MessageLoop* VideoCaptureManager::GetMessageLoop() { |
| + return vc_device_thread_.message_loop(); |
| +} |
| + |
| +} // namespace media |