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

View File

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