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