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 3 // found in the LICENSE file. 4 5 #include "content/renderer/media/user_media_client_impl.h" 6 7 #include <utility> 8 9 #include "base/hash.h" 10 #include "base/logging.h" 11 #include "base/strings/string_number_conversions.h" 12 #include "base/strings/string_util.h" 13 #include "base/strings/stringprintf.h" 14 #include "base/strings/utf_string_conversions.h" 15 #include "content/public/renderer/render_frame.h" 16 #include "content/renderer/media/media_stream.h" 17 #include "content/renderer/media/media_stream_audio_source.h" 18 #include "content/renderer/media/media_stream_dispatcher.h" 19 #include "content/renderer/media/media_stream_video_capturer_source.h" 20 #include "content/renderer/media/media_stream_video_track.h" 21 #include "content/renderer/media/peer_connection_tracker.h" 22 #include "content/renderer/media/webrtc/webrtc_video_capturer_adapter.h" 23 #include "content/renderer/media/webrtc_audio_capturer.h" 24 #include "content/renderer/media/webrtc_logging.h" 25 #include "content/renderer/media/webrtc_uma_histograms.h" 26 #include "content/renderer/render_thread_impl.h" 27 #include "third_party/WebKit/public/platform/WebMediaConstraints.h" 28 #include "third_party/WebKit/public/platform/WebMediaDeviceInfo.h" 29 #include "third_party/WebKit/public/platform/WebMediaStreamTrack.h" 30 #include "third_party/WebKit/public/platform/WebMediaStreamTrackSourcesRequest.h" 31 #include "third_party/WebKit/public/web/WebDocument.h" 32 #include "third_party/WebKit/public/web/WebLocalFrame.h" 33 34 namespace content { 35 namespace { 36 37 void CopyStreamConstraints(const blink::WebMediaConstraints& constraints, 38 StreamOptions::Constraints* mandatory, 39 StreamOptions::Constraints* optional) { 40 blink::WebVector<blink::WebMediaConstraint> mandatory_constraints; 41 constraints.getMandatoryConstraints(mandatory_constraints); 42 for (size_t i = 0; i < mandatory_constraints.size(); i++) { 43 mandatory->push_back(StreamOptions::Constraint( 44 mandatory_constraints[i].m_name.utf8(), 45 mandatory_constraints[i].m_value.utf8())); 46 } 47 48 blink::WebVector<blink::WebMediaConstraint> optional_constraints; 49 constraints.getOptionalConstraints(optional_constraints); 50 for (size_t i = 0; i < optional_constraints.size(); i++) { 51 optional->push_back(StreamOptions::Constraint( 52 optional_constraints[i].m_name.utf8(), 53 optional_constraints[i].m_value.utf8())); 54 } 55 } 56 57 static int g_next_request_id = 0; 58 59 } // namespace 60 61 struct UserMediaClientImpl::MediaDevicesRequestInfo { 62 MediaDevicesRequestInfo(const blink::WebMediaDevicesRequest& request, 63 int audio_input_request_id, 64 int video_input_request_id, 65 int audio_output_request_id) 66 : media_devices_request(request), 67 audio_input_request_id(audio_input_request_id), 68 video_input_request_id(video_input_request_id), 69 audio_output_request_id(audio_output_request_id), 70 has_audio_input_returned(false), 71 has_video_input_returned(false), 72 has_audio_output_returned(false) {} 73 74 MediaDevicesRequestInfo( 75 const blink::WebMediaStreamTrackSourcesRequest& request, 76 int audio_input_request_id, 77 int video_input_request_id) 78 : sources_request(request), 79 audio_input_request_id(audio_input_request_id), 80 video_input_request_id(video_input_request_id), 81 audio_output_request_id(-1), 82 has_audio_input_returned(false), 83 has_video_input_returned(false), 84 has_audio_output_returned(false) {} 85 86 bool IsSourcesRequest() { 87 // We can't check isNull() on |media_devices_request| and |sources_request|, 88 // because in unit tests they will always be null. 89 return audio_output_request_id == -1; 90 } 91 92 blink::WebMediaDevicesRequest media_devices_request; 93 blink::WebMediaStreamTrackSourcesRequest sources_request; 94 int audio_input_request_id; 95 int video_input_request_id; 96 int audio_output_request_id; 97 bool has_audio_input_returned; 98 bool has_video_input_returned; 99 bool has_audio_output_returned; 100 StreamDeviceInfoArray audio_input_devices; 101 StreamDeviceInfoArray video_input_devices; 102 StreamDeviceInfoArray audio_output_devices; 103 }; 104 105 UserMediaClientImpl::UserMediaClientImpl( 106 RenderFrame* render_frame, 107 PeerConnectionDependencyFactory* dependency_factory, 108 scoped_ptr<MediaStreamDispatcher> media_stream_dispatcher) 109 : RenderFrameObserver(render_frame), 110 dependency_factory_(dependency_factory), 111 media_stream_dispatcher_(media_stream_dispatcher.Pass()), 112 weak_factory_(this) { 113 DCHECK(dependency_factory_); 114 DCHECK(media_stream_dispatcher_.get()); 115 } 116 117 UserMediaClientImpl::~UserMediaClientImpl() { 118 // Force-close all outstanding user media requests and local sources here, 119 // before the outstanding WeakPtrs are invalidated, to ensure a clean 120 // shutdown. 121 FrameWillClose(); 122 } 123 124 void UserMediaClientImpl::requestUserMedia( 125 const blink::WebUserMediaRequest& user_media_request) { 126 // Save histogram data so we can see how much GetUserMedia is used. 127 // The histogram counts the number of calls to the JS API 128 // webGetUserMedia. 129 UpdateWebRTCMethodCount(WEBKIT_GET_USER_MEDIA); 130 DCHECK(CalledOnValidThread()); 131 132 if (RenderThreadImpl::current()) { 133 RenderThreadImpl::current()->peer_connection_tracker()->TrackGetUserMedia( 134 user_media_request); 135 } 136 137 int request_id = g_next_request_id++; 138 StreamOptions options; 139 GURL security_origin; 140 bool enable_automatic_output_device_selection = false; 141 142 // |user_media_request| can't be mocked. So in order to test at all we check 143 // if it isNull. 144 if (user_media_request.isNull()) { 145 // We are in a test. 146 options.audio_requested = true; 147 options.video_requested = true; 148 } else { 149 if (user_media_request.audio()) { 150 options.audio_requested = true; 151 CopyStreamConstraints(user_media_request.audioConstraints(), 152 &options.mandatory_audio, 153 &options.optional_audio); 154 155 // Check if this input device should be used to select a matching output 156 // device for audio rendering. 157 std::string enable; 158 if (options.GetFirstAudioConstraintByName( 159 kMediaStreamRenderToAssociatedSink, &enable, NULL) && 160 LowerCaseEqualsASCII(enable, "true")) { 161 enable_automatic_output_device_selection = true; 162 } 163 } 164 if (user_media_request.video()) { 165 options.video_requested = true; 166 CopyStreamConstraints(user_media_request.videoConstraints(), 167 &options.mandatory_video, 168 &options.optional_video); 169 } 170 171 security_origin = GURL(user_media_request.securityOrigin().toString()); 172 DCHECK(render_frame()->GetWebFrame() == 173 static_cast<blink::WebFrame*>( 174 user_media_request.ownerDocument().frame())); 175 } 176 177 DVLOG(1) << "UserMediaClientImpl::requestUserMedia(" << request_id << ", [ " 178 << "audio=" << (options.audio_requested) 179 << " select associated sink: " 180 << enable_automatic_output_device_selection 181 << ", video=" << (options.video_requested) << " ], " 182 << security_origin.spec() << ")"; 183 184 std::string audio_device_id; 185 bool mandatory_audio; 186 options.GetFirstAudioConstraintByName(kMediaStreamSourceInfoId, 187 &audio_device_id, &mandatory_audio); 188 std::string video_device_id; 189 bool mandatory_video; 190 options.GetFirstVideoConstraintByName(kMediaStreamSourceInfoId, 191 &video_device_id, &mandatory_video); 192 193 WebRtcLogMessage(base::StringPrintf( 194 "MSI::requestUserMedia. request_id=%d" 195 ", audio source id=%s mandatory= %s " 196 ", video source id=%s mandatory= %s", 197 request_id, 198 audio_device_id.c_str(), 199 mandatory_audio ? "true":"false", 200 video_device_id.c_str(), 201 mandatory_video ? "true":"false")); 202 203 user_media_requests_.push_back( 204 new UserMediaRequestInfo(request_id, user_media_request, 205 enable_automatic_output_device_selection)); 206 207 media_stream_dispatcher_->GenerateStream( 208 request_id, 209 weak_factory_.GetWeakPtr(), 210 options, 211 security_origin); 212 } 213 214 void UserMediaClientImpl::cancelUserMediaRequest( 215 const blink::WebUserMediaRequest& user_media_request) { 216 DCHECK(CalledOnValidThread()); 217 UserMediaRequestInfo* request = FindUserMediaRequestInfo(user_media_request); 218 if (request) { 219 // We can't abort the stream generation process. 220 // Instead, erase the request. Once the stream is generated we will stop the 221 // stream if the request does not exist. 222 LogUserMediaRequestWithNoResult(MEDIA_STREAM_REQUEST_EXPLICITLY_CANCELLED); 223 DeleteUserMediaRequestInfo(request); 224 } 225 } 226 227 void UserMediaClientImpl::requestMediaDevices( 228 const blink::WebMediaDevicesRequest& media_devices_request) { 229 UpdateWebRTCMethodCount(WEBKIT_GET_MEDIA_DEVICES); 230 DCHECK(CalledOnValidThread()); 231 232 int audio_input_request_id = g_next_request_id++; 233 int video_input_request_id = g_next_request_id++; 234 int audio_output_request_id = g_next_request_id++; 235 236 // |media_devices_request| can't be mocked, so in tests it will be empty (the 237 // underlying pointer is null). In order to use this function in a test we 238 // need to check if it isNull. 239 GURL security_origin; 240 if (!media_devices_request.isNull()) 241 security_origin = GURL(media_devices_request.securityOrigin().toString()); 242 243 DVLOG(1) << "UserMediaClientImpl::requestMediaDevices(" 244 << audio_input_request_id 245 << ", " << video_input_request_id << ", " << audio_output_request_id 246 << ", " << security_origin.spec() << ")"; 247 248 media_devices_requests_.push_back(new MediaDevicesRequestInfo( 249 media_devices_request, 250 audio_input_request_id, 251 video_input_request_id, 252 audio_output_request_id)); 253 254 media_stream_dispatcher_->EnumerateDevices( 255 audio_input_request_id, 256 weak_factory_.GetWeakPtr(), 257 MEDIA_DEVICE_AUDIO_CAPTURE, 258 security_origin); 259 260 media_stream_dispatcher_->EnumerateDevices( 261 video_input_request_id, 262 weak_factory_.GetWeakPtr(), 263 MEDIA_DEVICE_VIDEO_CAPTURE, 264 security_origin); 265 266 media_stream_dispatcher_->EnumerateDevices( 267 audio_output_request_id, 268 weak_factory_.GetWeakPtr(), 269 MEDIA_DEVICE_AUDIO_OUTPUT, 270 security_origin); 271 } 272 273 void UserMediaClientImpl::cancelMediaDevicesRequest( 274 const blink::WebMediaDevicesRequest& media_devices_request) { 275 DCHECK(CalledOnValidThread()); 276 MediaDevicesRequestInfo* request = 277 FindMediaDevicesRequestInfo(media_devices_request); 278 if (!request) 279 return; 280 CancelAndDeleteMediaDevicesRequest(request); 281 } 282 283 void UserMediaClientImpl::requestSources( 284 const blink::WebMediaStreamTrackSourcesRequest& sources_request) { 285 // We don't call UpdateWebRTCMethodCount() here to track the API count in UMA 286 // stats. This is instead counted in MediaStreamTrack::getSources in blink. 287 DCHECK(CalledOnValidThread()); 288 289 int audio_input_request_id = g_next_request_id++; 290 int video_input_request_id = g_next_request_id++; 291 292 // |sources_request| can't be mocked, so in tests it will be empty (the 293 // underlying pointer is null). In order to use this function in a test we 294 // need to check if it isNull. 295 GURL security_origin; 296 if (!sources_request.isNull()) 297 security_origin = GURL(sources_request.origin().utf8()); 298 299 DVLOG(1) << "UserMediaClientImpl::requestSources(" 300 << audio_input_request_id 301 << ", " << video_input_request_id 302 << ", " << security_origin.spec() << ")"; 303 304 media_devices_requests_.push_back(new MediaDevicesRequestInfo( 305 sources_request, 306 audio_input_request_id, 307 video_input_request_id)); 308 309 media_stream_dispatcher_->EnumerateDevices( 310 audio_input_request_id, 311 weak_factory_.GetWeakPtr(), 312 MEDIA_DEVICE_AUDIO_CAPTURE, 313 security_origin); 314 315 media_stream_dispatcher_->EnumerateDevices( 316 video_input_request_id, 317 weak_factory_.GetWeakPtr(), 318 MEDIA_DEVICE_VIDEO_CAPTURE, 319 security_origin); 320 } 321 322 // Callback from MediaStreamDispatcher. 323 // The requested stream have been generated by the MediaStreamDispatcher. 324 void UserMediaClientImpl::OnStreamGenerated( 325 int request_id, 326 const std::string& label, 327 const StreamDeviceInfoArray& audio_array, 328 const StreamDeviceInfoArray& video_array) { 329 DCHECK(CalledOnValidThread()); 330 DVLOG(1) << "UserMediaClientImpl::OnStreamGenerated stream:" << label; 331 332 UserMediaRequestInfo* request_info = FindUserMediaRequestInfo(request_id); 333 if (!request_info) { 334 // This can happen if the request is canceled or the frame reloads while 335 // MediaStreamDispatcher is processing the request. 336 DVLOG(1) << "Request ID not found"; 337 OnStreamGeneratedForCancelledRequest(audio_array, video_array); 338 return; 339 } 340 request_info->generated = true; 341 342 // WebUserMediaRequest don't have an implementation in unit tests. 343 // Therefore we need to check for isNull here and initialize the 344 // constraints. 345 blink::WebUserMediaRequest* request = &(request_info->request); 346 blink::WebMediaConstraints audio_constraints; 347 blink::WebMediaConstraints video_constraints; 348 if (request->isNull()) { 349 audio_constraints.initialize(); 350 video_constraints.initialize(); 351 } else { 352 audio_constraints = request->audioConstraints(); 353 video_constraints = request->videoConstraints(); 354 } 355 356 blink::WebVector<blink::WebMediaStreamTrack> audio_track_vector( 357 audio_array.size()); 358 CreateAudioTracks(audio_array, audio_constraints, &audio_track_vector, 359 request_info); 360 361 blink::WebVector<blink::WebMediaStreamTrack> video_track_vector( 362 video_array.size()); 363 CreateVideoTracks(video_array, video_constraints, &video_track_vector, 364 request_info); 365 366 blink::WebString webkit_id = base::UTF8ToUTF16(label); 367 blink::WebMediaStream* web_stream = &(request_info->web_stream); 368 369 web_stream->initialize(webkit_id, audio_track_vector, 370 video_track_vector); 371 web_stream->setExtraData( 372 new MediaStream( 373 *web_stream)); 374 375 // Wait for the tracks to be started successfully or to fail. 376 request_info->CallbackOnTracksStarted( 377 base::Bind(&UserMediaClientImpl::OnCreateNativeTracksCompleted, 378 weak_factory_.GetWeakPtr())); 379 } 380 381 void UserMediaClientImpl::OnStreamGeneratedForCancelledRequest( 382 const StreamDeviceInfoArray& audio_array, 383 const StreamDeviceInfoArray& video_array) { 384 // Only stop the device if the device is not used in another MediaStream. 385 for (StreamDeviceInfoArray::const_iterator device_it = audio_array.begin(); 386 device_it != audio_array.end(); ++device_it) { 387 if (!FindLocalSource(*device_it)) 388 media_stream_dispatcher_->StopStreamDevice(*device_it); 389 } 390 391 for (StreamDeviceInfoArray::const_iterator device_it = video_array.begin(); 392 device_it != video_array.end(); ++device_it) { 393 if (!FindLocalSource(*device_it)) 394 media_stream_dispatcher_->StopStreamDevice(*device_it); 395 } 396 } 397 398 void UserMediaClientImpl::FinalizeEnumerateDevices( 399 MediaDevicesRequestInfo* request) { 400 // All devices are ready for copying. We use a hashed audio output device id 401 // as the group id for input and output audio devices. If an input device 402 // doesn't have an associated output device, we use the input device's own id. 403 // We don't support group id for video devices, that's left empty. 404 blink::WebVector<blink::WebMediaDeviceInfo> 405 devices(request->audio_input_devices.size() + 406 request->video_input_devices.size() + 407 request->audio_output_devices.size()); 408 for (size_t i = 0; i < request->audio_input_devices.size(); ++i) { 409 const MediaStreamDevice& device = request->audio_input_devices[i].device; 410 DCHECK_EQ(device.type, MEDIA_DEVICE_AUDIO_CAPTURE); 411 std::string group_id = base::UintToString(base::Hash( 412 !device.matched_output_device_id.empty() ? 413 device.matched_output_device_id : 414 device.id)); 415 devices[i].initialize( 416 blink::WebString::fromUTF8(device.id), 417 blink::WebMediaDeviceInfo::MediaDeviceKindAudioInput, 418 blink::WebString::fromUTF8(device.name), 419 blink::WebString::fromUTF8(group_id)); 420 } 421 size_t offset = request->audio_input_devices.size(); 422 for (size_t i = 0; i < request->video_input_devices.size(); ++i) { 423 const MediaStreamDevice& device = request->video_input_devices[i].device; 424 DCHECK_EQ(device.type, MEDIA_DEVICE_VIDEO_CAPTURE); 425 devices[offset + i].initialize( 426 blink::WebString::fromUTF8(device.id), 427 blink::WebMediaDeviceInfo::MediaDeviceKindVideoInput, 428 blink::WebString::fromUTF8(device.name), 429 blink::WebString()); 430 } 431 offset += request->video_input_devices.size(); 432 for (size_t i = 0; i < request->audio_output_devices.size(); ++i) { 433 const MediaStreamDevice& device = request->audio_output_devices[i].device; 434 DCHECK_EQ(device.type, MEDIA_DEVICE_AUDIO_OUTPUT); 435 devices[offset + i].initialize( 436 blink::WebString::fromUTF8(device.id), 437 blink::WebMediaDeviceInfo::MediaDeviceKindAudioOutput, 438 blink::WebString::fromUTF8(device.name), 439 blink::WebString::fromUTF8(base::UintToString(base::Hash(device.id)))); 440 } 441 442 EnumerateDevicesSucceded(&request->media_devices_request, devices); 443 } 444 445 void UserMediaClientImpl::FinalizeEnumerateSources( 446 MediaDevicesRequestInfo* request) { 447 blink::WebVector<blink::WebSourceInfo> 448 sources(request->audio_input_devices.size() + 449 request->video_input_devices.size()); 450 for (size_t i = 0; i < request->audio_input_devices.size(); ++i) { 451 const MediaStreamDevice& device = request->audio_input_devices[i].device; 452 DCHECK_EQ(device.type, MEDIA_DEVICE_AUDIO_CAPTURE); 453 std::string group_id = base::UintToString(base::Hash( 454 !device.matched_output_device_id.empty() ? 455 device.matched_output_device_id : 456 device.id)); 457 sources[i].initialize(blink::WebString::fromUTF8(device.id), 458 blink::WebSourceInfo::SourceKindAudio, 459 blink::WebString::fromUTF8(device.name), 460 blink::WebSourceInfo::VideoFacingModeNone); 461 } 462 size_t offset = request->audio_input_devices.size(); 463 for (size_t i = 0; i < request->video_input_devices.size(); ++i) { 464 const MediaStreamDevice& device = request->video_input_devices[i].device; 465 DCHECK_EQ(device.type, MEDIA_DEVICE_VIDEO_CAPTURE); 466 blink::WebSourceInfo::VideoFacingMode video_facing; 467 switch (device.video_facing) { 468 case MEDIA_VIDEO_FACING_USER: 469 video_facing = blink::WebSourceInfo::VideoFacingModeUser; 470 break; 471 case MEDIA_VIDEO_FACING_ENVIRONMENT: 472 video_facing = blink::WebSourceInfo::VideoFacingModeEnvironment; 473 break; 474 default: 475 video_facing = blink::WebSourceInfo::VideoFacingModeNone; 476 } 477 sources[offset + i].initialize(blink::WebString::fromUTF8(device.id), 478 blink::WebSourceInfo::SourceKindVideo, 479 blink::WebString::fromUTF8(device.name), 480 video_facing); 481 } 482 483 EnumerateSourcesSucceded(&request->sources_request, sources); 484 } 485 486 // Callback from MediaStreamDispatcher. 487 // The requested stream failed to be generated. 488 void UserMediaClientImpl::OnStreamGenerationFailed( 489 int request_id, 490 MediaStreamRequestResult result) { 491 DCHECK(CalledOnValidThread()); 492 DVLOG(1) << "UserMediaClientImpl::OnStreamGenerationFailed(" 493 << request_id << ")"; 494 UserMediaRequestInfo* request_info = FindUserMediaRequestInfo(request_id); 495 if (!request_info) { 496 // This can happen if the request is canceled or the frame reloads while 497 // MediaStreamDispatcher is processing the request. 498 DVLOG(1) << "Request ID not found"; 499 return; 500 } 501 502 GetUserMediaRequestFailed(&request_info->request, result); 503 DeleteUserMediaRequestInfo(request_info); 504 } 505 506 // Callback from MediaStreamDispatcher. 507 // The browser process has stopped a device used by a MediaStream. 508 void UserMediaClientImpl::OnDeviceStopped( 509 const std::string& label, 510 const StreamDeviceInfo& device_info) { 511 DCHECK(CalledOnValidThread()); 512 DVLOG(1) << "UserMediaClientImpl::OnDeviceStopped(" 513 << "{device_id = " << device_info.device.id << "})"; 514 515 const blink::WebMediaStreamSource* source_ptr = FindLocalSource(device_info); 516 if (!source_ptr) { 517 // This happens if the same device is used in several guM requests or 518 // if a user happen stop a track from JS at the same time 519 // as the underlying media device is unplugged from the system. 520 return; 521 } 522 // By creating |source| it is guaranteed that the blink::WebMediaStreamSource 523 // object is valid during the cleanup. 524 blink::WebMediaStreamSource source(*source_ptr); 525 StopLocalSource(source, false); 526 527 for (LocalStreamSources::iterator device_it = local_sources_.begin(); 528 device_it != local_sources_.end(); ++device_it) { 529 if (device_it->id() == source.id()) { 530 local_sources_.erase(device_it); 531 break; 532 } 533 } 534 } 535 536 void UserMediaClientImpl::InitializeSourceObject( 537 const StreamDeviceInfo& device, 538 blink::WebMediaStreamSource::Type type, 539 const blink::WebMediaConstraints& constraints, 540 blink::WebMediaStreamSource* webkit_source) { 541 const blink::WebMediaStreamSource* existing_source = 542 FindLocalSource(device); 543 if (existing_source) { 544 *webkit_source = *existing_source; 545 DVLOG(1) << "Source already exist. Reusing source with id " 546 << webkit_source->id().utf8(); 547 return; 548 } 549 550 webkit_source->initialize( 551 base::UTF8ToUTF16(device.device.id), 552 type, 553 base::UTF8ToUTF16(device.device.name)); 554 555 DVLOG(1) << "Initialize source object :" 556 << "id = " << webkit_source->id().utf8() 557 << ", name = " << webkit_source->name().utf8(); 558 559 if (type == blink::WebMediaStreamSource::TypeVideo) { 560 webkit_source->setExtraData( 561 CreateVideoSource( 562 device, 563 base::Bind(&UserMediaClientImpl::OnLocalSourceStopped, 564 weak_factory_.GetWeakPtr()))); 565 } else { 566 DCHECK_EQ(blink::WebMediaStreamSource::TypeAudio, type); 567 MediaStreamAudioSource* audio_source( 568 new MediaStreamAudioSource( 569 RenderFrameObserver::routing_id(), 570 device, 571 base::Bind(&UserMediaClientImpl::OnLocalSourceStopped, 572 weak_factory_.GetWeakPtr()), 573 dependency_factory_)); 574 webkit_source->setExtraData(audio_source); 575 } 576 local_sources_.push_back(*webkit_source); 577 } 578 579 MediaStreamVideoSource* UserMediaClientImpl::CreateVideoSource( 580 const StreamDeviceInfo& device, 581 const MediaStreamSource::SourceStoppedCallback& stop_callback) { 582 return new content::MediaStreamVideoCapturerSource( 583 device, 584 stop_callback, 585 new VideoCapturerDelegate(device)); 586 } 587 588 void UserMediaClientImpl::CreateVideoTracks( 589 const StreamDeviceInfoArray& devices, 590 const blink::WebMediaConstraints& constraints, 591 blink::WebVector<blink::WebMediaStreamTrack>* webkit_tracks, 592 UserMediaRequestInfo* request) { 593 DCHECK_EQ(devices.size(), webkit_tracks->size()); 594 595 for (size_t i = 0; i < devices.size(); ++i) { 596 blink::WebMediaStreamSource webkit_source; 597 InitializeSourceObject(devices[i], 598 blink::WebMediaStreamSource::TypeVideo, 599 constraints, 600 &webkit_source); 601 (*webkit_tracks)[i] = 602 request->CreateAndStartVideoTrack(webkit_source, constraints); 603 } 604 } 605 606 void UserMediaClientImpl::CreateAudioTracks( 607 const StreamDeviceInfoArray& devices, 608 const blink::WebMediaConstraints& constraints, 609 blink::WebVector<blink::WebMediaStreamTrack>* webkit_tracks, 610 UserMediaRequestInfo* request) { 611 DCHECK_EQ(devices.size(), webkit_tracks->size()); 612 613 // Log the device names for this request. 614 for (StreamDeviceInfoArray::const_iterator it = devices.begin(); 615 it != devices.end(); ++it) { 616 WebRtcLogMessage(base::StringPrintf( 617 "Generated media stream for request id %d contains audio device name" 618 " \"%s\"", 619 request->request_id, 620 it->device.name.c_str())); 621 } 622 623 StreamDeviceInfoArray overridden_audio_array = devices; 624 if (!request->enable_automatic_output_device_selection) { 625 // If the GetUserMedia request did not explicitly set the constraint 626 // kMediaStreamRenderToAssociatedSink, the output device parameters must 627 // be removed. 628 for (StreamDeviceInfoArray::iterator it = overridden_audio_array.begin(); 629 it != overridden_audio_array.end(); ++it) { 630 it->device.matched_output_device_id = ""; 631 it->device.matched_output = MediaStreamDevice::AudioDeviceParameters(); 632 } 633 } 634 635 for (size_t i = 0; i < overridden_audio_array.size(); ++i) { 636 blink::WebMediaStreamSource webkit_source; 637 InitializeSourceObject(overridden_audio_array[i], 638 blink::WebMediaStreamSource::TypeAudio, 639 constraints, 640 &webkit_source); 641 (*webkit_tracks)[i].initialize(webkit_source); 642 request->StartAudioTrack((*webkit_tracks)[i], constraints); 643 } 644 } 645 646 void UserMediaClientImpl::OnCreateNativeTracksCompleted( 647 UserMediaRequestInfo* request, 648 MediaStreamRequestResult result, 649 const blink::WebString& result_name) { 650 DVLOG(1) << "UserMediaClientImpl::OnCreateNativeTracksComplete(" 651 << "{request_id = " << request->request_id << "} " 652 << "{result = " << result << "})"; 653 if (result == content::MEDIA_DEVICE_OK) 654 GetUserMediaRequestSucceeded(request->web_stream, &request->request); 655 else 656 GetUserMediaRequestTrackStartedFailed(&request->request, 657 result, 658 result_name); 659 660 DeleteUserMediaRequestInfo(request); 661 } 662 663 void UserMediaClientImpl::OnDevicesEnumerated( 664 int request_id, 665 const StreamDeviceInfoArray& device_array) { 666 DVLOG(1) << "UserMediaClientImpl::OnDevicesEnumerated(" << request_id << ")"; 667 668 MediaDevicesRequestInfo* request = FindMediaDevicesRequestInfo(request_id); 669 DCHECK(request); 670 671 if (request_id == request->audio_input_request_id) { 672 request->has_audio_input_returned = true; 673 DCHECK(request->audio_input_devices.empty()); 674 request->audio_input_devices = device_array; 675 } else if (request_id == request->video_input_request_id) { 676 request->has_video_input_returned = true; 677 DCHECK(request->video_input_devices.empty()); 678 request->video_input_devices = device_array; 679 } else { 680 DCHECK_EQ(request->audio_output_request_id, request_id); 681 request->has_audio_output_returned = true; 682 DCHECK(request->audio_output_devices.empty()); 683 request->audio_output_devices = device_array; 684 } 685 686 if (!request->has_audio_input_returned || 687 !request->has_video_input_returned || 688 (!request->IsSourcesRequest() && !request->has_audio_output_returned)) { 689 // Wait for the rest of the devices to complete. 690 return; 691 } 692 693 if (request->IsSourcesRequest()) 694 FinalizeEnumerateSources(request); 695 else 696 FinalizeEnumerateDevices(request); 697 698 CancelAndDeleteMediaDevicesRequest(request); 699 } 700 701 void UserMediaClientImpl::OnDeviceOpened( 702 int request_id, 703 const std::string& label, 704 const StreamDeviceInfo& video_device) { 705 DVLOG(1) << "UserMediaClientImpl::OnDeviceOpened(" 706 << request_id << ", " << label << ")"; 707 NOTIMPLEMENTED(); 708 } 709 710 void UserMediaClientImpl::OnDeviceOpenFailed(int request_id) { 711 DVLOG(1) << "UserMediaClientImpl::VideoDeviceOpenFailed(" 712 << request_id << ")"; 713 NOTIMPLEMENTED(); 714 } 715 716 void UserMediaClientImpl::GetUserMediaRequestSucceeded( 717 const blink::WebMediaStream& stream, 718 blink::WebUserMediaRequest* request_info) { 719 DVLOG(1) << "UserMediaClientImpl::GetUserMediaRequestSucceeded"; 720 LogUserMediaRequestResult(MEDIA_DEVICE_OK); 721 request_info->requestSucceeded(stream); 722 } 723 724 void UserMediaClientImpl::GetUserMediaRequestFailed( 725 blink::WebUserMediaRequest* request_info, 726 MediaStreamRequestResult result) { 727 LogUserMediaRequestResult(result); 728 switch (result) { 729 case MEDIA_DEVICE_OK: 730 NOTREACHED(); 731 break; 732 case MEDIA_DEVICE_PERMISSION_DENIED: 733 request_info->requestDenied(); 734 break; 735 case MEDIA_DEVICE_PERMISSION_DISMISSED: 736 request_info->requestFailedUASpecific("PermissionDismissedError"); 737 break; 738 case MEDIA_DEVICE_INVALID_STATE: 739 request_info->requestFailedUASpecific("InvalidStateError"); 740 break; 741 case MEDIA_DEVICE_NO_HARDWARE: 742 request_info->requestFailedUASpecific("DevicesNotFoundError"); 743 break; 744 case MEDIA_DEVICE_INVALID_SECURITY_ORIGIN: 745 request_info->requestFailedUASpecific("InvalidSecurityOriginError"); 746 break; 747 case MEDIA_DEVICE_TAB_CAPTURE_FAILURE: 748 request_info->requestFailedUASpecific("TabCaptureError"); 749 break; 750 case MEDIA_DEVICE_SCREEN_CAPTURE_FAILURE: 751 request_info->requestFailedUASpecific("ScreenCaptureError"); 752 break; 753 case MEDIA_DEVICE_CAPTURE_FAILURE: 754 request_info->requestFailedUASpecific("DeviceCaptureError"); 755 break; 756 default: 757 NOTREACHED(); 758 request_info->requestFailed(); 759 break; 760 } 761 } 762 763 void UserMediaClientImpl::GetUserMediaRequestTrackStartedFailed( 764 blink::WebUserMediaRequest* request_info, 765 MediaStreamRequestResult result, 766 const blink::WebString& result_name) { 767 switch (result) { 768 case MEDIA_DEVICE_CONSTRAINT_NOT_SATISFIED: 769 request_info->requestFailedConstraint(result_name); 770 break; 771 case MEDIA_DEVICE_TRACK_START_FAILURE: 772 request_info->requestFailedUASpecific("TrackStartError"); 773 break; 774 default: 775 NOTREACHED(); 776 request_info->requestFailed(); 777 break; 778 } 779 } 780 781 void UserMediaClientImpl::EnumerateDevicesSucceded( 782 blink::WebMediaDevicesRequest* request, 783 blink::WebVector<blink::WebMediaDeviceInfo>& devices) { 784 request->requestSucceeded(devices); 785 } 786 787 void UserMediaClientImpl::EnumerateSourcesSucceded( 788 blink::WebMediaStreamTrackSourcesRequest* request, 789 blink::WebVector<blink::WebSourceInfo>& sources) { 790 request->requestSucceeded(sources); 791 } 792 793 const blink::WebMediaStreamSource* UserMediaClientImpl::FindLocalSource( 794 const StreamDeviceInfo& device) const { 795 for (LocalStreamSources::const_iterator it = local_sources_.begin(); 796 it != local_sources_.end(); ++it) { 797 MediaStreamSource* const source = 798 static_cast<MediaStreamSource*>(it->extraData()); 799 const StreamDeviceInfo& active_device = source->device_info(); 800 if (active_device.device.id == device.device.id && 801 active_device.device.type == device.device.type && 802 active_device.session_id == device.session_id) { 803 return &(*it); 804 } 805 } 806 return NULL; 807 } 808 809 UserMediaClientImpl::UserMediaRequestInfo* 810 UserMediaClientImpl::FindUserMediaRequestInfo(int request_id) { 811 UserMediaRequests::iterator it = user_media_requests_.begin(); 812 for (; it != user_media_requests_.end(); ++it) { 813 if ((*it)->request_id == request_id) 814 return (*it); 815 } 816 return NULL; 817 } 818 819 UserMediaClientImpl::UserMediaRequestInfo* 820 UserMediaClientImpl::FindUserMediaRequestInfo( 821 const blink::WebUserMediaRequest& request) { 822 UserMediaRequests::iterator it = user_media_requests_.begin(); 823 for (; it != user_media_requests_.end(); ++it) { 824 if ((*it)->request == request) 825 return (*it); 826 } 827 return NULL; 828 } 829 830 void UserMediaClientImpl::DeleteUserMediaRequestInfo( 831 UserMediaRequestInfo* request) { 832 UserMediaRequests::iterator it = user_media_requests_.begin(); 833 for (; it != user_media_requests_.end(); ++it) { 834 if ((*it) == request) { 835 user_media_requests_.erase(it); 836 return; 837 } 838 } 839 NOTREACHED(); 840 } 841 842 void UserMediaClientImpl::DeleteAllUserMediaRequests() { 843 UserMediaRequests::iterator request_it = user_media_requests_.begin(); 844 while (request_it != user_media_requests_.end()) { 845 DVLOG(1) << "UserMediaClientImpl@" << this 846 << "::DeleteAllUserMediaRequests: " 847 << "Cancel user media request " << (*request_it)->request_id; 848 // If the request is not generated, it means that a request 849 // has been sent to the MediaStreamDispatcher to generate a stream 850 // but MediaStreamDispatcher has not yet responded and we need to cancel 851 // the request. 852 if (!(*request_it)->generated) { 853 DCHECK(!(*request_it)->HasPendingSources()); 854 media_stream_dispatcher_->CancelGenerateStream( 855 (*request_it)->request_id, weak_factory_.GetWeakPtr()); 856 LogUserMediaRequestWithNoResult(MEDIA_STREAM_REQUEST_NOT_GENERATED); 857 } else { 858 DCHECK((*request_it)->HasPendingSources()); 859 LogUserMediaRequestWithNoResult( 860 MEDIA_STREAM_REQUEST_PENDING_MEDIA_TRACKS); 861 } 862 request_it = user_media_requests_.erase(request_it); 863 } 864 } 865 866 UserMediaClientImpl::MediaDevicesRequestInfo* 867 UserMediaClientImpl::FindMediaDevicesRequestInfo( 868 int request_id) { 869 MediaDevicesRequests::iterator it = media_devices_requests_.begin(); 870 for (; it != media_devices_requests_.end(); ++it) { 871 if ((*it)->audio_input_request_id == request_id || 872 (*it)->video_input_request_id == request_id || 873 (*it)->audio_output_request_id == request_id) { 874 return (*it); 875 } 876 } 877 return NULL; 878 } 879 880 UserMediaClientImpl::MediaDevicesRequestInfo* 881 UserMediaClientImpl::FindMediaDevicesRequestInfo( 882 const blink::WebMediaDevicesRequest& request) { 883 MediaDevicesRequests::iterator it = media_devices_requests_.begin(); 884 for (; it != media_devices_requests_.end(); ++it) { 885 if ((*it)->media_devices_request == request) 886 return (*it); 887 } 888 return NULL; 889 } 890 891 void UserMediaClientImpl::CancelAndDeleteMediaDevicesRequest( 892 MediaDevicesRequestInfo* request) { 893 MediaDevicesRequests::iterator it = media_devices_requests_.begin(); 894 for (; it != media_devices_requests_.end(); ++it) { 895 if ((*it) == request) { 896 // Cancel device enumeration. 897 media_stream_dispatcher_->StopEnumerateDevices( 898 request->audio_input_request_id, weak_factory_.GetWeakPtr()); 899 media_stream_dispatcher_->StopEnumerateDevices( 900 request->video_input_request_id, weak_factory_.GetWeakPtr()); 901 media_stream_dispatcher_->StopEnumerateDevices( 902 request->audio_output_request_id, weak_factory_.GetWeakPtr()); 903 904 media_devices_requests_.erase(it); 905 return; 906 } 907 } 908 NOTREACHED(); 909 } 910 911 void UserMediaClientImpl::FrameWillClose() { 912 // Cancel all outstanding UserMediaRequests. 913 DeleteAllUserMediaRequests(); 914 915 // Loop through all current local sources and stop the sources. 916 LocalStreamSources::iterator sources_it = local_sources_.begin(); 917 while (sources_it != local_sources_.end()) { 918 StopLocalSource(*sources_it, true); 919 sources_it = local_sources_.erase(sources_it); 920 } 921 } 922 923 void UserMediaClientImpl::OnLocalSourceStopped( 924 const blink::WebMediaStreamSource& source) { 925 DCHECK(CalledOnValidThread()); 926 DVLOG(1) << "UserMediaClientImpl::OnLocalSourceStopped"; 927 928 bool device_found = false; 929 for (LocalStreamSources::iterator device_it = local_sources_.begin(); 930 device_it != local_sources_.end(); ++device_it) { 931 if (device_it->id() == source.id()) { 932 device_found = true; 933 local_sources_.erase(device_it); 934 break; 935 } 936 } 937 CHECK(device_found); 938 939 MediaStreamSource* source_impl = 940 static_cast<MediaStreamSource*>(source.extraData()); 941 media_stream_dispatcher_->StopStreamDevice(source_impl->device_info()); 942 } 943 944 void UserMediaClientImpl::StopLocalSource( 945 const blink::WebMediaStreamSource& source, 946 bool notify_dispatcher) { 947 MediaStreamSource* source_impl = 948 static_cast<MediaStreamSource*>(source.extraData()); 949 DVLOG(1) << "UserMediaClientImpl::StopLocalSource(" 950 << "{device_id = " << source_impl->device_info().device.id << "})"; 951 952 if (notify_dispatcher) 953 media_stream_dispatcher_->StopStreamDevice(source_impl->device_info()); 954 955 source_impl->ResetSourceStoppedCallback(); 956 source_impl->StopSource(); 957 } 958 959 UserMediaClientImpl::UserMediaRequestInfo::UserMediaRequestInfo( 960 int request_id, 961 const blink::WebUserMediaRequest& request, 962 bool enable_automatic_output_device_selection) 963 : request_id(request_id), 964 generated(false), 965 enable_automatic_output_device_selection( 966 enable_automatic_output_device_selection), 967 request(request), 968 request_result_(MEDIA_DEVICE_OK), 969 request_result_name_("") { 970 } 971 972 UserMediaClientImpl::UserMediaRequestInfo::~UserMediaRequestInfo() { 973 DVLOG(1) << "~UserMediaRequestInfo"; 974 } 975 976 void UserMediaClientImpl::UserMediaRequestInfo::StartAudioTrack( 977 const blink::WebMediaStreamTrack& track, 978 const blink::WebMediaConstraints& constraints) { 979 DCHECK(track.source().type() == blink::WebMediaStreamSource::TypeAudio); 980 MediaStreamAudioSource* native_source = 981 static_cast <MediaStreamAudioSource*>(track.source().extraData()); 982 DCHECK(native_source); 983 984 sources_.push_back(track.source()); 985 sources_waiting_for_callback_.push_back(native_source); 986 native_source->AddTrack( 987 track, constraints, base::Bind( 988 &UserMediaClientImpl::UserMediaRequestInfo::OnTrackStarted, 989 AsWeakPtr())); 990 } 991 992 blink::WebMediaStreamTrack 993 UserMediaClientImpl::UserMediaRequestInfo::CreateAndStartVideoTrack( 994 const blink::WebMediaStreamSource& source, 995 const blink::WebMediaConstraints& constraints) { 996 DCHECK(source.type() == blink::WebMediaStreamSource::TypeVideo); 997 MediaStreamVideoSource* native_source = 998 MediaStreamVideoSource::GetVideoSource(source); 999 DCHECK(native_source); 1000 sources_.push_back(source); 1001 sources_waiting_for_callback_.push_back(native_source); 1002 return MediaStreamVideoTrack::CreateVideoTrack( 1003 native_source, constraints, base::Bind( 1004 &UserMediaClientImpl::UserMediaRequestInfo::OnTrackStarted, 1005 AsWeakPtr()), 1006 true); 1007 } 1008 1009 void UserMediaClientImpl::UserMediaRequestInfo::CallbackOnTracksStarted( 1010 const ResourcesReady& callback) { 1011 DCHECK(ready_callback_.is_null()); 1012 ready_callback_ = callback; 1013 CheckAllTracksStarted(); 1014 } 1015 1016 void UserMediaClientImpl::UserMediaRequestInfo::OnTrackStarted( 1017 MediaStreamSource* source, 1018 MediaStreamRequestResult result, 1019 const blink::WebString& result_name) { 1020 DVLOG(1) << "OnTrackStarted result " << result; 1021 std::vector<MediaStreamSource*>::iterator it = 1022 std::find(sources_waiting_for_callback_.begin(), 1023 sources_waiting_for_callback_.end(), 1024 source); 1025 DCHECK(it != sources_waiting_for_callback_.end()); 1026 sources_waiting_for_callback_.erase(it); 1027 // All tracks must be started successfully. Otherwise the request is a 1028 // failure. 1029 if (result != MEDIA_DEVICE_OK) { 1030 request_result_ = result; 1031 request_result_name_ = result_name; 1032 } 1033 1034 CheckAllTracksStarted(); 1035 } 1036 1037 void UserMediaClientImpl::UserMediaRequestInfo::CheckAllTracksStarted() { 1038 if (!ready_callback_.is_null() && sources_waiting_for_callback_.empty()) { 1039 ready_callback_.Run(this, request_result_, request_result_name_); 1040 } 1041 } 1042 1043 bool UserMediaClientImpl::UserMediaRequestInfo::IsSourceUsed( 1044 const blink::WebMediaStreamSource& source) const { 1045 for (std::vector<blink::WebMediaStreamSource>::const_iterator source_it = 1046 sources_.begin(); 1047 source_it != sources_.end(); ++source_it) { 1048 if (source_it->id() == source.id()) 1049 return true; 1050 } 1051 return false; 1052 } 1053 1054 void UserMediaClientImpl::UserMediaRequestInfo::RemoveSource( 1055 const blink::WebMediaStreamSource& source) { 1056 for (std::vector<blink::WebMediaStreamSource>::iterator it = 1057 sources_.begin(); 1058 it != sources_.end(); ++it) { 1059 if (source.id() == it->id()) { 1060 sources_.erase(it); 1061 return; 1062 } 1063 } 1064 } 1065 1066 bool UserMediaClientImpl::UserMediaRequestInfo::HasPendingSources() const { 1067 return !sources_waiting_for_callback_.empty(); 1068 } 1069 1070 } // namespace content 1071