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}
This commit is contained in:
Henrik Boström 2022-09-26 14:56:21 +02:00 committed by WebRTC LUCI CQ
parent 136ef25acb
commit 31c373b865
3 changed files with 127 additions and 133 deletions

View File

@ -80,6 +80,9 @@ std::string RTCCodecStatsIDFromTransportAndCodecParameters(
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 @@ double DoubleAudioLevelFromIntAudioLevel(int audio_level) {
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 @@ std::unique_ptr<RTCCodecStats> CodecStatsFromRtpCodecParameters(
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 @@ std::unique_ptr<RTCInboundRTPStreamStats> CreateInboundAudioStreamStats(
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 @@ std::unique_ptr<RTCInboundRTPStreamStats> CreateInboundAudioStreamStats(
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 @@ void SetInboundRTPStreamStatsFromVideoReceiverInfo(
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 @@ void SetInboundRTPStreamStatsFromVideoReceiverInfo(
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 @@ void SetOutboundRTPStreamStatsFromVoiceSenderInfo(
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 @@ void SetOutboundRTPStreamStatsFromVoiceSenderInfo(
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 @@ void SetOutboundRTPStreamStatsFromVideoSenderInfo(
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 @@ void SetOutboundRTPStreamStatsFromVideoSenderInfo(
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 @@ void RTCStatsCollector::ProducePartialResultsOnNetworkThreadImpl(
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::ProduceCertificateStats_n(
}
}
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 @@ void RTCStatsCollector::ProduceAudioRTPStreamStats_n(
// 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 @@ void RTCStatsCollector::ProduceAudioRTPStreamStats_n(
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 @@ void RTCStatsCollector::ProduceVideoRTPStreamStats_n(
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 @@ void RTCStatsCollector::ProduceVideoRTPStreamStats_n(
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) {

View File

@ -180,11 +180,6 @@ class RTCStatsCollector : public rtc::RefCountInterface,
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 @@ class RTCStatsCollector : public rtc::RefCountInterface,
// 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,

View File

@ -1095,7 +1095,7 @@ TEST_F(RTCStatsCollectorTest, InvalidSsrcCollisionDoesNotCrash) {
// 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 @@ TEST_F(RTCStatsCollectorTest, CollectRTCCodecStats) {
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 @@ TEST_F(RTCStatsCollectorTest, CollectRTCCodecStats) {
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 @@ TEST_F(RTCStatsCollectorTest, CollectRTCCodecStats) {
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 @@ TEST_F(RTCStatsCollectorTest, CodecStatsAreCollectedPerTransport) {
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 @@ TEST_F(RTCStatsCollectorTest, SamePayloadTypeButDifferentFmtpLines) {
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 @@ TEST_F(RTCStatsCollectorTest, SamePayloadTypeButDifferentFmtpLines) {
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 @@ TEST_F(RTCStatsCollectorTest, SamePayloadTypeButDifferentFmtpLines) {
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();