From e548599b304bc156aa681b5a7687009058f2d1b1 Mon Sep 17 00:00:00 2001
From: "grunell@chromium.org"
 <grunell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>
Date: Wed, 11 Jan 2012 08:47:26 +0000
Subject: 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
---
 .../media/media_stream_dependency_factory.cc       |  72 +++++
 .../media/media_stream_dependency_factory.h        |  78 +++++
 content/renderer/media/media_stream_dispatcher.h   |  19 +-
 content/renderer/media/media_stream_impl.cc        | 351 +++++++++++++++++++--
 content/renderer/media/media_stream_impl.h         | 153 ++++++++-
 .../renderer/media/media_stream_impl_unittest.cc   |  45 +++
 .../media/mock_media_stream_dependency_factory.cc  |  50 +++
 .../media/mock_media_stream_dependency_factory.h   |  39 +++
 .../renderer/media/mock_media_stream_dispatcher.cc |  49 +++
 .../renderer/media/mock_media_stream_dispatcher.h  |  45 +++
 content/renderer/media/mock_media_stream_impl.cc   |  60 ++++
 content/renderer/media/mock_media_stream_impl.h    |  50 +++
 .../renderer/media/mock_peer_connection_impl.cc    |  91 ++++++
 content/renderer/media/mock_peer_connection_impl.h |  62 ++++
 .../mock_web_peer_connection_handler_client.cc     |  38 +++
 .../mock_web_peer_connection_handler_client.h      |  42 +++
 content/renderer/media/peer_connection_handler.cc  | 274 ++++++++++++++++
 content/renderer/media/peer_connection_handler.h   | 159 ++++++++++
 .../media/peer_connection_handler_unittest.cc      | 124 ++++++++
 19 files changed, 1764 insertions(+), 37 deletions(-)
 create mode 100644 content/renderer/media/media_stream_dependency_factory.cc
 create mode 100644 content/renderer/media/media_stream_dependency_factory.h
 create mode 100644 content/renderer/media/media_stream_impl_unittest.cc
 create mode 100644 content/renderer/media/mock_media_stream_dependency_factory.cc
 create mode 100644 content/renderer/media/mock_media_stream_dependency_factory.h
 create mode 100644 content/renderer/media/mock_media_stream_dispatcher.cc
 create mode 100644 content/renderer/media/mock_media_stream_dispatcher.h
 create mode 100644 content/renderer/media/mock_media_stream_impl.cc
 create mode 100644 content/renderer/media/mock_media_stream_impl.h
 create mode 100644 content/renderer/media/mock_peer_connection_impl.cc
 create mode 100644 content/renderer/media/mock_peer_connection_impl.h
 create mode 100644 content/renderer/media/mock_web_peer_connection_handler_client.cc
 create mode 100644 content/renderer/media/mock_web_peer_connection_handler_client.h
 create mode 100644 content/renderer/media/peer_connection_handler.cc
 create mode 100644 content/renderer/media/peer_connection_handler.h
 create mode 100644 content/renderer/media/peer_connection_handler_unittest.cc

(limited to 'content/renderer/media')

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();
+}
-- 
cgit v1.1