Split rtp_receiver for readability.

Bug: webrtc:10304
Change-Id: I85e421060d8560cf36cdb5970ae190efc77e7709
Reviewed-on: https://webrtc-review.googlesource.com/c/122085
Reviewed-by: Karl Wiberg <kwiberg@webrtc.org>
Commit-Queue: Ruslan Burakov <kuddai@google.com>
Cr-Commit-Position: refs/heads/master@{#26634}
diff --git a/pc/BUILD.gn b/pc/BUILD.gn
index 5ea089f..5e16e26 100644
--- a/pc/BUILD.gn
+++ b/pc/BUILD.gn
@@ -128,6 +128,8 @@
   visibility = [ "*" ]
   cflags = []
   sources = [
+    "audio_rtp_receiver.cc",
+    "audio_rtp_receiver.h",
     "audio_track.cc",
     "audio_track.h",
     "data_channel.cc",
@@ -175,6 +177,8 @@
     "stream_collection.h",
     "track_media_info_map.cc",
     "track_media_info_map.h",
+    "video_rtp_receiver.cc",
+    "video_rtp_receiver.h",
     "video_track.cc",
     "video_track.h",
     "video_track_source.cc",
diff --git a/pc/audio_rtp_receiver.cc b/pc/audio_rtp_receiver.cc
new file mode 100644
index 0000000..4602fda
--- /dev/null
+++ b/pc/audio_rtp_receiver.cc
@@ -0,0 +1,248 @@
+/*
+ *  Copyright 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "pc/audio_rtp_receiver.h"
+
+#include <stddef.h>
+#include <utility>
+#include <vector>
+
+#include "api/media_stream_proxy.h"
+#include "api/media_stream_track_proxy.h"
+#include "pc/audio_track.h"
+#include "pc/media_stream.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/location.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/trace_event.h"
+
+namespace webrtc {
+
+AudioRtpReceiver::AudioRtpReceiver(rtc::Thread* worker_thread,
+                                   std::string receiver_id,
+                                   std::vector<std::string> stream_ids)
+    : AudioRtpReceiver(worker_thread,
+                       receiver_id,
+                       CreateStreamsFromIds(std::move(stream_ids))) {}
+
+AudioRtpReceiver::AudioRtpReceiver(
+    rtc::Thread* worker_thread,
+    const std::string& receiver_id,
+    const std::vector<rtc::scoped_refptr<MediaStreamInterface>>& streams)
+    : worker_thread_(worker_thread),
+      id_(receiver_id),
+      source_(new rtc::RefCountedObject<RemoteAudioSource>(worker_thread)),
+      track_(AudioTrackProxy::Create(rtc::Thread::Current(),
+                                     AudioTrack::Create(receiver_id, source_))),
+      cached_track_enabled_(track_->enabled()),
+      attachment_id_(GenerateUniqueId()) {
+  RTC_DCHECK(worker_thread_);
+  RTC_DCHECK(track_->GetSource()->remote());
+  track_->RegisterObserver(this);
+  track_->GetSource()->RegisterAudioObserver(this);
+  SetStreams(streams);
+}
+
+AudioRtpReceiver::~AudioRtpReceiver() {
+  track_->GetSource()->UnregisterAudioObserver(this);
+  track_->UnregisterObserver(this);
+  Stop();
+}
+
+void AudioRtpReceiver::OnChanged() {
+  if (cached_track_enabled_ != track_->enabled()) {
+    cached_track_enabled_ = track_->enabled();
+    Reconfigure();
+  }
+}
+
+bool AudioRtpReceiver::SetOutputVolume(double volume) {
+  RTC_DCHECK_GE(volume, 0.0);
+  RTC_DCHECK_LE(volume, 10.0);
+  RTC_DCHECK(media_channel_);
+  RTC_DCHECK(ssrc_);
+  return worker_thread_->Invoke<bool>(RTC_FROM_HERE, [&] {
+    return media_channel_->SetOutputVolume(*ssrc_, volume);
+  });
+}
+
+void AudioRtpReceiver::OnSetVolume(double volume) {
+  RTC_DCHECK_GE(volume, 0);
+  RTC_DCHECK_LE(volume, 10);
+  cached_volume_ = volume;
+  if (!media_channel_ || !ssrc_) {
+    RTC_LOG(LS_ERROR)
+        << "AudioRtpReceiver::OnSetVolume: No audio channel exists.";
+    return;
+  }
+  // When the track is disabled, the volume of the source, which is the
+  // corresponding WebRtc Voice Engine channel will be 0. So we do not allow
+  // setting the volume to the source when the track is disabled.
+  if (!stopped_ && track_->enabled()) {
+    if (!SetOutputVolume(cached_volume_)) {
+      RTC_NOTREACHED();
+    }
+  }
+}
+
+std::vector<std::string> AudioRtpReceiver::stream_ids() const {
+  std::vector<std::string> stream_ids(streams_.size());
+  for (size_t i = 0; i < streams_.size(); ++i)
+    stream_ids[i] = streams_[i]->id();
+  return stream_ids;
+}
+
+RtpParameters AudioRtpReceiver::GetParameters() const {
+  if (!media_channel_ || !ssrc_ || stopped_) {
+    return RtpParameters();
+  }
+  return worker_thread_->Invoke<RtpParameters>(RTC_FROM_HERE, [&] {
+    return media_channel_->GetRtpReceiveParameters(*ssrc_);
+  });
+}
+
+bool AudioRtpReceiver::SetParameters(const RtpParameters& parameters) {
+  TRACE_EVENT0("webrtc", "AudioRtpReceiver::SetParameters");
+  if (!media_channel_ || !ssrc_ || stopped_) {
+    return false;
+  }
+  return worker_thread_->Invoke<bool>(RTC_FROM_HERE, [&] {
+    return media_channel_->SetRtpReceiveParameters(*ssrc_, parameters);
+  });
+}
+
+void AudioRtpReceiver::SetFrameDecryptor(
+    rtc::scoped_refptr<FrameDecryptorInterface> frame_decryptor) {
+  frame_decryptor_ = std::move(frame_decryptor);
+  // Special Case: Set the frame decryptor to any value on any existing channel.
+  if (media_channel_ && ssrc_.has_value() && !stopped_) {
+    worker_thread_->Invoke<void>(RTC_FROM_HERE, [&] {
+      media_channel_->SetFrameDecryptor(*ssrc_, frame_decryptor_);
+    });
+  }
+}
+
+rtc::scoped_refptr<FrameDecryptorInterface>
+AudioRtpReceiver::GetFrameDecryptor() const {
+  return frame_decryptor_;
+}
+
+void AudioRtpReceiver::Stop() {
+  // TODO(deadbeef): Need to do more here to fully stop receiving packets.
+  if (stopped_) {
+    return;
+  }
+  if (media_channel_ && ssrc_) {
+    // Allow that SetOutputVolume fail. This is the normal case when the
+    // underlying media channel has already been deleted.
+    SetOutputVolume(0.0);
+  }
+  stopped_ = true;
+}
+
+void AudioRtpReceiver::SetupMediaChannel(uint32_t ssrc) {
+  if (!media_channel_) {
+    RTC_LOG(LS_ERROR)
+        << "AudioRtpReceiver::SetupMediaChannel: No audio channel exists.";
+    return;
+  }
+  if (ssrc_ == ssrc) {
+    return;
+  }
+  if (ssrc_) {
+    source_->Stop(media_channel_, *ssrc_);
+  }
+  ssrc_ = ssrc;
+  source_->Start(media_channel_, *ssrc_);
+  Reconfigure();
+}
+
+void AudioRtpReceiver::set_stream_ids(std::vector<std::string> stream_ids) {
+  SetStreams(CreateStreamsFromIds(std::move(stream_ids)));
+}
+
+void AudioRtpReceiver::SetStreams(
+    const std::vector<rtc::scoped_refptr<MediaStreamInterface>>& streams) {
+  // Remove remote track from any streams that are going away.
+  for (const auto& existing_stream : streams_) {
+    bool removed = true;
+    for (const auto& stream : streams) {
+      if (existing_stream->id() == stream->id()) {
+        RTC_DCHECK_EQ(existing_stream.get(), stream.get());
+        removed = false;
+        break;
+      }
+    }
+    if (removed) {
+      existing_stream->RemoveTrack(track_);
+    }
+  }
+  // Add remote track to any streams that are new.
+  for (const auto& stream : streams) {
+    bool added = true;
+    for (const auto& existing_stream : streams_) {
+      if (stream->id() == existing_stream->id()) {
+        RTC_DCHECK_EQ(stream.get(), existing_stream.get());
+        added = false;
+        break;
+      }
+    }
+    if (added) {
+      stream->AddTrack(track_);
+    }
+  }
+  streams_ = streams;
+}
+
+std::vector<RtpSource> AudioRtpReceiver::GetSources() const {
+  if (!media_channel_ || !ssrc_ || stopped_) {
+    return {};
+  }
+  return worker_thread_->Invoke<std::vector<RtpSource>>(
+      RTC_FROM_HERE, [&] { return media_channel_->GetSources(*ssrc_); });
+}
+
+void AudioRtpReceiver::Reconfigure() {
+  RTC_DCHECK(!stopped_);
+  if (!media_channel_ || !ssrc_) {
+    RTC_LOG(LS_ERROR)
+        << "AudioRtpReceiver::Reconfigure: No audio channel exists.";
+    return;
+  }
+  if (!SetOutputVolume(track_->enabled() ? cached_volume_ : 0)) {
+    RTC_NOTREACHED();
+  }
+  // Reattach the frame decryptor if we were reconfigured.
+  MaybeAttachFrameDecryptorToMediaChannel(
+      ssrc_, worker_thread_, frame_decryptor_, media_channel_, stopped_);
+}
+
+void AudioRtpReceiver::SetObserver(RtpReceiverObserverInterface* observer) {
+  observer_ = observer;
+  // Deliver any notifications the observer may have missed by being set late.
+  if (received_first_packet_ && observer_) {
+    observer_->OnFirstPacketReceived(media_type());
+  }
+}
+
+void AudioRtpReceiver::SetMediaChannel(cricket::MediaChannel* media_channel) {
+  RTC_DCHECK(media_channel == nullptr ||
+             media_channel->media_type() == media_type());
+  media_channel_ = static_cast<cricket::VoiceMediaChannel*>(media_channel);
+}
+
+void AudioRtpReceiver::NotifyFirstPacketReceived() {
+  if (observer_) {
+    observer_->OnFirstPacketReceived(media_type());
+  }
+  received_first_packet_ = true;
+}
+
+}  // namespace webrtc
diff --git a/pc/audio_rtp_receiver.h b/pc/audio_rtp_receiver.h
new file mode 100644
index 0000000..82c8d88
--- /dev/null
+++ b/pc/audio_rtp_receiver.h
@@ -0,0 +1,126 @@
+/*
+ *  Copyright 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef PC_AUDIO_RTP_RECEIVER_H_
+#define PC_AUDIO_RTP_RECEIVER_H_
+
+#include <stdint.h>
+#include <string>
+#include <vector>
+
+#include "absl/types/optional.h"
+#include "api/crypto/frame_decryptor_interface.h"
+#include "api/media_stream_interface.h"
+#include "api/media_types.h"
+#include "api/rtp_parameters.h"
+#include "api/scoped_refptr.h"
+#include "media/base/media_channel.h"
+#include "pc/remote_audio_source.h"
+#include "pc/rtp_receiver.h"
+#include "rtc_base/ref_counted_object.h"
+#include "rtc_base/thread.h"
+
+namespace webrtc {
+
+class AudioRtpReceiver : public ObserverInterface,
+                         public AudioSourceInterface::AudioObserver,
+                         public rtc::RefCountedObject<RtpReceiverInternal> {
+ public:
+  AudioRtpReceiver(rtc::Thread* worker_thread,
+                   std::string receiver_id,
+                   std::vector<std::string> stream_ids);
+  // TODO(https://crbug.com/webrtc/9480): Remove this when streams() is removed.
+  AudioRtpReceiver(
+      rtc::Thread* worker_thread,
+      const std::string& receiver_id,
+      const std::vector<rtc::scoped_refptr<MediaStreamInterface>>& streams);
+  virtual ~AudioRtpReceiver();
+
+  // ObserverInterface implementation
+  void OnChanged() override;
+
+  // AudioSourceInterface::AudioObserver implementation
+  void OnSetVolume(double volume) override;
+
+  rtc::scoped_refptr<AudioTrackInterface> audio_track() const {
+    return track_.get();
+  }
+
+  // RtpReceiverInterface implementation
+  rtc::scoped_refptr<MediaStreamTrackInterface> track() const override {
+    return track_.get();
+  }
+  rtc::scoped_refptr<DtlsTransportInterface> dtls_transport() const override {
+    return dtls_transport_;
+  }
+  std::vector<std::string> stream_ids() const override;
+  std::vector<rtc::scoped_refptr<MediaStreamInterface>> streams()
+      const override {
+    return streams_;
+  }
+
+  cricket::MediaType media_type() const override {
+    return cricket::MEDIA_TYPE_AUDIO;
+  }
+
+  std::string id() const override { return id_; }
+
+  RtpParameters GetParameters() const override;
+  bool SetParameters(const RtpParameters& parameters) override;
+
+  void SetFrameDecryptor(
+      rtc::scoped_refptr<FrameDecryptorInterface> frame_decryptor) override;
+
+  rtc::scoped_refptr<FrameDecryptorInterface> GetFrameDecryptor()
+      const override;
+
+  // RtpReceiverInternal implementation.
+  void Stop() override;
+  void SetupMediaChannel(uint32_t ssrc) override;
+  uint32_t ssrc() const override { return ssrc_.value_or(0); }
+  void NotifyFirstPacketReceived() override;
+  void set_stream_ids(std::vector<std::string> stream_ids) override;
+  void set_transport(
+      rtc::scoped_refptr<DtlsTransportInterface> dtls_transport) override {
+    dtls_transport_ = dtls_transport;
+  }
+  void SetStreams(const std::vector<rtc::scoped_refptr<MediaStreamInterface>>&
+                      streams) override;
+  void SetObserver(RtpReceiverObserverInterface* observer) override;
+
+  void SetMediaChannel(cricket::MediaChannel* media_channel) override;
+
+  std::vector<RtpSource> GetSources() const override;
+  int AttachmentId() const override { return attachment_id_; }
+
+ private:
+  void Reconfigure();
+  bool SetOutputVolume(double volume);
+
+  rtc::Thread* const worker_thread_;
+  const std::string id_;
+  const rtc::scoped_refptr<RemoteAudioSource> source_;
+  const rtc::scoped_refptr<AudioTrackInterface> track_;
+  cricket::VoiceMediaChannel* media_channel_ = nullptr;
+  absl::optional<uint32_t> ssrc_;
+  std::vector<rtc::scoped_refptr<MediaStreamInterface>> streams_;
+  bool cached_track_enabled_;
+  double cached_volume_ = 1;
+  bool stopped_ = false;
+  RtpReceiverObserverInterface* observer_ = nullptr;
+  bool received_first_packet_ = false;
+  int attachment_id_ = 0;
+  rtc::scoped_refptr<FrameDecryptorInterface> frame_decryptor_;
+  rtc::scoped_refptr<DtlsTransportInterface> dtls_transport_;
+};
+
+}  // namespace webrtc
+
+#endif  // PC_AUDIO_RTP_RECEIVER_H_
diff --git a/pc/peer_connection.cc b/pc/peer_connection.cc
index 759bf47..0fe6732 100644
--- a/pc/peer_connection.cc
+++ b/pc/peer_connection.cc
@@ -30,6 +30,7 @@
 #include "logging/rtc_event_log/output/rtc_event_log_output_file.h"
 #include "logging/rtc_event_log/rtc_event_log.h"
 #include "media/sctp/sctp_transport.h"
+#include "pc/audio_rtp_receiver.h"
 #include "pc/audio_track.h"
 #include "pc/channel.h"
 #include "pc/channel_manager.h"
@@ -43,6 +44,7 @@
 #include "pc/sctp_utils.h"
 #include "pc/sdp_utils.h"
 #include "pc/stream_collection.h"
+#include "pc/video_rtp_receiver.h"
 #include "pc/video_track.h"
 #include "rtc_base/bind.h"
 #include "rtc_base/checks.h"
diff --git a/pc/rtp_receiver.cc b/pc/rtp_receiver.cc
index 60f5ea8..0b800c1 100644
--- a/pc/rtp_receiver.cc
+++ b/pc/rtp_receiver.cc
@@ -16,10 +16,7 @@
 
 #include "api/media_stream_proxy.h"
 #include "api/media_stream_track_proxy.h"
-#include "api/video_track_source_proxy.h"
-#include "pc/audio_track.h"
 #include "pc/media_stream.h"
-#include "pc/video_track.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/location.h"
 #include "rtc_base/logging.h"
@@ -27,17 +24,15 @@
 
 namespace webrtc {
 
-namespace {
-
 // This function is only expected to be called on the signalling thread.
-int GenerateUniqueId() {
+int RtpReceiverInternal::GenerateUniqueId() {
   static int g_unique_id = 0;
 
   return ++g_unique_id;
 }
 
-std::vector<rtc::scoped_refptr<MediaStreamInterface>> CreateStreamsFromIds(
-    std::vector<std::string> stream_ids) {
+std::vector<rtc::scoped_refptr<MediaStreamInterface>>
+RtpReceiverInternal::CreateStreamsFromIds(std::vector<std::string> stream_ids) {
   std::vector<rtc::scoped_refptr<MediaStreamInterface>> streams(
       stream_ids.size());
   for (size_t i = 0; i < stream_ids.size(); ++i) {
@@ -50,7 +45,7 @@
 // Attempt to attach the frame decryptor to the current media channel on the
 // correct worker thread only if both the media channel exists and a ssrc has
 // been allocated to the stream.
-void MaybeAttachFrameDecryptorToMediaChannel(
+void RtpReceiverInternal::MaybeAttachFrameDecryptorToMediaChannel(
     const absl::optional<uint32_t>& ssrc,
     rtc::Thread* worker_thread,
     rtc::scoped_refptr<webrtc::FrameDecryptorInterface> frame_decryptor,
@@ -63,410 +58,4 @@
   }
 }
 
-}  // namespace
-
-AudioRtpReceiver::AudioRtpReceiver(rtc::Thread* worker_thread,
-                                   std::string receiver_id,
-                                   std::vector<std::string> stream_ids)
-    : AudioRtpReceiver(worker_thread,
-                       receiver_id,
-                       CreateStreamsFromIds(std::move(stream_ids))) {}
-
-AudioRtpReceiver::AudioRtpReceiver(
-    rtc::Thread* worker_thread,
-    const std::string& receiver_id,
-    const std::vector<rtc::scoped_refptr<MediaStreamInterface>>& streams)
-    : worker_thread_(worker_thread),
-      id_(receiver_id),
-      source_(new rtc::RefCountedObject<RemoteAudioSource>(worker_thread)),
-      track_(AudioTrackProxy::Create(rtc::Thread::Current(),
-                                     AudioTrack::Create(receiver_id, source_))),
-      cached_track_enabled_(track_->enabled()),
-      attachment_id_(GenerateUniqueId()) {
-  RTC_DCHECK(worker_thread_);
-  RTC_DCHECK(track_->GetSource()->remote());
-  track_->RegisterObserver(this);
-  track_->GetSource()->RegisterAudioObserver(this);
-  SetStreams(streams);
-}
-
-AudioRtpReceiver::~AudioRtpReceiver() {
-  track_->GetSource()->UnregisterAudioObserver(this);
-  track_->UnregisterObserver(this);
-  Stop();
-}
-
-void AudioRtpReceiver::OnChanged() {
-  if (cached_track_enabled_ != track_->enabled()) {
-    cached_track_enabled_ = track_->enabled();
-    Reconfigure();
-  }
-}
-
-bool AudioRtpReceiver::SetOutputVolume(double volume) {
-  RTC_DCHECK_GE(volume, 0.0);
-  RTC_DCHECK_LE(volume, 10.0);
-  RTC_DCHECK(media_channel_);
-  RTC_DCHECK(ssrc_);
-  return worker_thread_->Invoke<bool>(RTC_FROM_HERE, [&] {
-    return media_channel_->SetOutputVolume(*ssrc_, volume);
-  });
-}
-
-void AudioRtpReceiver::OnSetVolume(double volume) {
-  RTC_DCHECK_GE(volume, 0);
-  RTC_DCHECK_LE(volume, 10);
-  cached_volume_ = volume;
-  if (!media_channel_ || !ssrc_) {
-    RTC_LOG(LS_ERROR)
-        << "AudioRtpReceiver::OnSetVolume: No audio channel exists.";
-    return;
-  }
-  // When the track is disabled, the volume of the source, which is the
-  // corresponding WebRtc Voice Engine channel will be 0. So we do not allow
-  // setting the volume to the source when the track is disabled.
-  if (!stopped_ && track_->enabled()) {
-    if (!SetOutputVolume(cached_volume_)) {
-      RTC_NOTREACHED();
-    }
-  }
-}
-
-std::vector<std::string> AudioRtpReceiver::stream_ids() const {
-  std::vector<std::string> stream_ids(streams_.size());
-  for (size_t i = 0; i < streams_.size(); ++i)
-    stream_ids[i] = streams_[i]->id();
-  return stream_ids;
-}
-
-RtpParameters AudioRtpReceiver::GetParameters() const {
-  if (!media_channel_ || !ssrc_ || stopped_) {
-    return RtpParameters();
-  }
-  return worker_thread_->Invoke<RtpParameters>(RTC_FROM_HERE, [&] {
-    return media_channel_->GetRtpReceiveParameters(*ssrc_);
-  });
-}
-
-bool AudioRtpReceiver::SetParameters(const RtpParameters& parameters) {
-  TRACE_EVENT0("webrtc", "AudioRtpReceiver::SetParameters");
-  if (!media_channel_ || !ssrc_ || stopped_) {
-    return false;
-  }
-  return worker_thread_->Invoke<bool>(RTC_FROM_HERE, [&] {
-    return media_channel_->SetRtpReceiveParameters(*ssrc_, parameters);
-  });
-}
-
-void AudioRtpReceiver::SetFrameDecryptor(
-    rtc::scoped_refptr<FrameDecryptorInterface> frame_decryptor) {
-  frame_decryptor_ = std::move(frame_decryptor);
-  // Special Case: Set the frame decryptor to any value on any existing channel.
-  if (media_channel_ && ssrc_.has_value() && !stopped_) {
-    worker_thread_->Invoke<void>(RTC_FROM_HERE, [&] {
-      media_channel_->SetFrameDecryptor(*ssrc_, frame_decryptor_);
-    });
-  }
-}
-
-rtc::scoped_refptr<FrameDecryptorInterface>
-AudioRtpReceiver::GetFrameDecryptor() const {
-  return frame_decryptor_;
-}
-
-void AudioRtpReceiver::Stop() {
-  // TODO(deadbeef): Need to do more here to fully stop receiving packets.
-  if (stopped_) {
-    return;
-  }
-  if (media_channel_ && ssrc_) {
-    // Allow that SetOutputVolume fail. This is the normal case when the
-    // underlying media channel has already been deleted.
-    SetOutputVolume(0.0);
-  }
-  stopped_ = true;
-}
-
-void AudioRtpReceiver::SetupMediaChannel(uint32_t ssrc) {
-  if (!media_channel_) {
-    RTC_LOG(LS_ERROR)
-        << "AudioRtpReceiver::SetupMediaChannel: No audio channel exists.";
-    return;
-  }
-  if (ssrc_ == ssrc) {
-    return;
-  }
-  if (ssrc_) {
-    source_->Stop(media_channel_, *ssrc_);
-  }
-  ssrc_ = ssrc;
-  source_->Start(media_channel_, *ssrc_);
-  Reconfigure();
-}
-
-void AudioRtpReceiver::set_stream_ids(std::vector<std::string> stream_ids) {
-  SetStreams(CreateStreamsFromIds(std::move(stream_ids)));
-}
-
-void AudioRtpReceiver::SetStreams(
-    const std::vector<rtc::scoped_refptr<MediaStreamInterface>>& streams) {
-  // Remove remote track from any streams that are going away.
-  for (const auto& existing_stream : streams_) {
-    bool removed = true;
-    for (const auto& stream : streams) {
-      if (existing_stream->id() == stream->id()) {
-        RTC_DCHECK_EQ(existing_stream.get(), stream.get());
-        removed = false;
-        break;
-      }
-    }
-    if (removed) {
-      existing_stream->RemoveTrack(track_);
-    }
-  }
-  // Add remote track to any streams that are new.
-  for (const auto& stream : streams) {
-    bool added = true;
-    for (const auto& existing_stream : streams_) {
-      if (stream->id() == existing_stream->id()) {
-        RTC_DCHECK_EQ(stream.get(), existing_stream.get());
-        added = false;
-        break;
-      }
-    }
-    if (added) {
-      stream->AddTrack(track_);
-    }
-  }
-  streams_ = streams;
-}
-
-std::vector<RtpSource> AudioRtpReceiver::GetSources() const {
-  if (!media_channel_ || !ssrc_ || stopped_) {
-    return {};
-  }
-  return worker_thread_->Invoke<std::vector<RtpSource>>(
-      RTC_FROM_HERE, [&] { return media_channel_->GetSources(*ssrc_); });
-}
-
-void AudioRtpReceiver::Reconfigure() {
-  RTC_DCHECK(!stopped_);
-  if (!media_channel_ || !ssrc_) {
-    RTC_LOG(LS_ERROR)
-        << "AudioRtpReceiver::Reconfigure: No audio channel exists.";
-    return;
-  }
-  if (!SetOutputVolume(track_->enabled() ? cached_volume_ : 0)) {
-    RTC_NOTREACHED();
-  }
-  // Reattach the frame decryptor if we were reconfigured.
-  MaybeAttachFrameDecryptorToMediaChannel(
-      ssrc_, worker_thread_, frame_decryptor_, media_channel_, stopped_);
-}
-
-void AudioRtpReceiver::SetObserver(RtpReceiverObserverInterface* observer) {
-  observer_ = observer;
-  // Deliver any notifications the observer may have missed by being set late.
-  if (received_first_packet_ && observer_) {
-    observer_->OnFirstPacketReceived(media_type());
-  }
-}
-
-void AudioRtpReceiver::SetMediaChannel(cricket::MediaChannel* media_channel) {
-  RTC_DCHECK(media_channel == nullptr ||
-             media_channel->media_type() == media_type());
-  media_channel_ = static_cast<cricket::VoiceMediaChannel*>(media_channel);
-}
-
-void AudioRtpReceiver::NotifyFirstPacketReceived() {
-  if (observer_) {
-    observer_->OnFirstPacketReceived(media_type());
-  }
-  received_first_packet_ = true;
-}
-
-VideoRtpReceiver::VideoRtpReceiver(rtc::Thread* worker_thread,
-                                   std::string receiver_id,
-                                   std::vector<std::string> stream_ids)
-    : VideoRtpReceiver(worker_thread,
-                       receiver_id,
-                       CreateStreamsFromIds(std::move(stream_ids))) {}
-
-VideoRtpReceiver::VideoRtpReceiver(
-    rtc::Thread* worker_thread,
-    const std::string& receiver_id,
-    const std::vector<rtc::scoped_refptr<MediaStreamInterface>>& streams)
-    : worker_thread_(worker_thread),
-      id_(receiver_id),
-      source_(new RefCountedObject<VideoRtpTrackSource>()),
-      track_(VideoTrackProxy::Create(
-          rtc::Thread::Current(),
-          worker_thread,
-          VideoTrack::Create(
-              receiver_id,
-              VideoTrackSourceProxy::Create(rtc::Thread::Current(),
-                                            worker_thread,
-                                            source_),
-              worker_thread))),
-      attachment_id_(GenerateUniqueId()) {
-  RTC_DCHECK(worker_thread_);
-  SetStreams(streams);
-  source_->SetState(MediaSourceInterface::kLive);
-}
-
-VideoRtpReceiver::~VideoRtpReceiver() {
-  // Since cricket::VideoRenderer is not reference counted,
-  // we need to remove it from the channel before we are deleted.
-  Stop();
-}
-
-std::vector<std::string> VideoRtpReceiver::stream_ids() const {
-  std::vector<std::string> stream_ids(streams_.size());
-  for (size_t i = 0; i < streams_.size(); ++i)
-    stream_ids[i] = streams_[i]->id();
-  return stream_ids;
-}
-
-bool VideoRtpReceiver::SetSink(rtc::VideoSinkInterface<VideoFrame>* sink) {
-  RTC_DCHECK(media_channel_);
-  RTC_DCHECK(ssrc_);
-  return worker_thread_->Invoke<bool>(
-      RTC_FROM_HERE, [&] { return media_channel_->SetSink(*ssrc_, sink); });
-}
-
-RtpParameters VideoRtpReceiver::GetParameters() const {
-  if (!media_channel_ || !ssrc_ || stopped_) {
-    return RtpParameters();
-  }
-  return worker_thread_->Invoke<RtpParameters>(RTC_FROM_HERE, [&] {
-    return media_channel_->GetRtpReceiveParameters(*ssrc_);
-  });
-}
-
-bool VideoRtpReceiver::SetParameters(const RtpParameters& parameters) {
-  TRACE_EVENT0("webrtc", "VideoRtpReceiver::SetParameters");
-  if (!media_channel_ || !ssrc_ || stopped_) {
-    return false;
-  }
-  return worker_thread_->Invoke<bool>(RTC_FROM_HERE, [&] {
-    return media_channel_->SetRtpReceiveParameters(*ssrc_, parameters);
-  });
-}
-
-void VideoRtpReceiver::SetFrameDecryptor(
-    rtc::scoped_refptr<FrameDecryptorInterface> frame_decryptor) {
-  frame_decryptor_ = std::move(frame_decryptor);
-  // Special Case: Set the frame decryptor to any value on any existing channel.
-  if (media_channel_ && ssrc_.has_value() && !stopped_) {
-    worker_thread_->Invoke<void>(RTC_FROM_HERE, [&] {
-      media_channel_->SetFrameDecryptor(*ssrc_, frame_decryptor_);
-    });
-  }
-}
-
-rtc::scoped_refptr<FrameDecryptorInterface>
-VideoRtpReceiver::GetFrameDecryptor() const {
-  return frame_decryptor_;
-}
-
-void VideoRtpReceiver::Stop() {
-  // TODO(deadbeef): Need to do more here to fully stop receiving packets.
-  if (stopped_) {
-    return;
-  }
-  source_->SetState(MediaSourceInterface::kEnded);
-  if (!media_channel_ || !ssrc_) {
-    RTC_LOG(LS_WARNING) << "VideoRtpReceiver::Stop: No video channel exists.";
-  } else {
-    // Allow that SetSink fail. This is the normal case when the underlying
-    // media channel has already been deleted.
-    SetSink(nullptr);
-  }
-  stopped_ = true;
-}
-
-void VideoRtpReceiver::SetupMediaChannel(uint32_t ssrc) {
-  if (!media_channel_) {
-    RTC_LOG(LS_ERROR)
-        << "VideoRtpReceiver::SetupMediaChannel: No video channel exists.";
-  }
-  if (ssrc_ == ssrc) {
-    return;
-  }
-  if (ssrc_) {
-    SetSink(nullptr);
-  }
-  ssrc_ = ssrc;
-  SetSink(source_->sink());
-  // Attach any existing frame decryptor to the media channel.
-  MaybeAttachFrameDecryptorToMediaChannel(
-      ssrc_, worker_thread_, frame_decryptor_, media_channel_, stopped_);
-}
-
-void VideoRtpReceiver::set_stream_ids(std::vector<std::string> stream_ids) {
-  SetStreams(CreateStreamsFromIds(std::move(stream_ids)));
-}
-
-void VideoRtpReceiver::SetStreams(
-    const std::vector<rtc::scoped_refptr<MediaStreamInterface>>& streams) {
-  // Remove remote track from any streams that are going away.
-  for (const auto& existing_stream : streams_) {
-    bool removed = true;
-    for (const auto& stream : streams) {
-      if (existing_stream->id() == stream->id()) {
-        RTC_DCHECK_EQ(existing_stream.get(), stream.get());
-        removed = false;
-        break;
-      }
-    }
-    if (removed) {
-      existing_stream->RemoveTrack(track_);
-    }
-  }
-  // Add remote track to any streams that are new.
-  for (const auto& stream : streams) {
-    bool added = true;
-    for (const auto& existing_stream : streams_) {
-      if (stream->id() == existing_stream->id()) {
-        RTC_DCHECK_EQ(stream.get(), existing_stream.get());
-        added = false;
-        break;
-      }
-    }
-    if (added) {
-      stream->AddTrack(track_);
-    }
-  }
-  streams_ = streams;
-}
-
-void VideoRtpReceiver::SetObserver(RtpReceiverObserverInterface* observer) {
-  observer_ = observer;
-  // Deliver any notifications the observer may have missed by being set late.
-  if (received_first_packet_ && observer_) {
-    observer_->OnFirstPacketReceived(media_type());
-  }
-}
-
-void VideoRtpReceiver::SetMediaChannel(cricket::MediaChannel* media_channel) {
-  RTC_DCHECK(media_channel == nullptr ||
-             media_channel->media_type() == media_type());
-  media_channel_ = static_cast<cricket::VideoMediaChannel*>(media_channel);
-}
-
-void VideoRtpReceiver::NotifyFirstPacketReceived() {
-  if (observer_) {
-    observer_->OnFirstPacketReceived(media_type());
-  }
-  received_first_packet_ = true;
-}
-
-std::vector<RtpSource> VideoRtpReceiver::GetSources() const {
-  if (!media_channel_ || !ssrc_ || stopped_) {
-    return {};
-  }
-  return worker_thread_->Invoke<std::vector<RtpSource>>(
-      RTC_FROM_HERE, [&] { return media_channel_->GetSources(*ssrc_); });
-}
-
 }  // namespace webrtc
diff --git a/pc/rtp_receiver.h b/pc/rtp_receiver.h
index 469502c..e56c859 100644
--- a/pc/rtp_receiver.h
+++ b/pc/rtp_receiver.h
@@ -31,7 +31,6 @@
 #include "api/video/video_source_interface.h"
 #include "media/base/media_channel.h"
 #include "media/base/video_broadcaster.h"
-#include "pc/remote_audio_source.h"
 #include "pc/video_track_source.h"
 #include "rtc_base/ref_counted_object.h"
 #include "rtc_base/thread.h"
@@ -78,203 +77,19 @@
   // otherwise remains constant. Used to generate IDs for stats.
   // The special value zero means that no track is attached.
   virtual int AttachmentId() const = 0;
-};
 
-class AudioRtpReceiver : public ObserverInterface,
-                         public AudioSourceInterface::AudioObserver,
-                         public rtc::RefCountedObject<RtpReceiverInternal> {
- public:
-  AudioRtpReceiver(rtc::Thread* worker_thread,
-                   std::string receiver_id,
-                   std::vector<std::string> stream_ids);
-  // TODO(https://crbug.com/webrtc/9480): Remove this when streams() is removed.
-  AudioRtpReceiver(
+ protected:
+  static int GenerateUniqueId();
+
+  static std::vector<rtc::scoped_refptr<MediaStreamInterface>>
+  CreateStreamsFromIds(std::vector<std::string> stream_ids);
+
+  static void MaybeAttachFrameDecryptorToMediaChannel(
+      const absl::optional<uint32_t>& ssrc,
       rtc::Thread* worker_thread,
-      const std::string& receiver_id,
-      const std::vector<rtc::scoped_refptr<MediaStreamInterface>>& streams);
-  virtual ~AudioRtpReceiver();
-
-  // ObserverInterface implementation
-  void OnChanged() override;
-
-  // AudioSourceInterface::AudioObserver implementation
-  void OnSetVolume(double volume) override;
-
-  rtc::scoped_refptr<AudioTrackInterface> audio_track() const {
-    return track_.get();
-  }
-
-  // RtpReceiverInterface implementation
-  rtc::scoped_refptr<MediaStreamTrackInterface> track() const override {
-    return track_.get();
-  }
-  rtc::scoped_refptr<DtlsTransportInterface> dtls_transport() const override {
-    return dtls_transport_;
-  }
-  std::vector<std::string> stream_ids() const override;
-  std::vector<rtc::scoped_refptr<MediaStreamInterface>> streams()
-      const override {
-    return streams_;
-  }
-
-  cricket::MediaType media_type() const override {
-    return cricket::MEDIA_TYPE_AUDIO;
-  }
-
-  std::string id() const override { return id_; }
-
-  RtpParameters GetParameters() const override;
-  bool SetParameters(const RtpParameters& parameters) override;
-
-  void SetFrameDecryptor(
-      rtc::scoped_refptr<FrameDecryptorInterface> frame_decryptor) override;
-
-  rtc::scoped_refptr<FrameDecryptorInterface> GetFrameDecryptor()
-      const override;
-
-  // RtpReceiverInternal implementation.
-  void Stop() override;
-  void SetupMediaChannel(uint32_t ssrc) override;
-  uint32_t ssrc() const override { return ssrc_.value_or(0); }
-  void NotifyFirstPacketReceived() override;
-  void set_stream_ids(std::vector<std::string> stream_ids) override;
-  void set_transport(
-      rtc::scoped_refptr<DtlsTransportInterface> dtls_transport) override {
-    dtls_transport_ = dtls_transport;
-  }
-  void SetStreams(const std::vector<rtc::scoped_refptr<MediaStreamInterface>>&
-                      streams) override;
-  void SetObserver(RtpReceiverObserverInterface* observer) override;
-
-  void SetMediaChannel(cricket::MediaChannel* media_channel) override;
-
-  std::vector<RtpSource> GetSources() const override;
-  int AttachmentId() const override { return attachment_id_; }
-
- private:
-  void Reconfigure();
-  bool SetOutputVolume(double volume);
-
-  rtc::Thread* const worker_thread_;
-  const std::string id_;
-  const rtc::scoped_refptr<RemoteAudioSource> source_;
-  const rtc::scoped_refptr<AudioTrackInterface> track_;
-  cricket::VoiceMediaChannel* media_channel_ = nullptr;
-  absl::optional<uint32_t> ssrc_;
-  std::vector<rtc::scoped_refptr<MediaStreamInterface>> streams_;
-  bool cached_track_enabled_;
-  double cached_volume_ = 1;
-  bool stopped_ = false;
-  RtpReceiverObserverInterface* observer_ = nullptr;
-  bool received_first_packet_ = false;
-  int attachment_id_ = 0;
-  rtc::scoped_refptr<FrameDecryptorInterface> frame_decryptor_;
-  rtc::scoped_refptr<DtlsTransportInterface> dtls_transport_;
-};
-
-class VideoRtpReceiver : public rtc::RefCountedObject<RtpReceiverInternal> {
- public:
-  // An SSRC of 0 will create a receiver that will match the first SSRC it
-  // sees.
-  VideoRtpReceiver(rtc::Thread* worker_thread,
-                   std::string receiver_id,
-                   std::vector<std::string> streams_ids);
-  // TODO(hbos): Remove this when streams() is removed.
-  // https://crbug.com/webrtc/9480
-  VideoRtpReceiver(
-      rtc::Thread* worker_thread,
-      const std::string& receiver_id,
-      const std::vector<rtc::scoped_refptr<MediaStreamInterface>>& streams);
-
-  virtual ~VideoRtpReceiver();
-
-  rtc::scoped_refptr<VideoTrackInterface> video_track() const {
-    return track_.get();
-  }
-
-  // RtpReceiverInterface implementation
-  rtc::scoped_refptr<MediaStreamTrackInterface> track() const override {
-    return track_.get();
-  }
-  rtc::scoped_refptr<DtlsTransportInterface> dtls_transport() const override {
-    return dtls_transport_;
-  }
-  std::vector<std::string> stream_ids() const override;
-  std::vector<rtc::scoped_refptr<MediaStreamInterface>> streams()
-      const override {
-    return streams_;
-  }
-
-  cricket::MediaType media_type() const override {
-    return cricket::MEDIA_TYPE_VIDEO;
-  }
-
-  std::string id() const override { return id_; }
-
-  RtpParameters GetParameters() const override;
-  bool SetParameters(const RtpParameters& parameters) override;
-
-  void SetFrameDecryptor(
-      rtc::scoped_refptr<FrameDecryptorInterface> frame_decryptor) override;
-
-  rtc::scoped_refptr<FrameDecryptorInterface> GetFrameDecryptor()
-      const override;
-
-  // RtpReceiverInternal implementation.
-  void Stop() override;
-  void SetupMediaChannel(uint32_t ssrc) override;
-  uint32_t ssrc() const override { return ssrc_.value_or(0); }
-  void NotifyFirstPacketReceived() override;
-  void set_stream_ids(std::vector<std::string> stream_ids) override;
-  void set_transport(
-      rtc::scoped_refptr<DtlsTransportInterface> dtls_transport) override {
-    dtls_transport_ = dtls_transport;
-  }
-  void SetStreams(const std::vector<rtc::scoped_refptr<MediaStreamInterface>>&
-                      streams) override;
-
-  void SetObserver(RtpReceiverObserverInterface* observer) override;
-
-  void SetMediaChannel(cricket::MediaChannel* media_channel) override;
-
-  int AttachmentId() const override { return attachment_id_; }
-
-  std::vector<RtpSource> GetSources() const override;
-
- private:
-  class VideoRtpTrackSource : public VideoTrackSource {
-   public:
-    VideoRtpTrackSource() : VideoTrackSource(true /* remote */) {}
-
-    rtc::VideoSourceInterface<VideoFrame>* source() override {
-      return &broadcaster_;
-    }
-    rtc::VideoSinkInterface<VideoFrame>* sink() { return &broadcaster_; }
-
-   private:
-    // |broadcaster_| is needed since the decoder can only handle one sink.
-    // It might be better if the decoder can handle multiple sinks and consider
-    // the VideoSinkWants.
-    rtc::VideoBroadcaster broadcaster_;
-  };
-
-  bool SetSink(rtc::VideoSinkInterface<VideoFrame>* sink);
-
-  rtc::Thread* const worker_thread_;
-  const std::string id_;
-  cricket::VideoMediaChannel* media_channel_ = nullptr;
-  absl::optional<uint32_t> ssrc_;
-  // |source_| is held here to be able to change the state of the source when
-  // the VideoRtpReceiver is stopped.
-  rtc::scoped_refptr<VideoRtpTrackSource> source_;
-  rtc::scoped_refptr<VideoTrackInterface> track_;
-  std::vector<rtc::scoped_refptr<MediaStreamInterface>> streams_;
-  bool stopped_ = false;
-  RtpReceiverObserverInterface* observer_ = nullptr;
-  bool received_first_packet_ = false;
-  int attachment_id_ = 0;
-  rtc::scoped_refptr<FrameDecryptorInterface> frame_decryptor_;
-  rtc::scoped_refptr<DtlsTransportInterface> dtls_transport_;
+      rtc::scoped_refptr<webrtc::FrameDecryptorInterface> frame_decryptor,
+      cricket::MediaChannel* media_channel,
+      bool stopped);
 };
 
 }  // namespace webrtc
