Piping audio interruption metrics to API layer

Bug: webrtc:10549
Change-Id: Ie6abe5819c5b73dc5f5f89bdc375bad77f44ce97
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/134303
Reviewed-by: Karl Wiberg <kwiberg@webrtc.org>
Reviewed-by: Ivo Creusen <ivoc@webrtc.org>
Commit-Queue: Henrik Lundin <henrik.lundin@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#27788}
This commit is contained in:
Henrik Lundin
2019-04-26 14:41:13 +02:00
committed by Commit Bot
parent c35b6e675a
commit 299c4e6846
14 changed files with 48 additions and 15 deletions

View File

@ -565,6 +565,8 @@ const char* StatsReport::Value::display_name() const {
return "googInitiator"; return "googInitiator";
case kStatsValueNameInterframeDelayMaxMs: case kStatsValueNameInterframeDelayMaxMs:
return "googInterframeDelayMax"; return "googInterframeDelayMax";
case kStatsValueNameInterruptionCount:
return "googInterruptionCount";
case kStatsValueNameIssuerId: case kStatsValueNameIssuerId:
return "googIssuerId"; return "googIssuerId";
case kStatsValueNameJitterReceived: case kStatsValueNameJitterReceived:
@ -647,6 +649,8 @@ const char* StatsReport::Value::display_name() const {
return "googTrackId"; return "googTrackId";
case kStatsValueNameTimingFrameInfo: case kStatsValueNameTimingFrameInfo:
return "googTimingFrameInfo"; return "googTimingFrameInfo";
case kStatsValueNameTotalInterruptionDurationMs:
return "googTotalInterruptionDurationMs";
case kStatsValueNameTypingNoiseState: case kStatsValueNameTypingNoiseState:
return "googTypingNoiseState"; return "googTypingNoiseState";
case kStatsValueNameWritable: case kStatsValueNameWritable:

View File

@ -192,6 +192,7 @@ class StatsReport {
kStatsValueNameHugeFramesSent, kStatsValueNameHugeFramesSent,
kStatsValueNameInitiator, kStatsValueNameInitiator,
kStatsValueNameInterframeDelayMaxMs, // Max over last 10 seconds. kStatsValueNameInterframeDelayMaxMs, // Max over last 10 seconds.
kStatsValueNameInterruptionCount,
kStatsValueNameIssuerId, kStatsValueNameIssuerId,
kStatsValueNameJitterBufferMs, kStatsValueNameJitterBufferMs,
kStatsValueNameJitterReceived, kStatsValueNameJitterReceived,
@ -232,6 +233,7 @@ class StatsReport {
kStatsValueNameTargetDelayMs, kStatsValueNameTargetDelayMs,
kStatsValueNameTargetEncBitrate, kStatsValueNameTargetEncBitrate,
kStatsValueNameTimingFrameInfo, // Result of |TimingFrameInfo::ToString| kStatsValueNameTimingFrameInfo, // Result of |TimingFrameInfo::ToString|
kStatsValueNameTotalInterruptionDurationMs,
kStatsValueNameTrackId, kStatsValueNameTrackId,
kStatsValueNameTransmitBitrate, kStatsValueNameTransmitBitrate,
kStatsValueNameTransportType, kStatsValueNameTransportType,

View File

@ -226,6 +226,8 @@ webrtc::AudioReceiveStream::Stats AudioReceiveStream::GetStats() const {
stats.relative_packet_arrival_delay_seconds = stats.relative_packet_arrival_delay_seconds =
static_cast<double>(ns.relativePacketArrivalDelayMs) / static_cast<double>(ns.relativePacketArrivalDelayMs) /
static_cast<double>(rtc::kNumMillisecsPerSec); static_cast<double>(rtc::kNumMillisecsPerSec);
stats.interruption_count = ns.interruptionCount;
stats.total_interruption_duration_ms = ns.totalInterruptionDurationMs;
auto ds = channel_receive_->GetDecodingCallStatistics(); auto ds = channel_receive_->GetDecodingCallStatistics();
stats.decoding_calls_to_silence_generator = ds.calls_to_silence_generator; stats.decoding_calls_to_silence_generator = ds.calls_to_silence_generator;

View File

@ -79,6 +79,8 @@ class AudioReceiveStream {
absl::optional<int64_t> last_packet_received_timestamp_ms; absl::optional<int64_t> last_packet_received_timestamp_ms;
uint64_t jitter_buffer_flushes = 0; uint64_t jitter_buffer_flushes = 0;
double relative_packet_arrival_delay_seconds = 0.0; double relative_packet_arrival_delay_seconds = 0.0;
int32_t interruption_count = 0;
int32_t total_interruption_duration_ms = 0;
}; };
struct Config { struct Config {

View File

@ -514,6 +514,10 @@ struct VoiceReceiverInfo : public MediaReceiverInfo {
uint64_t delayed_packet_outage_samples = 0; uint64_t delayed_packet_outage_samples = 0;
// Arrival delay of received audio packets. // Arrival delay of received audio packets.
double relative_packet_arrival_delay_seconds = 0.0; 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 { struct VideoSenderInfo : public MediaSenderInfo {

View File

@ -2274,6 +2274,8 @@ bool WebRtcVoiceMediaChannel::GetStats(VoiceMediaInfo* info) {
rinfo.jitter_buffer_flushes = stats.jitter_buffer_flushes; rinfo.jitter_buffer_flushes = stats.jitter_buffer_flushes;
rinfo.relative_packet_arrival_delay_seconds = rinfo.relative_packet_arrival_delay_seconds =
stats.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); info->receivers.push_back(rinfo);
} }

View File

@ -259,6 +259,9 @@ void AcmReceiver::GetNetworkStatistics(NetworkStatistics* acm_stat) {
neteq_lifetime_stat.delayed_packet_outage_samples; neteq_lifetime_stat.delayed_packet_outage_samples;
acm_stat->relativePacketArrivalDelayMs = acm_stat->relativePacketArrivalDelayMs =
neteq_lifetime_stat.relative_packet_arrival_delay_ms; 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 = NetEqOperationsAndState neteq_operations_and_state =
neteq_->GetOperationsAndState(); neteq_->GetOperationsAndState();

View File

@ -130,6 +130,10 @@ struct NetworkStatistics {
uint64_t delayedPacketOutageSamples; uint64_t delayedPacketOutageSamples;
// arrival delay of incoming packets // arrival delay of incoming packets
uint64_t relativePacketArrivalDelayMs; uint64_t relativePacketArrivalDelayMs;
// number of audio interruptions
int32_t interruptionCount;
// total duration of audio interruptions
int32_t totalInterruptionDurationMs;
}; };
} // namespace webrtc } // namespace webrtc

View File

@ -90,8 +90,8 @@ struct NetEqLifetimeStatistics {
// An interruption is a loss-concealment event lasting at least 150 ms. The // 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 // two stats below count the number os such events and the total duration of
// these events. // these events.
uint64_t interruption_count = 0; int32_t interruption_count = 0;
uint64_t total_interruption_duration_ms = 0; int32_t total_interruption_duration_ms = 0;
}; };
// Metrics that describe the operations performed in NetEq, and the internal // Metrics that describe the operations performed in NetEq, and the internal

View File

@ -745,7 +745,7 @@ TEST_F(NetEqImplTest, NoAudioInterruptionLoggedBeforeFirstDecode) {
} }
auto lifetime_stats = neteq_->GetLifetimeStatistics(); 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 // This test verifies that NetEq can handle comfort noise and enters/quits codec

View File

@ -135,31 +135,31 @@ TEST(StatisticsCalculator, InterruptionCounter) {
stats.DecodedOutputPlayed(); stats.DecodedOutputPlayed();
stats.EndExpandEvent(fs_hz); stats.EndExpandEvent(fs_hz);
auto lts = stats.GetLifetimeStatistics(); auto lts = stats.GetLifetimeStatistics();
EXPECT_EQ(0u, lts.interruption_count); EXPECT_EQ(0, lts.interruption_count);
EXPECT_EQ(0u, lts.total_interruption_duration_ms); EXPECT_EQ(0, lts.total_interruption_duration_ms);
// Add an event that is shorter than 150 ms. Should not be logged. // Add an event that is shorter than 150 ms. Should not be logged.
stats.ExpandedVoiceSamples(10 * fs_khz, false); // 10 ms. stats.ExpandedVoiceSamples(10 * fs_khz, false); // 10 ms.
stats.ExpandedNoiseSamples(139 * fs_khz, false); // 139 ms. stats.ExpandedNoiseSamples(139 * fs_khz, false); // 139 ms.
stats.EndExpandEvent(fs_hz); stats.EndExpandEvent(fs_hz);
lts = stats.GetLifetimeStatistics(); 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. // Add an event that is longer than 150 ms. Should be logged.
stats.ExpandedVoiceSamples(140 * fs_khz, false); // 140 ms. stats.ExpandedVoiceSamples(140 * fs_khz, false); // 140 ms.
stats.ExpandedNoiseSamples(11 * fs_khz, false); // 11 ms. stats.ExpandedNoiseSamples(11 * fs_khz, false); // 11 ms.
stats.EndExpandEvent(fs_hz); stats.EndExpandEvent(fs_hz);
lts = stats.GetLifetimeStatistics(); lts = stats.GetLifetimeStatistics();
EXPECT_EQ(1u, lts.interruption_count); EXPECT_EQ(1, lts.interruption_count);
EXPECT_EQ(151u, lts.total_interruption_duration_ms); EXPECT_EQ(151, lts.total_interruption_duration_ms);
// Add one more long event. // Add one more long event.
stats.ExpandedVoiceSamples(100 * fs_khz, false); // 100 ms. stats.ExpandedVoiceSamples(100 * fs_khz, false); // 100 ms.
stats.ExpandedNoiseSamples(5000 * fs_khz, false); // 5000 ms. stats.ExpandedNoiseSamples(5000 * fs_khz, false); // 5000 ms.
stats.EndExpandEvent(fs_hz); stats.EndExpandEvent(fs_hz);
lts = stats.GetLifetimeStatistics(); lts = stats.GetLifetimeStatistics();
EXPECT_EQ(2u, lts.interruption_count); EXPECT_EQ(2, lts.interruption_count);
EXPECT_EQ(5100u + 151u, lts.total_interruption_duration_ms); EXPECT_EQ(5100 + 151, lts.total_interruption_duration_ms);
} }
TEST(StatisticsCalculator, InterruptionCounterDoNotLogBeforeDecoding) { TEST(StatisticsCalculator, InterruptionCounterDoNotLogBeforeDecoding) {
@ -172,7 +172,7 @@ TEST(StatisticsCalculator, InterruptionCounterDoNotLogBeforeDecoding) {
stats.ExpandedVoiceSamples(151 * fs_khz, false); // 151 ms. stats.ExpandedVoiceSamples(151 * fs_khz, false); // 151 ms.
stats.EndExpandEvent(fs_hz); stats.EndExpandEvent(fs_hz);
auto lts = stats.GetLifetimeStatistics(); auto lts = stats.GetLifetimeStatistics();
EXPECT_EQ(0u, lts.interruption_count); EXPECT_EQ(0, lts.interruption_count);
// Call DecodedOutputPlayed(). Logging should happen after this. // Call DecodedOutputPlayed(). Logging should happen after this.
stats.DecodedOutputPlayed(); stats.DecodedOutputPlayed();
@ -181,7 +181,7 @@ TEST(StatisticsCalculator, InterruptionCounterDoNotLogBeforeDecoding) {
stats.ExpandedVoiceSamples(151 * fs_khz, false); // 151 ms. stats.ExpandedVoiceSamples(151 * fs_khz, false); // 151 ms.
stats.EndExpandEvent(fs_hz); stats.EndExpandEvent(fs_hz);
lts = stats.GetLifetimeStatistics(); lts = stats.GetLifetimeStatistics();
EXPECT_EQ(1u, lts.interruption_count); EXPECT_EQ(1, lts.interruption_count);
} }
} // namespace webrtc } // namespace webrtc

View File

@ -79,9 +79,8 @@ void NetEqStatsPlotter::SimulationEnded(int64_t simulation_time_ms) {
const auto lifetime_stats_vector = stats_getter_->lifetime_stats(); const auto lifetime_stats_vector = stats_getter_->lifetime_stats();
if (!lifetime_stats_vector->empty()) { if (!lifetime_stats_vector->empty()) {
auto lifetime_stats = lifetime_stats_vector->back().second; auto lifetime_stats = lifetime_stats_vector->back().second;
printf(" num_interruptions: %" PRId64 "\n", printf(" num_interruptions: %d\n", lifetime_stats.interruption_count);
lifetime_stats.interruption_count); printf(" sum_interruption_length_ms: %d ms\n",
printf(" sum_interruption_length_ms: %" PRId64 " ms\n",
lifetime_stats.total_interruption_duration_ms); lifetime_stats.total_interruption_duration_ms);
printf(" interruption ratio: %f%%\n", printf(" interruption ratio: %f%%\n",
100.0 * lifetime_stats.total_interruption_duration_ms / 100.0 * lifetime_stats.total_interruption_duration_ms /

View File

@ -165,6 +165,9 @@ void ExtractStats(const cricket::VoiceReceiverInfo& info, StatsReport* report) {
{StatsReport::kStatsValueNamePacketsReceived, info.packets_rcvd}, {StatsReport::kStatsValueNamePacketsReceived, info.packets_rcvd},
{StatsReport::kStatsValueNamePreferredJitterBufferMs, {StatsReport::kStatsValueNamePreferredJitterBufferMs,
info.jitter_buffer_preferred_ms}, info.jitter_buffer_preferred_ms},
{StatsReport::kStatsValueNameInterruptionCount, info.interruption_count},
{StatsReport::kStatsValueNameTotalInterruptionDurationMs,
info.total_interruption_duration_ms},
}; };
for (const auto& f : floats) for (const auto& f : floats)

View File

@ -383,6 +383,14 @@ void VerifyVoiceReceiverInfoReport(const StatsReport* report,
EXPECT_EQ(rtc::ToString(info.decoding_muted_output), value_in_report); EXPECT_EQ(rtc::ToString(info.decoding_muted_output), value_in_report);
EXPECT_TRUE(GetValue(report, StatsReport::kStatsValueNameCodecName, EXPECT_TRUE(GetValue(report, StatsReport::kStatsValueNameCodecName,
&value_in_report)); &value_in_report));
EXPECT_TRUE(GetValue(report, StatsReport::kStatsValueNameInterruptionCount,
&value_in_report));
EXPECT_EQ(rtc::ToString(info.interruption_count), value_in_report);
EXPECT_TRUE(GetValue(report,
StatsReport::kStatsValueNameTotalInterruptionDurationMs,
&value_in_report));
EXPECT_EQ(rtc::ToString(info.total_interruption_duration_ms),
value_in_report);
} }
void VerifyVoiceSenderInfoReport(const StatsReport* report, void VerifyVoiceSenderInfoReport(const StatsReport* report,