Chromium Code Reviews| Index: content/renderer/media/media_stream_dependency_factory.cc |
| diff --git a/content/renderer/media/media_stream_dependency_factory.cc b/content/renderer/media/media_stream_dependency_factory.cc |
| index e44ecd8edb49411ad47fe6bfb7c686c5efd9f6cb..a793a3a52bc76c6838d97fbfe2d5762742a1d619 100644 |
| --- a/content/renderer/media/media_stream_dependency_factory.cc |
| +++ b/content/renderer/media/media_stream_dependency_factory.cc |
| @@ -6,14 +6,23 @@ |
| #include <vector> |
| +#include "base/synchronization/waitable_event.h" |
| +#include "base/utf_string_conversions.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_capturer.h" |
| +#include "content/renderer/media/peer_connection_handler_jsep.h" |
| #include "content/renderer/media/video_capture_impl_manager.h" |
| #include "content/renderer/media/webrtc_audio_device_impl.h" |
| +#include "content/renderer/media/webrtc_uma_histograms.h" |
| #include "content/renderer/p2p/ipc_network_manager.h" |
| #include "content/renderer/p2p/ipc_socket_factory.h" |
| #include "content/renderer/p2p/port_allocator.h" |
| #include "jingle/glue/thread_wrapper.h" |
| #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" |
| +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebMediaStreamComponent.h" |
| +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebMediaStreamDescriptor.h" |
| +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebMediaStreamSource.h" |
| class P2PPortAllocatorFactory : public webrtc::PortAllocatorFactoryInterface { |
| public: |
| @@ -57,21 +66,110 @@ class P2PPortAllocatorFactory : public webrtc::PortAllocatorFactoryInterface { |
| virtual ~P2PPortAllocatorFactory() {} |
| private: |
| - // socket_dispatcher_ is a weak reference, owned by RenderView. It's valid |
| - // for the lifetime of RenderView. |
| + // |socket_dispatcher_| is a weak reference, owned by RenderThreadImpl. |
| + // It's valid for the lifetime of RenderThreadImpl. |
| content::P2PSocketDispatcher* socket_dispatcher_; |
|
tommi (sloooow) - chröme
2012/09/12 13:10:24
can this now be a scoped_refptr?
perkj_chrome
2012/09/12 13:54:39
Done.
|
| - // network_manager_ and socket_factory_ are a weak references, owned by |
| - // MediaStreamImpl. |
| + // |network_manager_| and |socket_factory_| are a weak references, owned by |
| + // MediaStreamDependencyFactory. |
| talk_base::NetworkManager* network_manager_; |
| talk_base::PacketSocketFactory* socket_factory_; |
| }; |
| MediaStreamDependencyFactory::MediaStreamDependencyFactory( |
| - VideoCaptureImplManager* vc_manager) |
| - : vc_manager_(vc_manager) { |
| + VideoCaptureImplManager* vc_manager, |
| + content::P2PSocketDispatcher* p2p_socket_dispatcher) |
| + : network_manager_(NULL), |
| + vc_manager_(vc_manager), |
| + p2p_socket_dispatcher_(p2p_socket_dispatcher), |
| + signaling_thread_(NULL), |
| + worker_thread_(NULL), |
| + chrome_worker_thread_("Chrome_libJingle_WorkerThread") { |
| } |
| -MediaStreamDependencyFactory::~MediaStreamDependencyFactory() {} |
| +MediaStreamDependencyFactory::~MediaStreamDependencyFactory() { |
| + CleanupPeerConnectionFactory(); |
| +} |
| + |
| +WebKit::WebPeerConnection00Handler* |
| +MediaStreamDependencyFactory::CreatePeerConnectionHandlerJsep( |
| + WebKit::WebPeerConnection00HandlerClient* client) { |
| + // Save histogram data so we can see how much PeerConnetion is used. |
| + // The histogram counts the number of calls to the JS API |
| + // webKitPeerConnection00. |
| + UpdateWebRTCMethodCount(kWebkitPeerConnection); |
| + |
| + if (!EnsurePeerConnectionFactory()) { |
| + return NULL; |
| + } |
| + |
| + PeerConnectionHandlerJsep* pc_handler = new PeerConnectionHandlerJsep( |
| + client, |
| + this); |
| + return pc_handler; |
| +} |
| + |
| +bool MediaStreamDependencyFactory::CreateNativeLocalMediaStream( |
| + WebKit::WebMediaStreamDescriptor* description) { |
| + // Creating the peer connection factory can fail if for example the audio |
| + // (input or output) or video device cannot be opened. Handling such cases |
| + // better is a higher level design discussion which involves the media |
| + // manager, webrtc and libjingle. We cannot create any native |
| + // track objects however, so we'll just have to skip that. Furthermore, |
| + // creating a peer connection later on will fail if we don't have a factory. |
| + if (!EnsurePeerConnectionFactory()) |
| + return false; |
| + |
| + std::string label = UTF16ToUTF8(description->label()); |
| + talk_base::scoped_refptr<webrtc::LocalMediaStreamInterface> native_stream = |
| + CreateLocalMediaStream(label); |
| + |
| + // Add audio tracks. |
| + WebKit::WebVector<WebKit::WebMediaStreamComponent> audio_components; |
| + description->audioSources(audio_components); |
| + for (size_t i = 0; i < audio_components.size(); ++i) { |
| + const WebKit::WebMediaStreamSource& source = audio_components[i].source(); |
| + MediaStreamSourceExtraData* source_data = |
| + static_cast<MediaStreamSourceExtraData*>(source.extraData()); |
| + if (!source_data) { |
| + // TODO(perkj): Implement support for sources from remote MediaStreams. |
| + NOTIMPLEMENTED(); |
| + continue; |
| + } |
| + // TODO(perkj): Refactor the creation of audio tracks to use a proper |
| + // interface for receiving audio input data. Currently NULL is passed since |
| + // the |audio_device| is the wrong class and is unused. |
| + talk_base::scoped_refptr<webrtc::LocalAudioTrackInterface> audio_track( |
| + CreateLocalAudioTrack(UTF16ToUTF8(source.id()), NULL)); |
| + native_stream->AddTrack(audio_track); |
| + audio_track->set_enabled(audio_components[i].isEnabled()); |
| + // TODO(xians): This set the source of all audio tracks to the same |
| + // microphone. Implement support for setting the source per audio track |
| + // instead. |
| + SetAudioDeviceSessionId(source_data->device_info().session_id); |
| + } |
| + |
| + // Add video tracks. |
| + WebKit::WebVector<WebKit::WebMediaStreamComponent> video_components; |
| + description->videoSources(video_components); |
| + for (size_t i = 0; i < video_components.size(); ++i) { |
| + const WebKit::WebMediaStreamSource& source = video_components[i].source(); |
| + MediaStreamSourceExtraData* source_data = |
| + static_cast<MediaStreamSourceExtraData*>(source.extraData()); |
| + if (!source_data) { |
| + // TODO(perkj): Implement support for sources from remote MediaStreams. |
| + NOTIMPLEMENTED(); |
| + continue; |
| + } |
| + talk_base::scoped_refptr<webrtc::LocalVideoTrackInterface> video_track( |
| + CreateLocalVideoTrack(UTF16ToUTF8(source.id()), |
| + source_data->device_info().session_id)); |
| + native_stream->AddTrack(video_track); |
| + video_track->set_enabled(video_components[i].isEnabled()); |
| + } |
| + |
| + description->setExtraData(new MediaStreamExtraData(native_stream)); |
| + return true; |
| +} |
| bool MediaStreamDependencyFactory::CreatePeerConnectionFactory( |
| talk_base::Thread* worker_thread, |
| @@ -99,11 +197,6 @@ bool MediaStreamDependencyFactory::CreatePeerConnectionFactory( |
| return pc_factory_.get() != NULL; |
| } |
| -void MediaStreamDependencyFactory::ReleasePeerConnectionFactory() { |
| - if (pc_factory_.get()) |
| - pc_factory_ = NULL; |
| -} |
| - |
| bool MediaStreamDependencyFactory::PeerConnectionFactoryCreated() { |
| return pc_factory_.get() != NULL; |
| } |
| @@ -155,3 +248,99 @@ webrtc::IceCandidateInterface* MediaStreamDependencyFactory::CreateIceCandidate( |
| void MediaStreamDependencyFactory::SetAudioDeviceSessionId(int session_id) { |
| audio_device_->SetSessionId(session_id); |
| } |
| + |
| +void MediaStreamDependencyFactory::InitializeWorkerThread( |
| + talk_base::Thread** thread, |
| + base::WaitableEvent* event) { |
| + jingle_glue::JingleThreadWrapper::EnsureForCurrentMessageLoop(); |
| + jingle_glue::JingleThreadWrapper::current()->set_send_allowed(true); |
| + *thread = jingle_glue::JingleThreadWrapper::current(); |
| + event->Signal(); |
| +} |
| + |
| +void MediaStreamDependencyFactory::CreateIpcNetworkManagerOnWorkerThread( |
| + base::WaitableEvent* event) { |
| + DCHECK_EQ(MessageLoop::current(), chrome_worker_thread_.message_loop()); |
| + network_manager_ = new content::IpcNetworkManager(p2p_socket_dispatcher_); |
| + event->Signal(); |
| +} |
| + |
| +void MediaStreamDependencyFactory::DeleteIpcNetworkManager() { |
| + DCHECK_EQ(MessageLoop::current(), chrome_worker_thread_.message_loop()); |
| + delete network_manager_; |
| + network_manager_ = NULL; |
| +} |
| + |
| +bool MediaStreamDependencyFactory::EnsurePeerConnectionFactory() { |
| + DCHECK(CalledOnValidThread()); |
| + if (!signaling_thread_) { |
| + jingle_glue::JingleThreadWrapper::EnsureForCurrentMessageLoop(); |
| + jingle_glue::JingleThreadWrapper::current()->set_send_allowed(true); |
| + signaling_thread_ = jingle_glue::JingleThreadWrapper::current(); |
|
tommi (sloooow) - chröme
2012/09/12 13:10:24
should we CHECK that this isn't NULL?
perkj_chrome
2012/09/12 13:54:39
Done.
|
| + } |
| + |
| + 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( |
| + &MediaStreamDependencyFactory::InitializeWorkerThread, |
| + base::Unretained(this), |
| + &worker_thread_, |
| + &event)); |
| + event.Wait(); |
| + DCHECK(worker_thread_); |
| + } |
| + |
| + if (!network_manager_) { |
| + base::WaitableEvent event(true, false); |
| + chrome_worker_thread_.message_loop()->PostTask(FROM_HERE, base::Bind( |
| + &MediaStreamDependencyFactory::CreateIpcNetworkManagerOnWorkerThread, |
|
tommi (sloooow) - chröme
2012/09/12 13:10:24
fix indent
perkj_chrome
2012/09/12 13:54:39
Done.
|
| + base::Unretained(this), |
| + &event)); |
| + event.Wait(); |
| + } |
| + |
| + if (!socket_factory_.get()) { |
| + socket_factory_.reset( |
| + new content::IpcPacketSocketFactory(p2p_socket_dispatcher_)); |
| + } |
| + |
| + if (!PeerConnectionFactoryCreated()) { |
|
tommi (sloooow) - chröme
2012/09/12 13:10:24
could you have this check at the top and return ea
perkj_chrome
2012/09/12 13:54:39
Done.
|
| + if (!CreatePeerConnectionFactory( |
| + worker_thread_, |
| + signaling_thread_, |
| + p2p_socket_dispatcher_, |
| + network_manager_, |
| + socket_factory_.get())) { |
| + LOG(ERROR) << "Could not create PeerConnection factory"; |
| + return false; |
| + } |
| + } |
| + |
| + return true; |
| +} |
| + |
| +void MediaStreamDependencyFactory::CleanupPeerConnectionFactory() { |
| + pc_factory_ = NULL; |
| + if (network_manager_) { |
| + // The network manager needs to free its resources on the thread they were |
| + // created, which is the worked thread. |
| + if (chrome_worker_thread_.IsRunning()) { |
| + chrome_worker_thread_.message_loop()->PostTask(FROM_HERE, base::Bind( |
| + &MediaStreamDependencyFactory::DeleteIpcNetworkManager, |
| + base::Unretained(this))); |
| + // Stopping the thread will wait until all tasks have been |
| + // processed before returning. We wait for the above task to finish before |
| + // letting the the function continue to avoid any potential race issues. |
| + chrome_worker_thread_.Stop(); |
| + } else { |
| + NOTREACHED() << "Worker thread not running."; |
| + } |
| + } |
| +} |