Home | History | Annotate | Download | only in neteq
      1 /*
      2  *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
      3  *
      4  *  Use of this source code is governed by a BSD-style license
      5  *  that can be found in the LICENSE file in the root of the source
      6  *  tree. An additional intellectual property rights grant can be found
      7  *  in the file PATENTS.  All contributing project authors may
      8  *  be found in the AUTHORS file in the root of the source tree.
      9  */
     10 
     11 #include "webrtc/modules/audio_coding/neteq/statistics_calculator.h"
     12 
     13 #include <assert.h>
     14 #include <string.h>  // memset
     15 #include <algorithm>
     16 
     17 #include "webrtc/base/checks.h"
     18 #include "webrtc/base/safe_conversions.h"
     19 #include "webrtc/modules/audio_coding/neteq/decision_logic.h"
     20 #include "webrtc/modules/audio_coding/neteq/delay_manager.h"
     21 #include "webrtc/system_wrappers/include/metrics.h"
     22 
     23 namespace webrtc {
     24 
     25 // Allocating the static const so that it can be passed by reference to
     26 // RTC_DCHECK.
     27 const size_t StatisticsCalculator::kLenWaitingTimes;
     28 
     29 StatisticsCalculator::PeriodicUmaLogger::PeriodicUmaLogger(
     30     const std::string& uma_name,
     31     int report_interval_ms,
     32     int max_value)
     33     : uma_name_(uma_name),
     34       report_interval_ms_(report_interval_ms),
     35       max_value_(max_value),
     36       timer_(0) {
     37 }
     38 
     39 StatisticsCalculator::PeriodicUmaLogger::~PeriodicUmaLogger() = default;
     40 
     41 void StatisticsCalculator::PeriodicUmaLogger::AdvanceClock(int step_ms) {
     42   timer_ += step_ms;
     43   if (timer_ < report_interval_ms_) {
     44     return;
     45   }
     46   LogToUma(Metric());
     47   Reset();
     48   timer_ -= report_interval_ms_;
     49   RTC_DCHECK_GE(timer_, 0);
     50 }
     51 
     52 void StatisticsCalculator::PeriodicUmaLogger::LogToUma(int value) const {
     53   RTC_HISTOGRAM_COUNTS_SPARSE(uma_name_, value, 1, max_value_, 50);
     54 }
     55 
     56 StatisticsCalculator::PeriodicUmaCount::PeriodicUmaCount(
     57     const std::string& uma_name,
     58     int report_interval_ms,
     59     int max_value)
     60     : PeriodicUmaLogger(uma_name, report_interval_ms, max_value) {
     61 }
     62 
     63 StatisticsCalculator::PeriodicUmaCount::~PeriodicUmaCount() {
     64   // Log the count for the current (incomplete) interval.
     65   LogToUma(Metric());
     66 }
     67 
     68 void StatisticsCalculator::PeriodicUmaCount::RegisterSample() {
     69   ++counter_;
     70 }
     71 
     72 int StatisticsCalculator::PeriodicUmaCount::Metric() const {
     73   return counter_;
     74 }
     75 
     76 void StatisticsCalculator::PeriodicUmaCount::Reset() {
     77   counter_ = 0;
     78 }
     79 
     80 StatisticsCalculator::PeriodicUmaAverage::PeriodicUmaAverage(
     81     const std::string& uma_name,
     82     int report_interval_ms,
     83     int max_value)
     84     : PeriodicUmaLogger(uma_name, report_interval_ms, max_value) {
     85 }
     86 
     87 StatisticsCalculator::PeriodicUmaAverage::~PeriodicUmaAverage() {
     88   // Log the average for the current (incomplete) interval.
     89   LogToUma(Metric());
     90 }
     91 
     92 void StatisticsCalculator::PeriodicUmaAverage::RegisterSample(int value) {
     93   sum_ += value;
     94   ++counter_;
     95 }
     96 
     97 int StatisticsCalculator::PeriodicUmaAverage::Metric() const {
     98   return static_cast<int>(sum_ / counter_);
     99 }
    100 
    101 void StatisticsCalculator::PeriodicUmaAverage::Reset() {
    102   sum_ = 0.0;
    103   counter_ = 0;
    104 }
    105 
    106 StatisticsCalculator::StatisticsCalculator()
    107     : preemptive_samples_(0),
    108       accelerate_samples_(0),
    109       added_zero_samples_(0),
    110       expanded_speech_samples_(0),
    111       expanded_noise_samples_(0),
    112       discarded_packets_(0),
    113       lost_timestamps_(0),
    114       timestamps_since_last_report_(0),
    115       secondary_decoded_samples_(0),
    116       delayed_packet_outage_counter_(
    117           "WebRTC.Audio.DelayedPacketOutageEventsPerMinute",
    118           60000,  // 60 seconds report interval.
    119           100),
    120       excess_buffer_delay_("WebRTC.Audio.AverageExcessBufferDelayMs",
    121                            60000,  // 60 seconds report interval.
    122                            1000) {
    123 }
    124 
    125 StatisticsCalculator::~StatisticsCalculator() = default;
    126 
    127 void StatisticsCalculator::Reset() {
    128   preemptive_samples_ = 0;
    129   accelerate_samples_ = 0;
    130   added_zero_samples_ = 0;
    131   expanded_speech_samples_ = 0;
    132   expanded_noise_samples_ = 0;
    133   secondary_decoded_samples_ = 0;
    134   waiting_times_.clear();
    135 }
    136 
    137 void StatisticsCalculator::ResetMcu() {
    138   discarded_packets_ = 0;
    139   lost_timestamps_ = 0;
    140   timestamps_since_last_report_ = 0;
    141 }
    142 
    143 void StatisticsCalculator::ExpandedVoiceSamples(size_t num_samples) {
    144   expanded_speech_samples_ += num_samples;
    145 }
    146 
    147 void StatisticsCalculator::ExpandedNoiseSamples(size_t num_samples) {
    148   expanded_noise_samples_ += num_samples;
    149 }
    150 
    151 void StatisticsCalculator::PreemptiveExpandedSamples(size_t num_samples) {
    152   preemptive_samples_ += num_samples;
    153 }
    154 
    155 void StatisticsCalculator::AcceleratedSamples(size_t num_samples) {
    156   accelerate_samples_ += num_samples;
    157 }
    158 
    159 void StatisticsCalculator::AddZeros(size_t num_samples) {
    160   added_zero_samples_ += num_samples;
    161 }
    162 
    163 void StatisticsCalculator::PacketsDiscarded(size_t num_packets) {
    164   discarded_packets_ += num_packets;
    165 }
    166 
    167 void StatisticsCalculator::LostSamples(size_t num_samples) {
    168   lost_timestamps_ += num_samples;
    169 }
    170 
    171 void StatisticsCalculator::IncreaseCounter(size_t num_samples, int fs_hz) {
    172   const int time_step_ms =
    173       rtc::CheckedDivExact(static_cast<int>(1000 * num_samples), fs_hz);
    174   delayed_packet_outage_counter_.AdvanceClock(time_step_ms);
    175   excess_buffer_delay_.AdvanceClock(time_step_ms);
    176   timestamps_since_last_report_ += static_cast<uint32_t>(num_samples);
    177   if (timestamps_since_last_report_ >
    178       static_cast<uint32_t>(fs_hz * kMaxReportPeriod)) {
    179     lost_timestamps_ = 0;
    180     timestamps_since_last_report_ = 0;
    181     discarded_packets_ = 0;
    182   }
    183 }
    184 
    185 void StatisticsCalculator::SecondaryDecodedSamples(int num_samples) {
    186   secondary_decoded_samples_ += num_samples;
    187 }
    188 
    189 void StatisticsCalculator::LogDelayedPacketOutageEvent(int outage_duration_ms) {
    190   RTC_HISTOGRAM_COUNTS_SPARSE("WebRTC.Audio.DelayedPacketOutageEventMs",
    191                               outage_duration_ms, 1 /* min */, 2000 /* max */,
    192                               100 /* bucket count */);
    193   delayed_packet_outage_counter_.RegisterSample();
    194 }
    195 
    196 void StatisticsCalculator::StoreWaitingTime(int waiting_time_ms) {
    197   excess_buffer_delay_.RegisterSample(waiting_time_ms);
    198   RTC_DCHECK_LE(waiting_times_.size(), kLenWaitingTimes);
    199   if (waiting_times_.size() == kLenWaitingTimes) {
    200     // Erase first value.
    201     waiting_times_.pop_front();
    202   }
    203   waiting_times_.push_back(waiting_time_ms);
    204 }
    205 
    206 void StatisticsCalculator::GetNetworkStatistics(
    207     int fs_hz,
    208     size_t num_samples_in_buffers,
    209     size_t samples_per_packet,
    210     const DelayManager& delay_manager,
    211     const DecisionLogic& decision_logic,
    212     NetEqNetworkStatistics *stats) {
    213   if (fs_hz <= 0 || !stats) {
    214     assert(false);
    215     return;
    216   }
    217 
    218   stats->added_zero_samples = added_zero_samples_;
    219   stats->current_buffer_size_ms =
    220       static_cast<uint16_t>(num_samples_in_buffers * 1000 / fs_hz);
    221   const int ms_per_packet = rtc::checked_cast<int>(
    222       decision_logic.packet_length_samples() / (fs_hz / 1000));
    223   stats->preferred_buffer_size_ms = (delay_manager.TargetLevel() >> 8) *
    224       ms_per_packet;
    225   stats->jitter_peaks_found = delay_manager.PeakFound();
    226   stats->clockdrift_ppm = delay_manager.AverageIAT();
    227 
    228   stats->packet_loss_rate =
    229       CalculateQ14Ratio(lost_timestamps_, timestamps_since_last_report_);
    230 
    231   const size_t discarded_samples = discarded_packets_ * samples_per_packet;
    232   stats->packet_discard_rate =
    233       CalculateQ14Ratio(discarded_samples, timestamps_since_last_report_);
    234 
    235   stats->accelerate_rate =
    236       CalculateQ14Ratio(accelerate_samples_, timestamps_since_last_report_);
    237 
    238   stats->preemptive_rate =
    239       CalculateQ14Ratio(preemptive_samples_, timestamps_since_last_report_);
    240 
    241   stats->expand_rate =
    242       CalculateQ14Ratio(expanded_speech_samples_ + expanded_noise_samples_,
    243                         timestamps_since_last_report_);
    244 
    245   stats->speech_expand_rate =
    246       CalculateQ14Ratio(expanded_speech_samples_,
    247                         timestamps_since_last_report_);
    248 
    249   stats->secondary_decoded_rate =
    250       CalculateQ14Ratio(secondary_decoded_samples_,
    251                         timestamps_since_last_report_);
    252 
    253   if (waiting_times_.size() == 0) {
    254     stats->mean_waiting_time_ms = -1;
    255     stats->median_waiting_time_ms = -1;
    256     stats->min_waiting_time_ms = -1;
    257     stats->max_waiting_time_ms = -1;
    258   } else {
    259     std::sort(waiting_times_.begin(), waiting_times_.end());
    260     // Find mid-point elements. If the size is odd, the two values
    261     // |middle_left| and |middle_right| will both be the one middle element; if
    262     // the size is even, they will be the the two neighboring elements at the
    263     // middle of the list.
    264     const int middle_left = waiting_times_[(waiting_times_.size() - 1) / 2];
    265     const int middle_right = waiting_times_[waiting_times_.size() / 2];
    266     // Calculate the average of the two. (Works also for odd sizes.)
    267     stats->median_waiting_time_ms = (middle_left + middle_right) / 2;
    268     stats->min_waiting_time_ms = waiting_times_.front();
    269     stats->max_waiting_time_ms = waiting_times_.back();
    270     double sum = 0;
    271     for (auto time : waiting_times_) {
    272       sum += time;
    273     }
    274     stats->mean_waiting_time_ms = static_cast<int>(sum / waiting_times_.size());
    275   }
    276 
    277   // Reset counters.
    278   ResetMcu();
    279   Reset();
    280 }
    281 
    282 uint16_t StatisticsCalculator::CalculateQ14Ratio(size_t numerator,
    283                                                  uint32_t denominator) {
    284   if (numerator == 0) {
    285     return 0;
    286   } else if (numerator < denominator) {
    287     // Ratio must be smaller than 1 in Q14.
    288     assert((numerator << 14) / denominator < (1 << 14));
    289     return static_cast<uint16_t>((numerator << 14) / denominator);
    290   } else {
    291     // Will not produce a ratio larger than 1, since this is probably an error.
    292     return 1 << 14;
    293   }
    294 }
    295 
    296 }  // namespace webrtc
    297