Configure media flow correctly with Unified Plan

This also changes RtpReceiver and RemoteAudioSource to have two-step
initialization, since in Unified Plan RtpReceivers are created much
earlier than in Plan B.

Bug: webrtc:7600
Change-Id: Ia135d25eb8bcab22969007b3a825a5a43ce62bf4
Reviewed-on: https://webrtc-review.googlesource.com/39382
Reviewed-by: Peter Thatcher <pthatcher@webrtc.org>
Commit-Queue: Steve Anton <steveanton@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#21681}
diff --git a/pc/peerconnection.cc b/pc/peerconnection.cc
index 6281d57..75eead6 100644
--- a/pc/peerconnection.cc
+++ b/pc/peerconnection.cc
@@ -1314,12 +1314,12 @@
   if (media_type == cricket::MEDIA_TYPE_AUDIO) {
     receiver = RtpReceiverProxyWithInternal<RtpReceiverInternal>::Create(
         signaling_thread(),
-        new AudioRtpReceiver(worker_thread(), receiver_id, {}, 0, nullptr));
+        new AudioRtpReceiver(worker_thread(), receiver_id, {}));
   } else {
     RTC_DCHECK_EQ(media_type, cricket::MEDIA_TYPE_VIDEO);
     receiver = RtpReceiverProxyWithInternal<RtpReceiverInternal>::Create(
         signaling_thread(),
-        new VideoRtpReceiver(worker_thread(), receiver_id, {}, 0, nullptr));
+        new VideoRtpReceiver(worker_thread(), receiver_id, {}));
   }
   return receiver;
 }
@@ -1766,7 +1766,27 @@
     AllocateSctpSids(role);
   }
 
-  if (!IsUnifiedPlan()) {
+  if (IsUnifiedPlan()) {
+    for (auto transceiver : transceivers_) {
+      const ContentInfo* content =
+          FindMediaSectionForTransceiver(transceiver, local_description());
+      if (!content) {
+        continue;
+      }
+      if (content->rejected && !transceiver->stopped()) {
+        transceiver->Stop();
+      }
+      if (!content->rejected) {
+        const auto& stream = content->media_description()->streams()[0];
+        transceiver->internal()->sender_internal()->set_stream_ids(
+            {stream.sync_label});
+        transceiver->internal()->sender_internal()->SetSsrc(
+            stream.first_ssrc());
+      }
+    }
+  } else {
+    // Plan B semantics.
+
     // Update state and SSRC of local MediaStreams and DataChannels based on the
     // local session description.
     const cricket::ContentInfo* audio_content =
@@ -2029,6 +2049,11 @@
       if (content->rejected && !transceiver->stopped()) {
         transceiver->Stop();
       }
+      if (!content->rejected) {
+        const auto& stream = content->media_description()->streams()[0];
+        transceiver->internal()->receiver_internal()->SetupMediaChannel(
+            stream.first_ssrc());
+      }
     }
     for (auto event : track_events) {
       observer_->OnAddTrack(event.receiver, event.streams);
@@ -2773,12 +2798,12 @@
     const RtpSenderInfo& remote_sender_info) {
   std::vector<rtc::scoped_refptr<MediaStreamInterface>> streams;
   streams.push_back(rtc::scoped_refptr<MediaStreamInterface>(stream));
-  rtc::scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>>
-      receiver = RtpReceiverProxyWithInternal<RtpReceiverInternal>::Create(
-          signaling_thread(),
-          new AudioRtpReceiver(worker_thread(), remote_sender_info.sender_id,
-                               streams, remote_sender_info.first_ssrc,
-                               voice_media_channel()));
+  auto* audio_receiver = new AudioRtpReceiver(
+      worker_thread(), remote_sender_info.sender_id, streams);
+  audio_receiver->SetMediaChannel(voice_media_channel());
+  audio_receiver->SetupMediaChannel(remote_sender_info.first_ssrc);
+  auto receiver = RtpReceiverProxyWithInternal<RtpReceiverInternal>::Create(
+      signaling_thread(), audio_receiver);
   GetAudioTransceiver()->internal()->AddReceiver(receiver);
   observer_->OnAddTrack(receiver, std::move(streams));
 }
@@ -2788,12 +2813,12 @@
     const RtpSenderInfo& remote_sender_info) {
   std::vector<rtc::scoped_refptr<MediaStreamInterface>> streams;
   streams.push_back(rtc::scoped_refptr<MediaStreamInterface>(stream));
-  rtc::scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>>
-      receiver = RtpReceiverProxyWithInternal<RtpReceiverInternal>::Create(
-          signaling_thread(),
-          new VideoRtpReceiver(worker_thread(), remote_sender_info.sender_id,
-                               streams, remote_sender_info.first_ssrc,
-                               video_media_channel()));
+  auto* video_receiver = new VideoRtpReceiver(
+      worker_thread(), remote_sender_info.sender_id, streams);
+  video_receiver->SetMediaChannel(video_media_channel());
+  video_receiver->SetupMediaChannel(remote_sender_info.first_ssrc);
+  auto receiver = RtpReceiverProxyWithInternal<RtpReceiverInternal>::Create(
+      signaling_thread(), video_receiver);
   GetVideoTransceiver()->internal()->AddReceiver(receiver);
   observer_->OnAddTrack(receiver, std::move(streams));
 }
