From cd4203bf7292d62ea6c99da095e2c9456aed22c8 Mon Sep 17 00:00:00 2001 From: Tim Na Date: Mon, 2 Nov 2020 22:24:52 -0800 Subject: [PATCH] Adding total duration and more test cases to VoipStatistics. - Introduced IngressStatistics to cover total_duration which comes from AudioLevel. Bug: webrtc:11989 Change-Id: Iba52d3722b5fe6286b048ab5690e32a4f75e972a Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/190940 Commit-Queue: Tim Na Reviewed-by: Ivo Creusen Reviewed-by: Karl Wiberg Cr-Commit-Position: refs/heads/master@{#32538} --- api/voip/voip_statistics.h | 14 +++- audio/voip/audio_channel.cc | 41 +++++++----- audio/voip/audio_channel.h | 2 +- audio/voip/audio_ingress.h | 5 ++ audio/voip/test/audio_channel_unittest.cc | 82 ++++++++++++++++++----- audio/voip/voip_core.cc | 4 +- audio/voip/voip_core.h | 2 +- 7 files changed, 111 insertions(+), 39 deletions(-) diff --git a/api/voip/voip_statistics.h b/api/voip/voip_statistics.h index 5f4e174832..cf01e95e9e 100644 --- a/api/voip/voip_statistics.h +++ b/api/voip/voip_statistics.h @@ -16,13 +16,23 @@ namespace webrtc { +struct IngressStatistics { + // Stats included from api/neteq/neteq.h. + NetEqLifetimeStatistics neteq_stats; + + // Represents the total duration in seconds of all samples that have been + // received. + // https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-totalsamplesduration + double total_duration = 0.0; +}; + // VoipStatistics interface provides the interfaces for querying metrics around // the jitter buffer (NetEq) performance. class VoipStatistics { public: - // Gets the statistics from NetEq. Returns absl::nullopt when channel_id is + // Gets the audio ingress statistics. Returns absl::nullopt when channel_id is // invalid. - virtual absl::optional GetNetEqStatistics( + virtual absl::optional GetIngressStatistics( ChannelId channel_id) = 0; protected: diff --git a/audio/voip/audio_channel.cc b/audio/voip/audio_channel.cc index 28bd27020b..926130dc7e 100644 --- a/audio/voip/audio_channel.cc +++ b/audio/voip/audio_channel.cc @@ -129,29 +129,34 @@ void AudioChannel::StopPlay() { } } -NetEqLifetimeStatistics AudioChannel::GetNetEqStatistics() { - NetEqLifetimeStatistics neteq_stats; +IngressStatistics AudioChannel::GetIngressStatistics() { + IngressStatistics ingress_stats; NetworkStatistics stats = ingress_->GetNetworkStatistics(); - neteq_stats.total_samples_received = stats.totalSamplesReceived; - neteq_stats.concealed_samples = stats.concealedSamples; - neteq_stats.concealment_events = stats.concealmentEvents; - neteq_stats.jitter_buffer_delay_ms = stats.jitterBufferDelayMs; - neteq_stats.jitter_buffer_emitted_count = stats.jitterBufferEmittedCount; - neteq_stats.jitter_buffer_target_delay_ms = stats.jitterBufferTargetDelayMs; - neteq_stats.inserted_samples_for_deceleration = + ingress_stats.neteq_stats.total_samples_received = stats.totalSamplesReceived; + ingress_stats.neteq_stats.concealed_samples = stats.concealedSamples; + ingress_stats.neteq_stats.concealment_events = stats.concealmentEvents; + ingress_stats.neteq_stats.jitter_buffer_delay_ms = stats.jitterBufferDelayMs; + ingress_stats.neteq_stats.jitter_buffer_emitted_count = + stats.jitterBufferEmittedCount; + ingress_stats.neteq_stats.jitter_buffer_target_delay_ms = + stats.jitterBufferTargetDelayMs; + ingress_stats.neteq_stats.inserted_samples_for_deceleration = stats.insertedSamplesForDeceleration; - neteq_stats.removed_samples_for_acceleration = + ingress_stats.neteq_stats.removed_samples_for_acceleration = stats.removedSamplesForAcceleration; - neteq_stats.silent_concealed_samples = stats.silentConcealedSamples; - neteq_stats.fec_packets_received = stats.fecPacketsReceived; - neteq_stats.fec_packets_discarded = stats.fecPacketsDiscarded; - neteq_stats.delayed_packet_outage_samples = stats.delayedPacketOutageSamples; - neteq_stats.relative_packet_arrival_delay_ms = + ingress_stats.neteq_stats.silent_concealed_samples = + stats.silentConcealedSamples; + ingress_stats.neteq_stats.fec_packets_received = stats.fecPacketsReceived; + ingress_stats.neteq_stats.fec_packets_discarded = stats.fecPacketsDiscarded; + ingress_stats.neteq_stats.delayed_packet_outage_samples = + stats.delayedPacketOutageSamples; + ingress_stats.neteq_stats.relative_packet_arrival_delay_ms = stats.relativePacketArrivalDelayMs; - neteq_stats.interruption_count = stats.interruptionCount; - neteq_stats.total_interruption_duration_ms = + ingress_stats.neteq_stats.interruption_count = stats.interruptionCount; + ingress_stats.neteq_stats.total_interruption_duration_ms = stats.totalInterruptionDurationMs; - return neteq_stats; + ingress_stats.total_duration = ingress_->GetTotalDuration(); + return ingress_stats; } } // namespace webrtc diff --git a/audio/voip/audio_channel.h b/audio/voip/audio_channel.h index 9d0e707fae..a8946a7aa6 100644 --- a/audio/voip/audio_channel.h +++ b/audio/voip/audio_channel.h @@ -82,7 +82,7 @@ class AudioChannel : public rtc::RefCountInterface { void SetReceiveCodecs(const std::map& codecs) { ingress_->SetReceiveCodecs(codecs); } - NetEqLifetimeStatistics GetNetEqStatistics(); + IngressStatistics GetIngressStatistics(); private: // ChannelId that this audio channel belongs for logging purpose. diff --git a/audio/voip/audio_ingress.h b/audio/voip/audio_ingress.h index beff6cd6df..acb84c0b94 100644 --- a/audio/voip/audio_ingress.h +++ b/audio/voip/audio_ingress.h @@ -75,6 +75,11 @@ class AudioIngress : public AudioMixer::Source { int GetSpeechOutputLevelFullRange() const { return output_audio_level_.LevelFullRange(); } + // Retrieves the total duration for all samples played so far as explained in + // audio/AudioLevel.h. + double GetTotalDuration() const { + return output_audio_level_.TotalDuration(); + } // Returns network round trip time (RTT) measued by RTCP exchange with // remote media endpoint. RTT value -1 indicates that it's not initialized. diff --git a/audio/voip/test/audio_channel_unittest.cc b/audio/voip/test/audio_channel_unittest.cc index 601545bcd8..34b595cf9b 100644 --- a/audio/voip/test/audio_channel_unittest.cc +++ b/audio/voip/test/audio_channel_unittest.cc @@ -140,34 +140,86 @@ TEST_F(AudioChannelTest, VerifyLocalSsrcAsAssigned) { } // Check metrics after processing an RTP packet. -TEST_F(AudioChannelTest, TestAudioStatistics) { - rtc::Event event; +TEST_F(AudioChannelTest, TestIngressStatistics) { + auto event = std::make_unique(); auto loop_rtp = [&](const uint8_t* packet, size_t length, Unused) { audio_channel_->ReceivedRTPPacket( rtc::ArrayView(packet, length)); - event.Set(); + event->Set(); return true; }; - EXPECT_CALL(transport_, SendRtp).WillOnce(Invoke(loop_rtp)); + EXPECT_CALL(transport_, SendRtp).WillRepeatedly(Invoke(loop_rtp)); auto audio_sender = audio_channel_->GetAudioSender(); audio_sender->SendAudioData(GetAudioFrame(0)); audio_sender->SendAudioData(GetAudioFrame(1)); - - event.Wait(/*give_up_after_ms=*/1000); + event->Wait(/*give_up_after_ms=*/1000); AudioFrame audio_frame; audio_mixer_->Mix(/*number_of_channels=*/1, &audio_frame); + audio_mixer_->Mix(/*number_of_channels=*/1, &audio_frame); - // Check a few fields as we wouldn't have enough samples verify most of them - // here. - absl::optional neteq_stats = - audio_channel_->GetNetEqStatistics(); - EXPECT_TRUE(neteq_stats); - EXPECT_EQ(neteq_stats->total_samples_received, 80ULL); - EXPECT_EQ(neteq_stats->concealed_samples, 0ULL); - EXPECT_EQ(neteq_stats->jitter_buffer_delay_ms, 1600ULL); - EXPECT_EQ(neteq_stats->interruption_count, 0); + absl::optional ingress_stats = + audio_channel_->GetIngressStatistics(); + EXPECT_TRUE(ingress_stats); + EXPECT_EQ(ingress_stats->neteq_stats.total_samples_received, 160ULL); + EXPECT_EQ(ingress_stats->neteq_stats.concealed_samples, 0ULL); + EXPECT_EQ(ingress_stats->neteq_stats.concealment_events, 0ULL); + EXPECT_EQ(ingress_stats->neteq_stats.inserted_samples_for_deceleration, 0ULL); + EXPECT_EQ(ingress_stats->neteq_stats.removed_samples_for_acceleration, 0ULL); + EXPECT_EQ(ingress_stats->neteq_stats.silent_concealed_samples, 0ULL); + // To extract the jitter buffer length in millisecond, jitter_buffer_delay_ms + // needs to be divided by jitter_buffer_emitted_count (number of samples). + EXPECT_EQ(ingress_stats->neteq_stats.jitter_buffer_delay_ms, 1600ULL); + EXPECT_EQ(ingress_stats->neteq_stats.jitter_buffer_emitted_count, 160ULL); + EXPECT_GT(ingress_stats->neteq_stats.jitter_buffer_target_delay_ms, 0ULL); + EXPECT_EQ(ingress_stats->neteq_stats.interruption_count, 0); + EXPECT_EQ(ingress_stats->neteq_stats.total_interruption_duration_ms, 0); + EXPECT_DOUBLE_EQ(ingress_stats->total_duration, 0.02); + + // Now without any RTP pending in jitter buffer pull more. + audio_mixer_->Mix(/*number_of_channels=*/1, &audio_frame); + audio_mixer_->Mix(/*number_of_channels=*/1, &audio_frame); + + // Send another RTP packet to intentionally break PLC. + event = std::make_unique(); + audio_sender->SendAudioData(GetAudioFrame(2)); + audio_sender->SendAudioData(GetAudioFrame(3)); + event->Wait(/*give_up_after_ms=*/1000); + + ingress_stats = audio_channel_->GetIngressStatistics(); + EXPECT_TRUE(ingress_stats); + EXPECT_EQ(ingress_stats->neteq_stats.total_samples_received, 320ULL); + EXPECT_EQ(ingress_stats->neteq_stats.concealed_samples, 168ULL); + EXPECT_EQ(ingress_stats->neteq_stats.concealment_events, 1ULL); + EXPECT_EQ(ingress_stats->neteq_stats.inserted_samples_for_deceleration, 0ULL); + EXPECT_EQ(ingress_stats->neteq_stats.removed_samples_for_acceleration, 0ULL); + EXPECT_EQ(ingress_stats->neteq_stats.silent_concealed_samples, 0ULL); + EXPECT_EQ(ingress_stats->neteq_stats.jitter_buffer_delay_ms, 1600ULL); + EXPECT_EQ(ingress_stats->neteq_stats.jitter_buffer_emitted_count, 160ULL); + EXPECT_GT(ingress_stats->neteq_stats.jitter_buffer_target_delay_ms, 0ULL); + EXPECT_EQ(ingress_stats->neteq_stats.interruption_count, 0); + EXPECT_EQ(ingress_stats->neteq_stats.total_interruption_duration_ms, 0); + EXPECT_DOUBLE_EQ(ingress_stats->total_duration, 0.04); + + // Pull the last RTP packet. + audio_mixer_->Mix(/*number_of_channels=*/1, &audio_frame); + audio_mixer_->Mix(/*number_of_channels=*/1, &audio_frame); + + ingress_stats = audio_channel_->GetIngressStatistics(); + EXPECT_TRUE(ingress_stats); + EXPECT_EQ(ingress_stats->neteq_stats.total_samples_received, 480ULL); + EXPECT_EQ(ingress_stats->neteq_stats.concealed_samples, 168ULL); + EXPECT_EQ(ingress_stats->neteq_stats.concealment_events, 1ULL); + EXPECT_EQ(ingress_stats->neteq_stats.inserted_samples_for_deceleration, 0ULL); + EXPECT_EQ(ingress_stats->neteq_stats.removed_samples_for_acceleration, 0ULL); + EXPECT_EQ(ingress_stats->neteq_stats.silent_concealed_samples, 0ULL); + EXPECT_EQ(ingress_stats->neteq_stats.jitter_buffer_delay_ms, 3200ULL); + EXPECT_EQ(ingress_stats->neteq_stats.jitter_buffer_emitted_count, 320ULL); + EXPECT_GT(ingress_stats->neteq_stats.jitter_buffer_target_delay_ms, 0ULL); + EXPECT_EQ(ingress_stats->neteq_stats.interruption_count, 0); + EXPECT_EQ(ingress_stats->neteq_stats.total_interruption_duration_ms, 0); + EXPECT_DOUBLE_EQ(ingress_stats->total_duration, 0.06); } } // namespace diff --git a/audio/voip/voip_core.cc b/audio/voip/voip_core.cc index 2c066e1d4a..a93df73a22 100644 --- a/audio/voip/voip_core.cc +++ b/audio/voip/voip_core.cc @@ -382,10 +382,10 @@ bool VoipCore::SendDtmfEvent(ChannelId channel, return false; } -absl::optional VoipCore::GetNetEqStatistics( +absl::optional VoipCore::GetIngressStatistics( ChannelId channel) { if (auto audio_channel = GetChannel(channel)) { - return audio_channel->GetNetEqStatistics(); + return audio_channel->GetIngressStatistics(); } return absl::nullopt; } diff --git a/audio/voip/voip_core.h b/audio/voip/voip_core.h index 1993fbef8c..8c1a44c338 100644 --- a/audio/voip/voip_core.h +++ b/audio/voip/voip_core.h @@ -107,7 +107,7 @@ class VoipCore : public VoipEngine, int duration_ms) override; // Implements VoipStatistics interfaces. - absl::optional GetNetEqStatistics( + absl::optional GetIngressStatistics( ChannelId channel) override; private: