Computing and propagating the audio stats totalprocessingdelay.

Bug: webrtc:344347965
Change-Id: Id7dd74ef085338d14582dcc0db98508d365301e6
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/352680
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Reviewed-by: Jakob Ivarsson‎ <jakobi@webrtc.org>
Commit-Queue: Jesus de Vicente Pena <devicentepena@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#42507}
This commit is contained in:
Jesús de Vicente Peña 2024-06-04 10:05:31 +02:00 committed by WebRTC LUCI CQ
parent 418bcf2acb
commit fc6df056b6
17 changed files with 80 additions and 13 deletions

View File

@ -94,6 +94,7 @@ struct NetEqLifetimeStatistics {
int32_t total_interruption_duration_ms = 0;
// Total number of comfort noise samples generated during DTX.
uint64_t generated_noise_samples = 0;
uint64_t total_processing_delay_us = 0;
};
// Metrics that describe the operations performed in NetEq, and the internal

View File

@ -321,6 +321,9 @@ webrtc::AudioReceiveStreamInterface::Stats AudioReceiveStreamImpl::GetStats(
static_cast<double>(rtc::kNumMillisecsPerSec);
stats.inserted_samples_for_deceleration = ns.insertedSamplesForDeceleration;
stats.removed_samples_for_acceleration = ns.removedSamplesForAcceleration;
stats.total_processing_delay_seconds =
static_cast<double>(ns.totalProcessingDelayUs) /
static_cast<double>(rtc::kNumMicrosecsPerSec);
stats.expand_rate = Q14ToFloat(ns.currentExpandRate);
stats.speech_expand_rate = Q14ToFloat(ns.currentSpeechExpandRate);
stats.secondary_decoded_rate = Q14ToFloat(ns.currentSecondaryDecodedRate);

View File

@ -82,6 +82,7 @@ const NetworkStatistics kNetworkStats = {
/*removedSamplesForAcceleration=*/321,
/*fecPacketsReceived=*/123,
/*fecPacketsDiscarded=*/101,
/*totalProcessingDelayMs=*/154,
/*packetsDiscarded=*/989,
/*currentExpandRate=*/789,
/*currentSpeechExpandRate=*/12,
@ -287,6 +288,9 @@ TEST(AudioReceiveStreamTest, GetStats) {
stats.removed_samples_for_acceleration);
EXPECT_EQ(kNetworkStats.fecPacketsReceived, stats.fec_packets_received);
EXPECT_EQ(kNetworkStats.fecPacketsDiscarded, stats.fec_packets_discarded);
EXPECT_EQ(static_cast<double>(kNetworkStats.totalProcessingDelayUs) /
static_cast<double>(rtc::kNumMicrosecsPerSec),
stats.total_processing_delay_seconds);
EXPECT_EQ(kNetworkStats.packetsDiscarded, stats.packets_discarded);
EXPECT_EQ(Q14ToFloat(kNetworkStats.currentExpandRate), stats.expand_rate);
EXPECT_EQ(Q14ToFloat(kNetworkStats.currentSpeechExpandRate),

View File

@ -62,6 +62,7 @@ class AudioReceiveStreamInterface : public MediaReceiveStreamInterface {
double jitter_buffer_minimum_delay_seconds = 0.0;
uint64_t inserted_samples_for_deceleration = 0;
uint64_t removed_samples_for_acceleration = 0;
double total_processing_delay_seconds = 0.0;
// Stats below DO NOT correspond directly to anything in the WebRTC stats
float expand_rate = 0.0f;
float speech_expand_rate = 0.0f;

View File

@ -476,6 +476,8 @@ struct MediaReceiverInfo {
absl::optional<uint64_t> fec_packets_discarded;
// https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-fecbytesreceived
absl::optional<uint64_t> fec_bytes_received;
// https://www.w3.org/TR/webrtc-stats/#dom-rtcinboundrtpstreamstats-totalprocessingdelay
double total_processing_delay_seconds = 0.0;
// Remote outbound stats derived by the received RTCP sender reports.
// https://w3c.github.io/webrtc-stats/#remoteoutboundrtpstats-dict*

View File

@ -2652,7 +2652,7 @@ bool WebRtcVoiceReceiveChannel::GetStats(VoiceMediaReceiveInfo* info,
rinfo.round_trip_time = stats.round_trip_time;
rinfo.round_trip_time_measurements = stats.round_trip_time_measurements;
rinfo.total_round_trip_time = stats.total_round_trip_time;
rinfo.total_processing_delay_seconds = stats.total_processing_delay_seconds;
if (recv_nack_enabled_) {
rinfo.nacks_sent = stats.nacks_sent;
}

View File

@ -717,6 +717,7 @@ class WebRtcVoiceEngineTestFake : public ::testing::TestWithParam<bool> {
stats.concealment_events = 12;
stats.jitter_buffer_delay_seconds = 34;
stats.jitter_buffer_emitted_count = 77;
stats.total_processing_delay_seconds = 0.123;
stats.expand_rate = 5.67f;
stats.speech_expand_rate = 8.90f;
stats.secondary_decoded_rate = 1.23f;
@ -765,6 +766,8 @@ class WebRtcVoiceEngineTestFake : public ::testing::TestWithParam<bool> {
stats.jitter_buffer_delay_seconds);
EXPECT_EQ(info.jitter_buffer_emitted_count,
stats.jitter_buffer_emitted_count);
EXPECT_EQ(info.total_processing_delay_seconds,
stats.total_processing_delay_seconds);
EXPECT_EQ(info.expand_rate, stats.expand_rate);
EXPECT_EQ(info.speech_expand_rate, stats.speech_expand_rate);
EXPECT_EQ(info.secondary_decoded_rate, stats.secondary_decoded_rate);

View File

@ -303,6 +303,8 @@ void AcmReceiver::GetNetworkStatistics(
neteq_lifetime_stat.removed_samples_for_acceleration;
acm_stat->fecPacketsReceived = neteq_lifetime_stat.fec_packets_received;
acm_stat->fecPacketsDiscarded = neteq_lifetime_stat.fec_packets_discarded;
acm_stat->totalProcessingDelayUs =
neteq_lifetime_stat.total_processing_delay_us;
acm_stat->packetsDiscarded = neteq_lifetime_stat.packets_discarded;
NetEqOperationsAndState neteq_operations_and_state =

View File

@ -94,6 +94,7 @@ struct NetworkStatistics {
uint64_t removedSamplesForAcceleration;
uint64_t fecPacketsReceived;
uint64_t fecPacketsDiscarded;
uint64_t totalProcessingDelayUs;
// Stats below correspond to similarly-named fields in the WebRTC stats spec.
// https://w3c.github.io/webrtc-stats/#dom-rtcreceivedrtpstreamstats
uint64_t packetsDiscarded;

View File

@ -1976,9 +1976,17 @@ int NetEqImpl::ExtractPackets(size_t required_samples,
extracted_samples = packet->timestamp - first_timestamp + packet_duration;
RTC_DCHECK(controller_);
stats_->JitterBufferDelay(packet_duration, waiting_time_ms,
controller_->TargetLevelMs(),
controller_->UnlimitedTargetLevelMs());
TimeDelta processing_time = TimeDelta::Zero();
if (packet->packet_info.has_value() &&
!packet->packet_info->receive_time().IsMinusInfinity()) {
processing_time =
clock_->CurrentTime() - packet->packet_info->receive_time();
}
stats_->JitterBufferDelay(
packet_duration, waiting_time_ms, controller_->TargetLevelMs(),
controller_->UnlimitedTargetLevelMs(), processing_time.us());
// Check what packet is available next.
next_packet = packet_buffer_->PeekNextPacket();

View File

@ -909,6 +909,7 @@ void NetEqDecodingTestFaxMode::TestJitterBufferDelay(bool apply_packet_loss) {
// Get packet.
if (packets_sent > kDelayInNumPackets) {
clock_.AdvanceTime(TimeDelta::Millis(kPacketLenMs));
neteq_->GetAudio(&out_frame_, &muted);
packets_received++;
@ -928,6 +929,7 @@ void NetEqDecodingTestFaxMode::TestJitterBufferDelay(bool apply_packet_loss) {
}
if (apply_packet_loss) {
clock_.AdvanceTime(TimeDelta::Millis(kPacketLenMs));
// Extra call to GetAudio to cause concealment.
neteq_->GetAudio(&out_frame_, &muted);
}
@ -939,6 +941,11 @@ void NetEqDecodingTestFaxMode::TestJitterBufferDelay(bool apply_packet_loss) {
EXPECT_EQ(expected_emitted_count, stats.jitter_buffer_emitted_count);
EXPECT_EQ(expected_target_delay,
rtc::checked_cast<int>(stats.jitter_buffer_target_delay_ms));
// In this test, since the packets are inserted with a receive time equal to
// the current clock time, the jitter buffer delay should match the total
// processing delay.
EXPECT_EQ(stats.jitter_buffer_delay_ms * 1000,
stats.total_processing_delay_us);
}
TEST_F(NetEqDecodingTestFaxMode, TestJitterBufferDelayWithoutLoss) {

View File

@ -262,17 +262,19 @@ void StatisticsCalculator::IncreaseCounter(size_t num_samples, int fs_hz) {
lifetime_stats_.total_samples_received += num_samples;
}
void StatisticsCalculator::JitterBufferDelay(
size_t num_samples,
uint64_t waiting_time_ms,
uint64_t target_delay_ms,
uint64_t unlimited_target_delay_ms) {
void StatisticsCalculator::JitterBufferDelay(size_t num_samples,
uint64_t waiting_time_ms,
uint64_t target_delay_ms,
uint64_t unlimited_target_delay_ms,
uint64_t processing_delay_us) {
lifetime_stats_.jitter_buffer_delay_ms += waiting_time_ms * num_samples;
lifetime_stats_.jitter_buffer_target_delay_ms +=
target_delay_ms * num_samples;
lifetime_stats_.jitter_buffer_minimum_delay_ms +=
unlimited_target_delay_ms * num_samples;
lifetime_stats_.jitter_buffer_emitted_count += num_samples;
lifetime_stats_.total_processing_delay_us +=
num_samples * processing_delay_us;
}
void StatisticsCalculator::SecondaryDecodedSamples(int num_samples) {

View File

@ -86,7 +86,8 @@ class StatisticsCalculator {
void JitterBufferDelay(size_t num_samples,
uint64_t waiting_time_ms,
uint64_t target_delay_ms,
uint64_t unlimited_target_delay_ms);
uint64_t unlimited_target_delay_ms,
uint64_t processing_delay_us);
// Stores new packet waiting time in waiting time statistics.
void StoreWaitingTime(int waiting_time_ms);

View File

@ -203,4 +203,33 @@ TEST(StatisticsCalculator, DiscardedPackets) {
statistics_calculator.GetLifetimeStatistics().packets_discarded);
}
TEST(StatisticsCalculator, JitterBufferDelay) {
StatisticsCalculator stats;
NetEqLifetimeStatistics lts;
lts = stats.GetLifetimeStatistics();
EXPECT_EQ(lts.total_processing_delay_us, 0ul);
stats.JitterBufferDelay(/*num_samples=*/480,
/*waiting_time_ms=*/90ul,
/*target_delay_ms=*/80ul,
/*unlimited_target_delay_ms=*/70,
/*processing_delay_us=*/100 * 1000ul);
lts = stats.GetLifetimeStatistics();
EXPECT_EQ(lts.jitter_buffer_delay_ms / 480, 90ul);
EXPECT_EQ(lts.jitter_buffer_target_delay_ms / 480, 80ul);
EXPECT_EQ(lts.jitter_buffer_minimum_delay_ms / 480, 70ul);
EXPECT_EQ(lts.total_processing_delay_us / 480, 100 * 1000ul);
EXPECT_EQ(lts.jitter_buffer_emitted_count, 480ul);
stats.JitterBufferDelay(/*num_samples=*/480,
/*waiting_time_ms=*/90ul,
/*target_delay_ms=*/80ul,
/*unlimited_target_delay_ms=*/70,
/*processing_delay_us=*/100 * 1000ul);
lts = stats.GetLifetimeStatistics();
EXPECT_EQ(lts.jitter_buffer_delay_ms / 960, 90ul);
EXPECT_EQ(lts.jitter_buffer_target_delay_ms / 960, 80ul);
EXPECT_EQ(lts.jitter_buffer_minimum_delay_ms / 960, 70ul);
EXPECT_EQ(lts.total_processing_delay_us / 960, 100 * 1000ul);
EXPECT_EQ(lts.jitter_buffer_emitted_count, 960ul);
}
} // namespace webrtc

View File

@ -423,6 +423,8 @@ void SetInboundRTPStreamStatsFromMediaReceiverInfo(
if (media_receiver_info.fec_bytes_received.has_value()) {
inbound_stats->fec_bytes_received = *media_receiver_info.fec_bytes_received;
}
inbound_stats->total_processing_delay =
media_receiver_info.total_processing_delay_seconds;
}
std::unique_ptr<RTCInboundRtpStreamStats> CreateInboundAudioStreamStats(

View File

@ -2201,6 +2201,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRtpStreamStats_Audio) {
voice_media_info.receivers[0].interruption_count = 7788;
voice_media_info.receivers[0].total_interruption_duration_ms = 778899;
voice_media_info.receivers[0].last_packet_received = absl::nullopt;
voice_media_info.receivers[0].total_processing_delay_seconds = 0.123;
RtpCodecParameters codec_parameters;
codec_parameters.payload_type = 42;
@ -2258,6 +2259,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRtpStreamStats_Audio) {
expected_audio.relative_packet_arrival_delay = 16;
expected_audio.interruption_count = 7788;
expected_audio.total_interruption_duration = 778.899;
expected_audio.total_processing_delay = 0.123;
expected_audio.playout_id = "AP";
ASSERT_TRUE(report->Get(expected_audio.id()));

View File

@ -608,6 +608,8 @@ class RTCStatsReportVerifier {
inbound_stream.jitter_buffer_target_delay);
verifier.TestAttributeIsNonNegative<double>(
inbound_stream.jitter_buffer_minimum_delay);
verifier.TestAttributeIsNonNegative<double>(
inbound_stream.total_processing_delay);
if (inbound_stream.kind.has_value() && *inbound_stream.kind == "video") {
verifier.TestAttributeIsUndefined(inbound_stream.total_samples_received);
verifier.TestAttributeIsUndefined(inbound_stream.concealed_samples);
@ -681,8 +683,6 @@ class RTCStatsReportVerifier {
inbound_stream.frames_dropped);
verifier.TestAttributeIsNonNegative<double>(
inbound_stream.total_decode_time);
verifier.TestAttributeIsNonNegative<double>(
inbound_stream.total_processing_delay);
verifier.TestAttributeIsNonNegative<double>(
inbound_stream.total_assembly_time);
verifier.TestAttributeIsDefined(
@ -717,7 +717,6 @@ class RTCStatsReportVerifier {
verifier.TestAttributeIsUndefined(inbound_stream.key_frames_decoded);
verifier.TestAttributeIsUndefined(inbound_stream.frames_dropped);
verifier.TestAttributeIsUndefined(inbound_stream.total_decode_time);
verifier.TestAttributeIsUndefined(inbound_stream.total_processing_delay);
verifier.TestAttributeIsUndefined(inbound_stream.total_assembly_time);
verifier.TestAttributeIsUndefined(
inbound_stream.frames_assembled_from_multiple_packets);