diff --git a/pc/peerconnection_integrationtest.cc b/pc/peerconnection_integrationtest.cc
index 8decc96..7bf6ad8 100644
--- a/pc/peerconnection_integrationtest.cc
+++ b/pc/peerconnection_integrationtest.cc
@@ -84,6 +84,7 @@
 using webrtc::PeerConnectionProxy;
 using webrtc::RTCErrorType;
 using webrtc::RtpReceiverInterface;
+using webrtc::SdpSemantics;
 using webrtc::SdpType;
 using webrtc::SessionDescriptionInterface;
 using webrtc::StreamCollectionInterface;
@@ -3625,6 +3626,23 @@
   EXPECT_EQ(sent_packets_a, sent_packets_b);
 }
 
+// Test that a basic 1 audio and 1 video track call works when Unified Plan
+// semantics configured for both sides.
+TEST_F(PeerConnectionIntegrationTest, UnifiedPlanMediaFlows) {
+  PeerConnectionInterface::RTCConfiguration config;
+  config.sdp_semantics = SdpSemantics::kUnifiedPlan;
+  ASSERT_TRUE(CreatePeerConnectionWrappersWithConfig(config, config));
+  ConnectFakeSignaling();
+  caller()->AddAudioVideoTracks();
+  callee()->AddAudioVideoTracks();
+  caller()->CreateAndSetAndSignalOffer();
+  ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+  ExpectNewFramesReceivedWithWait(
+      kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount,
+      kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount,
+      kMaxWaitForFramesMs);
+}
+
 }  // namespace
 
 #endif  // if !defined(THREAD_SANITIZER)
