Only expose RTCCodecStats that are referenced by an RTP stream.

According to a pprof, creating RTCCodecStats is one of the places where
we spend the most CPU time in the event of creating hundreds of them:
https://screenshot.googleplex.com/B6QNDvvoX8dK5vk

The lifetime was recently updated so that we no longer have to risk
creating hundreds of them, here is the relevant section:
https://w3c.github.io/webrtc-stats/#codec-dict*

This allows code simplifications and the deletion of
ProduceCodecStats_n since we can now do a lazy instantiation of codec
stats at the point of being referenced.

Bug: webrtc:14444
Change-Id: I342c5bfebe6a4be0359da3ea106692c7a217779e
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/275763
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Henrik Boström <hbos@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#38209}
diff --git a/pc/rtc_stats_collector.cc b/pc/rtc_stats_collector.cc
index 407f33b..3883a64 100644
--- a/pc/rtc_stats_collector.cc
+++ b/pc/rtc_stats_collector.cc
@@ -80,6 +80,9 @@
   char buf[1024];
   rtc::SimpleStringBuilder sb(buf);
   sb << 'C' << direction << transport_id << '_' << codec_params.payload_type;
+  // TODO(https://crbug.com/webrtc/14420): If we stop supporting different FMTP
+  // lines for the same PT and transport, which should be illegal SDP, then we
+  // wouldn't need `fmtp` to be part of the ID here.
   rtc::StringBuilder fmtp;
   if (WriteFmtpParameters(codec_params.parameters, &fmtp)) {
     sb << '_' << fmtp.Release();
@@ -354,19 +357,27 @@
   return audio_level / 32767.0;
 }
 
-std::unique_ptr<RTCCodecStats> CodecStatsFromRtpCodecParameters(
+// Gets the `codecId` identified by `transport_id` and `codec_params`. If no
+// such `RTCCodecStats` exist yet, create it and add it to `report`.
+std::string GetCodecIdAndMaybeCreateCodecStats(
     uint64_t timestamp_us,
     const char direction,
     const std::string& transport_id,
-    const RtpCodecParameters& codec_params) {
+    const RtpCodecParameters& codec_params,
+    RTCStatsReport* report) {
   RTC_DCHECK_GE(codec_params.payload_type, 0);
   RTC_DCHECK_LE(codec_params.payload_type, 127);
   RTC_DCHECK(codec_params.clock_rate);
   uint32_t payload_type = static_cast<uint32_t>(codec_params.payload_type);
-  std::unique_ptr<RTCCodecStats> codec_stats(std::make_unique<RTCCodecStats>(
-      RTCCodecStatsIDFromTransportAndCodecParameters(direction, transport_id,
-                                                     codec_params),
-      timestamp_us));
+  std::string codec_id = RTCCodecStatsIDFromTransportAndCodecParameters(
+      direction, transport_id, codec_params);
+  if (report->Get(codec_id) != nullptr) {
+    // The `RTCCodecStats` already exists.
+    return codec_id;
+  }
+  // Create the `RTCCodecStats` that we want to reference.
+  std::unique_ptr<RTCCodecStats> codec_stats(
+      std::make_unique<RTCCodecStats>(codec_id, timestamp_us));
   codec_stats->payload_type = payload_type;
   codec_stats->mime_type = codec_params.mime_type();
   if (codec_params.clock_rate) {
@@ -381,7 +392,8 @@
     codec_stats->sdp_fmtp_line = fmtp.Release();
   }
   codec_stats->transport_id = transport_id;
-  return codec_stats;
+  report->AddStats(std::move(codec_stats));
+  return codec_id;
 }
 
 void SetMediaStreamTrackStatsFromMediaStreamTrackInterface(
@@ -427,7 +439,8 @@
     const cricket::VoiceReceiverInfo& voice_receiver_info,
     const std::string& transport_id,
     const std::string& mid,
-    int64_t timestamp_us) {
+    int64_t timestamp_us,
+    RTCStatsReport* report) {
   auto inbound_audio = std::make_unique<RTCInboundRTPStreamStats>(
       /*id=*/RTCInboundRTPStreamStatsIDFromSSRC(
           transport_id, cricket::MEDIA_TYPE_AUDIO, voice_receiver_info.ssrc()),
@@ -443,8 +456,9 @@
         voice_receiver_info.codec_payload_type.value());
     RTC_DCHECK(codec_param_it != voice_media_info.receive_codecs.end());
     if (codec_param_it != voice_media_info.receive_codecs.end()) {
-      inbound_audio->codec_id = RTCCodecStatsIDFromTransportAndCodecParameters(
-          kDirectionInbound, transport_id, codec_param_it->second);
+      inbound_audio->codec_id = GetCodecIdAndMaybeCreateCodecStats(
+          inbound_audio->timestamp_us(), kDirectionInbound, transport_id,
+          codec_param_it->second, report);
     }
   }
   inbound_audio->jitter = static_cast<double>(voice_receiver_info.jitter_ms) /
@@ -541,7 +555,8 @@
     const std::string& mid,
     const cricket::VideoMediaInfo& video_media_info,
     const cricket::VideoReceiverInfo& video_receiver_info,
-    RTCInboundRTPStreamStats* inbound_video) {
+    RTCInboundRTPStreamStats* inbound_video,
+    RTCStatsReport* report) {
   SetInboundRTPStreamStatsFromMediaReceiverInfo(video_receiver_info,
                                                 inbound_video);
   inbound_video->transport_id = transport_id;
@@ -553,8 +568,9 @@
         video_receiver_info.codec_payload_type.value());
     RTC_DCHECK(codec_param_it != video_media_info.receive_codecs.end());
     if (codec_param_it != video_media_info.receive_codecs.end()) {
-      inbound_video->codec_id = RTCCodecStatsIDFromTransportAndCodecParameters(
-          kDirectionInbound, transport_id, codec_param_it->second);
+      inbound_video->codec_id = GetCodecIdAndMaybeCreateCodecStats(
+          inbound_video->timestamp_us(), kDirectionInbound, transport_id,
+          codec_param_it->second, report);
     }
   }
   inbound_video->jitter = static_cast<double>(video_receiver_info.jitter_ms) /
@@ -643,7 +659,8 @@
     const std::string& mid,
     const cricket::VoiceMediaInfo& voice_media_info,
     const cricket::VoiceSenderInfo& voice_sender_info,
-    RTCOutboundRTPStreamStats* outbound_audio) {
+    RTCOutboundRTPStreamStats* outbound_audio,
+    RTCStatsReport* report) {
   SetOutboundRTPStreamStatsFromMediaSenderInfo(voice_sender_info,
                                                outbound_audio);
   outbound_audio->transport_id = transport_id;
@@ -659,8 +676,9 @@
         voice_sender_info.codec_payload_type.value());
     RTC_DCHECK(codec_param_it != voice_media_info.send_codecs.end());
     if (codec_param_it != voice_media_info.send_codecs.end()) {
-      outbound_audio->codec_id = RTCCodecStatsIDFromTransportAndCodecParameters(
-          kDirectionOutbound, transport_id, codec_param_it->second);
+      outbound_audio->codec_id = GetCodecIdAndMaybeCreateCodecStats(
+          outbound_audio->timestamp_us(), kDirectionOutbound, transport_id,
+          codec_param_it->second, report);
     }
   }
   // `fir_count`, `pli_count` and `sli_count` are only valid for video and are
@@ -672,7 +690,8 @@
     const std::string& mid,
     const cricket::VideoMediaInfo& video_media_info,
     const cricket::VideoSenderInfo& video_sender_info,
-    RTCOutboundRTPStreamStats* outbound_video) {
+    RTCOutboundRTPStreamStats* outbound_video,
+    RTCStatsReport* report) {
   SetOutboundRTPStreamStatsFromMediaSenderInfo(video_sender_info,
                                                outbound_video);
   outbound_video->transport_id = transport_id;
@@ -684,8 +703,9 @@
         video_sender_info.codec_payload_type.value());
     RTC_DCHECK(codec_param_it != video_media_info.send_codecs.end());
     if (codec_param_it != video_media_info.send_codecs.end()) {
-      outbound_video->codec_id = RTCCodecStatsIDFromTransportAndCodecParameters(
-          kDirectionOutbound, transport_id, codec_param_it->second);
+      outbound_video->codec_id = GetCodecIdAndMaybeCreateCodecStats(
+          outbound_video->timestamp_us(), kDirectionOutbound, transport_id,
+          codec_param_it->second, report);
     }
   }
   outbound_video->fir_count =
