diff options
author | wjia@chromium.org <wjia@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-03 20:14:27 +0000 |
---|---|---|
committer | wjia@chromium.org <wjia@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-03 20:14:27 +0000 |
commit | 08ce8669f49a6414f358873b5ae6c156572204ce (patch) | |
tree | 4f7894e35872067a525c3581f77f255f6a742760 /content | |
parent | 6e4dcdbbbc88336cb4807d8ab6c03232c0137010 (diff) | |
download | chromium_src-08ce8669f49a6414f358873b5ae6c156572204ce.zip chromium_src-08ce8669f49a6414f358873b5ae6c156572204ce.tar.gz chromium_src-08ce8669f49a6414f358873b5ae6c156572204ce.tar.bz2 |
refactor video capture in browser process to support device sharing across multiple renderer processes
This is a split of http://codereview.chromium.org/8304017/
1. VideoCaptureController is owned by VideoCaptureManager.
2. VideoCaptureHost obtains an instance of controller from manager.
3. The largest resolution is used for the device capturing. Clients should expect frame buffers with larger resolution that they requsted. All cropping/resampling is done by clients.
4. Frame buffers are shared between renderer processes and browser process. The buffer received by clients are shared buffers. Clients are recommended to copy pixel data into their local storage. A client can slow down capture frame rate if it returns buffers slowly.
5. This patch only supported the default device. More change in media stream is needed to support user selected device. That will be a different patch.
BUG=none
TEST=trybots
Review URL: http://codereview.chromium.org/8417046
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@108533 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content')
9 files changed, 729 insertions, 176 deletions
diff --git a/content/browser/renderer_host/media/video_capture_controller.cc b/content/browser/renderer_host/media/video_capture_controller.cc index 266508c..2bfd1ab 100644 --- a/content/browser/renderer_host/media/video_capture_controller.cc +++ b/content/browser/renderer_host/media/video_capture_controller.cc @@ -13,56 +13,230 @@ using content::BrowserThread; -// The number of TransportDIBs VideoCaptureController allocate. +// The number of DIBs VideoCaptureController allocate. static const size_t kNoOfDIBS = 3; +struct VideoCaptureController::ControllerClient { + ControllerClient( + const VideoCaptureControllerID& id, + VideoCaptureControllerEventHandler* handler, + base::ProcessHandle render_process, + const media::VideoCaptureParams& params) + : controller_id(id), + event_handler(handler), + render_process_handle(render_process), + parameters(params), + report_ready_to_delete(false) { + } + + ~ControllerClient() {} + + // ID used for identifying this object. + VideoCaptureControllerID controller_id; + VideoCaptureControllerEventHandler* event_handler; + + // Handle to the render process that will receive the DIBs. + base::ProcessHandle render_process_handle; + media::VideoCaptureParams parameters; + + // Buffers used by this client. + std::list<int> buffers; + + // Record client's status when it has called StopCapture, but haven't + // returned all buffers. + bool report_ready_to_delete; +}; + +struct VideoCaptureController::SharedDIB { + SharedDIB(base::SharedMemory* ptr) + : shared_memory(ptr), + references(0) { + } + + ~SharedDIB() {} + + // The memory created to be shared with renderer processes. + scoped_ptr<base::SharedMemory> shared_memory; + + // Number of renderer processes which hold this shared memory. + // renderer process is represented by VidoeCaptureHost. + int references; +}; + VideoCaptureController::VideoCaptureController( - const VideoCaptureControllerID& id, - base::ProcessHandle render_process, - VideoCaptureControllerEventHandler* event_handler, media_stream::VideoCaptureManager* video_capture_manager) - : render_handle_(render_process), - report_ready_to_delete_(false), - event_handler_(event_handler), - id_(id), - video_capture_manager_(video_capture_manager) { - memset(¶ms_, 0, sizeof(params_)); + : frame_info_available_(false), + video_capture_manager_(video_capture_manager), + device_in_use_(false), + state_(media::VideoCapture::kStopped) { + memset(¤t_params_, 0, sizeof(current_params_)); } VideoCaptureController::~VideoCaptureController() { - // Delete all TransportDIBs. + // Delete all DIBs. STLDeleteContainerPairSecondPointers(owned_dibs_.begin(), owned_dibs_.end()); + STLDeleteContainerPointers(controller_clients_.begin(), + controller_clients_.end()); + STLDeleteContainerPointers(pending_clients_.begin(), + pending_clients_.end()); } void VideoCaptureController::StartCapture( + const VideoCaptureControllerID& id, + VideoCaptureControllerEventHandler* event_handler, + base::ProcessHandle render_process, const media::VideoCaptureParams& params) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + // Signal error in case device is already in error state. + if (state_ == media::VideoCapture::kError) { + event_handler->OnError(id); + return; + } + + // Do nothing if this client has called StartCapture before. + if (FindClient(id, event_handler, controller_clients_) || + FindClient(id, event_handler, pending_clients_)) + return; - params_ = params; + ControllerClient* client = new ControllerClient(id, event_handler, + render_process, params); + // In case capture has been started, need to check different condtions. + if (state_ == media::VideoCapture::kStarted) { + // This client has higher resolution than what is currently requested. + // Need restart capturing. + if (params.width > current_params_.width || + params.height > current_params_.height) { + video_capture_manager_->Stop(current_params_.session_id, + base::Bind(&VideoCaptureController::OnDeviceStopped, this)); + frame_info_available_ = false; + state_ = media::VideoCapture::kStopping; + pending_clients_.push_back(client); + return; + } + + // This client's resolution is no larger than what's currently requested. + // When frame_info has been returned by device, send them to client. + if (frame_info_available_) { + int buffer_size = (frame_info_.width * frame_info_.height * 3) / 2; + SendFrameInfoAndBuffers(client, buffer_size); + } + controller_clients_.push_back(client); + return; + } + + // In case the device is in the middle of stopping, put the client in + // pending queue. + if (state_ == media::VideoCapture::kStopping) { + pending_clients_.push_back(client); + return; + } + + // Fresh start. + controller_clients_.push_back(client); + current_params_ = params; // Order the manager to start the actual capture. video_capture_manager_->Start(params, this); + state_ = media::VideoCapture::kStarted; } -void VideoCaptureController::StopCapture(base::Closure stopped_cb) { +void VideoCaptureController::StopCapture( + const VideoCaptureControllerID& id, + VideoCaptureControllerEventHandler* event_handler, + bool force_buffer_return) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - video_capture_manager_->Stop(params_.session_id, - base::Bind(&VideoCaptureController::OnDeviceStopped, this, stopped_cb)); + ControllerClient* client = FindClient(id, event_handler, pending_clients_); + // If the client is still in pending queue, just remove it. + if (client) { + client->event_handler->OnReadyToDelete(client->controller_id); + pending_clients_.remove(client); + return; + } + + client = FindClient(id, event_handler, controller_clients_); + DCHECK(client) << "Client should have called StartCapture()"; + + if (force_buffer_return) { + // The client requests to return buffers which means it can't return + // buffers normally. After buffers are returned, client is free to + // delete itself. No need to call OnReadyToDelete. + + // Return all buffers held by the clients. + for (std::list<int>::iterator buffer_it = client->buffers.begin(); + buffer_it != client->buffers.end(); ++buffer_it) { + int buffer_id = *buffer_it; + DIBMap::iterator dib_it = owned_dibs_.find(buffer_id); + if (dib_it == owned_dibs_.end()) + continue; + + { + base::AutoLock lock(lock_); + DCHECK_GT(dib_it->second->references, 0) + << "The buffer is not used by renderer."; + dib_it->second->references -= 1; + } + } + client->buffers.clear(); + } else { + // Normal way to stop capture. + if (!client->buffers.empty()) { + // There are still some buffers held by the client. + client->report_ready_to_delete = true; + return; + } + // No buffer is held by the client. Ready to delete. + client->event_handler->OnReadyToDelete(client->controller_id); + } + + delete client; + controller_clients_.remove(client); + + // No more clients. Stop device. + if (controller_clients_.empty()) { + video_capture_manager_->Stop(current_params_.session_id, + base::Bind(&VideoCaptureController::OnDeviceStopped, this)); + frame_info_available_ = false; + state_ = media::VideoCapture::kStopping; + } } -void VideoCaptureController::ReturnBuffer(int buffer_id) { +void VideoCaptureController::ReturnBuffer( + const VideoCaptureControllerID& id, + VideoCaptureControllerEventHandler* event_handler, + int buffer_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - bool ready_to_delete; + ControllerClient* client = FindClient(id, event_handler, + controller_clients_); + DIBMap::iterator dib_it = owned_dibs_.find(buffer_id); + // If this buffer is not held by this client, or this client doesn't exist + // in controller, do nothing. + if (!client || dib_it == owned_dibs_.end()) + return; + + client->buffers.remove(buffer_id); + // If this client has called StopCapture and doesn't hold any buffer after + // after this return, ready to delete this client. + if (client->report_ready_to_delete && client->buffers.empty()) { + client->event_handler->OnReadyToDelete(client->controller_id); + delete client; + controller_clients_.remove(client); + } { base::AutoLock lock(lock_); - free_dibs_.push_back(buffer_id); - ready_to_delete = (free_dibs_.size() == owned_dibs_.size()) && - report_ready_to_delete_; + DCHECK_GT(dib_it->second->references, 0) + << "The buffer is not used by renderer."; + dib_it->second->references -= 1; + if (dib_it->second->references > 0) + return; } - if (ready_to_delete) { - event_handler_->OnReadyToDelete(id_); + + // When all buffers have been returned by clients and device has been + // called to stop, check if restart is needed. This could happen when + // some clients call StopCapture before returning all buffers. + if (!ClientHasDIB() && state_ == media::VideoCapture::kStopping) { + PostStopping(); } } @@ -75,22 +249,22 @@ void VideoCaptureController::OnIncomingCapturedFrame(const uint8* data, base::Time timestamp) { int buffer_id = 0; base::SharedMemory* dib = NULL; - // Check if there is a TransportDIB to fill. - bool buffer_exist = false; { base::AutoLock lock(lock_); - if (!report_ready_to_delete_ && free_dibs_.size() > 0) { - buffer_id = free_dibs_.front(); - free_dibs_.pop_front(); - DIBMap::iterator it = owned_dibs_.find(buffer_id); - if (it != owned_dibs_.end()) { - dib = it->second; - buffer_exist = true; + for (DIBMap::iterator dib_it = owned_dibs_.begin(); + dib_it != owned_dibs_.end(); dib_it++) { + if (dib_it->second->references == 0) { + buffer_id = dib_it->first; + // Use special value "-1" in order to not be treated as buffer at + // renderer side. + dib_it->second->references = -1; + dib = dib_it->second->shared_memory.get(); + break; } } } - if (!buffer_exist) { + if (!dib) { return; } @@ -98,6 +272,9 @@ void VideoCaptureController::OnIncomingCapturedFrame(const uint8* data, CHECK(dib->created_size() >= static_cast<size_t> (frame_info_.width * frame_info_.height * 3) / 2); + uint8* yplane = target; + uint8* uplane = target + frame_info_.width * frame_info_.height; + uint8* vplane = uplane + (frame_info_.width * frame_info_.height) / 4; // Do color conversion from the camera format to I420. switch (frame_info_.color) { @@ -108,29 +285,18 @@ void VideoCaptureController::OnIncomingCapturedFrame(const uint8* data, break; } case media::VideoCaptureDevice::kYUY2: { - uint8* yplane = target; - uint8* uplane = target + frame_info_.width * frame_info_.height; - uint8* vplane = uplane + (frame_info_.width * frame_info_.height) / 4; media::ConvertYUY2ToYUV(data, yplane, uplane, vplane, frame_info_.width, frame_info_.height); break; } case media::VideoCaptureDevice::kRGB24: { -#if defined(OS_WIN) // RGB on Windows start at the bottom line. - uint8* yplane = target; - uint8* uplane = target + frame_info_.width * frame_info_.height; - uint8* vplane = uplane + (frame_info_.width * frame_info_.height) / 4; int ystride = frame_info_.width; int uvstride = frame_info_.width / 2; - int rgb_stride = - 3 * frame_info_.width; +#if defined(OS_WIN) // RGB on Windows start at the bottom line. + int rgb_stride = -3 * frame_info_.width; const uint8* rgb_src = data + 3 * frame_info_.width * (frame_info_.height -1); #else - uint8* yplane = target; - uint8* uplane = target + frame_info_.width * frame_info_.height; - uint8* vplane = uplane + (frame_info_.width * frame_info_.height) / 4; - int ystride = frame_info_.width; - int uvstride = frame_info_.width / 2; int rgb_stride = 3 * frame_info_.width; const uint8* rgb_src = data; #endif @@ -140,9 +306,6 @@ void VideoCaptureController::OnIncomingCapturedFrame(const uint8* data, break; } case media::VideoCaptureDevice::kARGB: { - uint8* yplane = target; - uint8* uplane = target + frame_info_.width * frame_info_.height; - uint8* vplane = uplane + (frame_info_.width * frame_info_.height) / 4; media::ConvertRGB32ToYUV(data, yplane, uplane, vplane, frame_info_.width, frame_info_.height, frame_info_.width * 4, frame_info_.width, frame_info_.width / 2); @@ -152,62 +315,209 @@ void VideoCaptureController::OnIncomingCapturedFrame(const uint8* data, NOTREACHED(); } - event_handler_->OnBufferReady(id_, buffer_id, timestamp); + BrowserThread::PostTask(BrowserThread::IO, + FROM_HERE, + base::Bind(&VideoCaptureController::DoIncomingCapturedFrameOnIOThread, + this, buffer_id, timestamp)); } void VideoCaptureController::OnError() { - event_handler_->OnError(id_); - video_capture_manager_->Error(params_.session_id); + video_capture_manager_->Error(current_params_.session_id); + BrowserThread::PostTask(BrowserThread::IO, + FROM_HERE, + base::Bind(&VideoCaptureController::DoErrorOnIOThread, this)); } void VideoCaptureController::OnFrameInfo( const media::VideoCaptureDevice::Capability& info) { - DCHECK(owned_dibs_.empty()); - bool frames_created = true; - const size_t needed_size = (info.width * info.height * 3) / 2; - for (size_t i = 1; i <= kNoOfDIBS; ++i) { - base::SharedMemory* shared_memory = new base::SharedMemory(); - if (!shared_memory->CreateAndMapAnonymous(needed_size)) { - frames_created = false; - break; + BrowserThread::PostTask(BrowserThread::IO, + FROM_HERE, + base::Bind(&VideoCaptureController::DoFrameInfoOnIOThread, + this, info)); +} + +void VideoCaptureController::DoIncomingCapturedFrameOnIOThread( + int buffer_id, base::Time timestamp) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + int count = 0; + if (state_ == media::VideoCapture::kStarted) { + for (ControllerClients::iterator client_it = controller_clients_.begin(); + client_it != controller_clients_.end(); client_it++) { + if ((*client_it)->report_ready_to_delete) + continue; + + (*client_it)->event_handler->OnBufferReady((*client_it)->controller_id, + buffer_id, timestamp); + (*client_it)->buffers.push_back(buffer_id); + count++; } - base::SharedMemoryHandle remote_handle; - shared_memory->ShareToProcess(render_handle_, &remote_handle); + } + base::AutoLock lock(lock_); + DCHECK_EQ(owned_dibs_[buffer_id]->references, -1); + owned_dibs_[buffer_id]->references = count; +} + +void VideoCaptureController::DoErrorOnIOThread() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + state_ = media::VideoCapture::kError; + ControllerClients::iterator client_it; + for (client_it = controller_clients_.begin(); + client_it != controller_clients_.end(); client_it++) { + (*client_it)->event_handler->OnError((*client_it)->controller_id); + } + for (client_it = pending_clients_.begin(); + client_it != pending_clients_.end(); client_it++) { + (*client_it)->event_handler->OnError((*client_it)->controller_id); + } +} + +void VideoCaptureController::DoFrameInfoOnIOThread( + const media::VideoCaptureDevice::Capability info) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(owned_dibs_.empty()) + << "Device is restarted without releasing shared memory."; + + bool frames_created = true; + const size_t needed_size = (info.width * info.height * 3) / 2; + { base::AutoLock lock(lock_); - owned_dibs_.insert(std::make_pair(i, shared_memory)); - free_dibs_.push_back(i); - event_handler_->OnBufferCreated(id_, remote_handle, - static_cast<int>(needed_size), - static_cast<int>(i)); + for (size_t i = 1; i <= kNoOfDIBS; ++i) { + base::SharedMemory* shared_memory = new base::SharedMemory(); + if (!shared_memory->CreateAndMapAnonymous(needed_size)) { + frames_created = false; + break; + } + SharedDIB* dib = new SharedDIB(shared_memory); + owned_dibs_.insert(std::make_pair(i, dib)); + } + } + // Check whether all DIBs were created successfully. + if (!frames_created) { + state_ = media::VideoCapture::kError; + for (ControllerClients::iterator client_it = controller_clients_.begin(); + client_it != controller_clients_.end(); client_it++) { + (*client_it)->event_handler->OnError((*client_it)->controller_id); + } + return; } frame_info_= info; + frame_info_available_ = true; - // Check that all DIBs where created successfully. - if (!frames_created) { - event_handler_->OnError(id_); + for (ControllerClients::iterator client_it = controller_clients_.begin(); + client_it != controller_clients_.end(); client_it++) { + SendFrameInfoAndBuffers((*client_it), static_cast<int>(needed_size)); } - event_handler_->OnFrameInfo(id_, info.width, info.height, info.frame_rate); } -/////////////////////////////////////////////////////////////////////////////// -// Called by VideoCaptureManager when a device have been stopped. -// This will report to the event handler that this object is ready to be deleted -// if all DIBS have been returned. -void VideoCaptureController::OnDeviceStopped(base::Closure stopped_cb) { - bool ready_to_delete_now; +void VideoCaptureController::SendFrameInfoAndBuffers( + ControllerClient* client, int buffer_size) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(frame_info_available_); + client->event_handler->OnFrameInfo(client->controller_id, + frame_info_.width, frame_info_.height, + frame_info_.frame_rate); + for (DIBMap::iterator dib_it = owned_dibs_.begin(); + dib_it != owned_dibs_.end(); dib_it++) { + base::SharedMemory* shared_memory = dib_it->second->shared_memory.get(); + int index = dib_it->first; + base::SharedMemoryHandle remote_handle; + shared_memory->ShareToProcess(client->render_process_handle, + &remote_handle); + client->event_handler->OnBufferCreated(client->controller_id, + remote_handle, + buffer_size, + index); + } +} - { - base::AutoLock lock(lock_); - // Set flag to indicate we need to report when all DIBs have been returned. - report_ready_to_delete_ = true; - ready_to_delete_now = (free_dibs_.size() == owned_dibs_.size()); +// This function is called when all buffers have been returned to controller, +// or when device is stopped. It decides whether the device needs to be +// restarted. +void VideoCaptureController::PostStopping() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK_EQ(state_, media::VideoCapture::kStopping); + + // When clients still have some buffers, or device has not been stopped yet, + // do nothing. + if (ClientHasDIB() || device_in_use_) + return; + + // It's safe to free all DIB's on IO thread since device won't send + // buffer over. + STLDeleteValues(&owned_dibs_); + + // No more client. Therefore the controller is stopped. + if (controller_clients_.empty() && pending_clients_.empty()) { + state_ = media::VideoCapture::kStopped; + return; } - if (ready_to_delete_now) { - event_handler_->OnReadyToDelete(id_); + // Restart the device. + current_params_.width = 0; + current_params_.height = 0; + ControllerClients::iterator client_it; + for (client_it = controller_clients_.begin(); + client_it != controller_clients_.end(); client_it++) { + if (current_params_.width < (*client_it)->parameters.width) + current_params_.width = (*client_it)->parameters.width; + if (current_params_.height < (*client_it)->parameters.height) + current_params_.height = (*client_it)->parameters.height; + } + for (client_it = pending_clients_.begin(); + client_it != pending_clients_.end(); ) { + if (current_params_.width < (*client_it)->parameters.width) + current_params_.width = (*client_it)->parameters.width; + if (current_params_.height < (*client_it)->parameters.height) + current_params_.height = (*client_it)->parameters.height; + controller_clients_.push_back((*client_it)); + pending_clients_.erase(client_it++); } + // Request the manager to start the actual capture. + video_capture_manager_->Start(current_params_, this); + state_ = media::VideoCapture::kStarted; +} - if (!stopped_cb.is_null()) - stopped_cb.Run(); +bool VideoCaptureController::ClientHasDIB() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + base::AutoLock lock(lock_); + for (DIBMap::iterator dib_it = owned_dibs_.begin(); + dib_it != owned_dibs_.end(); dib_it++) { + if (dib_it->second->references > 0) + return true; + } + return false; } + +VideoCaptureController::ControllerClient* +VideoCaptureController::FindClient( + const VideoCaptureControllerID& id, + VideoCaptureControllerEventHandler* handler, + const ControllerClients& clients) { + for (ControllerClients::const_iterator client_it = clients.begin(); + client_it != clients.end(); client_it++) { + if ((*client_it)->controller_id == id && + (*client_it)->event_handler == handler) { + return *client_it; + } + } + return NULL; +} + +/////////////////////////////////////////////////////////////////////////////// +// Called by VideoCaptureManager when a device have been stopped. +void VideoCaptureController::OnDeviceStopped() { + BrowserThread::PostTask(BrowserThread::IO, + FROM_HERE, + base::Bind(&VideoCaptureController::DoDeviceStoppedOnIOThread, this)); +} + +void VideoCaptureController::DoDeviceStoppedOnIOThread() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + device_in_use_ = false; + if (state_ == media::VideoCapture::kStopping) { + PostStopping(); + } +} + diff --git a/content/browser/renderer_host/media/video_capture_controller.h b/content/browser/renderer_host/media/video_capture_controller.h index 1a9a4e9..c964a8e 100644 --- a/content/browser/renderer_host/media/video_capture_controller.h +++ b/content/browser/renderer_host/media/video_capture_controller.h @@ -5,12 +5,13 @@ // VideoCaptureController is the glue between VideoCaptureHost, // VideoCaptureManager and VideoCaptureDevice. // It provides functions for VideoCaptureHost to start a VideoCaptureDevice and -// is responsible for keeping track of TransportDIBs and filling them with I420 +// is responsible for keeping track of shared DIBs and filling them with I420 // video frames for IPC communication between VideoCaptureHost and // VideoCaptureMessageFilter. // It implements media::VideoCaptureDevice::EventHandler to get video frames // from a VideoCaptureDevice object and do color conversion straight into the -// TransportDIBs to avoid a memory copy. +// shared DIBs to avoid a memory copy. +// It serves multiple VideoCaptureControllerEventHandlers. #ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_CONTROLLER_H_ #define CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_CONTROLLER_H_ @@ -18,14 +19,15 @@ #include <list> #include <map> +#include "base/compiler_specific.h" #include "base/memory/ref_counted.h" #include "base/process.h" #include "base/synchronization/lock.h" #include "base/task.h" #include "content/browser/renderer_host/media/video_capture_controller_event_handler.h" +#include "media/video/capture/video_capture.h" #include "media/video/capture/video_capture_device.h" #include "media/video/capture/video_capture_types.h" -#include "ui/gfx/surface/transport_dib.h" namespace media_stream { class VideoCaptureManager; @@ -36,66 +38,97 @@ class VideoCaptureController public media::VideoCaptureDevice::EventHandler { public: VideoCaptureController( - const VideoCaptureControllerID& id, - base::ProcessHandle render_process, - VideoCaptureControllerEventHandler* event_handler, media_stream::VideoCaptureManager* video_capture_manager); virtual ~VideoCaptureController(); - // Starts video capturing and tries to use the resolution specified in - // params. - // When capturing has started VideoCaptureControllerEventHandler::OnFrameInfo - // is called with resolution that best matches the requested that the video + // Start video capturing and try to use the resolution specified in + // |params|. + // When capturing has started, the |event_handler| receives a call OnFrameInfo + // with resolution that best matches the requested that the video // capture device support. - void StartCapture(const media::VideoCaptureParams& params); + void StartCapture(const VideoCaptureControllerID& id, + VideoCaptureControllerEventHandler* event_handler, + base::ProcessHandle render_process, + const media::VideoCaptureParams& params); // Stop video capture. - // When the capture is stopped and all TransportDIBS have been returned + // When the capture is stopped and all DIBs have been returned, // VideoCaptureControllerEventHandler::OnReadyToDelete will be called. - // |stopped_cb| may be NULL. But a non-NULL Closure can be used to get - // a notification when the device is stopped, regardless of - // VideoCaptureController's state. - void StopCapture(base::Closure stopped_cb); + // |force_buffer_return| allows controller to take back all buffers used + // by |event_handler|. + void StopCapture(const VideoCaptureControllerID& id, + VideoCaptureControllerEventHandler* event_handler, + bool force_buffer_return); // Return a buffer previously given in // VideoCaptureControllerEventHandler::OnBufferReady. - void ReturnBuffer(int buffer_id); + void ReturnBuffer(const VideoCaptureControllerID& id, + VideoCaptureControllerEventHandler* event_handler, + int buffer_id); // Implement media::VideoCaptureDevice::EventHandler. virtual void OnIncomingCapturedFrame(const uint8* data, int length, - base::Time timestamp); - virtual void OnError(); - virtual void OnFrameInfo(const media::VideoCaptureDevice::Capability& info); + base::Time timestamp) OVERRIDE; + virtual void OnError() OVERRIDE; + virtual void OnFrameInfo( + const media::VideoCaptureDevice::Capability& info) OVERRIDE; private: - // Called by VideoCaptureManager when a device have been stopped. - void OnDeviceStopped(base::Closure stopped_cb); + struct ControllerClient; + typedef std::list<ControllerClient*> ControllerClients; + + // Callback when manager has stopped device. + void OnDeviceStopped(); + + // Worker functions on IO thread. + void DoIncomingCapturedFrameOnIOThread(int buffer_id, base::Time timestamp); + void DoFrameInfoOnIOThread(const media::VideoCaptureDevice::Capability info); + void DoErrorOnIOThread(); + void DoDeviceStateOnIOThread(bool in_use); + void DoDeviceStoppedOnIOThread(); + + // Send frame info and init buffers to |client|. + void SendFrameInfoAndBuffers(ControllerClient* client, int buffer_size); + + // Helpers. + // Find a client of |id| and |handler| in |clients|. + ControllerClient* FindClient( + const VideoCaptureControllerID& id, + VideoCaptureControllerEventHandler* handler, + const ControllerClients& clients); + // Decide what to do after kStopping state. Dependent on events, controller + // can stay in kStopping state, or go to kStopped, or restart capture. + void PostStopping(); + // Check if any DIB is used by client. + bool ClientHasDIB(); // Lock to protect free_dibs_ and owned_dibs_. base::Lock lock_; - // Handle to the render process that will receive the DIBs. - base::ProcessHandle render_handle_; - bool report_ready_to_delete_; - typedef std::list<int> DIBHandleList; - typedef std::map<int, base::SharedMemory*> DIBMap; - - // Free DIBS that can be filled with video frames. - DIBHandleList free_dibs_; - // All DIBS created by this object. + struct SharedDIB; + typedef std::map<int /*buffer_id*/, SharedDIB*> DIBMap; + // All DIBs created by this object. + // It's modified only on IO thread. DIBMap owned_dibs_; - VideoCaptureControllerEventHandler* event_handler_; - // The parameter that was requested when starting the capture device. - media::VideoCaptureParams params_; + // All clients served by this controller. + ControllerClients controller_clients_; + + // All clients waiting for service. + ControllerClients pending_clients_; + + // The parameter that currently used for the capturing. + media::VideoCaptureParams current_params_; - // ID used for identifying this object. - VideoCaptureControllerID id_; media::VideoCaptureDevice::Capability frame_info_; + bool frame_info_available_; media_stream::VideoCaptureManager* video_capture_manager_; + bool device_in_use_; + media::VideoCapture::State state_; + DISALLOW_IMPLICIT_CONSTRUCTORS(VideoCaptureController); }; diff --git a/content/browser/renderer_host/media/video_capture_controller_event_handler.cc b/content/browser/renderer_host/media/video_capture_controller_event_handler.cc index e168514..c58230f 100644 --- a/content/browser/renderer_host/media/video_capture_controller_event_handler.cc +++ b/content/browser/renderer_host/media/video_capture_controller_event_handler.cc @@ -12,3 +12,8 @@ bool VideoCaptureControllerID::operator<( const VideoCaptureControllerID& vc) const { return this->device_id < vc.device_id; } + +bool VideoCaptureControllerID::operator==( + const VideoCaptureControllerID& vc) const { + return this->device_id == vc.device_id; +} diff --git a/content/browser/renderer_host/media/video_capture_controller_event_handler.h b/content/browser/renderer_host/media/video_capture_controller_event_handler.h index 77314a9..345c0f5 100644 --- a/content/browser/renderer_host/media/video_capture_controller_event_handler.h +++ b/content/browser/renderer_host/media/video_capture_controller_event_handler.h @@ -11,9 +11,10 @@ // ID used for identifying an object of VideoCaptureController. struct CONTENT_EXPORT VideoCaptureControllerID { - VideoCaptureControllerID(int device_id); + explicit VideoCaptureControllerID(int device_id); bool operator<(const VideoCaptureControllerID& vc) const; + bool operator==(const VideoCaptureControllerID& vc) const; int device_id; }; diff --git a/content/browser/renderer_host/media/video_capture_host.cc b/content/browser/renderer_host/media/video_capture_host.cc index ece9c11..a4a4738 100644 --- a/content/browser/renderer_host/media/video_capture_host.cc +++ b/content/browser/renderer_host/media/video_capture_host.cc @@ -8,6 +8,7 @@ #include "base/memory/scoped_ptr.h" #include "base/stl_util.h" #include "content/browser/renderer_host/media/media_stream_manager.h" +#include "content/browser/renderer_host/media/video_capture_manager.h" #include "content/browser/resource_context.h" #include "content/common/media/video_capture_messages.h" @@ -26,11 +27,12 @@ void VideoCaptureHost::OnChannelClosing() { // Since the IPC channel is gone, close all requested VideCaptureDevices. for (EntryMap::iterator it = entries_.begin(); it != entries_.end(); it++) { VideoCaptureController* controller = it->second; - // Since the channel is closing we need a task to make sure VideoCaptureHost - // is not deleted before VideoCaptureController. - controller->StopCapture( - base::Bind(&VideoCaptureHost::OnReadyToDelete, this, it->first)); + VideoCaptureControllerID controller_id(it->first); + controller->StopCapture(controller_id, this, true); + GetVideoCaptureManager()->RemoveController(controller, this); } + entries_.clear(); + entry_state_.clear(); } void VideoCaptureHost::OnDestruct() const { @@ -110,7 +112,7 @@ void VideoCaptureHost::DoHandleError(int device_id) { EntryMap::iterator it = entries_.find(id); if (it != entries_.end()) { VideoCaptureController* controller = it->second; - controller->StopCapture(base::Closure()); + controller->StopCapture(id, this, false); } } @@ -148,31 +150,63 @@ bool VideoCaptureHost::OnMessageReceived(const IPC::Message& message, void VideoCaptureHost::OnStartCapture(int device_id, const media::VideoCaptureParams& params) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - VideoCaptureControllerID controller_id(device_id); + DCHECK(entries_.find(controller_id) == entries_.end()); + DCHECK(entry_state_.find(controller_id) == entry_state_.end()); + + entry_state_[controller_id] = media::VideoCapture::kStarted; + GetVideoCaptureManager()->AddController( + params, this, base::Bind(&VideoCaptureHost::OnControllerAdded, this, + device_id, params)); +} + +void VideoCaptureHost::OnControllerAdded( + int device_id, const media::VideoCaptureParams& params, + VideoCaptureController* controller) { + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&VideoCaptureHost::DoControllerAddedOnIOThreead, + this, device_id, params, make_scoped_refptr(controller))); +} +void VideoCaptureHost::DoControllerAddedOnIOThreead( + int device_id, const media::VideoCaptureParams params, + VideoCaptureController* controller) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + VideoCaptureControllerID controller_id(device_id); DCHECK(entries_.find(controller_id) == entries_.end()); + DCHECK(entry_state_.find(controller_id) != entry_state_.end()); + + if (controller == NULL) { + Send(new VideoCaptureMsg_StateChanged(device_id, + media::VideoCapture::kError)); + return; + } - scoped_refptr<VideoCaptureController> controller = - new VideoCaptureController( - controller_id, peer_handle(), this, - resource_context_->media_stream_manager()->video_capture_manager()); entries_.insert(std::make_pair(controller_id, controller)); - controller->StartCapture(params); + if (entry_state_[controller_id] == media::VideoCapture::kStarted) { + controller->StartCapture(controller_id, this, peer_handle(), params); + } else if (entry_state_[controller_id] == media::VideoCapture::kStopped) { + controller->StopCapture(controller_id, this, false); + } } void VideoCaptureHost::OnStopCapture(int device_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - VideoCaptureControllerID controller_id(device_id); + + if (entry_state_.find(controller_id) == entry_state_.end()) { + // It does not exist. So it must have been stopped already. + Send(new VideoCaptureMsg_StateChanged(device_id, + media::VideoCapture::kStopped)); + } + + entry_state_[controller_id] = media::VideoCapture::kStopped; + EntryMap::iterator it = entries_.find(controller_id); if (it != entries_.end()) { scoped_refptr<VideoCaptureController> controller = it->second; - controller->StopCapture(base::Closure()); - } else { - // It does not exist so it must have been stopped already. - Send(new VideoCaptureMsg_StateChanged(device_id, - media::VideoCapture::kStopped)); + controller->StopCapture(controller_id, this, false); } } @@ -190,7 +224,7 @@ void VideoCaptureHost::OnReceiveEmptyBuffer(int device_id, int buffer_id) { EntryMap::iterator it = entries_.find(controller_id); if (it != entries_.end()) { scoped_refptr<VideoCaptureController> controller = it->second; - controller->ReturnBuffer(buffer_id); + controller->ReturnBuffer(controller_id, this, buffer_id); } } @@ -201,5 +235,15 @@ void VideoCaptureHost::DoDeleteVideoCaptureController( // Report that the device have successfully been stopped. Send(new VideoCaptureMsg_StateChanged(id.device_id, media::VideoCapture::kStopped)); + EntryMap::iterator it = entries_.find(id); + if (it != entries_.end()) { + GetVideoCaptureManager()->RemoveController(it->second, this); + } entries_.erase(id); + entry_state_.erase(id); +} + +media_stream::VideoCaptureManager* VideoCaptureHost::GetVideoCaptureManager() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + return resource_context_->media_stream_manager()->video_capture_manager(); } diff --git a/content/browser/renderer_host/media/video_capture_host.h b/content/browser/renderer_host/media/video_capture_host.h index 65e275c..5306e55 100644 --- a/content/browser/renderer_host/media/video_capture_host.h +++ b/content/browser/renderer_host/media/video_capture_host.h @@ -88,6 +88,12 @@ class CONTENT_EXPORT VideoCaptureHost // between a VideCaptureMessageFilter and a VideoCaptureHost. void OnStartCapture(int device_id, const media::VideoCaptureParams& params); + void OnControllerAdded( + int device_id, const media::VideoCaptureParams& params, + VideoCaptureController* controller); + void DoControllerAddedOnIOThreead( + int device_id, const media::VideoCaptureParams params, + VideoCaptureController* controller); // IPC message: Stop capture on device referenced by |device_id|. void OnStopCapture(int device_id); @@ -125,13 +131,20 @@ class CONTENT_EXPORT VideoCaptureHost // Handle error coming from VideoCaptureDevice. void DoHandleError(int device_id); - typedef std::map<VideoCaptureControllerID, - scoped_refptr<VideoCaptureController> >EntryMap; + // Helpers. + media_stream::VideoCaptureManager* GetVideoCaptureManager(); + typedef std::map<VideoCaptureControllerID, + scoped_refptr<VideoCaptureController> > EntryMap; // A map of VideoCaptureControllerID to VideoCaptureController // objects that is currently active. EntryMap entries_; + typedef std::map<VideoCaptureControllerID, + media::VideoCapture::State> EntryState; + // Record state of each VideoCaptureControllerID. + EntryState entry_state_; + // Used to get a pointer to VideoCaptureManager to start/stop capture devices. const content::ResourceContext* resource_context_; diff --git a/content/browser/renderer_host/media/video_capture_host_unittest.cc b/content/browser/renderer_host/media/video_capture_host_unittest.cc index 866f2b3..56be837 100644 --- a/content/browser/renderer_host/media/video_capture_host_unittest.cc +++ b/content/browser/renderer_host/media/video_capture_host_unittest.cc @@ -274,18 +274,18 @@ class VideoCaptureHostTest : public testing::Test { void StartCapture() { InSequence s; - // 1. Newly created buffers will arrive. - EXPECT_CALL(*host_, OnNewBufferCreated(kDeviceId, _, _, _)) - .Times(AnyNumber()) - .WillRepeatedly(Return()); - - // 2. First - get info about the new resolution + // 1. First - get info about the new resolution EXPECT_CALL(*host_, OnDeviceInfo(kDeviceId)); - // 3. Change state to started + // 2. Change state to started EXPECT_CALL(*host_, OnStateChanged(kDeviceId, media::VideoCapture::kStarted)); + // 3. Newly created buffers will arrive. + EXPECT_CALL(*host_, OnNewBufferCreated(kDeviceId, _, _, _)) + .Times(AnyNumber()) + .WillRepeatedly(Return()); + // 4. First filled buffer will arrive. EXPECT_CALL(*host_, OnBufferFilled(kDeviceId, _, _)) .Times(AnyNumber()) diff --git a/content/browser/renderer_host/media/video_capture_manager.cc b/content/browser/renderer_host/media/video_capture_manager.cc index 97f102b..827b88e 100644 --- a/content/browser/renderer_host/media/video_capture_manager.cc +++ b/content/browser/renderer_host/media/video_capture_manager.cc @@ -5,7 +5,9 @@ #include "content/browser/renderer_host/media/video_capture_manager.h" #include "base/bind.h" -#include "base/memory/scoped_ptr.h" +#include "base/stl_util.h" +#include "content/browser/renderer_host/media/video_capture_controller.h" +#include "content/browser/renderer_host/media/video_capture_controller_event_handler.h" #include "content/public/browser/browser_thread.h" #include "media/video/capture/fake_video_capture_device.h" #include "media/video/capture/video_capture_device.h" @@ -19,6 +21,21 @@ namespace media_stream { // explicitly calling open device. enum { kFirstSessionId = VideoCaptureManager::kStartOpenSessionId + 1 }; +struct VideoCaptureManager::Controller { + Controller( + VideoCaptureController* vc_controller, + VideoCaptureControllerEventHandler* handler) + : controller(vc_controller), + ready_to_delete(false) { + handlers.push_front(handler); + } + ~Controller() {} + + scoped_refptr<VideoCaptureController> controller; + bool ready_to_delete; + Handlers handlers; +}; + VideoCaptureManager::VideoCaptureManager() : vc_device_thread_("VideoCaptureManagerThread"), listener_(NULL), @@ -40,6 +57,7 @@ VideoCaptureManager::~VideoCaptureManager() { it->second->DeAllocate(); delete it->second; } + STLDeleteValues(&controllers_); } void VideoCaptureManager::Register(MediaStreamProviderListener* listener) { @@ -195,32 +213,17 @@ void VideoCaptureManager::OnStart( DCHECK(IsOnCaptureDeviceThread()); DCHECK(video_capture_receiver != NULL); - // Solution for not using MediaStreamManager. - // This session id won't be returned by Open(). - if (capture_params.session_id == kStartOpenSessionId) { - // Start() is called without using Open(), we need to open a device. - media::VideoCaptureDevice::Names device_names; - GetAvailableDevices(&device_names); - if (device_names.empty()) { - // No devices available. - video_capture_receiver->OnError(); - return; - } - StreamDeviceInfo device(kVideoCapture, - device_names.front().device_name, - device_names.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()) { + media::VideoCaptureDevice* video_capture_device = + GetDeviceInternal(capture_params.session_id); + if (!video_capture_device) { // Invalid session id. video_capture_receiver->OnError(); return; } - media::VideoCaptureDevice* video_capture_device = it->second; + Controllers::iterator cit = controllers_.find(video_capture_device); + if (cit != controllers_.end()) { + cit->second->ready_to_delete = false; + } // Possible errors are signaled to video_capture_receiver by // video_capture_device. video_capture_receiver to perform actions. @@ -242,6 +245,14 @@ void VideoCaptureManager::OnStop( // video_capture_device. video_capture_receiver to perform actions. video_capture_device->Stop(); video_capture_device->DeAllocate(); + Controllers::iterator cit = controllers_.find(video_capture_device); + if (cit != controllers_.end()) { + cit->second->ready_to_delete = true; + if (cit->second->handlers.empty()) { + delete cit->second; + controllers_.erase(cit); + } + } } if (!stopped_cb.is_null()) @@ -372,4 +383,106 @@ bool VideoCaptureManager::DeviceOpened(const StreamDeviceInfo& device_info) { return false; } +void VideoCaptureManager::AddController( + const media::VideoCaptureParams& capture_params, + VideoCaptureControllerEventHandler* handler, + base::Callback<void(VideoCaptureController*)> added_cb) { + DCHECK(handler); + vc_device_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&VideoCaptureManager::DoAddControllerOnDeviceThread, + base::Unretained(this), capture_params, handler, added_cb)); +} + +void VideoCaptureManager::DoAddControllerOnDeviceThread( + const media::VideoCaptureParams capture_params, + VideoCaptureControllerEventHandler* handler, + base::Callback<void(VideoCaptureController*)> added_cb) { + DCHECK(IsOnCaptureDeviceThread()); + + media::VideoCaptureDevice* video_capture_device = + GetDeviceInternal(capture_params.session_id); + scoped_refptr<VideoCaptureController> controller; + if (video_capture_device) { + Controllers::iterator cit = controllers_.find(video_capture_device); + if (cit == controllers_.end()) { + controller = new VideoCaptureController(this); + controllers_[video_capture_device] = new Controller(controller, handler); + } else { + controllers_[video_capture_device]->handlers.push_front(handler); + controller = controllers_[video_capture_device]->controller; + } + } + added_cb.Run(controller); +} + +void VideoCaptureManager::RemoveController( + VideoCaptureController* controller, + VideoCaptureControllerEventHandler* handler) { + DCHECK(handler); + vc_device_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&VideoCaptureManager::DoRemoveControllerOnDeviceThread, + base::Unretained(this), + make_scoped_refptr(controller), + handler)); +} + +void VideoCaptureManager::DoRemoveControllerOnDeviceThread( + VideoCaptureController* controller, + VideoCaptureControllerEventHandler* handler) { + DCHECK(IsOnCaptureDeviceThread()); + + for (Controllers::iterator cit = controllers_.begin(); + cit != controllers_.end(); ++cit) { + if (controller == cit->second->controller) { + Handlers& handlers = cit->second->handlers; + for (Handlers::iterator hit = handlers.begin(); + hit != handlers.end(); ++hit) { + if ((*hit) == handler) { + handlers.erase(hit); + break; + } + } + if (handlers.empty() && cit->second->ready_to_delete) { + delete cit->second; + controllers_.erase(cit); + } + return; + } + } +} + +media::VideoCaptureDevice* VideoCaptureManager::GetDeviceInternal( + int capture_session_id) { + DCHECK(IsOnCaptureDeviceThread()); + VideoCaptureDevices::iterator dit = devices_.find(capture_session_id); + if (dit != devices_.end()) { + return dit->second; + } + + // Solution for not using MediaStreamManager. + // This session id won't be returned by Open(). + if (capture_session_id == kStartOpenSessionId) { + media::VideoCaptureDevice::Names device_names; + GetAvailableDevices(&device_names); + if (device_names.empty()) { + // No devices available. + return NULL; + } + StreamDeviceInfo device(kVideoCapture, + device_names.front().device_name, + device_names.front().unique_id, false); + + // Call OnOpen to open using the first device in the list. + OnOpen(capture_session_id, device); + + VideoCaptureDevices::iterator dit = devices_.find(capture_session_id); + if (dit != devices_.end()) { + return dit->second; + } + } + return NULL; +} + } // namespace media_stream diff --git a/content/browser/renderer_host/media/video_capture_manager.h b/content/browser/renderer_host/media/video_capture_manager.h index c7b3e31..b2b42b8 100644 --- a/content/browser/renderer_host/media/video_capture_manager.h +++ b/content/browser/renderer_host/media/video_capture_manager.h @@ -2,14 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// VideoCaptureManager is used to open/close, start/stop as well as enumerate -// available video capture devices. All functions are expected to be called from -// the Browser::IO thread. VideoCaptureManager will open OS dependent instances -// of VideoCaptureDevice. A device can only be opened once. +// VideoCaptureManager is used to open/close, start/stop, enumerate available +// video capture devices, and manage VideoCaptureController's. +// All functions are expected to be called from Browser::IO thread. +// VideoCaptureManager will open OS dependent instances of VideoCaptureDevice. +// A device can only be opened once. #ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_MANAGER_H_ #define CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_MANAGER_H_ +#include <list> #include <map> #include "base/threading/thread.h" @@ -19,6 +21,9 @@ #include "media/video/capture/video_capture_device.h" #include "media/video/capture/video_capture_types.h" +class VideoCaptureController; +class VideoCaptureControllerEventHandler; + namespace media_stream { // VideoCaptureManager opens/closes and start/stops video capture devices. @@ -66,7 +71,21 @@ class CONTENT_EXPORT VideoCaptureManager : public MediaStreamProvider { void UseFakeDevice(); MessageLoop* GetMessageLoop(); + // Called by VideoCaptureHost to get a controller for |capture_params|. + // The controller is returned via calling |added_cb|. + void AddController( + const media::VideoCaptureParams& capture_params, + VideoCaptureControllerEventHandler* handler, + base::Callback<void(VideoCaptureController*)> added_cb); + // Called by VideoCaptureHost to remove the |controller|. + void RemoveController( + VideoCaptureController* controller, + VideoCaptureControllerEventHandler* handler); + private: + typedef std::list<VideoCaptureControllerEventHandler*> Handlers; + struct Controller; + // Called by the public functions, executed on vc_device_thread_. void OnEnumerateDevices(); void OnOpen(int capture_session_id, const StreamDeviceInfo& device); @@ -75,6 +94,13 @@ class CONTENT_EXPORT VideoCaptureManager : public MediaStreamProvider { media::VideoCaptureDevice::EventHandler* video_capture_receiver); void OnStop(const media::VideoCaptureSessionId capture_session_id, base::Closure stopped_cb); + void DoAddControllerOnDeviceThread( + const media::VideoCaptureParams capture_params, + VideoCaptureControllerEventHandler* handler, + base::Callback<void(VideoCaptureController*)> added_cb); + void DoRemoveControllerOnDeviceThread( + VideoCaptureController* controller, + VideoCaptureControllerEventHandler* handler); // Executed on Browser::IO thread to call Listener. void OnOpened(int capture_session_id); @@ -94,6 +120,7 @@ class CONTENT_EXPORT VideoCaptureManager : public MediaStreamProvider { bool DeviceOpened(const media::VideoCaptureDevice::Name& device_name); bool DeviceOpened(const StreamDeviceInfo& device_info); bool IsOnCaptureDeviceThread() const; + media::VideoCaptureDevice* GetDeviceInternal(int capture_session_id); // Thread for all calls to VideoCaptureDevice. base::Thread vc_device_thread_; @@ -111,6 +138,13 @@ class CONTENT_EXPORT VideoCaptureManager : public MediaStreamProvider { // Set to true if using fake devices for testing, false by default. bool use_fake_device_; + // Only accessed from vc_device_thread_. + // VideoCaptureManager owns all VideoCaptureController's and is responsible + // for deleting the instances when they are not used any longer. + // VideoCaptureDevice is one-to-one mapped to VideoCaptureController. + typedef std::map<media::VideoCaptureDevice*, Controller*> Controllers; + Controllers controllers_; + DISALLOW_COPY_AND_ASSIGN(VideoCaptureManager); }; |