diff options
author | tommi@chromium.org <tommi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-28 21:24:07 +0000 |
---|---|---|
committer | tommi@chromium.org <tommi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-28 21:24:07 +0000 |
commit | d12b265876b50a9fb00c47e3e529e4b556064336 (patch) | |
tree | c0964afda07661c61969eb0da115f6818b17e000 /content/renderer | |
parent | 98878750f21211687db18e27056ffc4ae3d8c2ab (diff) | |
download | chromium_src-d12b265876b50a9fb00c47e3e529e4b556064336.zip chromium_src-d12b265876b50a9fb00c47e3e529e4b556064336.tar.gz chromium_src-d12b265876b50a9fb00c47e3e529e4b556064336.tar.bz2 |
Land change for grunell@: "Adding support for MediaStream and PeerConnection functionality. "
Original review: http://codereview.chromium.org/8060055/
TBR=darin, scherkus
Review URL: http://codereview.chromium.org/8691003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@111773 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content/renderer')
23 files changed, 1735 insertions, 40 deletions
diff --git a/content/renderer/media/media_stream_dependency_factory.cc b/content/renderer/media/media_stream_dependency_factory.cc new file mode 100644 index 0000000..a430ab2 --- /dev/null +++ b/content/renderer/media/media_stream_dependency_factory.cc @@ -0,0 +1,55 @@ +// Copyright (c) 2011 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_dependency_factory.h" + +#include "content/renderer/media/video_capture_module_impl.h" +#include "content/renderer/media/webrtc_audio_device_impl.h" +#include "content/renderer/p2p/ipc_network_manager.h" +#include "content/renderer/p2p/ipc_socket_factory.h" +#include "jingle/glue/thread_wrapper.h" +#include "third_party/libjingle/source/talk/app/webrtc/peerconnection.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" + +MediaStreamDependencyFactory::MediaStreamDependencyFactory() {} + +MediaStreamDependencyFactory::~MediaStreamDependencyFactory() {} + +cricket::WebRtcMediaEngine* +MediaStreamDependencyFactory::CreateWebRtcMediaEngine() { + webrtc::AudioDeviceModule* adm = new WebRtcAudioDeviceImpl(); + webrtc::AudioDeviceModule* adm_sc = new WebRtcAudioDeviceImpl(); + return new cricket::WebRtcMediaEngine(adm, adm_sc, NULL); +} + +bool MediaStreamDependencyFactory::CreatePeerConnectionFactory( + cricket::PortAllocator* port_allocator, + cricket::MediaEngineInterface* media_engine, + talk_base::Thread* worker_thread) { + if (!pc_factory_.get()) { + scoped_ptr<webrtc::PeerConnectionFactory> factory( + new webrtc::PeerConnectionFactory(port_allocator, + media_engine, + new cricket::DummyDeviceManager(), + worker_thread)); + if (factory->Initialize()) + pc_factory_.reset(factory.release()); + } + return pc_factory_.get() != NULL; +} + +void MediaStreamDependencyFactory::DeletePeerConnectionFactory() { + pc_factory_.reset(); +} + +bool MediaStreamDependencyFactory::PeerConnectionFactoryCreated() { + return pc_factory_.get() != NULL; +} + +webrtc::PeerConnection* MediaStreamDependencyFactory::CreatePeerConnection( + talk_base::Thread* signaling_thread) { + return pc_factory_->CreatePeerConnection(signaling_thread); +} diff --git a/content/renderer/media/media_stream_dependency_factory.h b/content/renderer/media/media_stream_dependency_factory.h new file mode 100644 index 0000000..21368be --- /dev/null +++ b/content/renderer/media/media_stream_dependency_factory.h @@ -0,0 +1,70 @@ +// Copyright (c) 2011 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. + +#ifndef CONTENT_RENDERER_MEDIA_MEDIA_STREAM_DEPENDENCY_FACTORY_H_ +#define CONTENT_RENDERER_MEDIA_MEDIA_STREAM_DEPENDENCY_FACTORY_H_ + +#include <string> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "third_party/libjingle/source/talk/app/webrtc/peerconnectionfactory.h" + +namespace content { +class IpcNetworkManager; +class IpcPacketSocketFactory; +class P2PSocketDispatcher; +} + +namespace cricket { +class HttpPortAllocator; +class MediaEngineInterface; +class PortAllocator; +class WebRtcMediaEngine; +} + +namespace talk_base { +class NetworkManager; +class PacketSocketFactory; +class Thread; +} + +namespace webrtc { +class PeerConnection; +class VideoCaptureModule; +} + +// Object factory for MediaStreamImpl and PeerConnectionHandler. +class MediaStreamDependencyFactory { + public: + MediaStreamDependencyFactory(); + virtual ~MediaStreamDependencyFactory(); + + // WebRtcMediaEngine is used in CreatePeerConnectionFactory(), see below. + virtual cricket::WebRtcMediaEngine* CreateWebRtcMediaEngine(); + + // Creates and deletes |pc_factory_|, which in turn is used for + // creating PeerConnection objects. |media_engine| is the engine created by + // CreateWebRtcMediaEngine(). |port_allocator| and |media_engine| will be + // owned by |pc_factory_|. |worker_thread| is owned by MediaStreamImpl. + virtual bool CreatePeerConnectionFactory( + cricket::PortAllocator* port_allocator, + cricket::MediaEngineInterface* media_engine, + talk_base::Thread* worker_thread); + virtual void DeletePeerConnectionFactory(); + virtual bool PeerConnectionFactoryCreated(); + + // Asks the PeerConnection factory to create a PeerConnection object. See + // MediaStreamImpl for details about |signaling_thread|. The PeerConnection + // object is owned by PeerConnectionHandler. + virtual webrtc::PeerConnection* CreatePeerConnection( + talk_base::Thread* signaling_thread); + + private: + scoped_ptr<webrtc::PeerConnectionFactory> pc_factory_; + + DISALLOW_COPY_AND_ASSIGN(MediaStreamDependencyFactory); +}; + +#endif // CONTENT_RENDERER_MEDIA_MEDIA_STREAM_DEPENDENCY_FACTORY_H_ diff --git a/content/renderer/media/media_stream_dispatcher.h b/content/renderer/media/media_stream_dispatcher.h index 6ff09c3..0ef7daa 100644 --- a/content/renderer/media/media_stream_dispatcher.h +++ b/content/renderer/media/media_stream_dispatcher.h @@ -32,21 +32,22 @@ class CONTENT_EXPORT MediaStreamDispatcher // Request a new media stream to be created. // This can be used either of WebKit or a plugin. // Note: The event_handler must be valid for as long as the stream exists. - void GenerateStream(int request_id, - MediaStreamDispatcherEventHandler* event_handler, - media_stream::StreamOptions components, - const std::string& security_origin); + virtual void GenerateStream( + int request_id, + MediaStreamDispatcherEventHandler* event_handler, + media_stream::StreamOptions components, + const std::string& security_origin); // Stop a started stream. Label is the label provided in OnStreamGenerated. - void StopStream(const std::string& label); + virtual void StopStream(const std::string& label); // Check if the label is a valid stream. - bool IsStream(const std::string& label); + virtual bool IsStream(const std::string& label); // Get the video session_id given a label. The label identifies a stream. // index is the index in the video_device_array of the stream. - int video_session_id(const std::string& label, int index); + virtual int video_session_id(const std::string& label, int index); // Returns an audio session_id given a label and an index. - int audio_session_id(const std::string& label, int index); + virtual int audio_session_id(const std::string& label, int index); private: FRIEND_TEST_ALL_PREFIXES(MediaStreamDispatcherTest, Basic); diff --git a/content/renderer/media/media_stream_impl.cc b/content/renderer/media/media_stream_impl.cc index 7573e78..a50ec61 100644 --- a/content/renderer/media/media_stream_impl.cc +++ b/content/renderer/media/media_stream_impl.cc @@ -4,12 +4,32 @@ #include "content/renderer/media/media_stream_impl.h" -#include "base/string_util.h" +#include <utility> + +#include "base/bind.h" +#include "base/logging.h" +#include "base/synchronization/waitable_event.h" +#include "base/utf_string_conversions.h" #include "content/renderer/media/capture_video_decoder.h" +#include "content/renderer/media/media_stream_dependency_factory.h" +#include "content/renderer/media/media_stream_dispatcher.h" +#include "content/renderer/media/peer_connection_handler.h" +#include "content/renderer/media/rtc_video_decoder.h" #include "content/renderer/media/video_capture_impl_manager.h" -#include "googleurl/src/gurl.h" +#include "content/renderer/media/video_capture_module_impl.h" +#include "content/renderer/media/webrtc_audio_device_impl.h" +#include "content/renderer/p2p/ipc_network_manager.h" +#include "content/renderer/p2p/ipc_socket_factory.h" +#include "content/renderer/p2p/socket_dispatcher.h" +#include "jingle/glue/thread_wrapper.h" #include "media/base/message_loop_factory.h" -#include "media/base/pipeline.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/WebMediaStreamDescriptor.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebMediaStreamRegistry.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebMediaStreamSource.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebVector.h" namespace { @@ -17,24 +37,199 @@ static const int kVideoCaptureWidth = 352; static const int kVideoCaptureHeight = 288; static const int kVideoCaptureFramePerSecond = 30; -static const int kStartOpenSessionId = 1; +} // namespace -// TODO(wjia): remove this string when full media stream code is checked in. -static const char kRawMediaScheme[] = "mediastream"; +int MediaStreamImpl::next_request_id_ = 0; -} // namespace +MediaStreamImpl::MediaStreamImpl( + MediaStreamDispatcher* media_stream_dispatcher, + content::P2PSocketDispatcher* p2p_socket_dispatcher, + VideoCaptureImplManager* vc_manager, + MediaStreamDependencyFactory* dependency_factory) + : dependency_factory_(dependency_factory), + media_stream_dispatcher_(media_stream_dispatcher), + media_engine_(NULL), + p2p_socket_dispatcher_(p2p_socket_dispatcher), + vc_manager_(vc_manager), + port_allocator_(NULL), + peer_connection_handler_(NULL), + message_loop_proxy_(base::MessageLoopProxy::current()), + signaling_thread_(NULL), + worker_thread_(NULL), + chrome_worker_thread_("Chrome_libJingle_WorkerThread"), + vcm_created_(false) { +} + +MediaStreamImpl::~MediaStreamImpl() { + if (dependency_factory_.get()) + dependency_factory_->DeletePeerConnectionFactory(); +} + +WebKit::WebPeerConnectionHandler* MediaStreamImpl::CreatePeerConnectionHandler( + WebKit::WebPeerConnectionHandlerClient* client) { + if (peer_connection_handler_) { + 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 (!dependency_factory_->PeerConnectionFactoryCreated()) { + ipc_network_manager_.reset( + new content::IpcNetworkManager(p2p_socket_dispatcher_)); + ipc_socket_factory_.reset( + new content::IpcPacketSocketFactory(p2p_socket_dispatcher_)); + port_allocator_ = new cricket::HttpPortAllocator( + ipc_network_manager_.get(), + ipc_socket_factory_.get(), + "PeerConnection"); + // TODO(mallinath): The following flags were added to solve a crash in + // HttpClient, we should probably remove them after that issue has been + // investigated. + port_allocator_->set_flags(cricket::PORTALLOCATOR_DISABLE_TCP | + cricket::PORTALLOCATOR_DISABLE_RELAY); + + if (!dependency_factory_->CreatePeerConnectionFactory(port_allocator_, + media_engine_, + worker_thread_)) { + LOG(ERROR) << "Could not initialize PeerConnection factory"; + return NULL; + } + } + + peer_connection_handler_ = new PeerConnectionHandler( + client, + this, + dependency_factory_.get(), + signaling_thread_, + port_allocator_); + + return peer_connection_handler_; +} + +void MediaStreamImpl::ClosePeerConnection() { + rtc_video_decoder_ = NULL; + media_engine_->SetVideoCaptureModule(NULL); + vcm_created_ = false; + peer_connection_handler_ = NULL; +} + +bool MediaStreamImpl::SetVideoCaptureModule(const std::string& label) { + 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; +} + +void MediaStreamImpl::requestUserMedia( + const WebKit::WebUserMediaRequest& user_media_request, + const WebKit::WebVector<WebKit::WebMediaStreamSource>& + media_stream_source_vector) { + DCHECK(!user_media_request.isNull()); + + int request_id = next_request_id_++; -MediaStreamImpl::MediaStreamImpl(VideoCaptureImplManager* vc_manager) - : vc_manager_(vc_manager) { + bool audio = user_media_request.audio(); + media_stream::StreamOptions::VideoOption video_option = + media_stream::StreamOptions::kNoCamera; + if (user_media_request.video()) { + // If no preference is set, use user facing camera. + video_option = media_stream::StreamOptions::kFacingUser; + if (user_media_request.cameraPreferenceUser() && + user_media_request.cameraPreferenceEnvironment()) { + video_option = media_stream::StreamOptions::kFacingBoth; + } else if (user_media_request.cameraPreferenceEnvironment()) { + video_option = media_stream::StreamOptions::kFacingEnvironment; + } + } + + std::string security_origin = UTF16ToUTF8( + user_media_request.securityOrigin().toString()); + + DVLOG(1) << "MediaStreamImpl::generateStream(" << request_id << ", [ " + << (audio ? "audio " : "") + << ((user_media_request.cameraPreferenceUser()) ? + "video_facing_user " : "") + << ((user_media_request.cameraPreferenceEnvironment()) ? + "video_facing_environment " : "") << "], " + << security_origin << ")"; + + user_media_requests_.insert( + std::pair<int, WebKit::WebUserMediaRequest>( + request_id, user_media_request)); + + media_stream_dispatcher_->GenerateStream( + request_id, + this, + media_stream::StreamOptions(audio, video_option), + security_origin); } -MediaStreamImpl::~MediaStreamImpl() {} +void MediaStreamImpl::cancelUserMediaRequest( + const WebKit::WebUserMediaRequest& user_media_request) { + // TODO(grunell): Implement. + NOTIMPLEMENTED(); +} scoped_refptr<media::VideoDecoder> MediaStreamImpl::GetVideoDecoder( - const GURL& url, media::MessageLoopFactory* message_loop_factory) { - bool raw_media = (url.spec().find(kRawMediaScheme) == 0); - media::VideoDecoder* decoder = NULL; - if (raw_media) { + const GURL& url, + media::MessageLoopFactory* message_loop_factory) { + WebKit::WebMediaStreamDescriptor descriptor( + WebKit::WebMediaStreamRegistry::lookupMediaStreamDescriptor(url)); + if (descriptor.isNull()) + return NULL; // This is not a valid stream. + WebKit::WebVector<WebKit::WebMediaStreamSource> source_vector; + descriptor.sources(source_vector); + std::string 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()); + break; + } + } + if (label.empty()) + return NULL; + + scoped_refptr<media::VideoDecoder> decoder; + if (media_stream_dispatcher_->IsStream(label)) { + // It's a local stream. + int video_session_id = media_stream_dispatcher_->video_session_id(label, 0); media::VideoCapture::VideoCaptureCapability capability; capability.width = kVideoCaptureWidth; capability.height = kVideoCaptureHeight; @@ -42,10 +237,107 @@ scoped_refptr<media::VideoDecoder> MediaStreamImpl::GetVideoDecoder( capability.expected_capture_delay = 0; capability.raw_type = media::VideoFrame::I420; capability.interlaced = false; - decoder = new CaptureVideoDecoder( - message_loop_factory->GetMessageLoopProxy("CaptureVideoDecoder").get(), - kStartOpenSessionId, vc_manager_.get(), capability); + message_loop_factory->GetMessageLoopProxy("CaptureVideoDecoderThread"), + video_session_id, + vc_manager_.get(), + 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()) { + // The renderer is used by PeerConnection, release it first. + if (peer_connection_handler_) + peer_connection_handler_->SetVideoRenderer(label, NULL); + } + 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_); } return decoder; } + +void MediaStreamImpl::OnStreamGenerated( + int request_id, + const std::string& label, + const media_stream::StreamDeviceInfoArray& audio_array, + const media_stream::StreamDeviceInfoArray& video_array) { + // 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), + WebKit::WebMediaStreamSource::TypeAudio, + track_label_audio); + } else { + source_vector[track_num].initialize( + UTF8ToUTF16(label), + WebKit::WebMediaStreamSource::TypeVideo, + track_label_video); + } + } + + MediaRequestMap::iterator it = user_media_requests_.find(request_id); + if (it == user_media_requests_.end()) { + DVLOG(1) << "Request ID not found"; + return; + } + WebKit::WebUserMediaRequest user_media_request = it->second; + user_media_requests_.erase(it); + stream_labels_.push_back(label); + + user_media_request.requestSucceeded(source_vector); +} + +void MediaStreamImpl::OnStreamGenerationFailed(int request_id) { + DVLOG(1) << "MediaStreamImpl::OnStreamGenerationFailed(" + << request_id << ")"; + MediaRequestMap::iterator it = user_media_requests_.find(request_id); + if (it == user_media_requests_.end()) { + DVLOG(1) << "Request ID not found"; + return; + } + WebKit::WebUserMediaRequest user_media_request = it->second; + user_media_requests_.erase(it); + + user_media_request.requestFailed(); +} + +void MediaStreamImpl::OnVideoDeviceFailed(const std::string& label, + int index) { + DVLOG(1) << "MediaStreamImpl::OnVideoDeviceFailed(" + << label << ", " << index << ")"; + // TODO(grunell): Implement. Currently not supported in WebKit. + NOTIMPLEMENTED(); +} + +void MediaStreamImpl::OnAudioDeviceFailed(const std::string& label, + int index) { + DVLOG(1) << "MediaStreamImpl::OnAudioDeviceFailed(" + << label << ", " << index << ")"; + // TODO(grunell): Implement. Currently not supported in WebKit. + NOTIMPLEMENTED(); +} + +void MediaStreamImpl::InitializeWorkerThread(talk_base::Thread** thread, + base::WaitableEvent* event) { + jingle_glue::JingleThreadWrapper::EnsureForCurrentThread(); + jingle_glue::JingleThreadWrapper::current()->set_send_allowed(true); + *thread = jingle_glue::JingleThreadWrapper::current(); + event->Signal(); +} diff --git a/content/renderer/media/media_stream_impl.h b/content/renderer/media/media_stream_impl.h index 79dc948..aeebe45 100644 --- a/content/renderer/media/media_stream_impl.h +++ b/content/renderer/media/media_stream_impl.h @@ -2,33 +2,162 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CONTENT_RENDERER_MEDIA_STREAM_MEDIA_STREAM_IMPL_H_ -#define CONTENT_RENDERER_MEDIA_STREAM_MEDIA_STREAM_IMPL_H_ +#ifndef CONTENT_RENDERER_MEDIA_MEDIA_STREAM_IMPL_H_ +#define CONTENT_RENDERER_MEDIA_MEDIA_STREAM_IMPL_H_ + +#include <list> +#include <map> +#include <string> #include "base/basictypes.h" +#include "base/gtest_prod_util.h" #include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop_proxy.h" +#include "base/threading/thread.h" +#include "content/renderer/media/media_stream_dispatcher_eventhandler.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebUserMediaClient.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebUserMediaRequest.h" #include "webkit/media/media_stream_client.h" +namespace base { +class WaitableEvent; +} + +namespace content { +class IpcNetworkManager; +class IpcPacketSocketFactory; +class P2PSocketDispatcher; +} + +namespace cricket { +class HttpPortAllocator; +class WebRtcMediaEngine; +} + +namespace talk_base { +class Thread; +} + +namespace WebKit { +class WebPeerConnectionHandler; +class WebPeerConnectionHandlerClient; +} + +class MediaStreamDispatcher; +class MediaStreamDependencyFactory; +class PeerConnectionHandler; class VideoCaptureImplManager; +class RTCVideoDecoder; -// A implementation of StreamClient to provide supporting functions, such as -// GetVideoDecoder. +// MediaStreamImpl is a delegate for the Media Stream API messages used by +// WebKit. It ties together WebKit, native PeerConnection in libjingle and +// MediaStreamManager (via MediaStreamDispatcher and MediaStreamDispatcherHost) +// in the browser process. class MediaStreamImpl - : public webkit_media::MediaStreamClient, + : public WebKit::WebUserMediaClient, + public webkit_media::MediaStreamClient, + public MediaStreamDispatcherEventHandler, public base::RefCountedThreadSafe<MediaStreamImpl> { public: - explicit MediaStreamImpl(VideoCaptureImplManager* vc_manager); + MediaStreamImpl( + MediaStreamDispatcher* media_stream_dispatcher, + content::P2PSocketDispatcher* p2p_socket_dispatcher, + VideoCaptureImplManager* vc_manager, + MediaStreamDependencyFactory* dependency_factory); virtual ~MediaStreamImpl(); - // Implement webkit_media::StreamClient. + virtual WebKit::WebPeerConnectionHandler* CreatePeerConnectionHandler( + WebKit::WebPeerConnectionHandlerClient* client); + virtual void ClosePeerConnection(); + + // Returns true if created successfully or already exists, false otherwise. + virtual bool SetVideoCaptureModule(const std::string& label); + + // WebKit::WebUserMediaClient implementation + virtual void requestUserMedia( + const WebKit::WebUserMediaRequest& user_media_request, + const WebKit::WebVector<WebKit::WebMediaStreamSource>& + media_stream_source_vector) OVERRIDE; + virtual void cancelUserMediaRequest( + const WebKit::WebUserMediaRequest& user_media_request) OVERRIDE; + + // webkit_media::MediaStreamClient implementation. virtual scoped_refptr<media::VideoDecoder> GetVideoDecoder( const GURL& url, media::MessageLoopFactory* message_loop_factory) OVERRIDE; + // MediaStreamDispatcherEventHandler implementation. + virtual void OnStreamGenerated( + int request_id, + const std::string& label, + const media_stream::StreamDeviceInfoArray& audio_array, + const media_stream::StreamDeviceInfoArray& video_array) OVERRIDE; + virtual void OnStreamGenerationFailed(int request_id) OVERRIDE; + virtual void OnVideoDeviceFailed( + const std::string& label, + int index) OVERRIDE; + virtual void OnAudioDeviceFailed( + const std::string& label, + int index) OVERRIDE; + private: + FRIEND_TEST_ALL_PREFIXES(MediaStreamImplTest, Basic); + + void InitializeWorkerThread( + talk_base::Thread** thread, + base::WaitableEvent* event); + + scoped_ptr<MediaStreamDependencyFactory> dependency_factory_; + + // media_stream_dispatcher_ is a weak reference, owned by RenderView. It's + // valid for the lifetime of RenderView. + MediaStreamDispatcher* media_stream_dispatcher_; + + // The media_engine_ is owned by pc_factory_ and is valid for the lifetime of + // pc_factory_. + cricket::WebRtcMediaEngine* media_engine_; + + // p2p_socket_dispatcher_ is a weak reference, owned by RenderView. It's valid + // for the lifetime of RenderView. + content::P2PSocketDispatcher* p2p_socket_dispatcher_; + scoped_refptr<VideoCaptureImplManager> vc_manager_; + scoped_ptr<content::IpcNetworkManager> ipc_network_manager_; + scoped_ptr<content::IpcPacketSocketFactory> ipc_socket_factory_; + + // port_allocator_ is a weak reference, owned by the PeerConection factory. + // It is valid for the lifetime of PeerConection factory. + cricket::HttpPortAllocator* port_allocator_; + + // peer_connection_handler_ is a weak reference, owned by WebKit. It's valid + // until stop is called on it (which will call ClosePeerConnection on us). + // TODO(grunell): Support several PeerConnectionsHandlers. + PeerConnectionHandler* peer_connection_handler_; + + scoped_refptr<RTCVideoDecoder> rtc_video_decoder_; + scoped_refptr<base::MessageLoopProxy> message_loop_proxy_; + + // PeerConnection threads. signaling_thread_ is created from the + // "current" chrome thread. + talk_base::Thread* signaling_thread_; + talk_base::Thread* worker_thread_; + base::Thread chrome_worker_thread_; + + static int next_request_id_; + typedef std::map<int, WebKit::WebUserMediaRequest> MediaRequestMap; + MediaRequestMap user_media_requests_; + + std::list<std::string> stream_labels_; + + // Make sure we only create the video capture module once. This is also + // temporary and will be handled differently when several PeerConnections + // and/or streams is supported. + // TODO(grunell): This shall be removed or changed when native PeerConnection + // has been updated to closer follow the specification. + bool vcm_created_; DISALLOW_COPY_AND_ASSIGN(MediaStreamImpl); }; -#endif // CONTENT_RENDERER_MEDIA_STREAM_MEDIA_STREAM_IMPL_H_ +#endif // CONTENT_RENDERER_MEDIA_MEDIA_STREAM_IMPL_H_ diff --git a/content/renderer/media/media_stream_impl_unittest.cc b/content/renderer/media/media_stream_impl_unittest.cc new file mode 100644 index 0000000..1c0a27d --- /dev/null +++ b/content/renderer/media/media_stream_impl_unittest.cc @@ -0,0 +1,48 @@ +// Copyright (c) 2011 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 "base/memory/scoped_ptr.h" +#include "base/message_loop.h" +#include "content/renderer/media/media_stream_impl.h" +#include "content/renderer/media/mock_media_stream_dependency_factory.h" +#include "content/renderer/media/mock_media_stream_dispatcher.h" +#include "content/renderer/media/mock_web_peer_connection_handler_client.h" +#include "content/renderer/media/video_capture_impl_manager.h" +#include "content/renderer/p2p/socket_dispatcher.h" +#include "testing/gtest/include/gtest/gtest.h" + +// TODO(grunell): Fix issue 105556. +TEST(MediaStreamImplTest, DISABLED_Basic) { + MessageLoop loop; + + // Create our test object. + scoped_ptr<MockMediaStreamDispatcher> ms_dispatcher( + new MockMediaStreamDispatcher()); + scoped_ptr<content::P2PSocketDispatcher> p2p_socket_dispatcher( + new content::P2PSocketDispatcher(NULL)); + scoped_refptr<VideoCaptureImplManager> vc_manager( + new VideoCaptureImplManager()); + MockMediaStreamDependencyFactory* dependency_factory = + new MockMediaStreamDependencyFactory(); + scoped_refptr<MediaStreamImpl> ms_impl(new MediaStreamImpl( + ms_dispatcher.get(), + p2p_socket_dispatcher.get(), + vc_manager.get(), + dependency_factory)); + + // TODO(grunell): Add tests for WebKit::WebUserMediaClient and + // MediaStreamDispatcherEventHandler implementations. + + WebKit::MockWebPeerConnectionHandlerClient client; + ms_impl->CreatePeerConnectionHandler(&client); + EXPECT_TRUE(ms_impl->peer_connection_handler_); + + ms_impl->stream_labels_.push_back("label1"); + ms_impl->stream_labels_.push_back("label2"); + EXPECT_EQ(2, ms_dispatcher->stop_stream_counter()); + EXPECT_TRUE(ms_impl->stream_labels_.empty()); + + ms_impl->ClosePeerConnection(); + EXPECT_FALSE(ms_impl->peer_connection_handler_); +} diff --git a/content/renderer/media/mock_media_stream_dependency_factory.cc b/content/renderer/media/mock_media_stream_dependency_factory.cc new file mode 100644 index 0000000..73c483c --- /dev/null +++ b/content/renderer/media/mock_media_stream_dependency_factory.cc @@ -0,0 +1,41 @@ +// Copyright (c) 2011 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 "base/logging.h" +#include "content/renderer/media/mock_media_stream_dependency_factory.h" +#include "content/renderer/media/mock_peer_connection_impl.h" +#include "third_party/libjingle/source/talk/session/phone/webrtcmediaengine.h" + +MockMediaStreamDependencyFactory::MockMediaStreamDependencyFactory() + : mock_pc_factory_created_(false) { +} + +MockMediaStreamDependencyFactory::~MockMediaStreamDependencyFactory() {} + +cricket::WebRtcMediaEngine* +MockMediaStreamDependencyFactory::CreateWebRtcMediaEngine() { + return new cricket::WebRtcMediaEngine(NULL, NULL, NULL); +} + +bool MockMediaStreamDependencyFactory::CreatePeerConnectionFactory( + cricket::PortAllocator* port_allocator, + cricket::MediaEngineInterface* media_engine, + talk_base::Thread* worker_thread) { + mock_pc_factory_created_ = true; + return true; +} + +void MockMediaStreamDependencyFactory::DeletePeerConnectionFactory() { + mock_pc_factory_created_ = false; +} + +bool MockMediaStreamDependencyFactory::PeerConnectionFactoryCreated() { + return mock_pc_factory_created_; +} + +webrtc::PeerConnection* MockMediaStreamDependencyFactory::CreatePeerConnection( + talk_base::Thread* signaling_thread) { + DCHECK(mock_pc_factory_created_); + return new webrtc::MockPeerConnectionImpl(); +} diff --git a/content/renderer/media/mock_media_stream_dependency_factory.h b/content/renderer/media/mock_media_stream_dependency_factory.h new file mode 100644 index 0000000..3842d8d --- /dev/null +++ b/content/renderer/media/mock_media_stream_dependency_factory.h @@ -0,0 +1,33 @@ +// Copyright (c) 2011 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. + +#ifndef CONTENT_RENDERER_MEDIA_MOCK_MEDIA_STREAM_DEPENDENCY_FACTORY_H_ +#define CONTENT_RENDERER_MEDIA_MOCK_MEDIA_STREAM_DEPENDENCY_FACTORY_H_ + +#include "base/compiler_specific.h" +#include "content/renderer/media/media_stream_dependency_factory.h" + +// A mock factory for creating different objects for MediaStreamImpl. +class MockMediaStreamDependencyFactory : public MediaStreamDependencyFactory { + public: + MockMediaStreamDependencyFactory(); + virtual ~MockMediaStreamDependencyFactory(); + + virtual cricket::WebRtcMediaEngine* CreateWebRtcMediaEngine() OVERRIDE; + virtual bool CreatePeerConnectionFactory( + cricket::PortAllocator* port_allocator, + cricket::MediaEngineInterface* media_engine, + talk_base::Thread* worker_thread) OVERRIDE; + virtual void DeletePeerConnectionFactory() OVERRIDE; + virtual bool PeerConnectionFactoryCreated() OVERRIDE; + virtual webrtc::PeerConnection* CreatePeerConnection( + talk_base::Thread* signaling_thread) OVERRIDE; + + private: + bool mock_pc_factory_created_; + + DISALLOW_COPY_AND_ASSIGN(MockMediaStreamDependencyFactory); +}; + +#endif // CONTENT_RENDERER_MEDIA_MOCK_MEDIA_STREAM_DEPENDENCY_FACTORY_H_ diff --git a/content/renderer/media/mock_media_stream_dispatcher.cc b/content/renderer/media/mock_media_stream_dispatcher.cc new file mode 100644 index 0000000..aa495cc --- /dev/null +++ b/content/renderer/media/mock_media_stream_dispatcher.cc @@ -0,0 +1,49 @@ +// Copyright (c) 2011 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/mock_media_stream_dispatcher.h" + +MockMediaStreamDispatcher::MockMediaStreamDispatcher() + : MediaStreamDispatcher(NULL), + request_id_(-1), + event_handler_(NULL), + components_(NULL), + stop_stream_counter_(0) { +} + +MockMediaStreamDispatcher::~MockMediaStreamDispatcher() {} + +void MockMediaStreamDispatcher::GenerateStream( + int request_id, + MediaStreamDispatcherEventHandler* event_handler, + media_stream::StreamOptions components, + const std::string& security_origin) { + request_id_ = request_id; + event_handler_ = event_handler; + delete components_; + components_ = new media_stream::StreamOptions(components.audio, + components.video_option); + security_origin_ = security_origin; +} + +void MockMediaStreamDispatcher::StopStream(const std::string& label) { + ++stop_stream_counter_; +} + +bool MockMediaStreamDispatcher::IsStream(const std::string& label) { + NOTIMPLEMENTED(); + return false; +} + +int MockMediaStreamDispatcher::video_session_id(const std::string& label, + int index) { + NOTIMPLEMENTED(); + return -1; +} + +int MockMediaStreamDispatcher::audio_session_id(const std::string& label, + int index) { + NOTIMPLEMENTED(); + return -1; +} diff --git a/content/renderer/media/mock_media_stream_dispatcher.h b/content/renderer/media/mock_media_stream_dispatcher.h new file mode 100644 index 0000000..6f4e80f --- /dev/null +++ b/content/renderer/media/mock_media_stream_dispatcher.h @@ -0,0 +1,45 @@ +// Copyright (c) 2011 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. + +#ifndef CONTENT_RENDERER_MEDIA_MOCK_MEDIA_STREAM_DISPATCHER_H_ +#define CONTENT_RENDERER_MEDIA_MOCK_MEDIA_STREAM_DISPATCHER_H_ + +#include <string> + +#include "content/renderer/media/media_stream_dispatcher.h" + +// This class is a mock implementation of MediaStreamDispatcher. +class MockMediaStreamDispatcher : public MediaStreamDispatcher { + public: + MockMediaStreamDispatcher(); + virtual ~MockMediaStreamDispatcher(); + + virtual void GenerateStream(int request_id, + MediaStreamDispatcherEventHandler* event_handler, + media_stream::StreamOptions components, + const std::string& security_origin) OVERRIDE; + virtual void StopStream(const std::string& label) OVERRIDE; + virtual bool IsStream(const std::string& label) OVERRIDE; + virtual int video_session_id(const std::string& label, int index) OVERRIDE; + virtual int audio_session_id(const std::string& label, int index) OVERRIDE; + + int request_id() const { return request_id_; } + MediaStreamDispatcherEventHandler* event_handler() const { + return event_handler_; + } + media_stream::StreamOptions* components() const { return components_; } + const std::string& security_origin() const { return security_origin_; } + int stop_stream_counter() const { return stop_stream_counter_; } + + private: + int request_id_; + MediaStreamDispatcherEventHandler* event_handler_; + media_stream::StreamOptions* components_; + std::string security_origin_; + int stop_stream_counter_; + + DISALLOW_COPY_AND_ASSIGN(MockMediaStreamDispatcher); +}; + +#endif // CONTENT_RENDERER_MEDIA_MOCK_MEDIA_STREAM_DISPATCHER_H_ diff --git a/content/renderer/media/mock_media_stream_impl.cc b/content/renderer/media/mock_media_stream_impl.cc new file mode 100644 index 0000000..d5bf0dc --- /dev/null +++ b/content/renderer/media/mock_media_stream_impl.cc @@ -0,0 +1,60 @@ +// Copyright (c) 2011 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/mock_media_stream_impl.h" + +#include "content/renderer/media/rtc_video_decoder.h" + +MockMediaStreamImpl::MockMediaStreamImpl() + : MediaStreamImpl(NULL, NULL, NULL, NULL) { +} + +MockMediaStreamImpl::~MockMediaStreamImpl() {} + +WebKit::WebPeerConnectionHandler* +MockMediaStreamImpl::CreatePeerConnectionHandler( + WebKit::WebPeerConnectionHandlerClient* client) { + NOTIMPLEMENTED(); + return NULL; +} + +void MockMediaStreamImpl::ClosePeerConnection() { + video_label_.clear(); +} + +bool MockMediaStreamImpl::SetVideoCaptureModule(const std::string& label) { + video_label_ = label; + return true; +} + +scoped_refptr<media::VideoDecoder> MockMediaStreamImpl::GetVideoDecoder( + const GURL& url, + media::MessageLoopFactory* message_loop_factory) { + NOTIMPLEMENTED(); + return NULL; +} + +void MockMediaStreamImpl::OnStreamGenerated( + int request_id, + const std::string& label, + const media_stream::StreamDeviceInfoArray& audio_array, + const media_stream::StreamDeviceInfoArray& video_array) { + NOTIMPLEMENTED(); +} + +void MockMediaStreamImpl::OnStreamGenerationFailed(int request_id) { + NOTIMPLEMENTED(); +} + +void MockMediaStreamImpl::OnVideoDeviceFailed( + const std::string& label, + int index) { + NOTIMPLEMENTED(); +} + +void MockMediaStreamImpl::OnAudioDeviceFailed( + const std::string& label, + int index) { + NOTIMPLEMENTED(); +} diff --git a/content/renderer/media/mock_media_stream_impl.h b/content/renderer/media/mock_media_stream_impl.h new file mode 100644 index 0000000..52d7e57 --- /dev/null +++ b/content/renderer/media/mock_media_stream_impl.h @@ -0,0 +1,50 @@ +// Copyright (c) 2011 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. + +#ifndef CONTENT_RENDERER_MEDIA_MOCK_MEDIA_STREAM_IMPL_H_ +#define CONTENT_RENDERER_MEDIA_MOCK_MEDIA_STREAM_IMPL_H_ + +#include <string> + +#include "content/renderer/media/media_stream_impl.h" + +class MockMediaStreamImpl : public MediaStreamImpl { + public: + MockMediaStreamImpl(); + virtual ~MockMediaStreamImpl(); + + virtual WebKit::WebPeerConnectionHandler* CreatePeerConnectionHandler( + WebKit::WebPeerConnectionHandlerClient* client); + virtual void ClosePeerConnection(); + // Returns true if created successfully or already exists, false otherwise. + virtual bool SetVideoCaptureModule(const std::string& label); + + // Implement webkit_glue::MediaStreamClient. + virtual scoped_refptr<media::VideoDecoder> GetVideoDecoder( + const GURL& url, + media::MessageLoopFactory* message_loop_factory) OVERRIDE; + + // Implement MediaStreamDispatcherEventHandler. + virtual void OnStreamGenerated( + int request_id, + const std::string& label, + const media_stream::StreamDeviceInfoArray& audio_array, + const media_stream::StreamDeviceInfoArray& video_array) OVERRIDE; + virtual void OnStreamGenerationFailed(int request_id) OVERRIDE; + virtual void OnVideoDeviceFailed( + const std::string& label, + int index) OVERRIDE; + virtual void OnAudioDeviceFailed( + const std::string& label, + int index) OVERRIDE; + + const std::string& video_label() const { return video_label_; } + + private: + std::string video_label_; + + DISALLOW_COPY_AND_ASSIGN(MockMediaStreamImpl); +}; + +#endif // CONTENT_RENDERER_MEDIA_MOCK_MEDIA_STREAM_IMPL_H_ diff --git a/content/renderer/media/mock_peer_connection_impl.cc b/content/renderer/media/mock_peer_connection_impl.cc new file mode 100644 index 0000000..f954f98 --- /dev/null +++ b/content/renderer/media/mock_peer_connection_impl.cc @@ -0,0 +1,91 @@ +// Copyright (c) 2011 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/mock_peer_connection_impl.h" + +#include "base/logging.h" + +namespace webrtc { + +MockPeerConnectionImpl::MockPeerConnectionImpl() + : observer_(NULL), + video_stream_(false), + connected_(false), + video_capture_set_(false) { +} + +MockPeerConnectionImpl::~MockPeerConnectionImpl() {} + +void MockPeerConnectionImpl::RegisterObserver( + PeerConnectionObserver* observer) { + observer_ = observer; +} + +bool MockPeerConnectionImpl::SignalingMessage( + const std::string& signaling_message) { + signaling_message_ = signaling_message; + return true; +} + +bool MockPeerConnectionImpl::AddStream( + const std::string& stream_id, + bool video) { + stream_id_ = stream_id; + video_stream_ = video; + return true; +} + +bool MockPeerConnectionImpl::RemoveStream(const std::string& stream_id) { + stream_id_.clear(); + video_stream_ = false; + return true; +} + +bool MockPeerConnectionImpl::Connect() { + connected_ = true; + return true; +} + +bool MockPeerConnectionImpl::Close() { + observer_ = NULL; + signaling_message_.clear(); + stream_id_.clear(); + video_stream_ = false; + connected_ = false; + video_capture_set_ = false; + return true; +} + +bool MockPeerConnectionImpl::SetAudioDevice( + const std::string& wave_in_device, + const std::string& wave_out_device, + int opts) { + NOTIMPLEMENTED(); + return false; +} + +bool MockPeerConnectionImpl::SetLocalVideoRenderer( + cricket::VideoRenderer* renderer) { + NOTIMPLEMENTED(); + return false; +} + +bool MockPeerConnectionImpl::SetVideoRenderer( + const std::string& stream_id, + cricket::VideoRenderer* renderer) { + video_renderer_stream_id_ = stream_id; + return true; +} + +bool MockPeerConnectionImpl::SetVideoCapture(const std::string& cam_device) { + video_capture_set_ = true; + return true; +} + +MockPeerConnectionImpl::ReadyState MockPeerConnectionImpl::GetReadyState() { + NOTIMPLEMENTED(); + return NEW; +} + +} // namespace webrtc diff --git a/content/renderer/media/mock_peer_connection_impl.h b/content/renderer/media/mock_peer_connection_impl.h new file mode 100644 index 0000000..500e6ac --- /dev/null +++ b/content/renderer/media/mock_peer_connection_impl.h @@ -0,0 +1,62 @@ +// Copyright (c) 2011 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. + +#ifndef CONTENT_RENDERER_MEDIA_MOCK_PEER_CONNECTION_IMPL_H_ +#define CONTENT_RENDERER_MEDIA_MOCK_PEER_CONNECTION_IMPL_H_ + +#include <string> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "third_party/libjingle/source/talk/app/webrtc/peerconnection.h" + +namespace webrtc { + +class MockPeerConnectionImpl : public PeerConnection { + public: + MockPeerConnectionImpl(); + virtual ~MockPeerConnectionImpl(); + + // PeerConnection implementation. + virtual void RegisterObserver(PeerConnectionObserver* observer) OVERRIDE; + virtual bool SignalingMessage(const std::string& msg) OVERRIDE; + virtual bool AddStream(const std::string& stream_id, bool video) OVERRIDE; + virtual bool RemoveStream(const std::string& stream_id) OVERRIDE; + virtual bool Connect() OVERRIDE; + virtual bool Close() OVERRIDE; + virtual bool SetAudioDevice( + const std::string& wave_in_device, + const std::string& wave_out_device, int opts) OVERRIDE; + virtual bool SetLocalVideoRenderer(cricket::VideoRenderer* renderer) OVERRIDE; + virtual bool SetVideoRenderer( + const std::string& stream_id, + cricket::VideoRenderer* renderer) OVERRIDE; + virtual bool SetVideoCapture(const std::string& cam_device) OVERRIDE; + virtual ReadyState GetReadyState() OVERRIDE; + + PeerConnectionObserver* observer() const { return observer_; } + const std::string& signaling_message() const { return signaling_message_; } + const std::string& stream_id() const { return stream_id_; } + bool video_stream() const { return video_stream_; } + bool connected() const { return connected_; } + bool video_capture_set() const { return video_capture_set_; } + const std::string& video_renderer_stream_id() const { + return video_renderer_stream_id_; + } + + private: + PeerConnectionObserver* observer_; + std::string signaling_message_; + std::string stream_id_; + bool video_stream_; + bool connected_; + bool video_capture_set_; + std::string video_renderer_stream_id_; + + DISALLOW_COPY_AND_ASSIGN(MockPeerConnectionImpl); +}; + +} // namespace webrtc + +#endif // CONTENT_RENDERER_MEDIA_MOCK_PEER_CONNECTION_IMPL_H_ diff --git a/content/renderer/media/mock_web_peer_connection_handler_client.cc b/content/renderer/media/mock_web_peer_connection_handler_client.cc new file mode 100644 index 0000000..eaba3ff --- /dev/null +++ b/content/renderer/media/mock_web_peer_connection_handler_client.cc @@ -0,0 +1,38 @@ +// Copyright (c) 2011 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 "base/logging.h" +#include "base/utf_string_conversions.h" +#include "content/renderer/media/mock_web_peer_connection_handler_client.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebMediaStreamDescriptor.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebString.h" + +namespace WebKit { + +MockWebPeerConnectionHandlerClient::MockWebPeerConnectionHandlerClient() {} + +MockWebPeerConnectionHandlerClient::~MockWebPeerConnectionHandlerClient() {} + +void MockWebPeerConnectionHandlerClient::didCompleteICEProcessing() {} + +void MockWebPeerConnectionHandlerClient::didGenerateSDP(const WebString& sdp) { + sdp_ = UTF16ToUTF8(sdp); +} + +void MockWebPeerConnectionHandlerClient::didReceiveDataStreamMessage( + const char* data, size_t length) { +} + +void MockWebPeerConnectionHandlerClient::didAddRemoteStream( + const WebMediaStreamDescriptor& stream_descriptor) { + stream_label_ = UTF16ToUTF8(stream_descriptor.label()); +} + +void MockWebPeerConnectionHandlerClient::didRemoveRemoteStream( + const WebMediaStreamDescriptor& stream_descriptor) { + DCHECK(stream_label_ == UTF16ToUTF8(stream_descriptor.label())); + stream_label_.clear(); +} + +} // namespace WebKit diff --git a/content/renderer/media/mock_web_peer_connection_handler_client.h b/content/renderer/media/mock_web_peer_connection_handler_client.h new file mode 100644 index 0000000..5ea7f9e --- /dev/null +++ b/content/renderer/media/mock_web_peer_connection_handler_client.h @@ -0,0 +1,42 @@ +// Copyright (c) 2011 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. + +#ifndef CONTENT_RENDERER_MEDIA_MOCK_WEB_PEER_CONNECTION_HANDLER_CLIENT_H_ +#define CONTENT_RENDERER_MEDIA_MOCK_WEB_PEER_CONNECTION_HANDLER_CLIENT_H_ + +#include <string> + +#include "base/basictypes.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebPeerConnectionHandlerClient.h" + +namespace WebKit { + +class MockWebPeerConnectionHandlerClient + : public WebPeerConnectionHandlerClient { + public: + MockWebPeerConnectionHandlerClient(); + virtual ~MockWebPeerConnectionHandlerClient(); + + // WebPeerConnectionHandlerClient implementation. + virtual void didCompleteICEProcessing(); + virtual void didGenerateSDP(const WebString& sdp); + virtual void didReceiveDataStreamMessage(const char* data, size_t length); + virtual void didAddRemoteStream( + const WebMediaStreamDescriptor& stream_descriptor); + virtual void didRemoveRemoteStream( + const WebMediaStreamDescriptor& stream_descriptor); + + const std::string& stream_label() { return stream_label_; } + const std::string& sdp() const { return sdp_; } + + private: + std::string stream_label_; + std::string sdp_; + + DISALLOW_COPY_AND_ASSIGN(MockWebPeerConnectionHandlerClient); +}; + +} // namespace WebKit + +#endif // CONTENT_RENDERER_MEDIA_MOCK_WEB_PEER_CONNECTION_HANDLER_CLIENT_H_ diff --git a/content/renderer/media/peer_connection_handler.cc b/content/renderer/media/peer_connection_handler.cc new file mode 100644 index 0000000..618921a --- /dev/null +++ b/content/renderer/media/peer_connection_handler.cc @@ -0,0 +1,254 @@ +// Copyright (c) 2011 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/peer_connection_handler.h" + +#include <stdlib.h> +#include <vector> + +#include "base/bind.h" +#include "base/logging.h" +#include "base/string_number_conversions.h" +#include "base/utf_string_conversions.h" +#include "content/renderer/media/media_stream_dependency_factory.h" +#include "content/renderer/media/media_stream_impl.h" +#include "third_party/libjingle/source/talk/base/thread.h" +#include "third_party/libjingle/source/talk/p2p/client/httpportallocator.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebMediaStreamDescriptor.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebMediaStreamSource.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebPeerConnectionHandlerClient.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebVector.h" + +PeerConnectionHandler::PeerConnectionHandler( + WebKit::WebPeerConnectionHandlerClient* client, + MediaStreamImpl* msi, + MediaStreamDependencyFactory* dependency_factory, + talk_base::Thread* signaling_thread, + cricket::HttpPortAllocator* port_allocator) + : client_(client), + media_stream_impl_(msi), + dependency_factory_(dependency_factory), + message_loop_proxy_(base::MessageLoopProxy::current()), + signaling_thread_(signaling_thread), + port_allocator_(port_allocator), + call_state_(NOT_STARTED) { +} + +PeerConnectionHandler::~PeerConnectionHandler() { + if (native_peer_connection_.get()) { + native_peer_connection_->RegisterObserver(NULL); + native_peer_connection_->Close(); + } +} + +bool PeerConnectionHandler::SetVideoRenderer( + const std::string& stream_label, + cricket::VideoRenderer* renderer) { + return native_peer_connection_->SetVideoRenderer(stream_label, renderer); +} + +void PeerConnectionHandler::initialize( + const WebKit::WebString& server_configuration, + const WebKit::WebSecurityOrigin& security_origin) { + // We support the following server configuration format: + // "STUN <address>:<port>". We only support STUN at the moment. + + // Strip "STUN ". + std::string strip_string = "STUN "; + std::string config = UTF16ToUTF8(server_configuration); + size_t pos = config.find(strip_string); + if (pos != 0) { + DVLOG(1) << "Invalid configuration string."; + return; + } + config = config.substr(strip_string.length()); + // Parse out port. + pos = config.find(':'); + if (pos == std::string::npos) { + DVLOG(1) << "Invalid configuration string."; + return; + } + int port = 0; + bool success = base::StringToInt(config.substr(pos+1), &port); + if (!success || (port == 0)) { + DVLOG(1) << "Invalid configuration string."; + return; + } + // Get address. + std::string address = config.substr(0, pos); + + std::vector<talk_base::SocketAddress> stun_hosts; + stun_hosts.push_back(talk_base::SocketAddress(address, port)); + port_allocator_->SetStunHosts(stun_hosts); + + native_peer_connection_.reset(dependency_factory_->CreatePeerConnection( + signaling_thread_)); + native_peer_connection_->RegisterObserver(this); +} + +void PeerConnectionHandler::produceInitialOffer( + const WebKit::WebVector<WebKit::WebMediaStreamDescriptor>& + pending_add_streams) { + // We currently don't support creating an initial offer without a stream. + // Native PeerConnection will anyway create the initial offer when the first + // (and only) stream is added, so it will be done when processPendingStreams + // is called if not here. + if (pending_add_streams.isEmpty()) { + DVLOG(1) << "Can't produce initial offer with no stream."; + return; + } + // TODO(grunell): Support several streams. + DCHECK_EQ(pending_add_streams.size(), 1u); + WebKit::WebVector<WebKit::WebMediaStreamSource> source_vector; + pending_add_streams[0].sources(source_vector); + DCHECK_GT(source_vector.size(), 0u); + std::string label = UTF16ToUTF8(source_vector[0].id()); + AddStream(label); +} + +void PeerConnectionHandler::handleInitialOffer(const WebKit::WebString& sdp) { + native_peer_connection_->SignalingMessage(UTF16ToUTF8(sdp)); +} + +void PeerConnectionHandler::processSDP(const WebKit::WebString& sdp) { + native_peer_connection_->SignalingMessage(UTF16ToUTF8(sdp)); +} + +void PeerConnectionHandler::processPendingStreams( + const WebKit::WebVector<WebKit::WebMediaStreamDescriptor>& + pending_add_streams, + const WebKit::WebVector<WebKit::WebMediaStreamDescriptor>& + pending_remove_streams) { + // TODO(grunell): Support several streams. + if (!pending_add_streams.isEmpty()) { + DCHECK_EQ(pending_add_streams.size(), 1u); + AddStream(UTF16ToUTF8(pending_add_streams[0].label())); + } + // Currently we ignore remove stream, no support in native PeerConnection. +} + +void PeerConnectionHandler::sendDataStreamMessage( + const char* data, + size_t length) { + // TODO(grunell): Implement. Not supported in native PeerConnection. + NOTIMPLEMENTED(); +} + +void PeerConnectionHandler::stop() { + if (native_peer_connection_.get()) + native_peer_connection_->RegisterObserver(NULL); + native_peer_connection_.reset(); + // The close function will delete us. + media_stream_impl_->ClosePeerConnection(); +} + +void PeerConnectionHandler::OnSignalingMessage(const std::string& msg) { + if (!message_loop_proxy_->BelongsToCurrentThread()) { + message_loop_proxy_->PostTask(FROM_HERE, base::Bind( + &PeerConnectionHandler::OnSignalingMessage, + base::Unretained(this), + msg)); + return; + } + client_->didGenerateSDP(UTF8ToUTF16(msg)); +} + +void PeerConnectionHandler::OnAddStream(const std::string& stream_id, + bool video) { + if (!video) + return; + + if (!message_loop_proxy_->BelongsToCurrentThread()) { + message_loop_proxy_->PostTask(FROM_HERE, base::Bind( + &PeerConnectionHandler::OnAddStreamCallback, + base::Unretained(this), + stream_id)); + } else { + OnAddStreamCallback(stream_id); + } +} + +void PeerConnectionHandler::OnRemoveStream( + const std::string& stream_id, + bool video) { + if (!video) + return; + + if (!message_loop_proxy_->BelongsToCurrentThread()) { + message_loop_proxy_->PostTask(FROM_HERE, base::Bind( + &PeerConnectionHandler::OnRemoveStreamCallback, + base::Unretained(this), + remote_label_)); + } else { + OnRemoveStreamCallback(remote_label_); + } +} + +void PeerConnectionHandler::AddStream(const std::string label) { + // TODO(grunell): Fix code in this function after a new native PeerConnection + // version has been rolled out. + if (call_state_ == NOT_STARTED) { + // TODO(grunell): Add audio and/or video depending on what's enabled + // in the stream. + std::string audio_label = label; + audio_label.append("-audio"); + native_peer_connection_->AddStream(audio_label, false); // Audio + native_peer_connection_->AddStream(label, true); // Video + call_state_ = INITIATING; + } + if (call_state_ == INITIATING || call_state_ == RECEIVING) { + local_label_ = label; + if (media_stream_impl_->SetVideoCaptureModule(label)) + native_peer_connection_->SetVideoCapture(""); + if (call_state_ == INITIATING) + native_peer_connection_->Connect(); + else if (call_state_ == RECEIVING) + call_state_ = SENDING_AND_RECEIVING; + } else { + DLOG(ERROR) << "Multiple streams not supported"; + } +} + +void PeerConnectionHandler::OnAddStreamCallback( + const std::string& stream_label) { + // TODO(grunell): Fix code in this function after a new native PeerConnection + // version has been rolled out. + if (call_state_ == NOT_STARTED) { + remote_label_ = stream_label; + call_state_ = RECEIVING; + } else if (call_state_ == INITIATING) { + remote_label_ = local_label_; + remote_label_ += "-remote"; + call_state_ = SENDING_AND_RECEIVING; + } + + // TODO(grunell): Support several tracks. + WebKit::WebVector<WebKit::WebMediaStreamSource> + source_vector(static_cast<size_t>(2)); + source_vector[0].initialize(WebKit::WebString::fromUTF8(remote_label_), + WebKit::WebMediaStreamSource::TypeVideo, + WebKit::WebString::fromUTF8("RemoteVideo")); + source_vector[1].initialize(WebKit::WebString::fromUTF8(remote_label_), + WebKit::WebMediaStreamSource::TypeAudio, + WebKit::WebString::fromUTF8("RemoteAudio")); + WebKit::WebMediaStreamDescriptor descriptor; + descriptor.initialize(UTF8ToUTF16(remote_label_), source_vector); + client_->didAddRemoteStream(descriptor); +} + +void PeerConnectionHandler::OnRemoveStreamCallback( + const std::string& stream_label) { + // TODO(grunell): Support several tracks. + WebKit::WebVector<WebKit::WebMediaStreamSource> + source_vector(static_cast<size_t>(2)); + source_vector[0].initialize(WebKit::WebString::fromUTF8(stream_label), + WebKit::WebMediaStreamSource::TypeVideo, + WebKit::WebString::fromUTF8("RemoteVideo")); + source_vector[1].initialize(WebKit::WebString::fromUTF8(stream_label), + WebKit::WebMediaStreamSource::TypeAudio, + WebKit::WebString::fromUTF8("RemoteAudio")); + WebKit::WebMediaStreamDescriptor descriptor; + descriptor.initialize(UTF8ToUTF16(stream_label), source_vector); + client_->didRemoveRemoteStream(descriptor); +} diff --git a/content/renderer/media/peer_connection_handler.h b/content/renderer/media/peer_connection_handler.h new file mode 100644 index 0000000..89b5012 --- /dev/null +++ b/content/renderer/media/peer_connection_handler.h @@ -0,0 +1,135 @@ +// Copyright (c) 2011 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. + +#ifndef CONTENT_RENDERER_MEDIA_PEER_CONNECTION_HANDLER_H_ +#define CONTENT_RENDERER_MEDIA_PEER_CONNECTION_HANDLER_H_ + +#include <string> + +#include "base/basictypes.h" +#include "base/gtest_prod_util.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop_proxy.h" +#include "third_party/libjingle/source/talk/app/webrtc/peerconnection.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebPeerConnectionHandler.h" + +namespace cricket { +class HttpPortAllocator; +} + +namespace talk_base { +class Thread; +} + +class MediaStreamDependencyFactory; +class MediaStreamImpl; + +// PeerConnectionHandler is a delegate for the PeerConnection API messages going +// between WebKit and native PeerConnection in libjingle. It's owned by +// MediaStreamImpl. +class PeerConnectionHandler + : public WebKit::WebPeerConnectionHandler, + public webrtc::PeerConnectionObserver { + public: + PeerConnectionHandler( + WebKit::WebPeerConnectionHandlerClient* client, + MediaStreamImpl* msi, + MediaStreamDependencyFactory* dependency_factory, + talk_base::Thread* signaling_thread, + cricket::HttpPortAllocator* port_allocator); + virtual ~PeerConnectionHandler(); + + // Set the video renderer for the specified stream. + virtual bool SetVideoRenderer(const std::string& stream_label, + cricket::VideoRenderer* renderer); + + // WebKit::WebPeerConnectionHandler implementation + virtual void initialize( + const WebKit::WebString& server_configuration, + const WebKit::WebSecurityOrigin& security_origin) OVERRIDE; + virtual void produceInitialOffer( + const WebKit::WebVector<WebKit::WebMediaStreamDescriptor>& + pending_add_streams) OVERRIDE; + virtual void handleInitialOffer(const WebKit::WebString& sdp) OVERRIDE; + virtual void processSDP(const WebKit::WebString& sdp) OVERRIDE; + virtual void processPendingStreams( + const WebKit::WebVector<WebKit::WebMediaStreamDescriptor>& + pending_add_streams, + const WebKit::WebVector<WebKit::WebMediaStreamDescriptor>& + pending_remove_streams) OVERRIDE; + virtual void sendDataStreamMessage(const char* data, size_t length) OVERRIDE; + virtual void stop() OVERRIDE; + + // webrtc::PeerConnectionObserver implementation + virtual void OnSignalingMessage(const std::string& msg) OVERRIDE; + virtual void OnAddStream(const std::string& stream_id, bool video) OVERRIDE; + virtual void OnRemoveStream( + const std::string& stream_id, + bool video) OVERRIDE; + + private: + FRIEND_TEST_ALL_PREFIXES(PeerConnectionHandlerTest, Basic); + + void AddStream(const std::string label); + void OnAddStreamCallback(const std::string& stream_label); + void OnRemoveStreamCallback(const std::string& stream_label); + + // client_ is a weak pointer, and is valid until stop() has returned. + WebKit::WebPeerConnectionHandlerClient* client_; + + // media_stream_impl_ is a weak pointer, and is valid for the lifetime of this + // class. + MediaStreamImpl* media_stream_impl_; + + // dependency_factory_ is a weak pointer, and is valid for the lifetime of + // MediaStreamImpl. + MediaStreamDependencyFactory* dependency_factory_; + + // native_peer_connection_ is the native PeerConnection object, + // it handles the ICE processing and media engine. + scoped_ptr<webrtc::PeerConnection> native_peer_connection_; + + scoped_refptr<base::MessageLoopProxy> message_loop_proxy_; + + talk_base::Thread* signaling_thread_; + + cricket::HttpPortAllocator* port_allocator_; + + // Currently, a stream in WebKit has audio and/or video and has one label. + // Local and remote streams have different labels. + // In native PeerConnection, a stream has audio or video (not both), and they + // have separate labels. A remote stream has the same label as the + // corresponding local stream. Hence the workarounds in the implementation to + // handle this. It could look like this: + // WebKit: Local, audio and video: label "foo". + // Remote, audio and video: label "foo-remote". + // Libjingle: Local and remote, audio: label "foo-audio". + // Local and remote, video: label "foo". + // TODO(grunell): This shall be removed or changed when native PeerConnection + // has been updated to closer follow the specification. + std::string local_label_; // Label used in WebKit + std::string remote_label_; // Label used in WebKit + + // Call states. Possible transitions: + // NOT_STARTED -> INITIATING -> SENDING_AND_RECEIVING + // NOT_STARTED -> RECEIVING + // RECEIVING -> NOT_STARTED + // RECEIVING -> SENDING_AND_RECEIVING + // SENDING_AND_RECEIVING -> NOT_STARTED + // Note that when in state SENDING_AND_RECEIVING, the other side may or may + // not send media. Thus, this state does not necessarily mean full duplex. + // TODO(grunell): This shall be removed or changed when native PeerConnection + // has been updated to closer follow the specification. + enum CallState { + NOT_STARTED, + INITIATING, + RECEIVING, + SENDING_AND_RECEIVING + }; + CallState call_state_; + + DISALLOW_COPY_AND_ASSIGN(PeerConnectionHandler); +}; + +#endif // CONTENT_RENDERER_MEDIA_PEER_CONNECTION_HANDLER_H_ diff --git a/content/renderer/media/peer_connection_handler_unittest.cc b/content/renderer/media/peer_connection_handler_unittest.cc new file mode 100644 index 0000000..5e026ce --- /dev/null +++ b/content/renderer/media/peer_connection_handler_unittest.cc @@ -0,0 +1,122 @@ +// Copyright (c) 2011 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 <string> + +#include "base/memory/scoped_ptr.h" +#include "base/message_loop.h" +#include "base/utf_string_conversions.h" +#include "content/renderer/media/mock_media_stream_dependency_factory.h" +#include "content/renderer/media/mock_media_stream_impl.h" +#include "content/renderer/media/mock_web_peer_connection_handler_client.h" +#include "content/renderer/media/mock_peer_connection_impl.h" +#include "content/renderer/media/peer_connection_handler.h" +#include "content/renderer/media/rtc_video_decoder.h" +#include "jingle/glue/thread_wrapper.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/libjingle/source/talk/app/webrtc/peerconnection.h" +#include "third_party/libjingle/source/talk/p2p/client/httpportallocator.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebMediaStreamDescriptor.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebMediaStreamSource.h" + +TEST(PeerConnectionHandlerTest, Basic) { + MessageLoop loop; + + scoped_ptr<WebKit::MockWebPeerConnectionHandlerClient> mock_client( + new WebKit::MockWebPeerConnectionHandlerClient()); + scoped_refptr<MockMediaStreamImpl> mock_ms_impl(new MockMediaStreamImpl()); + MockMediaStreamDependencyFactory* mock_dependency_factory = + new MockMediaStreamDependencyFactory(); + mock_dependency_factory->CreatePeerConnectionFactory(NULL, NULL, NULL); + scoped_ptr<cricket::HttpPortAllocator> port_allocator( + new cricket::HttpPortAllocator(NULL, "PeerConnection")); + scoped_ptr<PeerConnectionHandler> pc_handler( + new PeerConnectionHandler(mock_client.get(), + mock_ms_impl.get(), + mock_dependency_factory, + NULL, + port_allocator.get())); + + WebKit::WebString server_config( + WebKit::WebString::fromUTF8("STUN stun.l.google.com:19302")); + WebKit::WebSecurityOrigin security_origin; + pc_handler->initialize(server_config, security_origin); + EXPECT_TRUE(pc_handler->native_peer_connection_.get()); + webrtc::MockPeerConnectionImpl* mock_peer_connection = + static_cast<webrtc::MockPeerConnectionImpl*>( + pc_handler->native_peer_connection_.get()); + EXPECT_EQ(static_cast<webrtc::PeerConnectionObserver*>(pc_handler.get()), + mock_peer_connection->observer()); + + std::string label("label"); + WebKit::WebVector<WebKit::WebMediaStreamSource> + source_vector(static_cast<size_t>(1)); + source_vector[0].initialize(WebKit::WebString::fromUTF8(label), + WebKit::WebMediaStreamSource::TypeVideo, + WebKit::WebString::fromUTF8("RemoteVideo")); + WebKit::WebVector<WebKit::WebMediaStreamDescriptor> + pendingAddStreams(static_cast<size_t>(1)); + pendingAddStreams[0].initialize(UTF8ToUTF16(label), source_vector); + pc_handler->produceInitialOffer(pendingAddStreams); + EXPECT_EQ(label, mock_ms_impl->video_label()); + EXPECT_EQ(label, mock_peer_connection->stream_id()); + EXPECT_TRUE(mock_peer_connection->video_stream()); + EXPECT_TRUE(mock_peer_connection->connected()); + EXPECT_TRUE(mock_peer_connection->video_capture_set()); + + std::string message("message1"); + pc_handler->handleInitialOffer(WebKit::WebString::fromUTF8(message)); + EXPECT_EQ(message, mock_peer_connection->signaling_message()); + + message = "message2"; + pc_handler->processSDP(WebKit::WebString::fromUTF8(message)); + EXPECT_EQ(message, mock_peer_connection->signaling_message()); + + message = "message3"; + pc_handler->OnSignalingMessage(message); + EXPECT_EQ(message, mock_client->sdp()); + + std::string remote_label(label); + remote_label.append("-remote"); + pc_handler->OnAddStream(remote_label, true); + EXPECT_EQ(remote_label, mock_client->stream_label()); + + RTCVideoDecoder* rtc_video_decoder = new RTCVideoDecoder(&loop, ""); + pc_handler->SetVideoRenderer(label, rtc_video_decoder); + EXPECT_EQ(label, mock_peer_connection->video_renderer_stream_id()); + + pc_handler->OnRemoveStream(remote_label, true); + EXPECT_TRUE(mock_client->stream_label().empty()); + + pc_handler->stop(); + EXPECT_FALSE(pc_handler->native_peer_connection_.get()); + // PC handler is expected to be deleted when stop calls + // MediaStreamImpl::ClosePeerConnection. We own and delete it here instead of + // in the mock. + pc_handler.reset(); + + // processPendingStreams must be tested on a new PC handler since removing + // streams is currently not supported. + pc_handler.reset(new PeerConnectionHandler(mock_client.get(), + mock_ms_impl.get(), + mock_dependency_factory, + NULL, + port_allocator.get())); + pc_handler->initialize(server_config, security_origin); + EXPECT_TRUE(pc_handler->native_peer_connection_.get()); + mock_peer_connection = static_cast<webrtc::MockPeerConnectionImpl*>( + pc_handler->native_peer_connection_.get()); + + WebKit::WebVector<WebKit::WebMediaStreamDescriptor> + pendingRemoveStreams(static_cast<size_t>(0)); + pc_handler->processPendingStreams(pendingAddStreams, pendingRemoveStreams); + EXPECT_EQ(label, mock_ms_impl->video_label()); + EXPECT_EQ(label, mock_peer_connection->stream_id()); + EXPECT_TRUE(mock_peer_connection->video_stream()); + EXPECT_TRUE(mock_peer_connection->connected()); + EXPECT_TRUE(mock_peer_connection->video_capture_set()); + + pc_handler->stop(); + pc_handler.reset(); +} diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc index ed8e167..ae36902 100644 --- a/content/renderer/render_view_impl.cc +++ b/content/renderer/render_view_impl.cc @@ -59,6 +59,8 @@ #include "content/renderer/load_progress_tracker.h" #include "content/renderer/media/audio_message_filter.h" #include "content/renderer/media/audio_renderer_impl.h" +#include "content/renderer/media/media_stream_dependency_factory.h" +#include "content/renderer/media/media_stream_dispatcher.h" #include "content/renderer/media/media_stream_impl.h" #include "content/renderer/media/render_media_log.h" #include "content/renderer/mhtml_generator.h" @@ -104,6 +106,8 @@ #include "third_party/WebKit/Source/WebKit/chromium/public/WebMediaPlayerAction.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebNodeList.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebPageSerializer.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebPeerConnectionHandler.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebPeerConnectionHandlerClient.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebPlugin.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebPluginContainer.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebPluginDocument.h" @@ -124,6 +128,7 @@ #include "third_party/WebKit/Source/WebKit/chromium/public/WebURLError.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebURLRequest.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebURLResponse.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebUserMediaClient.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebVector.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebWindowFeatures.h" @@ -350,6 +355,7 @@ RenderViewImpl::RenderViewImpl( geolocation_dispatcher_(NULL), speech_input_dispatcher_(NULL), device_orientation_dispatcher_(NULL), + media_stream_dispatcher_(NULL), p2p_socket_dispatcher_(NULL), devtools_agent_(NULL), renderer_accessibility_(NULL), @@ -404,7 +410,8 @@ RenderViewImpl::RenderViewImpl( host_window_ = parent_hwnd; #if defined(ENABLE_P2P_APIS) - p2p_socket_dispatcher_ = new content::P2PSocketDispatcher(this); + if (!p2p_socket_dispatcher_) + p2p_socket_dispatcher_ = new content::P2PSocketDispatcher(this); #endif new MHTMLGenerator(this); @@ -418,12 +425,6 @@ RenderViewImpl::RenderViewImpl( new IdleUserDetector(this); - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); - if (command_line.HasSwitch(switches::kEnableMediaStream)) { - media_stream_impl_ = new MediaStreamImpl( - RenderThreadImpl::current()->video_capture_impl_manager()); - } - content::GetContentClient()->renderer()->RenderViewCreated(this); } @@ -459,6 +460,10 @@ RenderViewImpl::~RenderViewImpl() { DCHECK_NE(this, it->second) << "Failed to call Close?"; #endif + // MediaStreamImpl holds weak references to RenderViewObserver objects, + // ensure it's deleted before the observers. + media_stream_impl_ = NULL; + FOR_EACH_OBSERVER(RenderViewObserver, observers_, RenderViewGone()); FOR_EACH_OBSERVER(RenderViewObserver, observers_, OnDestruct()); } @@ -517,6 +522,15 @@ void RenderViewImpl::SetNextPageID(int32 next_page_id) { next_page_id_ = next_page_id; } +WebKit::WebPeerConnectionHandler* RenderViewImpl::CreatePeerConnectionHandler( + WebKit::WebPeerConnectionHandlerClient* client) { + const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); + if (!cmd_line->HasSwitch(switches::kEnableMediaStream)) + return NULL; + EnsureMediaStreamImpl(); + return media_stream_impl_->CreatePeerConnectionHandler(client); +} + void RenderViewImpl::AddObserver(RenderViewObserver* observer) { observers_.AddObserver(observer); } @@ -2891,6 +2905,25 @@ void RenderViewImpl::CheckPreferredSize() { preferred_size_)); } +void RenderViewImpl::EnsureMediaStreamImpl() { +#if defined(ENABLE_P2P_APIS) + if (!p2p_socket_dispatcher_) + p2p_socket_dispatcher_ = new content::P2PSocketDispatcher(this); +#endif + + if (!media_stream_dispatcher_) + media_stream_dispatcher_ = new MediaStreamDispatcher(this); + + if (!media_stream_impl_.get()) { + MediaStreamDependencyFactory* factory = new MediaStreamDependencyFactory(); + media_stream_impl_ = new MediaStreamImpl( + media_stream_dispatcher_, + p2p_socket_dispatcher_, + RenderThreadImpl::current()->video_capture_impl_manager(), + factory); + } +} + void RenderViewImpl::didChangeContentsSize(WebFrame* frame, const WebSize& size) { if (webview()->mainFrame() != frame) @@ -4610,6 +4643,14 @@ void RenderViewImpl::startActivity(const WebKit::WebString& action, routing_id_, intent_data, intent_id)); } +WebKit::WebUserMediaClient* RenderViewImpl::userMediaClient() { + const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); + if (!cmd_line->HasSwitch(switches::kEnableMediaStream)) + return NULL; + EnsureMediaStreamImpl(); + return media_stream_impl_; +} + bool RenderViewImpl::IsNonLocalTopLevelNavigation( const GURL& url, WebKit::WebFrame* frame, WebKit::WebNavigationType type) { // Must be a top level frame. diff --git a/content/renderer/render_view_impl.h b/content/renderer/render_view_impl.h index af3e977..f70954c5 100644 --- a/content/renderer/render_view_impl.h +++ b/content/renderer/render_view_impl.h @@ -63,6 +63,7 @@ class GURL; class IntentsDispatcher; class JavaBridgeDispatcher; class LoadProgressTracker; +class MediaStreamDispatcher; class MediaStreamImpl; class NotificationProvider; class PepperDeviceTest; @@ -116,12 +117,15 @@ class WebImage; class WebMediaPlayer; class WebMediaPlayerClient; class WebMouseEvent; +class WebPeerConnectionHandler; +class WebPeerConnectionHandlerClient; class WebSpeechInputController; class WebSpeechInputListener; class WebStorageNamespace; class WebTouchEvent; class WebURLLoader; class WebURLRequest; +class WebUserMediaClient; struct WebFileChooserParams; struct WebFindOptions; struct WebMediaPlayerAction; @@ -197,11 +201,18 @@ class RenderViewImpl : public RenderWidget, send_content_state_immediately_ = value; } + MediaStreamDispatcher* media_stream_dispatcher() { + return media_stream_dispatcher_; + } + // Current P2PSocketDispatcher. Set to NULL if P2P API is disabled. content::P2PSocketDispatcher* p2p_socket_dispatcher() { return p2p_socket_dispatcher_; } + WebKit::WebPeerConnectionHandler* CreatePeerConnectionHandler( + WebKit::WebPeerConnectionHandlerClient* client); + // Functions to add and remove observers for this object. void AddObserver(content::RenderViewObserver* observer); void RemoveObserver(content::RenderViewObserver* observer); @@ -390,6 +401,7 @@ class RenderViewImpl : public RenderWidget, const WebKit::WebString& type, const WebKit::WebString& data, int intent_id); + virtual WebKit::WebUserMediaClient* userMediaClient(); // WebKit::WebFrameClient implementation ------------------------------------- @@ -867,6 +879,8 @@ class RenderViewImpl : public RenderWidget, // Check whether the preferred size has changed. void CheckPreferredSize(); + void EnsureMediaStreamImpl(); + // This callback is triggered when DownloadFavicon completes, either // succesfully or with a failure. See DownloadFavicon for more // details. @@ -1142,6 +1156,9 @@ class RenderViewImpl : public RenderWidget, // Device orientation dispatcher attached to this view; lazily initialized. DeviceOrientationDispatcher* device_orientation_dispatcher_; + // MediaStream dispatcher attached to this view; lazily initialized. + MediaStreamDispatcher* media_stream_dispatcher_; + // MediaStreamImpl attached to this view; lazily initialized. scoped_refptr<MediaStreamImpl> media_stream_impl_; diff --git a/content/renderer/renderer_webkitplatformsupport_impl.cc b/content/renderer/renderer_webkitplatformsupport_impl.cc index e571411..3841dc6 100644 --- a/content/renderer/renderer_webkitplatformsupport_impl.cc +++ b/content/renderer/renderer_webkitplatformsupport_impl.cc @@ -24,6 +24,7 @@ #include "content/renderer/media/audio_device.h" #include "content/renderer/media/audio_hardware.h" #include "content/renderer/render_thread_impl.h" +#include "content/renderer/render_view_impl.h" #include "content/renderer/renderer_clipboard_client.h" #include "content/renderer/renderer_webaudiodevice_impl.h" #include "content/renderer/renderer_webidbfactory_impl.h" @@ -37,6 +38,9 @@ #include "third_party/WebKit/Source/WebKit/chromium/public/WebIDBFactory.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebIDBKey.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebIDBKeyPath.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebPeerConnectionHandler.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebPeerConnectionHandlerClient.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebRuntimeFeatures.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebSerializedScriptValue.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebStorageEventDispatcher.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebURL.h" @@ -631,3 +635,17 @@ void RendererWebKitPlatformSupportImpl::GetPlugins( RenderThreadImpl::current()->Send( new ViewHostMsg_GetPlugins(refresh, plugins)); } + +//------------------------------------------------------------------------------ + +WebKit::WebPeerConnectionHandler* +RendererWebKitPlatformSupportImpl::createPeerConnectionHandler( + WebKit::WebPeerConnectionHandlerClient* client) { + WebFrame* web_frame = WebFrame::frameForCurrentContext(); + if (!web_frame) + return NULL; + RenderViewImpl* render_view = RenderViewImpl::FromWebView(web_frame->view()); + if (!render_view) + return NULL; + return render_view->CreatePeerConnectionHandler(client); +} diff --git a/content/renderer/renderer_webkitplatformsupport_impl.h b/content/renderer/renderer_webkitplatformsupport_impl.h index c646799..d919ce4 100644 --- a/content/renderer/renderer_webkitplatformsupport_impl.h +++ b/content/renderer/renderer_webkitplatformsupport_impl.h @@ -87,6 +87,8 @@ class CONTENT_EXPORT RendererWebKitPlatformSupportImpl virtual WebKit::WebString userAgent(const WebKit::WebURL& url) OVERRIDE; virtual void GetPlugins(bool refresh, std::vector<webkit::WebPluginInfo>* plugins) OVERRIDE; + virtual WebKit::WebPeerConnectionHandler* createPeerConnectionHandler( + WebKit::WebPeerConnectionHandlerClient* client) OVERRIDE; private: bool CheckPreparsedJsCachingEnabled() const; |