Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(6684)

Unified Diff: content/browser/renderer_host/media/media_stream_manager.cc

Issue 7284037: Adding MediaStreamManager. (Closed) Base URL: http://src.chromium.org/svn/trunk/src/
Patch Set: Added DeviceRequest dtor, needed for clang. Created 9 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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,426 @@
+// 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(
scherkus (not reviewing) 2011/07/08 20:56:11 drive by... can you address this soon? this is on
+ 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)) {
+ 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_;
scherkus (not reviewing) 2011/07/08 20:56:11 use scoped_ptr<> in .h
+ delete video_capture_manager_;
scherkus (not reviewing) 2011/07/08 20:56:11 use scoped_ptr<> in .h
+}
+
+VideoCaptureManager* MediaStreamManager::video_capture_manager() {
+ 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)) {
+ // The request isn't complete, but there might be some devices already
+ // opened -> close them.
+ DeviceRequest* request = &(it->second);
+ if (request->state[kAudioCapture] == DeviceRequest::kOpening) {
+ for (StreamDeviceInfoArray::iterator it =
+ request->audio_devices.begin(); it != request->audio_devices.end();
+ ++it) {
+ if (it->in_use == true) {
+ // TODO(mflodman) Add when audio input device manager is available.
+ }
+ }
+ }
+ if (request->state[kVideoCapture] == DeviceRequest::kOpening) {
+ for (StreamDeviceInfoArray::iterator it =
+ request->video_devices.begin(); it != request->video_devices.end();
+ ++it) {
+ if (it->in_use == true) {
+ video_capture_manager_->Close(it->session_id);
+ }
+ }
+ }
+ requests_.erase(it++);
+ } 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;
+ StreamDeviceInfoArray* devices = NULL;
+ std::string label;
+ for (DeviceRequests::iterator request_it = requests_.begin();
+ request_it != requests_.end() && request == NULL; ++request_it) {
+ 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.
+ device_it->in_use = true;
+ label = request_it->first;
+ request = &(request_it->second);
+ break;
+ }
+ }
+ }
+ if (request == NULL) {
+ // The request doesn't exist.
+ return;
+ }
+
+ DCHECK_NE(request->state[stream_type], DeviceRequest::kRequested);
+
+ // Check if all devices for this stream type are opened. Update the state if
+ // they are.
+ for (StreamDeviceInfoArray::iterator device_it = devices->begin();
+ device_it != devices->end(); ++device_it) {
+ if (device_it->in_use == false) {
+ // Wait for more devices to be opened before we're done.
+ return;
+ }
+ }
+ request->state[stream_type] = DeviceRequest::kDone;
+
+ if (!RequestDone(*request)) {
+ // This stream_type is done, but not the other type.
+ return;
+ }
+
+ 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);
+ }
+ 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()) {
+ 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();
+ }
+ }
+ // Check if we received all stream types requested.
+ if (Requested(request_it->second.options, kAudioCapture) &&
+ request_it->second.audio_devices.size() == 0) {
+ request_it->second.state[kAudioCapture] = DeviceRequest::kError;
+ }
+ if (Requested(request_it->second.options, kVideoCapture) &&
+ request_it->second.video_devices.size() == 0) {
+ request_it->second.state[kVideoCapture] = DeviceRequest::kError;
+ }
+ 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 =
+ 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);
+}
+
+MediaStreamManager::DeviceRequest::~DeviceRequest() {}
+
+} // namespace media_stream

Powered by Google App Engine
This is Rietveld 408576698