Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "content/browser/renderer_host/media/media_stream_manager.h" | |
| 6 | |
| 7 #include <list> | |
| 8 | |
| 9 #include "base/lazy_instance.h" | |
| 10 #include "base/logging.h" | |
| 11 #include "base/rand_util.h" | |
| 12 #include "content/browser/browser_thread.h" | |
| 13 #include "content/browser/renderer_host/media/media_stream_device_settings.h" | |
| 14 #include "content/browser/renderer_host/media/media_stream_requester.h" | |
| 15 #include "content/browser/renderer_host/media/video_capture_manager.h" | |
| 16 #include "content/common/media/media_stream_options.h" | |
| 17 | |
| 18 namespace media_stream { | |
| 19 | |
| 20 // TODO(mflodman) Find out who should own MediaStreamManager. | |
| 21 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
| |
| 22 base::LINKER_INITIALIZED); | |
| 23 | |
| 24 // Creates a random label used to identify requests. | |
| 25 static std::string RandomLabel() { | |
| 26 // Alphbet according to WhatWG standard, i.e. containing 36 characters from | |
| 27 // range: U+0021, U+0023 to U+0027, U+002A to U+002B, U+002D to U+002E, | |
| 28 // U+0030 to U+0039, U+0041 to U+005A, U+005E to U+007E. | |
| 29 static const char alphabet[] = "!#$%&\'*+-.0123456789" | |
| 30 "abcdefghijklmnopqrstuvwxyz^_`ABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~"; | |
| 31 | |
| 32 std::string label(36, ' '); | |
| 33 for (size_t i = 0; i < label.size(); ++i) { | |
| 34 int random_char = base::RandGenerator(sizeof(alphabet) - 1); | |
| 35 label[i] = alphabet[random_char]; | |
| 36 } | |
| 37 return label; | |
| 38 } | |
| 39 | |
| 40 // Helper to verify if a media stream type is part of options or not. | |
| 41 static bool Requested(const StreamOptions& options, | |
| 42 MediaStreamType stream_type) { | |
| 43 if (stream_type == kVideoCapture && | |
| 44 (options.video_option != StreamOptions::kNoCamera)) { | |
| 45 return true; | |
| 46 } else if (stream_type == kAudioCapture && options.audio == true) { | |
| 47 return true; | |
| 48 } | |
| 49 return false; | |
| 50 } | |
| 51 | |
| 52 MediaStreamManager* MediaStreamManager::Get() { | |
| 53 return g_media_stream_manager.Pointer(); | |
| 54 } | |
| 55 | |
| 56 MediaStreamManager::~MediaStreamManager() { | |
| 57 delete device_settings_; | |
|
scherkus (not reviewing)
2011/07/08 20:56:11
use scoped_ptr<> in .h
| |
| 58 delete video_capture_manager_; | |
|
scherkus (not reviewing)
2011/07/08 20:56:11
use scoped_ptr<> in .h
| |
| 59 } | |
| 60 | |
| 61 VideoCaptureManager* MediaStreamManager::video_capture_manager() { | |
| 62 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 63 return video_capture_manager_; | |
| 64 } | |
| 65 | |
| 66 void MediaStreamManager::GenerateStream(MediaStreamRequester* requester, | |
| 67 int render_process_id, | |
| 68 int render_view_id, | |
| 69 const StreamOptions& options, | |
| 70 const std::string& security_origin, | |
| 71 std::string* label) { | |
| 72 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 73 | |
| 74 // TODO(mflodman) Remove next line when audio is supported. | |
| 75 (const_cast<StreamOptions&>(options)).audio = false; | |
| 76 | |
| 77 // Create a new request based on options. | |
| 78 DeviceRequest new_request = DeviceRequest(requester, options); | |
| 79 if (Requested(new_request.options, kAudioCapture)) { | |
| 80 new_request.state[kAudioCapture] = DeviceRequest::kRequested; | |
| 81 } | |
| 82 if (Requested(new_request.options, kVideoCapture)) { | |
| 83 new_request.state[kVideoCapture] = DeviceRequest::kRequested; | |
| 84 } | |
| 85 | |
| 86 // Create a label for this request and verify it is unique. | |
| 87 std::string request_label; | |
| 88 do { | |
| 89 request_label = RandomLabel(); | |
| 90 } while (requests_.find(request_label) != requests_.end()); | |
| 91 | |
| 92 requests_.insert(std::make_pair(request_label, new_request)); | |
| 93 | |
| 94 // Get user confirmation to use capture devices. | |
| 95 device_settings_->RequestCaptureDeviceUsage(request_label, render_process_id, | |
| 96 render_view_id, options, | |
| 97 security_origin); | |
| 98 (*label) = request_label; | |
| 99 } | |
| 100 | |
| 101 void MediaStreamManager::CancelRequests(MediaStreamRequester* requester) { | |
| 102 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 103 DeviceRequests::iterator it = requests_.begin(); | |
| 104 while (it != requests_.end()) { | |
| 105 if (it->second.requester == requester && !RequestDone(it->second)) { | |
| 106 // The request isn't complete, but there might be some devices already | |
| 107 // opened -> close them. | |
| 108 DeviceRequest* request = &(it->second); | |
| 109 if (request->state[kAudioCapture] == DeviceRequest::kOpening) { | |
| 110 for (StreamDeviceInfoArray::iterator it = | |
| 111 request->audio_devices.begin(); it != request->audio_devices.end(); | |
| 112 ++it) { | |
| 113 if (it->in_use == true) { | |
| 114 // TODO(mflodman) Add when audio input device manager is available. | |
| 115 } | |
| 116 } | |
| 117 } | |
| 118 if (request->state[kVideoCapture] == DeviceRequest::kOpening) { | |
| 119 for (StreamDeviceInfoArray::iterator it = | |
| 120 request->video_devices.begin(); it != request->video_devices.end(); | |
| 121 ++it) { | |
| 122 if (it->in_use == true) { | |
| 123 video_capture_manager_->Close(it->session_id); | |
| 124 } | |
| 125 } | |
| 126 } | |
| 127 requests_.erase(it++); | |
| 128 } else { | |
| 129 ++it; | |
| 130 } | |
| 131 } | |
| 132 } | |
| 133 | |
| 134 void MediaStreamManager::StopGeneratedStream(const std::string& label) { | |
| 135 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 136 // Find the request and close all open devices for the request. | |
| 137 DeviceRequests::iterator it = requests_.find(label); | |
| 138 if (it != requests_.end()) { | |
| 139 for (StreamDeviceInfoArray::iterator audio_it = | |
| 140 it->second.audio_devices.begin(); | |
| 141 audio_it != it->second.audio_devices.end(); ++audio_it) { | |
| 142 // TODO(mflodman) Add code when audio input manager exists. | |
| 143 NOTREACHED(); | |
| 144 } | |
| 145 for (StreamDeviceInfoArray::iterator video_it = | |
| 146 it->second.video_devices.begin(); | |
| 147 video_it != it->second.video_devices.end(); ++video_it) { | |
| 148 video_capture_manager_->Close(video_it->session_id); | |
| 149 } | |
| 150 requests_.erase(it); | |
| 151 return; | |
| 152 } | |
| 153 } | |
| 154 | |
| 155 void MediaStreamManager::Opened(MediaStreamType stream_type, | |
| 156 int capture_session_id) { | |
| 157 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 158 | |
| 159 // Find the request containing this device and mark it as used. | |
| 160 DeviceRequest* request = NULL; | |
| 161 StreamDeviceInfoArray* devices = NULL; | |
| 162 std::string label; | |
| 163 for (DeviceRequests::iterator request_it = requests_.begin(); | |
| 164 request_it != requests_.end() && request == NULL; ++request_it) { | |
| 165 if (stream_type == kAudioCapture) { | |
| 166 devices = &(request_it->second.audio_devices); | |
| 167 } else if (stream_type == kVideoCapture) { | |
| 168 devices = &(request_it->second.video_devices); | |
| 169 } else { | |
| 170 NOTREACHED(); | |
| 171 } | |
| 172 | |
| 173 for (StreamDeviceInfoArray::iterator device_it = devices->begin(); | |
| 174 device_it != devices->end(); ++device_it) { | |
| 175 if (device_it->session_id == capture_session_id) { | |
| 176 // We've found the request. | |
| 177 device_it->in_use = true; | |
| 178 label = request_it->first; | |
| 179 request = &(request_it->second); | |
| 180 break; | |
| 181 } | |
| 182 } | |
| 183 } | |
| 184 if (request == NULL) { | |
| 185 // The request doesn't exist. | |
| 186 return; | |
| 187 } | |
| 188 | |
| 189 DCHECK_NE(request->state[stream_type], DeviceRequest::kRequested); | |
| 190 | |
| 191 // Check if all devices for this stream type are opened. Update the state if | |
| 192 // they are. | |
| 193 for (StreamDeviceInfoArray::iterator device_it = devices->begin(); | |
| 194 device_it != devices->end(); ++device_it) { | |
| 195 if (device_it->in_use == false) { | |
| 196 // Wait for more devices to be opened before we're done. | |
| 197 return; | |
| 198 } | |
| 199 } | |
| 200 request->state[stream_type] = DeviceRequest::kDone; | |
| 201 | |
| 202 if (!RequestDone(*request)) { | |
| 203 // This stream_type is done, but not the other type. | |
| 204 return; | |
| 205 } | |
| 206 | |
| 207 request->requester->StreamGenerated(label, request->audio_devices, | |
| 208 request->video_devices); | |
| 209 } | |
| 210 | |
| 211 void MediaStreamManager::Closed(MediaStreamType stream_type, | |
| 212 int capture_session_id) { | |
| 213 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 214 } | |
| 215 | |
| 216 void MediaStreamManager::DevicesEnumerated( | |
| 217 MediaStreamType stream_type, const StreamDeviceInfoArray& devices) { | |
| 218 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 219 | |
| 220 // Publish the result for all requests waiting for device list(s). | |
| 221 // Find the requests waiting for this device list, store their labels and | |
| 222 // release the iterator before calling device settings. We might get a call | |
| 223 // back from device_settings that will need to iterate through devices. | |
| 224 std::list<std::string> label_list; | |
| 225 for (DeviceRequests::iterator it = requests_.begin(); it != requests_.end(); | |
| 226 ++it) { | |
| 227 if (it->second.state[stream_type] == DeviceRequest::kRequested && | |
| 228 Requested(it->second.options, stream_type)) { | |
| 229 label_list.push_back(it->first); | |
| 230 } | |
| 231 } | |
| 232 for (std::list<std::string>::iterator it = label_list.begin(); | |
| 233 it != label_list.end(); ++it) { | |
| 234 device_settings_->AvailableDevices(*it, stream_type, devices); | |
| 235 } | |
| 236 label_list.clear(); | |
| 237 enumeration_in_progress_[stream_type] = false; | |
| 238 } | |
| 239 | |
| 240 void MediaStreamManager::Error(MediaStreamType stream_type, | |
| 241 int capture_session_id, | |
| 242 MediaStreamProviderError error) { | |
| 243 // Find the device for the error call. | |
| 244 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 245 for (DeviceRequests::iterator it = requests_.begin(); it != requests_.end(); | |
| 246 ++it) { | |
| 247 StreamDeviceInfoArray* devices = NULL; | |
| 248 if (stream_type == kAudioCapture) { | |
| 249 devices = &(it->second.audio_devices); | |
| 250 } else if (stream_type == kVideoCapture) { | |
| 251 devices = &(it->second.video_devices); | |
| 252 } else { | |
| 253 NOTREACHED(); | |
| 254 } | |
| 255 | |
| 256 int device_idx = 0; | |
| 257 for (StreamDeviceInfoArray::iterator device_it = devices->begin(); | |
| 258 device_it != devices->end(); ++device_it, ++device_idx) { | |
| 259 if (device_it->session_id == capture_session_id) { | |
| 260 // We've found the failing device. Find the error case: | |
| 261 if (it->second.state[stream_type] == DeviceRequest::kDone) { | |
| 262 // 1. Already opened -> signal device failure and close device. | |
| 263 // Use device_idx to signal which of the devices encountered an | |
| 264 // error. | |
| 265 if (stream_type == kAudioCapture) { | |
| 266 it->second.requester->AudioDeviceFailed(it->first, device_idx); | |
| 267 } else if (stream_type == kVideoCapture) { | |
| 268 it->second.requester->VideoDeviceFailed(it->first, device_idx); | |
| 269 } | |
| 270 GetDeviceManager(stream_type)->Close(capture_session_id); | |
| 271 devices->erase(device_it); | |
| 272 } else if (it->second.audio_devices.size() | |
| 273 + it->second.video_devices.size() <= 1) { | |
| 274 // 2. Device not opened and no other devices for this request -> | |
| 275 // signal stream error and remove the request. | |
| 276 it->second.requester->StreamGenerationFailed(it->first); | |
| 277 requests_.erase(it); | |
| 278 } else { | |
| 279 // 3. Not opened but other devices exists for this request -> remove | |
| 280 // device from list, but don't signal an error. | |
| 281 devices->erase(device_it); | |
| 282 } | |
| 283 return; | |
| 284 } | |
| 285 } | |
| 286 } | |
| 287 } | |
| 288 | |
| 289 void MediaStreamManager::GetDevices(const std::string& label, | |
| 290 MediaStreamType stream_type) { | |
| 291 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 292 if (!enumeration_in_progress_[stream_type]) { | |
| 293 enumeration_in_progress_[stream_type] = true; | |
| 294 GetDeviceManager(stream_type)->EnumerateDevices(); | |
| 295 } | |
| 296 } | |
| 297 | |
| 298 void MediaStreamManager::DevicesAccepted(const std::string& label, | |
| 299 const StreamDeviceInfoArray& devices) { | |
| 300 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 301 DeviceRequests::iterator request_it = requests_.find(label); | |
| 302 if (request_it != requests_.end()) { | |
| 303 if (devices.empty()) { | |
| 304 // No available devices or user didn't accept device usage. | |
| 305 request_it->second.requester->StreamGenerationFailed(request_it->first); | |
| 306 requests_.erase(request_it); | |
| 307 return; | |
| 308 } | |
| 309 | |
| 310 // Loop through all device types for this request. | |
| 311 for (StreamDeviceInfoArray::const_iterator device_it = devices.begin(); | |
| 312 device_it != devices.end(); ++device_it) { | |
| 313 StreamDeviceInfo device_info = *device_it; | |
| 314 | |
| 315 // Set in_use to false to be able to track if this device has been | |
| 316 // opened. in_use might be true if the device type can be used in more | |
| 317 // than one session. | |
| 318 device_info.in_use = false; | |
| 319 device_info.session_id = | |
| 320 GetDeviceManager(device_info.stream_type)->Open(device_info); | |
| 321 request_it->second.state[device_it->stream_type] = | |
| 322 DeviceRequest::kOpening; | |
| 323 if (device_info.stream_type == kAudioCapture) { | |
| 324 request_it->second.audio_devices.push_back(device_info); | |
| 325 } else if (device_info.stream_type == kVideoCapture) { | |
| 326 request_it->second.video_devices.push_back(device_info); | |
| 327 } else { | |
| 328 NOTREACHED(); | |
| 329 } | |
| 330 } | |
| 331 // Check if we received all stream types requested. | |
| 332 if (Requested(request_it->second.options, kAudioCapture) && | |
| 333 request_it->second.audio_devices.size() == 0) { | |
| 334 request_it->second.state[kAudioCapture] = DeviceRequest::kError; | |
| 335 } | |
| 336 if (Requested(request_it->second.options, kVideoCapture) && | |
| 337 request_it->second.video_devices.size() == 0) { | |
| 338 request_it->second.state[kVideoCapture] = DeviceRequest::kError; | |
| 339 } | |
| 340 return; | |
| 341 } | |
| 342 } | |
| 343 | |
| 344 void MediaStreamManager::SettingsError(const std::string& label) { | |
| 345 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 346 // Erase this request and report an error. | |
| 347 DeviceRequests::iterator it = requests_.find(label); | |
| 348 if (it != requests_.end()) { | |
| 349 it->second.requester->StreamGenerationFailed(label); | |
| 350 requests_.erase(it); | |
| 351 return; | |
| 352 } | |
| 353 } | |
| 354 | |
| 355 void MediaStreamManager::UseFakeDevice() { | |
| 356 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 357 video_capture_manager_->UseFakeDevice(); | |
| 358 // TODO(mflodman) Add audio manager when available. | |
| 359 } | |
| 360 | |
| 361 bool MediaStreamManager::RequestDone(const DeviceRequest& request) const { | |
| 362 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 363 // Check if all devices are opened. | |
| 364 if (Requested(request.options, kAudioCapture)) { | |
| 365 for (StreamDeviceInfoArray::const_iterator it = | |
| 366 request.audio_devices.begin(); it != request.audio_devices.end(); | |
| 367 ++it) { | |
| 368 if (it->in_use == false) { | |
| 369 return false; | |
| 370 } | |
| 371 } | |
| 372 } | |
| 373 if (Requested(request.options, kVideoCapture)) { | |
| 374 for (StreamDeviceInfoArray::const_iterator it = | |
| 375 request.video_devices.begin(); it != request.video_devices.end(); | |
| 376 ++it) { | |
| 377 if (it->in_use == false) { | |
| 378 return false; | |
| 379 } | |
| 380 } | |
| 381 } | |
| 382 return true; | |
| 383 } | |
| 384 | |
| 385 // Called to get media capture device manager of specified type. | |
| 386 MediaStreamProvider* MediaStreamManager::GetDeviceManager( | |
| 387 MediaStreamType stream_type) const { | |
| 388 if (stream_type == kVideoCapture) { | |
| 389 return video_capture_manager_; | |
| 390 } else if (stream_type == kAudioCapture) { | |
| 391 // TODO(mflodman) Add support when audio input manager is available. | |
| 392 NOTREACHED(); | |
| 393 return NULL; | |
| 394 } | |
| 395 NOTREACHED(); | |
| 396 return NULL; | |
| 397 } | |
| 398 | |
| 399 MediaStreamManager::MediaStreamManager() | |
| 400 : video_capture_manager_(new VideoCaptureManager()), | |
| 401 enumeration_in_progress_(kNumMediaStreamTypes, false), | |
| 402 requests_(), | |
| 403 device_settings_(NULL) { | |
| 404 device_settings_ = new MediaStreamDeviceSettings(this); | |
| 405 video_capture_manager_->Register(this); | |
| 406 // TODO(mflodman) Add when audio input manager is available. | |
| 407 } | |
| 408 | |
| 409 MediaStreamManager::DeviceRequest::DeviceRequest() | |
| 410 : requester(NULL), | |
| 411 state(kNumMediaStreamTypes, kNotRequested) { | |
| 412 options.audio = false; | |
| 413 options.video_option = StreamOptions::kNoCamera; | |
| 414 } | |
| 415 | |
| 416 MediaStreamManager::DeviceRequest::DeviceRequest( | |
| 417 MediaStreamRequester* requester, const StreamOptions& request_options) | |
| 418 : requester(requester), | |
| 419 options(request_options), | |
| 420 state(kNumMediaStreamTypes, kNotRequested) { | |
| 421 DCHECK(requester); | |
| 422 } | |
| 423 | |
| 424 MediaStreamManager::DeviceRequest::~DeviceRequest() {} | |
| 425 | |
| 426 } // namespace media_stream | |
| OLD | NEW |