Chromium Code Reviews| Index: content/renderer/media/media_stream_impl.cc |
| diff --git a/content/renderer/media/media_stream_impl.cc b/content/renderer/media/media_stream_impl.cc |
| index ab48883cbd1eee4aaede86848f823cc6a537496a..fd698115561ae92f8b2cc5a9e05a2b12f4c7e57f 100644 |
| --- a/content/renderer/media/media_stream_impl.cc |
| +++ b/content/renderer/media/media_stream_impl.cc |
| @@ -23,9 +23,6 @@ |
| #include "content/renderer/p2p/socket_dispatcher.h" |
| #include "jingle/glue/thread_wrapper.h" |
| #include "media/base/message_loop_factory.h" |
| -#include "third_party/libjingle/source/talk/p2p/client/httpportallocator.h" |
| -#include "third_party/libjingle/source/talk/session/phone/dummydevicemanager.h" |
| -#include "third_party/libjingle/source/talk/session/phone/webrtcmediaengine.h" |
| #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebMediaStreamDescriptor.h" |
| #include "third_party/WebKit/Source/WebKit/chromium/public/WebMediaStreamRegistry.h" |
| #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebMediaStreamSource.h" |
| @@ -41,6 +38,9 @@ static const int kVideoCaptureFramePerSecond = 30; |
| int MediaStreamImpl::next_request_id_ = 0; |
| +// TODO(grunell): BEFORE COMMIT. Add DCHECK(CalledOnValidThread()) in all |
| +// functions. |
| + |
| MediaStreamImpl::MediaStreamImpl( |
| MediaStreamDispatcher* media_stream_dispatcher, |
| content::P2PSocketDispatcher* p2p_socket_dispatcher, |
| @@ -48,7 +48,6 @@ MediaStreamImpl::MediaStreamImpl( |
| MediaStreamDependencyFactory* dependency_factory) |
| : dependency_factory_(dependency_factory), |
| media_stream_dispatcher_(media_stream_dispatcher), |
| - media_engine_(NULL), |
| p2p_socket_dispatcher_(p2p_socket_dispatcher), |
| network_manager_(NULL), |
| vc_manager_(vc_manager), |
| @@ -56,14 +55,13 @@ MediaStreamImpl::MediaStreamImpl( |
| message_loop_proxy_(base::MessageLoopProxy::current()), |
| signaling_thread_(NULL), |
| worker_thread_(NULL), |
| - chrome_worker_thread_("Chrome_libJingle_WorkerThread"), |
| - vcm_created_(false) { |
| + chrome_worker_thread_("Chrome_libJingle_WorkerThread") { |
| } |
| MediaStreamImpl::~MediaStreamImpl() { |
| DCHECK(!peer_connection_handler_); |
| if (dependency_factory_.get()) |
| - dependency_factory_->DeletePeerConnectionFactory(); |
| + dependency_factory_->ReleasePeerConnectionFactory(); |
| if (network_manager_) { |
| // The network manager needs to free its resources on the thread they were |
| // created, which is the worked thread. |
| @@ -84,87 +82,34 @@ WebKit::WebPeerConnectionHandler* MediaStreamImpl::CreatePeerConnectionHandler( |
| DVLOG(1) << "A PeerConnection already exists"; |
| return NULL; |
| } |
| - |
| - if (!media_engine_) { |
| - media_engine_ = dependency_factory_->CreateWebRtcMediaEngine(); |
| - } |
| - |
| - if (!signaling_thread_) { |
| - jingle_glue::JingleThreadWrapper::EnsureForCurrentThread(); |
| - jingle_glue::JingleThreadWrapper::current()->set_send_allowed(true); |
| - signaling_thread_ = jingle_glue::JingleThreadWrapper::current(); |
| - } |
| - |
| - if (!worker_thread_) { |
| - if (!chrome_worker_thread_.IsRunning()) { |
| - if (!chrome_worker_thread_.Start()) { |
| - LOG(ERROR) << "Could not start worker thread"; |
| - delete media_engine_; |
| - media_engine_ = NULL; |
| - signaling_thread_ = NULL; |
| - return NULL; |
| - } |
| - } |
| - base::WaitableEvent event(true, false); |
| - chrome_worker_thread_.message_loop()->PostTask( |
| - FROM_HERE, |
| - base::Bind(&MediaStreamImpl::InitializeWorkerThread, this, |
| - &worker_thread_, &event)); |
| - event.Wait(); |
| - DCHECK(worker_thread_); |
| - } |
| - |
| - if (!network_manager_) |
| - network_manager_ = new content::IpcNetworkManager(p2p_socket_dispatcher_); |
| - |
| - if (!socket_factory_.get()) { |
| - socket_factory_.reset( |
| - new content::IpcPacketSocketFactory(p2p_socket_dispatcher_)); |
| - } |
| - |
| - if (!dependency_factory_->PeerConnectionFactoryCreated()) { |
| - if (!dependency_factory_->CreatePeerConnectionFactory(media_engine_, |
| - worker_thread_)) { |
| - LOG(ERROR) << "Could not initialize PeerConnection factory"; |
| - return NULL; |
| - } |
| - } |
| + EnsurePeerConnectionFactory(); |
| peer_connection_handler_ = new PeerConnectionHandler( |
| client, |
| this, |
| - dependency_factory_.get(), |
| - signaling_thread_, |
| - p2p_socket_dispatcher_, |
| - network_manager_, |
| - socket_factory_.get()); |
| + dependency_factory_.get()); |
| return peer_connection_handler_; |
| } |
| void MediaStreamImpl::ClosePeerConnection() { |
| DCHECK(CalledOnValidThread()); |
| - rtc_video_decoder_ = NULL; |
| - media_engine_->SetVideoCaptureModule(NULL); |
| - vcm_created_ = false; |
| + video_renderer_ = NULL; |
| peer_connection_handler_ = NULL; |
| + // TODO(grunell): This is a temporary workaround for an error in native |
| + // PeerConnection where added live tracks are not seen on the remote side. |
| + MediaStreamTrackPtrMap::const_iterator it = local_tracks_.begin(); |
| + for (; it != local_tracks_.end(); ++it) |
| + it->second->set_state(webrtc::MediaStreamTrackInterface::kEnded); |
| } |
| -bool MediaStreamImpl::SetVideoCaptureModule(const std::string& label) { |
| - DCHECK(CalledOnValidThread()); |
| - if (vcm_created_) |
| - return true; |
| - // Set the capture device. |
| - // TODO(grunell): Instead of using the first track, the selected track |
| - // should be used. |
| - int id = media_stream_dispatcher_->video_session_id(label, 0); |
| - if (id == media_stream::StreamDeviceInfo::kNoId) |
| - return false; |
| - webrtc::VideoCaptureModule* vcm = |
| - new VideoCaptureModuleImpl(id, vc_manager_.get()); |
| - vcm_created_ = true; |
| - media_engine_->SetVideoCaptureModule(vcm); |
| - return true; |
| +webrtc::MediaStreamTrackInterface* MediaStreamImpl::GetLocalMediaStreamTrack( |
| + std::string label) { |
| + MediaStreamTrackPtrMap::iterator it = local_tracks_.find(label); |
| + if (it == local_tracks_.end()) |
| + return NULL; |
| + MediaStreamTrackPtr stream = it->second; |
| + return stream.get(); |
| } |
| void MediaStreamImpl::requestUserMedia( |
| @@ -226,22 +171,33 @@ scoped_refptr<media::VideoDecoder> MediaStreamImpl::GetVideoDecoder( |
| WebKit::WebMediaStreamRegistry::lookupMediaStreamDescriptor(url)); |
| if (descriptor.isNull()) |
| return NULL; // This is not a valid stream. |
| + |
| + // We must find out if this is a local or remote stream. The tracks in a local |
| + // stream have IDs that are in the form |
| + // <MediaStreamManager-label>#{audio,video}-<session-ID>. We extract the |
|
tommi (sloooow) - chröme
2012/01/24 15:18:56
can we move the code that does this parsing out to
Henrik Grunell
2012/01/26 13:03:16
Broke out the generation and parsing of the track
|
| + // MSM-label and if found in the dispatcher we have a local stream, otherwise |
| + // we have a remote stream. There will be changes soon so that we don't have |
| + // to bother about the type of stream here. Hence this solution is OK for now. |
| WebKit::WebVector<WebKit::WebMediaStreamSource> source_vector; |
| descriptor.sources(source_vector); |
| - std::string label; |
| + std::string msm_label; |
| for (size_t i = 0; i < source_vector.size(); ++i) { |
| if (source_vector[i].type() == WebKit::WebMediaStreamSource::TypeVideo) { |
| - label = UTF16ToUTF8(source_vector[i].id()); |
| + // We assume there is one video track only. |
| + msm_label = UTF16ToUTF8(source_vector[i].id()); |
| + size_t pos = msm_label.rfind("#"); |
| + msm_label = msm_label.substr(0, pos); |
|
tommi (sloooow) - chröme
2012/01/24 15:18:56
what if pos is npos?
Henrik Grunell
2012/01/26 13:03:16
Commented on that in the new functions. (Doesn't m
|
| break; |
| } |
| } |
| - if (label.empty()) |
| + if (msm_label.empty()) |
| return NULL; |
| scoped_refptr<media::VideoDecoder> decoder; |
| - if (media_stream_dispatcher_->IsStream(label)) { |
| + if (media_stream_dispatcher_->IsStream(msm_label)) { |
| // It's a local stream. |
| - int video_session_id = media_stream_dispatcher_->video_session_id(label, 0); |
| + int video_session_id = |
| + media_stream_dispatcher_->video_session_id(msm_label, 0); |
| media::VideoCapture::VideoCaptureCapability capability; |
| capability.width = kVideoCaptureWidth; |
| capability.height = kVideoCaptureHeight; |
| @@ -256,20 +212,27 @@ scoped_refptr<media::VideoDecoder> MediaStreamImpl::GetVideoDecoder( |
| capability); |
| } else { |
| // It's a remote stream. |
| - size_t found = label.rfind("-remote"); |
| - if (found != std::string::npos) |
| - label = label.substr(0, found); |
| - if (rtc_video_decoder_.get()) { |
| + if (!video_renderer_.get()) |
| + video_renderer_ = new talk_base::RefCountedObject<VideoRendererWrapper>(); |
| + if (video_renderer_->renderer()) { |
| // The renderer is used by PeerConnection, release it first. |
| - if (peer_connection_handler_) |
| - peer_connection_handler_->SetVideoRenderer(label, NULL); |
| + if (peer_connection_handler_) { |
| + peer_connection_handler_->SetVideoRenderer( |
| + UTF16ToUTF8(descriptor.label()), |
| + NULL); |
| + } |
| + video_renderer_->SetVideoDecoder(NULL); |
| } |
| - rtc_video_decoder_ = new RTCVideoDecoder( |
| + RTCVideoDecoder* rtc_video_decoder = new RTCVideoDecoder( |
| message_loop_factory->GetMessageLoop("RtcVideoDecoderThread"), |
| url.spec()); |
| - decoder = rtc_video_decoder_; |
| - if (peer_connection_handler_) |
| - peer_connection_handler_->SetVideoRenderer(label, rtc_video_decoder_); |
| + decoder = rtc_video_decoder; |
| + video_renderer_->SetVideoDecoder(rtc_video_decoder); |
| + if (peer_connection_handler_) { |
| + peer_connection_handler_->SetVideoRenderer( |
| + UTF16ToUTF8(descriptor.label()), |
| + video_renderer_); |
| + } |
| } |
| return decoder; |
| } |
| @@ -280,32 +243,57 @@ void MediaStreamImpl::OnStreamGenerated( |
| const media_stream::StreamDeviceInfoArray& audio_array, |
| const media_stream::StreamDeviceInfoArray& video_array) { |
| DCHECK(CalledOnValidThread()); |
| + EnsurePeerConnectionFactory(); |
| - // We only support max one audio track and one video track. If the UI |
| - // for selecting device starts to allow several devices, we must implement |
| - // handling for this. |
| - DCHECK_LE(audio_array.size(), 1u); |
| - DCHECK_LE(video_array.size(), 1u); |
| WebKit::WebVector<WebKit::WebMediaStreamSource> source_vector( |
| audio_array.size() + video_array.size()); |
| - WebKit::WebString track_label_audio(UTF8ToUTF16("AudioDevice")); |
| - WebKit::WebString track_label_video(UTF8ToUTF16("VideoCapture")); |
| - size_t track_num = source_vector.size(); |
| - while (track_num--) { |
| - if (track_num < audio_array.size()) { |
| - source_vector[track_num].initialize( |
| - UTF8ToUTF16(label), |
| + // The label for a stream is globally unique. The track session id is globally |
| + // unique for the set of audio tracks and video tracks respectively. An audio |
| + // track and a video track can have the same session id (without being |
| + // related). Hence we create a unique track label from the stream label, track |
| + // type and track session id. |
| + |
| + // Add audio tracks. |
| + size_t i = 0; |
|
tommi (sloooow) - chröme
2012/01/24 15:18:56
unless you use 'i' outside the loops, just declare
Henrik Grunell
2012/01/26 13:03:16
Relic from earlier stage of local dev. Fixed.
|
| + std::string track_label; |
| + for (; i < audio_array.size(); ++i) { |
| + track_label = label; |
| + track_label += "#audio-"; |
| + track_label += audio_array[i].session_id; |
| + talk_base::scoped_refptr<webrtc::LocalAudioTrackInterface> audio_track( |
| + dependency_factory_->CreateLocalAudioTrack(audio_array[i].name, NULL)); |
| + local_tracks_.insert( |
| + std::pair<std::string, MediaStreamTrackPtr>(track_label, audio_track)); |
| + source_vector[i].initialize( |
| + UTF8ToUTF16(track_label), |
| WebKit::WebMediaStreamSource::TypeAudio, |
| - track_label_audio); |
| - } else { |
| - source_vector[track_num].initialize( |
| - UTF8ToUTF16(label), |
| + UTF8ToUTF16(audio_array[i].name)); |
| + } |
| + |
| + // Add video tracks. |
| + for (i = 0; i < video_array.size(); ++i) { |
| + track_label = label; |
| + track_label += "#video-"; |
| + track_label += video_array[i].session_id; |
| + webrtc::VideoCaptureModule* vcm = |
| + new VideoCaptureModuleImpl(video_array[i].session_id, |
| + vc_manager_.get()); |
| + talk_base::scoped_refptr<webrtc::LocalVideoTrackInterface> video_track( |
|
tommi (sloooow) - chröme
2012/01/24 15:18:56
should video_track be of type MediaStreamTrackPtr?
Henrik Grunell
2012/01/26 13:03:16
Yes, that's better. Changed it and for audio_track
|
| + dependency_factory_->CreateLocalVideoTrack( |
| + video_array[i].name, |
| + webrtc::CreateVideoCapturer(vcm))); |
|
tommi (sloooow) - chröme
2012/01/24 15:18:56
add a comment about the ownership of vcm?
Henrik Grunell
2012/01/26 13:03:16
Done.
|
| + local_tracks_.insert( |
| + std::pair<std::string, MediaStreamTrackPtr>(track_label, video_track)); |
| + source_vector[audio_array.size() + i].initialize( |
| + UTF8ToUTF16(track_label), |
| WebKit::WebMediaStreamSource::TypeVideo, |
| - track_label_video); |
| - } |
| + UTF8ToUTF16(video_array[i].name)); |
| } |
| + // TODO(grunell): Remove tracks from the map when support to stop is |
| + // added in WebKit. |
| + |
| MediaRequestMap::iterator it = user_media_requests_.find(request_id); |
| if (it == user_media_requests_.end()) { |
| DVLOG(1) << "Request ID not found"; |
| @@ -313,7 +301,6 @@ void MediaStreamImpl::OnStreamGenerated( |
| } |
| WebKit::WebUserMediaRequest user_media_request = it->second; |
| user_media_requests_.erase(it); |
| - stream_labels_.push_back(label); |
| user_media_request.requestSucceeded(source_vector); |
| } |
| @@ -364,3 +351,65 @@ void MediaStreamImpl::DeleteIpcNetworkManager() { |
| delete network_manager_; |
| network_manager_ = NULL; |
| } |
| + |
| +bool MediaStreamImpl::EnsurePeerConnectionFactory() { |
| + DCHECK(CalledOnValidThread()); |
| + if (!signaling_thread_) { |
| + jingle_glue::JingleThreadWrapper::EnsureForCurrentThread(); |
| + jingle_glue::JingleThreadWrapper::current()->set_send_allowed(true); |
| + signaling_thread_ = jingle_glue::JingleThreadWrapper::current(); |
| + } |
| + |
| + if (!worker_thread_) { |
| + if (!chrome_worker_thread_.IsRunning()) { |
| + if (!chrome_worker_thread_.Start()) { |
| + LOG(ERROR) << "Could not start worker thread"; |
| + signaling_thread_ = NULL; |
| + return false; |
| + } |
| + } |
| + base::WaitableEvent event(true, false); |
| + chrome_worker_thread_.message_loop()->PostTask(FROM_HERE, base::Bind( |
| + &MediaStreamImpl::InitializeWorkerThread, |
| + this, |
| + &worker_thread_, |
| + &event)); |
| + event.Wait(); |
|
tommi (sloooow) - chröme
2012/01/24 15:18:56
is there a specific reason why we need to do this
Henrik Grunell
2012/01/26 13:03:16
Yes there's a reason. worker_thread_ must be set b
|
| + DCHECK(worker_thread_); |
| + } |
| + |
| + if (!network_manager_) |
| + network_manager_ = new content::IpcNetworkManager(p2p_socket_dispatcher_); |
| + |
| + if (!socket_factory_.get()) { |
| + socket_factory_.reset( |
| + new content::IpcPacketSocketFactory(p2p_socket_dispatcher_)); |
| + } |
| + |
| + if (!dependency_factory_->PeerConnectionFactoryCreated()) { |
| + if (!dependency_factory_->CreatePeerConnectionFactory( |
| + worker_thread_, |
|
tommi (sloooow) - chröme
2012/01/24 15:18:56
it feels weird to me to see parameters indented to
Henrik Grunell
2012/01/26 13:03:16
That seems better. Fixed.
|
| + signaling_thread_, |
| + p2p_socket_dispatcher_, |
| + network_manager_, |
| + socket_factory_.get())) { |
| + LOG(ERROR) << "Could not initialize PeerConnection factory"; |
| + return false; |
| + } |
| + } |
| + |
| + return true; |
| +} |
| + |
| +MediaStreamImpl::VideoRendererWrapper::VideoRendererWrapper() {} |
| + |
| +MediaStreamImpl::VideoRendererWrapper::~VideoRendererWrapper() {} |
| + |
| +cricket::VideoRenderer* MediaStreamImpl::VideoRendererWrapper::renderer() { |
|
tommi (sloooow) - chröme
2012/01/24 15:18:56
nit: one liners like this one (all lower case) usu
Henrik Grunell
2012/01/26 13:03:16
Done.
|
| + return rtc_video_decoder_.get(); |
| +} |
| + |
| +void MediaStreamImpl::VideoRendererWrapper::SetVideoDecoder( |
| + RTCVideoDecoder* decoder) { |
| + rtc_video_decoder_ = decoder; |
| +} |