blob: 4e34f7905dfeae8d9fd355469d2ab99d92080e3c [file] [log] [blame]
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/audio_coding/neteq/tools/neteq_stats_getter.h"
#include <algorithm>
#include <numeric>
#include <utility>
#include "rtc_base/checks.h"
#include "rtc_base/strings/string_builder.h"
#include "rtc_base/time_utils.h"
namespace webrtc {
namespace test {
std::string NetEqStatsGetter::ConcealmentEvent::ToString() const {
char ss_buf[256];
rtc::SimpleStringBuilder ss(ss_buf);
ss << "ConcealmentEvent duration_ms:" << duration_ms
<< " event_number:" << concealment_event_number
<< " time_from_previous_event_end_ms:" << time_from_previous_event_end_ms;
return ss.str();
}
NetEqStatsGetter::NetEqStatsGetter(
std::unique_ptr<NetEqDelayAnalyzer> delay_analyzer)
: delay_analyzer_(std::move(delay_analyzer)) {}
void NetEqStatsGetter::BeforeGetAudio(NetEq* neteq) {
if (delay_analyzer_) {
delay_analyzer_->BeforeGetAudio(neteq);
}
}
void NetEqStatsGetter::AfterGetAudio(int64_t time_now_ms,
const AudioFrame& audio_frame,
bool muted,
NetEq* neteq) {
// TODO(minyue): Get stats should better not be called as a call back after
// get audio. It is called independently from get audio in practice.
const auto lifetime_stat = neteq->GetLifetimeStatistics();
if (last_stats_query_time_ms_ == 0 ||
rtc::TimeDiff(time_now_ms, last_stats_query_time_ms_) >=
stats_query_interval_ms_) {
NetEqNetworkStatistics stats;
RTC_CHECK_EQ(neteq->NetworkStatistics(&stats), 0);
stats_.push_back(std::make_pair(time_now_ms, stats));
lifetime_stats_.push_back(std::make_pair(time_now_ms, lifetime_stat));
last_stats_query_time_ms_ = time_now_ms;
}
const auto voice_concealed_samples =
lifetime_stat.concealed_samples - lifetime_stat.silent_concealed_samples;
if (current_concealment_event_ != lifetime_stat.concealment_events &&
voice_concealed_samples_until_last_event_ < voice_concealed_samples) {
if (last_event_end_time_ms_ > 0) {
// Do not account for the first event to avoid start of the call
// skewing.
ConcealmentEvent concealment_event;
uint64_t last_event_voice_concealed_samples =
voice_concealed_samples - voice_concealed_samples_until_last_event_;
RTC_CHECK_GT(last_event_voice_concealed_samples, 0);
concealment_event.duration_ms = last_event_voice_concealed_samples /
(audio_frame.sample_rate_hz_ / 1000);
concealment_event.concealment_event_number = current_concealment_event_;
concealment_event.time_from_previous_event_end_ms =
time_now_ms - last_event_end_time_ms_;
concealment_events_.emplace_back(concealment_event);
voice_concealed_samples_until_last_event_ = voice_concealed_samples;
}
last_event_end_time_ms_ = time_now_ms;
voice_concealed_samples_until_last_event_ = voice_concealed_samples;
current_concealment_event_ = lifetime_stat.concealment_events;
}
if (delay_analyzer_) {
delay_analyzer_->AfterGetAudio(time_now_ms, audio_frame, muted, neteq);
}
}
double NetEqStatsGetter::AverageSpeechExpandRate() const {
double sum_speech_expand = std::accumulate(
stats_.begin(), stats_.end(), double{0.0},
[](double a, std::pair<int64_t, NetEqNetworkStatistics> b) {
return a + static_cast<double>(b.second.speech_expand_rate);
});
return sum_speech_expand / 16384.0 / stats_.size();
}
NetEqStatsGetter::Stats NetEqStatsGetter::AverageStats() const {
Stats sum_stats = std::accumulate(
stats_.begin(), stats_.end(), Stats(),
[](Stats a, std::pair<int64_t, NetEqNetworkStatistics> bb) {
const auto& b = bb.second;
a.current_buffer_size_ms += b.current_buffer_size_ms;
a.preferred_buffer_size_ms += b.preferred_buffer_size_ms;
a.jitter_peaks_found += b.jitter_peaks_found;
a.packet_loss_rate += b.packet_loss_rate / 16384.0;
a.expand_rate += b.expand_rate / 16384.0;
a.speech_expand_rate += b.speech_expand_rate / 16384.0;
a.preemptive_rate += b.preemptive_rate / 16384.0;
a.accelerate_rate += b.accelerate_rate / 16384.0;
a.secondary_decoded_rate += b.secondary_decoded_rate / 16384.0;
a.secondary_discarded_rate += b.secondary_discarded_rate / 16384.0;
a.added_zero_samples += b.added_zero_samples;
a.mean_waiting_time_ms += b.mean_waiting_time_ms;
a.median_waiting_time_ms += b.median_waiting_time_ms;
a.min_waiting_time_ms = std::min(
a.min_waiting_time_ms, static_cast<double>(b.min_waiting_time_ms));
a.max_waiting_time_ms = std::max(
a.max_waiting_time_ms, static_cast<double>(b.max_waiting_time_ms));
return a;
});
sum_stats.current_buffer_size_ms /= stats_.size();
sum_stats.preferred_buffer_size_ms /= stats_.size();
sum_stats.jitter_peaks_found /= stats_.size();
sum_stats.packet_loss_rate /= stats_.size();
sum_stats.expand_rate /= stats_.size();
sum_stats.speech_expand_rate /= stats_.size();
sum_stats.preemptive_rate /= stats_.size();
sum_stats.accelerate_rate /= stats_.size();
sum_stats.secondary_decoded_rate /= stats_.size();
sum_stats.secondary_discarded_rate /= stats_.size();
sum_stats.added_zero_samples /= stats_.size();
sum_stats.mean_waiting_time_ms /= stats_.size();
sum_stats.median_waiting_time_ms /= stats_.size();
return sum_stats;
}
} // namespace test
} // namespace webrtc