summaryrefslogtreecommitdiffstats
path: root/content/renderer/media
diff options
context:
space:
mode:
authorgrunell@chromium.org <grunell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-01-11 08:47:26 +0000
committergrunell@chromium.org <grunell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-01-11 08:47:26 +0000
commite548599b304bc156aa681b5a7687009058f2d1b1 (patch)
tree51693245b10e2cf6c1703a4faf034bce039b2e4c /content/renderer/media
parentcdeeeb29d7a3a2a0988061bf63aad184060b4c14 (diff)
downloadchromium_src-e548599b304bc156aa681b5a7687009058f2d1b1.zip
chromium_src-e548599b304bc156aa681b5a7687009058f2d1b1.tar.gz
chromium_src-e548599b304bc156aa681b5a7687009058f2d1b1.tar.bz2
Adding support for MediaStream and PeerConnection functionality.
BUG= TEST=content_unittests Review URL: http://codereview.chromium.org/8060055 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@117187 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content/renderer/media')
-rw-r--r--content/renderer/media/media_stream_dependency_factory.cc72
-rw-r--r--content/renderer/media/media_stream_dependency_factory.h78
-rw-r--r--content/renderer/media/media_stream_dispatcher.h19
-rw-r--r--content/renderer/media/media_stream_impl.cc351
-rw-r--r--content/renderer/media/media_stream_impl.h153
-rw-r--r--content/renderer/media/media_stream_impl_unittest.cc45
-rw-r--r--content/renderer/media/mock_media_stream_dependency_factory.cc50
-rw-r--r--content/renderer/media/mock_media_stream_dependency_factory.h39
-rw-r--r--content/renderer/media/mock_media_stream_dispatcher.cc49
-rw-r--r--content/renderer/media/mock_media_stream_dispatcher.h45
-rw-r--r--content/renderer/media/mock_media_stream_impl.cc60
-rw-r--r--content/renderer/media/mock_media_stream_impl.h50
-rw-r--r--content/renderer/media/mock_peer_connection_impl.cc91
-rw-r--r--content/renderer/media/mock_peer_connection_impl.h62
-rw-r--r--content/renderer/media/mock_web_peer_connection_handler_client.cc38
-rw-r--r--content/renderer/media/mock_web_peer_connection_handler_client.h42
-rw-r--r--content/renderer/media/peer_connection_handler.cc274
-rw-r--r--content/renderer/media/peer_connection_handler.h159
-rw-r--r--content/renderer/media/peer_connection_handler_unittest.cc124
19 files changed, 1764 insertions, 37 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..4e78f4b
--- /dev/null
+++ b/content/renderer/media/media_stream_dependency_factory.cc
@@ -0,0 +1,72 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/renderer/media/media_stream_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 "content/renderer/p2p/port_allocator.h"
+#include "jingle/glue/thread_wrapper.h"
+#include "third_party/libjingle/source/talk/app/webrtc/peerconnection.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/WebFrame.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::MediaEngineInterface* media_engine,
+ talk_base::Thread* worker_thread) {
+ if (!pc_factory_.get()) {
+ scoped_ptr<webrtc::PeerConnectionFactory> factory(
+ new webrtc::PeerConnectionFactory(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;
+}
+
+cricket::PortAllocator* MediaStreamDependencyFactory::CreatePortAllocator(
+ content::P2PSocketDispatcher* socket_dispatcher,
+ talk_base::NetworkManager* network_manager,
+ talk_base::PacketSocketFactory* socket_factory,
+ const webkit_glue::P2PTransport::Config& config) {
+ WebKit::WebFrame* web_frame = WebKit::WebFrame::frameForCurrentContext();
+ if (!web_frame) {
+ DVLOG(1) << "WebFrame is NULL.";
+ return NULL;
+ }
+ return new content::P2PPortAllocator(web_frame,
+ socket_dispatcher,
+ network_manager,
+ socket_factory,
+ config);
+}
+
+webrtc::PeerConnection* MediaStreamDependencyFactory::CreatePeerConnection(
+ cricket::PortAllocator* port_allocator,
+ talk_base::Thread* signaling_thread) {
+ return pc_factory_->CreatePeerConnection(port_allocator, 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..0a279c2
--- /dev/null
+++ b/content/renderer/media/media_stream_dependency_factory.h
@@ -0,0 +1,78 @@
+// 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_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 "content/common/content_export.h"
+#include "third_party/libjingle/source/talk/app/webrtc/peerconnectionfactory.h"
+#include "webkit/glue/p2p_transport.h"
+
+namespace content {
+class IpcNetworkManager;
+class IpcPacketSocketFactory;
+class P2PSocketDispatcher;
+}
+
+namespace cricket {
+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 CONTENT_EXPORT MediaStreamDependencyFactory {
+ public:
+ MediaStreamDependencyFactory();
+ virtual ~MediaStreamDependencyFactory();
+
+ // WebRtcMediaEngine is used in CreatePeerConnectionFactory().
+ 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::MediaEngineInterface* media_engine,
+ talk_base::Thread* worker_thread);
+ virtual void DeletePeerConnectionFactory();
+ virtual bool PeerConnectionFactoryCreated();
+
+ // The port allocator is used in CreatePeerConnection().
+ virtual cricket::PortAllocator* CreatePortAllocator(
+ content::P2PSocketDispatcher* socket_dispatcher,
+ talk_base::NetworkManager* network_manager,
+ talk_base::PacketSocketFactory* socket_factory,
+ const webkit_glue::P2PTransport::Config& config);
+
+ // 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(
+ cricket::PortAllocator* port_allocator,
+ 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..3888f40 100644
--- a/content/renderer/media/media_stream_dispatcher.h
+++ b/content/renderer/media/media_stream_dispatcher.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// 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.
@@ -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..ab48883 100644
--- a/content/renderer/media/media_stream_impl.cc
+++ b/content/renderer/media/media_stream_impl.cc
@@ -1,15 +1,35 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/renderer/media/media_stream_impl.h"
-#include "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/platform/WebMediaStreamDescriptor.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebMediaStreamRegistry.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebMediaStreamSource.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebVector.h"
namespace {
@@ -17,24 +37,211 @@ 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),
+ 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"),
+ vcm_created_(false) {
+}
+
+MediaStreamImpl::~MediaStreamImpl() {
+ DCHECK(!peer_connection_handler_);
+ if (dependency_factory_.get())
+ dependency_factory_->DeletePeerConnectionFactory();
+ if (network_manager_) {
+ // The network manager needs to free its resources on the thread they were
+ // created, which is the worked thread.
+ if (chrome_worker_thread_.IsRunning()) {
+ chrome_worker_thread_.message_loop()->PostTask(FROM_HERE, base::Bind(
+ &MediaStreamImpl::DeleteIpcNetworkManager,
+ base::Unretained(this)));
+ } else {
+ NOTREACHED() << "Worker thread not running.";
+ }
+ }
+}
+
+WebKit::WebPeerConnectionHandler* MediaStreamImpl::CreatePeerConnectionHandler(
+ WebKit::WebPeerConnectionHandlerClient* client) {
+ DCHECK(CalledOnValidThread());
+ 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 (!network_manager_)
+ network_manager_ = new content::IpcNetworkManager(p2p_socket_dispatcher_);
+
+ if (!socket_factory_.get()) {
+ socket_factory_.reset(
+ new content::IpcPacketSocketFactory(p2p_socket_dispatcher_));
+ }
+
+ if (!dependency_factory_->PeerConnectionFactoryCreated()) {
+ if (!dependency_factory_->CreatePeerConnectionFactory(media_engine_,
+ worker_thread_)) {
+ LOG(ERROR) << "Could not initialize PeerConnection factory";
+ return NULL;
+ }
+ }
+
+ peer_connection_handler_ = new PeerConnectionHandler(
+ client,
+ this,
+ dependency_factory_.get(),
+ signaling_thread_,
+ p2p_socket_dispatcher_,
+ network_manager_,
+ socket_factory_.get());
+
+ return peer_connection_handler_;
+}
+
+void MediaStreamImpl::ClosePeerConnection() {
+ DCHECK(CalledOnValidThread());
+ rtc_video_decoder_ = NULL;
+ media_engine_->SetVideoCaptureModule(NULL);
+ vcm_created_ = false;
+ peer_connection_handler_ = NULL;
+}
+
+bool MediaStreamImpl::SetVideoCaptureModule(const std::string& label) {
+ DCHECK(CalledOnValidThread());
+ if (vcm_created_)
+ return true;
+ // Set the capture device.
+ // TODO(grunell): Instead of using the first track, the selected track
+ // should be used.
+ int id = media_stream_dispatcher_->video_session_id(label, 0);
+ if (id == media_stream::StreamDeviceInfo::kNoId)
+ return false;
+ webrtc::VideoCaptureModule* vcm =
+ new VideoCaptureModuleImpl(id, vc_manager_.get());
+ vcm_created_ = true;
+ media_engine_->SetVideoCaptureModule(vcm);
+ return true;
+}
+
+void MediaStreamImpl::requestUserMedia(
+ const WebKit::WebUserMediaRequest& user_media_request,
+ const WebKit::WebVector<WebKit::WebMediaStreamSource>&
+ media_stream_source_vector) {
+ DCHECK(CalledOnValidThread());
+ 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) {
+ DCHECK(CalledOnValidThread());
+ 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 +249,118 @@ 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) {
+ DCHECK(CalledOnValidThread());
+
+ // 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) {
+ DCHECK(CalledOnValidThread());
+ 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) {
+ DCHECK(CalledOnValidThread());
+ DVLOG(1) << "MediaStreamImpl::OnVideoDeviceFailed("
+ << label << ", " << index << ")";
+ // TODO(grunell): Implement. Currently not supported in WebKit.
+ NOTIMPLEMENTED();
+}
+
+void MediaStreamImpl::OnAudioDeviceFailed(const std::string& label,
+ int index) {
+ DCHECK(CalledOnValidThread());
+ 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();
+}
+
+void MediaStreamImpl::DeleteIpcNetworkManager() {
+ DCHECK_EQ(MessageLoop::current(), chrome_worker_thread_.message_loop());
+ delete network_manager_;
+ network_manager_ = NULL;
+}
diff --git a/content/renderer/media/media_stream_impl.h b/content/renderer/media/media_stream_impl.h
index 79dc948..5ed849f 100644
--- a/content/renderer/media/media_stream_impl.h
+++ b/content/renderer/media/media_stream_impl.h
@@ -1,34 +1,167 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// 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_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/non_thread_safe.h"
+#include "base/threading/thread.h"
+#include "content/common/content_export.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 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.
-class MediaStreamImpl
- : public webkit_media::MediaStreamClient,
+// 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. It must be created, called and destroyed on the
+// render thread.
+class CONTENT_EXPORT MediaStreamImpl
+ : public WebKit::WebUserMediaClient,
+ public webkit_media::MediaStreamClient,
+ public MediaStreamDispatcherEventHandler,
+ public base::NonThreadSafe,
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);
+ void DeleteIpcNetworkManager();
+
+ 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_;
+
+ // media_engine_ is owned by PeerConnectionFactory (which is owned by
+ // dependency_factory_) and is valid for the lifetime of
+ // PeerConnectionFactory.
+ 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_;
+
+ // We own network_manager_, must be deleted on the worker thread.
+ content::IpcNetworkManager* network_manager_;
+
+ scoped_ptr<content::IpcPacketSocketFactory> socket_factory_;
+
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_;
+
+ 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..ffa4e23
--- /dev/null
+++ b/content/renderer/media/media_stream_impl_unittest.cc
@@ -0,0 +1,45 @@
+// 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/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"
+#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebPeerConnectionHandler.h"
+
+TEST(MediaStreamImplTest, 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;
+ WebKit::WebPeerConnectionHandler* pc_handler =
+ ms_impl->CreatePeerConnectionHandler(&client);
+ EXPECT_TRUE(ms_impl->peer_connection_handler_);
+
+ ms_impl->ClosePeerConnection();
+ EXPECT_FALSE(ms_impl->peer_connection_handler_);
+ delete pc_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..5cbcbd4
--- /dev/null
+++ b/content/renderer/media/mock_media_stream_dependency_factory.cc
@@ -0,0 +1,50 @@
+// 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 "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::MediaEngineInterface* media_engine,
+ talk_base::Thread* worker_thread) {
+ mock_pc_factory_created_ = true;
+ media_engine_.reset(media_engine);
+ return true;
+}
+
+void MockMediaStreamDependencyFactory::DeletePeerConnectionFactory() {
+ mock_pc_factory_created_ = false;
+}
+
+bool MockMediaStreamDependencyFactory::PeerConnectionFactoryCreated() {
+ return mock_pc_factory_created_;
+}
+
+cricket::PortAllocator* MockMediaStreamDependencyFactory::CreatePortAllocator(
+ content::P2PSocketDispatcher* socket_dispatcher,
+ talk_base::NetworkManager* network_manager,
+ talk_base::PacketSocketFactory* socket_factory,
+ const webkit_glue::P2PTransport::Config& config) {
+ return NULL;
+}
+
+webrtc::PeerConnection* MockMediaStreamDependencyFactory::CreatePeerConnection(
+ cricket::PortAllocator* port_allocator,
+ 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..163baab
--- /dev/null
+++ b/content/renderer/media/mock_media_stream_dependency_factory.h
@@ -0,0 +1,39 @@
+// 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_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::MediaEngineInterface* media_engine,
+ talk_base::Thread* worker_thread) OVERRIDE;
+ virtual void DeletePeerConnectionFactory() OVERRIDE;
+ virtual bool PeerConnectionFactoryCreated() OVERRIDE;
+ virtual cricket::PortAllocator* CreatePortAllocator(
+ content::P2PSocketDispatcher* socket_dispatcher,
+ talk_base::NetworkManager* network_manager,
+ talk_base::PacketSocketFactory* socket_factory,
+ const webkit_glue::P2PTransport::Config& config) OVERRIDE;
+ virtual webrtc::PeerConnection* CreatePeerConnection(
+ cricket::PortAllocator* port_allocator,
+ talk_base::Thread* signaling_thread) OVERRIDE;
+
+ private:
+ bool mock_pc_factory_created_;
+ scoped_ptr<cricket::MediaEngineInterface> media_engine_;
+
+ 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..16894bf
--- /dev/null
+++ b/content/renderer/media/mock_media_stream_dispatcher.cc
@@ -0,0 +1,49 @@
+// 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/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..e712664
--- /dev/null
+++ b/content/renderer/media/mock_media_stream_dispatcher.h
@@ -0,0 +1,45 @@
+// 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_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..9109989
--- /dev/null
+++ b/content/renderer/media/mock_media_stream_impl.cc
@@ -0,0 +1,60 @@
+// 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/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..b297b49
--- /dev/null
+++ b/content/renderer/media/mock_media_stream_impl.h
@@ -0,0 +1,50 @@
+// 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_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) OVERRIDE;
+ virtual void ClosePeerConnection() OVERRIDE;
+ // Returns true if created successfully or already exists, false otherwise.
+ virtual bool SetVideoCaptureModule(const std::string& label) OVERRIDE;
+
+ // 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..365f18b
--- /dev/null
+++ b/content/renderer/media/mock_peer_connection_impl.cc
@@ -0,0 +1,91 @@
+// 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/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..903e98f
--- /dev/null
+++ b/content/renderer/media/mock_peer_connection_impl.h
@@ -0,0 +1,62 @@
+// 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_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..6f438bf
--- /dev/null
+++ b/content/renderer/media/mock_web_peer_connection_handler_client.cc
@@ -0,0 +1,38 @@
+// 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_handler_client.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 {
+
+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..1ee020d
--- /dev/null
+++ b/content/renderer/media/mock_web_peer_connection_handler_client.h
@@ -0,0 +1,42 @@
+// 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_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/platform/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..d1729e5
--- /dev/null
+++ b/content/renderer/media/peer_connection_handler.cc
@@ -0,0 +1,274 @@
+// 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.h"
+
+#include <stdlib.h>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/string_number_conversions.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.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 "content/renderer/p2p/ipc_network_manager.h"
+#include "content/renderer/p2p/ipc_socket_factory.h"
+#include "third_party/libjingle/source/talk/base/thread.h"
+#include "third_party/libjingle/source/talk/p2p/base/portallocator.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/WebPeerConnectionHandlerClient.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebVector.h"
+
+PeerConnectionHandler::PeerConnectionHandler(
+ WebKit::WebPeerConnectionHandlerClient* client,
+ MediaStreamImpl* msi,
+ MediaStreamDependencyFactory* dependency_factory,
+ talk_base::Thread* signaling_thread,
+ content::P2PSocketDispatcher* socket_dispatcher,
+ content::IpcNetworkManager* network_manager,
+ content::IpcPacketSocketFactory* socket_factory)
+ : client_(client),
+ media_stream_impl_(msi),
+ dependency_factory_(dependency_factory),
+ message_loop_proxy_(base::MessageLoopProxy::current()),
+ signaling_thread_(signaling_thread),
+ socket_dispatcher_(socket_dispatcher),
+ network_manager_(network_manager),
+ socket_factory_(socket_factory),
+ 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.
+ // TODO(grunell): Support TURN.
+
+ // 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);
+
+ webkit_glue::P2PTransport::Config p2p_config;
+ p2p_config.stun_server = address;
+ p2p_config.stun_server_port = port;
+
+ port_allocator_.reset(dependency_factory_->CreatePortAllocator(
+ socket_dispatcher_,
+ network_manager_,
+ socket_factory_,
+ p2p_config));
+
+ native_peer_connection_.reset(dependency_factory_->CreatePeerConnection(
+ port_allocator_.get(),
+ 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);
+ 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);
+ }
+ // 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..08cca94
--- /dev/null
+++ b/content/renderer/media/peer_connection_handler.h
@@ -0,0 +1,159 @@
+// 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_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 "content/common/content_export.h"
+#include "third_party/libjingle/source/talk/app/webrtc/peerconnection.h"
+#include "third_party/libjingle/source/talk/base/socketaddress.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebPeerConnectionHandler.h"
+
+namespace base {
+class Thread;
+class WaitableEvent;
+}
+
+namespace content {
+class IpcNetworkManager;
+class IpcPacketSocketFactory;
+class P2PSocketDispatcher;
+}
+
+namespace cricket {
+class PortAllocator;
+}
+
+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 CONTENT_EXPORT PeerConnectionHandler
+ : public WebKit::WebPeerConnectionHandler,
+ public webrtc::PeerConnectionObserver {
+ public:
+ PeerConnectionHandler(
+ WebKit::WebPeerConnectionHandlerClient* client,
+ MediaStreamImpl* msi,
+ MediaStreamDependencyFactory* dependency_factory,
+ talk_base::Thread* signaling_thread,
+ content::P2PSocketDispatcher* socket_dispatcher,
+ content::IpcNetworkManager* network_manager,
+ content::IpcPacketSocketFactory* socket_factory);
+ 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. 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.
+ scoped_ptr<webrtc::PeerConnection> native_peer_connection_;
+
+ scoped_refptr<base::MessageLoopProxy> message_loop_proxy_;
+
+ talk_base::Thread* signaling_thread_;
+
+ // socket_dispatcher_ is a weak reference, owned by RenderView. It's valid
+ // for the lifetime of RenderView.
+ content::P2PSocketDispatcher* socket_dispatcher_;
+
+ // network_manager_ and socket_factory_ are a weak references, owned by
+ // MediaStreamImpl.
+ content::IpcNetworkManager* network_manager_;
+ content::IpcPacketSocketFactory* socket_factory_;
+
+ scoped_ptr<cricket::PortAllocator> 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".
+ // Native: 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..fadc704
--- /dev/null
+++ b/content/renderer/media/peer_connection_handler_unittest.cc
@@ -0,0 +1,124 @@
+// 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_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/WebKit/Source/WebKit/chromium/public/platform/WebMediaStreamDescriptor.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebMediaStreamSource.h"
+
+TEST(PeerConnectionHandlerTest, Basic) {
+ MessageLoop loop;
+
+ scoped_ptr<WebKit::MockWebPeerConnectionHandlerClient> mock_client(
+ new WebKit::MockWebPeerConnectionHandlerClient());
+ scoped_refptr<MockMediaStreamImpl> mock_ms_impl(new MockMediaStreamImpl());
+ scoped_ptr<MockMediaStreamDependencyFactory> mock_dependency_factory(
+ new MockMediaStreamDependencyFactory());
+ mock_dependency_factory->CreatePeerConnectionFactory(NULL, NULL);
+ scoped_ptr<PeerConnectionHandler> pc_handler(
+ new PeerConnectionHandler(mock_client.get(),
+ mock_ms_impl.get(),
+ mock_dependency_factory.get(),
+ NULL,
+ NULL,
+ NULL,
+ NULL));
+
+ 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());
+
+ scoped_refptr<RTCVideoDecoder> rtc_video_decoder(
+ new RTCVideoDecoder(&loop, ""));
+ pc_handler->SetVideoRenderer(label, rtc_video_decoder.get());
+ 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.get(),
+ NULL,
+ NULL,
+ NULL,
+ NULL));
+ 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();
+}