Chromium Code Reviews| Index: content/browser/renderer_host/media/media_stream_manager.cc |
| =================================================================== |
| --- content/browser/renderer_host/media/media_stream_manager.cc (revision 0) |
| +++ content/browser/renderer_host/media/media_stream_manager.cc (revision 0) |
| @@ -0,0 +1,393 @@ |
| +// 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/renderer_host/media/media_stream_manager.h" |
| + |
| +#include <list> |
| + |
| +#include "base/lazy_instance.h" |
| +#include "base/logging.h" |
| +#include "base/rand_util.h" |
| +#include "content/browser/browser_thread.h" |
| +#include "content/browser/renderer_host/media/media_stream_device_settings.h" |
| +#include "content/browser/renderer_host/media/media_stream_requester.h" |
| +#include "content/browser/renderer_host/media/video_capture_manager.h" |
| +#include "content/common/media/media_stream_options.h" |
| + |
| +namespace media_stream { |
| + |
| +// TODO(mflodman) Find out who should own MediaStreamManager. |
| +base::LazyInstance<MediaStreamManager> g_media_stream_manager( |
| + base::LINKER_INITIALIZED); |
| + |
| +// Creates a random label used to identify requests. |
| +static std::string RandomLabel() { |
| + // Alphbet according to WhatWG standard, i.e. containing 36 characters from |
| + // range: U+0021, U+0023 to U+0027, U+002A to U+002B, U+002D to U+002E, |
| + // U+0030 to U+0039, U+0041 to U+005A, U+005E to U+007E. |
| + static const char alphabet[] = "!#$%&\'*+-.0123456789" |
| + "abcdefghijklmnopqrstuvwxyz^_`ABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~"; |
| + |
| + std::string label(36, ' '); |
| + for (size_t i = 0; i < label.size(); ++i) { |
| + int random_char = base::RandGenerator(sizeof(alphabet) - 1); |
| + label[i] = alphabet[random_char]; |
| + } |
| + return label; |
| +} |
| + |
| +// Helper to verify if a media stream type is part of options or not. |
| +static bool Requested(const StreamOptions& options, |
| + MediaStreamType stream_type) { |
| + if (stream_type == kVideoCapture |
| + && (options.video_option != StreamOptions::kNoCamera)) { |
|
wjia(left Chromium)
2011/07/06 02:58:42
"&&" should be at the end of previous line.
mflodman1
2011/07/06 11:31:45
Done.
|
| + return true; |
| + } else if (stream_type == kAudioCapture && options.audio == true) { |
| + return true; |
| + } |
| + return false; |
| +} |
| + |
| +MediaStreamManager* MediaStreamManager::Get() { |
| + return g_media_stream_manager.Pointer(); |
| +} |
| + |
| +MediaStreamManager::~MediaStreamManager() { |
| + delete device_settings_; |
| + delete video_capture_manager_; |
| +} |
| + |
| +VideoCaptureManager* MediaStreamManager::GetVideoCaptureManager() { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| + return video_capture_manager_; |
| +} |
| + |
| +void MediaStreamManager::GenerateStream(MediaStreamRequester* requester, |
| + int render_process_id, |
| + int render_view_id, |
| + const StreamOptions& options, |
| + const std::string& security_origin, |
| + std::string* label) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| + |
| + // TODO(mflodman) Remove next line when audio is supported. |
| + (const_cast<StreamOptions&>(options)).audio = false; |
| + |
| + // Create a new request based on options. |
| + DeviceRequest new_request = DeviceRequest(requester, options); |
| + if (Requested(new_request.options, kAudioCapture)) { |
| + new_request.state[kAudioCapture] = DeviceRequest::kRequested; |
| + } |
| + if (Requested(new_request.options, kVideoCapture)) { |
| + new_request.state[kVideoCapture] = DeviceRequest::kRequested; |
| + } |
| + |
| + // Create a label for this request and verify it is unique. |
| + std::string request_label; |
| + do { |
| + request_label = RandomLabel(); |
| + } while (requests_.find(request_label) != requests_.end()); |
| + |
| + requests_.insert(std::make_pair(request_label, new_request)); |
| + |
| + // Get user confirmation to use capture devices. |
| + device_settings_->RequestCaptureDeviceUsage(request_label, render_process_id, |
| + render_view_id, options, |
| + security_origin); |
| + (*label) = request_label; |
| +} |
| + |
| +void MediaStreamManager::CancelRequests(MediaStreamRequester* requester) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| + DeviceRequests::iterator it = requests_.begin(); |
| + while (it != requests_.end()) { |
| + if (it->second.requester == requester && !RequestDone(it->second)) { |
|
wjia(left Chromium)
2011/07/06 02:58:42
looks like a request can be cancelled if any reque
mflodman1
2011/07/06 11:31:45
Very good point! I added code here to close open d
|
| + requests_.erase(it); |
| + it = requests_.begin(); |
|
wjia(left Chromium)
2011/07/06 02:58:42
do you have to go from the beginning again? are yo
mflodman1
2011/07/06 11:31:45
You're right, there is no need to start from begin
|
| + } else { |
| + ++it; |
| + } |
| + } |
| +} |
| + |
| +void MediaStreamManager::StopGeneratedStream(const std::string& label) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| + // Find the request and close all open devices for the request. |
| + DeviceRequests::iterator it = requests_.find(label); |
| + if (it != requests_.end()) { |
| + for (StreamDeviceInfoArray::iterator audio_it = |
| + it->second.audio_devices.begin(); |
| + audio_it != it->second.audio_devices.end(); ++audio_it) { |
| + // TODO(mflodman) Add code when audio input manager exists. |
| + NOTREACHED(); |
| + } |
| + for (StreamDeviceInfoArray::iterator video_it = |
| + it->second.video_devices.begin(); |
| + video_it != it->second.video_devices.end(); ++video_it) { |
| + video_capture_manager_->Close(video_it->session_id); |
| + } |
| + requests_.erase(it); |
| + return; |
| + } |
| +} |
| + |
| +void MediaStreamManager::Opened(MediaStreamType stream_type, |
| + int capture_session_id) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| + |
| + // Find the request containing this device and mark it as used. |
| + DeviceRequest* request = NULL; |
| + StreamDeviceInfo* device = NULL; |
| + std::string label; |
| + for (DeviceRequests::iterator request_it = requests_.begin(); |
| + request_it != requests_.end() && request == NULL; ++request_it) { |
| + StreamDeviceInfoArray* devices = NULL; |
| + if (stream_type == kAudioCapture) { |
| + devices = &(request_it->second.audio_devices); |
| + } else if (stream_type == kVideoCapture) { |
| + devices = &(request_it->second.video_devices); |
| + } else { |
| + NOTREACHED(); |
| + } |
| + |
| + for (StreamDeviceInfoArray::iterator device_it = devices->begin(); |
| + device_it != devices->end(); ++device_it) { |
| + if (device_it->session_id == capture_session_id) { |
| + // We've found the request. |
| + label = request_it->first; |
| + request = &(request_it->second); |
| + device = &(*device_it); |
| + break; |
| + } |
| + } |
| + } |
| + if (request == NULL) { |
| + // The request doesn't exist. |
| + return; |
| + } |
| + |
| + DCHECK_NE(request->state[stream_type], DeviceRequest::kRequested); |
| + |
| + device->in_use = true; |
| + if (!RequestDone(*request)) { |
| + // Wait for more devices to be opened before we're done. |
| + return; |
| + } |
| + |
| + if (request->state[kAudioCapture] == DeviceRequest::kOpening) { |
| + request->state[kAudioCapture] = DeviceRequest::kDone; |
| + } |
| + if (request->state[kVideoCapture] == DeviceRequest::kOpening) { |
| + request->state[kVideoCapture] = DeviceRequest::kDone; |
| + } |
| + |
| + request->requester->StreamGenerated(label, request->audio_devices, |
| + request->video_devices); |
| +} |
| + |
| +void MediaStreamManager::Closed(MediaStreamType stream_type, |
| + int capture_session_id) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| +} |
| + |
| +void MediaStreamManager::DevicesEnumerated( |
| + MediaStreamType stream_type, const StreamDeviceInfoArray& devices) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| + |
| + // Publish the result for all requests waiting for device list(s). |
| + // Find the requests waiting for this device list, store their labels and |
| + // release the iterator before calling device settings. We might get a call |
| + // back from device_settings that will need to iterate through devices. |
| + std::list<std::string> label_list; |
| + for (DeviceRequests::iterator it = requests_.begin(); it != requests_.end(); |
| + ++it) { |
| + if (it->second.state[stream_type] == DeviceRequest::kRequested |
| + && Requested(it->second.options, stream_type)) { |
| + label_list.push_back(it->first); |
| + } |
| + } |
| + for (std::list<std::string>::iterator it = label_list.begin(); |
| + it != label_list.end(); ++it) { |
| + device_settings_->AvailableDevices(*it, stream_type, devices); |
|
wjia(left Chromium)
2011/07/06 02:58:42
the label_list and 2nd for loop are not needed if
mflodman1
2011/07/06 11:31:45
I had it like that originally, but the problem was
|
| + } |
| + label_list.clear(); |
| + enumeration_in_progress_[stream_type] = false; |
| +} |
| + |
| +void MediaStreamManager::Error(MediaStreamType stream_type, |
| + int capture_session_id, |
| + MediaStreamProviderError error) { |
| + // Find the device for the error call. |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| + for (DeviceRequests::iterator it = requests_.begin(); it != requests_.end(); |
| + ++it) { |
| + StreamDeviceInfoArray* devices = NULL; |
| + if (stream_type == kAudioCapture) { |
| + devices = &(it->second.audio_devices); |
| + } else if (stream_type == kVideoCapture) { |
| + devices = &(it->second.video_devices); |
| + } else { |
| + NOTREACHED(); |
| + } |
| + |
| + int device_idx = 0; |
| + for (StreamDeviceInfoArray::iterator device_it = devices->begin(); |
| + device_it != devices->end(); ++device_it, ++device_idx) { |
| + if (device_it->session_id == capture_session_id) { |
| + // We've found the failing device. Find the error case: |
| + if (it->second.state[stream_type] == DeviceRequest::kDone) { |
| + // 1. Already opened -> signal device failure and close device. |
| + // Use device_idx to signal which of the devices encountered an |
| + // error. |
| + if (stream_type == kAudioCapture) { |
| + it->second.requester->AudioDeviceFailed(it->first, device_idx); |
| + } else if (stream_type == kVideoCapture) { |
| + it->second.requester->VideoDeviceFailed(it->first, device_idx); |
| + } |
| + GetDeviceManager(stream_type)->Close(capture_session_id); |
| + devices->erase(device_it); |
| + } else if (it->second.audio_devices.size() |
| + + it->second.video_devices.size() <= 1) { |
| + // 2. Device not opened and no other devices for this request -> |
| + // signal stream error and remove the request. |
| + it->second.requester->StreamGenerationFailed(it->first); |
| + requests_.erase(it); |
| + } else { |
| + // 3. Not opened but other devices exists for this request -> remove |
| + // device from list, but don't signal an error. |
| + devices->erase(device_it); |
| + } |
| + return; |
| + } |
| + } |
| + } |
| +} |
| + |
| +void MediaStreamManager::GetDevices(const std::string& label, |
| + MediaStreamType stream_type) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| + if (!enumeration_in_progress_[stream_type]) { |
| + enumeration_in_progress_[stream_type] = true; |
| + GetDeviceManager(stream_type)->EnumerateDevices(); |
| + } |
| +} |
| + |
| +void MediaStreamManager::DevicesAccepted(const std::string& label, |
| + const StreamDeviceInfoArray& devices) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| + DeviceRequests::iterator request_it = requests_.find(label); |
| + if (request_it != requests_.end()) { |
|
wjia(left Chromium)
2011/07/06 02:58:42
is it possible only partial request is accepted? e
mflodman1
2011/07/06 11:31:45
Yes. No error is signalled to the requester if thi
|
| + if (devices.empty()) { |
| + // No available devices or user didn't accept device usage. |
| + request_it->second.requester->StreamGenerationFailed(request_it->first); |
| + requests_.erase(request_it); |
| + return; |
| + } |
| + |
| + // Loop through all device types for this request. |
| + for (StreamDeviceInfoArray::const_iterator device_it = devices.begin(); |
| + device_it != devices.end(); ++device_it) { |
| + StreamDeviceInfo device_info = *device_it; |
| + |
| + // Set in_use to false to be able to track if this device has been |
| + // opened. in_use might be true if the device type can be used in more |
| + // than one session. |
| + device_info.in_use = false; |
| + device_info.session_id = |
| + GetDeviceManager(device_info.stream_type)->Open(device_info); |
| + request_it->second.state[device_it->stream_type] = |
| + DeviceRequest::kOpening; |
| + if (device_info.stream_type == kAudioCapture) { |
| + request_it->second.audio_devices.push_back(device_info); |
| + } else if (device_info.stream_type == kVideoCapture) { |
| + request_it->second.video_devices.push_back(device_info); |
| + } else { |
| + NOTREACHED(); |
| + } |
| + } |
| + return; |
| + } |
| +} |
| + |
| +void MediaStreamManager::SettingsError(const std::string& label) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| + // Erase this request and report an error. |
| + DeviceRequests::iterator it = requests_.find(label); |
| + if (it != requests_.end()) { |
| + it->second.requester->StreamGenerationFailed(label); |
| + requests_.erase(it); |
| + return; |
| + } |
| +} |
| + |
| +void MediaStreamManager::UseFakeDevice() { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| + video_capture_manager_->UseFakeDevice(); |
| + // TODO(mflodman) Add audio manager when available. |
| +} |
| + |
| +bool MediaStreamManager::RequestDone(const DeviceRequest& request) const { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| + // Check if all devices are opened. |
| + if (Requested(request.options, kAudioCapture)) { |
| + for (StreamDeviceInfoArray::const_iterator it = |
| + request.audio_devices.begin(); it != request.audio_devices.end(); |
| + ++it) { |
| + if (it->in_use == false) { |
| + return false; |
| + } |
| + } |
| + } |
| + if (Requested(request.options, kVideoCapture)) { |
| + for (StreamDeviceInfoArray::const_iterator it = |
|
wjia(left Chromium)
2011/07/06 02:58:42
indentation.
mflodman1
2011/07/06 11:31:45
Done.
|
| + request.video_devices.begin(); it != request.video_devices.end(); |
| + ++it) { |
| + if (it->in_use == false) { |
| + return false; |
| + } |
| + } |
| + } |
| + return true; |
| +} |
| + |
| +// Called to get media capture device manager of specified type. |
| +MediaStreamProvider* MediaStreamManager::GetDeviceManager( |
| + MediaStreamType stream_type) const { |
| + if (stream_type == kVideoCapture) { |
| + return video_capture_manager_; |
| + } else if (stream_type == kAudioCapture) { |
| + // TODO(mflodman) Add support when audio input manager is available. |
| + NOTREACHED(); |
| + return NULL; |
| + } |
| + NOTREACHED(); |
| + return NULL; |
| +} |
| + |
| +MediaStreamManager::MediaStreamManager() |
| + : video_capture_manager_(new VideoCaptureManager()), |
| + enumeration_in_progress_(kNumMediaStreamTypes, false), |
| + requests_(), |
| + device_settings_(NULL) { |
| + device_settings_ = new MediaStreamDeviceSettings(this); |
| + video_capture_manager_->Register(this); |
| + // TODO(mflodman) Add when audio input manager is available. |
| +} |
| + |
| +MediaStreamManager::DeviceRequest::DeviceRequest() |
| + : requester(NULL), |
| + state(kNumMediaStreamTypes, kNotRequested) { |
| + options.audio = false; |
| + options.video_option = StreamOptions::kNoCamera; |
| +} |
| + |
| +MediaStreamManager::DeviceRequest::DeviceRequest( |
| + MediaStreamRequester* requester, const StreamOptions& request_options) |
| + : requester(requester), |
| + options(request_options), |
| + state(kNumMediaStreamTypes, kNotRequested) { |
| + DCHECK(requester); |
| +} |
| + |
| +} // namespace media_stream |