diff --git a/api/stats/rtcstats_objects.h b/api/stats/rtcstats_objects.h index 4428ab5dc4..15b015801f 100644 --- a/api/stats/rtcstats_objects.h +++ b/api/stats/rtcstats_objects.h @@ -425,6 +425,10 @@ class RTC_EXPORT RTCInboundRtpStreamStats final RTCStatsMember fec_packets_discarded; RTCStatsMember bytes_received; RTCStatsMember header_bytes_received; + // Inbound RTX stats. Only defined when RTX is used and it is therefore + // possible to distinguish retransmissions. + RTCStatsMember retransmitted_packets_received; + RTCStatsMember retransmitted_bytes_received; RTCStatsMember last_packet_received_timestamp; RTCStatsMember jitter_buffer_delay; RTCStatsMember jitter_buffer_target_delay; diff --git a/call/video_receive_stream.h b/call/video_receive_stream.h index bde8c8b615..4d6b36bafe 100644 --- a/call/video_receive_stream.h +++ b/call/video_receive_stream.h @@ -145,6 +145,7 @@ class VideoReceiveStreamInterface : public MediaReceiveStreamInterface { std::string c_name; RtpReceiveStats rtp_stats; RtcpPacketTypeCounter rtcp_packet_type_counts; + absl::optional rtx_rtp_stats; // Timing frame info: all important timestamps for a full lifetime of a // single 'timing frame'. diff --git a/media/base/media_channel.h b/media/base/media_channel.h index 378042f1b2..52aedeccd2 100644 --- a/media/base/media_channel.h +++ b/media/base/media_channel.h @@ -443,6 +443,9 @@ struct MediaReceiverInfo { int64_t header_and_padding_bytes_received = 0; int packets_received = 0; int packets_lost = 0; + + absl::optional retransmitted_bytes_received; + absl::optional retransmitted_packets_received; absl::optional nacks_sent; // Jitter (network-related) latency (cumulative). // https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-jitterbufferdelay diff --git a/media/engine/webrtc_video_engine.cc b/media/engine/webrtc_video_engine.cc index 83581bf9fd..af97dfaff4 100644 --- a/media/engine/webrtc_video_engine.cc +++ b/media/engine/webrtc_video_engine.cc @@ -3364,6 +3364,20 @@ WebRtcVideoChannel::WebRtcVideoReceiveStream::GetVideoReceiverInfo( info.timing_frame_info = stats.timing_frame_info; + if (stats.rtx_rtp_stats.has_value()) { + info.retransmitted_packets_received = + stats.rtx_rtp_stats->packet_counter.packets; + info.retransmitted_bytes_received = + stats.rtx_rtp_stats->packet_counter.payload_bytes; + // RTX information gets added to primary counters. + info.payload_bytes_received += + stats.rtx_rtp_stats->packet_counter.payload_bytes; + info.header_and_padding_bytes_received += + stats.rtx_rtp_stats->packet_counter.header_bytes + + stats.rtx_rtp_stats->packet_counter.padding_bytes; + info.packets_received += stats.rtx_rtp_stats->packet_counter.packets; + } + if (log_stats) RTC_LOG(LS_INFO) << stats.ToString(rtc::TimeMillis()); diff --git a/media/engine/webrtc_video_engine_unittest.cc b/media/engine/webrtc_video_engine_unittest.cc index 99d36b7a02..bbabddf9fb 100644 --- a/media/engine/webrtc_video_engine_unittest.cc +++ b/media/engine/webrtc_video_engine_unittest.cc @@ -6548,6 +6548,10 @@ TEST_F(WebRtcVideoChannelTest, GetStatsTranslatesDecodeStatsCorrectly) { stats.total_assembly_time = webrtc::TimeDelta::Millis(4); stats.frames_assembled_from_multiple_packets = 2; stats.power_efficient_decoder = true; + webrtc::RtpReceiveStats rtx_stats; + rtx_stats.packet_counter.packets = 5; + rtx_stats.packet_counter.payload_bytes = 23; + stats.rtx_rtp_stats = rtx_stats; stream->SetStats(stats); cricket::VideoMediaSendInfo send_info; @@ -6586,6 +6590,10 @@ TEST_F(WebRtcVideoChannelTest, GetStatsTranslatesDecodeStatsCorrectly) { EXPECT_EQ(stats.frames_assembled_from_multiple_packets, receive_info.receivers[0].frames_assembled_from_multiple_packets); EXPECT_TRUE(receive_info.receivers[0].power_efficient_decoder); + EXPECT_EQ(stats.rtx_rtp_stats->packet_counter.packets, + receive_info.receivers[0].retransmitted_packets_received); + EXPECT_EQ(stats.rtx_rtp_stats->packet_counter.payload_bytes, + receive_info.receivers[0].retransmitted_bytes_received); } TEST_F(WebRtcVideoChannelTest, diff --git a/pc/rtc_stats_collector.cc b/pc/rtc_stats_collector.cc index 9b9f5ba1fd..4535597123 100644 --- a/pc/rtc_stats_collector.cc +++ b/pc/rtc_stats_collector.cc @@ -421,6 +421,14 @@ void SetInboundRTPStreamStatsFromMediaReceiverInfo( static_cast(media_receiver_info.payload_bytes_received); inbound_stats->header_bytes_received = static_cast( media_receiver_info.header_and_padding_bytes_received); + if (media_receiver_info.retransmitted_bytes_received.has_value()) { + inbound_stats->retransmitted_bytes_received = + *media_receiver_info.retransmitted_bytes_received; + } + if (media_receiver_info.retransmitted_packets_received.has_value()) { + inbound_stats->retransmitted_packets_received = + *media_receiver_info.retransmitted_packets_received; + } inbound_stats->packets_lost = static_cast(media_receiver_info.packets_lost); inbound_stats->jitter_buffer_delay = diff --git a/pc/rtc_stats_collector_unittest.cc b/pc/rtc_stats_collector_unittest.cc index 9b0466d1b7..9db5b3f6f9 100644 --- a/pc/rtc_stats_collector_unittest.cc +++ b/pc/rtc_stats_collector_unittest.cc @@ -2667,7 +2667,6 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRtpStreamStats_Video) { video_media_info.receivers[0].jitter_buffer_target_delay_seconds = 1.1; video_media_info.receivers[0].jitter_buffer_minimum_delay_seconds = 0.999; video_media_info.receivers[0].jitter_buffer_emitted_count = 13; - video_media_info.receivers[0].last_packet_received_timestamp_ms = absl::nullopt; video_media_info.receivers[0].content_type = VideoContentType::UNSPECIFIED; @@ -2676,6 +2675,8 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRtpStreamStats_Video) { video_media_info.receivers[0].decoder_implementation_name = ""; video_media_info.receivers[0].min_playout_delay_ms = 50; video_media_info.receivers[0].power_efficient_decoder = false; + video_media_info.receivers[0].retransmitted_packets_received = 17; + video_media_info.receivers[0].retransmitted_bytes_received = 62; // Note: these two values intentionally differ, // only the decoded one should show up. @@ -2741,6 +2742,8 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRtpStreamStats_Video) { expected_video.min_playout_delay = 0.05; expected_video.frames_per_second = 5; expected_video.power_efficient_decoder = false; + expected_video.retransmitted_packets_received = 17; + expected_video.retransmitted_bytes_received = 62; ASSERT_TRUE(report->Get(expected_video.id())); EXPECT_EQ( diff --git a/pc/rtc_stats_integrationtest.cc b/pc/rtc_stats_integrationtest.cc index 42a7769c5a..3f6704293a 100644 --- a/pc/rtc_stats_integrationtest.cc +++ b/pc/rtc_stats_integrationtest.cc @@ -843,6 +843,20 @@ class RTCStatsReportVerifier { inbound_stream.total_samples_duration); verifier.TestMemberIsUndefined(inbound_stream.frames_received); } + + // RTX stats are typically only defined for video where RTX is negotiated. + if (inbound_stream.kind.is_defined() && *inbound_stream.kind == "video") { + verifier.TestMemberIsNonNegative( + inbound_stream.retransmitted_packets_received); + verifier.TestMemberIsNonNegative( + inbound_stream.retransmitted_bytes_received); + } else { + verifier.TestMemberIsUndefined( + inbound_stream.retransmitted_packets_received); + verifier.TestMemberIsUndefined( + inbound_stream.retransmitted_bytes_received); + } + // Test runtime too short to get an estimate (at least two RTCP sender // reports need to be received). verifier.MarkMemberTested(inbound_stream.estimated_playout_timestamp, true); diff --git a/stats/rtcstats_objects.cc b/stats/rtcstats_objects.cc index 997764b38c..5aef7f985a 100644 --- a/stats/rtcstats_objects.cc +++ b/stats/rtcstats_objects.cc @@ -466,6 +466,8 @@ WEBRTC_RTCSTATS_IMPL( &fec_packets_discarded, &bytes_received, &header_bytes_received, + &retransmitted_packets_received, + &retransmitted_bytes_received, &last_packet_received_timestamp, &jitter_buffer_delay, &jitter_buffer_target_delay, @@ -528,6 +530,8 @@ RTCInboundRtpStreamStats::RTCInboundRtpStreamStats(std::string id, fec_packets_discarded("fecPacketsDiscarded"), bytes_received("bytesReceived"), header_bytes_received("headerBytesReceived"), + retransmitted_packets_received("retransmittedPacketsReceived"), + retransmitted_bytes_received("retransmittedBytesReceived"), last_packet_received_timestamp("lastPacketReceivedTimestamp"), jitter_buffer_delay("jitterBufferDelay"), jitter_buffer_target_delay("jitterBufferTargetDelay"), diff --git a/video/video_receive_stream2.cc b/video/video_receive_stream2.cc index 9cc78c7241..d4ca5e8eb7 100644 --- a/video/video_receive_stream2.cc +++ b/video/video_receive_stream2.cc @@ -566,8 +566,10 @@ VideoReceiveStreamInterface::Stats VideoReceiveStream2::GetStats() const { if (rtx_ssrc()) { StreamStatistician* rtx_statistician = rtp_receive_statistics_->GetStatistician(rtx_ssrc()); - if (rtx_statistician) + if (rtx_statistician) { stats.total_bitrate_bps += rtx_statistician->BitrateReceived(); + stats.rtx_rtp_stats = rtx_statistician->GetStats(); + } } return stats; }