diff --git a/api/stats/rtcstats_objects.h b/api/stats/rtcstats_objects.h index 8496f8caa2..8392231218 100644 --- a/api/stats/rtcstats_objects.h +++ b/api/stats/rtcstats_objects.h @@ -309,8 +309,9 @@ class RTC_EXPORT RTCMediaStreamTrackStats final : public RTCStats { RTCStatsMember concealed_samples; RTCStatsMember concealment_events; // Non-standard audio-only member - // TODO(kuddai): Add descriptoin to standard. crbug.com/webrtc/10042 + // TODO(kuddai): Add description to standard. crbug.com/webrtc/10042 RTCNonStandardStatsMember jitter_buffer_flushes; + RTCNonStandardStatsMember delayed_packet_outage_samples; }; // https://w3c.github.io/webrtc-stats/#pcstats-dict* diff --git a/audio/audio_receive_stream.cc b/audio/audio_receive_stream.cc index 2da387713b..e21af555a4 100644 --- a/audio/audio_receive_stream.cc +++ b/audio/audio_receive_stream.cc @@ -214,6 +214,7 @@ webrtc::AudioReceiveStream::Stats AudioReceiveStream::GetStats() const { stats.accelerate_rate = Q14ToFloat(ns.currentAccelerateRate); stats.preemptive_expand_rate = Q14ToFloat(ns.currentPreemptiveRate); stats.jitter_buffer_flushes = ns.packetBufferFlushes; + stats.delayed_packet_outage_samples = ns.delayedPacketOutageSamples; auto ds = channel_receive_->GetDecodingCallStatistics(); stats.decoding_calls_to_silence_generator = ds.calls_to_silence_generator; diff --git a/call/audio_receive_stream.h b/call/audio_receive_stream.h index fe609de72d..11128efc97 100644 --- a/call/audio_receive_stream.h +++ b/call/audio_receive_stream.h @@ -63,6 +63,7 @@ class AudioReceiveStream { float secondary_discarded_rate = 0.0f; float accelerate_rate = 0.0f; float preemptive_expand_rate = 0.0f; + uint64_t delayed_packet_outage_samples = 0; int32_t decoding_calls_to_silence_generator = 0; int32_t decoding_calls_to_neteq = 0; int32_t decoding_normal = 0; diff --git a/media/base/mediachannel.h b/media/base/mediachannel.h index 04e70c5122..fca6ee4dd9 100644 --- a/media/base/mediachannel.h +++ b/media/base/mediachannel.h @@ -478,6 +478,8 @@ struct VoiceReceiverInfo : public MediaReceiverInfo { int64_t capture_start_ntp_time_ms = -1; // Count of the number of buffer flushes. uint64_t jitter_buffer_flushes = 0; + // Number of samples expanded due to delayed packets. + uint64_t delayed_packet_outage_samples = 0; }; struct VideoSenderInfo : public MediaSenderInfo { diff --git a/media/engine/webrtcvoiceengine.cc b/media/engine/webrtcvoiceengine.cc index c6751bb59e..75b1ecc8c6 100644 --- a/media/engine/webrtcvoiceengine.cc +++ b/media/engine/webrtcvoiceengine.cc @@ -2233,6 +2233,7 @@ bool WebRtcVoiceMediaChannel::GetStats(VoiceMediaInfo* info) { rinfo.secondary_discarded_rate = stats.secondary_discarded_rate; rinfo.accelerate_rate = stats.accelerate_rate; rinfo.preemptive_expand_rate = stats.preemptive_expand_rate; + rinfo.delayed_packet_outage_samples = stats.delayed_packet_outage_samples; rinfo.decoding_calls_to_silence_generator = stats.decoding_calls_to_silence_generator; rinfo.decoding_calls_to_neteq = stats.decoding_calls_to_neteq; diff --git a/modules/audio_coding/acm2/acm_receiver.cc b/modules/audio_coding/acm2/acm_receiver.cc index d02e50d43d..d3af7c0341 100644 --- a/modules/audio_coding/acm2/acm_receiver.cc +++ b/modules/audio_coding/acm2/acm_receiver.cc @@ -345,6 +345,8 @@ void AcmReceiver::GetNetworkStatistics(NetworkStatistics* acm_stat) { acm_stat->concealedSamples = neteq_lifetime_stat.concealed_samples; acm_stat->concealmentEvents = neteq_lifetime_stat.concealment_events; acm_stat->jitterBufferDelayMs = neteq_lifetime_stat.jitter_buffer_delay_ms; + acm_stat->delayedPacketOutageSamples = + neteq_lifetime_stat.delayed_packet_outage_samples; NetEqOperationsAndState neteq_operations_and_state = neteq_->GetOperationsAndState(); diff --git a/modules/audio_coding/include/audio_coding_module_typedefs.h b/modules/audio_coding/include/audio_coding_module_typedefs.h index 3c746de883..bafff72e5a 100644 --- a/modules/audio_coding/include/audio_coding_module_typedefs.h +++ b/modules/audio_coding/include/audio_coding_module_typedefs.h @@ -119,6 +119,8 @@ struct NetworkStatistics { size_t addedSamples; // count of the number of buffer flushes uint64_t packetBufferFlushes; + // number of samples expanded due to delayed packets + uint64_t delayedPacketOutageSamples; }; } // namespace webrtc diff --git a/modules/audio_coding/neteq/expand.cc b/modules/audio_coding/neteq/expand.cc index 97ce529770..4a06d09c1c 100644 --- a/modules/audio_coding/neteq/expand.cc +++ b/modules/audio_coding/neteq/expand.cc @@ -323,8 +323,7 @@ void Expand::SetParametersForNormalAfterExpand() { current_lag_index_ = 0; lag_index_direction_ = 0; stop_muting_ = true; // Do not mute signal any more. - statistics_->LogDelayedPacketOutageEvent( - rtc::dchecked_cast(expand_duration_samples_) / (fs_hz_ / 1000)); + statistics_->LogDelayedPacketOutageEvent(expand_duration_samples_, fs_hz_); } void Expand::SetParametersForMergeAfterExpand() { diff --git a/modules/audio_coding/neteq/expand_unittest.cc b/modules/audio_coding/neteq/expand_unittest.cc index b4e6466e90..09914daca8 100644 --- a/modules/audio_coding/neteq/expand_unittest.cc +++ b/modules/audio_coding/neteq/expand_unittest.cc @@ -51,14 +51,16 @@ TEST(Expand, CreateUsingFactory) { namespace { class FakeStatisticsCalculator : public StatisticsCalculator { public: - void LogDelayedPacketOutageEvent(int outage_duration_ms) override { - last_outage_duration_ms_ = outage_duration_ms; + void LogDelayedPacketOutageEvent(int num_samples, int fs_hz) override { + last_outage_duration_samples_ = num_samples; } - int last_outage_duration_ms() const { return last_outage_duration_ms_; } + int last_outage_duration_samples() const { + return last_outage_duration_samples_; + } private: - int last_outage_duration_ms_ = 0; + int last_outage_duration_samples_ = 0; }; // This is the same size that is given to the SyncBuffer object in NetEq. @@ -120,13 +122,12 @@ TEST_F(ExpandTest, DelayedPacketOutage) { EXPECT_EQ(0, expand_.Process(&output)); EXPECT_GT(output.Size(), 0u); sum_output_len_samples += output.Size(); - EXPECT_EQ(0, statistics_.last_outage_duration_ms()); + EXPECT_EQ(0, statistics_.last_outage_duration_samples()); } expand_.SetParametersForNormalAfterExpand(); // Convert |sum_output_len_samples| to milliseconds. - EXPECT_EQ(rtc::checked_cast(sum_output_len_samples / - (test_sample_rate_hz_ / 1000)), - statistics_.last_outage_duration_ms()); + EXPECT_EQ(rtc::checked_cast(sum_output_len_samples), + statistics_.last_outage_duration_samples()); } // This test is similar to DelayedPacketOutage, but ends by calling @@ -140,10 +141,10 @@ TEST_F(ExpandTest, LostPacketOutage) { EXPECT_EQ(0, expand_.Process(&output)); EXPECT_GT(output.Size(), 0u); sum_output_len_samples += output.Size(); - EXPECT_EQ(0, statistics_.last_outage_duration_ms()); + EXPECT_EQ(0, statistics_.last_outage_duration_samples()); } expand_.SetParametersForMergeAfterExpand(); - EXPECT_EQ(0, statistics_.last_outage_duration_ms()); + EXPECT_EQ(0, statistics_.last_outage_duration_samples()); } // This test is similar to the DelayedPacketOutage test above, but with the @@ -161,13 +162,12 @@ TEST_F(ExpandTest, CheckOutageStatsAfterReset) { expand_.Reset(); sum_output_len_samples = 0; } - EXPECT_EQ(0, statistics_.last_outage_duration_ms()); + EXPECT_EQ(0, statistics_.last_outage_duration_samples()); } expand_.SetParametersForNormalAfterExpand(); // Convert |sum_output_len_samples| to milliseconds. - EXPECT_EQ(rtc::checked_cast(sum_output_len_samples / - (test_sample_rate_hz_ / 1000)), - statistics_.last_outage_duration_ms()); + EXPECT_EQ(rtc::checked_cast(sum_output_len_samples), + statistics_.last_outage_duration_samples()); } namespace { diff --git a/modules/audio_coding/neteq/include/neteq.h b/modules/audio_coding/neteq/include/neteq.h index ec7ceb7d3f..e1d166cdbb 100644 --- a/modules/audio_coding/neteq/include/neteq.h +++ b/modules/audio_coding/neteq/include/neteq.h @@ -72,6 +72,7 @@ struct NetEqLifetimeStatistics { uint64_t jitter_buffer_delay_ms = 0; // Below stat is not part of the spec. uint64_t voice_concealed_samples = 0; + uint64_t delayed_packet_outage_samples = 0; }; // Metrics that describe the operations performed in NetEq, and the internal diff --git a/modules/audio_coding/neteq/statistics_calculator.cc b/modules/audio_coding/neteq/statistics_calculator.cc index 807d7ee401..50521fb03d 100644 --- a/modules/audio_coding/neteq/statistics_calculator.cc +++ b/modules/audio_coding/neteq/statistics_calculator.cc @@ -257,11 +257,14 @@ void StatisticsCalculator::FlushedPacketBuffer() { buffer_full_counter_.RegisterSample(); } -void StatisticsCalculator::LogDelayedPacketOutageEvent(int outage_duration_ms) { +void StatisticsCalculator::LogDelayedPacketOutageEvent(int num_samples, + int fs_hz) { + int outage_duration_ms = num_samples / (fs_hz / 1000); RTC_HISTOGRAM_COUNTS("WebRTC.Audio.DelayedPacketOutageEventMs", outage_duration_ms, 1 /* min */, 2000 /* max */, 100 /* bucket count */); delayed_packet_outage_counter_.RegisterSample(); + lifetime_stats_.delayed_packet_outage_samples += num_samples; } void StatisticsCalculator::StoreWaitingTime(int waiting_time_ms) { diff --git a/modules/audio_coding/neteq/statistics_calculator.h b/modules/audio_coding/neteq/statistics_calculator.h index 6a5f7f4d13..49b74a04b3 100644 --- a/modules/audio_coding/neteq/statistics_calculator.h +++ b/modules/audio_coding/neteq/statistics_calculator.h @@ -86,10 +86,10 @@ class StatisticsCalculator { // Rerport that the packet buffer was flushed. void FlushedPacketBuffer(); - // Logs a delayed packet outage event of |outage_duration_ms|. A delayed - // packet outage event is defined as an expand period caused not by an actual - // packet loss, but by a delayed packet. - virtual void LogDelayedPacketOutageEvent(int outage_duration_ms); + // Logs a delayed packet outage event of |num_samples| expanded at a sample + // rate of |fs_hz|. A delayed packet outage event is defined as an expand + // period caused not by an actual packet loss, but by a delayed packet. + virtual void LogDelayedPacketOutageEvent(int num_samples, int fs_hz); // Returns the current network statistics in |stats|. The current sample rate // is |fs_hz|, the total number of samples in packet buffer and sync buffer diff --git a/pc/rtcstats_integrationtest.cc b/pc/rtcstats_integrationtest.cc index ef29cbc71e..49084de4d2 100644 --- a/pc/rtcstats_integrationtest.cc +++ b/pc/rtcstats_integrationtest.cc @@ -606,12 +606,16 @@ class RTCStatsReportVerifier { media_stream_track.concealment_events); verifier.TestMemberIsNonNegative( media_stream_track.jitter_buffer_flushes); + verifier.TestMemberIsNonNegative( + media_stream_track.delayed_packet_outage_samples); } else { verifier.TestMemberIsUndefined(media_stream_track.jitter_buffer_delay); 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); } return verifier.ExpectAllMembersSuccessfullyTested(); } diff --git a/pc/rtcstatscollector.cc b/pc/rtcstatscollector.cc index 33d4af1bec..d48ecc01f3 100644 --- a/pc/rtcstatscollector.cc +++ b/pc/rtcstatscollector.cc @@ -456,6 +456,8 @@ ProduceMediaStreamTrackStatsFromVoiceReceiverInfo( voice_receiver_info.concealment_events; audio_track_stats->jitter_buffer_flushes = voice_receiver_info.jitter_buffer_flushes; + audio_track_stats->delayed_packet_outage_samples = + voice_receiver_info.delayed_packet_outage_samples; return audio_track_stats; } diff --git a/pc/rtcstatscollector_unittest.cc b/pc/rtcstatscollector_unittest.cc index e16c7e302b..adca9ef6c4 100644 --- a/pc/rtcstatscollector_unittest.cc +++ b/pc/rtcstatscollector_unittest.cc @@ -1427,6 +1427,7 @@ TEST_F(RTCStatsCollectorTest, voice_receiver_info.concealment_events = 12; voice_receiver_info.jitter_buffer_delay_seconds = 3456; voice_receiver_info.jitter_buffer_flushes = 7; + voice_receiver_info.delayed_packet_outage_samples = 15; stats_->CreateMockRtpSendersReceiversAndChannels( {}, {std::make_pair(remote_audio_track.get(), voice_receiver_info)}, {}, @@ -1461,6 +1462,7 @@ TEST_F(RTCStatsCollectorTest, expected_remote_audio_track.concealment_events = 12; expected_remote_audio_track.jitter_buffer_delay = 3456; expected_remote_audio_track.jitter_buffer_flushes = 7; + expected_remote_audio_track.delayed_packet_outage_samples = 15; ASSERT_TRUE(report->Get(expected_remote_audio_track.id())); EXPECT_EQ(expected_remote_audio_track, report->Get(expected_remote_audio_track.id()) diff --git a/stats/rtcstats_objects.cc b/stats/rtcstats_objects.cc index 669db14fc8..cd52a55971 100644 --- a/stats/rtcstats_objects.cc +++ b/stats/rtcstats_objects.cc @@ -375,7 +375,8 @@ WEBRTC_RTCSTATS_IMPL(RTCMediaStreamTrackStats, RTCStats, "track", &total_samples_duration, &concealed_samples, &concealment_events, - &jitter_buffer_flushes); + &jitter_buffer_flushes, + &delayed_packet_outage_samples); // clang-format on RTCMediaStreamTrackStats::RTCMediaStreamTrackStats(const std::string& id, @@ -412,7 +413,8 @@ RTCMediaStreamTrackStats::RTCMediaStreamTrackStats(std::string&& id, total_samples_duration("totalSamplesDuration"), concealed_samples("concealedSamples"), concealment_events("concealmentEvents"), - jitter_buffer_flushes("jitterBufferFlushes") { + jitter_buffer_flushes("jitterBufferFlushes"), + delayed_packet_outage_samples("delayedPacketOutageSamples") { RTC_DCHECK(kind == RTCMediaStreamTrackKind::kAudio || kind == RTCMediaStreamTrackKind::kVideo); } @@ -445,7 +447,8 @@ RTCMediaStreamTrackStats::RTCMediaStreamTrackStats( total_samples_duration(other.total_samples_duration), concealed_samples(other.concealed_samples), concealment_events(other.concealment_events), - jitter_buffer_flushes(other.jitter_buffer_flushes) {} + jitter_buffer_flushes(other.jitter_buffer_flushes), + delayed_packet_outage_samples(other.delayed_packet_outage_samples) {} RTCMediaStreamTrackStats::~RTCMediaStreamTrackStats() {}