Add `RTCRemoteOutboundRtpStreamStats` for audio streams
Changes:
- adding the `RTCRemoteOutboundRtpStreamStats` dictionary (see [1])
- collection of remote outbound stats (only for audio streams)
- adding `remote_id` to the inbound stats and set with the ID of the
corresponding remote outbound stats only if the latter are available
- unit tests
[1] https://www.w3.org/TR/webrtc-stats/#dom-rtcremoteoutboundrtpstreamstats
Tested: verified from chrome://webrtc-internals during an appr.tc call
Bug: webrtc:12529
Change-Id: Ide91dc04a3c387ba439618a9c6b64a95994a1940
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/211042
Commit-Queue: Alessio Bazzica <alessiob@webrtc.org>
Reviewed-by: Björn Terelius <terelius@webrtc.org>
Reviewed-by: Sam Zackrisson <saza@webrtc.org>
Reviewed-by: Henrik Boström <hbos@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#33545}
diff --git a/pc/rtc_stats_collector.cc b/pc/rtc_stats_collector.cc
index c14f414..36ee542 100644
--- a/pc/rtc_stats_collector.cc
+++ b/pc/rtc_stats_collector.cc
@@ -109,17 +109,23 @@
return sb.str();
}
-std::string RTCInboundRTPStreamStatsIDFromSSRC(bool audio, uint32_t ssrc) {
+std::string RTCInboundRTPStreamStatsIDFromSSRC(cricket::MediaType media_type,
+ uint32_t ssrc) {
char buf[1024];
rtc::SimpleStringBuilder sb(buf);
- sb << "RTCInboundRTP" << (audio ? "Audio" : "Video") << "Stream_" << ssrc;
+ sb << "RTCInboundRTP"
+ << (media_type == cricket::MEDIA_TYPE_AUDIO ? "Audio" : "Video")
+ << "Stream_" << ssrc;
return sb.str();
}
-std::string RTCOutboundRTPStreamStatsIDFromSSRC(bool audio, uint32_t ssrc) {
+std::string RTCOutboundRTPStreamStatsIDFromSSRC(cricket::MediaType media_type,
+ uint32_t ssrc) {
char buf[1024];
rtc::SimpleStringBuilder sb(buf);
- sb << "RTCOutboundRTP" << (audio ? "Audio" : "Video") << "Stream_" << ssrc;
+ sb << "RTCOutboundRTP"
+ << (media_type == cricket::MEDIA_TYPE_AUDIO ? "Audio" : "Video")
+ << "Stream_" << ssrc;
return sb.str();
}
@@ -134,6 +140,17 @@
return sb.str();
}
+std::string RTCRemoteOutboundRTPStreamStatsIDFromSSRC(
+ cricket::MediaType media_type,
+ uint32_t source_ssrc) {
+ char buf[1024];
+ rtc::SimpleStringBuilder sb(buf);
+ sb << "RTCRemoteOutboundRTP"
+ << (media_type == cricket::MEDIA_TYPE_AUDIO ? "Audio" : "Video")
+ << "Stream_" << source_ssrc;
+ return sb.str();
+}
+
std::string RTCMediaSourceStatsIDFromKindAndAttachment(
cricket::MediaType media_type,
int attachment_id) {
@@ -309,17 +326,21 @@
static_cast<int32_t>(media_receiver_info.packets_lost);
}
-void SetInboundRTPStreamStatsFromVoiceReceiverInfo(
- const std::string& mid,
+std::unique_ptr<RTCInboundRTPStreamStats> CreateInboundAudioStreamStats(
const cricket::VoiceReceiverInfo& voice_receiver_info,
- RTCInboundRTPStreamStats* inbound_audio) {
+ 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()),
+ timestamp_us);
SetInboundRTPStreamStatsFromMediaReceiverInfo(voice_receiver_info,
- inbound_audio);
+ inbound_audio.get());
inbound_audio->media_type = "audio";
inbound_audio->kind = "audio";
if (voice_receiver_info.codec_payload_type) {
inbound_audio->codec_id = RTCCodecStatsIDFromMidDirectionAndPayload(
- mid, true, *voice_receiver_info.codec_payload_type);
+ mid, /*inbound=*/true, *voice_receiver_info.codec_payload_type);
}
inbound_audio->jitter = static_cast<double>(voice_receiver_info.jitter_ms) /
rtc::kNumMillisecsPerSec;
@@ -358,6 +379,51 @@
voice_receiver_info.fec_packets_received;
inbound_audio->fec_packets_discarded =
voice_receiver_info.fec_packets_discarded;
+ return inbound_audio;
+}
+
+std::unique_ptr<RTCRemoteOutboundRtpStreamStats>
+CreateRemoteOutboundAudioStreamStats(
+ const cricket::VoiceReceiverInfo& voice_receiver_info,
+ const std::string& mid,
+ const std::string& inbound_audio_id,
+ const std::string& transport_id) {
+ if (!voice_receiver_info.last_sender_report_timestamp_ms.has_value()) {
+ // Cannot create `RTCRemoteOutboundRtpStreamStats` when the RTCP SR arrival
+ // timestamp is not available - i.e., until the first sender report is
+ // received.
+ return nullptr;
+ }
+ RTC_DCHECK_GT(voice_receiver_info.sender_reports_reports_count, 0);
+
+ // Create.
+ auto stats = std::make_unique<RTCRemoteOutboundRtpStreamStats>(
+ /*id=*/RTCRemoteOutboundRTPStreamStatsIDFromSSRC(
+ cricket::MEDIA_TYPE_AUDIO, voice_receiver_info.ssrc()),
+ /*timestamp_us=*/rtc::kNumMicrosecsPerMillisec *
+ voice_receiver_info.last_sender_report_timestamp_ms.value());
+
+ // Populate.
+ // - RTCRtpStreamStats.
+ stats->ssrc = voice_receiver_info.ssrc();
+ stats->kind = "audio";
+ stats->transport_id = transport_id;
+ stats->codec_id = RTCCodecStatsIDFromMidDirectionAndPayload(
+ mid,
+ /*inbound=*/true, // Remote-outbound same as local-inbound.
+ *voice_receiver_info.codec_payload_type);
+ // - RTCSentRtpStreamStats.
+ stats->packets_sent = voice_receiver_info.sender_reports_packets_sent;
+ stats->bytes_sent = voice_receiver_info.sender_reports_bytes_sent;
+ // - RTCRemoteOutboundRtpStreamStats.
+ stats->local_id = inbound_audio_id;
+ RTC_DCHECK(
+ voice_receiver_info.last_sender_report_remote_timestamp_ms.has_value());
+ stats->remote_timestamp = static_cast<double>(
+ voice_receiver_info.last_sender_report_remote_timestamp_ms.value());
+ stats->reports_sent = voice_receiver_info.sender_reports_reports_count;
+
+ return stats;
}
void SetInboundRTPStreamStatsFromVideoReceiverInfo(
@@ -370,7 +436,7 @@
inbound_video->kind = "video";
if (video_receiver_info.codec_payload_type) {
inbound_video->codec_id = RTCCodecStatsIDFromMidDirectionAndPayload(
- mid, true, *video_receiver_info.codec_payload_type);
+ mid, /*inbound=*/true, *video_receiver_info.codec_payload_type);
}
inbound_video->jitter = static_cast<double>(video_receiver_info.jitter_ms) /
rtc::kNumMillisecsPerSec;
@@ -454,7 +520,7 @@
outbound_audio->kind = "audio";
if (voice_sender_info.codec_payload_type) {
outbound_audio->codec_id = RTCCodecStatsIDFromMidDirectionAndPayload(
- mid, false, *voice_sender_info.codec_payload_type);
+ mid, /*inbound=*/false, *voice_sender_info.codec_payload_type);
}
// |fir_count|, |pli_count| and |sli_count| are only valid for video and are
// purposefully left undefined for audio.
@@ -470,7 +536,7 @@
outbound_video->kind = "video";
if (video_sender_info.codec_payload_type) {
outbound_video->codec_id = RTCCodecStatsIDFromMidDirectionAndPayload(
- mid, false, *video_sender_info.codec_payload_type);
+ mid, /*inbound=*/false, *video_sender_info.codec_payload_type);
}
outbound_video->fir_count =
static_cast<uint32_t>(video_sender_info.firs_rcvd);
@@ -550,8 +616,8 @@
remote_inbound->round_trip_time_measurements =
report_block_data.num_rtts();
- std::string local_id = RTCOutboundRTPStreamStatsIDFromSSRC(
- media_type == cricket::MEDIA_TYPE_AUDIO, report_block.source_ssrc);
+ std::string local_id =
+ RTCOutboundRTPStreamStatsIDFromSSRC(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()) {
@@ -1678,16 +1744,16 @@
std::string mid = *stats.mid;
std::string transport_id = RTCTransportStatsIDFromTransportChannel(
*stats.transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP);
- // Inbound
+ // Inbound and remote-outbound.
+ // The remote-outbound stats are based on RTCP sender reports sent from the
+ // remote endpoint providing metrics about the remote outbound streams.
for (const cricket::VoiceReceiverInfo& voice_receiver_info :
track_media_info_map.voice_media_info()->receivers) {
if (!voice_receiver_info.connected())
continue;
- auto inbound_audio = std::make_unique<RTCInboundRTPStreamStats>(
- RTCInboundRTPStreamStatsIDFromSSRC(true, voice_receiver_info.ssrc()),
- timestamp_us);
- SetInboundRTPStreamStatsFromVoiceReceiverInfo(mid, voice_receiver_info,
- inbound_audio.get());
+ // Inbound.
+ auto inbound_audio =
+ CreateInboundAudioStreamStats(voice_receiver_info, mid, timestamp_us);
// TODO(hta): This lookup should look for the sender, not the track.
rtc::scoped_refptr<AudioTrackInterface> audio_track =
track_media_info_map.GetAudioTrack(voice_receiver_info);
@@ -1698,16 +1764,27 @@
track_media_info_map.GetAttachmentIdByTrack(audio_track).value());
}
inbound_audio->transport_id = transport_id;
+ // Remote-outbound.
+ auto remote_outbound_audio = CreateRemoteOutboundAudioStreamStats(
+ voice_receiver_info, mid, inbound_audio->id(), transport_id);
+ // Add stats.
+ if (remote_outbound_audio) {
+ // When the remote outbound stats are available, the remote ID for the
+ // local inbound stats is set.
+ inbound_audio->remote_id = remote_outbound_audio->id();
+ report->AddStats(std::move(remote_outbound_audio));
+ }
report->AddStats(std::move(inbound_audio));
}
- // Outbound
+ // Outbound.
std::map<std::string, RTCOutboundRTPStreamStats*> audio_outbound_rtps;
for (const cricket::VoiceSenderInfo& voice_sender_info :
track_media_info_map.voice_media_info()->senders) {
if (!voice_sender_info.connected())
continue;
auto outbound_audio = std::make_unique<RTCOutboundRTPStreamStats>(
- RTCOutboundRTPStreamStatsIDFromSSRC(true, voice_sender_info.ssrc()),
+ RTCOutboundRTPStreamStatsIDFromSSRC(cricket::MEDIA_TYPE_AUDIO,
+ voice_sender_info.ssrc()),
timestamp_us);
SetOutboundRTPStreamStatsFromVoiceSenderInfo(mid, voice_sender_info,
outbound_audio.get());
@@ -1728,7 +1805,7 @@
std::make_pair(outbound_audio->id(), outbound_audio.get()));
report->AddStats(std::move(outbound_audio));
}
- // Remote-inbound
+ // Remote-inbound.
// These are Report Block-based, information sent from the remote endpoint,
// providing metrics about our Outbound streams. We take advantage of the fact
// that RTCOutboundRtpStreamStats, RTCCodecStats and RTCTransport have already
@@ -1765,7 +1842,8 @@
if (!video_receiver_info.connected())
continue;
auto inbound_video = std::make_unique<RTCInboundRTPStreamStats>(
- RTCInboundRTPStreamStatsIDFromSSRC(false, video_receiver_info.ssrc()),
+ RTCInboundRTPStreamStatsIDFromSSRC(cricket::MEDIA_TYPE_VIDEO,
+ video_receiver_info.ssrc()),
timestamp_us);
SetInboundRTPStreamStatsFromVideoReceiverInfo(mid, video_receiver_info,
inbound_video.get());
@@ -1779,6 +1857,7 @@
}
inbound_video->transport_id = transport_id;
report->AddStats(std::move(inbound_video));
+ // TODO(crbug.com/webrtc/12529): Add remote-outbound stats.
}
// Outbound
std::map<std::string, RTCOutboundRTPStreamStats*> video_outbound_rtps;
@@ -1787,7 +1866,8 @@
if (!video_sender_info.connected())
continue;
auto outbound_video = std::make_unique<RTCOutboundRTPStreamStats>(
- RTCOutboundRTPStreamStatsIDFromSSRC(false, video_sender_info.ssrc()),
+ RTCOutboundRTPStreamStatsIDFromSSRC(cricket::MEDIA_TYPE_VIDEO,
+ video_sender_info.ssrc()),
timestamp_us);
SetOutboundRTPStreamStatsFromVideoSenderInfo(mid, video_sender_info,
outbound_video.get());