| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/renderer/media/media_stream_impl.h" |
| |
| #include <utility> |
| |
| #include "base/logging.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "content/renderer/media/media_stream_audio_renderer.h" |
| #include "content/renderer/media/media_stream_dependency_factory.h" |
| #include "content/renderer/media/media_stream_dispatcher.h" |
| #include "content/renderer/media/media_stream_extra_data.h" |
| #include "content/renderer/media/media_stream_source_extra_data.h" |
| #include "content/renderer/media/rtc_video_renderer.h" |
| #include "content/renderer/media/webrtc_audio_capturer.h" |
| #include "content/renderer/media/webrtc_audio_renderer.h" |
| #include "content/renderer/media/webrtc_local_audio_renderer.h" |
| #include "content/renderer/media/webrtc_logging.h" |
| #include "content/renderer/media/webrtc_uma_histograms.h" |
| #include "content/renderer/render_thread_impl.h" |
| #include "media/base/audio_hardware_config.h" |
| #include "third_party/WebKit/public/platform/WebMediaConstraints.h" |
| #include "third_party/WebKit/public/platform/WebMediaStreamTrack.h" |
| #include "third_party/WebKit/public/web/WebDocument.h" |
| #include "third_party/WebKit/public/web/WebFrame.h" |
| #include "third_party/WebKit/public/web/WebMediaStreamRegistry.h" |
| |
| namespace content { |
| namespace { |
| |
| void CopyStreamConstraints(const blink::WebMediaConstraints& constraints, |
| StreamOptions::Constraints* mandatory, |
| StreamOptions::Constraints* optional) { |
| blink::WebVector<blink::WebMediaConstraint> mandatory_constraints; |
| constraints.getMandatoryConstraints(mandatory_constraints); |
| for (size_t i = 0; i < mandatory_constraints.size(); i++) { |
| mandatory->push_back(StreamOptions::Constraint( |
| mandatory_constraints[i].m_name.utf8(), |
| mandatory_constraints[i].m_value.utf8())); |
| } |
| |
| blink::WebVector<blink::WebMediaConstraint> optional_constraints; |
| constraints.getOptionalConstraints(optional_constraints); |
| for (size_t i = 0; i < optional_constraints.size(); i++) { |
| optional->push_back(StreamOptions::Constraint( |
| optional_constraints[i].m_name.utf8(), |
| optional_constraints[i].m_value.utf8())); |
| } |
| } |
| |
| static int g_next_request_id = 0; |
| |
| webrtc::MediaStreamInterface* GetNativeMediaStream( |
| const blink::WebMediaStream& web_stream) { |
| content::MediaStreamExtraData* extra_data = |
| static_cast<content::MediaStreamExtraData*>(web_stream.extraData()); |
| if (!extra_data) |
| return NULL; |
| return extra_data->stream().get(); |
| } |
| |
| void GetDefaultOutputDeviceParams( |
| int* output_sample_rate, int* output_buffer_size) { |
| // Fetch the default audio output hardware config. |
| media::AudioHardwareConfig* hardware_config = |
| RenderThreadImpl::current()->GetAudioHardwareConfig(); |
| *output_sample_rate = hardware_config->GetOutputSampleRate(); |
| *output_buffer_size = hardware_config->GetOutputBufferSize(); |
| } |
| |
| void RemoveSource(const blink::WebMediaStreamSource& source, |
| std::vector<blink::WebMediaStreamSource>* sources) { |
| for (std::vector<blink::WebMediaStreamSource>::iterator it = |
| sources->begin(); |
| it != sources->end(); ++it) { |
| if (source.id() == it->id()) { |
| sources->erase(it); |
| return; |
| } |
| } |
| } |
| |
| } // namespace |
| |
| MediaStreamImpl::MediaStreamImpl( |
| RenderView* render_view, |
| MediaStreamDispatcher* media_stream_dispatcher, |
| MediaStreamDependencyFactory* dependency_factory) |
| : RenderViewObserver(render_view), |
| dependency_factory_(dependency_factory), |
| media_stream_dispatcher_(media_stream_dispatcher) { |
| } |
| |
| MediaStreamImpl::~MediaStreamImpl() { |
| } |
| |
| void MediaStreamImpl::requestUserMedia( |
| const blink::WebUserMediaRequest& user_media_request) { |
| // Save histogram data so we can see how much GetUserMedia is used. |
| // The histogram counts the number of calls to the JS API |
| // webGetUserMedia. |
| UpdateWebRTCMethodCount(WEBKIT_GET_USER_MEDIA); |
| DCHECK(CalledOnValidThread()); |
| int request_id = g_next_request_id++; |
| StreamOptions options; |
| blink::WebFrame* frame = NULL; |
| GURL security_origin; |
| bool enable_automatic_output_device_selection = false; |
| |
| // |user_media_request| can't be mocked. So in order to test at all we check |
| // if it isNull. |
| if (user_media_request.isNull()) { |
| // We are in a test. |
| options.audio_requested = true; |
| options.video_requested = true; |
| } else { |
| if (user_media_request.audio()) { |
| options.audio_requested = true; |
| CopyStreamConstraints(user_media_request.audioConstraints(), |
| &options.mandatory_audio, |
| &options.optional_audio); |
| |
| // Check if this input device should be used to select a matching output |
| // device for audio rendering. |
| std::string enable; |
| if (options.GetFirstAudioConstraintByName( |
| kMediaStreamRenderToAssociatedSink, &enable, NULL) && |
| LowerCaseEqualsASCII(enable, "true")) { |
| enable_automatic_output_device_selection = true; |
| } |
| } |
| if (user_media_request.video()) { |
| options.video_requested = true; |
| CopyStreamConstraints(user_media_request.videoConstraints(), |
| &options.mandatory_video, |
| &options.optional_video); |
| } |
| |
| security_origin = GURL(user_media_request.securityOrigin().toString()); |
| // Get the WebFrame that requested a MediaStream. |
| // The frame is needed to tell the MediaStreamDispatcher when a stream goes |
| // out of scope. |
| frame = user_media_request.ownerDocument().frame(); |
| DCHECK(frame); |
| } |
| |
| DVLOG(1) << "MediaStreamImpl::requestUserMedia(" << request_id << ", [ " |
| << "audio=" << (options.audio_requested) |
| << " select associated sink: " |
| << enable_automatic_output_device_selection |
| << ", video=" << (options.video_requested) << " ], " |
| << security_origin.spec() << ")"; |
| |
| std::string audio_device_id; |
| bool mandatory_audio; |
| options.GetFirstAudioConstraintByName(kMediaStreamSourceInfoId, |
| &audio_device_id, &mandatory_audio); |
| std::string video_device_id; |
| bool mandatory_video; |
| options.GetFirstVideoConstraintByName(kMediaStreamSourceInfoId, |
| &video_device_id, &mandatory_video); |
| |
| WebRtcLogMessage(base::StringPrintf( |
| "MSI::requestUserMedia. request_id=%d" |
| ", audio source id=%s mandatory= %s " |
| ", video source id=%s mandatory= %s", |
| request_id, |
| audio_device_id.c_str(), |
| mandatory_audio ? "true":"false", |
| video_device_id.c_str(), |
| mandatory_video ? "true":"false")); |
| |
| user_media_requests_.push_back( |
| new UserMediaRequestInfo(request_id, frame, user_media_request, |
| enable_automatic_output_device_selection)); |
| |
| media_stream_dispatcher_->GenerateStream( |
| request_id, |
| AsWeakPtr(), |
| options, |
| security_origin); |
| } |
| |
| void MediaStreamImpl::cancelUserMediaRequest( |
| const blink::WebUserMediaRequest& user_media_request) { |
| DCHECK(CalledOnValidThread()); |
| UserMediaRequestInfo* request = FindUserMediaRequestInfo(user_media_request); |
| if (request) { |
| // We can't abort the stream generation process. |
| // Instead, erase the request. Once the stream is generated we will stop the |
| // stream if the request does not exist. |
| DeleteUserMediaRequestInfo(request); |
| } |
| } |
| |
| blink::WebMediaStream MediaStreamImpl::GetMediaStream( |
| const GURL& url) { |
| return blink::WebMediaStreamRegistry::lookupMediaStreamDescriptor(url); |
| } |
| |
| bool MediaStreamImpl::IsMediaStream(const GURL& url) { |
| blink::WebMediaStream web_stream( |
| blink::WebMediaStreamRegistry::lookupMediaStreamDescriptor(url)); |
| |
| if (web_stream.isNull() || !web_stream.extraData()) |
| return false; // This is not a valid stream. |
| |
| webrtc::MediaStreamInterface* stream = GetNativeMediaStream(web_stream); |
| return (stream && |
| (!stream->GetVideoTracks().empty() || !stream->GetAudioTracks().empty())); |
| } |
| |
| scoped_refptr<VideoFrameProvider> |
| MediaStreamImpl::GetVideoFrameProvider( |
| const GURL& url, |
| const base::Closure& error_cb, |
| const VideoFrameProvider::RepaintCB& repaint_cb) { |
| DCHECK(CalledOnValidThread()); |
| blink::WebMediaStream web_stream(GetMediaStream(url)); |
| |
| if (web_stream.isNull() || !web_stream.extraData()) |
| return NULL; // This is not a valid stream. |
| |
| DVLOG(1) << "MediaStreamImpl::GetVideoFrameProvider stream:" |
| << UTF16ToUTF8(web_stream.id()); |
| |
| blink::WebVector<blink::WebMediaStreamTrack> video_tracks; |
| web_stream.videoTracks(video_tracks); |
| if (video_tracks.isEmpty()) |
| return NULL; |
| |
| return new RTCVideoRenderer(video_tracks[0], error_cb, repaint_cb); |
| } |
| |
| scoped_refptr<MediaStreamAudioRenderer> |
| MediaStreamImpl::GetAudioRenderer(const GURL& url) { |
| DCHECK(CalledOnValidThread()); |
| blink::WebMediaStream web_stream(GetMediaStream(url)); |
| |
| if (web_stream.isNull() || !web_stream.extraData()) |
| return NULL; // This is not a valid stream. |
| |
| DVLOG(1) << "MediaStreamImpl::GetAudioRenderer stream:" |
| << UTF16ToUTF8(web_stream.id()); |
| |
| MediaStreamExtraData* extra_data = |
| static_cast<MediaStreamExtraData*>(web_stream.extraData()); |
| |
| if (extra_data->is_local()) { |
| // Create the local audio renderer if the stream contains audio tracks. |
| blink::WebVector<blink::WebMediaStreamTrack> audio_tracks; |
| web_stream.audioTracks(audio_tracks); |
| if (audio_tracks.isEmpty()) |
| return NULL; |
| |
| // TODO(xians): Add support for the case that the media stream contains |
| // multiple audio tracks. |
| return CreateLocalAudioRenderer(audio_tracks[0]); |
| } |
| |
| webrtc::MediaStreamInterface* stream = extra_data->stream().get(); |
| if (!stream || stream->GetAudioTracks().empty()) |
| return NULL; |
| |
| // This is a remote media stream. |
| WebRtcAudioDeviceImpl* audio_device = |
| dependency_factory_->GetWebRtcAudioDevice(); |
| |
| // Share the existing renderer if any, otherwise create a new one. |
| scoped_refptr<WebRtcAudioRenderer> renderer(audio_device->renderer()); |
| if (!renderer.get()) { |
| renderer = CreateRemoteAudioRenderer(extra_data->stream().get()); |
| |
| if (renderer.get() && !audio_device->SetAudioRenderer(renderer.get())) |
| renderer = NULL; |
| } |
| |
| return renderer.get() ? renderer->CreateSharedAudioRendererProxy() : NULL; |
| } |
| |
| // Callback from MediaStreamDispatcher. |
| // The requested stream have been generated by the MediaStreamDispatcher. |
| void MediaStreamImpl::OnStreamGenerated( |
| int request_id, |
| const std::string& label, |
| const StreamDeviceInfoArray& audio_array, |
| const StreamDeviceInfoArray& video_array) { |
| DCHECK(CalledOnValidThread()); |
| DVLOG(1) << "MediaStreamImpl::OnStreamGenerated stream:" << label; |
| |
| UserMediaRequestInfo* request_info = FindUserMediaRequestInfo(request_id); |
| if (!request_info) { |
| // This can happen if the request is canceled or the frame reloads while |
| // MediaStreamDispatcher is processing the request. |
| // Only stop the device if the device is not used in another MediaStream. |
| for (StreamDeviceInfoArray::const_iterator device_it = audio_array.begin(); |
| device_it != audio_array.end(); ++device_it) { |
| if (!FindLocalSource(*device_it)) |
| media_stream_dispatcher_->StopStreamDevice(*device_it); |
| } |
| |
| for (StreamDeviceInfoArray::const_iterator device_it = video_array.begin(); |
| device_it != video_array.end(); ++device_it) { |
| if (!FindLocalSource(*device_it)) |
| media_stream_dispatcher_->StopStreamDevice(*device_it); |
| } |
| |
| DVLOG(1) << "Request ID not found"; |
| return; |
| } |
| request_info->generated = true; |
| |
| blink::WebVector<blink::WebMediaStreamSource> audio_source_vector( |
| audio_array.size()); |
| |
| // Log the device names for this request. |
| for (StreamDeviceInfoArray::const_iterator it = audio_array.begin(); |
| it != audio_array.end(); ++it) { |
| WebRtcLogMessage(base::StringPrintf( |
| "Generated media stream for request id %d contains audio device name" |
| " \"%s\"", |
| request_id, |
| it->device.name.c_str())); |
| } |
| |
| StreamDeviceInfoArray overridden_audio_array = audio_array; |
| if (!request_info->enable_automatic_output_device_selection) { |
| // If the GetUserMedia request did not explicitly set the constraint |
| // kMediaStreamRenderToAssociatedSink, the output device parameters must |
| // be removed. |
| for (StreamDeviceInfoArray::iterator it = overridden_audio_array.begin(); |
| it != overridden_audio_array.end(); ++it) { |
| it->device.matched_output_device_id = ""; |
| it->device.matched_output = MediaStreamDevice::AudioDeviceParameters(); |
| } |
| } |
| CreateWebKitSourceVector(label, overridden_audio_array, |
| blink::WebMediaStreamSource::TypeAudio, |
| request_info->frame, |
| audio_source_vector); |
| |
| blink::WebVector<blink::WebMediaStreamSource> video_source_vector( |
| video_array.size()); |
| CreateWebKitSourceVector(label, video_array, |
| blink::WebMediaStreamSource::TypeVideo, |
| request_info->frame, |
| video_source_vector); |
| blink::WebUserMediaRequest* request = &(request_info->request); |
| blink::WebString webkit_id = UTF8ToUTF16(label); |
| blink::WebMediaStream* web_stream = &(request_info->web_stream); |
| |
| blink::WebVector<blink::WebMediaStreamTrack> audio_track_vector( |
| audio_array.size()); |
| for (size_t i = 0; i < audio_track_vector.size(); ++i) { |
| audio_track_vector[i].initialize(audio_source_vector[i]); |
| request_info->sources.push_back(audio_source_vector[i]); |
| } |
| |
| blink::WebVector<blink::WebMediaStreamTrack> video_track_vector( |
| video_array.size()); |
| for (size_t i = 0; i < video_track_vector.size(); ++i) { |
| video_track_vector[i].initialize(video_source_vector[i]); |
| request_info->sources.push_back(video_source_vector[i]); |
| } |
| |
| web_stream->initialize(webkit_id, audio_track_vector, |
| video_track_vector); |
| |
| // WebUserMediaRequest don't have an implementation in unit tests. |
| // Therefore we need to check for isNull here. |
| blink::WebMediaConstraints audio_constraints = request->isNull() ? |
| blink::WebMediaConstraints() : request->audioConstraints(); |
| blink::WebMediaConstraints video_constraints = request->isNull() ? |
| blink::WebMediaConstraints() : request->videoConstraints(); |
| |
| dependency_factory_->CreateNativeMediaSources( |
| RenderViewObserver::routing_id(), |
| audio_constraints, video_constraints, web_stream, |
| base::Bind(&MediaStreamImpl::OnCreateNativeSourcesComplete, AsWeakPtr())); |
| } |
| |
| // Callback from MediaStreamDispatcher. |
| // The requested stream failed to be generated. |
| void MediaStreamImpl::OnStreamGenerationFailed(int request_id) { |
| DCHECK(CalledOnValidThread()); |
| DVLOG(1) << "MediaStreamImpl::OnStreamGenerationFailed(" |
| << request_id << ")"; |
| UserMediaRequestInfo* request_info = FindUserMediaRequestInfo(request_id); |
| if (!request_info) { |
| // This can happen if the request is canceled or the frame reloads while |
| // MediaStreamDispatcher is processing the request. |
| DVLOG(1) << "Request ID not found"; |
| return; |
| } |
| CompleteGetUserMediaRequest(request_info->web_stream, |
| &request_info->request, |
| false); |
| DeleteUserMediaRequestInfo(request_info); |
| } |
| |
| // Callback from MediaStreamDispatcher. |
| // The browser process has stopped a device used by a MediaStream. |
| void MediaStreamImpl::OnDeviceStopped( |
| const std::string& label, |
| const StreamDeviceInfo& device_info) { |
| DCHECK(CalledOnValidThread()); |
| DVLOG(1) << "MediaStreamImpl::OnDeviceStopped(" |
| << "{device_id = " << device_info.device.id << "})"; |
| |
| const blink::WebMediaStreamSource* source_ptr = FindLocalSource(device_info); |
| if (!source_ptr) { |
| // This happens if the same device is used in several guM requests or |
| // if a user happen stop a track from JS at the same time |
| // as the underlying media device is unplugged from the system. |
| return; |
| } |
| // By creating |source| it is guaranteed that the blink::WebMediaStreamSource |
| // object is valid during the cleanup. |
| blink::WebMediaStreamSource source(*source_ptr); |
| StopLocalSource(source, false); |
| |
| for (LocalStreamSources::iterator device_it = local_sources_.begin(); |
| device_it != local_sources_.end(); ++device_it) { |
| if (device_it->source.id() == source.id()) { |
| local_sources_.erase(device_it); |
| break; |
| } |
| } |
| |
| // Remove the reference to this source from all |user_media_requests_|. |
| // TODO(perkj): The below is not necessary once we don't need to support |
| // MediaStream::Stop(). |
| UserMediaRequests::iterator it = user_media_requests_.begin(); |
| while (it != user_media_requests_.end()) { |
| RemoveSource(source, &(*it)->sources); |
| if ((*it)->sources.empty()) { |
| it = user_media_requests_.erase(it); |
| } else { |
| ++it; |
| } |
| } |
| } |
| |
| void MediaStreamImpl::CreateWebKitSourceVector( |
| const std::string& label, |
| const StreamDeviceInfoArray& devices, |
| blink::WebMediaStreamSource::Type type, |
| blink::WebFrame* frame, |
| blink::WebVector<blink::WebMediaStreamSource>& webkit_sources) { |
| CHECK_EQ(devices.size(), webkit_sources.size()); |
| for (size_t i = 0; i < devices.size(); ++i) { |
| const blink::WebMediaStreamSource* existing_source = |
| FindLocalSource(devices[i]); |
| if (existing_source) { |
| webkit_sources[i] = *existing_source; |
| DVLOG(1) << "Source already exist. Reusing source with id " |
| << webkit_sources[i]. id().utf8(); |
| continue; |
| } |
| webkit_sources[i].initialize( |
| UTF8ToUTF16(devices[i].device.id), |
| type, |
| UTF8ToUTF16(devices[i].device.name)); |
| MediaStreamSourceExtraData* source_extra_data( |
| new content::MediaStreamSourceExtraData( |
| devices[i], |
| base::Bind(&MediaStreamImpl::OnLocalSourceStop, AsWeakPtr()))); |
| // |source_extra_data| is owned by webkit_sources[i]. |
| webkit_sources[i].setExtraData(source_extra_data); |
| local_sources_.push_back(LocalStreamSource(frame, webkit_sources[i])); |
| } |
| } |
| |
| // Callback from MediaStreamDependencyFactory when the sources in |web_stream| |
| // have been generated. |
| void MediaStreamImpl::OnCreateNativeSourcesComplete( |
| blink::WebMediaStream* web_stream, |
| bool request_succeeded) { |
| UserMediaRequestInfo* request_info = FindUserMediaRequestInfo(web_stream); |
| if (!request_info) { |
| // This can happen if the request is canceled or the frame reloads while |
| // MediaStreamDependencyFactory is creating the sources. |
| DVLOG(1) << "Request ID not found"; |
| return; |
| } |
| |
| // Create a native representation of the stream. |
| if (request_succeeded) { |
| dependency_factory_->CreateNativeLocalMediaStream( |
| web_stream, |
| base::Bind(&MediaStreamImpl::OnLocalMediaStreamStop, AsWeakPtr())); |
| } |
| DVLOG(1) << "MediaStreamImpl::OnCreateNativeSourcesComplete(" |
| << "{request_id = " << request_info->request_id << "} " |
| << "{request_succeeded = " << request_succeeded << "})"; |
| CompleteGetUserMediaRequest(request_info->web_stream, &request_info->request, |
| request_succeeded); |
| if (!request_succeeded) { |
| // TODO(perkj): Once we don't support MediaStream::Stop the |request_info| |
| // can be deleted even if the request succeeds. |
| DeleteUserMediaRequestInfo(request_info); |
| StopUnreferencedSources(true); |
| } |
| } |
| |
| void MediaStreamImpl::OnDevicesEnumerated( |
| int request_id, |
| const StreamDeviceInfoArray& device_array) { |
| DVLOG(1) << "MediaStreamImpl::OnDevicesEnumerated(" |
| << request_id << ")"; |
| NOTIMPLEMENTED(); |
| } |
| |
| void MediaStreamImpl::OnDeviceOpened( |
| int request_id, |
| const std::string& label, |
| const StreamDeviceInfo& video_device) { |
| DVLOG(1) << "MediaStreamImpl::OnDeviceOpened(" |
| << request_id << ", " << label << ")"; |
| NOTIMPLEMENTED(); |
| } |
| |
| void MediaStreamImpl::OnDeviceOpenFailed(int request_id) { |
| DVLOG(1) << "MediaStreamImpl::VideoDeviceOpenFailed(" |
| << request_id << ")"; |
| NOTIMPLEMENTED(); |
| } |
| |
| void MediaStreamImpl::CompleteGetUserMediaRequest( |
| const blink::WebMediaStream& stream, |
| blink::WebUserMediaRequest* request_info, |
| bool request_succeeded) { |
| if (request_succeeded) { |
| request_info->requestSucceeded(stream); |
| } else { |
| request_info->requestFailed(); |
| } |
| } |
| |
| const blink::WebMediaStreamSource* MediaStreamImpl::FindLocalSource( |
| const StreamDeviceInfo& device) const { |
| for (LocalStreamSources::const_iterator it = local_sources_.begin(); |
| it != local_sources_.end(); ++it) { |
| MediaStreamSourceExtraData* extra_data = |
| static_cast<MediaStreamSourceExtraData*>( |
| it->source.extraData()); |
| const StreamDeviceInfo& active_device = extra_data->device_info(); |
| if (active_device.device.id == device.device.id && |
| active_device.device.type == device.device.type && |
| active_device.session_id == device.session_id) { |
| return &it->source; |
| } |
| } |
| return NULL; |
| } |
| |
| bool MediaStreamImpl::FindSourceInRequests( |
| const blink::WebMediaStreamSource& source) const { |
| for (UserMediaRequests::const_iterator req_it = user_media_requests_.begin(); |
| req_it != user_media_requests_.end(); ++req_it) { |
| const std::vector<blink::WebMediaStreamSource>& sources = |
| (*req_it)->sources; |
| for (std::vector<blink::WebMediaStreamSource>::const_iterator source_it = |
| sources.begin(); |
| source_it != sources.end(); ++source_it) { |
| if (source_it->id() == source.id()) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| MediaStreamImpl::UserMediaRequestInfo* |
| MediaStreamImpl::FindUserMediaRequestInfo(int request_id) { |
| UserMediaRequests::iterator it = user_media_requests_.begin(); |
| for (; it != user_media_requests_.end(); ++it) { |
| if ((*it)->request_id == request_id) |
| return (*it); |
| } |
| return NULL; |
| } |
| |
| MediaStreamImpl::UserMediaRequestInfo* |
| MediaStreamImpl::FindUserMediaRequestInfo( |
| const blink::WebUserMediaRequest& request) { |
| UserMediaRequests::iterator it = user_media_requests_.begin(); |
| for (; it != user_media_requests_.end(); ++it) { |
| if ((*it)->request == request) |
| return (*it); |
| } |
| return NULL; |
| } |
| |
| MediaStreamImpl::UserMediaRequestInfo* |
| MediaStreamImpl::FindUserMediaRequestInfo(const std::string& label) { |
| UserMediaRequests::iterator it = user_media_requests_.begin(); |
| for (; it != user_media_requests_.end(); ++it) { |
| if ((*it)->generated && (*it)->web_stream.id() == UTF8ToUTF16(label)) |
| return (*it); |
| } |
| return NULL; |
| } |
| |
| MediaStreamImpl::UserMediaRequestInfo* |
| MediaStreamImpl::FindUserMediaRequestInfo( |
| blink::WebMediaStream* web_stream) { |
| UserMediaRequests::iterator it = user_media_requests_.begin(); |
| for (; it != user_media_requests_.end(); ++it) { |
| if (&((*it)->web_stream) == web_stream) |
| return (*it); |
| } |
| return NULL; |
| } |
| |
| void MediaStreamImpl::DeleteUserMediaRequestInfo( |
| UserMediaRequestInfo* request) { |
| UserMediaRequests::iterator it = user_media_requests_.begin(); |
| for (; it != user_media_requests_.end(); ++it) { |
| if ((*it) == request) { |
| user_media_requests_.erase(it); |
| return; |
| } |
| } |
| NOTREACHED(); |
| } |
| |
| void MediaStreamImpl::FrameDetached(blink::WebFrame* frame) { |
| // Do same thing as FrameWillClose. |
| FrameWillClose(frame); |
| } |
| |
| void MediaStreamImpl::FrameWillClose(blink::WebFrame* frame) { |
| // Loop through all UserMediaRequests and find the requests that belong to the |
| // frame that is being closed. |
| UserMediaRequests::iterator request_it = user_media_requests_.begin(); |
| while (request_it != user_media_requests_.end()) { |
| if ((*request_it)->frame == frame) { |
| DVLOG(1) << "MediaStreamImpl::FrameWillClose: " |
| << "Cancel user media request " << (*request_it)->request_id; |
| // If the request is not generated, it means that a request |
| // has been sent to the MediaStreamDispatcher to generate a stream |
| // but MediaStreamDispatcher has not yet responded and we need to cancel |
| // the request. |
| if (!(*request_it)->generated) { |
| media_stream_dispatcher_->CancelGenerateStream( |
| (*request_it)->request_id, AsWeakPtr()); |
| } |
| request_it = user_media_requests_.erase(request_it); |
| } else { |
| ++request_it; |
| } |
| } |
| |
| // Loop through all current local sources and stop the sources that were |
| // created by the frame that will be closed. |
| LocalStreamSources::iterator sources_it = local_sources_.begin(); |
| while (sources_it != local_sources_.end()) { |
| if (sources_it->frame == frame) { |
| StopLocalSource(sources_it->source, true); |
| sources_it = local_sources_.erase(sources_it); |
| } else { |
| ++sources_it; |
| } |
| } |
| } |
| |
| void MediaStreamImpl::OnLocalMediaStreamStop( |
| const std::string& label) { |
| DVLOG(1) << "MediaStreamImpl::OnLocalMediaStreamStop(" << label << ")"; |
| |
| UserMediaRequestInfo* user_media_request = FindUserMediaRequestInfo(label); |
| if (user_media_request) { |
| DeleteUserMediaRequestInfo(user_media_request); |
| } |
| StopUnreferencedSources(true); |
| } |
| |
| void MediaStreamImpl::OnLocalSourceStop( |
| const blink::WebMediaStreamSource& source) { |
| DCHECK(CalledOnValidThread()); |
| |
| StopLocalSource(source, true); |
| |
| bool device_found = false; |
| for (LocalStreamSources::iterator device_it = local_sources_.begin(); |
| device_it != local_sources_.end(); ++device_it) { |
| if (device_it->source.id() == source.id()) { |
| device_found = true; |
| local_sources_.erase(device_it); |
| break; |
| } |
| } |
| CHECK(device_found); |
| |
| // Remove the reference to this source from all |user_media_requests_|. |
| // TODO(perkj): The below is not necessary once we don't need to support |
| // MediaStream::Stop(). |
| UserMediaRequests::iterator it = user_media_requests_.begin(); |
| while (it != user_media_requests_.end()) { |
| RemoveSource(source, &(*it)->sources); |
| if ((*it)->sources.empty()) { |
| it = user_media_requests_.erase(it); |
| } else { |
| ++it; |
| } |
| } |
| } |
| |
| void MediaStreamImpl::StopLocalSource( |
| const blink::WebMediaStreamSource& source, |
| bool notify_dispatcher) { |
| MediaStreamSourceExtraData* extra_data = |
| static_cast<MediaStreamSourceExtraData*> (source.extraData()); |
| CHECK(extra_data); |
| DVLOG(1) << "MediaStreamImpl::StopLocalSource(" |
| << "{device_id = " << extra_data->device_info().device.id << "})"; |
| |
| if (source.type() == blink::WebMediaStreamSource::TypeAudio) { |
| if (extra_data->GetAudioCapturer()) { |
| extra_data->GetAudioCapturer()->Stop(); |
| } |
| } |
| |
| if (notify_dispatcher) |
| media_stream_dispatcher_->StopStreamDevice(extra_data->device_info()); |
| |
| blink::WebMediaStreamSource writable_source(source); |
| writable_source.setReadyState( |
| blink::WebMediaStreamSource::ReadyStateEnded); |
| writable_source.setExtraData(NULL); |
| } |
| |
| void MediaStreamImpl::StopUnreferencedSources(bool notify_dispatcher) { |
| LocalStreamSources::iterator source_it = local_sources_.begin(); |
| while (source_it != local_sources_.end()) { |
| if (!FindSourceInRequests(source_it->source)) { |
| StopLocalSource(source_it->source, notify_dispatcher); |
| source_it = local_sources_.erase(source_it); |
| } else { |
| ++source_it; |
| } |
| } |
| } |
| |
| scoped_refptr<WebRtcAudioRenderer> MediaStreamImpl::CreateRemoteAudioRenderer( |
| webrtc::MediaStreamInterface* stream) { |
| if (stream->GetAudioTracks().empty()) |
| return NULL; |
| |
| DVLOG(1) << "MediaStreamImpl::CreateRemoteAudioRenderer label:" |
| << stream->label(); |
| |
| // TODO(tommi): Change the default value of session_id to be |
| // StreamDeviceInfo::kNoId. Also update AudioOutputDevice etc. |
| int session_id = 0, sample_rate = 0, buffer_size = 0; |
| if (!GetAuthorizedDeviceInfoForAudioRenderer(&session_id, |
| &sample_rate, |
| &buffer_size)) { |
| GetDefaultOutputDeviceParams(&sample_rate, &buffer_size); |
| } |
| |
| return new WebRtcAudioRenderer(RenderViewObserver::routing_id(), |
| session_id, sample_rate, buffer_size); |
| } |
| |
| scoped_refptr<WebRtcLocalAudioRenderer> |
| MediaStreamImpl::CreateLocalAudioRenderer( |
| const blink::WebMediaStreamTrack& audio_track) { |
| DVLOG(1) << "MediaStreamImpl::CreateLocalAudioRenderer"; |
| |
| int session_id = 0, sample_rate = 0, buffer_size = 0; |
| if (!GetAuthorizedDeviceInfoForAudioRenderer(&session_id, |
| &sample_rate, |
| &buffer_size)) { |
| GetDefaultOutputDeviceParams(&sample_rate, &buffer_size); |
| } |
| |
| // Create a new WebRtcLocalAudioRenderer instance and connect it to the |
| // existing WebRtcAudioCapturer so that the renderer can use it as source. |
| return new WebRtcLocalAudioRenderer( |
| audio_track, |
| RenderViewObserver::routing_id(), |
| session_id, |
| buffer_size); |
| } |
| |
| bool MediaStreamImpl::GetAuthorizedDeviceInfoForAudioRenderer( |
| int* session_id, |
| int* output_sample_rate, |
| int* output_frames_per_buffer) { |
| DCHECK(CalledOnValidThread()); |
| |
| WebRtcAudioDeviceImpl* audio_device = |
| dependency_factory_->GetWebRtcAudioDevice(); |
| if (!audio_device) |
| return false; |
| |
| if (!audio_device->GetDefaultCapturer()) |
| return false; |
| |
| return audio_device->GetDefaultCapturer()->GetPairedOutputParameters( |
| session_id, |
| output_sample_rate, |
| output_frames_per_buffer); |
| } |
| |
| MediaStreamSourceExtraData::MediaStreamSourceExtraData( |
| const StreamDeviceInfo& device_info, |
| const SourceStopCallback& stop_callback) |
| : device_info_(device_info), |
| stop_callback_(stop_callback) { |
| } |
| |
| MediaStreamSourceExtraData::MediaStreamSourceExtraData() { |
| } |
| |
| MediaStreamSourceExtraData::~MediaStreamSourceExtraData() {} |
| |
| void MediaStreamSourceExtraData::OnLocalSourceStop() { |
| if (!stop_callback_.is_null()) |
| stop_callback_.Run(owner()); |
| } |
| |
| MediaStreamExtraData::MediaStreamExtraData( |
| webrtc::MediaStreamInterface* stream, bool is_local) |
| : stream_(stream), |
| is_local_(is_local) { |
| } |
| |
| MediaStreamExtraData::~MediaStreamExtraData() { |
| } |
| |
| void MediaStreamExtraData::SetLocalStreamStopCallback( |
| const StreamStopCallback& stop_callback) { |
| stream_stop_callback_ = stop_callback; |
| } |
| |
| void MediaStreamExtraData::OnLocalStreamStop() { |
| if (!stream_stop_callback_.is_null()) |
| stream_stop_callback_.Run(stream_->label()); |
| } |
| |
| MediaStreamImpl::UserMediaRequestInfo::UserMediaRequestInfo( |
| int request_id, |
| blink::WebFrame* frame, |
| const blink::WebUserMediaRequest& request, |
| bool enable_automatic_output_device_selection) |
| : request_id(request_id), |
| generated(false), |
| enable_automatic_output_device_selection( |
| enable_automatic_output_device_selection), |
| frame(frame), |
| request(request) { |
| } |
| |
| MediaStreamImpl::UserMediaRequestInfo::~UserMediaRequestInfo() { |
| } |
| |
| } // namespace content |