diff options
28 files changed, 1391 insertions, 228 deletions
diff --git a/content/content_renderer.gypi b/content/content_renderer.gypi index e3ca2ea..d64fe40 100644 --- a/content/content_renderer.gypi +++ b/content/content_renderer.gypi @@ -269,6 +269,10 @@ 'renderer/media/media_stream_impl.cc', 'renderer/media/peer_connection_handler.cc', 'renderer/media/peer_connection_handler.h', + 'renderer/media/peer_connection_handler_base.cc', + 'renderer/media/peer_connection_handler_base.h', + 'renderer/media/peer_connection_handler_jsep.cc', + 'renderer/media/peer_connection_handler_jsep.h', 'renderer/media/video_capture_module_impl.cc', 'renderer/media/video_capture_module_impl.h', 'renderer/media/webrtc_audio_device_impl.cc', diff --git a/content/content_tests.gypi b/content/content_tests.gypi index 6ef791f..47a94f4 100644 --- a/content/content_tests.gypi +++ b/content/content_tests.gypi @@ -127,6 +127,8 @@ 'renderer/media/mock_media_stream_impl.h', 'renderer/media/mock_peer_connection_impl.cc', 'renderer/media/mock_peer_connection_impl.h', + 'renderer/media/mock_web_peer_connection_00_handler_client.cc', + 'renderer/media/mock_web_peer_connection_00_handler_client.h', 'renderer/media/mock_web_peer_connection_handler_client.cc', 'renderer/media/mock_web_peer_connection_handler_client.h', 'test/webrtc_audio_device_test.cc', @@ -299,6 +301,7 @@ 'sources': [ 'renderer/media/media_stream_dispatcher_unittest.cc', 'renderer/media/media_stream_impl_unittest.cc', + 'renderer/media/peer_connection_handler_jsep_unittest.cc', 'renderer/media/peer_connection_handler_unittest.cc', 'renderer/media/rtc_video_decoder_unittest.cc', 'renderer/media/webrtc_audio_device_unittest.cc', diff --git a/content/renderer/media/media_stream_center.cc b/content/renderer/media/media_stream_center.cc index 172a151..2e5a43b 100644 --- a/content/renderer/media/media_stream_center.cc +++ b/content/renderer/media/media_stream_center.cc @@ -4,10 +4,17 @@ #include "content/renderer/media/media_stream_center.h" +#include <string> + +#include "base/logging.h" +#include "base/utf_string_conversions.h" +#include "third_party/libjingle/source/talk/app/webrtc/jsep.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebICECandidateDescriptor.h" #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebMediaStreamCenterClient.h" #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebMediaStreamDescriptor.h" #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebMediaStreamSource.h" #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebMediaStreamSourcesRequest.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebSessionDescriptionDescriptor.h" #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebVector.h" namespace content { @@ -41,4 +48,36 @@ void MediaStreamCenter::didConstructMediaStream( const WebKit::WebMediaStreamDescriptor& stream) { } +WebKit::WebString MediaStreamCenter::constructSDP( + const WebKit::WebICECandidateDescriptor& candidate) { + webrtc::IceCandidateInterface* native_candidate = + webrtc::CreateIceCandidate(UTF16ToUTF8(candidate.label()), + UTF16ToUTF8(candidate.candidateLine())); + std::string sdp; + if (!native_candidate->ToString(&sdp)) + LOG(ERROR) << "Could not create SDP string"; + return UTF8ToUTF16(sdp); +} + +WebKit::WebString MediaStreamCenter::constructSDP( + const WebKit::WebSessionDescriptionDescriptor& description) { + webrtc::SessionDescriptionInterface* native_desc = + webrtc::CreateSessionDescription(UTF16ToUTF8(description.initialSDP())); + if (!native_desc) + return WebKit::WebString(); + + for (size_t i = 0; i < description.numberOfAddedCandidates(); ++i) { + WebKit::WebICECandidateDescriptor candidate = description.candidate(i); + webrtc::IceCandidateInterface* native_candidate = + webrtc::CreateIceCandidate(UTF16ToUTF8(candidate.label()), + UTF16ToUTF8(candidate.candidateLine())); + native_desc->AddCandidate(native_candidate); + } + + std::string sdp; + if (!native_desc->ToString(&sdp)) + LOG(ERROR) << "Could not create SDP string"; + return UTF8ToUTF16(sdp); +} + } // namespace content diff --git a/content/renderer/media/media_stream_center.h b/content/renderer/media/media_stream_center.h index 11c1fc4..d7020fd 100644 --- a/content/renderer/media/media_stream_center.h +++ b/content/renderer/media/media_stream_center.h @@ -38,6 +38,12 @@ class CONTENT_EXPORT MediaStreamCenter virtual void didConstructMediaStream( const WebKit::WebMediaStreamDescriptor& stream) OVERRIDE; + virtual WebKit::WebString constructSDP( + const WebKit::WebICECandidateDescriptor& candidate) OVERRIDE; + + virtual WebKit::WebString constructSDP( + const WebKit::WebSessionDescriptionDescriptor& description) OVERRIDE; + private: // Weak pointer, owned by WebKit. WebKit::WebMediaStreamCenterClient* client_; diff --git a/content/renderer/media/media_stream_dependency_factory.cc b/content/renderer/media/media_stream_dependency_factory.cc index f245485..2c8d997 100644 --- a/content/renderer/media/media_stream_dependency_factory.cc +++ b/content/renderer/media/media_stream_dependency_factory.cc @@ -137,3 +137,14 @@ MediaStreamDependencyFactory::CreateLocalAudioTrack( webrtc::AudioDeviceModule* audio_device) { return pc_factory_->CreateLocalAudioTrack(label, audio_device); } + +webrtc::SessionDescriptionInterface* +MediaStreamDependencyFactory::CreateSessionDescription(const std::string& sdp) { + return webrtc::CreateSessionDescription(sdp); +} + +webrtc::IceCandidateInterface* MediaStreamDependencyFactory::CreateIceCandidate( + const std::string& label, + const std::string& sdp) { + return webrtc::CreateIceCandidate(label, sdp); +} diff --git a/content/renderer/media/media_stream_dependency_factory.h b/content/renderer/media/media_stream_dependency_factory.h index 9bf0e80..559b8d9 100644 --- a/content/renderer/media/media_stream_dependency_factory.h +++ b/content/renderer/media/media_stream_dependency_factory.h @@ -74,6 +74,12 @@ class CONTENT_EXPORT MediaStreamDependencyFactory { CreateLocalAudioTrack(const std::string& label, webrtc::AudioDeviceModule* audio_device); + virtual webrtc::SessionDescriptionInterface* CreateSessionDescription( + const std::string& sdp); + virtual webrtc::IceCandidateInterface* CreateIceCandidate( + const std::string& label, + const std::string& sdp); + private: talk_base::scoped_refptr<webrtc::PeerConnectionFactoryInterface> pc_factory_; diff --git a/content/renderer/media/media_stream_impl.cc b/content/renderer/media/media_stream_impl.cc index 072def1..4940433 100644 --- a/content/renderer/media/media_stream_impl.cc +++ b/content/renderer/media/media_stream_impl.cc @@ -14,6 +14,7 @@ #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/peer_connection_handler_jsep.h" #include "content/renderer/media/video_capture_impl_manager.h" #include "content/renderer/media/video_capture_module_impl.h" #include "content/renderer/media/webrtc_audio_device_impl.h" @@ -81,15 +82,13 @@ MediaStreamImpl::MediaStreamImpl( p2p_socket_dispatcher_(p2p_socket_dispatcher), network_manager_(NULL), vc_manager_(vc_manager), - peer_connection_handler_(NULL), - message_loop_proxy_(base::MessageLoopProxy::current()), signaling_thread_(NULL), worker_thread_(NULL), chrome_worker_thread_("Chrome_libJingle_WorkerThread") { } MediaStreamImpl::~MediaStreamImpl() { - DCHECK(!peer_connection_handler_); + DCHECK(peer_connection_handlers_.empty()); if (dependency_factory_.get()) dependency_factory_->ReleasePeerConnectionFactory(); if (network_manager_) { @@ -112,30 +111,46 @@ MediaStreamImpl::~MediaStreamImpl() { WebKit::WebPeerConnectionHandler* MediaStreamImpl::CreatePeerConnectionHandler( WebKit::WebPeerConnectionHandlerClient* client) { DCHECK(CalledOnValidThread()); - if (peer_connection_handler_) { - DVLOG(1) << "A PeerConnection already exists"; + if (!EnsurePeerConnectionFactory()) return NULL; - } + + PeerConnectionHandler* pc_handler = new PeerConnectionHandler( + client, + this, + dependency_factory_.get()); + peer_connection_handlers_.push_back(pc_handler); + + return pc_handler; +} + +WebKit::WebPeerConnection00Handler* +MediaStreamImpl::CreatePeerConnectionHandlerJsep( + WebKit::WebPeerConnection00HandlerClient* client) { + DCHECK(CalledOnValidThread()); if (!EnsurePeerConnectionFactory()) return NULL; - peer_connection_handler_ = new PeerConnectionHandler( + PeerConnectionHandlerJsep* pc_handler = new PeerConnectionHandlerJsep( client, this, dependency_factory_.get()); + peer_connection_handlers_.push_back(pc_handler); - return peer_connection_handler_; + return pc_handler; } -void MediaStreamImpl::ClosePeerConnection() { +void MediaStreamImpl::ClosePeerConnection( + PeerConnectionHandlerBase* pc_handler) { DCHECK(CalledOnValidThread()); - 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); + VideoRendererMap::iterator vr_it = video_renderers_.begin(); + while (vr_it != video_renderers_.end()) { + if (vr_it->second.second == pc_handler) { + video_renderers_.erase(vr_it++); + } else { + ++vr_it; + } + } + peer_connection_handlers_.remove(pc_handler); } webrtc::MediaStreamTrackInterface* MediaStreamImpl::GetLocalMediaStreamTrack( @@ -144,8 +159,8 @@ webrtc::MediaStreamTrackInterface* MediaStreamImpl::GetLocalMediaStreamTrack( MediaStreamTrackPtrMap::iterator it = local_tracks_.find(label); if (it == local_tracks_.end()) return NULL; - MediaStreamTrackPtr stream = it->second; - return stream.get(); + MediaStreamTrackPtr track = it->second; + return track.get(); } void MediaStreamImpl::requestUserMedia( @@ -236,27 +251,34 @@ scoped_refptr<media::VideoDecoder> MediaStreamImpl::GetVideoDecoder( capability); } else { // It's a remote stream. - 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( - UTF16ToUTF8(descriptor.label()), - NULL); + std::string desc_label = UTF16ToUTF8(descriptor.label()); + PeerConnectionHandlerBase* pc_handler = NULL; + std::list<PeerConnectionHandlerBase*>::iterator it; + for (it = peer_connection_handlers_.begin(); + it != peer_connection_handlers_.end(); ++it) { + if ((*it)->HasStream(desc_label)) { + pc_handler = *it; + break; } - video_renderer_->SetVideoDecoder(NULL); } + DCHECK(it != peer_connection_handlers_.end()); + // TODO(grunell): We are not informed when a renderer should be deleted. + // When this has been fixed, ensure we delete it. For now, we hold on + // to all renderers until a PeerConnectionHandler is closed or we are + // deleted (then all renderers are deleted), so it sort of leaks. + // TODO(grunell): There is no support for multiple decoders per stream, this + // code will need to be updated when that is supported. + talk_base::scoped_refptr<VideoRendererWrapper> video_renderer = + new talk_base::RefCountedObject<VideoRendererWrapper>(); RTCVideoDecoder* rtc_video_decoder = new RTCVideoDecoder( message_loop_factory->GetMessageLoop("RtcVideoDecoderThread"), url.spec()); decoder = rtc_video_decoder; - video_renderer_->SetVideoDecoder(rtc_video_decoder); - if (peer_connection_handler_) { - peer_connection_handler_->SetVideoRenderer( - UTF16ToUTF8(descriptor.label()), - video_renderer_); - } + video_renderer->SetVideoDecoder(rtc_video_decoder); + pc_handler->SetVideoRenderer(desc_label, video_renderer); + video_renderers_.erase(desc_label); // Remove old renderer if exists. + video_renderers_.insert( + std::make_pair(desc_label, std::make_pair(video_renderer, pc_handler))); } return decoder; } diff --git a/content/renderer/media/media_stream_impl.h b/content/renderer/media/media_stream_impl.h index 7a59582..a9c2db7 100644 --- a/content/renderer/media/media_stream_impl.h +++ b/content/renderer/media/media_stream_impl.h @@ -8,6 +8,7 @@ #include <list> #include <map> #include <string> +#include <utility> #include "base/basictypes.h" #include "base/compiler_specific.h" @@ -42,13 +43,15 @@ class Thread; } namespace WebKit { +class WebPeerConnection00Handler; +class WebPeerConnection00HandlerClient; class WebPeerConnectionHandler; class WebPeerConnectionHandlerClient; } class MediaStreamDispatcher; class MediaStreamDependencyFactory; -class PeerConnectionHandler; +class PeerConnectionHandlerBase; class VideoCaptureImplManager; class RTCVideoDecoder; @@ -74,7 +77,9 @@ class CONTENT_EXPORT MediaStreamImpl virtual WebKit::WebPeerConnectionHandler* CreatePeerConnectionHandler( WebKit::WebPeerConnectionHandlerClient* client); - virtual void ClosePeerConnection(); + virtual WebKit::WebPeerConnection00Handler* CreatePeerConnectionHandlerJsep( + WebKit::WebPeerConnection00HandlerClient* client); + virtual void ClosePeerConnection(PeerConnectionHandlerBase* pc_handler); virtual webrtc::MediaStreamTrackInterface* GetLocalMediaStreamTrack( const std::string& label); @@ -117,6 +122,7 @@ class CONTENT_EXPORT MediaStreamImpl private: FRIEND_TEST_ALL_PREFIXES(MediaStreamImplTest, Basic); + FRIEND_TEST_ALL_PREFIXES(MediaStreamImplTest, MultiplePeerConnections); class VideoRendererWrapper : public webrtc::VideoRendererWrapperInterface { public: @@ -159,10 +165,10 @@ class CONTENT_EXPORT MediaStreamImpl scoped_refptr<VideoCaptureImplManager> vc_manager_; - // 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_; + // peer_connection_handlers_ contains raw references, owned by WebKit. A + // pointer is valid until stop is called on the object (which will call + // ClosePeerConnection on us and we remove the pointer). + std::list<PeerConnectionHandlerBase*> peer_connection_handlers_; // We keep a list of the generated local tracks, so that we can add capture // devices when generated and also use them for recording. @@ -171,8 +177,14 @@ class CONTENT_EXPORT MediaStreamImpl typedef std::map<std::string, MediaStreamTrackPtr> MediaStreamTrackPtrMap; MediaStreamTrackPtrMap local_tracks_; - talk_base::scoped_refptr<VideoRendererWrapper> video_renderer_; - scoped_refptr<base::MessageLoopProxy> message_loop_proxy_; + // Native PeerConnection only supports 1:1 mapping between MediaStream and + // video tag/renderer, so we restrict to this too. The key in + // VideoRendererMap is the stream label. + typedef talk_base::scoped_refptr<VideoRendererWrapper> VideoRendererPtr; + typedef std::pair<VideoRendererPtr, PeerConnectionHandlerBase*> + VideoRendererPair; + typedef std::map<std::string, VideoRendererPair> VideoRendererMap; + VideoRendererMap video_renderers_; // PeerConnection threads. signaling_thread_ is created from the // "current" chrome thread. diff --git a/content/renderer/media/media_stream_impl_unittest.cc b/content/renderer/media/media_stream_impl_unittest.cc index 110c69d..f00aa04 100644 --- a/content/renderer/media/media_stream_impl_unittest.cc +++ b/content/renderer/media/media_stream_impl_unittest.cc @@ -7,10 +7,14 @@ #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_00_handler_client.h" #include "content/renderer/media/mock_web_peer_connection_handler_client.h" +#include "content/renderer/media/peer_connection_handler.h" +#include "content/renderer/media/peer_connection_handler_jsep.h" #include "content/renderer/media/video_capture_impl_manager.h" #include "content/renderer/p2p/socket_dispatcher.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebPeerConnection00Handler.h" #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebPeerConnectionHandler.h" // Disabled due to http://crbug.com/112408 . @@ -38,9 +42,67 @@ TEST(MediaStreamImplTest, DISABLED_Basic) { WebKit::MockWebPeerConnectionHandlerClient client; WebKit::WebPeerConnectionHandler* pc_handler = ms_impl->CreatePeerConnectionHandler(&client); - EXPECT_TRUE(ms_impl->peer_connection_handler_); + EXPECT_EQ(1u, ms_impl->peer_connection_handlers_.size()); - ms_impl->ClosePeerConnection(); - EXPECT_FALSE(ms_impl->peer_connection_handler_); + // Delete PC handler explicitly after closing to mimic WebKit behavior. + ms_impl->ClosePeerConnection( + static_cast<PeerConnectionHandler*>(pc_handler)); + EXPECT_TRUE(ms_impl->peer_connection_handlers_.empty()); delete pc_handler; + + WebKit::MockWebPeerConnection00HandlerClient client_jsep; + WebKit::WebPeerConnection00Handler* pc_handler_jsep = + ms_impl->CreatePeerConnectionHandlerJsep(&client_jsep); + EXPECT_EQ(1u, ms_impl->peer_connection_handlers_.size()); + + // Delete PC handler explicitly after closing to mimic WebKit behavior. + ms_impl->ClosePeerConnection( + static_cast<PeerConnectionHandlerJsep*>(pc_handler_jsep)); + EXPECT_TRUE(ms_impl->peer_connection_handlers_.empty()); + delete pc_handler_jsep; +} + +// Disabled due to http://crbug.com/112408 . +TEST(MediaStreamImplTest, DISABLED_MultiplePeerConnections) { + 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; + WebKit::WebPeerConnectionHandler* pc_handler = + ms_impl->CreatePeerConnectionHandler(&client); + EXPECT_EQ(1u, ms_impl->peer_connection_handlers_.size()); + + WebKit::MockWebPeerConnection00HandlerClient client_jsep; + WebKit::WebPeerConnection00Handler* pc_handler_jsep = + ms_impl->CreatePeerConnectionHandlerJsep(&client_jsep); + EXPECT_EQ(2u, ms_impl->peer_connection_handlers_.size()); + + // Delete PC handler explicitly after closing to mimic WebKit behavior. + ms_impl->ClosePeerConnection( + static_cast<PeerConnectionHandler*>(pc_handler)); + EXPECT_EQ(1u, ms_impl->peer_connection_handlers_.size()); + delete pc_handler; + + // Delete PC handler explicitly after closing to mimic WebKit behavior. + ms_impl->ClosePeerConnection( + static_cast<PeerConnectionHandlerJsep*>(pc_handler_jsep)); + EXPECT_TRUE(ms_impl->peer_connection_handlers_.empty()); + delete pc_handler_jsep; } diff --git a/content/renderer/media/mock_media_stream_dependency_factory.cc b/content/renderer/media/mock_media_stream_dependency_factory.cc index 2d76882..7d54d11 100644 --- a/content/renderer/media/mock_media_stream_dependency_factory.cc +++ b/content/renderer/media/mock_media_stream_dependency_factory.cc @@ -123,6 +123,65 @@ void MockLocalVideoTrack::UnregisterObserver(ObserverInterface* observer) { NOTIMPLEMENTED(); } +class MockSessionDescription : public SessionDescriptionInterface { + public: + MockSessionDescription(const std::string& sdp) + : sdp_(sdp) { + } + virtual ~MockSessionDescription() {} + virtual const cricket::SessionDescription* description() const OVERRIDE { + NOTIMPLEMENTED(); + return NULL; + } + virtual bool AddCandidate(const IceCandidateInterface* candidate) OVERRIDE { + NOTIMPLEMENTED(); + return false; + } + virtual size_t number_of_mediasections() const OVERRIDE { + NOTIMPLEMENTED(); + return 0; + } + virtual const IceCandidateColletion* candidates( + size_t mediasection_index) const OVERRIDE { + NOTIMPLEMENTED(); + return NULL; + } + virtual bool ToString(std::string* out) const OVERRIDE { + *out = sdp_; + return true; + } + + private: + std::string sdp_; +}; + +class MockIceCandidate : public IceCandidateInterface { + public: + MockIceCandidate(const std::string& label, const std::string& sdp) + : label_(label), + sdp_(sdp) { + } + virtual ~MockIceCandidate() {} + virtual std::string label() const OVERRIDE { + return label_; + } + virtual const cricket::Candidate& candidate() const OVERRIDE { + // This function should never be called. It will intentionally crash. The + // base class forces us to return a reference. + NOTREACHED(); + cricket::Candidate* candidate = NULL; + return *candidate; + } + virtual bool ToString(std::string* out) const OVERRIDE { + *out = sdp_; + return true; + } + + private: + std::string label_; + std::string sdp_; +}; + } // namespace webrtc MockMediaStreamDependencyFactory::MockMediaStreamDependencyFactory() @@ -154,7 +213,7 @@ MockMediaStreamDependencyFactory::CreatePeerConnection( const std::string& config, webrtc::PeerConnectionObserver* observer) { DCHECK(mock_pc_factory_created_); - return new talk_base::RefCountedObject<webrtc::MockPeerConnectionImpl>(); + return new talk_base::RefCountedObject<webrtc::MockPeerConnectionImpl>(this); } talk_base::scoped_refptr<webrtc::PeerConnectionInterface> @@ -162,7 +221,7 @@ MockMediaStreamDependencyFactory::CreateRoapPeerConnection( const std::string& config, webrtc::PeerConnectionObserver* observer) { DCHECK(mock_pc_factory_created_); - return new talk_base::RefCountedObject<webrtc::MockPeerConnectionImpl>(); + return new talk_base::RefCountedObject<webrtc::MockPeerConnectionImpl>(this); } talk_base::scoped_refptr<webrtc::LocalMediaStreamInterface> @@ -189,3 +248,16 @@ MockMediaStreamDependencyFactory::CreateLocalAudioTrack( NOTIMPLEMENTED(); return NULL; } + +webrtc::SessionDescriptionInterface* +MockMediaStreamDependencyFactory::CreateSessionDescription( + const std::string& sdp) { + return new webrtc::MockSessionDescription(sdp); +} + +webrtc::IceCandidateInterface* +MockMediaStreamDependencyFactory::CreateIceCandidate( + const std::string& label, + const std::string& sdp) { + return new webrtc::MockIceCandidate(label, sdp); +} diff --git a/content/renderer/media/mock_media_stream_dependency_factory.h b/content/renderer/media/mock_media_stream_dependency_factory.h index f482ce5..2a8737a 100644 --- a/content/renderer/media/mock_media_stream_dependency_factory.h +++ b/content/renderer/media/mock_media_stream_dependency_factory.h @@ -77,6 +77,11 @@ class MockMediaStreamDependencyFactory : public MediaStreamDependencyFactory { CreateLocalAudioTrack( const std::string& label, webrtc::AudioDeviceModule* audio_device) OVERRIDE; + virtual webrtc::SessionDescriptionInterface* CreateSessionDescription( + const std::string& sdp) OVERRIDE; + virtual webrtc::IceCandidateInterface* CreateIceCandidate( + const std::string& label, + const std::string& sdp) OVERRIDE; private: bool mock_pc_factory_created_; diff --git a/content/renderer/media/mock_media_stream_impl.cc b/content/renderer/media/mock_media_stream_impl.cc index 4996e95..8ce04de 100644 --- a/content/renderer/media/mock_media_stream_impl.cc +++ b/content/renderer/media/mock_media_stream_impl.cc @@ -21,7 +21,8 @@ MockMediaStreamImpl::CreatePeerConnectionHandler( return NULL; } -void MockMediaStreamImpl::ClosePeerConnection() { +void MockMediaStreamImpl::ClosePeerConnection( + PeerConnectionHandlerBase* pc_handler) { } webrtc::MediaStreamTrackInterface* diff --git a/content/renderer/media/mock_media_stream_impl.h b/content/renderer/media/mock_media_stream_impl.h index 8340b9e..f751ed7 100644 --- a/content/renderer/media/mock_media_stream_impl.h +++ b/content/renderer/media/mock_media_stream_impl.h @@ -17,7 +17,8 @@ class MockMediaStreamImpl : public MediaStreamImpl { virtual WebKit::WebPeerConnectionHandler* CreatePeerConnectionHandler( WebKit::WebPeerConnectionHandlerClient* client) OVERRIDE; - virtual void ClosePeerConnection() OVERRIDE; + virtual void ClosePeerConnection( + PeerConnectionHandlerBase* pc_handler) OVERRIDE; virtual webrtc::MediaStreamTrackInterface* GetLocalMediaStreamTrack( const std::string& label) OVERRIDE; diff --git a/content/renderer/media/mock_peer_connection_impl.cc b/content/renderer/media/mock_peer_connection_impl.cc index ccfae44..7f68eae 100644 --- a/content/renderer/media/mock_peer_connection_impl.cc +++ b/content/renderer/media/mock_peer_connection_impl.cc @@ -2,6 +2,7 @@ // 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_dependency_factory.h" #include "content/renderer/media/mock_peer_connection_impl.h" #include <vector> @@ -36,10 +37,19 @@ class MockStreamCollection : public StreamCollectionInterface { std::vector<talk_base::scoped_refptr<MediaStreamInterface> > streams_; }; -MockPeerConnectionImpl::MockPeerConnectionImpl() - : stream_changes_committed_(false), +const char MockPeerConnectionImpl::kDummyOffer[] = "dummy offer"; + +MockPeerConnectionImpl::MockPeerConnectionImpl( + MockMediaStreamDependencyFactory* factory) + : dependency_factory_(factory), + stream_changes_committed_(false), local_streams_(new talk_base::RefCountedObject<MockStreamCollection>), - remote_streams_(new talk_base::RefCountedObject<MockStreamCollection>) { + remote_streams_(new talk_base::RefCountedObject<MockStreamCollection>), + hint_audio_(false), + hint_video_(false), + action_(kAnswer), + ice_options_(kOnlyRelay), + ready_state_(kNew) { } MockPeerConnectionImpl::~MockPeerConnectionImpl() {} @@ -85,8 +95,7 @@ void MockPeerConnectionImpl::Close() { } MockPeerConnectionImpl::ReadyState MockPeerConnectionImpl::ready_state() { - NOTIMPLEMENTED(); - return kNew; + return ready_state_; } MockPeerConnectionImpl::SdpState MockPeerConnectionImpl::sdp_state() { @@ -95,53 +104,54 @@ MockPeerConnectionImpl::SdpState MockPeerConnectionImpl::sdp_state() { } bool MockPeerConnectionImpl::StartIce(IceOptions options) { - NOTIMPLEMENTED(); - return false; + ice_options_ = options; + return true; } webrtc::SessionDescriptionInterface* MockPeerConnectionImpl::CreateOffer( const webrtc::MediaHints& hints) { - NOTIMPLEMENTED(); - return NULL; + hint_audio_ = hints.has_audio(); + hint_video_ = hints.has_video(); + return dependency_factory_->CreateSessionDescription(kDummyOffer); } webrtc::SessionDescriptionInterface* MockPeerConnectionImpl::CreateAnswer( const webrtc::MediaHints& hints, const webrtc::SessionDescriptionInterface* offer) { - NOTIMPLEMENTED(); - return NULL; + hint_audio_ = hints.has_audio(); + hint_video_ = hints.has_video(); + offer->ToString(&description_sdp_); + return dependency_factory_->CreateSessionDescription(description_sdp_); } bool MockPeerConnectionImpl::SetLocalDescription( Action action, webrtc::SessionDescriptionInterface* desc) { - NOTIMPLEMENTED(); - return false; + action_ = action; + return desc->ToString(&description_sdp_); } bool MockPeerConnectionImpl::SetRemoteDescription( Action action, webrtc::SessionDescriptionInterface* desc) { - NOTIMPLEMENTED(); - return false; + action_ = action; + return desc->ToString(&description_sdp_); } bool MockPeerConnectionImpl::ProcessIceMessage( const webrtc::IceCandidateInterface* ice_candidate) { - NOTIMPLEMENTED(); - return false; + ice_label_ = ice_candidate->label(); + return ice_candidate->ToString(&ice_sdp_); } const webrtc::SessionDescriptionInterface* MockPeerConnectionImpl::local_description() const { - NOTIMPLEMENTED(); - return NULL; + return dependency_factory_->CreateSessionDescription(description_sdp_); } const webrtc::SessionDescriptionInterface* MockPeerConnectionImpl::remote_description() const { - NOTIMPLEMENTED(); - return NULL; + return dependency_factory_->CreateSessionDescription(description_sdp_); } void MockPeerConnectionImpl::AddRemoteStream(MediaStreamInterface* stream) { diff --git a/content/renderer/media/mock_peer_connection_impl.h b/content/renderer/media/mock_peer_connection_impl.h index 2993ce4..60afd6c 100644 --- a/content/renderer/media/mock_peer_connection_impl.h +++ b/content/renderer/media/mock_peer_connection_impl.h @@ -11,13 +11,15 @@ #include "base/compiler_specific.h" #include "third_party/libjingle/source/talk/app/webrtc/peerconnection.h" +class MockMediaStreamDependencyFactory; + namespace webrtc { class MockStreamCollection; class MockPeerConnectionImpl : public PeerConnectionInterface { public: - MockPeerConnectionImpl(); + MockPeerConnectionImpl(MockMediaStreamDependencyFactory* factory); // PeerConnectionInterface implementation. virtual void ProcessSignalingMessage(const std::string& msg) OVERRIDE; @@ -54,20 +56,41 @@ class MockPeerConnectionImpl : public PeerConnectionInterface { void AddRemoteStream(MediaStreamInterface* stream); void ClearStreamChangesCommitted() { stream_changes_committed_ = false; } + void SetReadyState(ReadyState state) { ready_state_ = state; } const std::string& signaling_message() const { return signaling_message_; } const std::string& stream_label() const { return stream_label_; } bool stream_changes_committed() const { return stream_changes_committed_; } + bool hint_audio() const { return hint_audio_; } + bool hint_video() const { return hint_video_; } + Action action() const { return action_; } + const std::string& description_sdp() const { return description_sdp_; } + IceOptions ice_options() const { return ice_options_; } + const std::string& ice_label() const { return ice_label_; } + const std::string& ice_sdp() const { return ice_sdp_; } + + static const char kDummyOffer[]; protected: virtual ~MockPeerConnectionImpl(); private: + // Used for creating MockSessionDescription. + MockMediaStreamDependencyFactory* dependency_factory_; + std::string signaling_message_; std::string stream_label_; bool stream_changes_committed_; talk_base::scoped_refptr<MockStreamCollection> local_streams_; talk_base::scoped_refptr<MockStreamCollection> remote_streams_; + bool hint_audio_; + bool hint_video_; + Action action_; + std::string description_sdp_; + IceOptions ice_options_; + std::string ice_label_; + std::string ice_sdp_; + ReadyState ready_state_; DISALLOW_COPY_AND_ASSIGN(MockPeerConnectionImpl); }; diff --git a/content/renderer/media/mock_web_peer_connection_00_handler_client.cc b/content/renderer/media/mock_web_peer_connection_00_handler_client.cc new file mode 100644 index 0000000..770e3fb7 --- /dev/null +++ b/content/renderer/media/mock_web_peer_connection_00_handler_client.cc @@ -0,0 +1,56 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/logging.h" +#include "base/utf_string_conversions.h" +#include "content/renderer/media/mock_web_peer_connection_00_handler_client.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebICECandidateDescriptor.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebMediaStreamDescriptor.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h" + +namespace WebKit { + +MockWebPeerConnection00HandlerClient:: +MockWebPeerConnection00HandlerClient() + : ready_state_(ReadyStateNew), + more_to_follow_(false) { +} + +MockWebPeerConnection00HandlerClient:: +~MockWebPeerConnection00HandlerClient() {} + +void MockWebPeerConnection00HandlerClient::didGenerateICECandidate( + const WebICECandidateDescriptor& candidate, + bool more_to_follow) { + if (candidate.isNull()) { + candidate_label_.clear(); + candidate_sdp_.clear(); + } else { + candidate_label_ = UTF16ToUTF8(candidate.label()); + candidate_sdp_ = UTF16ToUTF8(candidate.candidateLine()); + } + more_to_follow_ = more_to_follow; +} + +void MockWebPeerConnection00HandlerClient::didChangeReadyState( + ReadyState state) { + ready_state_ = state; +} + +void MockWebPeerConnection00HandlerClient::didChangeICEState(ICEState state) { + NOTIMPLEMENTED(); +} + +void MockWebPeerConnection00HandlerClient::didAddRemoteStream( + const WebMediaStreamDescriptor& stream_descriptor) { + stream_label_ = UTF16ToUTF8(stream_descriptor.label()); +} + +void MockWebPeerConnection00HandlerClient::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_00_handler_client.h b/content/renderer/media/mock_web_peer_connection_00_handler_client.h new file mode 100644 index 0000000..7f3de5c --- /dev/null +++ b/content/renderer/media/mock_web_peer_connection_00_handler_client.h @@ -0,0 +1,51 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_RENDERER_MEDIA_MOCK_WEB_JSEP_PEER_CONNECTION_HANDLER_CLIENT_H_ +#define CONTENT_RENDERER_MEDIA_MOCK_WEB_JSEP_PEER_CONNECTION_HANDLER_CLIENT_H_ + +#include <string> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebPeerConnection00HandlerClient.h" + +namespace WebKit { + +class MockWebPeerConnection00HandlerClient + : public WebPeerConnection00HandlerClient { + public: + MockWebPeerConnection00HandlerClient(); + virtual ~MockWebPeerConnection00HandlerClient(); + + // WebPeerConnection00HandlerClient implementation. + virtual void didGenerateICECandidate( + const WebICECandidateDescriptor& candidate, + bool more_to_follow) OVERRIDE; + virtual void didChangeReadyState(ReadyState state) OVERRIDE; + virtual void didChangeICEState(ICEState state) OVERRIDE; + virtual void didAddRemoteStream( + const WebMediaStreamDescriptor& stream_descriptor) OVERRIDE; + virtual void didRemoveRemoteStream( + const WebMediaStreamDescriptor& stream_descriptor) OVERRIDE; + + const std::string& stream_label() const { return stream_label_; } + ReadyState ready_state() const { return ready_state_; } + const std::string& candidate_label() const { return candidate_label_; } + const std::string& candidate_sdp() const { return candidate_sdp_; } + bool more_to_follow() const { return more_to_follow_; } + + private: + std::string stream_label_; + ReadyState ready_state_; + std::string candidate_label_; + std::string candidate_sdp_; + bool more_to_follow_; + + DISALLOW_COPY_AND_ASSIGN(MockWebPeerConnection00HandlerClient); +}; + +} // namespace WebKit + +#endif // CONTENT_RENDERER_MEDIA_MOCK_WEB_JSEP_PEER_CONNECTION_HANDLER_CLIENT_H_ diff --git a/content/renderer/media/peer_connection_handler.cc b/content/renderer/media/peer_connection_handler.cc index 74acf2e..747e95f 100644 --- a/content/renderer/media/peer_connection_handler.cc +++ b/content/renderer/media/peer_connection_handler.cc @@ -22,32 +22,13 @@ PeerConnectionHandler::PeerConnectionHandler( WebKit::WebPeerConnectionHandlerClient* client, MediaStreamImpl* msi, MediaStreamDependencyFactory* dependency_factory) - : client_(client), - media_stream_impl_(msi), - dependency_factory_(dependency_factory), - message_loop_proxy_(base::MessageLoopProxy::current()) { + : PeerConnectionHandlerBase(msi, dependency_factory), + client_(client) { } PeerConnectionHandler::~PeerConnectionHandler() { } -void PeerConnectionHandler::SetVideoRenderer( - const std::string& stream_label, - webrtc::VideoRendererWrapperInterface* renderer) { - webrtc::MediaStreamInterface* stream = - native_peer_connection_->remote_streams()->find(stream_label); - webrtc::VideoTracks* video_tracks = stream->video_tracks(); - // We assume there is only one enabled video track. - for (size_t i = 0; i < video_tracks->count(); ++i) { - webrtc::VideoTrackInterface* video_track = video_tracks->at(i); - if (video_track->enabled()) { - video_track->SetRenderer(renderer); - return; - } - } - DVLOG(1) << "No enabled video track."; -} - void PeerConnectionHandler::initialize( const WebKit::WebString& server_configuration, const WebKit::WebString& username) { @@ -60,7 +41,8 @@ void PeerConnectionHandler::initialize( void PeerConnectionHandler::produceInitialOffer( const WebKit::WebVector<WebKit::WebMediaStreamDescriptor>& pending_add_streams) { - AddStreams(pending_add_streams); + for (size_t i = 0; i < pending_add_streams.size(); ++i) + AddStream(pending_add_streams[i]); native_peer_connection_->CommitStreamChanges(); } @@ -77,8 +59,10 @@ void PeerConnectionHandler::processPendingStreams( pending_add_streams, const WebKit::WebVector<WebKit::WebMediaStreamDescriptor>& pending_remove_streams) { - AddStreams(pending_add_streams); - RemoveStreams(pending_remove_streams); + for (size_t i = 0; i < pending_add_streams.size(); ++i) + AddStream(pending_add_streams[i]); + for (size_t i = 0; i < pending_remove_streams.size(); ++i) + RemoveStream(pending_remove_streams[i]); native_peer_connection_->CommitStreamChanges(); } @@ -94,7 +78,7 @@ void PeerConnectionHandler::stop() { // close. We need to investigate further. Not calling Close() on native // PeerConnection is OK for now. native_peer_connection_ = NULL; - media_stream_impl_->ClosePeerConnection(); + media_stream_impl_->ClosePeerConnection(this); } void PeerConnectionHandler::OnError() { @@ -154,54 +138,13 @@ void PeerConnectionHandler::OnRemoveStream( void PeerConnectionHandler::OnIceCandidate( const webrtc::IceCandidateInterface* candidate) { - // TODO(grunell): Implement. - NOTIMPLEMENTED(); + // Not used by ROAP PeerConnection. + NOTREACHED(); } void PeerConnectionHandler::OnIceComplete() { - // TODO(grunell): Implement. - NOTIMPLEMENTED(); -} - -void PeerConnectionHandler::AddStreams( - const WebKit::WebVector<WebKit::WebMediaStreamDescriptor>& streams) { - for (size_t i = 0; i < streams.size(); ++i) { - talk_base::scoped_refptr<webrtc::LocalMediaStreamInterface> stream = - dependency_factory_->CreateLocalMediaStream( - UTF16ToUTF8(streams[i].label())); - WebKit::WebVector<WebKit::WebMediaStreamSource> source_vector; - streams[i].sources(source_vector); - - // Get and add all tracks. - for (size_t j = 0; j < source_vector.size(); ++j) { - webrtc::MediaStreamTrackInterface* track = - media_stream_impl_->GetLocalMediaStreamTrack( - UTF16ToUTF8(source_vector[j].id())); - DCHECK(track); - if (source_vector[j].type() == WebKit::WebMediaStreamSource::TypeVideo) { - stream->AddTrack(static_cast<webrtc::VideoTrackInterface*>(track)); - } else { - stream->AddTrack(static_cast<webrtc::AudioTrackInterface*>(track)); - } - } - - native_peer_connection_->AddStream(stream); - } -} - -void PeerConnectionHandler::RemoveStreams( - const WebKit::WebVector<WebKit::WebMediaStreamDescriptor>& streams) { - talk_base::scoped_refptr<webrtc::StreamCollectionInterface> native_streams = - native_peer_connection_->local_streams(); - // TODO(perkj): Change libJingle PeerConnection::RemoveStream API to take a - // label as input instead of stream and return bool. - for (size_t i = 0; i < streams.size() && native_streams != NULL; ++i) { - webrtc::LocalMediaStreamInterface* stream = - static_cast<webrtc::LocalMediaStreamInterface*>(native_streams->find( - UTF16ToUTF8(streams[i].label()))); - DCHECK(stream); - native_peer_connection_->RemoveStream(stream); - } + // Not used by ROAP PeerConnection. + NOTREACHED(); } void PeerConnectionHandler::OnAddStreamCallback( @@ -227,40 +170,3 @@ void PeerConnectionHandler::OnRemoveStreamCallback( remote_streams_.erase(it); client_->didRemoveRemoteStream(descriptor); } - -WebKit::WebMediaStreamDescriptor -PeerConnectionHandler::CreateWebKitStreamDescriptor( - webrtc::MediaStreamInterface* stream) { - webrtc::AudioTracks* audio_tracks = stream->audio_tracks(); - webrtc::VideoTracks* video_tracks = stream->video_tracks(); - WebKit::WebVector<WebKit::WebMediaStreamSource> source_vector( - audio_tracks->count() + video_tracks->count()); - - // Add audio tracks. - size_t i = 0; - for (; i < audio_tracks->count(); ++i) { - webrtc::AudioTrackInterface* audio_track = audio_tracks->at(i); - DCHECK(audio_track); - source_vector[i].initialize( - // TODO(grunell): Set id to something unique. - UTF8ToUTF16(audio_track->label()), - WebKit::WebMediaStreamSource::TypeAudio, - UTF8ToUTF16(audio_track->label())); - } - - // Add video tracks. - for (i = 0; i < video_tracks->count(); ++i) { - webrtc::VideoTrackInterface* video_track = video_tracks->at(i); - DCHECK(video_track); - source_vector[audio_tracks->count() + i].initialize( - // TODO(grunell): Set id to something unique. - UTF8ToUTF16(video_track->label()), - WebKit::WebMediaStreamSource::TypeVideo, - UTF8ToUTF16(video_track->label())); - } - - WebKit::WebMediaStreamDescriptor descriptor; - descriptor.initialize(UTF8ToUTF16(stream->label()), source_vector); - - return descriptor; -} diff --git a/content/renderer/media/peer_connection_handler.h b/content/renderer/media/peer_connection_handler.h index aa969a1..e9a50e8 100644 --- a/content/renderer/media/peer_connection_handler.h +++ b/content/renderer/media/peer_connection_handler.h @@ -5,28 +5,21 @@ #ifndef CONTENT_RENDERER_MEDIA_PEER_CONNECTION_HANDLER_H_ #define CONTENT_RENDERER_MEDIA_PEER_CONNECTION_HANDLER_H_ -#include <map> #include <string> #include "base/basictypes.h" #include "base/compiler_specific.h" #include "base/gtest_prod_util.h" -#include "base/memory/scoped_ptr.h" -#include "base/message_loop_proxy.h" #include "content/common/content_export.h" -#include "third_party/libjingle/source/talk/app/webrtc/mediastream.h" -#include "third_party/libjingle/source/talk/app/webrtc/peerconnection.h" +#include "content/renderer/media/peer_connection_handler_base.h" #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebPeerConnectionHandler.h" -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 -// WebKit. +// PeerConnectionHandler is a delegate for the ROAP PeerConnection API messages +// going between WebKit and native PeerConnection in libjingle. It's owned by +// WebKit. ROAP PeerConnection will be removed soon. class CONTENT_EXPORT PeerConnectionHandler - : NON_EXPORTED_BASE(public WebKit::WebPeerConnectionHandler), - NON_EXPORTED_BASE(public webrtc::PeerConnectionObserver) { + : public PeerConnectionHandlerBase, + NON_EXPORTED_BASE(public WebKit::WebPeerConnectionHandler) { public: PeerConnectionHandler( WebKit::WebPeerConnectionHandlerClient* client, @@ -34,11 +27,6 @@ class CONTENT_EXPORT PeerConnectionHandler MediaStreamDependencyFactory* dependency_factory); virtual ~PeerConnectionHandler(); - // Set the video renderer for the specified stream. - virtual void SetVideoRenderer( - const std::string& stream_label, - webrtc::VideoRendererWrapperInterface* renderer); - // WebKit::WebPeerConnectionHandler implementation virtual void initialize( const WebKit::WebString& server_configuration, @@ -71,37 +59,12 @@ class CONTENT_EXPORT PeerConnectionHandler private: FRIEND_TEST_ALL_PREFIXES(PeerConnectionHandlerTest, Basic); - void AddStreams( - const WebKit::WebVector<WebKit::WebMediaStreamDescriptor>& streams); - void RemoveStreams( - const WebKit::WebVector<WebKit::WebMediaStreamDescriptor>& streams); void OnAddStreamCallback(webrtc::MediaStreamInterface* stream); void OnRemoveStreamCallback(webrtc::MediaStreamInterface* stream); - WebKit::WebMediaStreamDescriptor CreateWebKitStreamDescriptor( - webrtc::MediaStreamInterface* stream); // 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. Calls to it must be done on the render thread. - 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. - talk_base::scoped_refptr<webrtc::PeerConnectionInterface> - native_peer_connection_; - - typedef std::map<webrtc::MediaStreamInterface*, - WebKit::WebMediaStreamDescriptor> RemoteStreamMap; - RemoteStreamMap remote_streams_; - - scoped_refptr<base::MessageLoopProxy> message_loop_proxy_; - DISALLOW_COPY_AND_ASSIGN(PeerConnectionHandler); }; diff --git a/content/renderer/media/peer_connection_handler_base.cc b/content/renderer/media/peer_connection_handler_base.cc new file mode 100644 index 0000000..cf82ffe --- /dev/null +++ b/content/renderer/media/peer_connection_handler_base.cc @@ -0,0 +1,123 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/renderer/media/peer_connection_handler_base.h" + +#include "base/logging.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/WebKit/Source/WebKit/chromium/public/platform/WebMediaStreamDescriptor.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebMediaStreamSource.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h" + +PeerConnectionHandlerBase::PeerConnectionHandlerBase( + MediaStreamImpl* msi, + MediaStreamDependencyFactory* dependency_factory) + : media_stream_impl_(msi), + dependency_factory_(dependency_factory), + message_loop_proxy_(base::MessageLoopProxy::current()) { +} + +PeerConnectionHandlerBase::~PeerConnectionHandlerBase() { +} + +bool PeerConnectionHandlerBase::HasStream(const std::string& stream_label) { + webrtc::MediaStreamInterface* stream = + native_peer_connection_->remote_streams()->find(stream_label); + return stream != NULL; +} + +void PeerConnectionHandlerBase::SetVideoRenderer( + const std::string& stream_label, + webrtc::VideoRendererWrapperInterface* renderer) { + webrtc::MediaStreamInterface* stream = + native_peer_connection_->remote_streams()->find(stream_label); + webrtc::VideoTracks* video_tracks = stream->video_tracks(); + // We assume there is only one enabled video track. + for (size_t i = 0; i < video_tracks->count(); ++i) { + webrtc::VideoTrackInterface* video_track = video_tracks->at(i); + if (video_track->enabled()) { + video_track->SetRenderer(renderer); + return; + } + } + DVLOG(1) << "No enabled video track."; +} + +void PeerConnectionHandlerBase::AddStream( + const WebKit::WebMediaStreamDescriptor& stream) { + talk_base::scoped_refptr<webrtc::LocalMediaStreamInterface> native_stream = + dependency_factory_->CreateLocalMediaStream(UTF16ToUTF8(stream.label())); + WebKit::WebVector<WebKit::WebMediaStreamSource> source_vector; + stream.sources(source_vector); + + // Get and add all tracks. + for (size_t i = 0; i < source_vector.size(); ++i) { + webrtc::MediaStreamTrackInterface* track = media_stream_impl_ + ->GetLocalMediaStreamTrack(UTF16ToUTF8(source_vector[i].id())); + DCHECK(track); + if (source_vector[i].type() == WebKit::WebMediaStreamSource::TypeVideo) { + native_stream->AddTrack(static_cast<webrtc::VideoTrackInterface*>(track)); + } else { + DCHECK(source_vector[i].type() == + WebKit::WebMediaStreamSource::TypeAudio); + native_stream->AddTrack(static_cast<webrtc::AudioTrackInterface*>(track)); + } + } + + native_peer_connection_->AddStream(native_stream); +} + +void PeerConnectionHandlerBase::RemoveStream( + const WebKit::WebMediaStreamDescriptor& stream) { + talk_base::scoped_refptr<webrtc::StreamCollectionInterface> native_streams = + native_peer_connection_->local_streams(); + if (!native_streams) + return; + // TODO(perkj): Change libJingle PeerConnection::RemoveStream API to take a + // label as input instead of stream and return bool. + webrtc::LocalMediaStreamInterface* native_stream = + static_cast<webrtc::LocalMediaStreamInterface*>(native_streams->find( + UTF16ToUTF8(stream.label()))); + DCHECK(native_stream); + native_peer_connection_->RemoveStream(native_stream); +} + +WebKit::WebMediaStreamDescriptor +PeerConnectionHandlerBase::CreateWebKitStreamDescriptor( + webrtc::MediaStreamInterface* stream) { + webrtc::AudioTracks* audio_tracks = stream->audio_tracks(); + webrtc::VideoTracks* video_tracks = stream->video_tracks(); + WebKit::WebVector<WebKit::WebMediaStreamSource> source_vector( + audio_tracks->count() + video_tracks->count()); + + // Add audio tracks. + size_t i = 0; + for (; i < audio_tracks->count(); ++i) { + webrtc::AudioTrackInterface* audio_track = audio_tracks->at(i); + DCHECK(audio_track); + source_vector[i].initialize( + // TODO(grunell): Set id to something unique. + UTF8ToUTF16(audio_track->label()), + WebKit::WebMediaStreamSource::TypeAudio, + UTF8ToUTF16(audio_track->label())); + } + + // Add video tracks. + for (i = 0; i < video_tracks->count(); ++i) { + webrtc::VideoTrackInterface* video_track = video_tracks->at(i); + DCHECK(video_track); + source_vector[audio_tracks->count() + i].initialize( + // TODO(grunell): Set id to something unique. + UTF8ToUTF16(video_track->label()), + WebKit::WebMediaStreamSource::TypeVideo, + UTF8ToUTF16(video_track->label())); + } + + WebKit::WebMediaStreamDescriptor descriptor; + descriptor.initialize(UTF8ToUTF16(stream->label()), source_vector); + + return descriptor; +} diff --git a/content/renderer/media/peer_connection_handler_base.h b/content/renderer/media/peer_connection_handler_base.h new file mode 100644 index 0000000..4ae027f --- /dev/null +++ b/content/renderer/media/peer_connection_handler_base.h @@ -0,0 +1,71 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_RENDERER_MEDIA_PEER_CONNECTION_HANDLER_BASE_H_ +#define CONTENT_RENDERER_MEDIA_PEER_CONNECTION_HANDLER_BASE_H_ + +#include <map> +#include <string> + +#include "base/memory/scoped_ptr.h" +#include "base/message_loop_proxy.h" +#include "content/common/content_export.h" +#include "third_party/libjingle/source/talk/app/webrtc/mediastream.h" +#include "third_party/libjingle/source/talk/app/webrtc/peerconnection.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebMediaStreamDescriptor.h" + +class MediaStreamDependencyFactory; +class MediaStreamImpl; + +// PeerConnectionHandlerBase is the base class of a delegate for the +// PeerConnection (JSEP or ROAP) API messages going between WebKit and native +// PeerConnection in libjingle. ROAP PeerConnection will be removed soon. +class CONTENT_EXPORT PeerConnectionHandlerBase + : NON_EXPORTED_BASE(public webrtc::PeerConnectionObserver) { + public: + PeerConnectionHandlerBase( + MediaStreamImpl* msi, + MediaStreamDependencyFactory* dependency_factory); + + // Checks if a remote stream belongs to this PeerConnection. + virtual bool HasStream(const std::string& stream_label); + + // Set the video renderer for the specified stream. + virtual void SetVideoRenderer( + const std::string& stream_label, + webrtc::VideoRendererWrapperInterface* renderer); + + protected: + virtual ~PeerConnectionHandlerBase(); + + void AddStream(const WebKit::WebMediaStreamDescriptor& stream); + void RemoveStream(const WebKit::WebMediaStreamDescriptor& stream); + WebKit::WebMediaStreamDescriptor CreateWebKitStreamDescriptor( + webrtc::MediaStreamInterface* stream); + + // media_stream_impl_ is a raw pointer, and is valid for the lifetime of this + // class. Calls to it must be done on the render thread. + MediaStreamImpl* media_stream_impl_; + + // dependency_factory_ is a raw 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. + talk_base::scoped_refptr<webrtc::PeerConnectionInterface> + native_peer_connection_; + + typedef std::map<webrtc::MediaStreamInterface*, + WebKit::WebMediaStreamDescriptor> RemoteStreamMap; + RemoteStreamMap remote_streams_; + + // The message loop we are created on and on which to make calls to WebKit. + // This should be the render thread message loop. + scoped_refptr<base::MessageLoopProxy> message_loop_proxy_; + + DISALLOW_COPY_AND_ASSIGN(PeerConnectionHandlerBase); +}; + +#endif // CONTENT_RENDERER_MEDIA_PEER_CONNECTION_HANDLER_BASE_H_ diff --git a/content/renderer/media/peer_connection_handler_jsep.cc b/content/renderer/media/peer_connection_handler_jsep.cc new file mode 100644 index 0000000..b524abe --- /dev/null +++ b/content/renderer/media/peer_connection_handler_jsep.cc @@ -0,0 +1,368 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/renderer/media/peer_connection_handler_jsep.h" + +#include <utility> +#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/WebKit/Source/WebKit/chromium/public/platform/WebICECandidateDescriptor.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebICEOptions.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebPeerConnection00HandlerClient.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebMediaHints.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebMediaStreamDescriptor.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebMediaStreamSource.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebSessionDescriptionDescriptor.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebVector.h" + +PeerConnectionHandlerJsep::PeerConnectionHandlerJsep( + WebKit::WebPeerConnection00HandlerClient* client, + MediaStreamImpl* msi, + MediaStreamDependencyFactory* dependency_factory) + : PeerConnectionHandlerBase(msi, dependency_factory), + client_(client) { +} + +PeerConnectionHandlerJsep::~PeerConnectionHandlerJsep() { +} + +void PeerConnectionHandlerJsep::initialize( + const WebKit::WebString& server_configuration, + const WebKit::WebString& username) { + native_peer_connection_ = dependency_factory_->CreatePeerConnection( + UTF16ToUTF8(server_configuration), + this); + CHECK(native_peer_connection_); +} + +WebKit::WebSessionDescriptionDescriptor PeerConnectionHandlerJsep::createOffer( + const WebKit::WebMediaHints& hints) { + WebKit::WebSessionDescriptionDescriptor offer; + + webrtc::MediaHints native_hints(hints.audio(), hints.video()); + scoped_ptr<webrtc::SessionDescriptionInterface> native_offer( + native_peer_connection_->CreateOffer(native_hints)); + if (!native_offer.get()) { + LOG(ERROR) << "Failed to create native offer"; + return offer; + } + + offer = CreateWebKitSessionDescription(native_offer.get()); + return offer; +} + +WebKit::WebSessionDescriptionDescriptor PeerConnectionHandlerJsep::createAnswer( + const WebKit::WebString& offer, + const WebKit::WebMediaHints& hints) { + WebKit::WebSessionDescriptionDescriptor answer; + + webrtc::MediaHints native_hints(hints.audio(), hints.video()); + scoped_ptr<webrtc::SessionDescriptionInterface> native_offer( + dependency_factory_->CreateSessionDescription(UTF16ToUTF8(offer))); + if (!native_offer.get()) { + LOG(ERROR) << "Failed to create native offer"; + return answer; + } + + scoped_ptr<webrtc::SessionDescriptionInterface> native_answer( + native_peer_connection_->CreateAnswer(native_hints, native_offer.get())); + if (!native_answer.get()) { + LOG(ERROR) << "Failed to create native answer"; + return answer; + } + + answer = CreateWebKitSessionDescription(native_answer.get()); + return answer; +} + +bool PeerConnectionHandlerJsep::setLocalDescription( + Action action, + const WebKit::WebSessionDescriptionDescriptor& description) { + webrtc::PeerConnectionInterface::Action native_action; + if (!GetNativeAction(action, &native_action)) + return false; + + webrtc::SessionDescriptionInterface* native_desc = + CreateNativeSessionDescription(description); + if (!native_desc) + return false; + + return native_peer_connection_->SetLocalDescription(native_action, + native_desc); +} + +bool PeerConnectionHandlerJsep::setRemoteDescription( + Action action, + const WebKit::WebSessionDescriptionDescriptor& description) { + webrtc::PeerConnectionInterface::Action native_action; + if (!GetNativeAction(action, &native_action)) + return false; + + webrtc::SessionDescriptionInterface* native_desc = + CreateNativeSessionDescription(description); + if (!native_desc) + return false; + + return native_peer_connection_->SetRemoteDescription(native_action, + native_desc); +} + +WebKit::WebSessionDescriptionDescriptor +PeerConnectionHandlerJsep::localDescription() { + const webrtc::SessionDescriptionInterface* native_desc = + native_peer_connection_->local_description(); + WebKit::WebSessionDescriptionDescriptor description = + CreateWebKitSessionDescription(native_desc); + return description; +} + +WebKit::WebSessionDescriptionDescriptor +PeerConnectionHandlerJsep::remoteDescription() { + const webrtc::SessionDescriptionInterface* native_desc = + native_peer_connection_->remote_description(); + WebKit::WebSessionDescriptionDescriptor description = + CreateWebKitSessionDescription(native_desc); + return description; +} + +bool PeerConnectionHandlerJsep::startIce(const WebKit::WebICEOptions& options) { + webrtc::PeerConnectionInterface::IceOptions native_options; + switch (options.candidateTypeToUse()) { + case WebKit::WebICEOptions::CandidateTypeAll: + native_options = webrtc::PeerConnectionInterface::kUseAll; + break; + case WebKit::WebICEOptions::CandidateTypeNoRelay: + native_options = webrtc::PeerConnectionInterface::kNoRelay; + break; + case WebKit::WebICEOptions::CandidateTypeOnlyRelay: + native_options = webrtc::PeerConnectionInterface::kOnlyRelay; + break; + default: + NOTREACHED(); + return false; + } + native_peer_connection_->StartIce(native_options); + return true; +} + +bool PeerConnectionHandlerJsep::processIceMessage( + const WebKit::WebICECandidateDescriptor& candidate) { + scoped_ptr<webrtc::IceCandidateInterface> native_candidate( + dependency_factory_->CreateIceCandidate( + UTF16ToUTF8(candidate.label()), + UTF16ToUTF8(candidate.candidateLine()))); + if (!native_candidate.get()) { + LOG(ERROR) << "Could not create native ICE candidate"; + return false; + } + + bool return_value = + native_peer_connection_->ProcessIceMessage(native_candidate.get()); + if (!return_value) + LOG(ERROR) << "Error processing ICE message"; + return return_value; +} + +void PeerConnectionHandlerJsep::addStream( + const WebKit::WebMediaStreamDescriptor& stream) { + AddStream(stream); + native_peer_connection_->CommitStreamChanges(); +} + +void PeerConnectionHandlerJsep::removeStream( + const WebKit::WebMediaStreamDescriptor& stream) { + RemoveStream(stream); + native_peer_connection_->CommitStreamChanges(); +} + +void PeerConnectionHandlerJsep::stop() { + // TODO(ronghuawu): There's an issue with signaling messages being sent during + // close. We need to investigate further. Not calling Close() on native + // PeerConnection is OK for now. + native_peer_connection_ = NULL; + media_stream_impl_->ClosePeerConnection(this); +} + +void PeerConnectionHandlerJsep::OnError() { + // TODO(grunell): Implement. + NOTIMPLEMENTED(); +} + +void PeerConnectionHandlerJsep::OnMessage(const std::string& msg) { + // TODO(grunell): Implement. + NOTIMPLEMENTED(); +} + +void PeerConnectionHandlerJsep::OnSignalingMessage(const std::string& msg) { + // Not used by JSEP PeerConnection. + NOTREACHED(); +} + +void PeerConnectionHandlerJsep::OnStateChange(StateType state_changed) { + switch (state_changed) { + case kReadyState: + WebKit::WebPeerConnection00HandlerClient::ReadyState ready_state; + switch (native_peer_connection_->ready_state()) { + case webrtc::PeerConnectionInterface::kNew: + ready_state = WebKit::WebPeerConnection00HandlerClient::ReadyStateNew; + break; + case webrtc::PeerConnectionInterface::kNegotiating: + ready_state = + WebKit::WebPeerConnection00HandlerClient::ReadyStateNegotiating; + break; + case webrtc::PeerConnectionInterface::kActive: + ready_state = + WebKit::WebPeerConnection00HandlerClient::ReadyStateActive; + break; + case webrtc::PeerConnectionInterface::kClosing: + // Not used by JSEP. + NOTREACHED(); + return; + case webrtc::PeerConnectionInterface::kClosed: + ready_state = + WebKit::WebPeerConnection00HandlerClient::ReadyStateClosed; + break; + default: + NOTREACHED(); + return; + } + client_->didChangeReadyState(ready_state); + break; + case kIceState: + // TODO(grunell): Implement when available in native PeerConnection. + NOTIMPLEMENTED(); + break; + case kSdpState: + // Not used by JSEP. + NOTREACHED(); + break; + default: + NOTREACHED(); + break; + } +} + +void PeerConnectionHandlerJsep::OnAddStream( + webrtc::MediaStreamInterface* stream) { + if (!stream) + return; + + DCHECK(remote_streams_.find(stream) == remote_streams_.end()); + WebKit::WebMediaStreamDescriptor descriptor = + CreateWebKitStreamDescriptor(stream); + remote_streams_.insert( + std::pair<webrtc::MediaStreamInterface*, + WebKit::WebMediaStreamDescriptor>(stream, descriptor)); + client_->didAddRemoteStream(descriptor); +} + +void PeerConnectionHandlerJsep::OnRemoveStream( + webrtc::MediaStreamInterface* stream) { + if (!stream) + return; + + RemoteStreamMap::iterator it = remote_streams_.find(stream); + if (it == remote_streams_.end()) { + NOTREACHED() << "Stream not found"; + return; + } + WebKit::WebMediaStreamDescriptor descriptor = it->second; + DCHECK(!descriptor.isNull()); + remote_streams_.erase(it); + client_->didRemoveRemoteStream(descriptor); +} + +void PeerConnectionHandlerJsep::OnIceCandidate( + const webrtc::IceCandidateInterface* candidate) { + WebKit::WebICECandidateDescriptor web_candidate; + + std::string label = candidate->label(); + std::string sdp; + if (!candidate->ToString(&sdp)) { + LOG(ERROR) << "Could not get SDP string"; + return; + } + + web_candidate.initialize(UTF8ToUTF16(label), UTF8ToUTF16(sdp)); + + // moreToFollow parameter isn't supported in native PeerConnection, so we + // always use true here, and then false in OnIceComplete(). + client_->didGenerateICECandidate(web_candidate, true); +} + +void PeerConnectionHandlerJsep::OnIceComplete() { + // moreToFollow parameter isn't supported in native PeerConnection, so we + // send an empty WebIseCandidate with moreToFollow=false. + WebKit::WebICECandidateDescriptor web_candidate; + client_->didGenerateICECandidate(web_candidate, false); +} + +webrtc::SessionDescriptionInterface* +PeerConnectionHandlerJsep::CreateNativeSessionDescription( + const WebKit::WebSessionDescriptionDescriptor& description) { + std::string initial_sdp = UTF16ToUTF8(description.initialSDP()); + webrtc::SessionDescriptionInterface* native_desc = + dependency_factory_->CreateSessionDescription(initial_sdp); + if (!native_desc) { + LOG(ERROR) << "Failed to create native session description"; + return NULL; + } + + for (size_t i = 0; i < description.numberOfAddedCandidates(); ++i) { + WebKit::WebICECandidateDescriptor candidate = description.candidate(i); + scoped_ptr<webrtc::IceCandidateInterface> native_candidate( + dependency_factory_->CreateIceCandidate( + UTF16ToUTF8(candidate.label()), + UTF16ToUTF8(candidate.candidateLine()))); + if (!native_desc->AddCandidate(native_candidate.get())) + LOG(ERROR) << "Failed to add candidate to native session description"; + } + + return native_desc; +} + +WebKit::WebSessionDescriptionDescriptor +PeerConnectionHandlerJsep::CreateWebKitSessionDescription( + const webrtc::SessionDescriptionInterface* native_desc) { + WebKit::WebSessionDescriptionDescriptor description; + if (!native_desc) { + VLOG(1) << "Native session description is null"; + return description; + } + + std::string sdp; + if (!native_desc->ToString(&sdp)) { + LOG(ERROR) << "Failed to get SDP string of native session description"; + return description; + } + + description.initialize(UTF8ToUTF16(sdp)); + return description; +} + +bool PeerConnectionHandlerJsep::GetNativeAction( + const Action action, + webrtc::PeerConnectionInterface::Action* native_action) { + switch (action) { + case ActionSDPOffer: + *native_action = webrtc::PeerConnectionInterface::kOffer; + break; + case ActionSDPPRanswer: + VLOG(1) << "Action PRANSWER not supported yet"; + return false; + case ActionSDPAnswer: + *native_action = webrtc::PeerConnectionInterface::kAnswer; + break; + default: + NOTREACHED(); + return false; + } + return true; +} diff --git a/content/renderer/media/peer_connection_handler_jsep.h b/content/renderer/media/peer_connection_handler_jsep.h new file mode 100644 index 0000000..463b05e --- /dev/null +++ b/content/renderer/media/peer_connection_handler_jsep.h @@ -0,0 +1,85 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_RENDERER_MEDIA_PEER_CONNECTION_HANDLER_JSEP_H_ +#define CONTENT_RENDERER_MEDIA_PEER_CONNECTION_HANDLER_JSEP_H_ + +#include <string> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/gtest_prod_util.h" +#include "content/common/content_export.h" +#include "content/renderer/media/peer_connection_handler_base.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebPeerConnection00Handler.h" + +// PeerConnectionHandlerJsep is a delegate for the JSEP PeerConnection API +// messages going between WebKit and native PeerConnection in libjingle. It's +// owned by WebKit. +class CONTENT_EXPORT PeerConnectionHandlerJsep + : public PeerConnectionHandlerBase, + NON_EXPORTED_BASE(public WebKit::WebPeerConnection00Handler) { + public: + PeerConnectionHandlerJsep( + WebKit::WebPeerConnection00HandlerClient* client, + MediaStreamImpl* msi, + MediaStreamDependencyFactory* dependency_factory); + virtual ~PeerConnectionHandlerJsep(); + + // WebKit::WebPeerConnection00Handler implementation + virtual void initialize( + const WebKit::WebString& server_configuration, + const WebKit::WebString& username) OVERRIDE; + virtual WebKit::WebSessionDescriptionDescriptor createOffer( + const WebKit::WebMediaHints& hints) OVERRIDE; + virtual WebKit::WebSessionDescriptionDescriptor createAnswer( + const WebKit::WebString& offer, + const WebKit::WebMediaHints& hints) OVERRIDE; + virtual bool setLocalDescription( + Action action, + const WebKit::WebSessionDescriptionDescriptor& description) OVERRIDE; + virtual bool setRemoteDescription( + Action action, + const WebKit::WebSessionDescriptionDescriptor& description) OVERRIDE; + virtual WebKit::WebSessionDescriptionDescriptor localDescription() OVERRIDE; + virtual WebKit::WebSessionDescriptionDescriptor remoteDescription() OVERRIDE; + virtual bool startIce(const WebKit::WebICEOptions& options) OVERRIDE; + virtual bool processIceMessage( + const WebKit::WebICECandidateDescriptor& candidate) OVERRIDE; + virtual void addStream( + const WebKit::WebMediaStreamDescriptor& stream) OVERRIDE; + virtual void removeStream( + const WebKit::WebMediaStreamDescriptor& stream) OVERRIDE; + // We will be deleted by WebKit after stop has been returned. + virtual void stop() OVERRIDE; + + // webrtc::PeerConnectionObserver implementation + virtual void OnError() OVERRIDE; + virtual void OnMessage(const std::string& msg) OVERRIDE; + virtual void OnSignalingMessage(const std::string& msg) OVERRIDE; + virtual void OnStateChange(StateType state_changed) OVERRIDE; + virtual void OnAddStream(webrtc::MediaStreamInterface* stream) OVERRIDE; + virtual void OnRemoveStream(webrtc::MediaStreamInterface* stream) OVERRIDE; + virtual void OnIceCandidate( + const webrtc::IceCandidateInterface* candidate) OVERRIDE; + virtual void OnIceComplete() OVERRIDE; + + private: + FRIEND_TEST_ALL_PREFIXES(PeerConnectionHandlerJsepTest, Basic); + + webrtc::SessionDescriptionInterface* CreateNativeSessionDescription( + const WebKit::WebSessionDescriptionDescriptor& description); + WebKit::WebSessionDescriptionDescriptor CreateWebKitSessionDescription( + const webrtc::SessionDescriptionInterface* native_desc); + bool GetNativeAction( + const Action action, + webrtc::PeerConnectionInterface::Action* native_action); + + // client_ is a weak pointer, and is valid until stop() has returned. + WebKit::WebPeerConnection00HandlerClient* client_; + + DISALLOW_COPY_AND_ASSIGN(PeerConnectionHandlerJsep); +}; + +#endif // CONTENT_RENDERER_MEDIA_PEER_CONNECTION_HANDLER_JSEP_H_ diff --git a/content/renderer/media/peer_connection_handler_jsep_unittest.cc b/content/renderer/media/peer_connection_handler_jsep_unittest.cc new file mode 100644 index 0000000..03f3eb6 --- /dev/null +++ b/content/renderer/media/peer_connection_handler_jsep_unittest.cc @@ -0,0 +1,231 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <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_00_handler_client.h" +#include "content/renderer/media/mock_peer_connection_impl.h" +#include "content/renderer/media/peer_connection_handler_jsep.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/WebKit/Source/WebKit/chromium/public/platform/WebICECandidateDescriptor.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebICEOptions.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebMediaHints.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebMediaStreamDescriptor.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebMediaStreamSource.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebSessionDescriptionDescriptor.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h" + +namespace webrtc { + +class MockVideoRendererWrapper : public VideoRendererWrapperInterface { + public: + virtual cricket::VideoRenderer* renderer() OVERRIDE { return NULL; } + + protected: + virtual ~MockVideoRendererWrapper() {} +}; + +} // namespace webrtc + +TEST(PeerConnectionHandlerJsepTest, Basic) { + MessageLoop loop; + + scoped_ptr<WebKit::MockWebPeerConnection00HandlerClient> mock_client( + new WebKit::MockWebPeerConnection00HandlerClient()); + scoped_refptr<MockMediaStreamImpl> mock_ms_impl(new MockMediaStreamImpl()); + scoped_ptr<MockMediaStreamDependencyFactory> mock_dependency_factory( + new MockMediaStreamDependencyFactory()); + mock_dependency_factory->CreatePeerConnectionFactory(NULL, + NULL, + NULL, + NULL, + NULL); + scoped_ptr<PeerConnectionHandlerJsep> pc_handler( + new PeerConnectionHandlerJsep(mock_client.get(), + mock_ms_impl.get(), + mock_dependency_factory.get())); + + WebKit::WebString server_config( + WebKit::WebString::fromUTF8("STUN stun.l.google.com:19302")); + WebKit::WebString username; + pc_handler->initialize(server_config, username); + EXPECT_TRUE(pc_handler->native_peer_connection_.get()); + webrtc::MockPeerConnectionImpl* mock_peer_connection = + static_cast<webrtc::MockPeerConnectionImpl*>( + pc_handler->native_peer_connection_.get()); + + // Create offer. + WebKit::WebMediaHints hints; + hints.initialize(true, true); + WebKit::WebSessionDescriptionDescriptor offer = + pc_handler->createOffer(hints); + EXPECT_FALSE(offer.isNull()); + EXPECT_EQ(std::string(mock_peer_connection->kDummyOffer), + UTF16ToUTF8(offer.initialSDP())); + EXPECT_EQ(hints.audio(), mock_peer_connection->hint_audio()); + EXPECT_EQ(hints.video(), mock_peer_connection->hint_video()); + + // Create answer. + WebKit::WebString offer_string = "offer"; + hints.reset(); + hints.initialize(false, false); + WebKit::WebSessionDescriptionDescriptor answer = + pc_handler->createAnswer(offer_string, hints); + EXPECT_FALSE(answer.isNull()); + EXPECT_EQ(UTF16ToUTF8(offer_string), UTF16ToUTF8(answer.initialSDP())); + EXPECT_EQ(UTF16ToUTF8(offer_string), mock_peer_connection->description_sdp()); + EXPECT_EQ(hints.audio(), mock_peer_connection->hint_audio()); + EXPECT_EQ(hints.video(), mock_peer_connection->hint_video()); + + // Set local description. + PeerConnectionHandlerJsep::Action action = + PeerConnectionHandlerJsep::ActionSDPOffer; + WebKit::WebSessionDescriptionDescriptor description; + WebKit::WebString sdp = "test sdp"; + description.initialize(sdp); + EXPECT_TRUE(pc_handler->setLocalDescription(action, description)); + EXPECT_EQ(webrtc::PeerConnectionInterface::kOffer, + mock_peer_connection->action()); + EXPECT_EQ(UTF16ToUTF8(sdp), mock_peer_connection->description_sdp()); + + // Get local description. + description.reset(); + description = pc_handler->localDescription(); + EXPECT_FALSE(description.isNull()); + EXPECT_EQ(UTF16ToUTF8(sdp), UTF16ToUTF8(description.initialSDP())); + + // Set remote description. + action = PeerConnectionHandlerJsep::ActionSDPAnswer; + sdp = "test sdp 2"; + description.reset(); + description.initialize(sdp); + EXPECT_TRUE(pc_handler->setRemoteDescription(action, description)); + EXPECT_EQ(webrtc::PeerConnectionInterface::kAnswer, + mock_peer_connection->action()); + EXPECT_EQ(UTF16ToUTF8(sdp), mock_peer_connection->description_sdp()); + + // Get remote description. + description.reset(); + description = pc_handler->remoteDescription(); + EXPECT_FALSE(description.isNull()); + EXPECT_EQ(UTF16ToUTF8(sdp), UTF16ToUTF8(description.initialSDP())); + + // Start ICE. + WebKit::WebICEOptions options; + options.initialize(WebKit::WebICEOptions::CandidateTypeAll); + EXPECT_TRUE(pc_handler->startIce(options)); + EXPECT_EQ(webrtc::PeerConnectionInterface::kUseAll, + mock_peer_connection->ice_options()); + + // Process ICE message. + WebKit::WebICECandidateDescriptor candidate; + WebKit::WebString label = "test label"; + sdp = "test sdp"; + candidate.initialize(label, sdp); + EXPECT_TRUE(pc_handler->processIceMessage(candidate)); + EXPECT_EQ(UTF16ToUTF8(label), mock_peer_connection->ice_label()); + EXPECT_EQ(UTF16ToUTF8(sdp), mock_peer_connection->ice_sdp()); + + // Add stream. + // TODO(grunell): Add an audio track as well. + std::string stream_label("stream-label"); + std::string video_track_label("video-label"); + talk_base::scoped_refptr<webrtc::LocalVideoTrackInterface> local_video_track( + mock_dependency_factory->CreateLocalVideoTrack(video_track_label, NULL)); + mock_ms_impl->AddTrack(video_track_label, local_video_track); + WebKit::WebVector<WebKit::WebMediaStreamSource> source_vector( + static_cast<size_t>(1)); + source_vector[0].initialize(WebKit::WebString::fromUTF8(video_track_label), + WebKit::WebMediaStreamSource::TypeVideo, + WebKit::WebString::fromUTF8("RemoteVideo")); + WebKit::WebMediaStreamDescriptor local_stream; + local_stream.initialize(UTF8ToUTF16(stream_label), source_vector); + pc_handler->addStream(local_stream); + EXPECT_EQ(stream_label, mock_peer_connection->stream_label()); + EXPECT_TRUE(mock_peer_connection->stream_changes_committed()); + + // On add stream. + std::string remote_stream_label(stream_label); + remote_stream_label += "-remote"; + std::string remote_video_track_label(video_track_label); + remote_video_track_label += "-remote"; + // We use a local stream as a remote since for testing purposes we really + // only need the MediaStreamInterface. + talk_base::scoped_refptr<webrtc::LocalMediaStreamInterface> remote_stream( + mock_dependency_factory->CreateLocalMediaStream(remote_stream_label)); + talk_base::scoped_refptr<webrtc::LocalVideoTrackInterface> remote_video_track( + mock_dependency_factory->CreateLocalVideoTrack(remote_video_track_label, + NULL)); + remote_video_track->set_enabled(true); + remote_stream->AddTrack(remote_video_track); + mock_peer_connection->AddRemoteStream(remote_stream); + pc_handler->OnAddStream(remote_stream); + EXPECT_EQ(remote_stream_label, mock_client->stream_label()); + + // Set renderer. + talk_base::scoped_refptr<webrtc::MockVideoRendererWrapper> renderer( + new talk_base::RefCountedObject<webrtc::MockVideoRendererWrapper>()); + pc_handler->SetVideoRenderer(remote_stream_label, renderer); + EXPECT_EQ(renderer, static_cast<webrtc::MockLocalVideoTrack*>( + remote_video_track.get())->renderer()); + + // Remove stream. + WebKit::WebVector<WebKit::WebMediaStreamDescriptor> empty_streams( + static_cast<size_t>(0)); + pc_handler->removeStream(local_stream); + EXPECT_EQ("", mock_peer_connection->stream_label()); + mock_peer_connection->ClearStreamChangesCommitted(); + EXPECT_TRUE(!mock_peer_connection->stream_changes_committed()); + + // On remove stream. + pc_handler->OnRemoveStream(remote_stream); + EXPECT_TRUE(mock_client->stream_label().empty()); + + // Add stream again. + pc_handler->addStream(local_stream); + EXPECT_EQ(stream_label, mock_peer_connection->stream_label()); + EXPECT_TRUE(mock_peer_connection->stream_changes_committed()); + + // On state change. + mock_peer_connection->SetReadyState(webrtc::PeerConnectionInterface::kActive); + webrtc::PeerConnectionObserver::StateType state = + webrtc::PeerConnectionObserver::kReadyState; + pc_handler->OnStateChange(state); + EXPECT_EQ(WebKit::WebPeerConnection00HandlerClient::ReadyStateActive, + mock_client->ready_state()); + + // On ICE candidate. + std::string candidate_label = "test label"; + std::string candidate_sdp = "test sdp"; + webrtc::IceCandidateInterface* native_candidate = + mock_dependency_factory->CreateIceCandidate(candidate_label, + candidate_sdp); + pc_handler->OnIceCandidate(native_candidate); + EXPECT_EQ(candidate_label, mock_client->candidate_label()); + EXPECT_EQ(candidate_sdp, mock_client->candidate_sdp()); + EXPECT_TRUE(mock_client->more_to_follow()); + + // On ICE complete. + pc_handler->OnIceComplete(); + EXPECT_TRUE(mock_client->candidate_label().empty()); + EXPECT_TRUE(mock_client->candidate_sdp().empty()); + EXPECT_FALSE(mock_client->more_to_follow()); + + // Stop. + 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(); +} diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc index b498938..c110235 100644 --- a/content/renderer/render_view_impl.cc +++ b/content/renderer/render_view_impl.cc @@ -137,6 +137,8 @@ #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebDragData.h" #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebGraphicsContext3D.h" #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebImage.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebPeerConnection00Handler.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebPeerConnection00HandlerClient.h" #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebPeerConnectionHandler.h" #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebPeerConnectionHandlerClient.h" #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebPoint.h" @@ -228,6 +230,10 @@ using WebKit::WebNavigationType; using WebKit::WebNode; using WebKit::WebPageSerializer; using WebKit::WebPageSerializerClient; +using WebKit::WebPeerConnection00Handler; +using WebKit::WebPeerConnection00HandlerClient; +using WebKit::WebPeerConnectionHandler; +using WebKit::WebPeerConnectionHandlerClient; using WebKit::WebPlugin; using WebKit::WebPluginAction; using WebKit::WebPluginContainer; @@ -644,17 +650,22 @@ RenderViewImpl* RenderViewImpl::Create( guest); } -WebKit::WebPeerConnectionHandler* RenderViewImpl::CreatePeerConnectionHandler( - WebKit::WebPeerConnectionHandlerClient* client) { - const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); - if (!cmd_line->HasSwitch(switches::kEnableMediaStream)) - return NULL; +WebPeerConnectionHandler* RenderViewImpl::CreatePeerConnectionHandler( + WebPeerConnectionHandlerClient* client) { EnsureMediaStreamImpl(); if (!media_stream_impl_.get()) return NULL; return media_stream_impl_->CreatePeerConnectionHandler(client); } +WebPeerConnection00Handler* RenderViewImpl::CreatePeerConnectionHandlerJsep( + WebPeerConnection00HandlerClient* client) { + EnsureMediaStreamImpl(); + if (!media_stream_impl_.get()) + return NULL; + return media_stream_impl_->CreatePeerConnectionHandlerJsep(client); +} + void RenderViewImpl::AddObserver(RenderViewObserver* observer) { observers_.AddObserver(observer); } @@ -3276,6 +3287,10 @@ void RenderViewImpl::CheckPreferredSize() { } void RenderViewImpl::EnsureMediaStreamImpl() { + const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); + if (!cmd_line->HasSwitch(switches::kEnableMediaStream)) + return; + #if defined(ENABLE_P2P_APIS) if (!p2p_socket_dispatcher_) p2p_socket_dispatcher_ = new content::P2PSocketDispatcher(this); @@ -5144,9 +5159,6 @@ WebKit::WebPageVisibilityState RenderViewImpl::visibilityState() const { } WebKit::WebUserMediaClient* RenderViewImpl::userMediaClient() { - const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); - if (!cmd_line->HasSwitch(switches::kEnableMediaStream)) - return NULL; EnsureMediaStreamImpl(); return media_stream_impl_; } diff --git a/content/renderer/render_view_impl.h b/content/renderer/render_view_impl.h index cacdcb2..432fe7ec 100644 --- a/content/renderer/render_view_impl.h +++ b/content/renderer/render_view_impl.h @@ -124,6 +124,8 @@ class WebDragData; class WebGeolocationClient; class WebIconURL; class WebImage; +class WebPeerConnection00Handler; +class WebPeerConnection00HandlerClient; class WebMediaPlayer; class WebMediaPlayerClient; class WebMouseEvent; @@ -233,6 +235,8 @@ class RenderViewImpl : public RenderWidget, WebKit::WebPeerConnectionHandler* CreatePeerConnectionHandler( WebKit::WebPeerConnectionHandlerClient* client); + WebKit::WebPeerConnection00Handler* CreatePeerConnectionHandlerJsep( + WebKit::WebPeerConnection00HandlerClient* client); // Functions to add and remove observers for this object. void AddObserver(content::RenderViewObserver* observer); diff --git a/content/renderer/renderer_webkitplatformsupport_impl.cc b/content/renderer/renderer_webkitplatformsupport_impl.cc index 44e3e5c..2e94f71 100644 --- a/content/renderer/renderer_webkitplatformsupport_impl.cc +++ b/content/renderer/renderer_webkitplatformsupport_impl.cc @@ -89,6 +89,8 @@ using WebKit::WebIDBKeyPath; using WebKit::WebKitPlatformSupport; using WebKit::WebMediaStreamCenter; using WebKit::WebMediaStreamCenterClient; +using WebKit::WebPeerConnection00Handler; +using WebKit::WebPeerConnection00HandlerClient; using WebKit::WebPeerConnectionHandler; using WebKit::WebPeerConnectionHandlerClient; using WebKit::WebSerializedScriptValue; @@ -726,6 +728,18 @@ RendererWebKitPlatformSupportImpl::createPeerConnectionHandler( return render_view->CreatePeerConnectionHandler(client); } +WebPeerConnection00Handler* +RendererWebKitPlatformSupportImpl::createPeerConnection00Handler( + WebPeerConnection00HandlerClient* 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->CreatePeerConnectionHandlerJsep(client); +} + //------------------------------------------------------------------------------ WebMediaStreamCenter* diff --git a/content/renderer/renderer_webkitplatformsupport_impl.h b/content/renderer/renderer_webkitplatformsupport_impl.h index 3b8bd2d..43e202dd 100644 --- a/content/renderer/renderer_webkitplatformsupport_impl.h +++ b/content/renderer/renderer_webkitplatformsupport_impl.h @@ -95,6 +95,8 @@ class CONTENT_EXPORT RendererWebKitPlatformSupportImpl std::vector<webkit::WebPluginInfo>* plugins) OVERRIDE; virtual WebKit::WebPeerConnectionHandler* createPeerConnectionHandler( WebKit::WebPeerConnectionHandlerClient* client) OVERRIDE; + virtual WebKit::WebPeerConnection00Handler* createPeerConnection00Handler( + WebKit::WebPeerConnection00HandlerClient* client) OVERRIDE; virtual WebKit::WebMediaStreamCenter* createMediaStreamCenter( WebKit::WebMediaStreamCenterClient* client) OVERRIDE; |