Chromium Code Reviews| Index: content/browser/renderer_host/media/video_capture_manager.cc |
| diff --git a/content/browser/renderer_host/media/video_capture_manager.cc b/content/browser/renderer_host/media/video_capture_manager.cc |
| index 617a8696deeacb2c2f8d0ef5e8ceb1b24416b5f7..952065eda6b9502972b33cb6f93a1d988a32ecd9 100644 |
| --- a/content/browser/renderer_host/media/video_capture_manager.cc |
| +++ b/content/browser/renderer_host/media/video_capture_manager.cc |
| @@ -118,6 +118,7 @@ VideoCaptureManager::VideoCaptureManager( |
| VideoCaptureManager::~VideoCaptureManager() { |
| DCHECK(devices_.empty()); |
| + DCHECK(device_start_queue_.empty()); |
| } |
| void VideoCaptureManager::Register( |
| @@ -216,22 +217,132 @@ void VideoCaptureManager::Close(int capture_session_id) { |
| sessions_.erase(session_it); |
| } |
| -void VideoCaptureManager::DoStartDeviceOnDeviceThread( |
| +VideoCaptureManager::CaptureDeviceStartRequest::CaptureDeviceStartRequest( |
| + DeviceEntry* entry, |
| + media::VideoCaptureSessionId session_id, |
| + const media::VideoCaptureParams& params) |
| + : entry(entry), |
| + session_id(session_id), |
| + params(params), |
| + abort_start(false) { |
| +} |
| + |
| +void VideoCaptureManager::QueueStartDevice( |
| media::VideoCaptureSessionId session_id, |
| DeviceEntry* entry, |
| + const media::VideoCaptureParams& params) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| + device_start_queue_.push_back( |
| + CaptureDeviceStartRequest(entry, session_id, params)); |
| + if (device_start_queue_.size() == 1) |
| + HandleQueuedStartRequest(); |
| +} |
| + |
| +void VideoCaptureManager::DoStopDevice(DeviceEntry* entry) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| + DCHECK(std::find(devices_.begin(), devices_.end(), entry) != devices_.end()); |
| + |
| + // Reverse iterate to find the last start request. This also avoids a problem |
| + // where |entry| might get the same pointer value if it is deleted |
| + // and recreated while we are waiting for the device to be started. |
| + for (DeviceStartQueue::reverse_iterator request = |
| + device_start_queue_.rbegin(); |
| + request != device_start_queue_.rend(); ++request) { |
| + if (request->entry == entry) { |
| + request->abort_start = true; |
| + DVLOG(3) << "DoStopDevice, aborting start request for device " |
| + << entry->id << "."; |
| + return; |
| + } |
| + } |
| + |
| + DVLOG(3) << "DoStopDevice. Send stop request for device " << entry->id << "."; |
| + if (entry->video_capture_device.get()) { |
| + // |entry->video_capture_device| can be null if creating the device fails. |
| + device_task_runner_->PostTask( |
| + FROM_HERE, |
| + base::Bind(&VideoCaptureManager::DoStopDeviceOnDeviceThread, this, |
| + base::Passed(&entry->video_capture_device))); |
| + } |
| +} |
| + |
| +void VideoCaptureManager::HandleQueuedStartRequest() { |
| + // Remove all start requests that have been aborted. |
| + while(device_start_queue_.begin() != device_start_queue_.end() && |
| + device_start_queue_.begin()->abort_start) { |
| + device_start_queue_.pop_front(); |
| + } |
| + DeviceStartQueue::iterator request = device_start_queue_.begin(); |
| + if (request == device_start_queue_.end()) |
| + return; |
| + |
| + DeviceEntry* entry = request->entry; |
| + DCHECK(std::find(devices_.begin(), devices_.end(), request->entry) != |
| + devices_.end()); |
| + |
| + DVLOG(3) << "HandleQueuedStartRequest, Post start to device thread, device = " |
| + << entry->id; |
| + base::PostTaskAndReplyWithResult( |
| + device_task_runner_.get(), |
| + FROM_HERE, |
| + base::Bind( |
| + &VideoCaptureManager::DoStartDeviceOnDeviceThread, |
| + this, |
| + request->session_id, |
| + entry->id, |
| + entry->stream_type, |
| + request->params, |
| + base::Passed(entry->video_capture_controller->NewDeviceClient())), |
| + base::Bind(&VideoCaptureManager::OnDeviceStarted, this, entry)); |
| +} |
| + |
| +void VideoCaptureManager::OnDeviceStarted( |
| + DeviceEntry* device_entry, |
| + scoped_ptr<media::VideoCaptureDevice> device) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| + DCHECK(device_entry == device_start_queue_.begin()->entry); |
| + DVLOG(3) << "OnDeviceStarted"; |
| + if (device_start_queue_.front().abort_start) { |
| + // |device| can be null if creation failed in DoStartDeviceOnDeviceThread. |
| + // The device is no longer wanted. Stop the device again. |
| + DVLOG(3) << "OnDeviceStarted but start request have been aborted."; |
| + media::VideoCaptureDevice* device_ptr = device.get(); |
| + base::Closure closure = |
| + base::Bind(&VideoCaptureManager::DoStopDeviceOnDeviceThread, this, |
| + base::Passed(&device)); |
| + if (device_ptr && !device_task_runner_->PostTask(FROM_HERE, closure)) { |
| + // PostTask failed. The device must be stopped anyway. |
| + device_ptr->StopAndDeAllocate(); |
| + } |
| + } else { |
| + DCHECK(std::find(devices_.begin(), devices_.end(), device_entry) != |
| + devices_.end()); |
| + DCHECK(!device_entry->video_capture_device); |
|
tommi (sloooow) - chröme
2014/12/17 14:46:12
missing .get() or is this now supported in scoped_
perkj_chrome
2014/12/18 12:15:32
Seems to compile. Which operator would that be?
|
| + device_entry->video_capture_device.swap(device); |
| + } |
| + |
| + device_start_queue_.pop_front(); |
| + HandleQueuedStartRequest(); |
| +} |
| + |
| +scoped_ptr<media::VideoCaptureDevice> |
| +VideoCaptureManager::DoStartDeviceOnDeviceThread( |
| + media::VideoCaptureSessionId session_id, |
| + const std::string& id, |
| + MediaStreamType stream_type, |
| const media::VideoCaptureParams& params, |
| scoped_ptr<media::VideoCaptureDevice::Client> device_client) { |
| SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StartDeviceTime"); |
| DCHECK(IsOnDeviceThread()); |
| scoped_ptr<media::VideoCaptureDevice> video_capture_device; |
| - switch (entry->stream_type) { |
| + switch (stream_type) { |
| case MEDIA_DEVICE_VIDEO_CAPTURE: { |
| // We look up the device id from the renderer in our local enumeration |
| // since the renderer does not have all the information that might be |
| // held in the browser-side VideoCaptureDevice::Name structure. |
| media::VideoCaptureDeviceInfo* found = |
| - FindDeviceInfoById(entry->id, devices_info_cache_); |
| + FindDeviceInfoById(id, devices_info_cache_); |
| if (found) { |
| video_capture_device = |
| video_capture_device_factory_->Create(found->name); |
| @@ -240,20 +351,21 @@ void VideoCaptureManager::DoStartDeviceOnDeviceThread( |
| } |
| case MEDIA_TAB_VIDEO_CAPTURE: { |
| video_capture_device.reset( |
| - WebContentsVideoCaptureDevice::Create(entry->id)); |
| + WebContentsVideoCaptureDevice::Create(id)); |
| break; |
| } |
| case MEDIA_DESKTOP_VIDEO_CAPTURE: { |
| #if defined(ENABLE_SCREEN_CAPTURE) |
| - DesktopMediaID id = DesktopMediaID::Parse(entry->id); |
| + DesktopMediaID desktop_id = DesktopMediaID::Parse(id); |
| #if defined(USE_AURA) |
| - if (id.type == DesktopMediaID::TYPE_AURA_WINDOW) { |
| - video_capture_device.reset(DesktopCaptureDeviceAura::Create(id)); |
| + if (desktop_id.type == DesktopMediaID::TYPE_AURA_WINDOW) { |
| + video_capture_device.reset( |
| + DesktopCaptureDeviceAura::Create(desktop_id)); |
| } else |
| #endif |
| - if (id.type != DesktopMediaID::TYPE_NONE && |
| - id.type != DesktopMediaID::TYPE_AURA_WINDOW) { |
| - video_capture_device = DesktopCaptureDevice::Create(id); |
| + if (desktop_id.type != DesktopMediaID::TYPE_NONE && |
| + desktop_id.type != DesktopMediaID::TYPE_AURA_WINDOW) { |
| + video_capture_device = DesktopCaptureDevice::Create(desktop_id); |
| if (notification_window_ids_.find(session_id) != |
| notification_window_ids_.end()) { |
| static_cast<DesktopCaptureDevice*>(video_capture_device.get()) |
| @@ -273,11 +385,11 @@ void VideoCaptureManager::DoStartDeviceOnDeviceThread( |
| if (!video_capture_device) { |
| device_client->OnError("Could not create capture device"); |
| - return; |
| + return nullptr; |
| } |
| video_capture_device->AllocateAndStart(params, device_client.Pass()); |
| - entry->video_capture_device = video_capture_device.Pass(); |
| + return video_capture_device.Pass(); |
| } |
| void VideoCaptureManager::StartCaptureForClient( |
| @@ -306,16 +418,7 @@ void VideoCaptureManager::StartCaptureForClient( |
| if (entry->video_capture_controller->GetActiveClientCount() == 0) { |
| DVLOG(1) << "VideoCaptureManager starting device (type = " |
| << entry->stream_type << ", id = " << entry->id << ")"; |
| - |
| - device_task_runner_->PostTask( |
| - FROM_HERE, |
| - base::Bind( |
| - &VideoCaptureManager::DoStartDeviceOnDeviceThread, |
| - this, |
| - session_id, |
| - entry, |
| - params, |
| - base::Passed(entry->video_capture_controller->NewDeviceClient()))); |
| + QueueStartDevice(session_id, entry, params); |
| } |
| // Run the callback first, as AddClient() may trigger OnFrameInfo(). |
| done_cb.Run(entry->video_capture_controller->GetWeakPtr()); |
| @@ -392,10 +495,7 @@ void VideoCaptureManager::PauseCaptureForClient( |
| return; |
| // There is no more client, release the camera. |
| - device_task_runner_->PostTask( |
| - FROM_HERE, |
| - base::Bind(&VideoCaptureManager::DoStopDeviceOnDeviceThread, this, |
| - base::Unretained(entry))); |
| + DoStopDevice(entry); |
| } |
| void VideoCaptureManager::ResumeCaptureForClient( |
| @@ -423,15 +523,7 @@ void VideoCaptureManager::ResumeCaptureForClient( |
| return; |
| // This is first active client, allocate the camera. |
| - device_task_runner_->PostTask( |
| - FROM_HERE, |
| - base::Bind( |
| - &VideoCaptureManager::DoStartDeviceOnDeviceThread, |
| - this, |
| - session_id, |
| - entry, |
| - params, |
| - base::Passed(entry->video_capture_controller->NewDeviceClient()))); |
| + QueueStartDevice(session_id, entry, params); |
| } |
| bool VideoCaptureManager::GetDeviceSupportedFormats( |
| @@ -517,13 +609,12 @@ void VideoCaptureManager::SetDesktopCaptureWindowId( |
| window_id)); |
| } |
| -void VideoCaptureManager::DoStopDeviceOnDeviceThread(DeviceEntry* entry) { |
| +void VideoCaptureManager::DoStopDeviceOnDeviceThread( |
| + scoped_ptr<media::VideoCaptureDevice> device) { |
| SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StopDeviceTime"); |
| DCHECK(IsOnDeviceThread()); |
| - if (entry->video_capture_device) { |
| - entry->video_capture_device->StopAndDeAllocate(); |
| - } |
| - entry->video_capture_device.reset(); |
| + device->StopAndDeAllocate(); |
| + DVLOG(3) << "DoStopDeviceOnDeviceThread"; |
| } |
| void VideoCaptureManager::OnOpened( |
| @@ -653,12 +744,11 @@ void VideoCaptureManager::DestroyDeviceEntryIfNoClients(DeviceEntry* entry) { |
| // deleted immediately, and the device is freed asynchronously. After this |
| // point, subsequent requests to open this same device ID will create a new |
| // DeviceEntry, VideoCaptureController, and VideoCaptureDevice. |
| - devices_.erase(entry); |
| - entry->video_capture_controller.reset(); |
| - device_task_runner_->PostTask( |
| - FROM_HERE, |
| - base::Bind(&VideoCaptureManager::DoStopDeviceOnDeviceThread, this, |
| - base::Owned(entry))); |
| + DoStopDevice(entry); |
| + DeviceEntries::iterator device_it = std::find(devices_.begin(), |
| + devices_.end(), |
| + entry); |
| + devices_.erase(device_it); |
| } |
| } |
| @@ -688,7 +778,7 @@ VideoCaptureManager::DeviceEntry* VideoCaptureManager::GetOrCreateDeviceEntry( |
| DeviceEntry* new_device = new DeviceEntry(device_info.type, |
| device_info.id, |
| video_capture_controller.Pass()); |
| - devices_.insert(new_device); |
| + devices_.push_back(new_device); |
| return new_device; |
| } |