@@ -1484,7 +1504,6 @@
   rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;
 
   ProduceCertificateStats_n(timestamp_us, transport_cert_stats, partial_report);
-  ProduceCodecStats_n(timestamp_us, transceiver_stats_infos_, partial_report);
   ProduceIceCandidateAndPairStats_n(timestamp_us, transport_stats_by_name,
                                     call_stats_, partial_report);
   ProduceTransportStats_n(timestamp_us, transport_stats_by_name,
@@ -1583,100 +1602,6 @@
   }
 }
 
-void RTCStatsCollector::ProduceCodecStats_n(
-    int64_t timestamp_us,
-    const std::vector<RtpTransceiverStatsInfo>& transceiver_stats_infos,
-    RTCStatsReport* report) const {
-  RTC_DCHECK_RUN_ON(network_thread_);
-  rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;
-
-  // For each transport, payload types does uniquely identify codecs, but the
-  // FMTP line could in theory be different on different m= sections. As such,
-  // the (PT,FMTP) pair on a per-transport basis uniquely identifies an
-  // RTCCodecStats. These maps are used to avoid duplicates.
-  // TODO(https://crbug.com/webrtc/14420): If we stop supporting different FMTP
-  // lines, this can be simplified to only looking at the set of PTs.
-  typedef std::pair<int, cricket::CodecParameterMap>
-      PayloadTypeAndCodecParametersMap;
-  std::map<std::string, std::set<PayloadTypeAndCodecParametersMap>>
-      send_codecs_by_transport;
-  std::map<std::string, std::set<PayloadTypeAndCodecParametersMap>>
-      receive_codecs_by_transport;
-
-  for (const auto& stats : transceiver_stats_infos) {
-    if (!stats.mid) {
-      continue;
-    }
-    std::string transport_id = RTCTransportStatsIDFromTransportChannel(
-        *stats.transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP);
-
-    // Codecs (PT,FMTP) seen so far for this transport.
-    std::set<PayloadTypeAndCodecParametersMap>& send_codecs =
-        send_codecs_by_transport[transport_id];
-    std::set<PayloadTypeAndCodecParametersMap>& receive_codecs =
-        receive_codecs_by_transport[transport_id];
-
-    // Audio
-    if (stats.track_media_info_map.voice_media_info().has_value()) {
-      // Inbound
-      for (const auto& pair :
-           stats.track_media_info_map.voice_media_info()->receive_codecs) {
-        auto& codec = pair.second;
-        if (receive_codecs
-                .insert(PayloadTypeAndCodecParametersMap(codec.payload_type,
-                                                         codec.parameters))
-                .second != true) {
-          continue;  // (PT,FMTP) already seen.
-        }
-        report->AddStats(CodecStatsFromRtpCodecParameters(
-            timestamp_us, kDirectionInbound, transport_id, codec));
-      }
-      // Outbound
-      for (const auto& pair :
-           stats.track_media_info_map.voice_media_info()->send_codecs) {
-        auto& codec = pair.second;
-        if (send_codecs
-                .insert(PayloadTypeAndCodecParametersMap(codec.payload_type,
-                                                         codec.parameters))
-                .second != true) {
-          continue;  // (PT,FMTP) already seen.
-        }
-        report->AddStats(CodecStatsFromRtpCodecParameters(
-            timestamp_us, kDirectionOutbound, transport_id, codec));
-      }
-    }
-    // Video
-    if (stats.track_media_info_map.video_media_info().has_value()) {
-      // Inbound
-      for (const auto& pair :
-           stats.track_media_info_map.video_media_info()->receive_codecs) {
-        auto& codec = pair.second;
-        if (receive_codecs
-                .insert(PayloadTypeAndCodecParametersMap(codec.payload_type,
-                                                         codec.parameters))
-                .second != true) {
-          continue;  // (PT,FMTP) already seen.
-        }
-        report->AddStats(CodecStatsFromRtpCodecParameters(
-            timestamp_us, kDirectionInbound, transport_id, codec));
-      }
-      // Outbound
-      for (const auto& pair :
-           stats.track_media_info_map.video_media_info()->send_codecs) {
-        auto& codec = pair.second;
-        if (send_codecs
-                .insert(PayloadTypeAndCodecParametersMap(codec.payload_type,
-                                                         codec.parameters))
-                .second != true) {
-          continue;  // (PT,FMTP) already seen.
-        }
-        report->AddStats(CodecStatsFromRtpCodecParameters(
-            timestamp_us, kDirectionOutbound, transport_id, codec));
-      }
-    }
-  }
-}
-
 void RTCStatsCollector::ProduceDataChannelStats_s(
     int64_t timestamp_us,
     RTCStatsReport* report) const {
@@ -2020,7 +1945,7 @@
     // Inbound.
     auto inbound_audio = CreateInboundAudioStreamStats(
         stats.track_media_info_map.voice_media_info().value(),
-        voice_receiver_info, transport_id, mid, timestamp_us);
+        voice_receiver_info, transport_id, mid, timestamp_us, report);
     // TODO(hta): This lookup should look for the sender, not the track.
     rtc::scoped_refptr<AudioTrackInterface> audio_track =
         stats.track_media_info_map.GetAudioTrack(voice_receiver_info);
@@ -2068,7 +1993,7 @@
     SetOutboundRTPStreamStatsFromVoiceSenderInfo(
         transport_id, mid,
         stats.track_media_info_map.voice_media_info().value(),
-        voice_sender_info, outbound_audio.get());
+        voice_sender_info, outbound_audio.get(), report);
     rtc::scoped_refptr<AudioTrackInterface> audio_track =
         stats.track_media_info_map.GetAudioTrack(voice_sender_info);
     if (audio_track) {
@@ -2133,7 +2058,7 @@
     SetInboundRTPStreamStatsFromVideoReceiverInfo(
         transport_id, mid,
         stats.track_media_info_map.video_media_info().value(),
-        video_receiver_info, inbound_video.get());
+        video_receiver_info, inbound_video.get(), report);
     rtc::scoped_refptr<VideoTrackInterface> video_track =
         stats.track_media_info_map.GetVideoTrack(video_receiver_info);
     if (video_track) {
@@ -2162,7 +2087,7 @@
     SetOutboundRTPStreamStatsFromVideoSenderInfo(
         transport_id, mid,
         stats.track_media_info_map.video_media_info().value(),
-        video_sender_info, outbound_video.get());
+        video_sender_info, outbound_video.get(), report);
     rtc::scoped_refptr<VideoTrackInterface> video_track =
         stats.track_media_info_map.GetVideoTrack(video_sender_info);
     if (video_track) {
diff --git a/pc/rtc_stats_collector.h b/pc/rtc_stats_collector.h
index e5c6aed..0c297c4 100644
--- a/pc/rtc_stats_collector.h
+++ b/pc/rtc_stats_collector.h
@@ -180,11 +180,6 @@
       int64_t timestamp_us,
       const std::map<std::string, CertificateStatsPair>& transport_cert_stats,
       RTCStatsReport* report) const;
-  // Produces `RTCCodecStats`.
-  void ProduceCodecStats_n(
-      int64_t timestamp_us,
-      const std::vector<RtpTransceiverStatsInfo>& transceiver_stats_infos,
-      RTCStatsReport* report) const;
   // Produces `RTCDataChannelStats`.
   void ProduceDataChannelStats_s(int64_t timestamp_us,
                                  RTCStatsReport* report) const;
@@ -208,9 +203,11 @@
   // Produces `RTCPeerConnectionStats`.
   void ProducePeerConnectionStats_s(int64_t timestamp_us,
                                     RTCStatsReport* report) const;
-  // Produces `RTCInboundRTPStreamStats` and `RTCOutboundRTPStreamStats`.
-  // This has to be invoked after codecs and transport stats have been created
-  // because some metrics are calculated through lookup of other metrics.
+  // Produces `RTCInboundRTPStreamStats`, `RTCOutboundRTPStreamStats`,
+  // `RTCRemoteInboundRtpStreamStats`, `RTCRemoteOutboundRtpStreamStats` and any
+  // referenced `RTCCodecStats`. This has to be invoked after transport stats
+  // have been created because some metrics are calculated through lookup of
+  // other metrics.
   void ProduceRTPStreamStats_n(
       int64_t timestamp_us,
       const std::vector<RtpTransceiverStatsInfo>& transceiver_stats_infos,
diff --git a/pc/rtc_stats_collector_unittest.cc b/pc/rtc_stats_collector_unittest.cc
index 88bb6ae..371f120 100644
--- a/pc/rtc_stats_collector_unittest.cc
+++ b/pc/rtc_stats_collector_unittest.cc
@@ -1095,7 +1095,7 @@
   // should look. We only care about not crashing.
 }
 