diff --git a/pc/rtp_sender_receiver_unittest.cc b/pc/rtp_sender_receiver_unittest.cc
index ba0e0b5..e3a8d5e 100644
--- a/pc/rtp_sender_receiver_unittest.cc
+++ b/pc/rtp_sender_receiver_unittest.cc
@@ -41,6 +41,7 @@
 #include "p2p/base/dtls_transport_internal.h"
 #include "p2p/base/fake_dtls_transport.h"
 #include "p2p/base/p2p_constants.h"
+#include "pc/audio_rtp_receiver.h"
 #include "pc/audio_track.h"
 #include "pc/channel.h"
 #include "pc/channel_manager.h"
@@ -51,6 +52,7 @@
 #include "pc/rtp_sender.h"
 #include "pc/rtp_transport_internal.h"
 #include "pc/test/fake_video_track_source.h"
+#include "pc/video_rtp_receiver.h"
 #include "pc/video_track.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/gunit.h"
diff --git a/pc/rtp_transceiver.h b/pc/rtp_transceiver.h
index 7c234ce..fd74fde 100644
--- a/pc/rtp_transceiver.h
+++ b/pc/rtp_transceiver.h
@@ -15,6 +15,7 @@
 #include <vector>
 
 #include "api/rtp_transceiver_interface.h"
