| 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/renderer/media/user_media_client_impl.h" | 5 #include "content/renderer/media/user_media_client_impl.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 | 8 |
| 9 #include <algorithm> |
| 9 #include <utility> | 10 #include <utility> |
| 10 #include <vector> | |
| 11 | 11 |
| 12 #include "base/hash.h" | 12 #include "base/hash.h" |
| 13 #include "base/location.h" | 13 #include "base/location.h" |
| 14 #include "base/logging.h" | 14 #include "base/logging.h" |
| 15 #include "base/memory/ptr_util.h" |
| 15 #include "base/single_thread_task_runner.h" | 16 #include "base/single_thread_task_runner.h" |
| 16 #include "base/strings/string_number_conversions.h" | 17 #include "base/strings/string_number_conversions.h" |
| 17 #include "base/strings/string_util.h" | 18 #include "base/strings/string_util.h" |
| 18 #include "base/strings/stringprintf.h" | 19 #include "base/strings/stringprintf.h" |
| 19 #include "base/strings/utf_string_conversions.h" | 20 #include "base/strings/utf_string_conversions.h" |
| 20 #include "base/threading/thread_task_runner_handle.h" | 21 #include "base/threading/thread_task_runner_handle.h" |
| 21 #include "build/build_config.h" | 22 #include "build/build_config.h" |
| 22 #include "content/public/renderer/render_frame.h" | 23 #include "content/public/renderer/render_frame.h" |
| 23 #include "content/renderer/media/local_media_stream_audio_source.h" | 24 #include "content/renderer/media/local_media_stream_audio_source.h" |
| 24 #include "content/renderer/media/media_stream.h" | 25 #include "content/renderer/media/media_stream.h" |
| 25 #include "content/renderer/media/media_stream_constraints_util.h" | 26 #include "content/renderer/media/media_stream_constraints_util.h" |
| 26 #include "content/renderer/media/media_stream_dispatcher.h" | 27 #include "content/renderer/media/media_stream_dispatcher.h" |
| 27 #include "content/renderer/media/media_stream_video_capturer_source.h" | 28 #include "content/renderer/media/media_stream_video_capturer_source.h" |
| 28 #include "content/renderer/media/media_stream_video_track.h" | 29 #include "content/renderer/media/media_stream_video_track.h" |
| 29 #include "content/renderer/media/peer_connection_tracker.h" | 30 #include "content/renderer/media/peer_connection_tracker.h" |
| 30 #include "content/renderer/media/webrtc/processed_local_audio_source.h" | 31 #include "content/renderer/media/webrtc/processed_local_audio_source.h" |
| 31 #include "content/renderer/media/webrtc/webrtc_video_capturer_adapter.h" | 32 #include "content/renderer/media/webrtc/webrtc_video_capturer_adapter.h" |
| 32 #include "content/renderer/media/webrtc_logging.h" | 33 #include "content/renderer/media/webrtc_logging.h" |
| 33 #include "content/renderer/media/webrtc_uma_histograms.h" | 34 #include "content/renderer/media/webrtc_uma_histograms.h" |
| 34 #include "content/renderer/render_thread_impl.h" | 35 #include "content/renderer/render_thread_impl.h" |
| 35 #include "third_party/WebKit/public/platform/URLConversion.h" | 36 #include "third_party/WebKit/public/platform/URLConversion.h" |
| 36 #include "third_party/WebKit/public/platform/WebMediaConstraints.h" | 37 #include "third_party/WebKit/public/platform/WebMediaConstraints.h" |
| 37 #include "third_party/WebKit/public/platform/WebMediaDeviceInfo.h" | 38 #include "third_party/WebKit/public/platform/WebMediaDeviceInfo.h" |
| 38 #include "third_party/WebKit/public/platform/WebMediaStreamTrack.h" | 39 #include "third_party/WebKit/public/platform/WebMediaStreamTrack.h" |
| 40 #include "third_party/WebKit/public/platform/WebString.h" |
| 39 #include "third_party/WebKit/public/web/WebDocument.h" | 41 #include "third_party/WebKit/public/web/WebDocument.h" |
| 40 #include "third_party/WebKit/public/web/WebLocalFrame.h" | 42 #include "third_party/WebKit/public/web/WebLocalFrame.h" |
| 41 | 43 |
| 42 namespace content { | 44 namespace content { |
| 43 namespace { | 45 namespace { |
| 44 | 46 |
| 45 void CopyVector(const blink::WebVector<blink::WebString>& source, | 47 void CopyFirstString(const blink::StringConstraint& constraint, |
| 46 std::vector<std::string>* destination) { | 48 std::string* destination) { |
| 47 for (const auto& web_string : source) { | 49 if (!constraint.exact().isEmpty()) |
| 48 destination->push_back(web_string.utf8()); | 50 *destination = constraint.exact()[0].utf8(); |
| 51 } |
| 52 |
| 53 bool FindDeviceId(const blink::WebVector<blink::WebString> candidates, |
| 54 const MediaDeviceInfoArray& device_infos, |
| 55 std::string* device_id) { |
| 56 for (const auto& candidate : candidates) { |
| 57 auto it = std::find_if(device_infos.begin(), device_infos.end(), |
| 58 [&candidate](const MediaDeviceInfo& info) { |
| 59 return info.device_id == candidate.utf8(); |
| 60 }); |
| 61 |
| 62 if (it != device_infos.end()) { |
| 63 *device_id = it->device_id; |
| 64 return true; |
| 65 } |
| 66 } |
| 67 |
| 68 return false; |
| 69 } |
| 70 |
| 71 // If a device ID requested as exact basic constraint in |constraints| is found |
| 72 // in |device_infos|, it is copied to |*device_id|, and the function returns |
| 73 // false. If such a device is not found in |device_infos|, the function returns |
| 74 // false and |*device_id| is left unmodified. |
| 75 // If more than one device ID is requested as an exact basic constraint in |
| 76 // |constraint|, the function returns false and |*device_id| is left unmodified. |
| 77 // If no device ID is requested as an exact basic constraint, and at least one |
| 78 // device ID requested as an ideal basic constraint or as an exact or ideal |
| 79 // advanced constraint in |constraints| is found in |device_infos|, the first |
| 80 // such device ID is copied to |*device_id| and the function returns true. |
| 81 // If no such device ID is found, |*device_id| is left unmodified and the |
| 82 // function returns true. |
| 83 // TODO(guidou): Replace with a spec-compliant selection algorithm. See |
| 84 // http://crbug.com/657733. |
| 85 bool PickDeviceId(const blink::WebMediaConstraints& constraints, |
| 86 const MediaDeviceInfoArray& device_infos, |
| 87 std::string* device_id) { |
| 88 DCHECK(!constraints.isNull()); |
| 89 DCHECK(device_id->empty()); |
| 90 |
| 91 if (constraints.basic().deviceId.exact().size() > 1) { |
| 92 LOG(ERROR) << "Only one required device ID is supported"; |
| 93 return false; |
| 94 } |
| 95 |
| 96 if (constraints.basic().deviceId.exact().size() == 1 && |
| 97 !FindDeviceId(constraints.basic().deviceId.exact(), device_infos, |
| 98 device_id)) { |
| 99 LOG(ERROR) << "Invalid mandatory device ID = " |
| 100 << constraints.basic().deviceId.exact()[0].utf8(); |
| 101 return false; |
| 102 } |
| 103 |
| 104 // There is no required device ID. Look at the alternates. |
| 105 if (FindDeviceId(constraints.basic().deviceId.ideal(), device_infos, |
| 106 device_id)) { |
| 107 return true; |
| 108 } |
| 109 |
| 110 for (const auto& advanced : constraints.advanced()) { |
| 111 if (FindDeviceId(advanced.deviceId.exact(), device_infos, device_id)) { |
| 112 return true; |
| 113 } |
| 114 if (FindDeviceId(advanced.deviceId.ideal(), device_infos, device_id)) { |
| 115 return true; |
| 116 } |
| 117 } |
| 118 |
| 119 // No valid alternate device ID found. Select default device. |
| 120 return true; |
| 121 } |
| 122 |
| 123 bool IsDeviceSource(const std::string& source) { |
| 124 return source.empty(); |
| 125 } |
| 126 |
| 127 void CopyConstraintsToTrackControls( |
| 128 const blink::WebMediaConstraints& constraints, |
| 129 TrackControls* track_controls, |
| 130 bool* request_devices) { |
| 131 DCHECK(!constraints.isNull()); |
| 132 track_controls->requested = true; |
| 133 CopyFirstString(constraints.basic().mediaStreamSource, |
| 134 &track_controls->stream_source); |
| 135 if (IsDeviceSource(track_controls->stream_source)) { |
| 136 bool request_devices_advanced = false; |
| 137 for (const auto& advanced : constraints.advanced()) { |
| 138 if (!advanced.deviceId.isEmpty()) { |
| 139 request_devices_advanced = true; |
| 140 break; |
| 141 } |
| 142 } |
| 143 *request_devices = |
| 144 request_devices_advanced || !constraints.basic().deviceId.isEmpty(); |
| 145 } else { |
| 146 CopyFirstString(constraints.basic().deviceId, &track_controls->device_id); |
| 147 *request_devices = false; |
| 49 } | 148 } |
| 50 } | 149 } |
| 51 | 150 |
| 52 void CopyFirstString(const blink::WebVector<blink::WebString>& source, | 151 void CopyHotwordAndLocalEchoToStreamControls( |
| 53 std::string* destination) { | 152 const blink::WebMediaConstraints& audio_constraints, |
| 54 if (!source.isEmpty()) | 153 StreamControls* controls) { |
| 55 *destination = source[0].utf8(); | 154 if (audio_constraints.isNull()) |
| 56 } | 155 return; |
| 57 | 156 |
| 58 void CopyBlinkRequestToStreamControls(const blink::WebUserMediaRequest& request, | 157 if (audio_constraints.basic().hotwordEnabled.hasExact()) { |
| 59 StreamControls* controls) { | 158 controls->hotword_enabled = |
| 60 if (request.isNull()) { | 159 audio_constraints.basic().hotwordEnabled.exact(); |
| 61 return; | 160 } else { |
| 62 } | 161 for (const auto& audio_advanced : audio_constraints.advanced()) { |
| 63 if (!request.audioConstraints().isNull()) { | 162 if (audio_advanced.hotwordEnabled.hasExact()) { |
| 64 const blink::WebMediaTrackConstraintSet& audio_basic = | 163 controls->hotword_enabled = audio_advanced.hotwordEnabled.exact(); |
| 65 request.audioConstraints().basic(); | 164 break; |
| 66 CopyFirstString(audio_basic.mediaStreamSource.exact(), | |
| 67 &controls->audio.stream_source); | |
| 68 CopyVector(audio_basic.deviceId.exact(), &(controls->audio.device_ids)); | |
| 69 // Optionals. They may be either in ideal or in advanced.exact. | |
| 70 CopyVector(audio_basic.deviceId.ideal(), | |
| 71 &controls->audio.alternate_device_ids); | |
| 72 for (const auto& constraint : request.audioConstraints().advanced()) { | |
| 73 CopyVector(constraint.deviceId.exact(), | |
| 74 &controls->audio.alternate_device_ids); | |
| 75 CopyVector(constraint.deviceId.ideal(), | |
| 76 &controls->audio.alternate_device_ids); | |
| 77 } | |
| 78 if (audio_basic.hotwordEnabled.hasExact()) { | |
| 79 controls->hotword_enabled = audio_basic.hotwordEnabled.exact(); | |
| 80 } else { | |
| 81 for (const auto& audio_advanced : request.audioConstraints().advanced()) { | |
| 82 if (audio_advanced.hotwordEnabled.hasExact()) { | |
| 83 controls->hotword_enabled = audio_advanced.hotwordEnabled.exact(); | |
| 84 break; | |
| 85 } | |
| 86 } | 165 } |
| 87 } | 166 } |
| 167 } |
| 88 | 168 |
| 89 if (request.audioConstraints().basic().disableLocalEcho.hasExact()) { | 169 if (audio_constraints.basic().disableLocalEcho.hasExact()) { |
| 90 controls->disable_local_echo = | 170 controls->disable_local_echo = |
| 91 request.audioConstraints().basic().disableLocalEcho.exact(); | 171 audio_constraints.basic().disableLocalEcho.exact(); |
| 92 } else { | 172 } else { |
| 93 controls->disable_local_echo = | 173 controls->disable_local_echo = |
| 94 controls->audio.stream_source != kMediaStreamSourceDesktop; | 174 controls->audio.stream_source != kMediaStreamSourceDesktop; |
| 95 } | |
| 96 } | |
| 97 if (!request.videoConstraints().isNull()) { | |
| 98 const blink::WebMediaTrackConstraintSet& video_basic = | |
| 99 request.videoConstraints().basic(); | |
| 100 CopyFirstString(video_basic.mediaStreamSource.exact(), | |
| 101 &(controls->video.stream_source)); | |
| 102 CopyVector(video_basic.deviceId.exact(), &(controls->video.device_ids)); | |
| 103 CopyVector(video_basic.deviceId.ideal(), | |
| 104 &(controls->video.alternate_device_ids)); | |
| 105 for (const auto& constraint : request.videoConstraints().advanced()) { | |
| 106 CopyVector(constraint.deviceId.exact(), | |
| 107 &controls->video.alternate_device_ids); | |
| 108 CopyVector(constraint.deviceId.ideal(), | |
| 109 &controls->video.alternate_device_ids); | |
| 110 } | |
| 111 } | 175 } |
| 112 } | 176 } |
| 113 | 177 |
| 114 bool IsSameDevice(const StreamDeviceInfo& device, | 178 bool IsSameDevice(const StreamDeviceInfo& device, |
| 115 const StreamDeviceInfo& other_device) { | 179 const StreamDeviceInfo& other_device) { |
| 116 return device.device.id == other_device.device.id && | 180 return device.device.id == other_device.device.id && |
| 117 device.device.type == other_device.device.type && | 181 device.device.type == other_device.device.type && |
| 118 device.session_id == other_device.session_id; | 182 device.session_id == other_device.session_id; |
| 119 } | 183 } |
| 120 | 184 |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 176 // webGetUserMedia. | 240 // webGetUserMedia. |
| 177 UpdateWebRTCMethodCount(WEBKIT_GET_USER_MEDIA); | 241 UpdateWebRTCMethodCount(WEBKIT_GET_USER_MEDIA); |
| 178 DCHECK(CalledOnValidThread()); | 242 DCHECK(CalledOnValidThread()); |
| 179 | 243 |
| 180 if (RenderThreadImpl::current()) { | 244 if (RenderThreadImpl::current()) { |
| 181 RenderThreadImpl::current()->peer_connection_tracker()->TrackGetUserMedia( | 245 RenderThreadImpl::current()->peer_connection_tracker()->TrackGetUserMedia( |
| 182 user_media_request); | 246 user_media_request); |
| 183 } | 247 } |
| 184 | 248 |
| 185 int request_id = g_next_request_id++; | 249 int request_id = g_next_request_id++; |
| 186 StreamControls controls; | 250 std::unique_ptr<StreamControls> controls = base::MakeUnique<StreamControls>(); |
| 187 url::Origin security_origin; | |
| 188 bool enable_automatic_output_device_selection = false; | |
| 189 | 251 |
| 190 // |user_media_request| can't be mocked. So in order to test at all we check | 252 // |user_media_request| can't be mocked. So in order to test at all we check |
| 191 // if it isNull. | 253 // if it isNull. |
| 254 // TODO(guidou): Remove this test-specific code path. |
| 192 if (user_media_request.isNull()) { | 255 if (user_media_request.isNull()) { |
| 193 // We are in a test. | 256 // We are in a test. |
| 194 controls.audio.requested = true; | 257 controls->audio.requested = true; |
| 195 controls.video.requested = true; | 258 controls->video.requested = true; |
| 259 FinalizeRequestUserMedia( |
| 260 request_id, user_media_request, std::move(controls), |
| 261 false /* automatic_output_device_selection */, url::Origin()); |
| 196 } else { | 262 } else { |
| 263 // ownerDocument may be null if we are in a test. |
| 264 // In that case, it's OK to not check frame(). |
| 265 DCHECK(user_media_request.ownerDocument().isNull() || |
| 266 render_frame()->GetWebFrame() == |
| 267 static_cast<blink::WebFrame*>( |
| 268 user_media_request.ownerDocument().frame())); |
| 269 |
| 270 bool enable_automatic_output_device_selection = false; |
| 271 bool request_audio_input_devices = false; |
| 197 if (user_media_request.audio()) { | 272 if (user_media_request.audio()) { |
| 198 controls.audio.requested = true; | 273 CopyConstraintsToTrackControls(user_media_request.audioConstraints(), |
| 274 &controls->audio, |
| 275 &request_audio_input_devices); |
| 276 CopyHotwordAndLocalEchoToStreamControls( |
| 277 user_media_request.audioConstraints(), controls.get()); |
| 199 // Check if this input device should be used to select a matching output | 278 // Check if this input device should be used to select a matching output |
| 200 // device for audio rendering. | 279 // device for audio rendering. |
| 201 GetConstraintValueAsBoolean( | 280 GetConstraintValueAsBoolean( |
| 202 user_media_request.audioConstraints(), | 281 user_media_request.audioConstraints(), |
| 203 &blink::WebMediaTrackConstraintSet::renderToAssociatedSink, | 282 &blink::WebMediaTrackConstraintSet::renderToAssociatedSink, |
| 204 &enable_automatic_output_device_selection); | 283 &enable_automatic_output_device_selection); |
| 205 } | 284 } |
| 285 bool request_video_input_devices = false; |
| 206 if (user_media_request.video()) { | 286 if (user_media_request.video()) { |
| 207 controls.video.requested = true; | 287 CopyConstraintsToTrackControls(user_media_request.videoConstraints(), |
| 288 &controls->video, |
| 289 &request_video_input_devices); |
| 208 } | 290 } |
| 209 CopyBlinkRequestToStreamControls(user_media_request, &controls); | 291 |
| 210 security_origin = user_media_request.getSecurityOrigin(); | 292 url::Origin security_origin = user_media_request.getSecurityOrigin(); |
| 211 // ownerDocument may be null if we are in a test. | 293 if (request_audio_input_devices || request_video_input_devices) { |
| 212 // In that case, it's OK to not check frame(). | 294 GetMediaDevicesDispatcher()->EnumerateDevices( |
| 213 DCHECK(user_media_request.ownerDocument().isNull() || | 295 request_audio_input_devices, request_video_input_devices, |
| 214 render_frame()->GetWebFrame() == | 296 false /* request_audio_output_devices */, security_origin, |
| 215 static_cast<blink::WebFrame*>( | 297 base::Bind(&UserMediaClientImpl::SelectUserMediaDevice, |
| 216 user_media_request.ownerDocument().frame())); | 298 weak_factory_.GetWeakPtr(), request_id, user_media_request, |
| 299 base::Passed(&controls), |
| 300 enable_automatic_output_device_selection, |
| 301 security_origin)); |
| 302 } else { |
| 303 FinalizeRequestUserMedia( |
| 304 request_id, user_media_request, std::move(controls), |
| 305 enable_automatic_output_device_selection, security_origin); |
| 306 } |
| 307 } |
| 308 } |
| 309 |
| 310 void UserMediaClientImpl::SelectUserMediaDevice( |
| 311 int request_id, |
| 312 const blink::WebUserMediaRequest& user_media_request, |
| 313 std::unique_ptr<StreamControls> controls, |
| 314 bool enable_automatic_output_device_selection, |
| 315 const url::Origin& security_origin, |
| 316 const EnumerationResult& device_enumeration) { |
| 317 DCHECK(CalledOnValidThread()); |
| 318 |
| 319 if (controls->audio.requested && |
| 320 IsDeviceSource(controls->audio.stream_source)) { |
| 321 if (!PickDeviceId(user_media_request.audioConstraints(), |
| 322 device_enumeration[MEDIA_DEVICE_TYPE_AUDIO_INPUT], |
| 323 &controls->audio.device_id)) { |
| 324 GetUserMediaRequestFailed(user_media_request, MEDIA_DEVICE_NO_HARDWARE, |
| 325 ""); |
| 326 return; |
| 327 } |
| 217 } | 328 } |
| 218 | 329 |
| 219 DVLOG(1) << "UserMediaClientImpl::requestUserMedia(" << request_id << ", [ " | 330 if (controls->video.requested && |
| 220 << "audio=" << (controls.audio.requested) | 331 IsDeviceSource(controls->video.stream_source)) { |
| 221 << " select associated sink: " | 332 if (!PickDeviceId(user_media_request.videoConstraints(), |
| 222 << enable_automatic_output_device_selection | 333 device_enumeration[MEDIA_DEVICE_TYPE_VIDEO_INPUT], |
| 223 << ", video=" << (controls.video.requested) << " ], " | 334 &controls->video.device_id)) { |
| 224 << security_origin << ")"; | 335 GetUserMediaRequestFailed(user_media_request, MEDIA_DEVICE_NO_HARDWARE, |
| 225 | 336 ""); |
| 226 std::string audio_device_id; | 337 return; |
| 227 if (!user_media_request.isNull() && user_media_request.audio()) { | 338 } |
| 228 GetConstraintValueAsString(user_media_request.audioConstraints(), | |
| 229 &blink::WebMediaTrackConstraintSet::deviceId, | |
| 230 &audio_device_id); | |
| 231 } | 339 } |
| 232 | 340 |
| 233 std::string video_device_id; | 341 FinalizeRequestUserMedia(request_id, user_media_request, std::move(controls), |
| 234 if (!user_media_request.isNull() && user_media_request.video()) { | 342 enable_automatic_output_device_selection, |
| 235 GetConstraintValueAsString(user_media_request.videoConstraints(), | 343 security_origin); |
| 236 &blink::WebMediaTrackConstraintSet::deviceId, | 344 } |
| 237 &video_device_id); | |
| 238 } | |
| 239 | 345 |
| 240 WebRtcLogMessage(base::StringPrintf( | 346 void UserMediaClientImpl::FinalizeRequestUserMedia( |
| 241 "MSI::requestUserMedia. request_id=%d" | 347 int request_id, |
| 242 ", audio source id=%s" | 348 const blink::WebUserMediaRequest& user_media_request, |
| 243 ", video source id=%s", | 349 std::unique_ptr<StreamControls> controls, |
| 244 request_id, audio_device_id.c_str(), video_device_id.c_str())); | 350 bool enable_automatic_output_device_selection, |
| 351 const url::Origin& security_origin) { |
| 352 DCHECK(CalledOnValidThread()); |
| 353 |
| 354 WebRtcLogMessage( |
| 355 base::StringPrintf("MSI::requestUserMedia. request_id=%d" |
| 356 ", audio source id=%s" |
| 357 ", video source id=%s", |
| 358 request_id, controls->audio.device_id.c_str(), |
| 359 controls->video.device_id.c_str())); |
| 245 | 360 |
| 246 user_media_requests_.push_back( | 361 user_media_requests_.push_back( |
| 247 new UserMediaRequestInfo(request_id, user_media_request, | 362 new UserMediaRequestInfo(request_id, user_media_request, |
| 248 enable_automatic_output_device_selection)); | 363 enable_automatic_output_device_selection)); |
| 249 | 364 |
| 250 media_stream_dispatcher_->GenerateStream( | 365 media_stream_dispatcher_->GenerateStream( |
| 251 request_id, weak_factory_.GetWeakPtr(), controls, security_origin); | 366 request_id, weak_factory_.GetWeakPtr(), *controls, security_origin); |
| 252 } | 367 } |
| 253 | 368 |
| 254 void UserMediaClientImpl::cancelUserMediaRequest( | 369 void UserMediaClientImpl::cancelUserMediaRequest( |
| 255 const blink::WebUserMediaRequest& user_media_request) { | 370 const blink::WebUserMediaRequest& user_media_request) { |
| 256 DCHECK(CalledOnValidThread()); | 371 DCHECK(CalledOnValidThread()); |
| 257 UserMediaRequestInfo* request = FindUserMediaRequestInfo(user_media_request); | 372 UserMediaRequestInfo* request = FindUserMediaRequestInfo(user_media_request); |
| 258 if (request) { | 373 if (request) { |
| 259 // We can't abort the stream generation process. | 374 // We can't abort the stream generation process. |
| 260 // Instead, erase the request. Once the stream is generated we will stop the | 375 // Instead, erase the request. Once the stream is generated we will stop the |
| 261 // stream if the request does not exist. | 376 // stream if the request does not exist. |
| (...skipping 716 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 978 | 1093 |
| 979 bool UserMediaClientImpl::UserMediaRequestInfo::HasPendingSources() const { | 1094 bool UserMediaClientImpl::UserMediaRequestInfo::HasPendingSources() const { |
| 980 return !sources_waiting_for_callback_.empty(); | 1095 return !sources_waiting_for_callback_.empty(); |
| 981 } | 1096 } |
| 982 | 1097 |
| 983 void UserMediaClientImpl::OnDestruct() { | 1098 void UserMediaClientImpl::OnDestruct() { |
| 984 delete this; | 1099 delete this; |
| 985 } | 1100 } |
| 986 | 1101 |
| 987 } // namespace content | 1102 } // namespace content |
| OLD | NEW |