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 <natim@webrtc.org>
Reviewed-by: Ivo Creusen <ivoc@webrtc.org>
Reviewed-by: Karl Wiberg <kwiberg@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#32538}
diff --git a/api/voip/voip_statistics.h b/api/voip/voip_statistics.h
index 5f4e174..cf01e95 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<NetEqLifetimeStatistics> GetNetEqStatistics(
+ virtual absl::optional<IngressStatistics> GetIngressStatistics(
ChannelId channel_id) = 0;
protected:
diff --git a/audio/voip/audio_channel.cc b/audio/voip/audio_channel.cc
index 28bd270..926130d 100644
--- a/audio/voip/audio_channel.cc
+++ b/audio/voip/audio_channel.cc
@@ -129,29 +129,34 @@
}
}
-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 9d0e707..a8946a7 100644
--- a/audio/voip/audio_channel.h
+++ b/audio/voip/audio_channel.h
@@ -82,7 +82,7 @@
void SetReceiveCodecs(const std::map<int, SdpAudioFormat>& 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 beff6cd..acb84c0 100644
--- a/audio/voip/audio_ingress.h
+++ b/audio/voip/audio_ingress.h
@@ -75,6 +75,11 @@
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 601545b..34b595c 100644
--- a/audio/voip/test/audio_channel_unittest.cc
+++ b/audio/voip/test/audio_channel_unittest.cc
@@ -140,34 +140,86 @@
}
// Check metrics after processing an RTP packet.
-TEST_F(AudioChannelTest, TestAudioStatistics) {
- rtc::Event event;
+TEST_F(AudioChannelTest, TestIngressStatistics) {
+ auto event = std::make_unique<rtc::Event>();
auto loop_rtp = [&](const uint8_t* packet, size_t length, Unused) {
audio_channel_->ReceivedRTPPacket(
rtc::ArrayView<const uint8_t>(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<NetEqLifetimeStatistics> 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<IngressStatistics> 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<rtc::Event>();
+ 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 2c066e1..a93df73 100644
--- a/audio/voip/voip_core.cc
+++ b/audio/voip/voip_core.cc
@@ -382,10 +382,10 @@
return false;
}
-absl::optional<NetEqLifetimeStatistics> VoipCore::GetNetEqStatistics(
+absl::optional<IngressStatistics> 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 1993fbe..8c1a44c 100644
--- a/audio/voip/voip_core.h
+++ b/audio/voip/voip_core.h
@@ -107,7 +107,7 @@
int duration_ms) override;
// Implements VoipStatistics interfaces.
- absl::optional<NetEqLifetimeStatistics> GetNetEqStatistics(
+ absl::optional<IngressStatistics> GetIngressStatistics(
ChannelId channel) override;
private: