diff --git a/call/video_receive_stream.cc b/call/video_receive_stream.cc index 2112647261..79783fd81b 100644 --- a/call/video_receive_stream.cc +++ b/call/video_receive_stream.cc @@ -53,6 +53,8 @@ std::string VideoReceiveStream::Stats::ToString(int64_t time_ms) const { ss << "cur_delay_ms: " << current_delay_ms << ", "; ss << "targ_delay_ms: " << target_delay_ms << ", "; ss << "jb_delay_ms: " << jitter_buffer_ms << ", "; + ss << "jb_cumulative_delay_seconds: " << jitter_buffer_delay_seconds << ", "; + ss << "jb_emitted_count: " << jitter_buffer_emitted_count << ", "; ss << "min_playout_delay_ms: " << min_playout_delay_ms << ", "; ss << "sync_offset_ms: " << sync_offset_ms << ", "; ss << "cum_loss: " << rtcp_stats.packets_lost << ", "; diff --git a/call/video_receive_stream.h b/call/video_receive_stream.h index 63fc5b7b07..8bd9982bf4 100644 --- a/call/video_receive_stream.h +++ b/call/video_receive_stream.h @@ -77,6 +77,10 @@ class VideoReceiveStream { int current_delay_ms = 0; int target_delay_ms = 0; int jitter_buffer_ms = 0; + // https://w3c.github.io/webrtc-stats/#dom-rtcvideoreceiverstats-jitterbufferdelay + double jitter_buffer_delay_seconds = 0; + // https://w3c.github.io/webrtc-stats/#dom-rtcvideoreceiverstats-jitterbufferemittedcount + uint64_t jitter_buffer_emitted_count = 0; int min_playout_delay_ms = 0; int render_delay_ms = 10; int64_t interframe_delay_max_ms = -1; diff --git a/media/base/media_channel.h b/media/base/media_channel.h index c991de39ea..123b2893a8 100644 --- a/media/base/media_channel.h +++ b/media/base/media_channel.h @@ -617,6 +617,12 @@ struct VideoReceiverInfo : public MediaReceiverInfo { int max_decode_ms = 0; // Jitter (network-related) latency. int jitter_buffer_ms = 0; + // Jitter (network-related) latency (cumulative). + // https://w3c.github.io/webrtc-stats/#dom-rtcvideoreceiverstats-jitterbufferdelay + double jitter_buffer_delay_seconds = 0; + // Number of observations for cumulative jitter latency. + // https://w3c.github.io/webrtc-stats/#dom-rtcvideoreceiverstats-jitterbufferemittedcount + uint64_t jitter_buffer_emitted_count = 0; // Requested minimum playout latency. int min_playout_delay_ms = 0; // Requested latency to account for rendering delay. diff --git a/media/engine/webrtc_video_engine.cc b/media/engine/webrtc_video_engine.cc index 92ed38d4d3..4219c438ef 100644 --- a/media/engine/webrtc_video_engine.cc +++ b/media/engine/webrtc_video_engine.cc @@ -2734,6 +2734,8 @@ WebRtcVideoChannel::WebRtcVideoReceiveStream::GetVideoReceiverInfo( info.current_delay_ms = stats.current_delay_ms; info.target_delay_ms = stats.target_delay_ms; info.jitter_buffer_ms = stats.jitter_buffer_ms; + info.jitter_buffer_delay_seconds = stats.jitter_buffer_delay_seconds; + info.jitter_buffer_emitted_count = stats.jitter_buffer_emitted_count; info.min_playout_delay_ms = stats.min_playout_delay_ms; info.render_delay_ms = stats.render_delay_ms; info.frames_received = diff --git a/media/engine/webrtc_video_engine_unittest.cc b/media/engine/webrtc_video_engine_unittest.cc index 39dc3b422c..2aa10e640b 100644 --- a/media/engine/webrtc_video_engine_unittest.cc +++ b/media/engine/webrtc_video_engine_unittest.cc @@ -5000,6 +5000,8 @@ TEST_F(WebRtcVideoChannelTest, GetStatsTranslatesDecodeStatsCorrectly) { stats.current_delay_ms = 4; stats.target_delay_ms = 5; stats.jitter_buffer_ms = 6; + stats.jitter_buffer_delay_seconds = 60; + stats.jitter_buffer_emitted_count = 6; stats.min_playout_delay_ms = 7; stats.render_delay_ms = 8; stats.width = 9; @@ -5020,6 +5022,10 @@ TEST_F(WebRtcVideoChannelTest, GetStatsTranslatesDecodeStatsCorrectly) { EXPECT_EQ(stats.current_delay_ms, info.receivers[0].current_delay_ms); EXPECT_EQ(stats.target_delay_ms, info.receivers[0].target_delay_ms); EXPECT_EQ(stats.jitter_buffer_ms, info.receivers[0].jitter_buffer_ms); + EXPECT_EQ(stats.jitter_buffer_delay_seconds, + info.receivers[0].jitter_buffer_delay_seconds); + EXPECT_EQ(stats.jitter_buffer_emitted_count, + info.receivers[0].jitter_buffer_emitted_count); EXPECT_EQ(stats.min_playout_delay_ms, info.receivers[0].min_playout_delay_ms); EXPECT_EQ(stats.render_delay_ms, info.receivers[0].render_delay_ms); EXPECT_EQ(stats.width, info.receivers[0].frame_width); diff --git a/pc/rtc_stats_collector.cc b/pc/rtc_stats_collector.cc index 4386579d75..c180dd6ff4 100644 --- a/pc/rtc_stats_collector.cc +++ b/pc/rtc_stats_collector.cc @@ -675,6 +675,10 @@ ProduceMediaStreamTrackStatsFromVideoReceiverInfo( video_track_stats->frame_height = static_cast( video_receiver_info.frame_height); } + video_track_stats->jitter_buffer_delay = + video_receiver_info.jitter_buffer_delay_seconds; + video_track_stats->jitter_buffer_emitted_count = + video_receiver_info.jitter_buffer_emitted_count; video_track_stats->frames_received = video_receiver_info.frames_received; // TODO(hbos): When we support receiving simulcast, this should be the total // number of frames correctly decoded, independent of which SSRC it was diff --git a/pc/rtc_stats_collector_unittest.cc b/pc/rtc_stats_collector_unittest.cc index 51e6eb9283..979db600f8 100644 --- a/pc/rtc_stats_collector_unittest.cc +++ b/pc/rtc_stats_collector_unittest.cc @@ -1647,6 +1647,8 @@ TEST_F(RTCStatsCollectorTest, video_receiver_info_ssrc3.local_stats[0].ssrc = 3; video_receiver_info_ssrc3.frame_width = 6789; video_receiver_info_ssrc3.frame_height = 9876; + video_receiver_info_ssrc3.jitter_buffer_delay_seconds = 2.5; + video_receiver_info_ssrc3.jitter_buffer_emitted_count = 25; video_receiver_info_ssrc3.frames_received = 1000; video_receiver_info_ssrc3.frames_decoded = 995; video_receiver_info_ssrc3.frames_rendered = 990; @@ -1694,6 +1696,8 @@ TEST_F(RTCStatsCollectorTest, expected_remote_video_track_ssrc3.detached = false; expected_remote_video_track_ssrc3.frame_width = 6789; expected_remote_video_track_ssrc3.frame_height = 9876; + expected_remote_video_track_ssrc3.jitter_buffer_delay = 2.5; + expected_remote_video_track_ssrc3.jitter_buffer_emitted_count = 25; expected_remote_video_track_ssrc3.frames_received = 1000; expected_remote_video_track_ssrc3.frames_decoded = 995; expected_remote_video_track_ssrc3.frames_dropped = 1000 - 990; diff --git a/pc/rtc_stats_integrationtest.cc b/pc/rtc_stats_integrationtest.cc index 36518c9c86..c21c796f14 100644 --- a/pc/rtc_stats_integrationtest.cc +++ b/pc/rtc_stats_integrationtest.cc @@ -555,24 +555,16 @@ class RTCStatsReportVerifier { verifier.TestMemberIsDefined(media_stream_track.ended); verifier.TestMemberIsDefined(media_stream_track.detached); verifier.TestMemberIsDefined(media_stream_track.kind); + RTC_DCHECK(media_stream_track.remote_source.is_defined()); // Video or audio media stream track? if (*media_stream_track.kind == RTCMediaStreamTrackKind::kVideo) { // The type of the referenced media source depends on kind. - if (media_stream_track.remote_source.is_defined() && - !*media_stream_track.remote_source) { - verifier.TestMemberIsIDReference(media_stream_track.media_source_id, - RTCVideoSourceStats::kType); - } else { - // Remote tracks don't have media source stats. - verifier.TestMemberIsUndefined(media_stream_track.media_source_id); - } - // Video-only members - verifier.TestMemberIsNonNegative( - media_stream_track.frame_width); - verifier.TestMemberIsNonNegative( - media_stream_track.frame_height); - verifier.TestMemberIsUndefined(media_stream_track.frames_per_second); if (*media_stream_track.remote_source) { + verifier.TestMemberIsUndefined(media_stream_track.media_source_id); + verifier.TestMemberIsNonNegative( + media_stream_track.jitter_buffer_delay); + verifier.TestMemberIsNonNegative( + media_stream_track.jitter_buffer_emitted_count); verifier.TestMemberIsUndefined(media_stream_track.frames_sent); verifier.TestMemberIsUndefined(media_stream_track.huge_frames_sent); verifier.TestMemberIsNonNegative( @@ -594,6 +586,12 @@ class RTCStatsReportVerifier { verifier.TestMemberIsNonNegative( media_stream_track.sum_squared_frame_durations); } else { + verifier.TestMemberIsIDReference(media_stream_track.media_source_id, + RTCVideoSourceStats::kType); + // Local tracks have no jitter buffer. + verifier.TestMemberIsUndefined(media_stream_track.jitter_buffer_delay); + verifier.TestMemberIsUndefined( + media_stream_track.jitter_buffer_emitted_count); verifier.TestMemberIsNonNegative( media_stream_track.frames_sent); verifier.TestMemberIsNonNegative( @@ -612,6 +610,12 @@ class RTCStatsReportVerifier { verifier.TestMemberIsUndefined( media_stream_track.sum_squared_frame_durations); } + // Video-only members + verifier.TestMemberIsNonNegative( + media_stream_track.frame_width); + verifier.TestMemberIsNonNegative( + media_stream_track.frame_height); + verifier.TestMemberIsUndefined(media_stream_track.frames_per_second); verifier.TestMemberIsUndefined(media_stream_track.frames_corrupted); verifier.TestMemberIsUndefined(media_stream_track.partial_frames_lost); verifier.TestMemberIsUndefined(media_stream_track.full_frames_lost); @@ -622,16 +626,69 @@ class RTCStatsReportVerifier { media_stream_track.echo_return_loss_enhancement); verifier.TestMemberIsUndefined(media_stream_track.total_audio_energy); verifier.TestMemberIsUndefined(media_stream_track.total_samples_duration); + verifier.TestMemberIsUndefined(media_stream_track.total_samples_received); + verifier.TestMemberIsUndefined(media_stream_track.concealed_samples); + verifier.TestMemberIsUndefined(media_stream_track.concealment_events); + verifier.TestMemberIsUndefined(media_stream_track.jitter_buffer_flushes); + verifier.TestMemberIsUndefined( + media_stream_track.delayed_packet_outage_samples); + verifier.TestMemberIsUndefined( + media_stream_track.relative_packet_arrival_delay); + verifier.TestMemberIsUndefined(media_stream_track.interruption_count); + verifier.TestMemberIsUndefined( + media_stream_track.total_interruption_duration); } else { RTC_DCHECK_EQ(*media_stream_track.kind, RTCMediaStreamTrackKind::kAudio); // The type of the referenced media source depends on kind. - if (media_stream_track.remote_source.is_defined() && - !*media_stream_track.remote_source) { - verifier.TestMemberIsIDReference(media_stream_track.media_source_id, - RTCAudioSourceStats::kType); - } else { + if (*media_stream_track.remote_source) { // Remote tracks don't have media source stats. verifier.TestMemberIsUndefined(media_stream_track.media_source_id); + verifier.TestMemberIsNonNegative( + media_stream_track.jitter_buffer_delay); + verifier.TestMemberIsNonNegative( + media_stream_track.jitter_buffer_emitted_count); + verifier.TestMemberIsNonNegative( + media_stream_track.total_samples_received); + verifier.TestMemberIsNonNegative( + media_stream_track.concealed_samples); + verifier.TestMemberIsNonNegative( + media_stream_track.concealment_events); + verifier.TestMemberIsNonNegative( + media_stream_track.inserted_samples_for_deceleration); + verifier.TestMemberIsNonNegative( + media_stream_track.removed_samples_for_acceleration); + verifier.TestMemberIsNonNegative( + media_stream_track.silent_concealed_samples); + verifier.TestMemberIsNonNegative( + media_stream_track.jitter_buffer_flushes); + verifier.TestMemberIsNonNegative( + media_stream_track.delayed_packet_outage_samples); + verifier.TestMemberIsNonNegative( + media_stream_track.relative_packet_arrival_delay); + verifier.TestMemberIsNonNegative( + media_stream_track.interruption_count); + verifier.TestMemberIsNonNegative( + media_stream_track.total_interruption_duration); + } else { + verifier.TestMemberIsIDReference(media_stream_track.media_source_id, + RTCAudioSourceStats::kType); + // Local audio tracks have no jitter buffer. + verifier.TestMemberIsUndefined(media_stream_track.jitter_buffer_delay); + verifier.TestMemberIsUndefined( + media_stream_track.jitter_buffer_emitted_count); + verifier.TestMemberIsUndefined( + media_stream_track.total_samples_received); + verifier.TestMemberIsUndefined(media_stream_track.concealed_samples); + verifier.TestMemberIsUndefined(media_stream_track.concealment_events); + verifier.TestMemberIsUndefined( + media_stream_track.jitter_buffer_flushes); + verifier.TestMemberIsUndefined( + media_stream_track.delayed_packet_outage_samples); + verifier.TestMemberIsUndefined( + media_stream_track.relative_packet_arrival_delay); + verifier.TestMemberIsUndefined(media_stream_track.interruption_count); + verifier.TestMemberIsUndefined( + media_stream_track.total_interruption_duration); } // Video-only members should be undefined verifier.TestMemberIsUndefined(media_stream_track.frame_width); @@ -666,53 +723,6 @@ class RTCStatsReportVerifier { verifier.MarkMemberTested(media_stream_track.echo_return_loss_enhancement, true); } - // totalSamplesReceived, concealedSamples and concealmentEvents are only - // present on inbound audio tracks. - // jitterBufferDelay is currently only implemented for audio. - if (*media_stream_track.kind == RTCMediaStreamTrackKind::kAudio && - *media_stream_track.remote_source) { - verifier.TestMemberIsNonNegative( - media_stream_track.jitter_buffer_delay); - verifier.TestMemberIsNonNegative( - media_stream_track.jitter_buffer_emitted_count); - verifier.TestMemberIsNonNegative( - media_stream_track.total_samples_received); - verifier.TestMemberIsNonNegative( - media_stream_track.concealed_samples); - verifier.TestMemberIsNonNegative( - media_stream_track.concealment_events); - verifier.TestMemberIsNonNegative( - media_stream_track.inserted_samples_for_deceleration); - verifier.TestMemberIsNonNegative( - media_stream_track.removed_samples_for_acceleration); - verifier.TestMemberIsNonNegative( - media_stream_track.silent_concealed_samples); - verifier.TestMemberIsNonNegative( - media_stream_track.jitter_buffer_flushes); - verifier.TestMemberIsNonNegative( - media_stream_track.delayed_packet_outage_samples); - verifier.TestMemberIsNonNegative( - media_stream_track.relative_packet_arrival_delay); - verifier.TestMemberIsNonNegative( - media_stream_track.interruption_count); - verifier.TestMemberIsNonNegative( - media_stream_track.total_interruption_duration); - } else { - verifier.TestMemberIsUndefined(media_stream_track.jitter_buffer_delay); - verifier.TestMemberIsUndefined( - media_stream_track.jitter_buffer_emitted_count); - verifier.TestMemberIsUndefined(media_stream_track.total_samples_received); - verifier.TestMemberIsUndefined(media_stream_track.concealed_samples); - verifier.TestMemberIsUndefined(media_stream_track.concealment_events); - verifier.TestMemberIsUndefined(media_stream_track.jitter_buffer_flushes); - verifier.TestMemberIsUndefined( - media_stream_track.delayed_packet_outage_samples); - verifier.TestMemberIsUndefined( - media_stream_track.relative_packet_arrival_delay); - verifier.TestMemberIsUndefined(media_stream_track.interruption_count); - verifier.TestMemberIsUndefined( - media_stream_track.total_interruption_duration); - } return verifier.ExpectAllMembersSuccessfullyTested(); } diff --git a/video/receive_statistics_proxy.cc b/video/receive_statistics_proxy.cc index 9be6933939..49ae0d5a1a 100644 --- a/video/receive_statistics_proxy.cc +++ b/video/receive_statistics_proxy.cc @@ -596,6 +596,10 @@ VideoReceiveStream::Stats ReceiveStatisticsProxy::GetStats() const { video_quality_observer_->SumSquaredFrameDurationsSec(); stats_.content_type = last_content_type_; stats_.timing_frame_info = timing_frame_info_counter_.Max(now_ms); + stats_.jitter_buffer_delay_seconds = + static_cast(current_delay_counter_.Sum(1).value_or(0)) / + rtc::kNumMillisecsPerSec; + stats_.jitter_buffer_emitted_count = current_delay_counter_.NumSamples(); return stats_; }