diff --git a/pc/remoteaudiosource.cc b/pc/remoteaudiosource.cc
index a260be8..f58e23d 100644
--- a/pc/remoteaudiosource.cc
+++ b/pc/remoteaudiosource.cc
@@ -23,35 +23,33 @@
 
 namespace webrtc {
 
-class RemoteAudioSource::Sink : public AudioSinkInterface {
+// This proxy is passed to the underlying media engine to receive audio data as
+// they come in. The data will then be passed back up to the RemoteAudioSource
+// which will fan it out to all the sinks that have been added to it.
+class RemoteAudioSource::AudioDataProxy : public AudioSinkInterface {
  public:
-  explicit Sink(RemoteAudioSource* source) : source_(source) {}
-  ~Sink() override { source_->OnAudioChannelGone(); }
+  explicit AudioDataProxy(RemoteAudioSource* source) : source_(source) {
+    RTC_DCHECK(source);
+  }
+  ~AudioDataProxy() override { source_->OnAudioChannelGone(); }
 
- private:
+  // AudioSinkInterface implementation.
   void OnData(const AudioSinkInterface::Data& audio) override {
-    if (source_)
-      source_->OnData(audio);
+    source_->OnData(audio);
   }
 
+ private:
   const rtc::scoped_refptr<RemoteAudioSource> source_;
-  RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(Sink);
+
+  RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(AudioDataProxy);
 };
 
-rtc::scoped_refptr<RemoteAudioSource> RemoteAudioSource::Create(
-    rtc::Thread* worker_thread,
-    cricket::VoiceMediaChannel* media_channel,
-    uint32_t ssrc) {
-  rtc::scoped_refptr<RemoteAudioSource> ret(
-      new rtc::RefCountedObject<RemoteAudioSource>());
-  ret->Initialize(worker_thread, media_channel, ssrc);
-  return ret;
-}
-
-RemoteAudioSource::RemoteAudioSource()
+RemoteAudioSource::RemoteAudioSource(rtc::Thread* worker_thread)
     : main_thread_(rtc::Thread::Current()),
+      worker_thread_(worker_thread),
       state_(MediaSourceInterface::kLive) {
   RTC_DCHECK(main_thread_);
+  RTC_DCHECK(worker_thread_);
 }
 
 RemoteAudioSource::~RemoteAudioSource() {
@@ -60,17 +58,24 @@
   RTC_DCHECK(sinks_.empty());
 }
 
-void RemoteAudioSource::Initialize(rtc::Thread* worker_thread,
-                                   cricket::VoiceMediaChannel* media_channel,
-                                   uint32_t ssrc) {
-  RTC_DCHECK(main_thread_->IsCurrent());
-  // To make sure we always get notified when the channel goes out of scope,
-  // we register for callbacks here and not on demand in AddSink.
-  if (media_channel) {  // May be null in tests.
-    worker_thread->Invoke<void>(RTC_FROM_HERE, [&] {
-      media_channel->SetRawAudioSink(ssrc, rtc::MakeUnique<Sink>(this));
-    });
-  }
+void RemoteAudioSource::Start(cricket::VoiceMediaChannel* media_channel,
+                              uint32_t ssrc) {
+  RTC_DCHECK_RUN_ON(main_thread_);
+  RTC_DCHECK(media_channel);
+  // Register for callbacks immediately before AddSink so that we always get
+  // notified when a channel goes out of scope (signaled when "AudioDataProxy"
+  // is destroyed).
+  worker_thread_->Invoke<void>(RTC_FROM_HERE, [&] {
+    media_channel->SetRawAudioSink(ssrc, rtc::MakeUnique<AudioDataProxy>(this));
+  });
+}
+
+void RemoteAudioSource::Stop(cricket::VoiceMediaChannel* media_channel,
+                             uint32_t ssrc) {
+  RTC_DCHECK_RUN_ON(main_thread_);
+  RTC_DCHECK(media_channel);
+  worker_thread_->Invoke<void>(
+      RTC_FROM_HERE, [&] { media_channel->SetRawAudioSink(ssrc, nullptr); });
 }
 
 MediaSourceInterface::SourceState RemoteAudioSource::state() const {
@@ -86,8 +91,9 @@
 void RemoteAudioSource::SetVolume(double volume) {
   RTC_DCHECK_GE(volume, 0);
   RTC_DCHECK_LE(volume, 10);
-  for (auto* observer : audio_observers_)
+  for (auto* observer : audio_observers_) {
     observer->OnSetVolume(volume);
+  }
 }
 
 void RemoteAudioSource::RegisterAudioObserver(AudioObserver* observer) {
diff --git a/pc/remoteaudiosource.h b/pc/remoteaudiosource.h
index eadbcca..4d87331 100644
--- a/pc/remoteaudiosource.h
+++ b/pc/remoteaudiosource.h
@@ -28,50 +28,46 @@
 namespace webrtc {
 
 // This class implements the audio source used by the remote audio track.
+// This class works by configuring itself as a sink with the underlying media
+// engine, then when receiving data will fan out to all added sinks.
 class RemoteAudioSource : public Notifier<AudioSourceInterface>,
                           rtc::MessageHandler {
  public:
-  // Creates an instance of RemoteAudioSource.
-  static rtc::scoped_refptr<RemoteAudioSource> Create(
-      rtc::Thread* worker_thread,
-      cricket::VoiceMediaChannel* media_channel,
-      uint32_t ssrc);
+  explicit RemoteAudioSource(rtc::Thread* worker_thread);
+
+  // Register and unregister remote audio source with the underlying media
+  // engine.
+  void Start(cricket::VoiceMediaChannel* media_channel, uint32_t ssrc);
+  void Stop(cricket::VoiceMediaChannel* media_channel, uint32_t ssrc);
 
   // MediaSourceInterface implementation.
   MediaSourceInterface::SourceState state() const override;
   bool remote() const override;
 
-  void AddSink(AudioTrackSinkInterface* sink) override;
-  void RemoveSink(AudioTrackSinkInterface* sink) override;
-
- protected:
-  RemoteAudioSource();
-  ~RemoteAudioSource() override;
-
-  // Post construction initialize where we can do things like save a reference
-  // to ourselves (need to be fully constructed).
-  void Initialize(rtc::Thread* worker_thread,
-                  cricket::VoiceMediaChannel* media_channel,
-                  uint32_t ssrc);
-
- private:
-  typedef std::list<AudioObserver*> AudioObserverList;
-
   // AudioSourceInterface implementation.
   void SetVolume(double volume) override;
   void RegisterAudioObserver(AudioObserver* observer) override;
   void UnregisterAudioObserver(AudioObserver* observer) override;
 
-  class Sink;
+  void AddSink(AudioTrackSinkInterface* sink) override;
+  void RemoveSink(AudioTrackSinkInterface* sink) override;
+
+ protected:
+  ~RemoteAudioSource() override;
+
+ private:
+  // These are callbacks from the media engine.
+  class AudioDataProxy;
   void OnData(const AudioSinkInterface::Data& audio);
   void OnAudioChannelGone();
 
   void OnMessage(rtc::Message* msg) override;
 
-  AudioObserverList audio_observers_;
+  rtc::Thread* const main_thread_;
+  rtc::Thread* const worker_thread_;
+  std::list<AudioObserver*> audio_observers_;
   rtc::CriticalSection sink_lock_;
   std::list<AudioTrackSinkInterface*> sinks_;
-  rtc::Thread* const main_thread_;
   SourceState state_;
 };
 
diff --git a/pc/rtpreceiver.cc b/pc/rtpreceiver.cc
index e318c17..05990f4 100644
--- a/pc/rtpreceiver.cc
+++ b/pc/rtpreceiver.cc
@@ -35,17 +35,12 @@
 AudioRtpReceiver::AudioRtpReceiver(
     rtc::Thread* worker_thread,
     const std::string& receiver_id,
-    const std::vector<rtc::scoped_refptr<MediaStreamInterface>>& streams,
-    uint32_t ssrc,
-    cricket::VoiceMediaChannel* media_channel)
+    const std::vector<rtc::scoped_refptr<MediaStreamInterface>>& streams)
     : worker_thread_(worker_thread),
       id_(receiver_id),
-      ssrc_(ssrc),
-      track_(AudioTrackProxy::Create(
-          rtc::Thread::Current(),
-          AudioTrack::Create(
-              receiver_id,
-              RemoteAudioSource::Create(worker_thread, media_channel, ssrc)))),
+      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_);
@@ -53,8 +48,6 @@
   track_->RegisterObserver(this);
   track_->GetSource()->RegisterAudioObserver(this);
   SetStreams(streams);
-  SetMediaChannel(media_channel);
-  Reconfigure();
 }
 
 AudioRtpReceiver::~AudioRtpReceiver() {
@@ -74,8 +67,9 @@
   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);
+    return media_channel_->SetOutputVolume(*ssrc_, volume);
   });
 }
 
@@ -83,7 +77,7 @@
   RTC_DCHECK_GE(volume, 0);
   RTC_DCHECK_LE(volume, 10);
   cached_volume_ = volume;
-  if (!media_channel_) {
+  if (!media_channel_ || !ssrc_) {
     RTC_LOG(LS_ERROR)
         << "AudioRtpReceiver::OnSetVolume: No audio channel exists.";
     return;
@@ -99,21 +93,21 @@
 }
 
 RtpParameters AudioRtpReceiver::GetParameters() const {
-  if (!media_channel_ || stopped_) {
+  if (!media_channel_ || !ssrc_ || stopped_) {
     return RtpParameters();
   }
   return worker_thread_->Invoke<RtpParameters>(RTC_FROM_HERE, [&] {
-    return media_channel_->GetRtpReceiveParameters(ssrc_);
+    return media_channel_->GetRtpReceiveParameters(*ssrc_);
   });
 }
 
 bool AudioRtpReceiver::SetParameters(const RtpParameters& parameters) {
   TRACE_EVENT0("webrtc", "AudioRtpReceiver::SetParameters");
-  if (!media_channel_ || stopped_) {
+  if (!media_channel_ || !ssrc_ || stopped_) {
     return false;
   }
   return worker_thread_->Invoke<bool>(RTC_FROM_HERE, [&] {
-    return media_channel_->SetRtpReceiveParameters(ssrc_, parameters);
+    return media_channel_->SetRtpReceiveParameters(*ssrc_, parameters);
   });
 }
 
@@ -122,7 +116,7 @@
   if (stopped_) {
     return;
   }
-  if (media_channel_) {
+  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);
@@ -130,6 +124,23 @@
   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::SetStreams(
     const std::vector<rtc::scoped_refptr<MediaStreamInterface>>& streams) {
   // Remove remote track from any streams that are going away.
@@ -164,13 +175,16 @@
 }
 
 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_); });
+      RTC_FROM_HERE, [&] { return media_channel_->GetSources(*ssrc_); });
 }
 
 void AudioRtpReceiver::Reconfigure() {
   RTC_DCHECK(!stopped_);
-  if (!media_channel_) {
+  if (!media_channel_ || !ssrc_) {
     RTC_LOG(LS_ERROR)
         << "AudioRtpReceiver::Reconfigure: No audio channel exists.";
     return;
@@ -203,12 +217,9 @@
 VideoRtpReceiver::VideoRtpReceiver(
     rtc::Thread* worker_thread,
     const std::string& receiver_id,
-    const std::vector<rtc::scoped_refptr<MediaStreamInterface>>& streams,
-    uint32_t ssrc,
-    cricket::VideoMediaChannel* media_channel)
+    const std::vector<rtc::scoped_refptr<MediaStreamInterface>>& streams)
     : worker_thread_(worker_thread),
       id_(receiver_id),
-      ssrc_(ssrc),
       source_(new RefCountedObject<VideoTrackSource>(&broadcaster_,
                                                      true /* remote */)),
       track_(VideoTrackProxy::Create(
@@ -224,7 +235,6 @@
   RTC_DCHECK(worker_thread_);
   SetStreams(streams);
   source_->SetState(MediaSourceInterface::kLive);
-  SetMediaChannel(media_channel);
 }
 
 VideoRtpReceiver::~VideoRtpReceiver() {
@@ -235,26 +245,27 @@
 
 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); });
+      RTC_FROM_HERE, [&] { return media_channel_->SetSink(*ssrc_, sink); });
 }
 
 RtpParameters VideoRtpReceiver::GetParameters() const {
-  if (!media_channel_ || stopped_) {
+  if (!media_channel_ || !ssrc_ || stopped_) {
     return RtpParameters();
   }
   return worker_thread_->Invoke<RtpParameters>(RTC_FROM_HERE, [&] {
-    return media_channel_->GetRtpReceiveParameters(ssrc_);
+    return media_channel_->GetRtpReceiveParameters(*ssrc_);
   });
 }
 
 bool VideoRtpReceiver::SetParameters(const RtpParameters& parameters) {
   TRACE_EVENT0("webrtc", "VideoRtpReceiver::SetParameters");
-  if (!media_channel_ || stopped_) {
+  if (!media_channel_ || !ssrc_ || stopped_) {
     return false;
   }
   return worker_thread_->Invoke<bool>(RTC_FROM_HERE, [&] {
-    return media_channel_->SetRtpReceiveParameters(ssrc_, parameters);
+    return media_channel_->SetRtpReceiveParameters(*ssrc_, parameters);
   });
 }
 
@@ -265,7 +276,7 @@
   }
   source_->SetState(MediaSourceInterface::kEnded);
   source_->OnSourceDestroyed();
-  if (!media_channel_) {
+  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
@@ -275,6 +286,21 @@
   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(&broadcaster_);
+}
+
 void VideoRtpReceiver::SetStreams(
     const std::vector<rtc::scoped_refptr<MediaStreamInterface>>& streams) {
   // Remove remote track from any streams that are going away.
@@ -318,15 +344,7 @@
 
 void VideoRtpReceiver::SetMediaChannel(
     cricket::VideoMediaChannel* media_channel) {
-  if (media_channel_) {
-    SetSink(nullptr);
-  }
   media_channel_ = media_channel;
-  if (media_channel_) {
-    if (!SetSink(&broadcaster_)) {
-      RTC_NOTREACHED();
-    }
-  }
 }
 
 void VideoRtpReceiver::NotifyFirstPacketReceived() {
diff --git a/pc/rtpreceiver.h b/pc/rtpreceiver.h
index 82c3134..887f828 100644
--- a/pc/rtpreceiver.h
+++ b/pc/rtpreceiver.h
@@ -34,6 +34,11 @@
  public:
   virtual void Stop() = 0;
 
+  // Configures the RtpReceiver with the underlying media channel, with the
+  // given SSRC as the stream identifier. If |ssrc| is 0, the receiver will
+  // receive packets on unsignaled SSRCs.
+  virtual void SetupMediaChannel(uint32_t ssrc) = 0;
+
   // This SSRC is used as an identifier for the receiver between the API layer
   // and the WebRtcVideoEngine, WebRtcVoiceEngine layer.
   virtual uint32_t ssrc() const = 0;
@@ -53,16 +58,10 @@
                          public AudioSourceInterface::AudioObserver,
                          public rtc::RefCountedObject<RtpReceiverInternal> {
  public:
-  // An SSRC of 0 will create a receiver that will match the first SSRC it
-  // sees.
-  // TODO(deadbeef): Use rtc::Optional, or have another constructor that
-  // doesn't take an SSRC, and make this one DCHECK(ssrc != 0).
   AudioRtpReceiver(
       rtc::Thread* worker_thread,
       const std::string& receiver_id,
-      const std::vector<rtc::scoped_refptr<MediaStreamInterface>>& streams,
-      uint32_t ssrc,
-      cricket::VoiceMediaChannel* media_channel);
+      const std::vector<rtc::scoped_refptr<MediaStreamInterface>>& streams);
   virtual ~AudioRtpReceiver();
 
   // ObserverInterface implementation
@@ -95,7 +94,8 @@
 
   // RtpReceiverInternal implementation.
   void Stop() override;
-  uint32_t ssrc() const override { return ssrc_; }
+  void SetupMediaChannel(uint32_t ssrc) override;
+  uint32_t ssrc() const override { return ssrc_.value_or(0); }
   void NotifyFirstPacketReceived() override;
   void SetStreams(const std::vector<rtc::scoped_refptr<MediaStreamInterface>>&
                       streams) override;
@@ -115,9 +115,10 @@
 
   rtc::Thread* const worker_thread_;
   const std::string id_;
-  const uint32_t ssrc_;
-  cricket::VoiceMediaChannel* media_channel_ = nullptr;
+  const rtc::scoped_refptr<RemoteAudioSource> source_;
   const rtc::scoped_refptr<AudioTrackInterface> track_;
+  cricket::VoiceMediaChannel* media_channel_ = nullptr;
+  rtc::Optional<uint32_t> ssrc_;
   std::vector<rtc::scoped_refptr<MediaStreamInterface>> streams_;
   bool cached_track_enabled_;
   double cached_volume_ = 1;
@@ -134,9 +135,7 @@
   VideoRtpReceiver(
       rtc::Thread* worker_thread,
       const std::string& receiver_id,
-      const std::vector<rtc::scoped_refptr<MediaStreamInterface>>& streams,
-      uint32_t ssrc,
-      cricket::VideoMediaChannel* media_channel);
+      const std::vector<rtc::scoped_refptr<MediaStreamInterface>>& streams);
 
   virtual ~VideoRtpReceiver();
 
@@ -164,7 +163,8 @@
 
   // RtpReceiverInternal implementation.
   void Stop() override;
-  uint32_t ssrc() const override { return ssrc_; }
+  void SetupMediaChannel(uint32_t ssrc) override;
+  uint32_t ssrc() const override { return ssrc_.value_or(0); }
   void NotifyFirstPacketReceived() override;
   void SetStreams(const std::vector<rtc::scoped_refptr<MediaStreamInterface>>&
                       streams) override;
@@ -180,8 +180,8 @@
 
   rtc::Thread* const worker_thread_;
   const std::string id_;
-  uint32_t ssrc_;
   cricket::VideoMediaChannel* media_channel_ = nullptr;
+  rtc::Optional<uint32_t> ssrc_;
   // |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.
diff --git a/pc/rtpsenderreceiver_unittest.cc b/pc/rtpsenderreceiver_unittest.cc
index 9da9c6d..ff425e2 100644
--- a/pc/rtpsenderreceiver_unittest.cc
+++ b/pc/rtpsenderreceiver_unittest.cc
@@ -183,8 +183,9 @@
   void CreateAudioRtpReceiver(
       std::vector<rtc::scoped_refptr<MediaStreamInterface>> streams = {}) {
     audio_rtp_receiver_ = new AudioRtpReceiver(
-        rtc::Thread::Current(), kAudioTrackId, std::move(streams), kAudioSsrc,
-        voice_media_channel_);
+        rtc::Thread::Current(), kAudioTrackId, std::move(streams));
+    audio_rtp_receiver_->SetMediaChannel(voice_media_channel_);
+    audio_rtp_receiver_->SetupMediaChannel(kAudioSsrc);
     audio_track_ = audio_rtp_receiver_->audio_track();
     VerifyVoiceChannelOutput();
   }
@@ -192,8 +193,9 @@
   void CreateVideoRtpReceiver(
       std::vector<rtc::scoped_refptr<MediaStreamInterface>> streams = {}) {
     video_rtp_receiver_ = new VideoRtpReceiver(
-        rtc::Thread::Current(), kVideoTrackId, std::move(streams), kVideoSsrc,
-        video_media_channel_);
+        rtc::Thread::Current(), kVideoTrackId, std::move(streams));
+    video_rtp_receiver_->SetMediaChannel(video_media_channel_);
+    video_rtp_receiver_->SetupMediaChannel(kVideoSsrc);
     video_track_ = video_rtp_receiver_->video_track();
     VerifyVideoChannelOutput();
   }