[Stats] Add support for SSRC collisions.

In non-BUNDLE use cases, it is possible for multiple RTP streams to have
the same SSRC (as long as the SSRC is unique within the same transport).

This CL adds support for "outbound-rtp" and "inbound-rtp" stream stats
to have the same SSRC on different transports by adding the transport to
the stats ID. This avoids multiple RTP stream stats having the same
stats ID and fixes the problem. It's a stupid use case, but it should
work.

There could still be a stats ID collision in the event of multiple
"remote-inbound-rtp" or "remote-outbound-rtp" reference the same SSRC
but on separate transports for the same reason, and would require the
same fix... but one bug at a time. Not addressed in this CL.

Bug: webrtc:14443
Change-Id: I1a2ffd79fc67c2765e6dbd1ccc6828d4e91c4589
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/275769
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Henrik Boström <hbos@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#38201}
This commit is contained in:
Henrik Boström 2022-09-26 12:36:44 +02:00 committed by WebRTC LUCI CQ
parent 182044184e
commit b43e3bbd87
2 changed files with 40 additions and 33 deletions

View File

@ -114,19 +114,23 @@ std::string RTCTransportStatsIDFromTransportChannel(
return sb.str();
}
std::string RTCInboundRTPStreamStatsIDFromSSRC(cricket::MediaType media_type,
std::string RTCInboundRTPStreamStatsIDFromSSRC(const std::string& transport_id,
cricket::MediaType media_type,
uint32_t ssrc) {
char buf[1024];
rtc::SimpleStringBuilder sb(buf);
sb << 'I' << (media_type == cricket::MEDIA_TYPE_AUDIO ? 'A' : 'V') << ssrc;
sb << 'I' << transport_id
<< (media_type == cricket::MEDIA_TYPE_AUDIO ? 'A' : 'V') << ssrc;
return sb.str();
}
std::string RTCOutboundRTPStreamStatsIDFromSSRC(cricket::MediaType media_type,
std::string RTCOutboundRTPStreamStatsIDFromSSRC(const std::string& transport_id,
cricket::MediaType media_type,
uint32_t ssrc) {
char buf[1024];
rtc::SimpleStringBuilder sb(buf);
sb << 'O' << (media_type == cricket::MEDIA_TYPE_AUDIO ? 'A' : 'V') << ssrc;
sb << 'O' << transport_id
<< (media_type == cricket::MEDIA_TYPE_AUDIO ? 'A' : 'V') << ssrc;
return sb.str();
}
@ -425,8 +429,8 @@ std::unique_ptr<RTCInboundRTPStreamStats> CreateInboundAudioStreamStats(
const std::string& mid,
int64_t timestamp_us) {
auto inbound_audio = std::make_unique<RTCInboundRTPStreamStats>(
/*id=*/RTCInboundRTPStreamStatsIDFromSSRC(cricket::MEDIA_TYPE_AUDIO,
voice_receiver_info.ssrc()),
/*id=*/RTCInboundRTPStreamStatsIDFromSSRC(
transport_id, cricket::MEDIA_TYPE_AUDIO, voice_receiver_info.ssrc()),
timestamp_us);
SetInboundRTPStreamStatsFromMediaReceiverInfo(voice_receiver_info,
inbound_audio.get());
@ -740,6 +744,7 @@ void SetOutboundRTPStreamStatsFromVideoSenderInfo(
std::unique_ptr<RTCRemoteInboundRtpStreamStats>
ProduceRemoteInboundRtpStreamStatsFromReportBlockData(
const std::string& transport_id,
const ReportBlockData& report_block_data,
cricket::MediaType media_type,
const std::map<std::string, RTCOutboundRTPStreamStats*>& outbound_rtps,
@ -767,8 +772,8 @@ ProduceRemoteInboundRtpStreamStatsFromReportBlockData(
remote_inbound->round_trip_time_measurements =
report_block_data.num_rtts();
std::string local_id =
RTCOutboundRTPStreamStatsIDFromSSRC(media_type, report_block.source_ssrc);
std::string local_id = RTCOutboundRTPStreamStatsIDFromSSRC(
transport_id, media_type, report_block.source_ssrc);
// Look up local stat from `outbound_rtps` where the pointers are non-const.
auto local_id_it = outbound_rtps.find(local_id);
if (local_id_it != outbound_rtps.end()) {
@ -777,9 +782,7 @@ ProduceRemoteInboundRtpStreamStatsFromReportBlockData(
outbound_rtp.remote_id = remote_inbound->id();
// The RTP/RTCP transport is obtained from the
// RTCOutboundRtpStreamStats's transport.
const auto* transport_from_id = outbound_rtp.transport_id.is_defined()
? report.Get(*outbound_rtp.transport_id)
: nullptr;
const auto* transport_from_id = report.Get(transport_id);
if (transport_from_id) {
const auto& transport = transport_from_id->cast_to<RTCTransportStats>();
// If RTP and RTCP are not multiplexed, there is a separate RTCP
@ -2050,8 +2053,8 @@ void RTCStatsCollector::ProduceAudioRTPStreamStats_n(
if (!voice_sender_info.connected())
continue;
auto outbound_audio = std::make_unique<RTCOutboundRTPStreamStats>(
RTCOutboundRTPStreamStatsIDFromSSRC(cricket::MEDIA_TYPE_AUDIO,
voice_sender_info.ssrc()),
RTCOutboundRTPStreamStatsIDFromSSRC(
transport_id, cricket::MEDIA_TYPE_AUDIO, voice_sender_info.ssrc()),
timestamp_us);
SetOutboundRTPStreamStatsFromVoiceSenderInfo(
transport_id, mid,
@ -2088,8 +2091,8 @@ void RTCStatsCollector::ProduceAudioRTPStreamStats_n(
stats.track_media_info_map.voice_media_info()->senders) {
for (const auto& report_block_data : voice_sender_info.report_block_datas) {
report->AddStats(ProduceRemoteInboundRtpStreamStatsFromReportBlockData(
report_block_data, cricket::MEDIA_TYPE_AUDIO, audio_outbound_rtps,
*report));
transport_id, report_block_data, cricket::MEDIA_TYPE_AUDIO,
audio_outbound_rtps, *report));
}
}
}
@ -2114,7 +2117,8 @@ void RTCStatsCollector::ProduceVideoRTPStreamStats_n(
if (!video_receiver_info.connected())
continue;
auto inbound_video = std::make_unique<RTCInboundRTPStreamStats>(
RTCInboundRTPStreamStatsIDFromSSRC(cricket::MEDIA_TYPE_VIDEO,
RTCInboundRTPStreamStatsIDFromSSRC(transport_id,
cricket::MEDIA_TYPE_VIDEO,
video_receiver_info.ssrc()),
timestamp_us);
SetInboundRTPStreamStatsFromVideoReceiverInfo(
@ -2143,8 +2147,8 @@ void RTCStatsCollector::ProduceVideoRTPStreamStats_n(
if (!video_sender_info.connected())
continue;
auto outbound_video = std::make_unique<RTCOutboundRTPStreamStats>(
RTCOutboundRTPStreamStatsIDFromSSRC(cricket::MEDIA_TYPE_VIDEO,
video_sender_info.ssrc()),
RTCOutboundRTPStreamStatsIDFromSSRC(
transport_id, cricket::MEDIA_TYPE_VIDEO, video_sender_info.ssrc()),
timestamp_us);
SetOutboundRTPStreamStatsFromVideoSenderInfo(
transport_id, mid,
@ -2181,8 +2185,8 @@ void RTCStatsCollector::ProduceVideoRTPStreamStats_n(
stats.track_media_info_map.video_media_info()->senders) {
for (const auto& report_block_data : video_sender_info.report_block_datas) {
report->AddStats(ProduceRemoteInboundRtpStreamStatsFromReportBlockData(
report_block_data, cricket::MEDIA_TYPE_VIDEO, video_outbound_rtps,
*report));
transport_id, report_block_data, cricket::MEDIA_TYPE_VIDEO,
video_outbound_rtps, *report));
}
}
}

View File

@ -720,7 +720,7 @@ class RTCStatsCollectorTest : public ::testing::Test {
video_media_info.receive_codecs.insert(
std::make_pair(recv_codec.payload_type, recv_codec));
// outbound-rtp
graph.outbound_rtp_id = "OV3";
graph.outbound_rtp_id = "OTTransportName1V3";
video_media_info.senders.push_back(cricket::VideoSenderInfo());
video_media_info.senders[0].local_stats.push_back(
cricket::SsrcSenderInfo());
@ -728,7 +728,7 @@ class RTCStatsCollectorTest : public ::testing::Test {
video_media_info.senders[0].codec_payload_type = send_codec.payload_type;
video_media_info.aggregated_senders.push_back(video_media_info.senders[0]);
// inbound-rtp
graph.inbound_rtp_id = "IV4";
graph.inbound_rtp_id = "ITTransportName1V4";
video_media_info.receivers.push_back(cricket::VideoReceiverInfo());
video_media_info.receivers[0].local_stats.push_back(
cricket::SsrcReceiverInfo());
@ -820,13 +820,13 @@ class RTCStatsCollectorTest : public ::testing::Test {
media_info.receive_codecs.insert(
std::make_pair(recv_codec.payload_type, recv_codec));
// outbound-rtp
graph.outbound_rtp_id = "OA3";
graph.outbound_rtp_id = "OTTransportName1A3";
media_info.senders.push_back(cricket::VoiceSenderInfo());
media_info.senders[0].local_stats.push_back(cricket::SsrcSenderInfo());
media_info.senders[0].local_stats[0].ssrc = kLocalSsrc;
media_info.senders[0].codec_payload_type = send_codec.payload_type;
// inbound-rtp
graph.inbound_rtp_id = "IA4";
graph.inbound_rtp_id = "ITTransportName1A4";
media_info.receivers.push_back(cricket::VoiceReceiverInfo());
media_info.receivers[0].local_stats.push_back(cricket::SsrcReceiverInfo());
media_info.receivers[0].local_stats[0].ssrc = kRemoteSsrc;
@ -1043,10 +1043,8 @@ TEST_F(RTCStatsCollectorTest, ValidSsrcCollisionDoesNotCrash) {
rtc::scoped_refptr<const RTCStatsReport> report = stats_->GetStatsReport();
auto inbound_rtps = report->GetStatsOfType<RTCInboundRTPStreamStats>();
auto outbound_rtps = report->GetStatsOfType<RTCOutboundRTPStreamStats>();
// TODO(https://crbug.com/webrtc/14443): When valid SSRC collisions are
// handled correctly, we should expect to see 4 of each type of object here.
EXPECT_EQ(inbound_rtps.size(), 2u);
EXPECT_EQ(outbound_rtps.size(), 2u);
EXPECT_EQ(inbound_rtps.size(), 4u);
EXPECT_EQ(outbound_rtps.size(), 4u);
}
// These SSRC collisions are illegal, so it is not clear if this setup can
@ -2259,7 +2257,8 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRTPStreamStats_Audio) {
auto stats_of_track_type = report->GetStatsOfType<RTCMediaStreamTrackStats>();
ASSERT_EQ(1U, stats_of_track_type.size());
RTCInboundRTPStreamStats expected_audio("IA1", report->timestamp_us());
RTCInboundRTPStreamStats expected_audio("ITTransportName1A1",
report->timestamp_us());
expected_audio.ssrc = 1;
expected_audio.media_type = "audio";
expected_audio.kind = "audio";
@ -2378,7 +2377,8 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRTPStreamStats_Video) {
rtc::scoped_refptr<const RTCStatsReport> report = stats_->GetStatsReport();
RTCInboundRTPStreamStats expected_video("IV1", report->timestamp_us());
RTCInboundRTPStreamStats expected_video("ITTransportName1V1",
report->timestamp_us());
expected_video.ssrc = 1;
expected_video.media_type = "video";
expected_video.kind = "video";
@ -2476,7 +2476,8 @@ TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRTPStreamStats_Audio) {
rtc::scoped_refptr<const RTCStatsReport> report = stats_->GetStatsReport();
RTCOutboundRTPStreamStats expected_audio("OA1", report->timestamp_us());
RTCOutboundRTPStreamStats expected_audio("OTTransportName1A1",
report->timestamp_us());
expected_audio.media_source_id = "SA50";
// `expected_audio.remote_id` should be undefined.
expected_audio.mid = "AudioMid";
@ -2906,7 +2907,8 @@ TEST_F(RTCStatsCollectorTest, CollectNoStreamRTCOutboundRTPStreamStats_Audio) {
rtc::scoped_refptr<const RTCStatsReport> report = stats_->GetStatsReport();
RTCOutboundRTPStreamStats expected_audio("OA1", report->timestamp_us());
RTCOutboundRTPStreamStats expected_audio("OTTransportName1A1",
report->timestamp_us());
expected_audio.media_source_id = "SA50";
expected_audio.mid = "AudioMid";
expected_audio.ssrc = 1;
@ -3234,7 +3236,8 @@ TEST_P(RTCStatsCollectorTestWithParamKind,
"TTransportName1"; // 1 for RTP (we have no RTCP
// transport)
expected_remote_inbound_rtp.packets_lost = 7;
expected_remote_inbound_rtp.local_id = "O" + MediaTypeCharStr() + stream_id;
expected_remote_inbound_rtp.local_id =
"OTTransportName1" + MediaTypeCharStr() + stream_id;
expected_remote_inbound_rtp.round_trip_time = kRoundTripTimeSample2Seconds;
expected_remote_inbound_rtp.total_round_trip_time =
kRoundTripTimeSample1Seconds + kRoundTripTimeSample2Seconds;