Reland "Piping audio interruption metrics to API layer"
The metrics are now added as RTCNonStandardStatsMember objects in
RTCMediaStreamTrackStats. Unit tests are updated.
This is a reland of https://webrtc-review.googlesource.com/c/src/+/134303,
with fixes.
TBR=kwiberg@webrtc.org
Bug: webrtc:10549
Change-Id: I29dcc6fbfc69156715664e71acfa054c1b2d9038
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/134500
Commit-Queue: Henrik Lundin <henrik.lundin@webrtc.org>
Reviewed-by: Henrik Boström <hbos@webrtc.org>
Reviewed-by: Ivo Creusen <ivoc@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#27806}
diff --git a/api/stats/rtcstats_objects.h b/api/stats/rtcstats_objects.h
index 4ddfa93..96c7a03 100644
--- a/api/stats/rtcstats_objects.h
+++ b/api/stats/rtcstats_objects.h
@@ -321,6 +321,10 @@
RTCNonStandardStatsMember<uint64_t> jitter_buffer_flushes;
RTCNonStandardStatsMember<uint64_t> delayed_packet_outage_samples;
RTCNonStandardStatsMember<double> relative_packet_arrival_delay;
+ // TODO(henrik.lundin): Add description of the interruption metrics at
+ // https://github.com/henbos/webrtc-provisional-stats/issues/17
+ RTCNonStandardStatsMember<uint32_t> interruption_count;
+ RTCNonStandardStatsMember<double> total_interruption_duration;
// Non-standard video-only members.
// https://henbos.github.io/webrtc-provisional-stats/#RTCVideoReceiverStats-dict*
RTCNonStandardStatsMember<uint32_t> freeze_count;
diff --git a/audio/audio_receive_stream.cc b/audio/audio_receive_stream.cc
index b4948ee..677ee20 100644
--- a/audio/audio_receive_stream.cc
+++ b/audio/audio_receive_stream.cc
@@ -226,6 +226,8 @@
stats.relative_packet_arrival_delay_seconds =
static_cast<double>(ns.relativePacketArrivalDelayMs) /
static_cast<double>(rtc::kNumMillisecsPerSec);
+ stats.interruption_count = ns.interruptionCount;
+ stats.total_interruption_duration_ms = ns.totalInterruptionDurationMs;
auto ds = channel_receive_->GetDecodingCallStatistics();
stats.decoding_calls_to_silence_generator = ds.calls_to_silence_generator;
diff --git a/call/audio_receive_stream.h b/call/audio_receive_stream.h
index 257042b..9091afd 100644
--- a/call/audio_receive_stream.h
+++ b/call/audio_receive_stream.h
@@ -79,6 +79,8 @@
absl::optional<int64_t> last_packet_received_timestamp_ms;
uint64_t jitter_buffer_flushes = 0;
double relative_packet_arrival_delay_seconds = 0.0;
+ int32_t interruption_count = 0;
+ int32_t total_interruption_duration_ms = 0;
};
struct Config {
diff --git a/media/base/media_channel.h b/media/base/media_channel.h
index 710fd1a..69570e7 100644
--- a/media/base/media_channel.h
+++ b/media/base/media_channel.h
@@ -514,6 +514,10 @@
uint64_t delayed_packet_outage_samples = 0;
// Arrival delay of received audio packets.
double relative_packet_arrival_delay_seconds = 0.0;
+ // Count and total duration of audio interruptions (loss-concealement periods
+ // longer than 150 ms).
+ int32_t interruption_count = 0;
+ int32_t total_interruption_duration_ms = 0;
};
struct VideoSenderInfo : public MediaSenderInfo {
diff --git a/media/engine/webrtc_voice_engine.cc b/media/engine/webrtc_voice_engine.cc
index 56cef2c..3b85064 100644
--- a/media/engine/webrtc_voice_engine.cc
+++ b/media/engine/webrtc_voice_engine.cc
@@ -2264,6 +2264,8 @@
rinfo.jitter_buffer_flushes = stats.jitter_buffer_flushes;
rinfo.relative_packet_arrival_delay_seconds =
stats.relative_packet_arrival_delay_seconds;
+ rinfo.interruption_count = stats.interruption_count;
+ rinfo.total_interruption_duration_ms = stats.total_interruption_duration_ms;
info->receivers.push_back(rinfo);
}
diff --git a/modules/audio_coding/acm2/acm_receiver.cc b/modules/audio_coding/acm2/acm_receiver.cc
index da7d621..c10a71c 100644
--- a/modules/audio_coding/acm2/acm_receiver.cc
+++ b/modules/audio_coding/acm2/acm_receiver.cc
@@ -259,6 +259,9 @@
neteq_lifetime_stat.delayed_packet_outage_samples;
acm_stat->relativePacketArrivalDelayMs =
neteq_lifetime_stat.relative_packet_arrival_delay_ms;
+ acm_stat->interruptionCount = neteq_lifetime_stat.interruption_count;
+ acm_stat->totalInterruptionDurationMs =
+ neteq_lifetime_stat.total_interruption_duration_ms;
NetEqOperationsAndState neteq_operations_and_state =
neteq_->GetOperationsAndState();
diff --git a/modules/audio_coding/include/audio_coding_module_typedefs.h b/modules/audio_coding/include/audio_coding_module_typedefs.h
index 8063a29..621c478 100644
--- a/modules/audio_coding/include/audio_coding_module_typedefs.h
+++ b/modules/audio_coding/include/audio_coding_module_typedefs.h
@@ -130,6 +130,10 @@
uint64_t delayedPacketOutageSamples;
// arrival delay of incoming packets
uint64_t relativePacketArrivalDelayMs;
+ // number of audio interruptions
+ int32_t interruptionCount;
+ // total duration of audio interruptions
+ int32_t totalInterruptionDurationMs;
};
} // namespace webrtc
diff --git a/modules/audio_coding/neteq/include/neteq.h b/modules/audio_coding/neteq/include/neteq.h
index c0c836f..d91850f 100644
--- a/modules/audio_coding/neteq/include/neteq.h
+++ b/modules/audio_coding/neteq/include/neteq.h
@@ -90,8 +90,8 @@
// An interruption is a loss-concealment event lasting at least 150 ms. The
// two stats below count the number os such events and the total duration of
// these events.
- uint64_t interruption_count = 0;
- uint64_t total_interruption_duration_ms = 0;
+ int32_t interruption_count = 0;
+ int32_t total_interruption_duration_ms = 0;
};
// Metrics that describe the operations performed in NetEq, and the internal
diff --git a/modules/audio_coding/neteq/neteq_impl_unittest.cc b/modules/audio_coding/neteq/neteq_impl_unittest.cc
index 025ed69..8269526 100644
--- a/modules/audio_coding/neteq/neteq_impl_unittest.cc
+++ b/modules/audio_coding/neteq/neteq_impl_unittest.cc
@@ -745,7 +745,7 @@
}
auto lifetime_stats = neteq_->GetLifetimeStatistics();
- EXPECT_EQ(0u, lifetime_stats.interruption_count);
+ EXPECT_EQ(0, lifetime_stats.interruption_count);
}
// This test verifies that NetEq can handle comfort noise and enters/quits codec
diff --git a/modules/audio_coding/neteq/statistics_calculator_unittest.cc b/modules/audio_coding/neteq/statistics_calculator_unittest.cc
index a851074..abfa3c5 100644
--- a/modules/audio_coding/neteq/statistics_calculator_unittest.cc
+++ b/modules/audio_coding/neteq/statistics_calculator_unittest.cc
@@ -135,31 +135,31 @@
stats.DecodedOutputPlayed();
stats.EndExpandEvent(fs_hz);
auto lts = stats.GetLifetimeStatistics();
- EXPECT_EQ(0u, lts.interruption_count);
- EXPECT_EQ(0u, lts.total_interruption_duration_ms);
+ EXPECT_EQ(0, lts.interruption_count);
+ EXPECT_EQ(0, lts.total_interruption_duration_ms);
// Add an event that is shorter than 150 ms. Should not be logged.
stats.ExpandedVoiceSamples(10 * fs_khz, false); // 10 ms.
stats.ExpandedNoiseSamples(139 * fs_khz, false); // 139 ms.
stats.EndExpandEvent(fs_hz);
lts = stats.GetLifetimeStatistics();
- EXPECT_EQ(0u, lts.interruption_count);
+ EXPECT_EQ(0, lts.interruption_count);
// Add an event that is longer than 150 ms. Should be logged.
stats.ExpandedVoiceSamples(140 * fs_khz, false); // 140 ms.
stats.ExpandedNoiseSamples(11 * fs_khz, false); // 11 ms.
stats.EndExpandEvent(fs_hz);
lts = stats.GetLifetimeStatistics();
- EXPECT_EQ(1u, lts.interruption_count);
- EXPECT_EQ(151u, lts.total_interruption_duration_ms);
+ EXPECT_EQ(1, lts.interruption_count);
+ EXPECT_EQ(151, lts.total_interruption_duration_ms);
// Add one more long event.
stats.ExpandedVoiceSamples(100 * fs_khz, false); // 100 ms.
stats.ExpandedNoiseSamples(5000 * fs_khz, false); // 5000 ms.
stats.EndExpandEvent(fs_hz);
lts = stats.GetLifetimeStatistics();
- EXPECT_EQ(2u, lts.interruption_count);
- EXPECT_EQ(5100u + 151u, lts.total_interruption_duration_ms);
+ EXPECT_EQ(2, lts.interruption_count);
+ EXPECT_EQ(5100 + 151, lts.total_interruption_duration_ms);
}
TEST(StatisticsCalculator, InterruptionCounterDoNotLogBeforeDecoding) {
@@ -172,7 +172,7 @@
stats.ExpandedVoiceSamples(151 * fs_khz, false); // 151 ms.
stats.EndExpandEvent(fs_hz);
auto lts = stats.GetLifetimeStatistics();
- EXPECT_EQ(0u, lts.interruption_count);
+ EXPECT_EQ(0, lts.interruption_count);
// Call DecodedOutputPlayed(). Logging should happen after this.
stats.DecodedOutputPlayed();
@@ -181,7 +181,7 @@
stats.ExpandedVoiceSamples(151 * fs_khz, false); // 151 ms.
stats.EndExpandEvent(fs_hz);
lts = stats.GetLifetimeStatistics();
- EXPECT_EQ(1u, lts.interruption_count);
+ EXPECT_EQ(1, lts.interruption_count);
}
} // namespace webrtc
diff --git a/modules/audio_coding/neteq/tools/neteq_stats_plotter.cc b/modules/audio_coding/neteq/tools/neteq_stats_plotter.cc
index c7bec9c..7c3aed8 100644
--- a/modules/audio_coding/neteq/tools/neteq_stats_plotter.cc
+++ b/modules/audio_coding/neteq/tools/neteq_stats_plotter.cc
@@ -79,9 +79,8 @@
const auto lifetime_stats_vector = stats_getter_->lifetime_stats();
if (!lifetime_stats_vector->empty()) {
auto lifetime_stats = lifetime_stats_vector->back().second;
- printf(" num_interruptions: %" PRId64 "\n",
- lifetime_stats.interruption_count);
- printf(" sum_interruption_length_ms: %" PRId64 " ms\n",
+ printf(" num_interruptions: %d\n", lifetime_stats.interruption_count);
+ printf(" sum_interruption_length_ms: %d ms\n",
lifetime_stats.total_interruption_duration_ms);
printf(" interruption ratio: %f%%\n",
100.0 * lifetime_stats.total_interruption_duration_ms /
diff --git a/pc/rtc_stats_collector.cc b/pc/rtc_stats_collector.cc
index 611dfe3..8849d86 100644
--- a/pc/rtc_stats_collector.cc
+++ b/pc/rtc_stats_collector.cc
@@ -490,6 +490,13 @@
voice_receiver_info.delayed_packet_outage_samples;
audio_track_stats->relative_packet_arrival_delay =
voice_receiver_info.relative_packet_arrival_delay_seconds;
+ audio_track_stats->interruption_count =
+ voice_receiver_info.interruption_count >= 0
+ ? voice_receiver_info.interruption_count
+ : 0;
+ audio_track_stats->total_interruption_duration =
+ static_cast<double>(voice_receiver_info.total_interruption_duration_ms) /
+ rtc::kNumMillisecsPerSec;
return audio_track_stats;
}
diff --git a/pc/rtc_stats_collector_unittest.cc b/pc/rtc_stats_collector_unittest.cc
index ba80732..0539379 100644
--- a/pc/rtc_stats_collector_unittest.cc
+++ b/pc/rtc_stats_collector_unittest.cc
@@ -1429,6 +1429,8 @@
voice_receiver_info.jitter_buffer_flushes = 7;
voice_receiver_info.delayed_packet_outage_samples = 15;
voice_receiver_info.relative_packet_arrival_delay_seconds = 16;
+ voice_receiver_info.interruption_count = 7788;
+ voice_receiver_info.total_interruption_duration_ms = 778899;
stats_->CreateMockRtpSendersReceiversAndChannels(
{}, {std::make_pair(remote_audio_track.get(), voice_receiver_info)}, {},
@@ -1466,6 +1468,8 @@
expected_remote_audio_track.jitter_buffer_flushes = 7;
expected_remote_audio_track.delayed_packet_outage_samples = 15;
expected_remote_audio_track.relative_packet_arrival_delay = 16;
+ expected_remote_audio_track.interruption_count = 7788;
+ expected_remote_audio_track.total_interruption_duration = 778.899;
ASSERT_TRUE(report->Get(expected_remote_audio_track.id()));
EXPECT_EQ(expected_remote_audio_track,
report->Get(expected_remote_audio_track.id())
diff --git a/pc/rtc_stats_integrationtest.cc b/pc/rtc_stats_integrationtest.cc
index bb13c20..d576c60 100644
--- a/pc/rtc_stats_integrationtest.cc
+++ b/pc/rtc_stats_integrationtest.cc
@@ -654,6 +654,10 @@
media_stream_track.delayed_packet_outage_samples);
verifier.TestMemberIsNonNegative<double>(
media_stream_track.relative_packet_arrival_delay);
+ verifier.TestMemberIsNonNegative<uint32_t>(
+ media_stream_track.interruption_count);
+ verifier.TestMemberIsNonNegative<double>(
+ media_stream_track.total_interruption_duration);
} else {
verifier.TestMemberIsUndefined(media_stream_track.jitter_buffer_delay);
verifier.TestMemberIsUndefined(
@@ -666,6 +670,9 @@
media_stream_track.delayed_packet_outage_samples);
verifier.TestMemberIsUndefined(
media_stream_track.relative_packet_arrival_delay);
+ verifier.TestMemberIsUndefined(media_stream_track.interruption_count);
+ verifier.TestMemberIsUndefined(
+ media_stream_track.total_interruption_duration);
}
return verifier.ExpectAllMembersSuccessfullyTested();
}
diff --git a/stats/rtcstats_objects.cc b/stats/rtcstats_objects.cc
index 89d5a85..6c914e7 100644
--- a/stats/rtcstats_objects.cc
+++ b/stats/rtcstats_objects.cc
@@ -389,6 +389,8 @@
&jitter_buffer_flushes,
&delayed_packet_outage_samples,
&relative_packet_arrival_delay,
+ &interruption_count,
+ &total_interruption_duration,
&freeze_count,
&pause_count,
&total_freezes_duration,
@@ -442,6 +444,8 @@
relative_packet_arrival_delay(
"relativePacketArrivalDelay",
{NonStandardGroupId::kRtcStatsRelativePacketArrivalDelay}),
+ interruption_count("interruptionCount"),
+ total_interruption_duration("totalInterruptionDuration"),
freeze_count("freezeCount"),
pause_count("pauseCount"),
total_freezes_duration("totalFreezesDuration"),
@@ -484,6 +488,8 @@
jitter_buffer_flushes(other.jitter_buffer_flushes),
delayed_packet_outage_samples(other.delayed_packet_outage_samples),
relative_packet_arrival_delay(other.relative_packet_arrival_delay),
+ interruption_count(other.interruption_count),
+ total_interruption_duration(other.total_interruption_duration),
freeze_count(other.freeze_count),
pause_count(other.pause_count),
total_freezes_duration(other.total_freezes_duration),