Creating Simulcast offer and answer in Peer Connection.

CreateOffer and CreateAnswer will now examine the layers on the
transceiver to determine if multiple layers are requested (Simulcast).
In this scenario RIDs will be used in the layers (instead of SSRCs).
When the offer is created, only RIDs are signalled in the offer.
When the offer is set locally SetLocalDescription() SSRCs will be
generated for each layer by the Channel and sent downstream to the
MediaChannel.
The MediaChannel receives configuration that looks identical to that of
legacy simulcast, and should be able to integrate the streams correctly
regardless of how they were signalled.
Setting multiple layers on the transciever is still not supported
through the API.

Bug: webrtc:10075
Change-Id: Id4ad3637b87b68ef6ca7eec69166fee2d9dfa36f
Reviewed-on: https://webrtc-review.googlesource.com/c/119780
Reviewed-by: Seth Hampson <shampson@webrtc.org>
Reviewed-by: Steve Anton <steveanton@webrtc.org>
Commit-Queue: Amit Hilbuch <amithi@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#26428}
diff --git a/media/base/stream_params.cc b/media/base/stream_params.cc
index f89e565..329aa30 100644
--- a/media/base/stream_params.cc
+++ b/media/base/stream_params.cc
@@ -195,6 +195,38 @@
   sb << "}";
   return sb.str();
 }
+
+void StreamParams::GenerateSsrcs(int num_layers,
+                                 bool generate_fid,
+                                 bool generate_fec_fr,
+                                 rtc::UniqueRandomIdGenerator* ssrc_generator) {
+  RTC_DCHECK_GE(num_layers, 0);
+  RTC_DCHECK(ssrc_generator);
+  std::vector<uint32_t> primary_ssrcs;
+  for (int i = 0; i < num_layers; ++i) {
+    uint32_t ssrc = ssrc_generator->GenerateId();
+    primary_ssrcs.push_back(ssrc);
+    add_ssrc(ssrc);
+  }
+
+  if (num_layers > 1) {
+    SsrcGroup simulcast(kSimSsrcGroupSemantics, primary_ssrcs);
+    ssrc_groups.push_back(simulcast);
+  }
+
+  if (generate_fid) {
+    for (uint32_t ssrc : primary_ssrcs) {
+      AddFidSsrc(ssrc, ssrc_generator->GenerateId());
+    }
+  }
+
+  if (generate_fec_fr) {
+    for (uint32_t ssrc : primary_ssrcs) {
+      AddFecFrSsrc(ssrc, ssrc_generator->GenerateId());
+    }
+  }
+}
+
 void StreamParams::GetPrimarySsrcs(std::vector<uint32_t>* ssrcs) const {
   const SsrcGroup* sim_group = get_ssrc_group(kSimSsrcGroupSemantics);
   if (sim_group == NULL) {
diff --git a/media/base/stream_params.h b/media/base/stream_params.h
index b53e047..de567d0 100644
--- a/media/base/stream_params.h
+++ b/media/base/stream_params.h
@@ -54,6 +54,7 @@
 
 #include "media/base/rid_description.h"
 #include "rtc_base/constructor_magic.h"
+#include "rtc_base/unique_id_generator.h"
 
 namespace cricket {
 
@@ -154,6 +155,14 @@
     return GetSecondarySsrc(kFecFrSsrcGroupSemantics, primary_ssrc, fecfr_ssrc);
   }
 
+  // Convenience function to populate the StreamParams with the requested number
+  // of SSRCs along with accompanying FID and FEC-FR ssrcs if requested.
+  // SSRCs are generated using the given generator.
+  void GenerateSsrcs(int num_layers,
+                     bool generate_fid,
+                     bool generate_fec_fr,
+                     rtc::UniqueRandomIdGenerator* ssrc_generator);
+
   // Convenience to get all the SIM SSRCs if there are SIM ssrcs, or
   // the first SSRC otherwise.
   void GetPrimarySsrcs(std::vector<uint32_t>* ssrcs) const;
diff --git a/media/base/stream_params_unittest.cc b/media/base/stream_params_unittest.cc
index f004133..53d355a 100644
--- a/media/base/stream_params_unittest.cc
+++ b/media/base/stream_params_unittest.cc
@@ -14,8 +14,12 @@
 
 #include "media/base/test_utils.h"
 #include "rtc_base/arraysize.h"
+#include "test/gmock.h"
 #include "test/gtest.h"
 
+using ::testing::Each;
+using ::testing::Ne;
+
 static const uint32_t kSsrcs1[] = {1};
 static const uint32_t kSsrcs2[] = {1, 2};
 static const uint32_t kSsrcs3[] = {1, 2, 3};
@@ -321,3 +325,72 @@
   stream3.ssrc_groups.push_back(sg);
   EXPECT_FALSE(cricket::IsSimulcastStream(stream3));
 }
+
+TEST(StreamParams, TestGenerateSsrcs_SingleStreamWithRtxAndFlex) {
+  rtc::UniqueRandomIdGenerator generator;
+  cricket::StreamParams stream;
+  stream.GenerateSsrcs(1, true, true, &generator);
+  uint32_t primary_ssrc = stream.first_ssrc();
+  ASSERT_NE(0u, primary_ssrc);
+  uint32_t rtx_ssrc = 0;
+  uint32_t flex_ssrc = 0;
+  EXPECT_EQ(3u, stream.ssrcs.size());
+  EXPECT_TRUE(stream.GetFidSsrc(primary_ssrc, &rtx_ssrc));
+  EXPECT_NE(0u, rtx_ssrc);
+  EXPECT_TRUE(stream.GetFecFrSsrc(primary_ssrc, &flex_ssrc));
+  EXPECT_NE(0u, flex_ssrc);
+  EXPECT_FALSE(stream.has_ssrc_group(cricket::kSimSsrcGroupSemantics));
+  EXPECT_TRUE(stream.has_ssrc_group(cricket::kFidSsrcGroupSemantics));
+  EXPECT_TRUE(stream.has_ssrc_group(cricket::kFecFrSsrcGroupSemantics));
+}
+
+TEST(StreamParams, TestGenerateSsrcs_SingleStreamWithRtx) {
+  rtc::UniqueRandomIdGenerator generator;
+  cricket::StreamParams stream;
+  stream.GenerateSsrcs(1, true, false, &generator);
+  uint32_t primary_ssrc = stream.first_ssrc();
+  ASSERT_NE(0u, primary_ssrc);
+  uint32_t rtx_ssrc = 0;
+  uint32_t flex_ssrc = 0;
+  EXPECT_EQ(2u, stream.ssrcs.size());
+  EXPECT_TRUE(stream.GetFidSsrc(primary_ssrc, &rtx_ssrc));
+  EXPECT_NE(0u, rtx_ssrc);
+  EXPECT_FALSE(stream.GetFecFrSsrc(primary_ssrc, &flex_ssrc));
+  EXPECT_EQ(0u, flex_ssrc);
+  EXPECT_FALSE(stream.has_ssrc_group(cricket::kSimSsrcGroupSemantics));
+  EXPECT_TRUE(stream.has_ssrc_group(cricket::kFidSsrcGroupSemantics));
+}
+
+TEST(StreamParams, TestGenerateSsrcs_SingleStreamWithFlex) {
+  rtc::UniqueRandomIdGenerator generator;
+  cricket::StreamParams stream;
+  stream.GenerateSsrcs(1, false, true, &generator);
+  uint32_t primary_ssrc = stream.first_ssrc();
+  ASSERT_NE(0u, primary_ssrc);
+  uint32_t rtx_ssrc = 0;
+  uint32_t flex_ssrc = 0;
+  EXPECT_EQ(2u, stream.ssrcs.size());
+  EXPECT_FALSE(stream.GetFidSsrc(primary_ssrc, &rtx_ssrc));
+  EXPECT_EQ(0u, rtx_ssrc);
+  EXPECT_TRUE(stream.GetFecFrSsrc(primary_ssrc, &flex_ssrc));
+  EXPECT_NE(0u, flex_ssrc);
+  EXPECT_FALSE(stream.has_ssrc_group(cricket::kSimSsrcGroupSemantics));
+  EXPECT_TRUE(stream.has_ssrc_group(cricket::kFecFrSsrcGroupSemantics));
+}
+
+TEST(StreamParams, TestGenerateSsrcs_SimulcastLayersAndRtx) {
+  const size_t kNumStreams = 3;
+  rtc::UniqueRandomIdGenerator generator;
+  cricket::StreamParams stream;
+  stream.GenerateSsrcs(kNumStreams, true, false, &generator);
+  EXPECT_EQ(kNumStreams * 2, stream.ssrcs.size());
+  std::vector<uint32_t> primary_ssrcs, rtx_ssrcs;
+  stream.GetPrimarySsrcs(&primary_ssrcs);
+  EXPECT_EQ(kNumStreams, primary_ssrcs.size());
+  EXPECT_THAT(primary_ssrcs, Each(Ne(0u)));
+  stream.GetFidSsrcs(primary_ssrcs, &rtx_ssrcs);
+  EXPECT_EQ(kNumStreams, rtx_ssrcs.size());
+  EXPECT_THAT(rtx_ssrcs, Each(Ne(0u)));
+  EXPECT_TRUE(stream.has_ssrc_group(cricket::kSimSsrcGroupSemantics));
+  EXPECT_TRUE(stream.has_ssrc_group(cricket::kFidSsrcGroupSemantics));
+}
diff --git a/pc/channel.cc b/pc/channel.cc
index d26a511..049e950 100644
--- a/pc/channel.cc
+++ b/pc/channel.cc
@@ -37,6 +37,7 @@
 
 namespace cricket {
 using rtc::Bind;
+using rtc::UniqueRandomIdGenerator;
 using webrtc::SdpType;
 
 namespace {
@@ -46,6 +47,39 @@
   rtc::PacketOptions options;
 };
 
+// Finds a stream based on target's Primary SSRC or RIDs.
+// This struct is used in BaseChannel::UpdateLocalStreams_w.
+struct StreamFinder {
+  explicit StreamFinder(const StreamParams* target) : target_(target) {
+    RTC_DCHECK(target);
+  }
+
+  bool operator()(const StreamParams& sp) const {
+    if (target_->has_ssrcs() && sp.has_ssrcs()) {
+      return sp.has_ssrc(target_->first_ssrc());
+    }
+
+    if (!target_->has_rids() && !sp.has_rids()) {
+      return false;
+    }
+
+    const std::vector<RidDescription>& target_rids = target_->rids();
+    const std::vector<RidDescription>& source_rids = sp.rids();
+    if (source_rids.size() != target_rids.size()) {
+      return false;
+    }
+
+    // Check that all RIDs match.
+    return std::equal(source_rids.begin(), source_rids.end(),
+                      target_rids.begin(),
+                      [](const RidDescription& lhs, const RidDescription& rhs) {
+                        return lhs.rid == rhs.rid;
+                      });
+  }
+
+  const StreamParams* target_;
+};
+
 }  // namespace
 
 enum {
@@ -102,15 +136,18 @@
                          std::unique_ptr<MediaChannel> media_channel,
                          const std::string& content_name,
                          bool srtp_required,
-                         webrtc::CryptoOptions crypto_options)
+                         webrtc::CryptoOptions crypto_options,
+                         UniqueRandomIdGenerator* ssrc_generator)
     : worker_thread_(worker_thread),
       network_thread_(network_thread),
       signaling_thread_(signaling_thread),
       content_name_(content_name),
       srtp_required_(srtp_required),
       crypto_options_(crypto_options),
-      media_channel_(std::move(media_channel)) {
+      media_channel_(std::move(media_channel)),
+      ssrc_generator_(ssrc_generator) {
   RTC_DCHECK_RUN_ON(worker_thread_);
+  RTC_DCHECK(ssrc_generator_);
   demuxer_criteria_.mid = content_name;
   RTC_LOG(LS_INFO) << "Created channel for " << content_name;
 }
@@ -581,35 +618,77 @@
 bool BaseChannel::UpdateLocalStreams_w(const std::vector<StreamParams>& streams,
                                        SdpType type,
                                        std::string* error_desc) {
+  // In the case of RIDs (where SSRCs are not negotiated), this method will
+  // generate an SSRC for each layer in StreamParams. That representation will
+  // be stored internally in |local_streams_|.
+  // In subsequent offers, the same stream can appear in |streams| again
+  // (without the SSRCs), so it should be looked up using RIDs (if available)
+  // and then by primary SSRC.
+  // In both scenarios, it is safe to assume that the media channel will be
+  // created with a StreamParams object with SSRCs. However, it is not safe to
+  // assume that |local_streams_| will always have SSRCs as there are scenarios
+  // in which niether SSRCs or RIDs are negotiated.
+
   // Check for streams that have been removed.
   bool ret = true;
   for (const StreamParams& old_stream : local_streams_) {
-    if (old_stream.has_ssrcs() &&
-        !GetStreamBySsrc(streams, old_stream.first_ssrc())) {
-      if (!media_channel()->RemoveSendStream(old_stream.first_ssrc())) {
-        rtc::StringBuilder desc;
-        desc << "Failed to remove send stream with ssrc "
-             << old_stream.first_ssrc() << ".";
-        SafeSetError(desc.str(), error_desc);
-        ret = false;
-      }
+    if (!old_stream.has_ssrcs() ||
+        GetStream(streams, StreamFinder(&old_stream))) {
+      continue;
+    }
+    if (!media_channel()->RemoveSendStream(old_stream.first_ssrc())) {
+      rtc::StringBuilder desc;
+      desc << "Failed to remove send stream with ssrc "
+           << old_stream.first_ssrc() << ".";
+      SafeSetError(desc.str(), error_desc);
+      ret = false;
     }
   }
   // Check for new streams.
-  for (const StreamParams& new_stream : streams) {
-    if (new_stream.has_ssrcs() &&
-        !GetStreamBySsrc(local_streams_, new_stream.first_ssrc())) {
-      if (media_channel()->AddSendStream(new_stream)) {
-        RTC_LOG(LS_INFO) << "Add send stream ssrc: " << new_stream.ssrcs[0];
-      } else {
-        rtc::StringBuilder desc;
-        desc << "Failed to add send stream ssrc: " << new_stream.first_ssrc();
-        SafeSetError(desc.str(), error_desc);
-        ret = false;
-      }
+  std::vector<StreamParams> all_streams;
+  for (const StreamParams& stream : streams) {
+    StreamParams* existing = GetStream(local_streams_, StreamFinder(&stream));
+    if (existing) {
+      // Parameters cannot change for an existing stream.
+      all_streams.push_back(*existing);
+      continue;
+    }
+
+    all_streams.push_back(stream);
+    StreamParams& new_stream = all_streams.back();
+
+    if (!new_stream.has_ssrcs() && !new_stream.has_rids()) {
+      continue;
+    }
+
+    RTC_DCHECK(new_stream.has_ssrcs() || new_stream.has_rids());
+    if (new_stream.has_ssrcs() && new_stream.has_rids()) {
+      rtc::StringBuilder desc;
+      desc << "Failed to add send stream: " << new_stream.first_ssrc()
+           << ". Stream has both SSRCs and RIDs.";
+      SafeSetError(desc.str(), error_desc);
+      ret = false;
+      continue;
+    }
+
+    // At this point we use the legacy simulcast group in StreamParams to
+    // indicate that we want multiple layers to the media channel.
+    if (!new_stream.has_ssrcs()) {
+      // TODO(bugs.webrtc.org/10250): Indicate if flex is desired here.
+      new_stream.GenerateSsrcs(new_stream.rids().size(), /* rtx = */ true,
+                               /* flex_fec = */ false, ssrc_generator_);
+    }
+
+    if (media_channel()->AddSendStream(new_stream)) {
+      RTC_LOG(LS_INFO) << "Add send stream ssrc: " << new_stream.ssrcs[0];
+    } else {
+      rtc::StringBuilder desc;
+      desc << "Failed to add send stream ssrc: " << new_stream.first_ssrc();
+      SafeSetError(desc.str(), error_desc);
+      ret = false;
     }
   }
-  local_streams_ = streams;
+  local_streams_ = all_streams;
   return ret;
 }
 
@@ -729,19 +808,19 @@
 VoiceChannel::VoiceChannel(rtc::Thread* worker_thread,
                            rtc::Thread* network_thread,
                            rtc::Thread* signaling_thread,
-                           // TODO(nisse): Delete unused argument.
-                           MediaEngineInterface* /* media_engine */,
                            std::unique_ptr<VoiceMediaChannel> media_channel,
                            const std::string& content_name,
                            bool srtp_required,
-                           webrtc::CryptoOptions crypto_options)
+                           webrtc::CryptoOptions crypto_options,
+                           UniqueRandomIdGenerator* ssrc_generator)
     : BaseChannel(worker_thread,
                   network_thread,
                   signaling_thread,
                   std::move(media_channel),
                   content_name,
                   srtp_required,
-                  crypto_options) {}
+                  crypto_options,
+                  ssrc_generator) {}
 
 VoiceChannel::~VoiceChannel() {
   if (media_transport()) {
@@ -895,14 +974,16 @@
                            std::unique_ptr<VideoMediaChannel> media_channel,
                            const std::string& content_name,
                            bool srtp_required,
-                           webrtc::CryptoOptions crypto_options)
+                           webrtc::CryptoOptions crypto_options,
+                           UniqueRandomIdGenerator* ssrc_generator)
     : BaseChannel(worker_thread,
                   network_thread,
                   signaling_thread,
                   std::move(media_channel),
                   content_name,
                   srtp_required,
-                  crypto_options) {}
+                  crypto_options,
+                  ssrc_generator) {}
 
 VideoChannel::~VideoChannel() {
   TRACE_EVENT0("webrtc", "VideoChannel::~VideoChannel");
@@ -1034,14 +1115,16 @@
                                std::unique_ptr<DataMediaChannel> media_channel,
                                const std::string& content_name,
                                bool srtp_required,
-                               webrtc::CryptoOptions crypto_options)
+                               webrtc::CryptoOptions crypto_options,
+                               UniqueRandomIdGenerator* ssrc_generator)
     : BaseChannel(worker_thread,
                   network_thread,
                   signaling_thread,
                   std::move(media_channel),
                   content_name,
                   srtp_required,
-                  crypto_options) {}
+                  crypto_options,
+                  ssrc_generator) {}
 
 RtpDataChannel::~RtpDataChannel() {
   TRACE_EVENT0("webrtc", "RtpDataChannel::~RtpDataChannel");
diff --git a/pc/channel.h b/pc/channel.h
index 227c593..1a4cc72 100644
--- a/pc/channel.h
+++ b/pc/channel.h
@@ -41,6 +41,7 @@
 #include "rtc_base/critical_section.h"
 #include "rtc_base/network.h"
 #include "rtc_base/third_party/sigslot/sigslot.h"
+#include "rtc_base/unique_id_generator.h"
 
 namespace webrtc {
 class AudioSinkInterface;
@@ -78,6 +79,8 @@
  public:
   // If |srtp_required| is true, the channel will not send or receive any
   // RTP/RTCP packets without using SRTP (either using SDES or DTLS-SRTP).
+  // The BaseChannel does not own the UniqueRandomIdGenerator so it is the
+  // responsibility of the user to ensure it outlives this object.
   // TODO(zhihuang:) Create a BaseChannel::Config struct for the parameter lists
   // which will make it easier to change the constructor.
   BaseChannel(rtc::Thread* worker_thread,
@@ -86,7 +89,8 @@
               std::unique_ptr<MediaChannel> media_channel,
               const std::string& content_name,
               bool srtp_required,
-              webrtc::CryptoOptions crypto_options);
+              webrtc::CryptoOptions crypto_options,
+              rtc::UniqueRandomIdGenerator* ssrc_generator);
   virtual ~BaseChannel();
   virtual void Init_w(webrtc::RtpTransportInternal* rtp_transport,
                       webrtc::MediaTransportInterface* media_transport);
@@ -125,10 +129,10 @@
 
   bool Enable(bool enable) override;
 
-  const std::vector<StreamParams>& local_streams() const {
+  const std::vector<StreamParams>& local_streams() const override {
     return local_streams_;
   }
-  const std::vector<StreamParams>& remote_streams() const {
+  const std::vector<StreamParams>& remote_streams() const override {
     return remote_streams_;
   }
 
@@ -345,6 +349,11 @@
       webrtc::RtpTransceiverDirection::kInactive;
 
   webrtc::RtpDemuxerCriteria demuxer_criteria_;
+  // This generator is used to generate SSRCs for local streams.
+  // This is needed in cases where SSRCs are not negotiated or set explicitly
+  // like in Simulcast.
+  // This object is not owned by the channel so it must outlive it.
+  rtc::UniqueRandomIdGenerator* const ssrc_generator_;
 };
 
 // VoiceChannel is a specialization that adds support for early media, DTMF,
@@ -355,11 +364,11 @@
   VoiceChannel(rtc::Thread* worker_thread,
                rtc::Thread* network_thread,
                rtc::Thread* signaling_thread,
-               MediaEngineInterface* media_engine,
                std::unique_ptr<VoiceMediaChannel> channel,
                const std::string& content_name,
                bool srtp_required,
-               webrtc::CryptoOptions crypto_options);
+               webrtc::CryptoOptions crypto_options,
+               rtc::UniqueRandomIdGenerator* ssrc_generator);
   ~VoiceChannel();
 
   // downcasts a MediaChannel
@@ -402,7 +411,8 @@
                std::unique_ptr<VideoMediaChannel> media_channel,
                const std::string& content_name,
                bool srtp_required,
-               webrtc::CryptoOptions crypto_options);
+               webrtc::CryptoOptions crypto_options,
+               rtc::UniqueRandomIdGenerator* ssrc_generator);
   ~VideoChannel();
 
   // downcasts a MediaChannel
@@ -443,7 +453,8 @@
                  std::unique_ptr<DataMediaChannel> channel,
                  const std::string& content_name,
                  bool srtp_required,
-                 webrtc::CryptoOptions crypto_options);
+                 webrtc::CryptoOptions crypto_options,
+                 rtc::UniqueRandomIdGenerator* ssrc_generator);
   ~RtpDataChannel();
   // TODO(zhihuang): Remove this once the RtpTransport can be shared between
   // BaseChannels.
diff --git a/pc/channel_interface.h b/pc/channel_interface.h
index b623254..cd29ed4 100644
--- a/pc/channel_interface.h
+++ b/pc/channel_interface.h
@@ -12,6 +12,7 @@
 #define PC_CHANNEL_INTERFACE_H_
 
 #include <string>
+#include <vector>
 
 #include "api/jsep.h"
 #include "api/media_types.h"
@@ -52,6 +53,10 @@
                                 webrtc::SdpType type,
                                 std::string* error_desc) = 0;
 
+  // Access to the local and remote streams that were set on the channel.
+  virtual const std::vector<StreamParams>& local_streams() const = 0;
+  virtual const std::vector<StreamParams>& remote_streams() const = 0;
+
   // Set an RTP level transport.
   // Some examples:
   //   * An RtpTransport without encryption.
diff --git a/pc/channel_manager.cc b/pc/channel_manager.cc
index c936caf..f5798a2 100644
--- a/pc/channel_manager.cc
+++ b/pc/channel_manager.cc
@@ -163,12 +163,13 @@
     const std::string& content_name,
     bool srtp_required,
     const webrtc::CryptoOptions& crypto_options,
+    rtc::UniqueRandomIdGenerator* ssrc_generator,
     const AudioOptions& options) {
   if (!worker_thread_->IsCurrent()) {
     return worker_thread_->Invoke<VoiceChannel*>(RTC_FROM_HERE, [&] {
-      return CreateVoiceChannel(call, media_config, rtp_transport,
-                                media_transport, signaling_thread, content_name,
-                                srtp_required, crypto_options, options);
+      return CreateVoiceChannel(
+          call, media_config, rtp_transport, media_transport, signaling_thread,
+          content_name, srtp_required, crypto_options, ssrc_generator, options);
     });
   }
 
@@ -186,9 +187,9 @@
   }
 
   auto voice_channel = absl::make_unique<VoiceChannel>(
-      worker_thread_, network_thread_, signaling_thread, media_engine_.get(),
+      worker_thread_, network_thread_, signaling_thread,
       absl::WrapUnique(media_channel), content_name, srtp_required,
-      crypto_options);
+      crypto_options, ssrc_generator);
 
   voice_channel->Init_w(rtp_transport, media_transport);
 
@@ -231,12 +232,13 @@
     const std::string& content_name,
     bool srtp_required,
     const webrtc::CryptoOptions& crypto_options,
+    rtc::UniqueRandomIdGenerator* ssrc_generator,
     const VideoOptions& options) {
   if (!worker_thread_->IsCurrent()) {
     return worker_thread_->Invoke<VideoChannel*>(RTC_FROM_HERE, [&] {
-      return CreateVideoChannel(call, media_config, rtp_transport,
-                                media_transport, signaling_thread, content_name,
-                                srtp_required, crypto_options, options);
+      return CreateVideoChannel(
+          call, media_config, rtp_transport, media_transport, signaling_thread,
+          content_name, srtp_required, crypto_options, ssrc_generator, options);
     });
   }
 
@@ -256,7 +258,7 @@
   auto video_channel = absl::make_unique<VideoChannel>(
       worker_thread_, network_thread_, signaling_thread,
       absl::WrapUnique(media_channel), content_name, srtp_required,
-      crypto_options);
+      crypto_options, ssrc_generator);
 
   video_channel->Init_w(rtp_transport, media_transport);
 
@@ -296,11 +298,13 @@
     rtc::Thread* signaling_thread,
     const std::string& content_name,
     bool srtp_required,
-    const webrtc::CryptoOptions& crypto_options) {
+    const webrtc::CryptoOptions& crypto_options,
+    rtc::UniqueRandomIdGenerator* ssrc_generator) {
   if (!worker_thread_->IsCurrent()) {
     return worker_thread_->Invoke<RtpDataChannel*>(RTC_FROM_HERE, [&] {
       return CreateRtpDataChannel(media_config, rtp_transport, signaling_thread,
-                                  content_name, srtp_required, crypto_options);
+                                  content_name, srtp_required, crypto_options,
+                                  ssrc_generator);
     });
   }
 
@@ -315,7 +319,7 @@
   auto data_channel = absl::make_unique<RtpDataChannel>(
       worker_thread_, network_thread_, signaling_thread,
       absl::WrapUnique(media_channel), content_name, srtp_required,
-      crypto_options);
+      crypto_options, ssrc_generator);
   data_channel->Init_w(rtp_transport);
 
   RtpDataChannel* data_channel_ptr = data_channel.get();
diff --git a/pc/channel_manager.h b/pc/channel_manager.h
index 38d79ee..b70ed2e 100644
--- a/pc/channel_manager.h
+++ b/pc/channel_manager.h
@@ -100,6 +100,7 @@
       const std::string& content_name,
       bool srtp_required,
       const webrtc::CryptoOptions& crypto_options,
+      rtc::UniqueRandomIdGenerator* ssrc_generator,
       const AudioOptions& options);
   // Destroys a voice channel created by CreateVoiceChannel.
   void DestroyVoiceChannel(VoiceChannel* voice_channel);
@@ -116,6 +117,7 @@
       const std::string& content_name,
       bool srtp_required,
       const webrtc::CryptoOptions& crypto_options,
+      rtc::UniqueRandomIdGenerator* ssrc_generator,
       const VideoOptions& options);
   // Destroys a video channel created by CreateVideoChannel.
   void DestroyVideoChannel(VideoChannel* video_channel);
@@ -126,7 +128,8 @@
       rtc::Thread* signaling_thread,
       const std::string& content_name,
       bool srtp_required,
-      const webrtc::CryptoOptions& crypto_options);
+      const webrtc::CryptoOptions& crypto_options,
+      rtc::UniqueRandomIdGenerator* ssrc_generator);
   // Destroys a data channel created by CreateRtpDataChannel.
   void DestroyRtpDataChannel(RtpDataChannel* data_channel);
 
diff --git a/pc/channel_manager_unittest.cc b/pc/channel_manager_unittest.cc
index 999e36e..36b7981 100644
--- a/pc/channel_manager_unittest.cc
+++ b/pc/channel_manager_unittest.cc
@@ -83,16 +83,17 @@
     cricket::VoiceChannel* voice_channel = cm_->CreateVoiceChannel(
         &fake_call_, cricket::MediaConfig(), rtp_transport, media_transport,
         rtc::Thread::Current(), cricket::CN_AUDIO, kDefaultSrtpRequired,
-        webrtc::CryptoOptions(), AudioOptions());
+        webrtc::CryptoOptions(), &ssrc_generator_, AudioOptions());
     EXPECT_TRUE(voice_channel != nullptr);
     cricket::VideoChannel* video_channel = cm_->CreateVideoChannel(
         &fake_call_, cricket::MediaConfig(), rtp_transport, media_transport,
         rtc::Thread::Current(), cricket::CN_VIDEO, kDefaultSrtpRequired,
-        webrtc::CryptoOptions(), VideoOptions());
+        webrtc::CryptoOptions(), &ssrc_generator_, VideoOptions());
     EXPECT_TRUE(video_channel != nullptr);
     cricket::RtpDataChannel* rtp_data_channel = cm_->CreateRtpDataChannel(
         cricket::MediaConfig(), rtp_transport, rtc::Thread::Current(),
-        cricket::CN_DATA, kDefaultSrtpRequired, webrtc::CryptoOptions());
+        cricket::CN_DATA, kDefaultSrtpRequired, webrtc::CryptoOptions(),
+        &ssrc_generator_);
     EXPECT_TRUE(rtp_data_channel != nullptr);
     cm_->DestroyVideoChannel(video_channel);
     cm_->DestroyVoiceChannel(voice_channel);
@@ -109,6 +110,7 @@
   std::unique_ptr<cricket::ChannelManager> cm_;
   cricket::FakeCall fake_call_;
   webrtc::FakeMediaTransportFactory fake_media_transport_factory_;
+  rtc::UniqueRandomIdGenerator ssrc_generator_;
 };
 
 // Test that we startup/shutdown properly.
diff --git a/pc/channel_unittest.cc b/pc/channel_unittest.cc
index 1ead135..6e3aa19 100644
--- a/pc/channel_unittest.cc
+++ b/pc/channel_unittest.cc
@@ -39,6 +39,8 @@
 
 using cricket::DtlsTransportInternal;
 using cricket::FakeVoiceMediaChannel;
+using cricket::RidDescription;
+using cricket::RidDirection;
 using cricket::StreamParams;
 using webrtc::RtpTransceiverDirection;
 using webrtc::SdpType;
@@ -223,10 +225,10 @@
         fake_rtp_dtls_transport2_.get(), fake_rtcp_dtls_transport2_.get(),
         flags2);
 
-    channel1_ = CreateChannel(worker_thread, network_thread_, &media_engine_,
-                              std::move(ch1), rtp_transport1_.get(), flags1);
-    channel2_ = CreateChannel(worker_thread, network_thread_, &media_engine_,
-                              std::move(ch2), rtp_transport2_.get(), flags2);
+    channel1_ = CreateChannel(worker_thread, network_thread_, std::move(ch1),
+                              rtp_transport1_.get(), flags1);
+    channel2_ = CreateChannel(worker_thread, network_thread_, std::move(ch2),
+                              rtp_transport2_.get(), flags2);
     channel1_->SignalRtcpMuxFullyActive.connect(
         this, &ChannelTest<T>::OnRtcpMuxFullyActive1);
     channel2_->SignalRtcpMuxFullyActive.connect(
@@ -253,14 +255,14 @@
   std::unique_ptr<typename T::Channel> CreateChannel(
       rtc::Thread* worker_thread,
       rtc::Thread* network_thread,
-      cricket::MediaEngineInterface* engine,
       std::unique_ptr<typename T::MediaChannel> ch,
       webrtc::RtpTransportInternal* rtp_transport,
       int flags) {
     rtc::Thread* signaling_thread = rtc::Thread::Current();
     auto channel = absl::make_unique<typename T::Channel>(
-        worker_thread, network_thread, signaling_thread, engine, std::move(ch),
-        cricket::CN_AUDIO, (flags & DTLS) != 0, webrtc::CryptoOptions());
+        worker_thread, network_thread, signaling_thread, std::move(ch),
+        cricket::CN_AUDIO, (flags & DTLS) != 0, webrtc::CryptoOptions(),
+        &ssrc_generator_);
     channel->Init_w(rtp_transport, /*media_transport=*/nullptr);
     return channel;
   }
@@ -1434,6 +1436,59 @@
     EXPECT_EQ(kRcvBufSize, option_val);
   }
 
+  void CreateSimulcastContent(const std::vector<std::string>& rids,
+                              typename T::Content* content) {
+    std::vector<RidDescription> rid_descriptions;
+    for (const std::string name : rids) {
+      rid_descriptions.push_back(RidDescription(name, RidDirection::kSend));
+    }
+
+    StreamParams stream;
+    stream.set_rids(rid_descriptions);
+    CreateContent(0, kPcmuCodec, kH264Codec, content);
+    // This is for unified plan, so there can be only one StreamParams.
+    content->mutable_streams().clear();
+    content->AddStream(stream);
+  }
+
+  void VerifySimulcastStreamParams(const StreamParams& expected,
+                                   const typename T::Channel* channel) {
+    const std::vector<StreamParams>& streams = channel->local_streams();
+    ASSERT_EQ(1u, streams.size());
+    const StreamParams& result = streams[0];
+    EXPECT_EQ(expected.rids(), result.rids());
+    EXPECT_TRUE(result.has_ssrcs());
+    EXPECT_EQ(expected.rids().size() * 2, result.ssrcs.size());
+    std::vector<uint32_t> primary_ssrcs;
+    result.GetPrimarySsrcs(&primary_ssrcs);
+    EXPECT_EQ(expected.rids().size(), primary_ssrcs.size());
+  }
+
+  void TestUpdateLocalStreamsWithSimulcast() {
+    CreateChannels(0, 0);
+    typename T::Content content1, content2, content3;
+    CreateSimulcastContent({"f", "h", "q"}, &content1);
+    EXPECT_TRUE(
+        channel1_->SetLocalContent(&content1, SdpType::kOffer, nullptr));
+    VerifySimulcastStreamParams(content1.streams()[0], channel1_.get());
+    StreamParams stream1 = channel1_->local_streams()[0];
+
+    // Create a similar offer. SetLocalContent should not remove and add.
+    CreateSimulcastContent({"f", "h", "q"}, &content2);
+    EXPECT_TRUE(
+        channel1_->SetLocalContent(&content2, SdpType::kOffer, nullptr));
+    VerifySimulcastStreamParams(content2.streams()[0], channel1_.get());
+    StreamParams stream2 = channel1_->local_streams()[0];
+    // Check that the streams are identical (SSRCs didn't change).
+    EXPECT_EQ(stream1, stream2);
+
+    // Create third offer that has same RIDs in different order.
+    CreateSimulcastContent({"f", "q", "h"}, &content3);
+    EXPECT_TRUE(
+        channel1_->SetLocalContent(&content3, SdpType::kOffer, nullptr));
+    VerifySimulcastStreamParams(content3.streams()[0], channel1_.get());
+  }
+
  protected:
   void WaitForThreads() { WaitForThreads(rtc::ArrayView<rtc::Thread*>()); }
   static void ProcessThreadQueue(rtc::Thread* thread) {
@@ -1489,6 +1544,7 @@
   int rtcp_mux_activated_callbacks1_ = 0;
   int rtcp_mux_activated_callbacks2_ = 0;
   cricket::CandidatePairInterface* last_selected_candidate_pair_;
+  rtc::UniqueRandomIdGenerator ssrc_generator_;
 };
 
 template <>
@@ -1562,14 +1618,14 @@
 std::unique_ptr<cricket::VideoChannel> ChannelTest<VideoTraits>::CreateChannel(
     rtc::Thread* worker_thread,
     rtc::Thread* network_thread,
-    cricket::MediaEngineInterface* engine,
     std::unique_ptr<cricket::FakeVideoMediaChannel> ch,
     webrtc::RtpTransportInternal* rtp_transport,
     int flags) {
   rtc::Thread* signaling_thread = rtc::Thread::Current();
   auto channel = absl::make_unique<cricket::VideoChannel>(
       worker_thread, network_thread, signaling_thread, std::move(ch),
-      cricket::CN_VIDEO, (flags & DTLS) != 0, webrtc::CryptoOptions());
+      cricket::CN_VIDEO, (flags & DTLS) != 0, webrtc::CryptoOptions(),
+      &ssrc_generator_);
   channel->Init_w(rtp_transport, /*media_transport=*/nullptr);
   return channel;
 }
@@ -2063,6 +2119,10 @@
   Base::SocketOptionsMergedOnSetTransport();
 }
 
+TEST_F(VideoChannelSingleThreadTest, UpdateLocalStreamsWithSimulcast) {
+  Base::TestUpdateLocalStreamsWithSimulcast();
+}
+
 // VideoChannelDoubleThreadTest
 TEST_F(VideoChannelDoubleThreadTest, TestInit) {
   Base::TestInit();
@@ -2231,14 +2291,14 @@
 std::unique_ptr<cricket::RtpDataChannel> ChannelTest<DataTraits>::CreateChannel(
     rtc::Thread* worker_thread,
     rtc::Thread* network_thread,
-    cricket::MediaEngineInterface* engine,
     std::unique_ptr<cricket::FakeDataMediaChannel> ch,
     webrtc::RtpTransportInternal* rtp_transport,
     int flags) {
   rtc::Thread* signaling_thread = rtc::Thread::Current();
   auto channel = absl::make_unique<cricket::RtpDataChannel>(
       worker_thread, network_thread, signaling_thread, std::move(ch),
-      cricket::CN_DATA, (flags & DTLS) != 0, webrtc::CryptoOptions());
+      cricket::CN_DATA, (flags & DTLS) != 0, webrtc::CryptoOptions(),
+      &ssrc_generator_);
   channel->Init_w(rtp_transport);
   return channel;
 }
diff --git a/pc/media_session.cc b/pc/media_session.cc
index 3c98117..2e2f0cd 100644
--- a/pc/media_session.cc
+++ b/pc/media_session.cc
@@ -390,52 +390,24 @@
 static StreamParams CreateStreamParamsForNewSenderWithSsrcs(
     const SenderOptions& sender,
     const std::string& rtcp_cname,
-    const StreamParamsVec& current_streams,
     bool include_rtx_streams,
-    bool include_flexfec_stream) {
+    bool include_flexfec_stream,
+    UniqueRandomIdGenerator* ssrc_generator) {
   StreamParams result;
   result.id = sender.track_id;
 
-  std::vector<uint32_t> known_ssrcs;
-  for (const StreamParams& params : current_streams) {
-    for (uint32_t ssrc : params.ssrcs) {
-      known_ssrcs.push_back(ssrc);
-    }
+  // TODO(brandtr): Update when we support multistream protection.
+  if (include_flexfec_stream && sender.num_sim_layers > 1) {
+    include_flexfec_stream = false;
+    RTC_LOG(LS_WARNING)
+        << "Our FlexFEC implementation only supports protecting "
+           "a single media streams. This session has multiple "
+           "media streams however, so no FlexFEC SSRC will be generated.";
   }
 
-  UniqueRandomIdGenerator ssrc_generator(known_ssrcs);
-  // We need to keep |primary_ssrcs| separate from |result.ssrcs|  because
-  // iterators are invalidated when rtx and flexfec ssrcs are added to the list.
-  std::vector<uint32_t> primary_ssrcs;
-  for (int i = 0; i < sender.num_sim_layers; ++i) {
-    primary_ssrcs.push_back(ssrc_generator());
-  }
-  result.ssrcs = primary_ssrcs;
+  result.GenerateSsrcs(sender.num_sim_layers, include_rtx_streams,
+                       include_flexfec_stream, ssrc_generator);
 
-  if (sender.num_sim_layers > 1) {
-    SsrcGroup group(kSimSsrcGroupSemantics, result.ssrcs);
-    result.ssrc_groups.push_back(group);
-  }
-  // Generate an RTX ssrc for every ssrc in the group.
-  if (include_rtx_streams) {
-    for (uint32_t ssrc : primary_ssrcs) {
-      result.AddFidSsrc(ssrc, ssrc_generator());
-    }
-  }
-  // Generate extra ssrc for include_flexfec_stream case.
-  if (include_flexfec_stream) {
-    // TODO(brandtr): Update when we support multistream protection.
-    if (primary_ssrcs.size() == 1) {
-      for (uint32_t ssrc : primary_ssrcs) {
-        result.AddFecFrSsrc(ssrc, ssrc_generator());
-      }
-    } else if (!primary_ssrcs.empty()) {
-      RTC_LOG(LS_WARNING)
-          << "Our FlexFEC implementation only supports protecting "
-             "a single media streams. This session has multiple "
-             "media streams however, so no FlexFEC SSRC will be generated.";
-    }
-  }
   result.cname = rtcp_cname;
   result.set_stream_ids(sender.stream_ids);
 
@@ -523,6 +495,7 @@
 static bool AddStreamParams(
     const std::vector<SenderOptions>& sender_options,
     const std::string& rtcp_cname,
+    UniqueRandomIdGenerator* ssrc_generator,
     StreamParamsVec* current_streams,
     MediaContentDescriptionImpl<C>* content_description) {
   // SCTP streams are not negotiated using SDP/ContentDescriptions.
@@ -548,8 +521,8 @@
               ?
               // Signal SSRCs and legacy simulcast (if requested).
               CreateStreamParamsForNewSenderWithSsrcs(
-                  sender, rtcp_cname, *current_streams, include_rtx_streams,
-                  include_flexfec_stream)
+                  sender, rtcp_cname, include_rtx_streams,
+                  include_flexfec_stream, ssrc_generator)
               :
               // Signal RIDs and spec-compliant simulcast (if requested).
               CreateStreamParamsForNewSenderWithRids(sender, rtcp_cname);
@@ -787,6 +760,7 @@
     const CryptoParamsVec* current_cryptos,
     const std::vector<std::string>& crypto_suites,
     const RtpHeaderExtensions& rtp_extensions,
+    UniqueRandomIdGenerator* ssrc_generator,
     StreamParamsVec* current_streams,
     MediaContentDescriptionImpl<C>* offer) {
   offer->AddCodecs(codecs);
@@ -798,7 +772,8 @@
   offer->set_rtp_header_extensions(rtp_extensions);
 
   if (!AddStreamParams(media_description_options.sender_options,
-                       session_options.rtcp_cname, current_streams, offer)) {
+                       session_options.rtcp_cname, ssrc_generator,
+                       current_streams, offer)) {
     return false;
   }
 
@@ -1166,6 +1141,7 @@
     const SecurePolicy& sdes_policy,
     const CryptoParamsVec* current_cryptos,
     const RtpHeaderExtensions& local_rtp_extenstions,
+    UniqueRandomIdGenerator* ssrc_generator,
     bool enable_encrypted_rtp_header_extensions,
     StreamParamsVec* current_streams,
     bool bundle_enabled,
@@ -1203,7 +1179,8 @@
   }
 
   if (!AddStreamParams(media_description_options.sender_options,
-                       session_options.rtcp_cname, current_streams, answer)) {
+                       session_options.rtcp_cname, ssrc_generator,
+                       current_streams, answer)) {
     return false;  // Something went seriously wrong.
   }
 
@@ -1343,13 +1320,18 @@
 }
 
 MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
-    const TransportDescriptionFactory* transport_desc_factory)
-    : transport_desc_factory_(transport_desc_factory) {}
+    const TransportDescriptionFactory* transport_desc_factory,
+    rtc::UniqueRandomIdGenerator* ssrc_generator)
+    : ssrc_generator_(ssrc_generator),
+      transport_desc_factory_(transport_desc_factory) {
+  RTC_DCHECK(ssrc_generator_);
+}
 
 MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
     ChannelManager* channel_manager,
-    const TransportDescriptionFactory* transport_desc_factory)
-    : transport_desc_factory_(transport_desc_factory) {
+    const TransportDescriptionFactory* transport_desc_factory,
+    rtc::UniqueRandomIdGenerator* ssrc_generator)
+    : MediaSessionDescriptionFactory(transport_desc_factory, ssrc_generator) {
   channel_manager->GetSupportedAudioSendCodecs(&audio_send_codecs_);
   channel_manager->GetSupportedAudioReceiveCodecs(&audio_recv_codecs_);
   channel_manager->GetSupportedAudioRtpHeaderExtensions(&audio_rtp_extensions_);
@@ -2039,10 +2021,11 @@
   std::vector<std::string> crypto_suites;
   GetSupportedAudioSdesCryptoSuiteNames(session_options.crypto_options,
                                         &crypto_suites);
-  if (!CreateMediaContentOffer(
-          media_description_options, session_options, filtered_codecs,
-          sdes_policy, GetCryptos(current_content), crypto_suites,
-          audio_rtp_extensions, current_streams, audio.get())) {
+  if (!CreateMediaContentOffer(media_description_options, session_options,
+                               filtered_codecs, sdes_policy,
+                               GetCryptos(current_content), crypto_suites,
+                               audio_rtp_extensions, ssrc_generator_,
+                               current_streams, audio.get())) {
     return false;
   }
 
@@ -2109,10 +2092,11 @@
     }
   }
 
-  if (!CreateMediaContentOffer(
-          media_description_options, session_options, filtered_codecs,
-          sdes_policy, GetCryptos(current_content), crypto_suites,
-          video_rtp_extensions, current_streams, video.get())) {
+  if (!CreateMediaContentOffer(media_description_options, session_options,
+                               filtered_codecs, sdes_policy,
+                               GetCryptos(current_content), crypto_suites,
+                               video_rtp_extensions, ssrc_generator_,
+                               current_streams, video.get())) {
     return false;
   }
 
@@ -2180,7 +2164,7 @@
   if (!CreateMediaContentOffer(
           media_description_options, session_options, data_codecs, sdes_policy,
           GetCryptos(current_content), crypto_suites, RtpHeaderExtensions(),
-          current_streams, data.get())) {
+          ssrc_generator_, current_streams, data.get())) {
     return false;
   }
 
@@ -2283,7 +2267,7 @@
   if (!CreateMediaContentAnswer(
           offer_audio_description, media_description_options, session_options,
           filtered_codecs, sdes_policy, GetCryptos(current_content),
-          audio_rtp_header_extensions(),
+          audio_rtp_header_extensions(), ssrc_generator_,
           enable_encrypted_rtp_header_extensions_, current_streams,
           bundle_enabled, audio_answer.get())) {
     return false;  // Fails the session setup.
@@ -2372,7 +2356,7 @@
   if (!CreateMediaContentAnswer(
           offer_video_description, media_description_options, session_options,
           filtered_codecs, sdes_policy, GetCryptos(current_content),
-          video_rtp_header_extensions(),
+          video_rtp_header_extensions(), ssrc_generator_,
           enable_encrypted_rtp_header_extensions_, current_streams,
           bundle_enabled, video_answer.get())) {
     return false;  // Failed the sessin setup.
@@ -2432,8 +2416,9 @@
   if (!CreateMediaContentAnswer(
           offer_data_description, media_description_options, session_options,
           data_codecs, sdes_policy, GetCryptos(current_content),
-          RtpHeaderExtensions(), enable_encrypted_rtp_header_extensions_,
-          current_streams, bundle_enabled, data_answer.get())) {
+          RtpHeaderExtensions(), ssrc_generator_,
+          enable_encrypted_rtp_header_extensions_, current_streams,
+          bundle_enabled, data_answer.get())) {
     return false;  // Fails the session setup.
   }
 
diff --git a/pc/media_session.h b/pc/media_session.h
index 2251b5b..538e219 100644
--- a/pc/media_session.h
+++ b/pc/media_session.h
@@ -26,6 +26,7 @@
 #include "p2p/base/transport_description_factory.h"
 #include "pc/jsep_transport.h"
 #include "pc/session_description.h"
+#include "rtc_base/unique_id_generator.h"
 
 namespace cricket {
 
@@ -127,15 +128,19 @@
 // of the various fields to determine the proper result.
 class MediaSessionDescriptionFactory {
  public:
-  // Default ctor; use methods below to set configuration.
-  // The TransportDescriptionFactory is not owned by MediaSessionDescFactory,
-  // so it must be kept alive by the user of this class.
-  explicit MediaSessionDescriptionFactory(
-      const TransportDescriptionFactory* factory);
+  // Simple constructor that does not set any configuration for the factory.
+  // When using this constructor, the methods below can be used to set the
+  // configuration.
+  // The TransportDescriptionFactory and the UniqueRandomIdGenerator are not
+  // owned by MediaSessionDescriptionFactory, so they must be kept alive by the
+  // user of this class.
+  MediaSessionDescriptionFactory(const TransportDescriptionFactory* factory,
+                                 rtc::UniqueRandomIdGenerator* ssrc_generator);
   // This helper automatically sets up the factory to get its configuration
   // from the specified ChannelManager.
   MediaSessionDescriptionFactory(ChannelManager* cmanager,
-                                 const TransportDescriptionFactory* factory);
+                                 const TransportDescriptionFactory* factory,
+                                 rtc::UniqueRandomIdGenerator* ssrc_generator);
 
   const AudioCodecs& audio_sendrecv_codecs() const;
   const AudioCodecs& audio_send_codecs() const;
@@ -300,6 +305,8 @@
   VideoCodecs video_codecs_;
   RtpHeaderExtensions video_rtp_extensions_;
   DataCodecs data_codecs_;
+  // This object is not owned by the channel so it must outlive it.
+  rtc::UniqueRandomIdGenerator* const ssrc_generator_;
   bool enable_encrypted_rtp_header_extensions_ = false;
   // TODO(zhihuang): Rename secure_ to sdec_policy_; rename the related getter
   // and setter.
diff --git a/pc/media_session_unittest.cc b/pc/media_session_unittest.cc
index b6be353..8525d61 100644
--- a/pc/media_session_unittest.cc
+++ b/pc/media_session_unittest.cc
@@ -28,6 +28,7 @@
 #include "rtc_base/message_digest.h"
 #include "rtc_base/ssl_adapter.h"
 #include "rtc_base/strings/string_builder.h"
+#include "rtc_base/unique_id_generator.h"
 #include "test/gmock.h"
 
 #define ASSERT_CRYPTO(cd, s, cs)      \
@@ -79,6 +80,7 @@
 using rtc::CS_AEAD_AES_256_GCM;
 using rtc::CS_AES_CM_128_HMAC_SHA1_32;
 using rtc::CS_AES_CM_128_HMAC_SHA1_80;
+using rtc::UniqueRandomIdGenerator;
 using testing::Each;
 using testing::ElementsAreArray;
 using testing::Eq;
@@ -382,7 +384,8 @@
 // these tests may be obsolete as a result, and should be refactored or removed.
 class MediaSessionDescriptionFactoryTest : public testing::Test {
  public:
-  MediaSessionDescriptionFactoryTest() : f1_(&tdf1_), f2_(&tdf2_) {
+  MediaSessionDescriptionFactoryTest()
+      : f1_(&tdf1_, &ssrc_generator1), f2_(&tdf2_, &ssrc_generator2) {
     f1_.set_audio_codecs(MAKE_VECTOR(kAudioCodecs1),
                          MAKE_VECTOR(kAudioCodecs1));
     f1_.set_video_codecs(MAKE_VECTOR(kVideoCodecs1));
@@ -688,6 +691,8 @@
   }
 
  protected:
+  UniqueRandomIdGenerator ssrc_generator1;
+  UniqueRandomIdGenerator ssrc_generator2;
   MediaSessionDescriptionFactory f1_;
   MediaSessionDescriptionFactory f2_;
   TransportDescriptionFactory tdf1_;
@@ -4016,7 +4021,8 @@
 
 class MediaProtocolTest : public ::testing::TestWithParam<const char*> {
  public:
-  MediaProtocolTest() : f1_(&tdf1_), f2_(&tdf2_) {
+  MediaProtocolTest()
+      : f1_(&tdf1_, &ssrc_generator1), f2_(&tdf2_, &ssrc_generator2) {
     f1_.set_audio_codecs(MAKE_VECTOR(kAudioCodecs1),
                          MAKE_VECTOR(kAudioCodecs1));
     f1_.set_video_codecs(MAKE_VECTOR(kVideoCodecs1));
@@ -4040,6 +4046,8 @@
   MediaSessionDescriptionFactory f2_;
   TransportDescriptionFactory tdf1_;
   TransportDescriptionFactory tdf2_;
+  UniqueRandomIdGenerator ssrc_generator1;
+  UniqueRandomIdGenerator ssrc_generator2;
 };
 
 TEST_P(MediaProtocolTest, TestAudioVideoAcceptance) {
@@ -4074,7 +4082,8 @@
 
 TEST_F(MediaSessionDescriptionFactoryTest, TestSetAudioCodecs) {
   TransportDescriptionFactory tdf;
-  MediaSessionDescriptionFactory sf(&tdf);
+  UniqueRandomIdGenerator ssrc_generator;
+  MediaSessionDescriptionFactory sf(&tdf, &ssrc_generator);
   std::vector<AudioCodec> send_codecs = MAKE_VECTOR(kAudioCodecs1);
   std::vector<AudioCodec> recv_codecs = MAKE_VECTOR(kAudioCodecs2);
 
@@ -4130,7 +4139,8 @@
 // an object that is not semantically equivalent to the set object.
 TEST_F(MediaSessionDescriptionFactoryTest, VideoHasRidExtensionsInUnifiedPlan) {
   TransportDescriptionFactory tdf;
-  MediaSessionDescriptionFactory sf(&tdf);
+  UniqueRandomIdGenerator ssrc_generator;
+  MediaSessionDescriptionFactory sf(&tdf, &ssrc_generator);
   sf.set_is_unified_plan(true);
   cricket::RtpHeaderExtensions extensions;
   sf.set_video_rtp_header_extensions(extensions);
@@ -4155,7 +4165,8 @@
 // an object that is not semantically equivalent to the set object.
 TEST_F(MediaSessionDescriptionFactoryTest, AudioHasRidExtensionsInUnifiedPlan) {
   TransportDescriptionFactory tdf;
-  MediaSessionDescriptionFactory sf(&tdf);
+  UniqueRandomIdGenerator ssrc_generator;
+  MediaSessionDescriptionFactory sf(&tdf, &ssrc_generator);
   sf.set_is_unified_plan(true);
   cricket::RtpHeaderExtensions extensions;
   sf.set_audio_rtp_header_extensions(extensions);
@@ -4193,7 +4204,8 @@
 
 void TestAudioCodecsOffer(RtpTransceiverDirection direction) {
   TransportDescriptionFactory tdf;
-  MediaSessionDescriptionFactory sf(&tdf);
+  UniqueRandomIdGenerator ssrc_generator;
+  MediaSessionDescriptionFactory sf(&tdf, &ssrc_generator);
   const std::vector<AudioCodec> send_codecs = MAKE_VECTOR(kAudioCodecs1);
   const std::vector<AudioCodec> recv_codecs = MAKE_VECTOR(kAudioCodecs2);
   const std::vector<AudioCodec> sendrecv_codecs =
@@ -4290,8 +4302,9 @@
                            bool add_legacy_stream) {
   TransportDescriptionFactory offer_tdf;
   TransportDescriptionFactory answer_tdf;
-  MediaSessionDescriptionFactory offer_factory(&offer_tdf);
-  MediaSessionDescriptionFactory answer_factory(&answer_tdf);
+  UniqueRandomIdGenerator ssrc_generator1, ssrc_generator2;
+  MediaSessionDescriptionFactory offer_factory(&offer_tdf, &ssrc_generator1);
+  MediaSessionDescriptionFactory answer_factory(&answer_tdf, &ssrc_generator2);
   offer_factory.set_audio_codecs(
       VectorFromIndices(kOfferAnswerCodecs, kOfferSendCodecs),
       VectorFromIndices(kOfferAnswerCodecs, kOfferRecvCodecs));
diff --git a/pc/peer_connection.cc b/pc/peer_connection.cc
index 3be5ca3..33f4ce1 100644
--- a/pc/peer_connection.cc
+++ b/pc/peer_connection.cc
@@ -59,8 +59,13 @@
 using cricket::ContentInfos;
 using cricket::MediaContentDescription;
 using cricket::MediaProtocolType;
+using cricket::RidDescription;
+using cricket::RidDirection;
 using cricket::SessionDescription;
+using cricket::SimulcastDescription;
+using cricket::SimulcastLayer;
 using cricket::SimulcastLayerList;
+using cricket::StreamParams;
 using cricket::TransportInfo;
 
 using cricket::LOCAL_PORT_TYPE;
@@ -1092,7 +1097,7 @@
 
   webrtc_session_desc_factory_.reset(new WebRtcSessionDescriptionFactory(
       signaling_thread(), channel_manager(), this, session_id(),
-      std::move(dependencies.cert_generator), certificate));
+      std::move(dependencies.cert_generator), certificate, &ssrc_generator_));
   webrtc_session_desc_factory_->SignalCertificateReady.connect(
       this, &PeerConnection::OnCertificateReady);
 
@@ -2198,18 +2203,20 @@
       if (!content) {
         continue;
       }
-      const auto& streams = content->media_description()->streams();
-      if (!content->rejected && !streams.empty()) {
-        transceiver->internal()->sender_internal()->set_stream_ids(
-            streams[0].stream_ids());
-        transceiver->internal()->sender_internal()->SetSsrc(
-            streams[0].first_ssrc());
-      } else {
+      cricket::ChannelInterface* channel = transceiver->internal()->channel();
+      if (content->rejected || !channel || channel->local_streams().empty()) {
         // 0 is a special value meaning "this sender has no associated send
         // stream". Need to call this so the sender won't attempt to configure
         // a no longer existing stream and run into DCHECKs in the lower
         // layers.
         transceiver->internal()->sender_internal()->SetSsrc(0);
+      } else {
+        // Get the StreamParams from the channel which could generate SSRCs.
+        const std::vector<StreamParams>& streams = channel->local_streams();
+        transceiver->internal()->sender_internal()->set_stream_ids(
+            streams[0].stream_ids());
+        transceiver->internal()->sender_internal()->SetSsrc(
+            streams[0].first_ssrc());
       }
     }
   } else {
@@ -2911,6 +2918,72 @@
   return RTCError::OK();
 }
 
+// This method will extract any send encodings that were sent by the remote
+// connection. This is currently only relevant for Simulcast scenario (where
+// the number of layers may be communicated by the server).
+static std::vector<RtpEncodingParameters> GetSendEncodingsFromRemoteDescription(
+    const MediaContentDescription& desc) {
+  if (!desc.HasSimulcast()) {
+    return {};
+  }
+  std::vector<RtpEncodingParameters> result;
+  const SimulcastDescription& simulcast = desc.simulcast_description();
+
+  // This is a remote description, the parameters we are after should appear
+  // as receive streams.
+  for (const auto& alternatives : simulcast.receive_layers()) {
+    RTC_DCHECK(!alternatives.empty());
+    // There is currently no way to specify or choose from alternatives.
+    // We will always use the first alternative, which is the most preferred.
+    const SimulcastLayer& layer = alternatives[0];
+    RtpEncodingParameters parameters;
+    parameters.rid = layer.rid;
+    parameters.active = !layer.is_paused;
+    result.push_back(parameters);
+  }
+
+  return result;
+}
+
+static RTCError UpdateSimulcastLayerStatusInSender(
+    const std::vector<SimulcastLayer>& layers,
+    RtpSenderInterface* sender) {
+  RTC_DCHECK(sender);
+  RtpParameters parameters = sender->GetParameters();
+
+  // The simulcast envelope cannot be changed, only the status of the streams.
+  // So we will iterate over the send encodings rather than the layers.
+  for (RtpEncodingParameters& encoding : parameters.encodings) {
+    auto iter = std::find_if(layers.begin(), layers.end(),
+                             [&encoding](const SimulcastLayer& layer) {
+                               return layer.rid == encoding.rid;
+                             });
+    // A layer that cannot be found may have been removed by the remote party.
+    encoding.active = iter != layers.end() && !iter->is_paused;
+  }
+
+  return sender->SetParameters(parameters);
+}
+
+static RTCError DisableSimulcastInSender(RtpSenderInterface* sender) {
+  RTC_DCHECK(sender);
+  RtpParameters parameters = sender->GetParameters();
+  if (parameters.encodings.empty()) {
+    return RTCError::OK();
+  }
+
+  bool first_layer_status = parameters.encodings[0].active;
+  for (RtpEncodingParameters& encoding : parameters.encodings) {
+    // TODO(bugs.webrtc.org/10251): Active does not really disable the layer.
+    // The user might enable it at a later point in time even though it was
+    // rejected.
+    encoding.active = false;
+  }
+  parameters.encodings[0].active = first_layer_status;
+
+  return sender->SetParameters(parameters);
+}
+
 RTCErrorOr<rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>>
 PeerConnection::AssociateTransceiver(cricket::ContentSource source,
                                      SdpType type,
@@ -2969,8 +3042,10 @@
                        << " at i=" << mline_index
                        << " in response to the remote description.";
       std::string sender_id = rtc::CreateRandomUuid();
-      auto sender =
-          CreateSender(media_desc->type(), sender_id, nullptr, {}, {});
+      std::vector<RtpEncodingParameters> send_encodings =
+          GetSendEncodingsFromRemoteDescription(*media_desc);
+      auto sender = CreateSender(media_desc->type(), sender_id, nullptr, {},
+                                 send_encodings);
       std::string receiver_id;
       if (!media_desc->streams().empty()) {
         receiver_id = media_desc->streams()[0].id;
@@ -2982,6 +3057,22 @@
       transceiver->internal()->set_direction(
           RtpTransceiverDirection::kRecvOnly);
     }
+
+    // Check if the offer indicated simulcast but the answer rejected it.
+    // This can happen when simulcast is not supported on the remote party.
+    // This check can be simplified to comparing the number of send encodings,
+    // but that might break legacy implementation in which simulcast is not
+    // signaled in the remote description.
+    if (old_local_content && old_local_content->media_description() &&
+        old_local_content->media_description()->HasSimulcast() &&
+        !media_desc->HasSimulcast()) {
+      RTCError error =
+          DisableSimulcastInSender(transceiver->internal()->sender());
+      if (!error.ok()) {
+        RTC_LOG(LS_ERROR) << "Failed to remove rejected simulcast.";
+        return std::move(error);
+      }
+    }
   }
   RTC_DCHECK(transceiver);
   if (transceiver->media_type() != media_desc->type()) {
@@ -2989,6 +3080,20 @@
         RTCErrorType::INVALID_PARAMETER,
         "Transceiver type does not match media description type.");
   }
+  if (media_desc->HasSimulcast()) {
+    std::vector<SimulcastLayer> layers =
+        source == cricket::CS_LOCAL
+            ? media_desc->simulcast_description().send_layers().GetAllLayers()
+            : media_desc->simulcast_description()
+                  .receive_layers()
+                  .GetAllLayers();
+    RTCError error = UpdateSimulcastLayerStatusInSender(
+        layers, transceiver->internal()->sender());
+    if (!error.ok()) {
+      RTC_LOG(LS_ERROR) << "Failed updating status for simulcast layers.";
+      return std::move(error);
+    }
+  }
   // Associate the found or created RtpTransceiver with the m= section by
   // setting the value of the RtpTransceiver's mid property to the MID of the m=
   // section, and establish a mapping between the transceiver and the index of
@@ -4035,6 +4140,20 @@
                            offer_answer_options.num_simulcast_layers);
 }
 
+static void GetSimulcastInformationFromRtpParameters(
+    const RtpParameters& parameters,
+    RidDirection direction,
+    std::vector<RidDescription>* rids,
+    SimulcastLayerList* layers) {
+  for (const RtpEncodingParameters& encoding : parameters.encodings) {
+    if (encoding.rid.empty()) {
+      continue;
+    }
+    rids->push_back(RidDescription(encoding.rid, direction));
+    layers->AddLayer(SimulcastLayer(encoding.rid, !encoding.active));
+  }
+}
+
 static cricket::MediaDescriptionOptions
 GetMediaDescriptionOptionsForTransceiver(
     rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>
@@ -4048,18 +4167,46 @@
   //    sendrecv.
   // 2. If the MSID is included, then it must be included in any subsequent
   //    offer/answer exactly the same until the RtpTransceiver is stopped.
-  if (!transceiver->stopped() &&
-      (RtpTransceiverDirectionHasSend(transceiver->direction()) ||
-       transceiver->internal()->has_ever_been_used_to_send())) {
-    cricket::SenderOptions sender_options;
-    sender_options.track_id = transceiver->sender()->id();
-    sender_options.stream_ids = transceiver->sender()->stream_ids();
-    int num_send_encoding_layers =
-        transceiver->sender()->init_send_encodings().size();
-    sender_options.num_sim_layers =
-        !num_send_encoding_layers ? 1 : num_send_encoding_layers;
-    media_description_options.sender_options.push_back(sender_options);
+  if (transceiver->stopped() ||
+      (!RtpTransceiverDirectionHasSend(transceiver->direction()) &&
+       !transceiver->internal()->has_ever_been_used_to_send())) {
+    return media_description_options;
   }
+
+  cricket::SenderOptions sender_options;
+  sender_options.track_id = transceiver->sender()->id();
+  sender_options.stream_ids = transceiver->sender()->stream_ids();
+
+  // The following sets up RIDs and Simulcast.
+  // RIDs are included if Simulcast is requested or if any RID was specified.
+  RtpParameters send_parameters = transceiver->sender()->GetParameters();
+  RtpParameters receive_parameters = transceiver->receiver()->GetParameters();
+  bool has_rids = std::any_of(send_parameters.encodings.begin(),
+                              send_parameters.encodings.end(),
+                              [](const RtpEncodingParameters& encoding) {
+                                return !encoding.rid.empty();
+                              });
+
+  std::vector<RidDescription> send_rids, receive_rids;
+  SimulcastLayerList send_layers, receive_layers;
+  GetSimulcastInformationFromRtpParameters(send_parameters, RidDirection::kSend,
+                                           &send_rids, &send_layers);
+  GetSimulcastInformationFromRtpParameters(receive_parameters,
+                                           RidDirection::kReceive,
+                                           &receive_rids, &receive_layers);
+  if (has_rids) {
+    sender_options.rids = send_rids;
+  }
+
+  sender_options.simulcast_layers = send_layers;
+  media_description_options.receive_simulcast_layers = receive_layers;
+  media_description_options.receive_rids = receive_rids;
+  // When RIDs are configured, we must set num_sim_layers to 0 to.
+  // Otherwise, num_sim_layers must be 1 because either there is no
+  // simulcast, or simulcast is acheived by munging the SDP.
+  sender_options.num_sim_layers = has_rids ? 0 : 1;
+  media_description_options.sender_options.push_back(sender_options);
+
   return media_description_options;
 }
 
@@ -5930,7 +6077,7 @@
   cricket::VoiceChannel* voice_channel = channel_manager()->CreateVoiceChannel(
       call_.get(), configuration_.media_config, rtp_transport, media_transport,
       signaling_thread(), mid, SrtpRequired(), GetCryptoOptions(),
-      audio_options_);
+      &ssrc_generator_, audio_options_);
   if (!voice_channel) {
     return nullptr;
   }
@@ -5955,7 +6102,7 @@
   cricket::VideoChannel* video_channel = channel_manager()->CreateVideoChannel(
       call_.get(), configuration_.media_config, rtp_transport, media_transport,
       signaling_thread(), mid, SrtpRequired(), GetCryptoOptions(),
-      video_options_);
+      &ssrc_generator_, video_options_);
   if (!video_channel) {
     return nullptr;
   }
@@ -6002,7 +6149,7 @@
       RtpTransportInternal* rtp_transport = GetRtpTransport(mid);
       rtp_data_channel_ = channel_manager()->CreateRtpDataChannel(
           configuration_.media_config, rtp_transport, signaling_thread(), mid,
-          SrtpRequired(), GetCryptoOptions());
+          SrtpRequired(), GetCryptoOptions(), &ssrc_generator_);
       if (!rtp_data_channel_) {
         return false;
       }
diff --git a/pc/peer_connection.h b/pc/peer_connection.h
index dde19ba..7c144ce 100644
--- a/pc/peer_connection.h
+++ b/pc/peer_connection.h
@@ -1149,6 +1149,12 @@
 
   int usage_event_accumulator_ = 0;
   bool return_histogram_very_quickly_ = false;
+
+  // This object should be used to generate any SSRC that is not explicitly
+  // specified by the user (or by the remote party).
+  // The generator is not used directly, instead it is passed on to the
+  // channel manager and the session description factory.
+  rtc::UniqueRandomIdGenerator ssrc_generator_;
 };
 
 }  // namespace webrtc
diff --git a/pc/rtp_sender_receiver_unittest.cc b/pc/rtp_sender_receiver_unittest.cc
index 34092fb..f3fb63a 100644
--- a/pc/rtp_sender_receiver_unittest.cc
+++ b/pc/rtp_sender_receiver_unittest.cc
@@ -105,11 +105,13 @@
     voice_channel_ = channel_manager_.CreateVoiceChannel(
         &fake_call_, cricket::MediaConfig(), rtp_transport_.get(),
         /*media_transport=*/nullptr, rtc::Thread::Current(), cricket::CN_AUDIO,
-        srtp_required, webrtc::CryptoOptions(), cricket::AudioOptions());
+        srtp_required, webrtc::CryptoOptions(), &ssrc_generator_,
+        cricket::AudioOptions());
     video_channel_ = channel_manager_.CreateVideoChannel(
         &fake_call_, cricket::MediaConfig(), rtp_transport_.get(),
         /*media_transport=*/nullptr, rtc::Thread::Current(), cricket::CN_VIDEO,
-        srtp_required, webrtc::CryptoOptions(), cricket::VideoOptions());
+        srtp_required, webrtc::CryptoOptions(), &ssrc_generator_,
+        cricket::VideoOptions());
     voice_channel_->Enable(true);
     video_channel_->Enable(true);
     voice_media_channel_ = media_engine_->GetVoiceChannel(0);
@@ -360,6 +362,7 @@
   rtc::scoped_refptr<VideoTrackInterface> video_track_;
   rtc::scoped_refptr<AudioTrackInterface> audio_track_;
   bool audio_sender_destroyed_signal_fired_ = false;
+  rtc::UniqueRandomIdGenerator ssrc_generator_;
 };
 
 // Test that |voice_channel_| is updated when an audio track is associated
diff --git a/pc/test/fake_peer_connection_for_stats.h b/pc/test/fake_peer_connection_for_stats.h
index e33514a..b954fc5 100644
--- a/pc/test/fake_peer_connection_for_stats.h
+++ b/pc/test/fake_peer_connection_for_stats.h
@@ -125,9 +125,9 @@
         absl::make_unique<FakeVoiceMediaChannelForStats>();
     auto* voice_media_channel_ptr = voice_media_channel.get();
     voice_channel_ = absl::make_unique<cricket::VoiceChannel>(
-        worker_thread_, network_thread_, signaling_thread_, nullptr,
+        worker_thread_, network_thread_, signaling_thread_,
         std::move(voice_media_channel), mid, kDefaultSrtpRequired,
-        webrtc::CryptoOptions());
+        webrtc::CryptoOptions(), &ssrc_generator_);
     voice_channel_->set_transport_name_for_testing(transport_name);
     GetOrCreateFirstTransceiverOfType(cricket::MEDIA_TYPE_AUDIO)
         ->internal()
@@ -145,7 +145,7 @@
     video_channel_ = absl::make_unique<cricket::VideoChannel>(
         worker_thread_, network_thread_, signaling_thread_,
         std::move(video_media_channel), mid, kDefaultSrtpRequired,
-        webrtc::CryptoOptions());
+        webrtc::CryptoOptions(), &ssrc_generator_);
     video_channel_->set_transport_name_for_testing(transport_name);
     GetOrCreateFirstTransceiverOfType(cricket::MEDIA_TYPE_VIDEO)
         ->internal()
@@ -380,6 +380,8 @@
       local_certificates_by_transport_;
   std::map<std::string, std::unique_ptr<rtc::SSLCertChain>>
       remote_cert_chains_by_transport_;
+
+  rtc::UniqueRandomIdGenerator ssrc_generator_;
 };
 
 }  // namespace webrtc
diff --git a/pc/test/mock_channel_interface.h b/pc/test/mock_channel_interface.h
index b559e84..255bd2f 100644
--- a/pc/test/mock_channel_interface.h
+++ b/pc/test/mock_channel_interface.h
@@ -12,6 +12,7 @@
 #define PC_TEST_MOCK_CHANNEL_INTERFACE_H_
 
 #include <string>
+#include <vector>
 
 #include "pc/channel_interface.h"
 #include "test/gmock.h"
@@ -39,6 +40,8 @@
                bool(const cricket::MediaContentDescription*,
                     webrtc::SdpType,
                     std::string*));
+  MOCK_CONST_METHOD0(local_streams, const std::vector<StreamParams>&());
+  MOCK_CONST_METHOD0(remote_streams, const std::vector<StreamParams>&());
   MOCK_METHOD1(SetRtpTransport, bool(webrtc::RtpTransportInternal*));
 };
 
diff --git a/pc/webrtc_session_description_factory.cc b/pc/webrtc_session_description_factory.cc
index 563632c..d859559 100644
--- a/pc/webrtc_session_description_factory.cc
+++ b/pc/webrtc_session_description_factory.cc
@@ -31,6 +31,7 @@
 #include "rtc_base/string_encode.h"
 
 using cricket::MediaSessionOptions;
+using rtc::UniqueRandomIdGenerator;
 
 namespace webrtc {
 namespace {
@@ -131,9 +132,12 @@
     PeerConnectionInternal* pc,
     const std::string& session_id,
     std::unique_ptr<rtc::RTCCertificateGeneratorInterface> cert_generator,
-    const rtc::scoped_refptr<rtc::RTCCertificate>& certificate)
+    const rtc::scoped_refptr<rtc::RTCCertificate>& certificate,
+    UniqueRandomIdGenerator* ssrc_generator)
     : signaling_thread_(signaling_thread),
-      session_desc_factory_(channel_manager, &transport_desc_factory_),
+      session_desc_factory_(channel_manager,
+                            &transport_desc_factory_,
+                            ssrc_generator),
       // RFC 4566 suggested a Network Time Protocol (NTP) format timestamp
       // as the session id and session version. To simplify, it should be fine
       // to just use a random number as session id and start version from
diff --git a/pc/webrtc_session_description_factory.h b/pc/webrtc_session_description_factory.h
index af689a9..b94ddbf 100644
--- a/pc/webrtc_session_description_factory.h
+++ b/pc/webrtc_session_description_factory.h
@@ -30,6 +30,7 @@
 #include "rtc_base/rtc_certificate_generator.h"
 #include "rtc_base/third_party/sigslot/sigslot.h"
 #include "rtc_base/thread.h"
+#include "rtc_base/unique_id_generator.h"
 
 namespace webrtc {
 
@@ -82,7 +83,8 @@
       PeerConnectionInternal* pc,
       const std::string& session_id,
       std::unique_ptr<rtc::RTCCertificateGeneratorInterface> cert_generator,
-      const rtc::scoped_refptr<rtc::RTCCertificate>& certificate);
+      const rtc::scoped_refptr<rtc::RTCCertificate>& certificate,
+      rtc::UniqueRandomIdGenerator* ssrc_generator);
   virtual ~WebRtcSessionDescriptionFactory();
 
   static void CopyCandidatesFromSessionDescription(