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:
parent
55b3ccd021
commit
cd4203bf72
@ -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:
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user