-TEST_F(RTCStatsCollectorTest, CollectRTCCodecStats) {
+TEST_F(RTCStatsCollectorTest, CollectRTCCodecStatsOnlyIfReferenced) {
   // Audio
   cricket::VoiceMediaInfo voice_media_info;
 
@@ -1118,8 +1118,6 @@
   voice_media_info.send_codecs.insert(
       std::make_pair(outbound_audio_codec.payload_type, outbound_audio_codec));
 
-  pc_->AddVoiceChannel("AudioMid", "TransportName", voice_media_info);
-
   // Video
   cricket::VideoMediaInfo video_media_info;
 
@@ -1142,7 +1140,31 @@
   video_media_info.send_codecs.insert(
       std::make_pair(outbound_video_codec.payload_type, outbound_video_codec));
 
-  pc_->AddVideoChannel("VideoMid", "TransportName", video_media_info);
+  // Ensure the above codecs are referenced.
+  cricket::VoiceReceiverInfo inbound_audio_info;
+  inbound_audio_info.add_ssrc(10);
+  inbound_audio_info.codec_payload_type = 1;
+  voice_media_info.receivers.push_back(inbound_audio_info);
+
+  cricket::VoiceSenderInfo outbound_audio_info;
+  outbound_audio_info.add_ssrc(20);
+  outbound_audio_info.codec_payload_type = 2;
+  voice_media_info.senders.push_back(outbound_audio_info);
+
+  cricket::VideoReceiverInfo inbound_video_info;
+  inbound_video_info.add_ssrc(30);
+  inbound_video_info.codec_payload_type = 3;
+  video_media_info.receivers.push_back(inbound_video_info);
+
+  cricket::VideoSenderInfo outbound_video_info;
+  outbound_video_info.add_ssrc(40);
+  outbound_video_info.codec_payload_type = 4;
+  video_media_info.senders.push_back(outbound_video_info);
+
+  FakeVoiceMediaChannelForStats* audio_channel =
+      pc_->AddVoiceChannel("AudioMid", "TransportName", voice_media_info);
+  FakeVideoMediaChannelForStats* video_channel =
+      pc_->AddVideoChannel("VideoMid", "TransportName", video_media_info);
 
   rtc::scoped_refptr<const RTCStatsReport> report = stats_->GetStatsReport();
 
@@ -1200,6 +1222,22 @@
   EXPECT_EQ(expected_outbound_video_codec,
             report->Get(expected_outbound_video_codec.id())
                 ->cast_to<RTCCodecStats>());
+
+  // Now remove all the RTP streams such that there are no live codecId
+  // references to the codecs, this should result in none of the RTCCodecStats
+  // being exposed, despite `send_codecs` and `receive_codecs` still being set.
+  voice_media_info.senders.clear();
+  voice_media_info.receivers.clear();
+  audio_channel->SetStats(voice_media_info);
+  video_media_info.senders.clear();
+  video_media_info.receivers.clear();
+  video_channel->SetStats(video_media_info);
+  stats_->stats_collector()->ClearCachedStatsReport();
+  report = stats_->GetStatsReport();
+  EXPECT_FALSE(report->Get(expected_inbound_audio_codec.id()));
+  EXPECT_FALSE(report->Get(expected_outbound_audio_codec.id()));
+  EXPECT_FALSE(report->Get(expected_inbound_video_codec.id()));
+  EXPECT_FALSE(report->Get(expected_outbound_video_codec.id()));
 }
 
 TEST_F(RTCStatsCollectorTest, CodecStatsAreCollectedPerTransport) {
@@ -1217,17 +1255,35 @@
   outbound_codec_pt11.name = "VP8";
   outbound_codec_pt11.clock_rate = 9000;
 
+  // Insert codecs into `send_codecs` and ensure the PTs are referenced by RTP
+  // streams.
   cricket::VideoMediaInfo info_pt10;
   info_pt10.send_codecs.insert(
       std::make_pair(outbound_codec_pt10.payload_type, outbound_codec_pt10));
+  info_pt10.senders.emplace_back();
+  info_pt10.senders[0].add_ssrc(42);
+  info_pt10.senders[0].codec_payload_type = outbound_codec_pt10.payload_type;
+
   cricket::VideoMediaInfo info_pt11;
   info_pt11.send_codecs.insert(
       std::make_pair(outbound_codec_pt11.payload_type, outbound_codec_pt11));
+  info_pt11.senders.emplace_back();
+  info_pt11.senders[0].add_ssrc(43);
+  info_pt11.senders[0].codec_payload_type = outbound_codec_pt11.payload_type;
+
   cricket::VideoMediaInfo info_pt10_pt11;
   info_pt10_pt11.send_codecs.insert(
       std::make_pair(outbound_codec_pt10.payload_type, outbound_codec_pt10));
   info_pt10_pt11.send_codecs.insert(
       std::make_pair(outbound_codec_pt11.payload_type, outbound_codec_pt11));
+  info_pt10_pt11.senders.emplace_back();
+  info_pt10_pt11.senders[0].add_ssrc(44);
+  info_pt10_pt11.senders[0].codec_payload_type =
+      outbound_codec_pt10.payload_type;
+  info_pt10_pt11.senders.emplace_back();
+  info_pt10_pt11.senders[1].add_ssrc(45);
+  info_pt10_pt11.senders[1].codec_payload_type =
+      outbound_codec_pt11.payload_type;
 
   // First two mids contain subsets, the third one contains all PTs.
   pc_->AddVideoChannel("Mid1", "FirstTransport", info_pt10);
@@ -1269,11 +1325,19 @@
       std::make_pair("useinbandfec", "1"));
 
   cricket::VideoMediaInfo info_nofec;
-  info_nofec.send_codecs.insert(std::make_pair(
+  info_nofec.receive_codecs.insert(std::make_pair(
       inbound_codec_pt111_nofec.payload_type, inbound_codec_pt111_nofec));
+  info_nofec.receivers.emplace_back();
+  info_nofec.receivers[0].add_ssrc(123);
+  info_nofec.receivers[0].codec_payload_type =
+      inbound_codec_pt111_nofec.payload_type;
   cricket::VideoMediaInfo info_fec;
-  info_fec.send_codecs.insert(std::make_pair(
+  info_fec.receive_codecs.insert(std::make_pair(
       inbound_codec_pt111_fec.payload_type, inbound_codec_pt111_fec));
+  info_fec.receivers.emplace_back();
+  info_fec.receivers[0].add_ssrc(321);
+  info_fec.receivers[0].codec_payload_type =
+      inbound_codec_pt111_fec.payload_type;
 
   // First two mids contain subsets, the third one contains all PTs.
   pc_->AddVideoChannel("Mid1", "BundledTransport", info_nofec);
@@ -1285,6 +1349,10 @@
   auto codec_stats = report->GetStatsOfType<RTCCodecStats>();
   EXPECT_EQ(codec_stats.size(), 2u);
 
+  // Ensure SSRC uniqueness before the next AddVideoChannel() call. SSRCs need
+  // to be unique on different m= sections when using BUNDLE.
+  info_nofec.receivers[0].local_stats[0].ssrc = 12;
+  info_fec.receivers[0].local_stats[0].ssrc = 21;
   // Adding more m= sections that does have the same FMTP lines does not result
   // in duplicates.
   pc_->AddVideoChannel("Mid3", "BundledTransport", info_nofec);
@@ -1304,8 +1372,12 @@
   inbound_codec_pt112_fec.parameters.insert(
       std::make_pair("useinbandfec", "1"));
   cricket::VideoMediaInfo info_fec_pt112;
-  info_fec_pt112.send_codecs.insert(std::make_pair(
+  info_fec_pt112.receive_codecs.insert(std::make_pair(
       inbound_codec_pt112_fec.payload_type, inbound_codec_pt112_fec));
+  info_fec_pt112.receivers.emplace_back();
+  info_fec_pt112.receivers[0].add_ssrc(112);
+  info_fec_pt112.receivers[0].codec_payload_type =
+      inbound_codec_pt112_fec.payload_type;
   pc_->AddVideoChannel("Mid5", "BundledTransport", info_fec_pt112);
   stats_->stats_collector()->ClearCachedStatsReport();
   report = stats_->GetStatsReport();