Home | History | Annotate | Download | only in video
      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/video/send_statistics_proxy.h"
     12 
     13 #include <algorithm>
     14 #include <cmath>
     15 #include <map>
     16 
     17 #include "webrtc/base/checks.h"
     18 #include "webrtc/base/logging.h"
     19 #include "webrtc/system_wrappers/include/critical_section_wrapper.h"
     20 #include "webrtc/system_wrappers/include/metrics.h"
     21 
     22 namespace webrtc {
     23 namespace {
     24 const float kEncodeTimeWeigthFactor = 0.5f;
     25 
     26 // Used by histograms. Values of entries should not be changed.
     27 enum HistogramCodecType {
     28   kVideoUnknown = 0,
     29   kVideoVp8 = 1,
     30   kVideoVp9 = 2,
     31   kVideoH264 = 3,
     32   kVideoMax = 64,
     33 };
     34 
     35 const char* GetUmaPrefix(VideoEncoderConfig::ContentType content_type) {
     36   switch (content_type) {
     37     case VideoEncoderConfig::ContentType::kRealtimeVideo:
     38       return "WebRTC.Video.";
     39     case VideoEncoderConfig::ContentType::kScreen:
     40       return "WebRTC.Video.Screenshare.";
     41   }
     42   RTC_NOTREACHED();
     43   return nullptr;
     44 }
     45 
     46 HistogramCodecType PayloadNameToHistogramCodecType(
     47     const std::string& payload_name) {
     48   if (payload_name == "VP8") {
     49     return kVideoVp8;
     50   } else if (payload_name == "VP9") {
     51     return kVideoVp9;
     52   } else if (payload_name == "H264") {
     53     return kVideoH264;
     54   } else {
     55     return kVideoUnknown;
     56   }
     57 }
     58 
     59 void UpdateCodecTypeHistogram(const std::string& payload_name) {
     60   RTC_HISTOGRAM_ENUMERATION_SPARSE("WebRTC.Video.Encoder.CodecType",
     61       PayloadNameToHistogramCodecType(payload_name), kVideoMax);
     62 }
     63 }  // namespace
     64 
     65 
     66 const int SendStatisticsProxy::kStatsTimeoutMs = 5000;
     67 
     68 SendStatisticsProxy::SendStatisticsProxy(
     69     Clock* clock,
     70     const VideoSendStream::Config& config,
     71     VideoEncoderConfig::ContentType content_type)
     72     : clock_(clock),
     73       config_(config),
     74       content_type_(content_type),
     75       last_sent_frame_timestamp_(0),
     76       encode_time_(kEncodeTimeWeigthFactor),
     77       uma_container_(new UmaSamplesContainer(GetUmaPrefix(content_type_))) {
     78   UpdateCodecTypeHistogram(config_.encoder_settings.payload_name);
     79 }
     80 
     81 SendStatisticsProxy::~SendStatisticsProxy() {}
     82 
     83 SendStatisticsProxy::UmaSamplesContainer::UmaSamplesContainer(
     84     const char* prefix)
     85     : uma_prefix_(prefix),
     86       max_sent_width_per_timestamp_(0),
     87       max_sent_height_per_timestamp_(0),
     88       input_frame_rate_tracker_(100u, 10u),
     89       sent_frame_rate_tracker_(100u, 10u) {}
     90 
     91 SendStatisticsProxy::UmaSamplesContainer::~UmaSamplesContainer() {
     92   UpdateHistograms();
     93 }
     94 
     95 void SendStatisticsProxy::UmaSamplesContainer::UpdateHistograms() {
     96   const int kMinRequiredSamples = 200;
     97   int in_width = input_width_counter_.Avg(kMinRequiredSamples);
     98   int in_height = input_height_counter_.Avg(kMinRequiredSamples);
     99   int in_fps = round(input_frame_rate_tracker_.ComputeTotalRate());
    100   if (in_width != -1) {
    101     RTC_HISTOGRAM_COUNTS_SPARSE_10000(uma_prefix_ + "InputWidthInPixels",
    102                                       in_width);
    103     RTC_HISTOGRAM_COUNTS_SPARSE_10000(uma_prefix_ + "InputHeightInPixels",
    104                                       in_height);
    105     RTC_HISTOGRAM_COUNTS_SPARSE_100(uma_prefix_ + "InputFramesPerSecond",
    106                                     in_fps);
    107   }
    108   int sent_width = sent_width_counter_.Avg(kMinRequiredSamples);
    109   int sent_height = sent_height_counter_.Avg(kMinRequiredSamples);
    110   int sent_fps = round(sent_frame_rate_tracker_.ComputeTotalRate());
    111   if (sent_width != -1) {
    112     RTC_HISTOGRAM_COUNTS_SPARSE_10000(uma_prefix_ + "SentWidthInPixels",
    113                                       sent_width);
    114     RTC_HISTOGRAM_COUNTS_SPARSE_10000(uma_prefix_ + "SentHeightInPixels",
    115                                       sent_height);
    116     RTC_HISTOGRAM_COUNTS_SPARSE_100(uma_prefix_ + "SentFramesPerSecond",
    117                                     sent_fps);
    118   }
    119   int encode_ms = encode_time_counter_.Avg(kMinRequiredSamples);
    120   if (encode_ms != -1)
    121     RTC_HISTOGRAM_COUNTS_SPARSE_1000(uma_prefix_ + "EncodeTimeInMs", encode_ms);
    122 
    123   int key_frames_permille = key_frame_counter_.Permille(kMinRequiredSamples);
    124   if (key_frames_permille != -1) {
    125     RTC_HISTOGRAM_COUNTS_SPARSE_1000(uma_prefix_ + "KeyFramesSentInPermille",
    126                                      key_frames_permille);
    127   }
    128   int quality_limited =
    129       quality_limited_frame_counter_.Percent(kMinRequiredSamples);
    130   if (quality_limited != -1) {
    131     RTC_HISTOGRAM_PERCENTAGE_SPARSE(
    132         uma_prefix_ + "QualityLimitedResolutionInPercent", quality_limited);
    133   }
    134   int downscales = quality_downscales_counter_.Avg(kMinRequiredSamples);
    135   if (downscales != -1) {
    136     RTC_HISTOGRAM_ENUMERATION_SPARSE(
    137         uma_prefix_ + "QualityLimitedResolutionDownscales", downscales, 20);
    138   }
    139   int bw_limited = bw_limited_frame_counter_.Percent(kMinRequiredSamples);
    140   if (bw_limited != -1) {
    141     RTC_HISTOGRAM_PERCENTAGE_SPARSE(
    142         uma_prefix_ + "BandwidthLimitedResolutionInPercent", bw_limited);
    143   }
    144   int num_disabled = bw_resolutions_disabled_counter_.Avg(kMinRequiredSamples);
    145   if (num_disabled != -1) {
    146     RTC_HISTOGRAM_ENUMERATION_SPARSE(
    147         uma_prefix_ + "BandwidthLimitedResolutionsDisabled", num_disabled, 10);
    148   }
    149   int delay_ms = delay_counter_.Avg(kMinRequiredSamples);
    150   if (delay_ms != -1)
    151     RTC_HISTOGRAM_COUNTS_SPARSE_100000(uma_prefix_ + "SendSideDelayInMs",
    152                                        delay_ms);
    153 
    154   int max_delay_ms = max_delay_counter_.Avg(kMinRequiredSamples);
    155   if (max_delay_ms != -1) {
    156     RTC_HISTOGRAM_COUNTS_SPARSE_100000(uma_prefix_ + "SendSideDelayMaxInMs",
    157                                        max_delay_ms);
    158   }
    159 }
    160 
    161 void SendStatisticsProxy::SetContentType(
    162     VideoEncoderConfig::ContentType content_type) {
    163   rtc::CritScope lock(&crit_);
    164   if (content_type_ != content_type) {
    165     uma_container_.reset(new UmaSamplesContainer(GetUmaPrefix(content_type)));
    166     content_type_ = content_type;
    167   }
    168 }
    169 
    170 void SendStatisticsProxy::OnEncoderImplementationName(
    171     const char* implementation_name) {
    172   rtc::CritScope lock(&crit_);
    173   stats_.encoder_implementation_name = implementation_name;
    174 }
    175 
    176 void SendStatisticsProxy::OnOutgoingRate(uint32_t framerate, uint32_t bitrate) {
    177   rtc::CritScope lock(&crit_);
    178   stats_.encode_frame_rate = framerate;
    179   stats_.media_bitrate_bps = bitrate;
    180 }
    181 
    182 void SendStatisticsProxy::CpuOveruseMetricsUpdated(
    183     const CpuOveruseMetrics& metrics) {
    184   rtc::CritScope lock(&crit_);
    185   stats_.encode_usage_percent = metrics.encode_usage_percent;
    186 }
    187 
    188 void SendStatisticsProxy::OnSuspendChange(bool is_suspended) {
    189   rtc::CritScope lock(&crit_);
    190   stats_.suspended = is_suspended;
    191 }
    192 
    193 VideoSendStream::Stats SendStatisticsProxy::GetStats() {
    194   rtc::CritScope lock(&crit_);
    195   PurgeOldStats();
    196   stats_.input_frame_rate =
    197       round(uma_container_->input_frame_rate_tracker_.ComputeRate());
    198   return stats_;
    199 }
    200 
    201 void SendStatisticsProxy::PurgeOldStats() {
    202   int64_t old_stats_ms = clock_->TimeInMilliseconds() - kStatsTimeoutMs;
    203   for (std::map<uint32_t, VideoSendStream::StreamStats>::iterator it =
    204            stats_.substreams.begin();
    205        it != stats_.substreams.end(); ++it) {
    206     uint32_t ssrc = it->first;
    207     if (update_times_[ssrc].resolution_update_ms <= old_stats_ms) {
    208       it->second.width = 0;
    209       it->second.height = 0;
    210     }
    211   }
    212 }
    213 
    214 VideoSendStream::StreamStats* SendStatisticsProxy::GetStatsEntry(
    215     uint32_t ssrc) {
    216   std::map<uint32_t, VideoSendStream::StreamStats>::iterator it =
    217       stats_.substreams.find(ssrc);
    218   if (it != stats_.substreams.end())
    219     return &it->second;
    220 
    221   if (std::find(config_.rtp.ssrcs.begin(), config_.rtp.ssrcs.end(), ssrc) ==
    222           config_.rtp.ssrcs.end() &&
    223       std::find(config_.rtp.rtx.ssrcs.begin(),
    224                 config_.rtp.rtx.ssrcs.end(),
    225                 ssrc) == config_.rtp.rtx.ssrcs.end()) {
    226     return nullptr;
    227   }
    228 
    229   return &stats_.substreams[ssrc];  // Insert new entry and return ptr.
    230 }
    231 
    232 void SendStatisticsProxy::OnInactiveSsrc(uint32_t ssrc) {
    233   rtc::CritScope lock(&crit_);
    234   VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
    235   if (stats == nullptr)
    236     return;
    237 
    238   stats->total_bitrate_bps = 0;
    239   stats->retransmit_bitrate_bps = 0;
    240   stats->height = 0;
    241   stats->width = 0;
    242 }
    243 
    244 void SendStatisticsProxy::OnSetRates(uint32_t bitrate_bps, int framerate) {
    245   rtc::CritScope lock(&crit_);
    246   stats_.target_media_bitrate_bps = bitrate_bps;
    247 }
    248 
    249 void SendStatisticsProxy::OnSendEncodedImage(
    250     const EncodedImage& encoded_image,
    251     const RTPVideoHeader* rtp_video_header) {
    252   size_t simulcast_idx =
    253       rtp_video_header != nullptr ? rtp_video_header->simulcastIdx : 0;
    254   if (simulcast_idx >= config_.rtp.ssrcs.size()) {
    255     LOG(LS_ERROR) << "Encoded image outside simulcast range (" << simulcast_idx
    256                   << " >= " << config_.rtp.ssrcs.size() << ").";
    257     return;
    258   }
    259   uint32_t ssrc = config_.rtp.ssrcs[simulcast_idx];
    260 
    261   rtc::CritScope lock(&crit_);
    262   VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
    263   if (stats == nullptr)
    264     return;
    265 
    266   stats->width = encoded_image._encodedWidth;
    267   stats->height = encoded_image._encodedHeight;
    268   update_times_[ssrc].resolution_update_ms = clock_->TimeInMilliseconds();
    269 
    270   uma_container_->key_frame_counter_.Add(encoded_image._frameType ==
    271                                          kVideoFrameKey);
    272 
    273   stats_.bw_limited_resolution =
    274       encoded_image.adapt_reason_.quality_resolution_downscales > 0 ||
    275       encoded_image.adapt_reason_.bw_resolutions_disabled > 0;
    276 
    277   if (encoded_image.adapt_reason_.quality_resolution_downscales != -1) {
    278     bool downscaled =
    279         encoded_image.adapt_reason_.quality_resolution_downscales > 0;
    280     uma_container_->quality_limited_frame_counter_.Add(downscaled);
    281     if (downscaled) {
    282       uma_container_->quality_downscales_counter_.Add(
    283           encoded_image.adapt_reason_.quality_resolution_downscales);
    284     }
    285   }
    286   if (encoded_image.adapt_reason_.bw_resolutions_disabled != -1) {
    287     bool bw_limited = encoded_image.adapt_reason_.bw_resolutions_disabled > 0;
    288     uma_container_->bw_limited_frame_counter_.Add(bw_limited);
    289     if (bw_limited) {
    290       uma_container_->bw_resolutions_disabled_counter_.Add(
    291           encoded_image.adapt_reason_.bw_resolutions_disabled);
    292     }
    293   }
    294 
    295   // TODO(asapersson): This is incorrect if simulcast layers are encoded on
    296   // different threads and there is no guarantee that one frame of all layers
    297   // are encoded before the next start.
    298   if (last_sent_frame_timestamp_ > 0 &&
    299       encoded_image._timeStamp != last_sent_frame_timestamp_) {
    300     uma_container_->sent_frame_rate_tracker_.AddSamples(1);
    301     uma_container_->sent_width_counter_.Add(
    302         uma_container_->max_sent_width_per_timestamp_);
    303     uma_container_->sent_height_counter_.Add(
    304         uma_container_->max_sent_height_per_timestamp_);
    305     uma_container_->max_sent_width_per_timestamp_ = 0;
    306     uma_container_->max_sent_height_per_timestamp_ = 0;
    307   }
    308   last_sent_frame_timestamp_ = encoded_image._timeStamp;
    309   uma_container_->max_sent_width_per_timestamp_ =
    310       std::max(uma_container_->max_sent_width_per_timestamp_,
    311                static_cast<int>(encoded_image._encodedWidth));
    312   uma_container_->max_sent_height_per_timestamp_ =
    313       std::max(uma_container_->max_sent_height_per_timestamp_,
    314                static_cast<int>(encoded_image._encodedHeight));
    315 }
    316 
    317 void SendStatisticsProxy::OnIncomingFrame(int width, int height) {
    318   rtc::CritScope lock(&crit_);
    319   uma_container_->input_frame_rate_tracker_.AddSamples(1);
    320   uma_container_->input_width_counter_.Add(width);
    321   uma_container_->input_height_counter_.Add(height);
    322 }
    323 
    324 void SendStatisticsProxy::OnEncodedFrame(int encode_time_ms) {
    325   rtc::CritScope lock(&crit_);
    326   uma_container_->encode_time_counter_.Add(encode_time_ms);
    327   encode_time_.Apply(1.0f, encode_time_ms);
    328   stats_.avg_encode_time_ms = round(encode_time_.filtered());
    329 }
    330 
    331 void SendStatisticsProxy::RtcpPacketTypesCounterUpdated(
    332     uint32_t ssrc,
    333     const RtcpPacketTypeCounter& packet_counter) {
    334   rtc::CritScope lock(&crit_);
    335   VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
    336   if (stats == nullptr)
    337     return;
    338 
    339   stats->rtcp_packet_type_counts = packet_counter;
    340 }
    341 
    342 void SendStatisticsProxy::StatisticsUpdated(const RtcpStatistics& statistics,
    343                                             uint32_t ssrc) {
    344   rtc::CritScope lock(&crit_);
    345   VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
    346   if (stats == nullptr)
    347     return;
    348 
    349   stats->rtcp_stats = statistics;
    350 }
    351 
    352 void SendStatisticsProxy::CNameChanged(const char* cname, uint32_t ssrc) {
    353 }
    354 
    355 void SendStatisticsProxy::DataCountersUpdated(
    356     const StreamDataCounters& counters,
    357     uint32_t ssrc) {
    358   rtc::CritScope lock(&crit_);
    359   VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
    360   RTC_DCHECK(stats != nullptr)
    361       << "DataCountersUpdated reported for unknown ssrc: " << ssrc;
    362 
    363   stats->rtp_stats = counters;
    364 }
    365 
    366 void SendStatisticsProxy::Notify(const BitrateStatistics& total_stats,
    367                                  const BitrateStatistics& retransmit_stats,
    368                                  uint32_t ssrc) {
    369   rtc::CritScope lock(&crit_);
    370   VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
    371   if (stats == nullptr)
    372     return;
    373 
    374   stats->total_bitrate_bps = total_stats.bitrate_bps;
    375   stats->retransmit_bitrate_bps = retransmit_stats.bitrate_bps;
    376 }
    377 
    378 void SendStatisticsProxy::FrameCountUpdated(const FrameCounts& frame_counts,
    379                                             uint32_t ssrc) {
    380   rtc::CritScope lock(&crit_);
    381   VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
    382   if (stats == nullptr)
    383     return;
    384 
    385   stats->frame_counts = frame_counts;
    386 }
    387 
    388 void SendStatisticsProxy::SendSideDelayUpdated(int avg_delay_ms,
    389                                                int max_delay_ms,
    390                                                uint32_t ssrc) {
    391   rtc::CritScope lock(&crit_);
    392   VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
    393   if (stats == nullptr)
    394     return;
    395   stats->avg_delay_ms = avg_delay_ms;
    396   stats->max_delay_ms = max_delay_ms;
    397 
    398   uma_container_->delay_counter_.Add(avg_delay_ms);
    399   uma_container_->max_delay_counter_.Add(max_delay_ms);
    400 }
    401 
    402 void SendStatisticsProxy::SampleCounter::Add(int sample) {
    403   sum += sample;
    404   ++num_samples;
    405 }
    406 
    407 int SendStatisticsProxy::SampleCounter::Avg(int min_required_samples) const {
    408   if (num_samples < min_required_samples || num_samples == 0)
    409     return -1;
    410   return (sum + (num_samples / 2)) / num_samples;
    411 }
    412 
    413 void SendStatisticsProxy::BoolSampleCounter::Add(bool sample) {
    414   if (sample)
    415     ++sum;
    416   ++num_samples;
    417 }
    418 
    419 int SendStatisticsProxy::BoolSampleCounter::Percent(
    420     int min_required_samples) const {
    421   return Fraction(min_required_samples, 100.0f);
    422 }
    423 
    424 int SendStatisticsProxy::BoolSampleCounter::Permille(
    425     int min_required_samples) const {
    426   return Fraction(min_required_samples, 1000.0f);
    427 }
    428 
    429 int SendStatisticsProxy::BoolSampleCounter::Fraction(
    430     int min_required_samples, float multiplier) const {
    431   if (num_samples < min_required_samples || num_samples == 0)
    432     return -1;
    433   return static_cast<int>((sum * multiplier / num_samples) + 0.5f);
    434 }
    435 }  // namespace webrtc
    436