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