| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/browser/renderer_host/media/media_stream_manager.h" | 5 #include "content/browser/renderer_host/media/media_stream_manager.h" |
| 6 | 6 |
| 7 #include <list> | 7 #include <list> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/compiler_specific.h" | 10 #include "base/compiler_specific.h" |
| 11 #include "base/logging.h" | 11 #include "base/logging.h" |
| 12 #include "base/rand_util.h" | 12 #include "base/rand_util.h" |
| 13 #include "base/win/scoped_com_initializer.h" | 13 #include "base/win/scoped_com_initializer.h" |
| 14 #include "content/browser/browser_main_loop.h" |
| 14 #include "content/browser/renderer_host/media/audio_input_device_manager.h" | 15 #include "content/browser/renderer_host/media/audio_input_device_manager.h" |
| 15 #include "content/browser/renderer_host/media/media_stream_device_settings.h" | 16 #include "content/browser/renderer_host/media/media_stream_device_settings.h" |
| 16 #include "content/browser/renderer_host/media/media_stream_requester.h" | 17 #include "content/browser/renderer_host/media/media_stream_requester.h" |
| 17 #include "content/browser/renderer_host/media/video_capture_manager.h" | 18 #include "content/browser/renderer_host/media/video_capture_manager.h" |
| 18 #include "content/common/media/media_stream_options.h" | 19 #include "content/common/media/media_stream_options.h" |
| 19 #include "content/public/browser/browser_thread.h" | 20 #include "content/public/browser/browser_thread.h" |
| 20 #include "content/public/browser/content_browser_client.h" | 21 #include "content/public/browser/content_browser_client.h" |
| 21 #include "content/public/browser/media_observer.h" | 22 #include "content/public/browser/media_observer.h" |
| 22 #include "googleurl/src/gurl.h" | 23 #include "googleurl/src/gurl.h" |
| 23 | 24 |
| (...skipping 16 matching lines...) Expand all Loading... |
| 40 for (size_t i = 0; i < label.size(); ++i) { | 41 for (size_t i = 0; i < label.size(); ++i) { |
| 41 int random_char = base::RandGenerator(sizeof(kAlphabet) - 1); | 42 int random_char = base::RandGenerator(sizeof(kAlphabet) - 1); |
| 42 label[i] = kAlphabet[random_char]; | 43 label[i] = kAlphabet[random_char]; |
| 43 } | 44 } |
| 44 return label; | 45 return label; |
| 45 } | 46 } |
| 46 | 47 |
| 47 // Helper to verify if a media stream type is part of options or not. | 48 // Helper to verify if a media stream type is part of options or not. |
| 48 static bool Requested(const StreamOptions& options, | 49 static bool Requested(const StreamOptions& options, |
| 49 MediaStreamType stream_type) { | 50 MediaStreamType stream_type) { |
| 50 return (stream_type == content::MEDIA_STREAM_DEVICE_TYPE_VIDEO_CAPTURE && | 51 return (options.audio_type == stream_type || |
| 51 options.video) || | 52 options.video_type == stream_type); |
| 52 (stream_type == content::MEDIA_STREAM_DEVICE_TYPE_AUDIO_CAPTURE && | |
| 53 options.audio); | |
| 54 } | 53 } |
| 55 | 54 |
| 56 DeviceThread::DeviceThread(const char* name) | 55 DeviceThread::DeviceThread(const char* name) |
| 57 : base::Thread(name) { | 56 : base::Thread(name) { |
| 58 } | 57 } |
| 59 | 58 |
| 60 DeviceThread::~DeviceThread() { | 59 DeviceThread::~DeviceThread() { |
| 61 Stop(); | 60 Stop(); |
| 62 } | 61 } |
| 63 | 62 |
| (...skipping 23 matching lines...) Expand all Loading... |
| 87 ENUMERATE_DEVICES, | 86 ENUMERATE_DEVICES, |
| 88 OPEN_DEVICE | 87 OPEN_DEVICE |
| 89 }; | 88 }; |
| 90 | 89 |
| 91 DeviceRequest() | 90 DeviceRequest() |
| 92 : requester(NULL), | 91 : requester(NULL), |
| 93 state(content::NUM_MEDIA_STREAM_DEVICE_TYPES, STATE_NOT_REQUESTED), | 92 state(content::NUM_MEDIA_STREAM_DEVICE_TYPES, STATE_NOT_REQUESTED), |
| 94 type(GENERATE_STREAM), | 93 type(GENERATE_STREAM), |
| 95 render_process_id(-1), | 94 render_process_id(-1), |
| 96 render_view_id(-1) { | 95 render_view_id(-1) { |
| 97 options.audio = false; | |
| 98 options.video = false; | |
| 99 } | 96 } |
| 100 | 97 |
| 101 DeviceRequest(MediaStreamRequester* requester, | 98 DeviceRequest(MediaStreamRequester* requester, |
| 102 const StreamOptions& request_options, | 99 const StreamOptions& request_options, |
| 103 int render_process_id, | 100 int render_process_id, |
| 104 int render_view_id, | 101 int render_view_id, |
| 105 const GURL& request_security_origin) | 102 const GURL& request_security_origin) |
| 106 : requester(requester), | 103 : requester(requester), |
| 107 options(request_options), | 104 options(request_options), |
| 108 state(content::NUM_MEDIA_STREAM_DEVICE_TYPES, STATE_NOT_REQUESTED), | 105 state(content::NUM_MEDIA_STREAM_DEVICE_TYPES, STATE_NOT_REQUESTED), |
| 109 type(GENERATE_STREAM), | 106 type(GENERATE_STREAM), |
| 110 render_process_id(render_process_id), | 107 render_process_id(render_process_id), |
| 111 render_view_id(render_view_id), | 108 render_view_id(render_view_id), |
| 112 security_origin(request_security_origin) { | 109 security_origin(request_security_origin) { |
| 113 DCHECK(requester); | 110 DCHECK(requester); |
| 114 } | 111 } |
| 115 | 112 |
| 116 ~DeviceRequest() {} | 113 ~DeviceRequest() {} |
| 117 | 114 |
| 118 MediaStreamRequester* requester; | 115 MediaStreamRequester* requester; |
| 119 StreamOptions options; | 116 StreamOptions options; |
| 120 std::vector<RequestState> state; | 117 std::vector<RequestState> state; |
| 121 RequestType type; | 118 RequestType type; |
| 122 int render_process_id; | 119 int render_process_id; |
| 123 int render_view_id; | 120 int render_view_id; |
| 124 GURL security_origin; | 121 GURL security_origin; |
| 125 std::string requested_device_id; | 122 StreamDeviceInfoArray devices; |
| 126 StreamDeviceInfoArray audio_devices; | |
| 127 StreamDeviceInfoArray video_devices; | |
| 128 }; | 123 }; |
| 129 | 124 |
| 130 MediaStreamManager::EnumerationCache::EnumerationCache() | 125 MediaStreamManager::EnumerationCache::EnumerationCache() |
| 131 : valid(false) { | 126 : valid(false) { |
| 132 } | 127 } |
| 133 | 128 |
| 134 MediaStreamManager::EnumerationCache::~EnumerationCache() { | 129 MediaStreamManager::EnumerationCache::~EnumerationCache() { |
| 135 } | 130 } |
| 136 | 131 |
| 137 MediaStreamManager::MediaStreamManager( | 132 MediaStreamManager::MediaStreamManager() |
| 138 AudioInputDeviceManager* audio_input_device_manager, | |
| 139 VideoCaptureManager* video_capture_manager) | |
| 140 : ALLOW_THIS_IN_INITIALIZER_LIST( | 133 : ALLOW_THIS_IN_INITIALIZER_LIST( |
| 141 device_settings_(new MediaStreamDeviceSettings(this))), | 134 device_settings_(new MediaStreamDeviceSettings(this))), |
| 142 audio_input_device_manager_(audio_input_device_manager), | 135 audio_manager_(NULL), |
| 143 video_capture_manager_(video_capture_manager), | |
| 144 monitoring_started_(false), | 136 monitoring_started_(false), |
| 145 io_loop_(NULL) { | 137 io_loop_(NULL) { |
| 146 memset(active_enumeration_ref_count_, 0, | 138 memset(active_enumeration_ref_count_, 0, |
| 147 sizeof(active_enumeration_ref_count_)); | 139 sizeof(active_enumeration_ref_count_)); |
| 148 } | 140 } |
| 149 | 141 |
| 150 MediaStreamManager::~MediaStreamManager() { | 142 MediaStreamManager::~MediaStreamManager() { |
| 151 DCHECK(requests_.empty()); | 143 DCHECK(requests_.empty()); |
| 152 DCHECK(!device_thread_.get()); | 144 DCHECK(!device_thread_.get()); |
| 153 DCHECK(!io_loop_); | 145 DCHECK(!io_loop_); |
| 154 } | 146 } |
| 155 | 147 |
| 156 VideoCaptureManager* MediaStreamManager::video_capture_manager() { | |
| 157 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 158 DCHECK(video_capture_manager_); | |
| 159 EnsureDeviceThreadAndListener(); | |
| 160 return video_capture_manager_; | |
| 161 } | |
| 162 | |
| 163 AudioInputDeviceManager* MediaStreamManager::audio_input_device_manager() { | |
| 164 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 165 DCHECK(audio_input_device_manager_); | |
| 166 EnsureDeviceThreadAndListener(); | |
| 167 return audio_input_device_manager_; | |
| 168 } | |
| 169 | |
| 170 void MediaStreamManager::GenerateStream(MediaStreamRequester* requester, | 148 void MediaStreamManager::GenerateStream(MediaStreamRequester* requester, |
| 171 int render_process_id, | 149 int render_process_id, |
| 172 int render_view_id, | 150 int render_view_id, |
| 173 const StreamOptions& options, | 151 const StreamOptions& options, |
| 174 const GURL& security_origin, | 152 const GURL& security_origin, |
| 175 std::string* label) { | 153 std::string* label) { |
| 176 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 154 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 177 | 155 |
| 178 // Create a new request based on options. | 156 // Create a new request based on options. |
| 179 DeviceRequest new_request(requester, options, | 157 DeviceRequest new_request(requester, options, |
| (...skipping 10 matching lines...) Expand all Loading... |
| 190 security_origin); | 168 security_origin); |
| 191 } | 169 } |
| 192 | 170 |
| 193 void MediaStreamManager::CancelGenerateStream(const std::string& label) { | 171 void MediaStreamManager::CancelGenerateStream(const std::string& label) { |
| 194 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 172 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 195 | 173 |
| 196 DeviceRequests::iterator it = requests_.find(label); | 174 DeviceRequests::iterator it = requests_.find(label); |
| 197 if (it != requests_.end()) { | 175 if (it != requests_.end()) { |
| 198 // The request isn't complete. | 176 // The request isn't complete. |
| 199 if (!RequestDone(it->second)) { | 177 if (!RequestDone(it->second)) { |
| 200 DeviceRequest* request = &(it->second); | 178 DeviceRequest& request = it->second; |
| 201 if (request->state[content::MEDIA_STREAM_DEVICE_TYPE_AUDIO_CAPTURE] == | 179 for (int i = content::MEDIA_STREAM_DEVICE_TYPE_NO_SERVICE + 1; |
| 202 DeviceRequest::STATE_OPENING) { | 180 i < content::NUM_MEDIA_STREAM_DEVICE_TYPES; ++i) { |
| 203 for (StreamDeviceInfoArray::iterator it = | 181 const MediaStreamType stream_type = static_cast<MediaStreamType>(i); |
| 204 request->audio_devices.begin(); it != request->audio_devices.end(); | 182 if (request.state[stream_type] != DeviceRequest::STATE_OPENING) { |
| 205 ++it) { | 183 continue; |
| 206 audio_input_device_manager()->Close(it->session_id); | |
| 207 } | 184 } |
| 208 } | 185 for (StreamDeviceInfoArray::iterator device_it = |
| 209 if (request->state[content::MEDIA_STREAM_DEVICE_TYPE_VIDEO_CAPTURE] == | 186 request.devices.begin(); |
| 210 DeviceRequest::STATE_OPENING) { | 187 device_it != request.devices.end(); ++device_it) { |
| 211 for (StreamDeviceInfoArray::iterator it = | 188 if (device_it->stream_type == stream_type) { |
| 212 request->video_devices.begin(); it != request->video_devices.end(); | 189 GetDeviceManager(stream_type)->Close(device_it->session_id); |
| 213 ++it) { | 190 } |
| 214 video_capture_manager()->Close(it->session_id); | |
| 215 } | 191 } |
| 216 } | 192 } |
| 217 requests_.erase(it); | 193 requests_.erase(it); |
| 218 } else { | 194 } else { |
| 219 StopGeneratedStream(label); | 195 StopGeneratedStream(label); |
| 220 } | 196 } |
| 221 device_settings_->RemovePendingCaptureRequest(label); | 197 device_settings_->RemovePendingCaptureRequest(label); |
| 222 } | 198 } |
| 223 } | 199 } |
| 224 | 200 |
| 225 void MediaStreamManager::StopGeneratedStream(const std::string& label) { | 201 void MediaStreamManager::StopGeneratedStream(const std::string& label) { |
| 226 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 202 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 227 // Find the request and close all open devices for the request. | 203 // Find the request and close all open devices for the request. |
| 228 DeviceRequests::iterator it = requests_.find(label); | 204 DeviceRequests::iterator it = requests_.find(label); |
| 229 if (it != requests_.end()) { | 205 if (it != requests_.end()) { |
| 230 if (it->second.type == DeviceRequest::ENUMERATE_DEVICES) { | 206 if (it->second.type == DeviceRequest::ENUMERATE_DEVICES) { |
| 231 StopEnumerateDevices(label); | 207 StopEnumerateDevices(label); |
| 232 return; | 208 return; |
| 233 } | 209 } |
| 234 for (StreamDeviceInfoArray::iterator audio_it = | 210 for (StreamDeviceInfoArray::iterator device_it = it->second.devices.begin(); |
| 235 it->second.audio_devices.begin(); | 211 device_it != it->second.devices.end(); ++device_it) { |
| 236 audio_it != it->second.audio_devices.end(); ++audio_it) { | 212 GetDeviceManager(device_it->stream_type)->Close(device_it->session_id); |
| 237 audio_input_device_manager()->Close(audio_it->session_id); | |
| 238 } | |
| 239 for (StreamDeviceInfoArray::iterator video_it = | |
| 240 it->second.video_devices.begin(); | |
| 241 video_it != it->second.video_devices.end(); ++video_it) { | |
| 242 video_capture_manager()->Close(video_it->session_id); | |
| 243 } | 213 } |
| 244 if (it->second.type == DeviceRequest::GENERATE_STREAM && | 214 if (it->second.type == DeviceRequest::GENERATE_STREAM && |
| 245 RequestDone(it->second)) { | 215 RequestDone(it->second)) { |
| 246 NotifyObserverDevicesClosed(&(it->second)); | 216 NotifyObserverDevicesClosed(&(it->second)); |
| 247 } | 217 } |
| 248 requests_.erase(it); | 218 requests_.erase(it); |
| 249 } | 219 } |
| 250 } | 220 } |
| 251 | 221 |
| 252 void MediaStreamManager::EnumerateDevices( | 222 void MediaStreamManager::EnumerateDevices( |
| 253 MediaStreamRequester* requester, | 223 MediaStreamRequester* requester, |
| 254 int render_process_id, | 224 int render_process_id, |
| 255 int render_view_id, | 225 int render_view_id, |
| 256 MediaStreamType type, | 226 MediaStreamType type, |
| 257 const GURL& security_origin, | 227 const GURL& security_origin, |
| 258 std::string* label) { | 228 std::string* label) { |
| 259 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 229 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 260 DCHECK(type == content::MEDIA_STREAM_DEVICE_TYPE_AUDIO_CAPTURE || | 230 DCHECK_LT(content::MEDIA_STREAM_DEVICE_TYPE_NO_SERVICE, type); |
| 261 type == content::MEDIA_STREAM_DEVICE_TYPE_VIDEO_CAPTURE); | 231 DCHECK_GT(content::NUM_MEDIA_STREAM_DEVICE_TYPES, type); |
| 262 | 232 |
| 263 // Create a new request. | 233 // Create a new request. |
| 264 StreamOptions options; | 234 StreamOptions options; |
| 265 EnumerationCache* cache = NULL; | 235 EnumerationCache* cache; |
| 266 if (type == content::MEDIA_STREAM_DEVICE_TYPE_AUDIO_CAPTURE) { | 236 if (type == content::MEDIA_STREAM_DEVICE_TYPE_USER_AUDIO_CAPTURE) { |
| 267 options.audio = true; | 237 options.audio_type = type; |
| 268 cache = &audio_enumeration_cache_; | 238 cache = &user_audio_enumeration_cache_; |
| 239 } else if (type == content::MEDIA_STREAM_DEVICE_TYPE_USER_VIDEO_CAPTURE) { |
| 240 options.video_type = type; |
| 241 cache = &user_video_enumeration_cache_; |
| 269 } else { | 242 } else { |
| 270 options.video = true; | 243 if (content::IsAudioMediaStreamDeviceType(type)) { |
| 271 cache = &video_enumeration_cache_; | 244 options.audio_type = type; |
| 245 } else if (content::IsVideoMediaStreamDeviceType(type)) { |
| 246 options.video_type = type; |
| 247 } |
| 248 cache = NULL; |
| 272 } | 249 } |
| 273 | 250 |
| 274 DeviceRequest new_request(requester, options, | 251 DeviceRequest new_request(requester, options, |
| 275 render_process_id, | 252 render_process_id, |
| 276 render_view_id, | 253 render_view_id, |
| 277 security_origin); | 254 security_origin); |
| 278 new_request.type = DeviceRequest::ENUMERATE_DEVICES; | 255 new_request.type = DeviceRequest::ENUMERATE_DEVICES; |
| 279 | 256 |
| 280 if (cache->valid) { | 257 if (cache && cache->valid) { |
| 281 // Cached device list of this type exists. Just send it out. | 258 // Cached device list of this type exists. Just send it out. |
| 282 new_request.state[type] = DeviceRequest::STATE_REQUESTED; | 259 new_request.state[type] = DeviceRequest::STATE_REQUESTED; |
| 283 AddRequest(&new_request, label); | 260 AddRequest(&new_request, label); |
| 284 // Need to post a task since the requester won't have label till | 261 // Need to post a task since the requester won't have label till |
| 285 // this function returns. | 262 // this function returns. |
| 286 BrowserThread::PostTask(BrowserThread::IO, | 263 BrowserThread::PostTask(BrowserThread::IO, |
| 287 FROM_HERE, | 264 FROM_HERE, |
| 288 base::Bind(&MediaStreamManager::SendCachedDeviceList, | 265 base::Bind(&MediaStreamManager::SendCachedDeviceList, |
| 289 base::Unretained(this), cache, *label)); | 266 base::Unretained(this), cache, *label)); |
| 290 } else { | 267 } else { |
| 291 StartEnumeration(&new_request, label); | 268 StartEnumeration(&new_request, label); |
| 292 StartMonitoring(); | 269 if (cache) { |
| 270 StartMonitoring(); |
| 271 } |
| 293 } | 272 } |
| 294 } | 273 } |
| 295 | 274 |
| 296 void MediaStreamManager::StopEnumerateDevices(const std::string& label) { | 275 void MediaStreamManager::StopEnumerateDevices(const std::string& label) { |
| 297 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 276 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 298 | 277 |
| 299 DeviceRequests::iterator it = requests_.find(label); | 278 DeviceRequests::iterator it = requests_.find(label); |
| 300 if (it != requests_.end()) { | 279 if (it != requests_.end()) { |
| 301 DCHECK_EQ(it->second.type, DeviceRequest::ENUMERATE_DEVICES); | 280 DCHECK_EQ(it->second.type, DeviceRequest::ENUMERATE_DEVICES); |
| 302 requests_.erase(it); | 281 requests_.erase(it); |
| 303 if (!HasEnumerationRequest()) { | 282 if (!HasEnumerationRequest()) { |
| 304 StopMonitoring(); | 283 StopMonitoring(); |
| 305 } | 284 } |
| 306 } | 285 } |
| 307 } | 286 } |
| 308 | 287 |
| 309 void MediaStreamManager::OpenDevice( | 288 void MediaStreamManager::OpenDevice( |
| 310 MediaStreamRequester* requester, | 289 MediaStreamRequester* requester, |
| 311 int render_process_id, | 290 int render_process_id, |
| 312 int render_view_id, | 291 int render_view_id, |
| 313 const std::string& device_id, | 292 const std::string& device_id, |
| 314 MediaStreamType type, | 293 MediaStreamType type, |
| 315 const GURL& security_origin, | 294 const GURL& security_origin, |
| 316 std::string* label) { | 295 std::string* label) { |
| 317 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 296 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 318 | 297 |
| 319 // Create a new request. | 298 // Create a new request. |
| 320 StreamOptions options; | 299 StreamOptions options; |
| 321 if (type == content::MEDIA_STREAM_DEVICE_TYPE_AUDIO_CAPTURE) | 300 if (content::IsAudioMediaStreamDeviceType(type)) { |
| 322 options.audio = true; | 301 options.audio_type = type; |
| 323 else | 302 } else if (content::IsVideoMediaStreamDeviceType(type)) { |
| 324 options.video = true; | 303 options.video_type = type; |
| 304 } else { |
| 305 LOG(FATAL) << "Cannot handle request for MediaStreamType " << type; |
| 306 return; |
| 307 } |
| 308 options.opt_device_id = device_id; |
| 325 | 309 |
| 326 DeviceRequest new_request(requester, options, | 310 DeviceRequest new_request(requester, options, |
| 327 render_process_id, | 311 render_process_id, |
| 328 render_view_id, | 312 render_view_id, |
| 329 security_origin); | 313 security_origin); |
| 330 new_request.type = DeviceRequest::OPEN_DEVICE; | 314 new_request.type = DeviceRequest::OPEN_DEVICE; |
| 331 new_request.requested_device_id = device_id; | |
| 332 | 315 |
| 333 StartEnumeration(&new_request, label); | 316 StartEnumeration(&new_request, label); |
| 334 } | 317 } |
| 335 | 318 |
| 336 void MediaStreamManager::SendCachedDeviceList( | 319 void MediaStreamManager::SendCachedDeviceList( |
| 337 EnumerationCache* cache, | 320 EnumerationCache* cache, |
| 338 const std::string& label) { | 321 const std::string& label) { |
| 339 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 322 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 340 if (cache->valid) { | 323 if (cache->valid) { |
| 341 DeviceRequests::iterator it = requests_.find(label); | 324 DeviceRequests::iterator it = requests_.find(label); |
| 342 if (it != requests_.end()) { | 325 if (it != requests_.end()) { |
| 343 it->second.requester->DevicesEnumerated(label, cache->devices); | 326 it->second.requester->DevicesEnumerated(label, cache->devices); |
| 344 } | 327 } |
| 345 } | 328 } |
| 346 } | 329 } |
| 347 | 330 |
| 348 void MediaStreamManager::StartMonitoring() { | 331 void MediaStreamManager::StartMonitoring() { |
| 349 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 332 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 350 if (!monitoring_started_) { | 333 if (!monitoring_started_) { |
| 351 monitoring_started_ = true; | 334 monitoring_started_ = true; |
| 352 base::SystemMonitor::Get()->AddDevicesChangedObserver(this); | 335 base::SystemMonitor::Get()->AddDevicesChangedObserver(this); |
| 353 } | 336 } |
| 354 } | 337 } |
| 355 | 338 |
| 356 void MediaStreamManager::StopMonitoring() { | 339 void MediaStreamManager::StopMonitoring() { |
| 357 DCHECK_EQ(MessageLoop::current(), io_loop_); | 340 DCHECK_EQ(MessageLoop::current(), io_loop_); |
| 358 if (monitoring_started_ && !HasEnumerationRequest()) { | 341 if (monitoring_started_ && !HasEnumerationRequest()) { |
| 359 base::SystemMonitor::Get()->RemoveDevicesChangedObserver(this); | 342 base::SystemMonitor::Get()->RemoveDevicesChangedObserver(this); |
| 360 monitoring_started_ = false; | 343 monitoring_started_ = false; |
| 361 ClearEnumerationCache(&audio_enumeration_cache_); | 344 ClearEnumerationCache(&user_audio_enumeration_cache_); |
| 362 ClearEnumerationCache(&video_enumeration_cache_); | 345 ClearEnumerationCache(&user_video_enumeration_cache_); |
| 363 } | 346 } |
| 364 } | 347 } |
| 365 | 348 |
| 366 void MediaStreamManager::ClearEnumerationCache(EnumerationCache* cache) { | 349 void MediaStreamManager::ClearEnumerationCache(EnumerationCache* cache) { |
| 367 DCHECK_EQ(MessageLoop::current(), io_loop_); | 350 DCHECK_EQ(MessageLoop::current(), io_loop_); |
| 368 cache->valid = false; | 351 cache->valid = false; |
| 369 } | 352 } |
| 370 | 353 |
| 371 void MediaStreamManager::StartEnumeration( | 354 void MediaStreamManager::StartEnumeration( |
| 372 DeviceRequest* new_request, | 355 DeviceRequest* new_request, |
| 373 std::string* label) { | 356 std::string* label) { |
| 374 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 357 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 375 | 358 |
| 376 MediaStreamType stream_type = content::MEDIA_STREAM_DEVICE_TYPE_AUDIO_CAPTURE; | 359 for (int i = content::MEDIA_STREAM_DEVICE_TYPE_NO_SERVICE + 1; |
| 377 if (Requested(new_request->options, stream_type)) { | 360 i < content::NUM_MEDIA_STREAM_DEVICE_TYPES; ++i) { |
| 378 new_request->state[stream_type] = DeviceRequest::STATE_REQUESTED; | 361 const MediaStreamType stream_type = static_cast<MediaStreamType>(i); |
| 379 DCHECK_GE(active_enumeration_ref_count_[stream_type], 0); | 362 if (Requested(new_request->options, stream_type)) { |
| 380 if (!active_enumeration_ref_count_[stream_type]) { | 363 new_request->state[stream_type] = DeviceRequest::STATE_REQUESTED; |
| 381 ++active_enumeration_ref_count_[stream_type]; | 364 DCHECK_GE(active_enumeration_ref_count_[stream_type], 0); |
| 382 GetDeviceManager(stream_type)->EnumerateDevices(); | 365 if (active_enumeration_ref_count_[stream_type] == 0) { |
| 383 } | 366 ++active_enumeration_ref_count_[stream_type]; |
| 384 } | 367 GetDeviceManager(stream_type)->EnumerateDevices(); |
| 385 stream_type = content::MEDIA_STREAM_DEVICE_TYPE_VIDEO_CAPTURE; | 368 } |
| 386 if (Requested(new_request->options, stream_type)) { | |
| 387 new_request->state[stream_type] = DeviceRequest::STATE_REQUESTED; | |
| 388 DCHECK_GE(active_enumeration_ref_count_[stream_type], 0); | |
| 389 if (!active_enumeration_ref_count_[stream_type]) { | |
| 390 ++active_enumeration_ref_count_[stream_type]; | |
| 391 GetDeviceManager(stream_type)->EnumerateDevices(); | |
| 392 } | 369 } |
| 393 } | 370 } |
| 394 | 371 |
| 395 AddRequest(new_request, label); | 372 AddRequest(new_request, label); |
| 396 } | 373 } |
| 397 | 374 |
| 398 void MediaStreamManager::AddRequest( | 375 void MediaStreamManager::AddRequest( |
| 399 DeviceRequest* new_request, | 376 DeviceRequest* new_request, |
| 400 std::string* label) { | 377 std::string* label) { |
| 401 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 378 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 402 | 379 |
| 403 // Create a label for this request and verify it is unique. | 380 // Create a label for this request and verify it is unique. |
| 404 std::string request_label; | 381 std::string request_label; |
| 405 do { | 382 do { |
| 406 request_label = RandomLabel(); | 383 request_label = RandomLabel(); |
| 407 } while (requests_.find(request_label) != requests_.end()); | 384 } while (requests_.find(request_label) != requests_.end()); |
| 408 | 385 |
| 409 requests_.insert(std::make_pair(request_label, *new_request)); | 386 requests_.insert(std::make_pair(request_label, *new_request)); |
| 410 | 387 |
| 411 (*label) = request_label; | 388 (*label) = request_label; |
| 412 } | 389 } |
| 413 | 390 |
| 414 void MediaStreamManager::EnsureDeviceThreadAndListener() { | 391 void MediaStreamManager::EnsureDeviceManagerStarted( |
| 392 MediaStreamType stream_type) { |
| 415 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 393 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 416 if (device_thread_.get()) | |
| 417 return; | |
| 418 | 394 |
| 419 device_thread_.reset(new DeviceThread("MediaStreamDeviceThread")); | 395 if (!device_thread_.get()) { |
| 420 CHECK(device_thread_->Start()); | 396 device_thread_.reset(new DeviceThread("MediaStreamDeviceThread")); |
| 397 CHECK(device_thread_->Start()); |
| 398 } |
| 421 | 399 |
| 422 audio_input_device_manager_->Register(this, | 400 if (!audio_manager_) { |
| 423 device_thread_->message_loop_proxy()); | 401 audio_manager_ = content::BrowserMainLoop::GetAudioManager(); |
| 424 video_capture_manager_->Register(this, device_thread_->message_loop_proxy()); | 402 } |
| 403 |
| 404 if (!device_manager_[stream_type].get()) { |
| 405 switch (stream_type) { |
| 406 case content::MEDIA_STREAM_DEVICE_TYPE_USER_AUDIO_CAPTURE: |
| 407 case content::MEDIA_STREAM_DEVICE_TYPE_TAB_AUDIO_CAPTURE: |
| 408 device_manager_[stream_type] = |
| 409 new media_stream::AudioInputDeviceManager(audio_manager_, |
| 410 stream_type); |
| 411 break; |
| 412 case content::MEDIA_STREAM_DEVICE_TYPE_USER_VIDEO_CAPTURE: |
| 413 case content::MEDIA_STREAM_DEVICE_TYPE_TAB_VIDEO_CAPTURE: |
| 414 device_manager_[stream_type] = |
| 415 new media_stream::VideoCaptureManager(stream_type); |
| 416 break; |
| 417 default: |
| 418 LOG(FATAL) << "Cannot create device manager for invalid or " |
| 419 << "unsupported MediaStreamType " << stream_type; |
| 420 return; |
| 421 } |
| 422 device_manager_[stream_type]-> |
| 423 Register(this, device_thread_->message_loop_proxy()); |
| 424 } |
| 425 | 425 |
| 426 // We want to be notified of IO message loop destruction to delete the thread | 426 // We want to be notified of IO message loop destruction to delete the thread |
| 427 // and the device managers. | 427 // and the device managers. |
| 428 io_loop_ = MessageLoop::current(); | 428 if (!io_loop_) { |
| 429 io_loop_->AddDestructionObserver(this); | 429 io_loop_ = MessageLoop::current(); |
| 430 io_loop_->AddDestructionObserver(this); |
| 431 } |
| 430 } | 432 } |
| 431 | 433 |
| 432 void MediaStreamManager::Opened(MediaStreamType stream_type, | 434 void MediaStreamManager::Opened(MediaStreamType stream_type, |
| 433 int capture_session_id) { | 435 int capture_session_id) { |
| 434 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 436 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 435 | 437 |
| 436 // Find the request containing this device and mark it as used. | 438 // Find the request containing this device and mark it as used. |
| 437 DeviceRequest* request = NULL; | 439 DeviceRequest* request = NULL; |
| 438 StreamDeviceInfoArray* devices = NULL; | 440 StreamDeviceInfoArray* devices = NULL; |
| 439 std::string label; | 441 std::string label; |
| 440 for (DeviceRequests::iterator request_it = requests_.begin(); | 442 for (DeviceRequests::iterator request_it = requests_.begin(); |
| 441 request_it != requests_.end() && request == NULL; ++request_it) { | 443 request_it != requests_.end() && request == NULL; ++request_it) { |
| 442 if (stream_type == content::MEDIA_STREAM_DEVICE_TYPE_AUDIO_CAPTURE) { | 444 devices = &(request_it->second.devices); |
| 443 devices = &(request_it->second.audio_devices); | |
| 444 } else if (stream_type == content::MEDIA_STREAM_DEVICE_TYPE_VIDEO_CAPTURE) { | |
| 445 devices = &(request_it->second.video_devices); | |
| 446 } else { | |
| 447 NOTREACHED(); | |
| 448 } | |
| 449 | |
| 450 for (StreamDeviceInfoArray::iterator device_it = devices->begin(); | 445 for (StreamDeviceInfoArray::iterator device_it = devices->begin(); |
| 451 device_it != devices->end(); ++device_it) { | 446 device_it != devices->end(); ++device_it) { |
| 452 if (device_it->session_id == capture_session_id) { | 447 if (device_it->stream_type == stream_type && |
| 448 device_it->session_id == capture_session_id) { |
| 453 // We've found the request. | 449 // We've found the request. |
| 454 device_it->in_use = true; | 450 device_it->in_use = true; |
| 455 label = request_it->first; | 451 label = request_it->first; |
| 456 request = &(request_it->second); | 452 request = &(request_it->second); |
| 457 break; | 453 break; |
| 458 } | 454 } |
| 459 } | 455 } |
| 460 } | 456 } |
| 461 if (request == NULL) { | 457 if (request == NULL) { |
| 462 // The request doesn't exist. | 458 // The request doesn't exist. |
| 463 return; | 459 return; |
| 464 } | 460 } |
| 465 | 461 |
| 466 DCHECK_NE(request->state[stream_type], DeviceRequest::STATE_REQUESTED); | 462 DCHECK_NE(request->state[stream_type], DeviceRequest::STATE_REQUESTED); |
| 467 | 463 |
| 468 // Check if all devices for this stream type are opened. Update the state if | 464 // Check if all devices for this stream type are opened. Update the state if |
| 469 // they are. | 465 // they are. |
| 470 for (StreamDeviceInfoArray::iterator device_it = devices->begin(); | 466 for (StreamDeviceInfoArray::iterator device_it = devices->begin(); |
| 471 device_it != devices->end(); ++device_it) { | 467 device_it != devices->end(); ++device_it) { |
| 468 if (device_it->stream_type != stream_type) { |
| 469 continue; |
| 470 } |
| 472 if (device_it->in_use == false) { | 471 if (device_it->in_use == false) { |
| 473 // Wait for more devices to be opened before we're done. | 472 // Wait for more devices to be opened before we're done. |
| 474 return; | 473 return; |
| 475 } | 474 } |
| 476 } | 475 } |
| 477 request->state[stream_type] = DeviceRequest::STATE_DONE; | 476 request->state[stream_type] = DeviceRequest::STATE_DONE; |
| 478 | 477 |
| 479 if (!RequestDone(*request)) { | 478 if (!RequestDone(*request)) { |
| 480 // This stream_type is done, but not the other type. | 479 // This stream_type is done, but not the other type. |
| 481 return; | 480 return; |
| 482 } | 481 } |
| 483 | 482 |
| 483 // Partition the array of devices into audio vs video. |
| 484 StreamDeviceInfoArray audio_devices, video_devices; |
| 485 for (StreamDeviceInfoArray::iterator device_it = devices->begin(); |
| 486 device_it != devices->end(); ++device_it) { |
| 487 if (content::IsAudioMediaStreamDeviceType(device_it->stream_type)) { |
| 488 audio_devices.push_back(*device_it); |
| 489 } else if (content::IsVideoMediaStreamDeviceType(device_it->stream_type)) { |
| 490 video_devices.push_back(*device_it); |
| 491 } else { |
| 492 NOTREACHED(); |
| 493 } |
| 494 } |
| 495 |
| 484 switch (request->type) { | 496 switch (request->type) { |
| 485 case DeviceRequest::OPEN_DEVICE: | 497 case DeviceRequest::OPEN_DEVICE: |
| 486 request->requester->DeviceOpened(label, (*devices)[0]); | 498 if (content::IsAudioMediaStreamDeviceType(stream_type)) { |
| 499 request->requester->DeviceOpened(label, audio_devices.front()); |
| 500 } else if (content::IsVideoMediaStreamDeviceType(stream_type)) { |
| 501 request->requester->DeviceOpened(label, video_devices.front()); |
| 502 } else { |
| 503 NOTREACHED(); |
| 504 } |
| 487 break; | 505 break; |
| 488 case DeviceRequest::GENERATE_STREAM: | 506 case DeviceRequest::GENERATE_STREAM: |
| 489 request->requester->StreamGenerated(label, request->audio_devices, | 507 request->requester->StreamGenerated(label, audio_devices, video_devices); |
| 490 request->video_devices); | |
| 491 NotifyObserverDevicesOpened(request); | 508 NotifyObserverDevicesOpened(request); |
| 492 break; | 509 break; |
| 493 default: | 510 default: |
| 494 NOTREACHED(); | 511 NOTREACHED(); |
| 512 break; |
| 495 } | 513 } |
| 496 } | 514 } |
| 497 | 515 |
| 498 void MediaStreamManager::Closed(MediaStreamType stream_type, | 516 void MediaStreamManager::Closed(MediaStreamType stream_type, |
| 499 int capture_session_id) { | 517 int capture_session_id) { |
| 500 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 518 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 501 } | 519 } |
| 502 | 520 |
| 503 void MediaStreamManager::DevicesEnumerated( | 521 void MediaStreamManager::DevicesEnumerated( |
| 504 MediaStreamType stream_type, const StreamDeviceInfoArray& devices) { | 522 MediaStreamType stream_type, const StreamDeviceInfoArray& devices) { |
| 505 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 523 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 506 | 524 |
| 507 // Only cache device list when there is EnumerateDevices request, since | 525 // Only cache the device list when there is a EnumerateDevices request, since |
| 508 // other requests don't turn on device monitoring. | 526 // other requests don't turn on device monitoring. |
| 527 // NOTE: Caching only applies to the user audio/video capture devices. |
| 528 EnumerationCache* cache; |
| 529 if (stream_type == content::MEDIA_STREAM_DEVICE_TYPE_USER_AUDIO_CAPTURE) { |
| 530 cache = &user_audio_enumeration_cache_; |
| 531 } else if (stream_type == |
| 532 content::MEDIA_STREAM_DEVICE_TYPE_USER_VIDEO_CAPTURE) { |
| 533 cache = &user_video_enumeration_cache_; |
| 534 } else { |
| 535 cache = NULL; |
| 536 } |
| 509 bool need_update_clients = false; | 537 bool need_update_clients = false; |
| 510 EnumerationCache* cache = | 538 if (cache && |
| 511 (stream_type == content::MEDIA_STREAM_DEVICE_TYPE_AUDIO_CAPTURE ? | 539 HasEnumerationRequest(stream_type) && |
| 512 &audio_enumeration_cache_ : &video_enumeration_cache_); | |
| 513 if (HasEnumerationRequest(stream_type) && | |
| 514 (!cache->valid || | 540 (!cache->valid || |
| 515 devices.size() != cache->devices.size() || | 541 devices.size() != cache->devices.size() || |
| 516 !std::equal(devices.begin(), devices.end(), cache->devices.begin(), | 542 !std::equal(devices.begin(), devices.end(), cache->devices.begin(), |
| 517 media_stream::StreamDeviceInfo::IsEqual))) { | 543 media_stream::StreamDeviceInfo::IsEqual))) { |
| 518 cache->valid = true; | 544 cache->valid = true; |
| 519 cache->devices = devices; | 545 cache->devices = devices; |
| 520 need_update_clients = true; | 546 need_update_clients = true; |
| 547 } else { |
| 548 need_update_clients = (cache == NULL); |
| 521 } | 549 } |
| 522 | 550 |
| 523 // Publish the result for all requests waiting for device list(s). | 551 // Publish the result for all requests waiting for device list(s). |
| 524 // Find the requests waiting for this device list, store their labels and | 552 // Find the requests waiting for this device list, store their labels and |
| 525 // release the iterator before calling device settings. We might get a call | 553 // release the iterator before calling device settings. We might get a call |
| 526 // back from device_settings that will need to iterate through devices. | 554 // back from device_settings that will need to iterate through devices. |
| 527 std::list<std::string> label_list; | 555 std::list<std::string> label_list; |
| 528 for (DeviceRequests::iterator it = requests_.begin(); it != requests_.end(); | 556 for (DeviceRequests::iterator it = requests_.begin(); it != requests_.end(); |
| 529 ++it) { | 557 ++it) { |
| 530 if (it->second.state[stream_type] == DeviceRequest::STATE_REQUESTED && | 558 if (it->second.state[stream_type] == DeviceRequest::STATE_REQUESTED && |
| 531 Requested(it->second.options, stream_type)) { | 559 Requested(it->second.options, stream_type)) { |
| 532 if (it->second.type != DeviceRequest::ENUMERATE_DEVICES) | 560 if (it->second.type != DeviceRequest::ENUMERATE_DEVICES) |
| 533 it->second.state[stream_type] = DeviceRequest::STATE_PENDING_APPROVAL; | 561 it->second.state[stream_type] = DeviceRequest::STATE_PENDING_APPROVAL; |
| 534 label_list.push_back(it->first); | 562 label_list.push_back(it->first); |
| 535 } | 563 } |
| 536 } | 564 } |
| 537 for (std::list<std::string>::iterator it = label_list.begin(); | 565 for (std::list<std::string>::iterator it = label_list.begin(); |
| 538 it != label_list.end(); ++it) { | 566 it != label_list.end(); ++it) { |
| 539 DeviceRequest& request = requests_[*it]; | 567 DeviceRequest& request = requests_[*it]; |
| 568 |
| 569 // If one specific device has been requested, prune all other devices out. |
| 570 StreamDeviceInfoArray tmp; |
| 571 const StreamDeviceInfoArray* pruned_devices; |
| 572 if (request.options.opt_device_id.empty()) { |
| 573 pruned_devices = &devices; |
| 574 } else { |
| 575 for (StreamDeviceInfoArray::const_iterator device_it = devices.begin(); |
| 576 device_it != devices.end(); ++device_it) { |
| 577 if (device_it->device_id == request.options.opt_device_id) { |
| 578 tmp.push_back(*device_it); |
| 579 } |
| 580 } |
| 581 pruned_devices = &tmp; |
| 582 } |
| 583 |
| 540 switch (request.type) { | 584 switch (request.type) { |
| 541 case DeviceRequest::ENUMERATE_DEVICES: | 585 case DeviceRequest::ENUMERATE_DEVICES: |
| 542 if (need_update_clients) | 586 if (need_update_clients) |
| 543 request.requester->DevicesEnumerated(*it, devices); | 587 request.requester->DevicesEnumerated(*it, *pruned_devices); |
| 544 break; | 588 break; |
| 545 case DeviceRequest::OPEN_DEVICE: | 589 case DeviceRequest::OPEN_DEVICE: |
| 546 for (StreamDeviceInfoArray::const_iterator device_it = devices.begin(); | 590 if (!pruned_devices->empty()) { |
| 547 device_it != devices.end(); device_it++) { | 591 StreamDeviceInfo device = pruned_devices->front(); |
| 548 if (request.requested_device_id == device_it->device_id) { | 592 DCHECK_EQ(request.options.opt_device_id, device.device_id); |
| 549 StreamDeviceInfo device = *device_it; | 593 device.in_use = false; |
| 550 device.in_use = false; | 594 device.session_id = |
| 551 device.session_id = | 595 GetDeviceManager(device.stream_type)->Open(device); |
| 552 GetDeviceManager(device_it->stream_type)->Open(device); | 596 request.state[device.stream_type] = DeviceRequest::STATE_OPENING; |
| 553 request.state[device_it->stream_type] = | 597 request.devices.push_back(device); |
| 554 DeviceRequest::STATE_OPENING; | |
| 555 if (stream_type == content::MEDIA_STREAM_DEVICE_TYPE_AUDIO_CAPTURE) | |
| 556 request.audio_devices.push_back(device); | |
| 557 else | |
| 558 request.video_devices.push_back(device); | |
| 559 break; | |
| 560 } | |
| 561 } | 598 } |
| 562 break; | 599 break; |
| 563 default: | 600 default: |
| 564 device_settings_->AvailableDevices(*it, stream_type, devices); | 601 device_settings_->AvailableDevices(*it, stream_type, *pruned_devices); |
| 602 break; |
| 565 } | 603 } |
| 566 } | 604 } |
| 567 label_list.clear(); | 605 label_list.clear(); |
| 568 --active_enumeration_ref_count_[stream_type]; | 606 --active_enumeration_ref_count_[stream_type]; |
| 569 DCHECK_GE(active_enumeration_ref_count_[stream_type], 0); | 607 DCHECK_GE(active_enumeration_ref_count_[stream_type], 0); |
| 570 } | 608 } |
| 571 | 609 |
| 572 void MediaStreamManager::Error(MediaStreamType stream_type, | 610 void MediaStreamManager::Error(MediaStreamType stream_type, |
| 573 int capture_session_id, | 611 int capture_session_id, |
| 574 MediaStreamProviderError error) { | 612 MediaStreamProviderError error) { |
| 575 // Find the device for the error call. | 613 // Find the device for the error call. |
| 576 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 614 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 615 |
| 577 for (DeviceRequests::iterator it = requests_.begin(); it != requests_.end(); | 616 for (DeviceRequests::iterator it = requests_.begin(); it != requests_.end(); |
| 578 ++it) { | 617 ++it) { |
| 579 StreamDeviceInfoArray* devices = NULL; | 618 StreamDeviceInfoArray& devices = it->second.devices; |
| 580 if (stream_type == content::MEDIA_STREAM_DEVICE_TYPE_AUDIO_CAPTURE) { | |
| 581 devices = &(it->second.audio_devices); | |
| 582 } else if (stream_type == content::MEDIA_STREAM_DEVICE_TYPE_VIDEO_CAPTURE) { | |
| 583 devices = &(it->second.video_devices); | |
| 584 } else { | |
| 585 NOTREACHED(); | |
| 586 } | |
| 587 | 619 |
| 588 int device_idx = 0; | 620 int audio_device_idx = -1; |
| 589 for (StreamDeviceInfoArray::iterator device_it = devices->begin(); | 621 int video_device_idx = -1; |
| 590 device_it != devices->end(); ++device_it, ++device_idx) { | 622 for (StreamDeviceInfoArray::iterator device_it = devices.begin(); |
| 591 if (device_it->session_id == capture_session_id) { | 623 device_it != devices.end(); ++device_it) { |
| 592 // We've found the failing device. Find the error case: | 624 if (content::IsAudioMediaStreamDeviceType(device_it->stream_type)) { |
| 593 if (it->second.state[stream_type] == DeviceRequest::STATE_DONE) { | 625 ++audio_device_idx; |
| 594 // 1. Already opened -> signal device failure and close device. | 626 } else if (content::IsVideoMediaStreamDeviceType( |
| 595 // Use device_idx to signal which of the devices encountered an | 627 device_it->stream_type)) { |
| 596 // error. | 628 ++video_device_idx; |
| 597 if (stream_type == content::MEDIA_STREAM_DEVICE_TYPE_AUDIO_CAPTURE) { | 629 } else { |
| 598 it->second.requester->AudioDeviceFailed(it->first, device_idx); | 630 NOTREACHED(); |
| 599 } else if (stream_type == | 631 continue; |
| 600 content::MEDIA_STREAM_DEVICE_TYPE_VIDEO_CAPTURE) { | 632 } |
| 601 it->second.requester->VideoDeviceFailed(it->first, device_idx); | 633 if (device_it->stream_type != stream_type || |
| 602 } | 634 device_it->session_id != capture_session_id) { |
| 603 GetDeviceManager(stream_type)->Close(capture_session_id); | 635 continue; |
| 604 // We don't erase the devices here so that we can update the UI | 636 } |
| 605 // properly in StopGeneratedStream(). | 637 // We've found the failing device. Find the error case: |
| 606 it->second.state[stream_type] = DeviceRequest::STATE_ERROR; | 638 if (it->second.state[stream_type] == DeviceRequest::STATE_DONE) { |
| 639 // 1. Already opened -> signal device failure and close device. |
| 640 // Use device_idx to signal which of the devices encountered an |
| 641 // error. |
| 642 if (content::IsAudioMediaStreamDeviceType(stream_type)) { |
| 643 it->second.requester->AudioDeviceFailed(it->first, audio_device_idx); |
| 644 } else if (content::IsVideoMediaStreamDeviceType(stream_type)) { |
| 645 it->second.requester->VideoDeviceFailed(it->first, video_device_idx); |
| 607 } else { | 646 } else { |
| 608 // Request is not done, devices are not opened in this case. | 647 NOTREACHED(); |
| 609 if ((it->second.audio_devices.size() + | 648 return; |
| 610 it->second.video_devices.size()) <= 1) { | |
| 611 // 2. Device not opened and no other devices for this request -> | |
| 612 // signal stream error and remove the request. | |
| 613 it->second.requester->StreamGenerationFailed(it->first); | |
| 614 requests_.erase(it); | |
| 615 } else { | |
| 616 // 3. Not opened but other devices exists for this request -> remove | |
| 617 // device from list, but don't signal an error. | |
| 618 devices->erase(device_it); | |
| 619 } | |
| 620 } | 649 } |
| 621 return; | 650 GetDeviceManager(stream_type)->Close(capture_session_id); |
| 651 // We don't erase the devices here so that we can update the UI |
| 652 // properly in StopGeneratedStream(). |
| 653 it->second.state[stream_type] = DeviceRequest::STATE_ERROR; |
| 654 } else { |
| 655 // Request is not done, devices are not opened in this case. |
| 656 if (devices.size() <= 1) { |
| 657 // 2. Device not opened and no other devices for this request -> |
| 658 // signal stream error and remove the request. |
| 659 it->second.requester->StreamGenerationFailed(it->first); |
| 660 requests_.erase(it); |
| 661 } else { |
| 662 // 3. Not opened but other devices exists for this request -> remove |
| 663 // device from list, but don't signal an error. |
| 664 devices.erase(device_it); |
| 665 } |
| 622 } | 666 } |
| 667 return; |
| 623 } | 668 } |
| 624 } | 669 } |
| 625 } | 670 } |
| 626 | 671 |
| 627 void MediaStreamManager::DevicesAccepted(const std::string& label, | 672 void MediaStreamManager::DevicesAccepted(const std::string& label, |
| 628 const StreamDeviceInfoArray& devices) { | 673 const StreamDeviceInfoArray& devices) { |
| 629 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 674 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 630 DeviceRequests::iterator request_it = requests_.find(label); | 675 DeviceRequests::iterator request_it = requests_.find(label); |
| 631 if (request_it != requests_.end()) { | 676 if (request_it == requests_.end()) { |
| 632 if (devices.empty()) { | 677 return; |
| 633 // No available devices or user didn't accept device usage. | 678 } |
| 634 request_it->second.requester->StreamGenerationFailed(request_it->first); | 679 |
| 635 requests_.erase(request_it); | 680 DeviceRequest& request = request_it->second; |
| 636 return; | 681 |
| 682 if (devices.empty()) { |
| 683 // No available devices or user didn't accept device usage. |
| 684 request.requester->StreamGenerationFailed(request_it->first); |
| 685 requests_.erase(request_it); |
| 686 return; |
| 687 } |
| 688 |
| 689 // Process all newly-accepted devices for this request. |
| 690 for (StreamDeviceInfoArray::const_iterator device_it = devices.begin(); |
| 691 device_it != devices.end(); ++device_it) { |
| 692 StreamDeviceInfo device_info = *device_it; // make a copy |
| 693 |
| 694 // Set in_use to false to be able to track if this device has been |
| 695 // opened. in_use might be true if the device type can be used in more |
| 696 // than one session. |
| 697 DCHECK_EQ(request.state[device_it->stream_type], |
| 698 DeviceRequest::STATE_PENDING_APPROVAL); |
| 699 device_info.in_use = false; |
| 700 device_info.session_id = |
| 701 GetDeviceManager(device_info.stream_type)->Open(device_info); |
| 702 request.state[device_it->stream_type] = DeviceRequest::STATE_OPENING; |
| 703 request.devices.push_back(device_info); |
| 704 } |
| 705 |
| 706 // Check whether we've received all stream types requested. |
| 707 bool found_audio = false, found_video = false; |
| 708 for (StreamDeviceInfoArray::const_iterator i = request.devices.begin(); |
| 709 i != request.devices.end(); ++i) { |
| 710 if (i->stream_type == request.options.audio_type) { |
| 711 found_audio = true; |
| 712 } else if (i->stream_type == request.options.video_type) { |
| 713 found_video = true; |
| 714 } else { |
| 715 NOTREACHED(); |
| 637 } | 716 } |
| 638 | 717 } |
| 639 // Loop through all device types for this request. | 718 if (request.options.audio_type != |
| 640 for (StreamDeviceInfoArray::const_iterator device_it = devices.begin(); | 719 content::MEDIA_STREAM_DEVICE_TYPE_NO_SERVICE && |
| 641 device_it != devices.end(); ++device_it) { | 720 !found_audio) { |
| 642 StreamDeviceInfo device_info = *device_it; | 721 request.state[request.options.audio_type] = DeviceRequest::STATE_ERROR; |
| 643 | 722 } |
| 644 // Set in_use to false to be able to track if this device has been | 723 if (request.options.video_type != |
| 645 // opened. in_use might be true if the device type can be used in more | 724 content::MEDIA_STREAM_DEVICE_TYPE_NO_SERVICE && |
| 646 // than one session. | 725 !found_video) { |
| 647 DCHECK_EQ(request_it->second.state[device_it->stream_type], | 726 request.state[request.options.video_type] = DeviceRequest::STATE_ERROR; |
| 648 DeviceRequest::STATE_PENDING_APPROVAL); | |
| 649 device_info.in_use = false; | |
| 650 device_info.session_id = | |
| 651 GetDeviceManager(device_info.stream_type)->Open(device_info); | |
| 652 request_it->second.state[device_it->stream_type] = | |
| 653 DeviceRequest::STATE_OPENING; | |
| 654 if (device_info.stream_type == | |
| 655 content::MEDIA_STREAM_DEVICE_TYPE_AUDIO_CAPTURE) { | |
| 656 request_it->second.audio_devices.push_back(device_info); | |
| 657 } else if (device_info.stream_type == | |
| 658 content::MEDIA_STREAM_DEVICE_TYPE_VIDEO_CAPTURE) { | |
| 659 request_it->second.video_devices.push_back(device_info); | |
| 660 } else { | |
| 661 NOTREACHED(); | |
| 662 } | |
| 663 } | |
| 664 // Check if we received all stream types requested. | |
| 665 MediaStreamType stream_type = | |
| 666 content::MEDIA_STREAM_DEVICE_TYPE_AUDIO_CAPTURE; | |
| 667 if (Requested(request_it->second.options, stream_type) && | |
| 668 request_it->second.audio_devices.size() == 0) { | |
| 669 request_it->second.state[stream_type] = DeviceRequest::STATE_ERROR; | |
| 670 } | |
| 671 stream_type = content::MEDIA_STREAM_DEVICE_TYPE_VIDEO_CAPTURE; | |
| 672 if (Requested(request_it->second.options, stream_type) && | |
| 673 request_it->second.video_devices.size() == 0) { | |
| 674 request_it->second.state[stream_type] = DeviceRequest::STATE_ERROR; | |
| 675 } | |
| 676 return; | |
| 677 } | 727 } |
| 678 } | 728 } |
| 679 | 729 |
| 680 void MediaStreamManager::SettingsError(const std::string& label) { | 730 void MediaStreamManager::SettingsError(const std::string& label) { |
| 681 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 731 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 682 // Erase this request and report an error. | 732 // Erase this request and report an error. |
| 683 DeviceRequests::iterator it = requests_.find(label); | 733 DeviceRequests::iterator it = requests_.find(label); |
| 684 if (it != requests_.end()) { | 734 if (it != requests_.end()) { |
| 685 DCHECK_EQ(it->second.type, DeviceRequest::GENERATE_STREAM); | 735 DCHECK_EQ(it->second.type, DeviceRequest::GENERATE_STREAM); |
| 686 it->second.requester->StreamGenerationFailed(label); | 736 it->second.requester->StreamGenerationFailed(label); |
| 687 requests_.erase(it); | 737 requests_.erase(it); |
| 688 return; | 738 return; |
| 689 } | 739 } |
| 690 } | 740 } |
| 691 | 741 |
| 742 void MediaStreamManager::UseAudioManager(media::AudioManager* audio_manager) { |
| 743 LOG_IF(FATAL, (audio_manager_ != NULL) && (audio_manager_ != audio_manager)) |
| 744 << "Cannot override AudioManager after it has been set."; |
| 745 audio_manager_ = audio_manager; |
| 746 } |
| 747 |
| 692 void MediaStreamManager::UseFakeDevice() { | 748 void MediaStreamManager::UseFakeDevice() { |
| 693 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 749 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 694 video_capture_manager()->UseFakeDevice(); | 750 for (int i = content::MEDIA_STREAM_DEVICE_TYPE_NO_SERVICE + 1; |
| 751 i < content::NUM_MEDIA_STREAM_DEVICE_TYPES; ++i) { |
| 752 const MediaStreamType stream_type = static_cast<MediaStreamType>(i); |
| 753 if (content::IsVideoMediaStreamDeviceType(stream_type)) { |
| 754 GetVideoCaptureManager(stream_type)->UseFakeDevice(); |
| 755 } |
| 756 } |
| 695 device_settings_->UseFakeUI(); | 757 device_settings_->UseFakeUI(); |
| 696 } | 758 } |
| 697 | 759 |
| 698 void MediaStreamManager::WillDestroyCurrentMessageLoop() { | 760 void MediaStreamManager::WillDestroyCurrentMessageLoop() { |
| 699 DCHECK_EQ(MessageLoop::current(), io_loop_); | 761 DCHECK_EQ(MessageLoop::current(), io_loop_); |
| 700 DCHECK(requests_.empty()); | 762 DCHECK(requests_.empty()); |
| 701 if (device_thread_.get()) { | 763 if (device_thread_.get()) { |
| 702 StopMonitoring(); | 764 StopMonitoring(); |
| 703 | 765 |
| 704 video_capture_manager_->Unregister(); | 766 for (int i = content::MEDIA_STREAM_DEVICE_TYPE_NO_SERVICE + 1; |
| 705 audio_input_device_manager_->Unregister(); | 767 i < content::NUM_MEDIA_STREAM_DEVICE_TYPES; ++i) { |
| 768 if (device_manager_[i].get()) { |
| 769 device_manager_[i]->Unregister(); |
| 770 } |
| 771 } |
| 772 |
| 706 device_thread_.reset(); | 773 device_thread_.reset(); |
| 707 } | 774 } |
| 708 | 775 |
| 709 audio_input_device_manager_ = NULL; | 776 for (int i = content::MEDIA_STREAM_DEVICE_TYPE_NO_SERVICE + 1; |
| 710 video_capture_manager_ = NULL; | 777 i < content::NUM_MEDIA_STREAM_DEVICE_TYPES; ++i) { |
| 778 device_manager_[i] = NULL; |
| 779 } |
| 711 io_loop_ = NULL; | 780 io_loop_ = NULL; |
| 712 device_settings_.reset(); | 781 device_settings_.reset(); |
| 713 } | 782 } |
| 714 | 783 |
| 715 void MediaStreamManager::NotifyObserverDevicesOpened(DeviceRequest* request) { | 784 void MediaStreamManager::NotifyObserverDevicesOpened(DeviceRequest* request) { |
| 716 content::MediaObserver* media_observer = | 785 content::MediaObserver* media_observer = |
| 717 content::GetContentClient()->browser()->GetMediaObserver(); | 786 content::GetContentClient()->browser()->GetMediaObserver(); |
| 718 content::MediaStreamDevices opened_devices; | 787 content::MediaStreamDevices opened_devices; |
| 719 DevicesFromRequest(request, &opened_devices); | 788 DevicesFromRequest(request, &opened_devices); |
| 720 DCHECK(!opened_devices.empty()); | 789 DCHECK(!opened_devices.empty()); |
| 721 media_observer->OnCaptureDevicesOpened(request->render_process_id, | 790 media_observer->OnCaptureDevicesOpened(request->render_process_id, |
| 722 request->render_view_id, | 791 request->render_view_id, |
| 723 opened_devices); | 792 opened_devices); |
| 724 } | 793 } |
| 725 | 794 |
| 726 void MediaStreamManager::NotifyObserverDevicesClosed(DeviceRequest* request) { | 795 void MediaStreamManager::NotifyObserverDevicesClosed(DeviceRequest* request) { |
| 727 content::MediaObserver* media_observer = | 796 content::MediaObserver* media_observer = |
| 728 content::GetContentClient()->browser()->GetMediaObserver(); | 797 content::GetContentClient()->browser()->GetMediaObserver(); |
| 729 content::MediaStreamDevices closed_devices; | 798 content::MediaStreamDevices closed_devices; |
| 730 DevicesFromRequest(request, &closed_devices); | 799 DevicesFromRequest(request, &closed_devices); |
| 731 if (closed_devices.empty()) | 800 if (closed_devices.empty()) |
| 732 return; | 801 return; |
| 733 media_observer->OnCaptureDevicesClosed(request->render_process_id, | 802 media_observer->OnCaptureDevicesClosed(request->render_process_id, |
| 734 request->render_view_id, | 803 request->render_view_id, |
| 735 closed_devices); | 804 closed_devices); |
| 736 } | 805 } |
| 737 | 806 |
| 738 void MediaStreamManager::DevicesFromRequest( | 807 void MediaStreamManager::DevicesFromRequest( |
| 739 DeviceRequest* request, content::MediaStreamDevices* devices) { | 808 DeviceRequest* request, content::MediaStreamDevices* devices) { |
| 740 StreamDeviceInfoArray::const_iterator it = request->audio_devices.begin(); | 809 for (StreamDeviceInfoArray::const_iterator it = request->devices.begin(); |
| 741 for (; it != request->audio_devices.end(); ++it) { | 810 it != request->devices.end(); ++it) { |
| 742 devices->push_back( | 811 devices->push_back(content::MediaStreamDevice( |
| 743 content::MediaStreamDevice( | 812 it->stream_type, it->device_id, it->name)); |
| 744 content::MEDIA_STREAM_DEVICE_TYPE_AUDIO_CAPTURE, | |
| 745 it->device_id, | |
| 746 it->name)); | |
| 747 } | |
| 748 for (it = request->video_devices.begin(); it != request->video_devices.end(); | |
| 749 ++it) { | |
| 750 devices->push_back( | |
| 751 content::MediaStreamDevice( | |
| 752 content::MEDIA_STREAM_DEVICE_TYPE_VIDEO_CAPTURE, | |
| 753 it->device_id, | |
| 754 it->name)); | |
| 755 } | 813 } |
| 756 } | 814 } |
| 757 | 815 |
| 758 bool MediaStreamManager::RequestDone(const DeviceRequest& request) const { | 816 bool MediaStreamManager::RequestDone(const DeviceRequest& request) const { |
| 759 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 817 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 760 // Check if all devices are opened. | |
| 761 MediaStreamType stream_type = content::MEDIA_STREAM_DEVICE_TYPE_AUDIO_CAPTURE; | |
| 762 if (Requested(request.options, stream_type)) { | |
| 763 if (request.state[stream_type] != DeviceRequest::STATE_DONE && | |
| 764 request.state[stream_type] != DeviceRequest::STATE_ERROR) { | |
| 765 return false; | |
| 766 } | |
| 767 | 818 |
| 768 for (StreamDeviceInfoArray::const_iterator it = | 819 const bool requested_audio = |
| 769 request.audio_devices.begin(); it != request.audio_devices.end(); | 820 content::IsAudioMediaStreamDeviceType(request.options.audio_type); |
| 770 ++it) { | 821 const bool requested_video = |
| 771 if (it->in_use == false) { | 822 content::IsVideoMediaStreamDeviceType(request.options.video_type); |
| 772 return false; | 823 |
| 773 } | 824 const bool audio_done = |
| 774 } | 825 !requested_audio || |
| 826 request.state[request.options.audio_type] == DeviceRequest::STATE_DONE || |
| 827 request.state[request.options.audio_type] == DeviceRequest::STATE_ERROR; |
| 828 if (!audio_done) { |
| 829 return false; |
| 775 } | 830 } |
| 776 | 831 |
| 777 stream_type = content::MEDIA_STREAM_DEVICE_TYPE_VIDEO_CAPTURE; | 832 const bool video_done = |
| 778 if (Requested(request.options, stream_type)) { | 833 !requested_video || |
| 779 if (request.state[stream_type] != DeviceRequest::STATE_DONE && | 834 request.state[request.options.video_type] == DeviceRequest::STATE_DONE || |
| 780 request.state[stream_type] != DeviceRequest::STATE_ERROR) { | 835 request.state[request.options.video_type] == DeviceRequest::STATE_ERROR; |
| 836 if (!video_done) { |
| 837 return false; |
| 838 } |
| 839 |
| 840 for (StreamDeviceInfoArray::const_iterator it = request.devices.begin(); |
| 841 it != request.devices.end(); ++it) { |
| 842 if ((content::IsAudioMediaStreamDeviceType(it->stream_type) && |
| 843 !requested_audio) || |
| 844 (content::IsVideoMediaStreamDeviceType(it->stream_type) && |
| 845 !requested_video)) { |
| 846 continue; |
| 847 } |
| 848 if (it->in_use == false) { |
| 781 return false; | 849 return false; |
| 782 } | 850 } |
| 783 | |
| 784 for (StreamDeviceInfoArray::const_iterator it = | |
| 785 request.video_devices.begin(); it != request.video_devices.end(); | |
| 786 ++it) { | |
| 787 if (it->in_use == false) { | |
| 788 return false; | |
| 789 } | |
| 790 } | |
| 791 } | 851 } |
| 792 | 852 |
| 793 return true; | 853 return true; |
| 794 } | 854 } |
| 795 | 855 |
| 796 // Called to get media capture device manager of specified type. | |
| 797 MediaStreamProvider* MediaStreamManager::GetDeviceManager( | 856 MediaStreamProvider* MediaStreamManager::GetDeviceManager( |
| 798 MediaStreamType stream_type) { | 857 MediaStreamType stream_type) { |
| 799 if (stream_type == content::MEDIA_STREAM_DEVICE_TYPE_VIDEO_CAPTURE) { | 858 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 800 return video_capture_manager(); | 859 EnsureDeviceManagerStarted(stream_type); |
| 801 } else if (stream_type == content::MEDIA_STREAM_DEVICE_TYPE_AUDIO_CAPTURE) { | 860 return device_manager_[stream_type]; |
| 802 return audio_input_device_manager(); | 861 } |
| 803 } | 862 |
| 804 NOTREACHED(); | 863 VideoCaptureManager* MediaStreamManager::GetVideoCaptureManager( |
| 805 return NULL; | 864 MediaStreamType stream_type) { |
| 865 DCHECK(content::IsVideoMediaStreamDeviceType(stream_type)); |
| 866 return reinterpret_cast<VideoCaptureManager*>(GetDeviceManager(stream_type)); |
| 867 } |
| 868 |
| 869 AudioInputDeviceManager* MediaStreamManager::GetAudioInputDeviceManager( |
| 870 MediaStreamType stream_type) { |
| 871 DCHECK(content::IsAudioMediaStreamDeviceType(stream_type)); |
| 872 return reinterpret_cast<AudioInputDeviceManager*>( |
| 873 GetDeviceManager(stream_type)); |
| 806 } | 874 } |
| 807 | 875 |
| 808 void MediaStreamManager::OnDevicesChanged( | 876 void MediaStreamManager::OnDevicesChanged( |
| 809 base::SystemMonitor::DeviceType device_type) { | 877 base::SystemMonitor::DeviceType device_type) { |
| 810 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 878 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 879 |
| 880 // NOTE: This method is only called in response to user audio/video device |
| 881 // changes (from the operating system). |
| 882 |
| 811 MediaStreamType stream_type; | 883 MediaStreamType stream_type; |
| 812 EnumerationCache* cache; | 884 EnumerationCache* cache; |
| 813 if (device_type == base::SystemMonitor::DEVTYPE_AUDIO_CAPTURE) { | 885 if (device_type == base::SystemMonitor::DEVTYPE_AUDIO_CAPTURE) { |
| 814 stream_type = content::MEDIA_STREAM_DEVICE_TYPE_AUDIO_CAPTURE; | 886 stream_type = content::MEDIA_STREAM_DEVICE_TYPE_USER_AUDIO_CAPTURE; |
| 815 cache = &audio_enumeration_cache_; | 887 cache = &user_audio_enumeration_cache_; |
| 816 } else if (device_type == base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE) { | 888 } else if (device_type == base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE) { |
| 817 stream_type = content::MEDIA_STREAM_DEVICE_TYPE_VIDEO_CAPTURE; | 889 stream_type = content::MEDIA_STREAM_DEVICE_TYPE_USER_VIDEO_CAPTURE; |
| 818 cache = &video_enumeration_cache_; | 890 cache = &user_video_enumeration_cache_; |
| 819 } else { | 891 } else { |
| 820 return; // Uninteresting device change. | 892 return; // Uninteresting device change. |
| 821 } | 893 } |
| 822 | 894 |
| 823 if (!HasEnumerationRequest(stream_type)) { | 895 if (!HasEnumerationRequest(stream_type)) { |
| 824 // There is no request for that type, No need to enumerate devices. | 896 // There is no request for that type, No need to enumerate devices. |
| 825 // Therefore, invalidate the cache of that type. | 897 // Therefore, invalidate the cache of that type. |
| 826 ClearEnumerationCache(cache); | 898 ClearEnumerationCache(cache); |
| 827 return; | 899 return; |
| 828 } | 900 } |
| (...skipping 23 matching lines...) Expand all Loading... |
| 852 it != requests_.end(); ++it) { | 924 it != requests_.end(); ++it) { |
| 853 if (it->second.type == DeviceRequest::ENUMERATE_DEVICES && | 925 if (it->second.type == DeviceRequest::ENUMERATE_DEVICES && |
| 854 Requested(it->second.options, stream_type)) { | 926 Requested(it->second.options, stream_type)) { |
| 855 return true; | 927 return true; |
| 856 } | 928 } |
| 857 } | 929 } |
| 858 return false; | 930 return false; |
| 859 } | 931 } |
| 860 | 932 |
| 861 } // namespace media_stream | 933 } // namespace media_stream |
| OLD | NEW |