send_statistics_proxy.cc revision 5d0379da2cbdcce6f8494209c7ab559cd6de076e
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/system_wrappers/interface/critical_section_wrapper.h" 19#include "webrtc/system_wrappers/interface/logging.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} 74 75void SendStatisticsProxy::OnOutgoingRate(uint32_t framerate, uint32_t bitrate) { 76 rtc::CritScope lock(&crit_); 77 stats_.encode_frame_rate = framerate; 78 stats_.media_bitrate_bps = bitrate; 79} 80 81void SendStatisticsProxy::CpuOveruseMetricsUpdated( 82 const CpuOveruseMetrics& metrics) { 83 rtc::CritScope lock(&crit_); 84 // TODO(asapersson): Change to use OnEncodedFrame() for avg_encode_time_ms. 85 stats_.avg_encode_time_ms = metrics.avg_encode_time_ms; 86 stats_.encode_usage_percent = metrics.encode_usage_percent; 87} 88 89void SendStatisticsProxy::OnSuspendChange(bool is_suspended) { 90 rtc::CritScope lock(&crit_); 91 stats_.suspended = is_suspended; 92} 93 94VideoSendStream::Stats SendStatisticsProxy::GetStats() { 95 rtc::CritScope lock(&crit_); 96 PurgeOldStats(); 97 stats_.input_frame_rate = 98 static_cast<int>(input_frame_rate_tracker_.ComputeRate()); 99 return stats_; 100} 101 102void SendStatisticsProxy::PurgeOldStats() { 103 int64_t old_stats_ms = clock_->TimeInMilliseconds() - kStatsTimeoutMs; 104 for (std::map<uint32_t, VideoSendStream::StreamStats>::iterator it = 105 stats_.substreams.begin(); 106 it != stats_.substreams.end(); ++it) { 107 uint32_t ssrc = it->first; 108 if (update_times_[ssrc].resolution_update_ms <= old_stats_ms) { 109 it->second.width = 0; 110 it->second.height = 0; 111 } 112 } 113} 114 115VideoSendStream::StreamStats* SendStatisticsProxy::GetStatsEntry( 116 uint32_t ssrc) { 117 std::map<uint32_t, VideoSendStream::StreamStats>::iterator it = 118 stats_.substreams.find(ssrc); 119 if (it != stats_.substreams.end()) 120 return &it->second; 121 122 if (std::find(config_.rtp.ssrcs.begin(), config_.rtp.ssrcs.end(), ssrc) == 123 config_.rtp.ssrcs.end() && 124 std::find(config_.rtp.rtx.ssrcs.begin(), 125 config_.rtp.rtx.ssrcs.end(), 126 ssrc) == config_.rtp.rtx.ssrcs.end()) { 127 return nullptr; 128 } 129 130 return &stats_.substreams[ssrc]; // Insert new entry and return ptr. 131} 132 133void SendStatisticsProxy::OnInactiveSsrc(uint32_t ssrc) { 134 rtc::CritScope lock(&crit_); 135 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc); 136 if (stats == nullptr) 137 return; 138 139 stats->total_bitrate_bps = 0; 140 stats->retransmit_bitrate_bps = 0; 141 stats->height = 0; 142 stats->width = 0; 143} 144 145void SendStatisticsProxy::OnSetRates(uint32_t bitrate_bps, int framerate) { 146 rtc::CritScope lock(&crit_); 147 stats_.target_media_bitrate_bps = bitrate_bps; 148} 149 150void SendStatisticsProxy::OnSendEncodedImage( 151 const EncodedImage& encoded_image, 152 const RTPVideoHeader* rtp_video_header) { 153 size_t simulcast_idx = 154 rtp_video_header != nullptr ? rtp_video_header->simulcastIdx : 0; 155 if (simulcast_idx >= config_.rtp.ssrcs.size()) { 156 LOG(LS_ERROR) << "Encoded image outside simulcast range (" << simulcast_idx 157 << " >= " << config_.rtp.ssrcs.size() << ")."; 158 return; 159 } 160 uint32_t ssrc = config_.rtp.ssrcs[simulcast_idx]; 161 162 rtc::CritScope lock(&crit_); 163 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc); 164 if (stats == nullptr) 165 return; 166 167 stats->width = encoded_image._encodedWidth; 168 stats->height = encoded_image._encodedHeight; 169 update_times_[ssrc].resolution_update_ms = clock_->TimeInMilliseconds(); 170 171 key_frame_counter_.Add(encoded_image._frameType == kKeyFrame); 172 173 // TODO(asapersson): This is incorrect if simulcast layers are encoded on 174 // different threads and there is no guarantee that one frame of all layers 175 // are encoded before the next start. 176 if (last_sent_frame_timestamp_ > 0 && 177 encoded_image._timeStamp != last_sent_frame_timestamp_) { 178 sent_frame_rate_tracker_.AddSamples(1); 179 sent_width_counter_.Add(max_sent_width_per_timestamp_); 180 sent_height_counter_.Add(max_sent_height_per_timestamp_); 181 max_sent_width_per_timestamp_ = 0; 182 max_sent_height_per_timestamp_ = 0; 183 } 184 last_sent_frame_timestamp_ = encoded_image._timeStamp; 185 max_sent_width_per_timestamp_ = 186 std::max(max_sent_width_per_timestamp_, 187 static_cast<int>(encoded_image._encodedWidth)); 188 max_sent_height_per_timestamp_ = 189 std::max(max_sent_height_per_timestamp_, 190 static_cast<int>(encoded_image._encodedHeight)); 191} 192 193void SendStatisticsProxy::OnIncomingFrame(int width, int height) { 194 rtc::CritScope lock(&crit_); 195 input_frame_rate_tracker_.AddSamples(1); 196 input_width_counter_.Add(width); 197 input_height_counter_.Add(height); 198} 199 200void SendStatisticsProxy::OnEncodedFrame(int encode_time_ms) { 201 rtc::CritScope lock(&crit_); 202 encode_time_counter_.Add(encode_time_ms); 203} 204 205void SendStatisticsProxy::RtcpPacketTypesCounterUpdated( 206 uint32_t ssrc, 207 const RtcpPacketTypeCounter& packet_counter) { 208 rtc::CritScope lock(&crit_); 209 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc); 210 if (stats == nullptr) 211 return; 212 213 stats->rtcp_packet_type_counts = packet_counter; 214} 215 216void SendStatisticsProxy::StatisticsUpdated(const RtcpStatistics& statistics, 217 uint32_t ssrc) { 218 rtc::CritScope lock(&crit_); 219 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc); 220 if (stats == nullptr) 221 return; 222 223 stats->rtcp_stats = statistics; 224} 225 226void SendStatisticsProxy::CNameChanged(const char* cname, uint32_t ssrc) { 227} 228 229void SendStatisticsProxy::DataCountersUpdated( 230 const StreamDataCounters& counters, 231 uint32_t ssrc) { 232 rtc::CritScope lock(&crit_); 233 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc); 234 RTC_DCHECK(stats != nullptr) 235 << "DataCountersUpdated reported for unknown ssrc: " << ssrc; 236 237 stats->rtp_stats = counters; 238} 239 240void SendStatisticsProxy::Notify(const BitrateStatistics& total_stats, 241 const BitrateStatistics& retransmit_stats, 242 uint32_t ssrc) { 243 rtc::CritScope lock(&crit_); 244 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc); 245 if (stats == nullptr) 246 return; 247 248 stats->total_bitrate_bps = total_stats.bitrate_bps; 249 stats->retransmit_bitrate_bps = retransmit_stats.bitrate_bps; 250} 251 252void SendStatisticsProxy::FrameCountUpdated(const FrameCounts& frame_counts, 253 uint32_t ssrc) { 254 rtc::CritScope lock(&crit_); 255 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc); 256 if (stats == nullptr) 257 return; 258 259 stats->frame_counts = frame_counts; 260} 261 262void SendStatisticsProxy::SendSideDelayUpdated(int avg_delay_ms, 263 int max_delay_ms, 264 uint32_t ssrc) { 265 rtc::CritScope lock(&crit_); 266 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc); 267 if (stats == nullptr) 268 return; 269 stats->avg_delay_ms = avg_delay_ms; 270 stats->max_delay_ms = max_delay_ms; 271} 272 273void SendStatisticsProxy::SampleCounter::Add(int sample) { 274 sum += sample; 275 ++num_samples; 276} 277 278int SendStatisticsProxy::SampleCounter::Avg(int min_required_samples) const { 279 if (num_samples < min_required_samples || num_samples == 0) 280 return -1; 281 return sum / num_samples; 282} 283 284void SendStatisticsProxy::BoolSampleCounter::Add(bool sample) { 285 if (sample) 286 ++sum; 287 ++num_samples; 288} 289 290int SendStatisticsProxy::BoolSampleCounter::Percent( 291 int min_required_samples) const { 292 return Fraction(min_required_samples, 100.0f); 293} 294 295int SendStatisticsProxy::BoolSampleCounter::Permille( 296 int min_required_samples) const { 297 return Fraction(min_required_samples, 1000.0f); 298} 299 300int SendStatisticsProxy::BoolSampleCounter::Fraction( 301 int min_required_samples, float multiplier) const { 302 if (num_samples < min_required_samples || num_samples == 0) 303 return -1; 304 return static_cast<int>((sum * multiplier / num_samples) + 0.5f); 305} 306 307} // namespace webrtc 308