diff --git a/api/neteq/neteq.h b/api/neteq/neteq.h index 67dbd507a1..d632e03f35 100644 --- a/api/neteq/neteq.h +++ b/api/neteq/neteq.h @@ -68,6 +68,7 @@ struct NetEqLifetimeStatistics { uint64_t concealment_events = 0; uint64_t jitter_buffer_delay_ms = 0; uint64_t jitter_buffer_emitted_count = 0; + uint64_t jitter_buffer_target_delay_ms = 0; uint64_t inserted_samples_for_deceleration = 0; uint64_t removed_samples_for_acceleration = 0; uint64_t silent_concealed_samples = 0; diff --git a/api/stats/rtcstats_objects.h b/api/stats/rtcstats_objects.h index 197a810d29..e83c83d97e 100644 --- a/api/stats/rtcstats_objects.h +++ b/api/stats/rtcstats_objects.h @@ -327,6 +327,14 @@ class RTC_EXPORT RTCMediaStreamTrackStats final : public RTCStats { RTCNonStandardStatsMember jitter_buffer_flushes; RTCNonStandardStatsMember delayed_packet_outage_samples; RTCNonStandardStatsMember relative_packet_arrival_delay; + // Non-standard metric showing target delay of jitter buffer. + // This value is increased by the target jitter buffer delay every time a + // sample is emitted by the jitter buffer. The added target is the target + // delay, in seconds, at the time that the sample was emitted from the jitter + // buffer. (https://github.com/w3c/webrtc-provisional-stats/pull/20) + // Currently it is implemented only for audio. + // TODO(titovartem) implement for video streams when will be requested. + RTCNonStandardStatsMember jitter_buffer_target_delay; // TODO(henrik.lundin): Add description of the interruption metrics at // https://github.com/henbos/webrtc-provisional-stats/issues/17 RTCNonStandardStatsMember interruption_count; diff --git a/audio/audio_receive_stream.cc b/audio/audio_receive_stream.cc index 7c6adcd461..ce1b344129 100644 --- a/audio/audio_receive_stream.cc +++ b/audio/audio_receive_stream.cc @@ -222,6 +222,9 @@ webrtc::AudioReceiveStream::Stats AudioReceiveStream::GetStats() const { static_cast(ns.jitterBufferDelayMs) / static_cast(rtc::kNumMillisecsPerSec); stats.jitter_buffer_emitted_count = ns.jitterBufferEmittedCount; + stats.jitter_buffer_target_delay_seconds = + static_cast(ns.jitterBufferTargetDelayMs) / + static_cast(rtc::kNumMillisecsPerSec); stats.inserted_samples_for_deceleration = ns.insertedSamplesForDeceleration; stats.removed_samples_for_acceleration = ns.removedSamplesForAcceleration; stats.expand_rate = Q14ToFloat(ns.currentExpandRate); diff --git a/audio/audio_receive_stream_unittest.cc b/audio/audio_receive_stream_unittest.cc index b8eff0a443..0b2cae507e 100644 --- a/audio/audio_receive_stream_unittest.cc +++ b/audio/audio_receive_stream_unittest.cc @@ -69,9 +69,9 @@ const std::pair kReceiveCodec = { 123, {"codec_name_recv", 96000, 0}}; const NetworkStatistics kNetworkStats = { - 123, 456, false, 789012, 3456, 123, 456, 789, 543, 432, - 321, 123, 101, 0, {}, 789, 12, 345, 678, 901, - 0, -1, -1, -1, -1, 0, 0, 0, 0}; + 123, 456, false, 789012, 3456, 123, 456, 789, 543, 123, + 432, 321, 123, 101, 0, {}, 789, 12, 345, 678, + 901, 0, -1, -1, -1, -1, 0, 0, 0, 0}; const AudioDecodingCallStats kAudioDecodeStats = MakeAudioDecodeStatsForTest(); struct ConfigHelper { @@ -294,6 +294,9 @@ TEST(AudioReceiveStreamTest, GetStats) { stats.jitter_buffer_delay_seconds); EXPECT_EQ(kNetworkStats.jitterBufferEmittedCount, stats.jitter_buffer_emitted_count); + EXPECT_EQ(static_cast(kNetworkStats.jitterBufferTargetDelayMs) / + static_cast(rtc::kNumMillisecsPerSec), + stats.jitter_buffer_target_delay_seconds); EXPECT_EQ(Q14ToFloat(kNetworkStats.currentExpandRate), stats.expand_rate); EXPECT_EQ(Q14ToFloat(kNetworkStats.currentSpeechExpandRate), stats.speech_expand_rate); diff --git a/call/audio_receive_stream.h b/call/audio_receive_stream.h index 090fb82090..4a50cdbc46 100644 --- a/call/audio_receive_stream.h +++ b/call/audio_receive_stream.h @@ -58,6 +58,7 @@ class AudioReceiveStream { uint64_t concealment_events = 0; double jitter_buffer_delay_seconds = 0.0; uint64_t jitter_buffer_emitted_count = 0; + double jitter_buffer_target_delay_seconds = 0.0; uint64_t inserted_samples_for_deceleration = 0; uint64_t removed_samples_for_acceleration = 0; // Stats below DO NOT correspond directly to anything in the WebRTC stats diff --git a/media/base/media_channel.h b/media/base/media_channel.h index a62c618562..7ca6002404 100644 --- a/media/base/media_channel.h +++ b/media/base/media_channel.h @@ -512,6 +512,7 @@ struct VoiceReceiverInfo : public MediaReceiverInfo { uint64_t concealment_events = 0; double jitter_buffer_delay_seconds = 0.0; uint64_t jitter_buffer_emitted_count = 0; + double jitter_buffer_target_delay_seconds = 0.0; uint64_t inserted_samples_for_deceleration = 0; uint64_t removed_samples_for_acceleration = 0; uint64_t fec_packets_received = 0; diff --git a/media/engine/webrtc_voice_engine.cc b/media/engine/webrtc_voice_engine.cc index 7da9abd384..45b46bf82d 100644 --- a/media/engine/webrtc_voice_engine.cc +++ b/media/engine/webrtc_voice_engine.cc @@ -2230,6 +2230,8 @@ bool WebRtcVoiceMediaChannel::GetStats(VoiceMediaInfo* info) { rinfo.concealment_events = stats.concealment_events; rinfo.jitter_buffer_delay_seconds = stats.jitter_buffer_delay_seconds; rinfo.jitter_buffer_emitted_count = stats.jitter_buffer_emitted_count; + rinfo.jitter_buffer_target_delay_seconds = + stats.jitter_buffer_target_delay_seconds; rinfo.inserted_samples_for_deceleration = stats.inserted_samples_for_deceleration; rinfo.removed_samples_for_acceleration = diff --git a/modules/audio_coding/acm2/acm_receiver.cc b/modules/audio_coding/acm2/acm_receiver.cc index 9cecb98cec..29eff19e9e 100644 --- a/modules/audio_coding/acm2/acm_receiver.cc +++ b/modules/audio_coding/acm2/acm_receiver.cc @@ -272,6 +272,8 @@ void AcmReceiver::GetNetworkStatistics(NetworkStatistics* acm_stat) const { neteq_lifetime_stat.silent_concealed_samples; acm_stat->concealmentEvents = neteq_lifetime_stat.concealment_events; acm_stat->jitterBufferDelayMs = neteq_lifetime_stat.jitter_buffer_delay_ms; + acm_stat->jitterBufferTargetDelayMs = + neteq_lifetime_stat.jitter_buffer_target_delay_ms; acm_stat->jitterBufferEmittedCount = neteq_lifetime_stat.jitter_buffer_emitted_count; acm_stat->delayedPacketOutageSamples = diff --git a/modules/audio_coding/include/audio_coding_module_typedefs.h b/modules/audio_coding/include/audio_coding_module_typedefs.h index 2f40acde07..a5d4b246c8 100644 --- a/modules/audio_coding/include/audio_coding_module_typedefs.h +++ b/modules/audio_coding/include/audio_coding_module_typedefs.h @@ -90,6 +90,8 @@ struct NetworkStatistics { uint64_t concealmentEvents; uint64_t jitterBufferDelayMs; uint64_t jitterBufferEmittedCount; + // Non standard stats propagated to spec complaint GetStats API. + uint64_t jitterBufferTargetDelayMs; uint64_t insertedSamplesForDeceleration; uint64_t removedSamplesForAcceleration; uint64_t fecPacketsReceived; diff --git a/modules/audio_coding/neteq/neteq_impl.cc b/modules/audio_coding/neteq/neteq_impl.cc index 265043acb4..0b7510d341 100644 --- a/modules/audio_coding/neteq/neteq_impl.cc +++ b/modules/audio_coding/neteq/neteq_impl.cc @@ -1987,7 +1987,9 @@ int NetEqImpl::ExtractPackets(size_t required_samples, } extracted_samples = packet->timestamp - first_timestamp + packet_duration; - stats_->JitterBufferDelay(packet_duration, waiting_time_ms); + RTC_DCHECK(controller_); + stats_->JitterBufferDelay(packet_duration, waiting_time_ms, + controller_->TargetLevelMs()); packet_list->push_back(std::move(*packet)); // Store packet in list. packet = absl::nullopt; // Ensure it's never used after the move. diff --git a/modules/audio_coding/neteq/neteq_unittest.cc b/modules/audio_coding/neteq/neteq_unittest.cc index 701a3c568a..d78e2c6488 100644 --- a/modules/audio_coding/neteq/neteq_unittest.cc +++ b/modules/audio_coding/neteq/neteq_unittest.cc @@ -986,6 +986,7 @@ void NetEqDecodingTestFaxMode::TestJitterBufferDelay(bool apply_packet_loss) { int packets_sent = 0; int packets_received = 0; int expected_delay = 0; + int expected_target_delay = 0; uint64_t expected_emitted_count = 0; while (packets_received < kNumPackets) { // Insert packet. @@ -1010,6 +1011,7 @@ void NetEqDecodingTestFaxMode::TestJitterBufferDelay(bool apply_packet_loss) { // number of samples that are sent for play out. int current_delay_ms = packets_delay * kPacketLenMs; expected_delay += current_delay_ms * kSamples; + expected_target_delay += neteq_->TargetDelayMs() * kSamples; expected_emitted_count += kSamples; } } @@ -1021,8 +1023,11 @@ void NetEqDecodingTestFaxMode::TestJitterBufferDelay(bool apply_packet_loss) { // Check jitter buffer delay. NetEqLifetimeStatistics stats = neteq_->GetLifetimeStatistics(); - EXPECT_EQ(expected_delay, static_cast(stats.jitter_buffer_delay_ms)); + EXPECT_EQ(expected_delay, + rtc::checked_cast(stats.jitter_buffer_delay_ms)); EXPECT_EQ(expected_emitted_count, stats.jitter_buffer_emitted_count); + EXPECT_EQ(expected_target_delay, + rtc::checked_cast(stats.jitter_buffer_target_delay_ms)); } TEST_F(NetEqDecodingTestFaxMode, TestJitterBufferDelayWithoutLoss) { @@ -1043,6 +1048,7 @@ TEST_F(NetEqDecodingTestFaxMode, TestJitterBufferDelayWithAcceleration) { rtp_info.markerBit = 0; const uint8_t payload[kPayloadBytes] = {0}; + int expected_target_delay = neteq_->TargetDelayMs() * kSamples; neteq_->InsertPacket(rtp_info, payload); bool muted; @@ -1055,6 +1061,7 @@ TEST_F(NetEqDecodingTestFaxMode, TestJitterBufferDelayWithAcceleration) { rtp_info.timestamp += kSamples; neteq_->InsertPacket(rtp_info, payload); + expected_target_delay += neteq_->TargetDelayMs() * 2 * kSamples; // We have two packets in the buffer and kAccelerate operation will // extract 20 ms of data. neteq_->GetAudio(&out_frame_, &muted, NetEq::Operation::kAccelerate); @@ -1063,6 +1070,8 @@ TEST_F(NetEqDecodingTestFaxMode, TestJitterBufferDelayWithAcceleration) { NetEqLifetimeStatistics stats = neteq_->GetLifetimeStatistics(); EXPECT_EQ(10 * kSamples * 3, stats.jitter_buffer_delay_ms); EXPECT_EQ(kSamples * 3, stats.jitter_buffer_emitted_count); + EXPECT_EQ(expected_target_delay, + rtc::checked_cast(stats.jitter_buffer_target_delay_ms)); } namespace test { diff --git a/modules/audio_coding/neteq/statistics_calculator.cc b/modules/audio_coding/neteq/statistics_calculator.cc index 081ec334f6..fa2925ce46 100644 --- a/modules/audio_coding/neteq/statistics_calculator.cc +++ b/modules/audio_coding/neteq/statistics_calculator.cc @@ -275,8 +275,11 @@ void StatisticsCalculator::IncreaseCounter(size_t num_samples, int fs_hz) { } void StatisticsCalculator::JitterBufferDelay(size_t num_samples, - uint64_t waiting_time_ms) { + uint64_t waiting_time_ms, + uint64_t target_delay_ms) { lifetime_stats_.jitter_buffer_delay_ms += waiting_time_ms * num_samples; + lifetime_stats_.jitter_buffer_target_delay_ms += + target_delay_ms * num_samples; lifetime_stats_.jitter_buffer_emitted_count += num_samples; } diff --git a/modules/audio_coding/neteq/statistics_calculator.h b/modules/audio_coding/neteq/statistics_calculator.h index 38e463c782..333f4a76a9 100644 --- a/modules/audio_coding/neteq/statistics_calculator.h +++ b/modules/audio_coding/neteq/statistics_calculator.h @@ -83,7 +83,9 @@ class StatisticsCalculator { void IncreaseCounter(size_t num_samples, int fs_hz); // Update jitter buffer delay counter. - void JitterBufferDelay(size_t num_samples, uint64_t waiting_time_ms); + void JitterBufferDelay(size_t num_samples, + uint64_t waiting_time_ms, + uint64_t target_delay_ms); // Stores new packet waiting time in waiting time statistics. void StoreWaitingTime(int waiting_time_ms); diff --git a/pc/rtc_stats_collector.cc b/pc/rtc_stats_collector.cc index 5b34f1cac8..29c7d099b2 100644 --- a/pc/rtc_stats_collector.cc +++ b/pc/rtc_stats_collector.cc @@ -648,6 +648,8 @@ ProduceMediaStreamTrackStatsFromVoiceReceiverInfo( voice_receiver_info.delayed_packet_outage_samples; audio_track_stats->relative_packet_arrival_delay = voice_receiver_info.relative_packet_arrival_delay_seconds; + audio_track_stats->jitter_buffer_target_delay = + voice_receiver_info.jitter_buffer_target_delay_seconds; audio_track_stats->interruption_count = voice_receiver_info.interruption_count >= 0 ? voice_receiver_info.interruption_count diff --git a/pc/rtc_stats_collector_unittest.cc b/pc/rtc_stats_collector_unittest.cc index f886e14333..12f6059d9f 100644 --- a/pc/rtc_stats_collector_unittest.cc +++ b/pc/rtc_stats_collector_unittest.cc @@ -1547,6 +1547,7 @@ TEST_F(RTCStatsCollectorTest, voice_receiver_info.silent_concealed_samples = 765; voice_receiver_info.jitter_buffer_delay_seconds = 3456; voice_receiver_info.jitter_buffer_emitted_count = 13; + voice_receiver_info.jitter_buffer_target_delay_seconds = 7.894; voice_receiver_info.jitter_buffer_flushes = 7; voice_receiver_info.delayed_packet_outage_samples = 15; voice_receiver_info.relative_packet_arrival_delay_seconds = 16; @@ -1591,6 +1592,7 @@ TEST_F(RTCStatsCollectorTest, expected_remote_audio_track.silent_concealed_samples = 765; expected_remote_audio_track.jitter_buffer_delay = 3456; expected_remote_audio_track.jitter_buffer_emitted_count = 13; + expected_remote_audio_track.jitter_buffer_target_delay = 7.894; expected_remote_audio_track.jitter_buffer_flushes = 7; expected_remote_audio_track.delayed_packet_outage_samples = 15; expected_remote_audio_track.relative_packet_arrival_delay = 16; diff --git a/pc/rtc_stats_integrationtest.cc b/pc/rtc_stats_integrationtest.cc index 93a46efb8e..fa7d56a896 100644 --- a/pc/rtc_stats_integrationtest.cc +++ b/pc/rtc_stats_integrationtest.cc @@ -650,6 +650,8 @@ class RTCStatsReportVerifier { verifier.TestMemberIsUndefined(media_stream_track.interruption_count); verifier.TestMemberIsUndefined( media_stream_track.total_interruption_duration); + verifier.TestMemberIsUndefined( + media_stream_track.jitter_buffer_target_delay); } else { RTC_DCHECK_EQ(*media_stream_track.kind, RTCMediaStreamTrackKind::kAudio); // The type of the referenced media source depends on kind. @@ -660,6 +662,8 @@ class RTCStatsReportVerifier { media_stream_track.jitter_buffer_delay); verifier.TestMemberIsNonNegative( media_stream_track.jitter_buffer_emitted_count); + verifier.TestMemberIsNonNegative( + media_stream_track.jitter_buffer_target_delay); verifier.TestMemberIsPositive(media_stream_track.audio_level); verifier.TestMemberIsPositive( media_stream_track.total_audio_energy); @@ -694,6 +698,8 @@ class RTCStatsReportVerifier { verifier.TestMemberIsUndefined(media_stream_track.jitter_buffer_delay); verifier.TestMemberIsUndefined( media_stream_track.jitter_buffer_emitted_count); + verifier.TestMemberIsUndefined( + media_stream_track.jitter_buffer_target_delay); verifier.TestMemberIsUndefined(media_stream_track.audio_level); verifier.TestMemberIsUndefined(media_stream_track.total_audio_energy); verifier.TestMemberIsUndefined( diff --git a/stats/rtcstats_objects.cc b/stats/rtcstats_objects.cc index 77cbc09954..d99c9e7864 100644 --- a/stats/rtcstats_objects.cc +++ b/stats/rtcstats_objects.cc @@ -395,6 +395,7 @@ WEBRTC_RTCSTATS_IMPL(RTCMediaStreamTrackStats, RTCStats, "track", &jitter_buffer_flushes, &delayed_packet_outage_samples, &relative_packet_arrival_delay, + &jitter_buffer_target_delay, &interruption_count, &total_interruption_duration, &freeze_count, @@ -454,6 +455,7 @@ RTCMediaStreamTrackStats::RTCMediaStreamTrackStats(std::string&& id, relative_packet_arrival_delay( "relativePacketArrivalDelay", {NonStandardGroupId::kRtcStatsRelativePacketArrivalDelay}), + jitter_buffer_target_delay("jitterBufferTargetDelay"), interruption_count("interruptionCount"), total_interruption_duration("totalInterruptionDuration"), freeze_count("freezeCount"), @@ -503,6 +505,7 @@ RTCMediaStreamTrackStats::RTCMediaStreamTrackStats( jitter_buffer_flushes(other.jitter_buffer_flushes), delayed_packet_outage_samples(other.delayed_packet_outage_samples), relative_packet_arrival_delay(other.relative_packet_arrival_delay), + jitter_buffer_target_delay(other.jitter_buffer_target_delay), interruption_count(other.interruption_count), total_interruption_duration(other.total_interruption_duration), freeze_count(other.freeze_count),