+#include "pc/channel_interface.h"
 #include "pc/rtp_receiver.h"
 #include "pc/rtp_sender.h"
 
diff --git a/pc/video_rtp_receiver.cc b/pc/video_rtp_receiver.cc
new file mode 100644
index 0000000..2d9a23f
--- /dev/null
+++ b/pc/video_rtp_receiver.cc
@@ -0,0 +1,213 @@
+/*
+ *  Copyright 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "pc/video_rtp_receiver.h"
+
+#include <stddef.h>
+#include <utility>
+#include <vector>
+
+#include "api/media_stream_proxy.h"
+#include "api/media_stream_track_proxy.h"
+#include "api/video_track_source_proxy.h"
+#include "pc/media_stream.h"
+#include "pc/video_track.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/location.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/trace_event.h"
+
+namespace webrtc {
+
+VideoRtpReceiver::VideoRtpReceiver(rtc::Thread* worker_thread,
+                                   std::string receiver_id,
+                                   std::vector<std::string> stream_ids)
+    : VideoRtpReceiver(worker_thread,
+                       receiver_id,
+                       CreateStreamsFromIds(std::move(stream_ids))) {}
+
+VideoRtpReceiver::VideoRtpReceiver(
+    rtc::Thread* worker_thread,
+    const std::string& receiver_id,
+    const std::vector<rtc::scoped_refptr<MediaStreamInterface>>& streams)
+    : worker_thread_(worker_thread),
+      id_(receiver_id),
+      source_(new RefCountedObject<VideoRtpTrackSource>()),
+      track_(VideoTrackProxy::Create(
+          rtc::Thread::Current(),
+          worker_thread,
+          VideoTrack::Create(
+              receiver_id,
+              VideoTrackSourceProxy::Create(rtc::Thread::Current(),
+                                            worker_thread,
+                                            source_),
+              worker_thread))),
+      attachment_id_(GenerateUniqueId()) {
+  RTC_DCHECK(worker_thread_);
+  SetStreams(streams);
+  source_->SetState(MediaSourceInterface::kLive);
+}
+
+VideoRtpReceiver::~VideoRtpReceiver() {
+  // Since cricket::VideoRenderer is not reference counted,
+  // we need to remove it from the channel before we are deleted.
+  Stop();
+}
+
+std::vector<std::string> VideoRtpReceiver::stream_ids() const {
+  std::vector<std::string> stream_ids(streams_.size());
+  for (size_t i = 0; i < streams_.size(); ++i)
+    stream_ids[i] = streams_[i]->id();
+  return stream_ids;
+}
+
+bool VideoRtpReceiver::SetSink(rtc::VideoSinkInterface<VideoFrame>* sink) {
+  RTC_DCHECK(media_channel_);
+  RTC_DCHECK(ssrc_);
+  return worker_thread_->Invoke<bool>(
+      RTC_FROM_HERE, [&] { return media_channel_->SetSink(*ssrc_, sink); });
+}
+
+RtpParameters VideoRtpReceiver::GetParameters() const {
+  if (!media_channel_ || !ssrc_ || stopped_) {
+    return RtpParameters();
+  }
+  return worker_thread_->Invoke<RtpParameters>(RTC_FROM_HERE, [&] {
+    return media_channel_->GetRtpReceiveParameters(*ssrc_);
+  });
+}
+
+bool VideoRtpReceiver::SetParameters(const RtpParameters& parameters) {
+  TRACE_EVENT0("webrtc", "VideoRtpReceiver::SetParameters");
+  if (!media_channel_ || !ssrc_ || stopped_) {
+    return false;
+  }
+  return worker_thread_->Invoke<bool>(RTC_FROM_HERE, [&] {
+    return media_channel_->SetRtpReceiveParameters(*ssrc_, parameters);
+  });
+}
+
+void VideoRtpReceiver::SetFrameDecryptor(
+    rtc::scoped_refptr<FrameDecryptorInterface> frame_decryptor) {
+  frame_decryptor_ = std::move(frame_decryptor);
+  // Special Case: Set the frame decryptor to any value on any existing channel.
+  if (media_channel_ && ssrc_.has_value() && !stopped_) {
+    worker_thread_->Invoke<void>(RTC_FROM_HERE, [&] {
+      media_channel_->SetFrameDecryptor(*ssrc_, frame_decryptor_);
+    });
+  }
+}
+
+rtc::scoped_refptr<FrameDecryptorInterface>
+VideoRtpReceiver::GetFrameDecryptor() const {
+  return frame_decryptor_;
+}
+
+void VideoRtpReceiver::Stop() {
+  // TODO(deadbeef): Need to do more here to fully stop receiving packets.
+  if (stopped_) {
+    return;
+  }
+  source_->SetState(MediaSourceInterface::kEnded);
+  if (!media_channel_ || !ssrc_) {
+    RTC_LOG(LS_WARNING) << "VideoRtpReceiver::Stop: No video channel exists.";
+  } else {
+    // Allow that SetSink fail. This is the normal case when the underlying
+    // media channel has already been deleted.
+    SetSink(nullptr);
+  }
+  stopped_ = true;
+}
+
+void VideoRtpReceiver::SetupMediaChannel(uint32_t ssrc) {
+  if (!media_channel_) {
+    RTC_LOG(LS_ERROR)
+        << "VideoRtpReceiver::SetupMediaChannel: No video channel exists.";
+  }
+  if (ssrc_ == ssrc) {
+    return;
+  }
+  if (ssrc_) {
+    SetSink(nullptr);
+  }
+  ssrc_ = ssrc;
+  SetSink(source_->sink());
+  // Attach any existing frame decryptor to the media channel.
+  MaybeAttachFrameDecryptorToMediaChannel(
+      ssrc_, worker_thread_, frame_decryptor_, media_channel_, stopped_);
+}
+
+void VideoRtpReceiver::set_stream_ids(std::vector<std::string> stream_ids) {
+  SetStreams(CreateStreamsFromIds(std::move(stream_ids)));
+}
+
+void VideoRtpReceiver::SetStreams(
+    const std::vector<rtc::scoped_refptr<MediaStreamInterface>>& streams) {
+  // Remove remote track from any streams that are going away.
+  for (const auto& existing_stream : streams_) {
+    bool removed = true;
+    for (const auto& stream : streams) {
+      if (existing_stream->id() == stream->id()) {
+        RTC_DCHECK_EQ(existing_stream.get(), stream.get());
+        removed = false;
+        break;
+      }
+    }
+    if (removed) {
+      existing_stream->RemoveTrack(track_);
+    }
+  }
+  // Add remote track to any streams that are new.
+  for (const auto& stream : streams) {
+    bool added = true;
+    for (const auto& existing_stream : streams_) {
+      if (stream->id() == existing_stream->id()) {
+        RTC_DCHECK_EQ(stream.get(), existing_stream.get());
+        added = false;
+        break;
+      }
+    }
+    if (added) {
+      stream->AddTrack(track_);
+    }
+  }
+  streams_ = streams;
+}
+
+void VideoRtpReceiver::SetObserver(RtpReceiverObserverInterface* observer) {
+  observer_ = observer;
+  // Deliver any notifications the observer may have missed by being set late.
+  if (received_first_packet_ && observer_) {
+    observer_->OnFirstPacketReceived(media_type());
+  }
+}
+
+void VideoRtpReceiver::SetMediaChannel(cricket::MediaChannel* media_channel) {
+  RTC_DCHECK(media_channel == nullptr ||
+             media_channel->media_type() == media_type());
+  media_channel_ = static_cast<cricket::VideoMediaChannel*>(media_channel);
+}
+
+void VideoRtpReceiver::NotifyFirstPacketReceived() {
+  if (observer_) {
+    observer_->OnFirstPacketReceived(media_type());
+  }
+  received_first_packet_ = true;
+}
+
+std::vector<RtpSource> VideoRtpReceiver::GetSources() const {
+  if (!media_channel_ || !ssrc_ || stopped_) {
+    return {};
+  }
+  return worker_thread_->Invoke<std::vector<RtpSource>>(
+      RTC_FROM_HERE, [&] { return media_channel_->GetSources(*ssrc_); });
+}
+
+}  // namespace webrtc
diff --git a/pc/video_rtp_receiver.h b/pc/video_rtp_receiver.h
new file mode 100644
index 0000000..ab507ae
--- /dev/null
+++ b/pc/video_rtp_receiver.h
@@ -0,0 +1,144 @@
+/*
+ *  Copyright 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef PC_VIDEO_RTP_RECEIVER_H_
+#define PC_VIDEO_RTP_RECEIVER_H_
+
+#include <stdint.h>
+#include <string>
+#include <vector>
+
+#include "absl/types/optional.h"
+#include "api/crypto/frame_decryptor_interface.h"
+#include "api/media_stream_interface.h"
+#include "api/media_types.h"
+#include "api/rtp_parameters.h"
+#include "api/rtp_receiver_interface.h"
+#include "api/scoped_refptr.h"
+#include "api/video/video_frame.h"
+#include "api/video/video_sink_interface.h"
+#include "api/video/video_source_interface.h"
+#include "media/base/media_channel.h"
+#include "media/base/video_broadcaster.h"
+#include "pc/rtp_receiver.h"
+#include "pc/video_track_source.h"
+#include "rtc_base/ref_counted_object.h"
+#include "rtc_base/thread.h"
+
+namespace webrtc {
+
+class VideoRtpReceiver : public rtc::RefCountedObject<RtpReceiverInternal> {
+ public:
+  // An SSRC of 0 will create a receiver that will match the first SSRC it
+  // sees.
+  VideoRtpReceiver(rtc::Thread* worker_thread,
+                   std::string receiver_id,
+                   std::vector<std::string> streams_ids);
+  // TODO(hbos): Remove this when streams() is removed.
+  // https://crbug.com/webrtc/9480
+  VideoRtpReceiver(
+      rtc::Thread* worker_thread,
+      const std::string& receiver_id,
+      const std::vector<rtc::scoped_refptr<MediaStreamInterface>>& streams);
+
+  virtual ~VideoRtpReceiver();
+
+  rtc::scoped_refptr<VideoTrackInterface> video_track() const {
+    return track_.get();
+  }
+
+  // RtpReceiverInterface implementation
+  rtc::scoped_refptr<MediaStreamTrackInterface> track() const override {
+    return track_.get();
+  }
+  rtc::scoped_refptr<DtlsTransportInterface> dtls_transport() const override {
+    return dtls_transport_;
+  }
+  std::vector<std::string> stream_ids() const override;
+  std::vector<rtc::scoped_refptr<MediaStreamInterface>> streams()
+      const override {
+    return streams_;
+  }
+
+  cricket::MediaType media_type() const override {
+    return cricket::MEDIA_TYPE_VIDEO;
+  }
+
+  std::string id() const override { return id_; }
+
+  RtpParameters GetParameters() const override;
+  bool SetParameters(const RtpParameters& parameters) override;
+
+  void SetFrameDecryptor(
+      rtc::scoped_refptr<FrameDecryptorInterface> frame_decryptor) override;
+
+  rtc::scoped_refptr<FrameDecryptorInterface> GetFrameDecryptor()
+      const override;
+
+  // RtpReceiverInternal implementation.
+  void Stop() override;
+  void SetupMediaChannel(uint32_t ssrc) override;
+  uint32_t ssrc() const override { return ssrc_.value_or(0); }
+  void NotifyFirstPacketReceived() override;
+  void set_stream_ids(std::vector<std::string> stream_ids) override;
+  void set_transport(
+      rtc::scoped_refptr<DtlsTransportInterface> dtls_transport) override {
+    dtls_transport_ = dtls_transport;
+  }
+  void SetStreams(const std::vector<rtc::scoped_refptr<MediaStreamInterface>>&
+                      streams) override;
+
+  void SetObserver(RtpReceiverObserverInterface* observer) override;
+
+  void SetMediaChannel(cricket::MediaChannel* media_channel) override;
+
+  int AttachmentId() const override { return attachment_id_; }
+
+  std::vector<RtpSource> GetSources() const override;
+
+ private:
+  class VideoRtpTrackSource : public VideoTrackSource {
+   public:
+    VideoRtpTrackSource() : VideoTrackSource(true /* remote */) {}
+
+    rtc::VideoSourceInterface<VideoFrame>* source() override {
+      return &broadcaster_;
+    }
+    rtc::VideoSinkInterface<VideoFrame>* sink() { return &broadcaster_; }
+
+   private:
+    // |broadcaster_| is needed since the decoder can only handle one sink.
+    // It might be better if the decoder can handle multiple sinks and consider
+    // the VideoSinkWants.
+    rtc::VideoBroadcaster broadcaster_;
+  };
+
+  bool SetSink(rtc::VideoSinkInterface<VideoFrame>* sink);
+
+  rtc::Thread* const worker_thread_;
+  const std::string id_;
+  cricket::VideoMediaChannel* media_channel_ = nullptr;
+  absl::optional<uint32_t> ssrc_;
+  // |source_| is held here to be able to change the state of the source when
+  // the VideoRtpReceiver is stopped.
+  rtc::scoped_refptr<VideoRtpTrackSource> source_;
+  rtc::scoped_refptr<VideoTrackInterface> track_;
+  std::vector<rtc::scoped_refptr<MediaStreamInterface>> streams_;
+  bool stopped_ = false;
+  RtpReceiverObserverInterface* observer_ = nullptr;
+  bool received_first_packet_ = false;
+  int attachment_id_ = 0;
+  rtc::scoped_refptr<FrameDecryptorInterface> frame_decryptor_;
+  rtc::scoped_refptr<DtlsTransportInterface> dtls_transport_;
+};
+
+}  // namespace webrtc
+
+#endif  // PC_VIDEO_RTP_RECEIVER_H_