diff --git a/api/stats/rtcstats_objects.h b/api/stats/rtcstats_objects.h index 8a1fe56b43..81ec084f16 100644 --- a/api/stats/rtcstats_objects.h +++ b/api/stats/rtcstats_objects.h @@ -397,6 +397,7 @@ class RTC_EXPORT RTCInboundRTPStreamStats final : public RTCRTPStreamStats { RTCStatsMember packets_received; RTCStatsMember bytes_received; RTCStatsMember packets_lost; // Signed per RFC 3550 + RTCStatsMember last_packet_received_timestamp; // TODO(hbos): Collect and populate this value for both "audio" and "video", // currently not collected for "video". https://bugs.webrtc.org/7065 RTCStatsMember jitter; diff --git a/audio/audio_receive_stream.cc b/audio/audio_receive_stream.cc index e13e880017..b4948eed40 100644 --- a/audio/audio_receive_stream.cc +++ b/audio/audio_receive_stream.cc @@ -190,6 +190,8 @@ webrtc::AudioReceiveStream::Stats AudioReceiveStream::GetStats() const { stats.packets_lost = call_stats.cumulativeLost; stats.fraction_lost = Q8ToFloat(call_stats.fractionLost); stats.capture_start_ntp_time_ms = call_stats.capture_start_ntp_time_ms_; + stats.last_packet_received_timestamp_ms = + call_stats.last_packet_received_timestamp_ms; stats.codec_name = receive_codec->second.name; stats.codec_payload_type = receive_codec->first; stats.ext_seqnum = call_stats.extendedMax; diff --git a/audio/channel_receive.cc b/audio/channel_receive.cc index 4f00d9fdf2..f65d125cde 100644 --- a/audio/channel_receive.cc +++ b/audio/channel_receive.cc @@ -757,17 +757,23 @@ CallReceiveStatistics ChannelReceive::GetRTCPStatistics() const { stats.rttMs = GetRTT(); // --- Data counters - - size_t bytesReceived(0); - uint32_t packetsReceived(0); - if (statistician) { - statistician->GetDataCounters(&bytesReceived, &packetsReceived); + StreamDataCounters data_counters; + statistician->GetReceiveStreamDataCounters(&data_counters); + // TODO(http://crbug.com/webrtc/10525): Bytes received should only include + // payload bytes, not header and padding bytes. + stats.bytesReceived = data_counters.transmitted.payload_bytes + + data_counters.transmitted.header_bytes + + data_counters.transmitted.padding_bytes; + stats.packetsReceived = data_counters.transmitted.packets; + stats.last_packet_received_timestamp_ms = + data_counters.last_packet_received_timestamp_ms; + } else { + stats.bytesReceived = 0; + stats.packetsReceived = 0; + stats.last_packet_received_timestamp_ms = absl::nullopt; } - stats.bytesReceived = bytesReceived; - stats.packetsReceived = packetsReceived; - // --- Timestamps { rtc::CritScope lock(&ts_stats_lock_); diff --git a/audio/channel_receive.h b/audio/channel_receive.h index a67b0db872..1f7887463d 100644 --- a/audio/channel_receive.h +++ b/audio/channel_receive.h @@ -59,6 +59,10 @@ struct CallReceiveStatistics { // The capture ntp time (in local timebase) of the first played out audio // frame. int64_t capture_start_ntp_time_ms_; + // The timestamp at which the last packet was received, i.e. the time of the + // local clock when it was received - not the RTP timestamp of that packet. + // https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-lastpacketreceivedtimestamp + absl::optional last_packet_received_timestamp_ms; }; namespace voe { diff --git a/call/audio_receive_stream.h b/call/audio_receive_stream.h index 3f1a5adce3..257042b56d 100644 --- a/call/audio_receive_stream.h +++ b/call/audio_receive_stream.h @@ -73,6 +73,10 @@ class AudioReceiveStream { int32_t decoding_plc_cng = 0; int32_t decoding_muted_output = 0; int64_t capture_start_ntp_time_ms = 0; + // The timestamp at which the last packet was received, i.e. the time of the + // local clock when it was received - not the RTP timestamp of that packet. + // https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-lastpacketreceivedtimestamp + absl::optional last_packet_received_timestamp_ms; uint64_t jitter_buffer_flushes = 0; double relative_packet_arrival_delay_seconds = 0.0; }; diff --git a/media/base/media_channel.h b/media/base/media_channel.h index b64803b213..c21f89e5d8 100644 --- a/media/base/media_channel.h +++ b/media/base/media_channel.h @@ -435,6 +435,10 @@ struct MediaReceiverInfo { int packets_rcvd = 0; int packets_lost = 0; float fraction_lost = 0.0f; + // The timestamp at which the last packet was received, i.e. the time of the + // local clock when it was received - not the RTP timestamp of that packet. + // https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-lastpacketreceivedtimestamp + absl::optional last_packet_received_timestamp_ms; std::string codec_name; absl::optional codec_payload_type; std::vector local_stats; diff --git a/media/engine/webrtc_video_engine.cc b/media/engine/webrtc_video_engine.cc index 762f0333e8..57ff3723d0 100644 --- a/media/engine/webrtc_video_engine.cc +++ b/media/engine/webrtc_video_engine.cc @@ -2697,6 +2697,8 @@ WebRtcVideoChannel::WebRtcVideoReceiveStream::GetVideoReceiverInfo( info.frames_decoded = stats.frames_decoded; info.frames_rendered = stats.frames_rendered; info.qp_sum = stats.qp_sum; + info.last_packet_received_timestamp_ms = + stats.rtp_stats.last_packet_received_timestamp_ms; info.first_frame_received_to_decoded_ms = stats.first_frame_received_to_decoded_ms; info.interframe_delay_max_ms = stats.interframe_delay_max_ms; diff --git a/media/engine/webrtc_voice_engine.cc b/media/engine/webrtc_voice_engine.cc index b35090de94..0765427e6f 100644 --- a/media/engine/webrtc_voice_engine.cc +++ b/media/engine/webrtc_voice_engine.cc @@ -2267,6 +2267,8 @@ bool WebRtcVoiceMediaChannel::GetStats(VoiceMediaInfo* info) { rinfo.decoding_plc_cng = stats.decoding_plc_cng; rinfo.decoding_muted_output = stats.decoding_muted_output; rinfo.capture_start_ntp_time_ms = stats.capture_start_ntp_time_ms; + rinfo.last_packet_received_timestamp_ms = + stats.last_packet_received_timestamp_ms; rinfo.jitter_buffer_flushes = stats.jitter_buffer_flushes; rinfo.relative_packet_arrival_delay_seconds = stats.relative_packet_arrival_delay_seconds; diff --git a/pc/rtc_stats_collector.cc b/pc/rtc_stats_collector.cc index 74273562a7..3bf856636d 100644 --- a/pc/rtc_stats_collector.cc +++ b/pc/rtc_stats_collector.cc @@ -244,6 +244,12 @@ void SetInboundRTPStreamStatsFromVoiceReceiverInfo( rtc::kNumMillisecsPerSec; // |fir_count|, |pli_count| and |sli_count| are only valid for video and are // purposefully left undefined for audio. + if (voice_receiver_info.last_packet_received_timestamp_ms) { + inbound_audio->last_packet_received_timestamp = + static_cast( + *voice_receiver_info.last_packet_received_timestamp_ms) / + rtc::kNumMillisecsPerSec; + } } void SetInboundRTPStreamStatsFromVideoReceiverInfo( @@ -267,6 +273,12 @@ void SetInboundRTPStreamStatsFromVideoReceiverInfo( inbound_video->frames_decoded = video_receiver_info.frames_decoded; if (video_receiver_info.qp_sum) inbound_video->qp_sum = *video_receiver_info.qp_sum; + if (video_receiver_info.last_packet_received_timestamp_ms) { + inbound_video->last_packet_received_timestamp = + static_cast( + *video_receiver_info.last_packet_received_timestamp_ms) / + rtc::kNumMillisecsPerSec; + } // TODO(https://crbug.com/webrtc/10529): When info's |content_info| is // optional, support the "unspecified" value. if (video_receiver_info.content_type == VideoContentType::SCREENSHARE) diff --git a/pc/rtc_stats_collector_unittest.cc b/pc/rtc_stats_collector_unittest.cc index 7e843a6869..82cd241eed 100644 --- a/pc/rtc_stats_collector_unittest.cc +++ b/pc/rtc_stats_collector_unittest.cc @@ -1625,6 +1625,8 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRTPStreamStats_Audio) { voice_media_info.receivers[0].codec_payload_type = 42; voice_media_info.receivers[0].jitter_ms = 4500; voice_media_info.receivers[0].fraction_lost = 5.5f; + voice_media_info.receivers[0].last_packet_received_timestamp_ms = + absl::nullopt; RtpCodecParameters codec_parameters; codec_parameters.payload_type = 42; @@ -1656,8 +1658,21 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRTPStreamStats_Audio) { expected_audio.packets_received = 2; expected_audio.bytes_received = 3; expected_audio.packets_lost = -1; + // |expected_audio.last_packet_received_timestamp| should be undefined. expected_audio.jitter = 4.5; expected_audio.fraction_lost = 5.5; + ASSERT_TRUE(report->Get(expected_audio.id())); + EXPECT_EQ( + report->Get(expected_audio.id())->cast_to(), + expected_audio); + + // Set previously undefined values and "GetStats" again. + voice_media_info.receivers[0].last_packet_received_timestamp_ms = 3000; + expected_audio.last_packet_received_timestamp = 3.0; + voice_media_channel->SetStats(voice_media_info); + + report = stats_->GetFreshStatsReport(); + ASSERT_TRUE(report->Get(expected_audio.id())); EXPECT_EQ( report->Get(expected_audio.id())->cast_to(), @@ -1684,6 +1699,8 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRTPStreamStats_Video) { video_media_info.receivers[0].nacks_sent = 7; video_media_info.receivers[0].frames_decoded = 8; video_media_info.receivers[0].qp_sum = absl::nullopt; + video_media_info.receivers[0].last_packet_received_timestamp_ms = + absl::nullopt; video_media_info.receivers[0].content_type = VideoContentType::UNSPECIFIED; RtpCodecParameters codec_parameters; @@ -1719,6 +1736,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRTPStreamStats_Video) { expected_video.fraction_lost = 4.5; expected_video.frames_decoded = 8; // |expected_video.qp_sum| should be undefined. + // |expected_video.last_packet_received_timestamp| should be undefined. // |expected_video.content_type| should be undefined. ASSERT_TRUE(report->Get(expected_video.id())); @@ -1728,7 +1746,9 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRTPStreamStats_Video) { // Set previously undefined values and "GetStats" again. video_media_info.receivers[0].qp_sum = 9; + video_media_info.receivers[0].last_packet_received_timestamp_ms = 1000; expected_video.qp_sum = 9; + expected_video.last_packet_received_timestamp = 1.0; video_media_info.receivers[0].content_type = VideoContentType::SCREENSHARE; expected_video.content_type = "screenshare"; video_media_channel->SetStats(video_media_info); diff --git a/pc/rtc_stats_integrationtest.cc b/pc/rtc_stats_integrationtest.cc index 69357691e2..9b1b6d02c2 100644 --- a/pc/rtc_stats_integrationtest.cc +++ b/pc/rtc_stats_integrationtest.cc @@ -719,6 +719,7 @@ class RTCStatsReportVerifier { // packets_lost is defined as signed, but this should never happen in // this test. See RFC 3550. verifier.TestMemberIsNonNegative(inbound_stream.packets_lost); + verifier.TestMemberIsDefined(inbound_stream.last_packet_received_timestamp); if (inbound_stream.media_type.is_defined() && *inbound_stream.media_type == "video") { verifier.TestMemberIsUndefined(inbound_stream.jitter); diff --git a/stats/rtcstats_objects.cc b/stats/rtcstats_objects.cc index 7c023d1822..4bf3e68346 100644 --- a/stats/rtcstats_objects.cc +++ b/stats/rtcstats_objects.cc @@ -578,6 +578,7 @@ WEBRTC_RTCSTATS_IMPL( &packets_received, &bytes_received, &packets_lost, + &last_packet_received_timestamp, &jitter, &fraction_lost, &round_trip_time, @@ -605,6 +606,7 @@ RTCInboundRTPStreamStats::RTCInboundRTPStreamStats(std::string&& id, packets_received("packetsReceived"), bytes_received("bytesReceived"), packets_lost("packetsLost"), + last_packet_received_timestamp("lastPacketReceivedTimestamp"), jitter("jitter"), fraction_lost("fractionLost"), round_trip_time("roundTripTime"), @@ -627,6 +629,7 @@ RTCInboundRTPStreamStats::RTCInboundRTPStreamStats( packets_received(other.packets_received), bytes_received(other.bytes_received), packets_lost(other.packets_lost), + last_packet_received_timestamp(other.last_packet_received_timestamp), jitter(other.jitter), fraction_lost(other.fraction_lost), round_trip_time(other.round_trip_time),