Add stats totalSamplesReceived and concealedSamples
Adds two new stats to RTCMediaStreamTrackStats:
* totalSamplesReceived is the total number of samples received on
the audio channel and includes real and synthetic samples.
* concealedSamples is the total number of synthetic samples
received on the audio channel used to conceal packet loss.
Bug: webrtc:8076
Change-Id: I36e9828525ec341490cf3310a972b56a8b443667
Reviewed-on: https://chromium-review.googlesource.com/615902
Commit-Queue: Steve Anton <steveanton@webrtc.org>
Reviewed-by: Tommi <tommi@webrtc.org>
Reviewed-by: Taylor Brandstetter <deadbeef@webrtc.org>
Reviewed-by: Henrik Boström <hbos@webrtc.org>
Reviewed-by: Henrik Lundin <henrik.lundin@webrtc.org>
Cr-Original-Commit-Position: refs/heads/master@{#19506}
Cr-Mirrored-From: https://chromium.googlesource.com/external/webrtc
Cr-Mirrored-Commit: 2dbc69fa64a7ea183e090dbfaa68ab16d4bcef7a
diff --git a/api/stats/rtcstats_objects.h b/api/stats/rtcstats_objects.h
index 071a7fb..0cbea7c 100644
--- a/api/stats/rtcstats_objects.h
+++ b/api/stats/rtcstats_objects.h
@@ -273,9 +273,11 @@
// Audio-only members
RTCStatsMember<double> audio_level;
RTCStatsMember<double> total_audio_energy;
- RTCStatsMember<double> total_samples_duration;
RTCStatsMember<double> echo_return_loss;
RTCStatsMember<double> echo_return_loss_enhancement;
+ RTCStatsMember<uint64_t> total_samples_received;
+ RTCStatsMember<double> total_samples_duration;
+ RTCStatsMember<uint64_t> concealed_samples;
};
// https://w3c.github.io/webrtc-stats/#pcstats-dict*
diff --git a/api/statstypes.cc b/api/statstypes.cc
index 777e9f8..fb9e1e5 100644
--- a/api/statstypes.cc
+++ b/api/statstypes.cc
@@ -371,6 +371,8 @@
return "audioInputLevel";
case kStatsValueNameBytesSent:
return "bytesSent";
+ case kStatsValueNameConcealedSamples:
+ return "concealedSamples";
case kStatsValueNamePacketsSent:
return "packetsSent";
case kStatsValueNameBytesReceived:
@@ -383,6 +385,8 @@
return "packetsLost";
case kStatsValueNameProtocol:
return "protocol";
+ case kStatsValueNameTotalSamplesReceived:
+ return "totalSamplesReceived";
case kStatsValueNameTransportId:
return "transportId";
case kStatsValueNameSelectedCandidatePairId:
diff --git a/api/statstypes.h b/api/statstypes.h
index 57fe82d..819bfe0 100644
--- a/api/statstypes.h
+++ b/api/statstypes.h
@@ -104,6 +104,7 @@
kStatsValueNameBytesReceived,
kStatsValueNameBytesSent,
kStatsValueNameCodecImplementationName,
+ kStatsValueNameConcealedSamples,
kStatsValueNameDataChannelId,
kStatsValueNameFramesDecoded,
kStatsValueNameFramesEncoded,
@@ -120,6 +121,7 @@
kStatsValueNameState,
kStatsValueNameTotalAudioEnergy,
kStatsValueNameTotalSamplesDuration,
+ kStatsValueNameTotalSamplesReceived,
kStatsValueNameTransportId,
kStatsValueNameSentPingRequestsTotal,
kStatsValueNameSentPingRequestsBeforeFirstResponse,
diff --git a/audio/audio_receive_stream.cc b/audio/audio_receive_stream.cc
index be5c18a..d2200f4 100644
--- a/audio/audio_receive_stream.cc
+++ b/audio/audio_receive_stream.cc
@@ -194,6 +194,8 @@
auto ns = channel_proxy_->GetNetworkStatistics();
stats.jitter_buffer_ms = ns.currentBufferSize;
stats.jitter_buffer_preferred_ms = ns.preferredBufferSize;
+ stats.total_samples_received = ns.totalSamplesReceived;
+ stats.concealed_samples = ns.concealedSamples;
stats.expand_rate = Q14ToFloat(ns.currentExpandRate);
stats.speech_expand_rate = Q14ToFloat(ns.currentSpeechExpandRate);
stats.secondary_decoded_rate = Q14ToFloat(ns.currentSecondaryDecodedRate);
diff --git a/audio/audio_receive_stream_unittest.cc b/audio/audio_receive_stream_unittest.cc
index 0b1d47a..9723ee65 100644
--- a/audio/audio_receive_stream_unittest.cc
+++ b/audio/audio_receive_stream_unittest.cc
@@ -64,8 +64,9 @@
345, 678, 901, 234, -12, 3456, 7890, 567, 890, 123};
const CodecInst kCodecInst = {
123, "codec_name_recv", 96000, -187, 0, -103};
-const NetworkStatistics kNetworkStats = {
- 123, 456, false, 0, {}, 789, 12, 345, 678, 901, 0, -1, -1, -1, -1, -1, 0};
+const NetworkStatistics kNetworkStats = {123, 456, false, 789012, 3456, 0, {},
+ 789, 12, 345, 678, 901, 0, -1,
+ -1, -1, -1, -1, 0};
const AudioDecodingCallStats kAudioDecodeStats = MakeAudioDecodeStatsForTest();
struct ConfigHelper {
@@ -318,7 +319,9 @@
stats.delay_estimate_ms);
EXPECT_EQ(static_cast<int32_t>(kSpeechOutputLevel), stats.audio_level);
EXPECT_EQ(kTotalOutputEnergy, stats.total_output_energy);
+ EXPECT_EQ(kNetworkStats.totalSamplesReceived, stats.total_samples_received);
EXPECT_EQ(kTotalOutputDuration, stats.total_output_duration);
+ EXPECT_EQ(kNetworkStats.concealedSamples, stats.concealed_samples);
EXPECT_EQ(Q14ToFloat(kNetworkStats.currentExpandRate), stats.expand_rate);
EXPECT_EQ(Q14ToFloat(kNetworkStats.currentSpeechExpandRate),
stats.speech_expand_rate);
diff --git a/call/audio_receive_stream.h b/call/audio_receive_stream.h
index d692425..78f1bff 100644
--- a/call/audio_receive_stream.h
+++ b/call/audio_receive_stream.h
@@ -52,7 +52,15 @@
// See description of "totalAudioEnergy" in the WebRTC stats spec:
// https://w3c.github.io/webrtc-stats/#dom-rtcmediastreamtrackstats-totalaudioenergy
double total_output_energy = 0.0;
+ // See description of "totalSamplesReceived" in the WebRTC stats spec:
+ // https://w3c.github.io/webrtc-stats/#dom-rtcmediastreamtrackstats-totalsamplesreceived
+ uint64_t total_samples_received = 0;
+ // See description of "totalSamplesDuration" in the WebRTC stats spec:
+ // https://w3c.github.io/webrtc-stats/#dom-rtcmediastreamtrackstats-totalsamplesduration
double total_output_duration = 0.0;
+ // See description of "concealedSamples" in the WebRTC stats spec:
+ // https://w3c.github.io/webrtc-stats/#dom-rtcmediastreamtrackstats-concealedsamples
+ uint64_t concealed_samples = 0;
float expand_rate = 0.0f;
float speech_expand_rate = 0.0f;
float secondary_decoded_rate = 0.0f;
diff --git a/common_types.h b/common_types.h
index 4034d54..c4b5ce8 100644
--- a/common_types.h
+++ b/common_types.h
@@ -367,6 +367,13 @@
uint16_t preferredBufferSize;
// adding extra delay due to "peaky jitter"
bool jitterPeaksFound;
+ // Total number of audio samples received, including synthesized samples.
+ // https://w3c.github.io/webrtc-stats/#dom-rtcmediastreamtrackstats-totalsamplesreceived
+ uint64_t totalSamplesReceived;
+ // Total number of inbound audio samples that are based on synthesized data to
+ // conceal packet loss.
+ // https://w3c.github.io/webrtc-stats/#dom-rtcmediastreamtrackstats-concealedsamples
+ uint64_t concealedSamples;
// Loss rate (network + late); fraction between 0 and 1, scaled to Q14.
uint16_t currentPacketLossRate;
// Late loss rate; fraction between 0 and 1, scaled to Q14.
diff --git a/media/base/mediachannel.h b/media/base/mediachannel.h
index baa8c3a..76328ef 100644
--- a/media/base/mediachannel.h
+++ b/media/base/mediachannel.h
@@ -652,7 +652,9 @@
delay_estimate_ms(0),
audio_level(0),
total_output_energy(0.0),
+ total_samples_received(0),
total_output_duration(0.0),
+ concealed_samples(0),
expand_rate(0),
speech_expand_rate(0),
secondary_decoded_rate(0),
@@ -676,7 +678,15 @@
// See description of "totalAudioEnergy" in the WebRTC stats spec:
// https://w3c.github.io/webrtc-stats/#dom-rtcmediastreamtrackstats-totalaudioenergy
double total_output_energy;
+ // See description of "totalSamplesReceived" in the WebRTC stats spec:
+ // https://w3c.github.io/webrtc-stats/#dom-rtcmediastreamtrackstats-totalsamplesreceived
+ uint64_t total_samples_received;
+ // See description of "totalSamplesDuration" in the WebRTC stats spec:
+ // https://w3c.github.io/webrtc-stats/#dom-rtcmediastreamtrackstats-totalsamplesduration
double total_output_duration;
+ // See description of "concealedSamples" in the WebRTC stats spec:
+ // https://w3c.github.io/webrtc-stats/#dom-rtcmediastreamtrackstats-concealedsamples
+ uint64_t concealed_samples;
// fraction of synthesized audio inserted through expansion.
float expand_rate;
// fraction of synthesized speech inserted through expansion.
diff --git a/media/engine/webrtcvoiceengine.cc b/media/engine/webrtcvoiceengine.cc
index b6a84da..fdbb5c7 100644
--- a/media/engine/webrtcvoiceengine.cc
+++ b/media/engine/webrtcvoiceengine.cc
@@ -2282,7 +2282,9 @@
rinfo.delay_estimate_ms = stats.delay_estimate_ms;
rinfo.audio_level = stats.audio_level;
rinfo.total_output_energy = stats.total_output_energy;
+ rinfo.total_samples_received = stats.total_samples_received;
rinfo.total_output_duration = stats.total_output_duration;
+ rinfo.concealed_samples = stats.concealed_samples;
rinfo.expand_rate = stats.expand_rate;
rinfo.speech_expand_rate = stats.speech_expand_rate;
rinfo.secondary_decoded_rate = stats.secondary_decoded_rate;
diff --git a/media/engine/webrtcvoiceengine_unittest.cc b/media/engine/webrtcvoiceengine_unittest.cc
index d5743ca..28d09e8 100644
--- a/media/engine/webrtcvoiceengine_unittest.cc
+++ b/media/engine/webrtcvoiceengine_unittest.cc
@@ -596,6 +596,8 @@
stats.jitter_buffer_preferred_ms = 567;
stats.delay_estimate_ms = 890;
stats.audio_level = 1234;
+ stats.total_samples_received = 5678901;
+ stats.concealed_samples = 234;
stats.expand_rate = 5.67f;
stats.speech_expand_rate = 8.90f;
stats.secondary_decoded_rate = 1.23f;
@@ -632,6 +634,8 @@
stats.jitter_buffer_preferred_ms);
EXPECT_EQ(info.delay_estimate_ms, stats.delay_estimate_ms);
EXPECT_EQ(info.audio_level, stats.audio_level);
+ EXPECT_EQ(info.total_samples_received, stats.total_samples_received);
+ EXPECT_EQ(info.concealed_samples, stats.concealed_samples);
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);
diff --git a/modules/audio_coding/acm2/acm_receiver.cc b/modules/audio_coding/acm2/acm_receiver.cc
index b512f84..8912eaa 100644
--- a/modules/audio_coding/acm2/acm_receiver.cc
+++ b/modules/audio_coding/acm2/acm_receiver.cc
@@ -332,6 +332,10 @@
acm_stat->medianWaitingTimeMs = neteq_stat.median_waiting_time_ms;
acm_stat->minWaitingTimeMs = neteq_stat.min_waiting_time_ms;
acm_stat->maxWaitingTimeMs = neteq_stat.max_waiting_time_ms;
+
+ NetEqLifetimeStatistics neteq_lifetime_stat = neteq_->GetLifetimeStatistics();
+ acm_stat->totalSamplesReceived = neteq_lifetime_stat.total_samples_received;
+ acm_stat->concealedSamples = neteq_lifetime_stat.concealed_samples;
}
int AcmReceiver::DecoderByPayloadType(uint8_t payload_type,
diff --git a/modules/audio_coding/neteq/include/neteq.h b/modules/audio_coding/neteq/include/neteq.h
index d05b76e..56c30e5 100644
--- a/modules/audio_coding/neteq/include/neteq.h
+++ b/modules/audio_coding/neteq/include/neteq.h
@@ -58,6 +58,18 @@
int max_waiting_time_ms;
};
+// NetEq statistics that persist over the lifetime of the class.
+// These metrics are never reset.
+struct NetEqLifetimeStatistics {
+ // Total number of audio samples received, including synthesized samples.
+ // https://w3c.github.io/webrtc-stats/#dom-rtcmediastreamtrackstats-totalsamplesreceived
+ uint64_t total_samples_received = 0;
+ // Total number of inbound audio samples that are based on synthesized data to
+ // conceal packet loss.
+ // https://w3c.github.io/webrtc-stats/#dom-rtcmediastreamtrackstats-concealedsamples
+ uint64_t concealed_samples = 0;
+};
+
enum NetEqPlayoutMode {
kPlayoutOn,
kPlayoutOff,
@@ -220,6 +232,10 @@
// after the call.
virtual int NetworkStatistics(NetEqNetworkStatistics* stats) = 0;
+ // Returns a copy of this class's lifetime statistics. These statistics are
+ // never reset.
+ virtual NetEqLifetimeStatistics GetLifetimeStatistics() const = 0;
+
// Writes the current RTCP statistics to |stats|. The statistics are reset
// and a new report period is started with the call.
virtual void GetRtcpStatistics(RtcpStatistics* stats) = 0;
diff --git a/modules/audio_coding/neteq/neteq_impl.cc b/modules/audio_coding/neteq/neteq_impl.cc
index 4b95253..7858e84 100644
--- a/modules/audio_coding/neteq/neteq_impl.cc
+++ b/modules/audio_coding/neteq/neteq_impl.cc
@@ -380,6 +380,11 @@
return 0;
}
+NetEqLifetimeStatistics NetEqImpl::GetLifetimeStatistics() const {
+ rtc::CritScope lock(&crit_sect_);
+ return stats_.GetLifetimeStatistics();
+}
+
void NetEqImpl::GetRtcpStatistics(RtcpStatistics* stats) {
rtc::CritScope lock(&crit_sect_);
if (stats) {
diff --git a/modules/audio_coding/neteq/neteq_impl.h b/modules/audio_coding/neteq/neteq_impl.h
index 0eeff2e..f4b014a 100644
--- a/modules/audio_coding/neteq/neteq_impl.h
+++ b/modules/audio_coding/neteq/neteq_impl.h
@@ -185,6 +185,8 @@
// and a new report period is started with the call.
void GetRtcpStatistics(RtcpStatistics* stats) override;
+ NetEqLifetimeStatistics GetLifetimeStatistics() const override;
+
// Same as RtcpStatistics(), but does not reset anything.
void GetRtcpStatisticsNoReset(RtcpStatistics* stats) override;
diff --git a/modules/audio_coding/neteq/statistics_calculator.cc b/modules/audio_coding/neteq/statistics_calculator.cc
index 3faed62..d7d1644 100644
--- a/modules/audio_coding/neteq/statistics_calculator.cc
+++ b/modules/audio_coding/neteq/statistics_calculator.cc
@@ -153,24 +153,29 @@
void StatisticsCalculator::ExpandedVoiceSamples(size_t num_samples) {
expanded_speech_samples_ += num_samples;
+ lifetime_stats_.concealed_samples += num_samples;
}
void StatisticsCalculator::ExpandedNoiseSamples(size_t num_samples) {
expanded_noise_samples_ += num_samples;
+ lifetime_stats_.concealed_samples += num_samples;
}
void StatisticsCalculator::ExpandedVoiceSamplesCorrection(int num_samples) {
expanded_speech_samples_ =
AddIntToSizeTWithLowerCap(num_samples, expanded_speech_samples_);
+ lifetime_stats_.concealed_samples += num_samples;
}
void StatisticsCalculator::ExpandedNoiseSamplesCorrection(int num_samples) {
expanded_noise_samples_ =
AddIntToSizeTWithLowerCap(num_samples, expanded_noise_samples_);
+ lifetime_stats_.concealed_samples += num_samples;
}
void StatisticsCalculator::PreemptiveExpandedSamples(size_t num_samples) {
preemptive_samples_ += num_samples;
+ lifetime_stats_.concealed_samples += num_samples;
}
void StatisticsCalculator::AcceleratedSamples(size_t num_samples) {
@@ -205,6 +210,7 @@
timestamps_since_last_report_ = 0;
discarded_packets_ = 0;
}
+ lifetime_stats_.total_samples_received += num_samples;
}
void StatisticsCalculator::SecondaryDecodedSamples(int num_samples) {
@@ -307,6 +313,10 @@
Reset();
}
+NetEqLifetimeStatistics StatisticsCalculator::GetLifetimeStatistics() const {
+ return lifetime_stats_;
+}
+
uint16_t StatisticsCalculator::CalculateQ14Ratio(size_t numerator,
uint32_t denominator) {
if (numerator == 0) {
diff --git a/modules/audio_coding/neteq/statistics_calculator.h b/modules/audio_coding/neteq/statistics_calculator.h
index 2877a16..f261a66 100644
--- a/modules/audio_coding/neteq/statistics_calculator.h
+++ b/modules/audio_coding/neteq/statistics_calculator.h
@@ -99,6 +99,10 @@
const DecisionLogic& decision_logic,
NetEqNetworkStatistics *stats);
+ // Returns a copy of this class's lifetime statistics. These statistics are
+ // never reset.
+ NetEqLifetimeStatistics GetLifetimeStatistics() const;
+
private:
static const int kMaxReportPeriod = 60; // Seconds before auto-reset.
static const size_t kLenWaitingTimes = 100;
@@ -158,6 +162,8 @@
// Calculates numerator / denominator, and returns the value in Q14.
static uint16_t CalculateQ14Ratio(size_t numerator, uint32_t denominator);
+ // TODO(steveanton): Add unit tests for the lifetime stats.
+ NetEqLifetimeStatistics lifetime_stats_;
size_t preemptive_samples_;
size_t accelerate_samples_;
size_t added_zero_samples_;
diff --git a/pc/rtcstats_integrationtest.cc b/pc/rtcstats_integrationtest.cc
index 56a4ef1..b886bcc 100644
--- a/pc/rtcstats_integrationtest.cc
+++ b/pc/rtcstats_integrationtest.cc
@@ -525,6 +525,18 @@
verifier.MarkMemberTested(
media_stream_track.echo_return_loss_enhancement, true);
}
+ // totalSamplesReceived and concealedSamples are only present on inbound
+ // audio tracks.
+ if (*media_stream_track.kind == RTCMediaStreamTrackKind::kAudio &&
+ *media_stream_track.remote_source) {
+ verifier.TestMemberIsNonNegative<uint64_t>(
+ media_stream_track.total_samples_received);
+ verifier.TestMemberIsNonNegative<uint64_t>(
+ media_stream_track.concealed_samples);
+ } else {
+ verifier.TestMemberIsUndefined(media_stream_track.total_samples_received);
+ verifier.TestMemberIsUndefined(media_stream_track.concealed_samples);
+ }
return verifier.ExpectAllMembersSuccessfullyTested();
}
diff --git a/pc/rtcstatscollector.cc b/pc/rtcstatscollector.cc
index 4952012..5c506e4 100644
--- a/pc/rtcstatscollector.cc
+++ b/pc/rtcstatscollector.cc
@@ -424,8 +424,11 @@
}
audio_track_stats->total_audio_energy =
voice_receiver_info.total_output_energy;
+ audio_track_stats->total_samples_received =
+ voice_receiver_info.total_samples_received;
audio_track_stats->total_samples_duration =
voice_receiver_info.total_output_duration;
+ audio_track_stats->concealed_samples = voice_receiver_info.concealed_samples;
return audio_track_stats;
}
diff --git a/pc/rtcstatscollector_unittest.cc b/pc/rtcstatscollector_unittest.cc
index 4f3b35f..cc258ed 100644
--- a/pc/rtcstatscollector_unittest.cc
+++ b/pc/rtcstatscollector_unittest.cc
@@ -1554,7 +1554,9 @@
voice_receiver_info.local_stats[0].ssrc = 3;
voice_receiver_info.audio_level = 16383;
voice_receiver_info.total_output_energy = 0.125;
+ voice_receiver_info.total_samples_received = 4567;
voice_receiver_info.total_output_duration = 0.25;
+ voice_receiver_info.concealed_samples = 123;
test_->CreateMockRtpSendersReceiversAndChannels(
{ std::make_pair(local_audio_track.get(), voice_sender_info_ssrc1),
@@ -1628,7 +1630,9 @@
expected_remote_audio_track.detached = false;
expected_remote_audio_track.audio_level = 16383.0 / 32767.0;
expected_remote_audio_track.total_audio_energy = 0.125;
+ expected_remote_audio_track.total_samples_received = 4567;
expected_remote_audio_track.total_samples_duration = 0.25;
+ expected_remote_audio_track.concealed_samples = 123;
ASSERT_TRUE(report->Get(expected_remote_audio_track.id()));
EXPECT_EQ(expected_remote_audio_track,
report->Get(expected_remote_audio_track.id())->cast_to<
diff --git a/stats/rtcstats_objects.cc b/stats/rtcstats_objects.cc
index 19e94b3..284dfe0 100644
--- a/stats/rtcstats_objects.cc
+++ b/stats/rtcstats_objects.cc
@@ -379,9 +379,11 @@
&full_frames_lost,
&audio_level,
&total_audio_energy,
- &total_samples_duration,
&echo_return_loss,
- &echo_return_loss_enhancement);
+ &echo_return_loss_enhancement,
+ &total_samples_received,
+ &total_samples_duration,
+ &concealed_samples);
// clang-format on
RTCMediaStreamTrackStats::RTCMediaStreamTrackStats(
@@ -410,9 +412,11 @@
full_frames_lost("fullFramesLost"),
audio_level("audioLevel"),
total_audio_energy("totalAudioEnergy"),
- total_samples_duration("totalSamplesDuration"),
echo_return_loss("echoReturnLoss"),
- echo_return_loss_enhancement("echoReturnLossEnhancement") {
+ echo_return_loss_enhancement("echoReturnLossEnhancement"),
+ total_samples_received("totalSamplesReceived"),
+ total_samples_duration("totalSamplesDuration"),
+ concealed_samples("concealedSamples") {
RTC_DCHECK(kind == RTCMediaStreamTrackKind::kAudio ||
kind == RTCMediaStreamTrackKind::kVideo);
}
@@ -437,9 +441,11 @@
full_frames_lost(other.full_frames_lost),
audio_level(other.audio_level),
total_audio_energy(other.total_audio_energy),
- total_samples_duration(other.total_samples_duration),
echo_return_loss(other.echo_return_loss),
- echo_return_loss_enhancement(other.echo_return_loss_enhancement) {}
+ echo_return_loss_enhancement(other.echo_return_loss_enhancement),
+ total_samples_received(other.total_samples_received),
+ total_samples_duration(other.total_samples_duration),
+ concealed_samples(other.concealed_samples) {}
RTCMediaStreamTrackStats::~RTCMediaStreamTrackStats() {
}