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}
This commit is contained in:
Tim Na 2020-11-02 22:24:52 -08:00 committed by Commit Bot
parent 55b3ccd021
commit cd4203bf72
7 changed files with 111 additions and 39 deletions

View File

@ -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:

View File

@ -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

View File

@ -82,7 +82,7 @@ class AudioChannel : public rtc::RefCountInterface {
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.

View File

@ -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.

View File

@ -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<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

View File

@ -382,10 +382,10 @@ bool VoipCore::SendDtmfEvent(ChannelId channel,
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;
}

View File

@ -107,7 +107,7 @@ class VoipCore : public VoipEngine,
int duration_ms) override;
// Implements VoipStatistics interfaces.
absl::optional<NetEqLifetimeStatistics> GetNetEqStatistics(
absl::optional<IngressStatistics> GetIngressStatistics(
ChannelId channel) override;
private: