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