diff options
Diffstat (limited to 'webrtc/video/send_statistics_proxy.cc')
-rw-r--r-- | webrtc/video/send_statistics_proxy.cc | 376 |
1 files changed, 376 insertions, 0 deletions
diff --git a/webrtc/video/send_statistics_proxy.cc b/webrtc/video/send_statistics_proxy.cc new file mode 100644 index 0000000000..5be9970583 --- /dev/null +++ b/webrtc/video/send_statistics_proxy.cc @@ -0,0 +1,376 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/video/send_statistics_proxy.h" + +#include <algorithm> +#include <map> + +#include "webrtc/base/checks.h" + +#include "webrtc/base/logging.h" +#include "webrtc/system_wrappers/include/critical_section_wrapper.h" +#include "webrtc/system_wrappers/include/metrics.h" + +namespace webrtc { +namespace { +// Used by histograms. Values of entries should not be changed. +enum HistogramCodecType { + kVideoUnknown = 0, + kVideoVp8 = 1, + kVideoVp9 = 2, + kVideoH264 = 3, + kVideoMax = 64, +}; + +HistogramCodecType PayloadNameToHistogramCodecType( + const std::string& payload_name) { + if (payload_name == "VP8") { + return kVideoVp8; + } else if (payload_name == "VP9") { + return kVideoVp9; + } else if (payload_name == "H264") { + return kVideoH264; + } else { + return kVideoUnknown; + } +} + +void UpdateCodecTypeHistogram(const std::string& payload_name) { + RTC_HISTOGRAM_ENUMERATION("WebRTC.Video.Encoder.CodecType", + PayloadNameToHistogramCodecType(payload_name), kVideoMax); +} +} // namespace + + +const int SendStatisticsProxy::kStatsTimeoutMs = 5000; + +SendStatisticsProxy::SendStatisticsProxy(Clock* clock, + const VideoSendStream::Config& config) + : clock_(clock), + config_(config), + input_frame_rate_tracker_(100u, 10u), + sent_frame_rate_tracker_(100u, 10u), + last_sent_frame_timestamp_(0), + max_sent_width_per_timestamp_(0), + max_sent_height_per_timestamp_(0) { + UpdateCodecTypeHistogram(config_.encoder_settings.payload_name); +} + +SendStatisticsProxy::~SendStatisticsProxy() { + UpdateHistograms(); +} + +void SendStatisticsProxy::UpdateHistograms() { + int input_fps = + static_cast<int>(input_frame_rate_tracker_.ComputeTotalRate()); + if (input_fps > 0) + RTC_HISTOGRAM_COUNTS_100("WebRTC.Video.InputFramesPerSecond", input_fps); + int sent_fps = + static_cast<int>(sent_frame_rate_tracker_.ComputeTotalRate()); + if (sent_fps > 0) + RTC_HISTOGRAM_COUNTS_100("WebRTC.Video.SentFramesPerSecond", sent_fps); + + const int kMinRequiredSamples = 200; + int in_width = input_width_counter_.Avg(kMinRequiredSamples); + int in_height = input_height_counter_.Avg(kMinRequiredSamples); + if (in_width != -1) { + RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.InputWidthInPixels", in_width); + RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.InputHeightInPixels", in_height); + } + int sent_width = sent_width_counter_.Avg(kMinRequiredSamples); + int sent_height = sent_height_counter_.Avg(kMinRequiredSamples); + if (sent_width != -1) { + RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.SentWidthInPixels", sent_width); + RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.SentHeightInPixels", sent_height); + } + int encode_ms = encode_time_counter_.Avg(kMinRequiredSamples); + if (encode_ms != -1) + RTC_HISTOGRAM_COUNTS_1000("WebRTC.Video.EncodeTimeInMs", encode_ms); + + int key_frames_permille = key_frame_counter_.Permille(kMinRequiredSamples); + if (key_frames_permille != -1) { + RTC_HISTOGRAM_COUNTS_1000("WebRTC.Video.KeyFramesSentInPermille", + key_frames_permille); + } + int quality_limited = + quality_limited_frame_counter_.Percent(kMinRequiredSamples); + if (quality_limited != -1) { + RTC_HISTOGRAM_PERCENTAGE("WebRTC.Video.QualityLimitedResolutionInPercent", + quality_limited); + } + int downscales = quality_downscales_counter_.Avg(kMinRequiredSamples); + if (downscales != -1) { + RTC_HISTOGRAM_ENUMERATION("WebRTC.Video.QualityLimitedResolutionDownscales", + downscales, 20); + } + int bw_limited = bw_limited_frame_counter_.Percent(kMinRequiredSamples); + if (bw_limited != -1) { + RTC_HISTOGRAM_PERCENTAGE("WebRTC.Video.BandwidthLimitedResolutionInPercent", + bw_limited); + } + int num_disabled = bw_resolutions_disabled_counter_.Avg(kMinRequiredSamples); + if (num_disabled != -1) { + RTC_HISTOGRAM_ENUMERATION( + "WebRTC.Video.BandwidthLimitedResolutionsDisabled", num_disabled, 10); + } +} + +void SendStatisticsProxy::OnOutgoingRate(uint32_t framerate, uint32_t bitrate) { + rtc::CritScope lock(&crit_); + stats_.encode_frame_rate = framerate; + stats_.media_bitrate_bps = bitrate; +} + +void SendStatisticsProxy::CpuOveruseMetricsUpdated( + const CpuOveruseMetrics& metrics) { + rtc::CritScope lock(&crit_); + // TODO(asapersson): Change to use OnEncodedFrame() for avg_encode_time_ms. + stats_.avg_encode_time_ms = metrics.avg_encode_time_ms; + stats_.encode_usage_percent = metrics.encode_usage_percent; +} + +void SendStatisticsProxy::OnSuspendChange(bool is_suspended) { + rtc::CritScope lock(&crit_); + stats_.suspended = is_suspended; +} + +VideoSendStream::Stats SendStatisticsProxy::GetStats() { + rtc::CritScope lock(&crit_); + PurgeOldStats(); + stats_.input_frame_rate = + static_cast<int>(input_frame_rate_tracker_.ComputeRate()); + return stats_; +} + +void SendStatisticsProxy::PurgeOldStats() { + int64_t old_stats_ms = clock_->TimeInMilliseconds() - kStatsTimeoutMs; + for (std::map<uint32_t, VideoSendStream::StreamStats>::iterator it = + stats_.substreams.begin(); + it != stats_.substreams.end(); ++it) { + uint32_t ssrc = it->first; + if (update_times_[ssrc].resolution_update_ms <= old_stats_ms) { + it->second.width = 0; + it->second.height = 0; + } + } +} + +VideoSendStream::StreamStats* SendStatisticsProxy::GetStatsEntry( + uint32_t ssrc) { + std::map<uint32_t, VideoSendStream::StreamStats>::iterator it = + stats_.substreams.find(ssrc); + if (it != stats_.substreams.end()) + return &it->second; + + if (std::find(config_.rtp.ssrcs.begin(), config_.rtp.ssrcs.end(), ssrc) == + config_.rtp.ssrcs.end() && + std::find(config_.rtp.rtx.ssrcs.begin(), + config_.rtp.rtx.ssrcs.end(), + ssrc) == config_.rtp.rtx.ssrcs.end()) { + return nullptr; + } + + return &stats_.substreams[ssrc]; // Insert new entry and return ptr. +} + +void SendStatisticsProxy::OnInactiveSsrc(uint32_t ssrc) { + rtc::CritScope lock(&crit_); + VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc); + if (stats == nullptr) + return; + + stats->total_bitrate_bps = 0; + stats->retransmit_bitrate_bps = 0; + stats->height = 0; + stats->width = 0; +} + +void SendStatisticsProxy::OnSetRates(uint32_t bitrate_bps, int framerate) { + rtc::CritScope lock(&crit_); + stats_.target_media_bitrate_bps = bitrate_bps; +} + +void SendStatisticsProxy::OnSendEncodedImage( + const EncodedImage& encoded_image, + const RTPVideoHeader* rtp_video_header) { + size_t simulcast_idx = + rtp_video_header != nullptr ? rtp_video_header->simulcastIdx : 0; + if (simulcast_idx >= config_.rtp.ssrcs.size()) { + LOG(LS_ERROR) << "Encoded image outside simulcast range (" << simulcast_idx + << " >= " << config_.rtp.ssrcs.size() << ")."; + return; + } + uint32_t ssrc = config_.rtp.ssrcs[simulcast_idx]; + + rtc::CritScope lock(&crit_); + VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc); + if (stats == nullptr) + return; + + stats->width = encoded_image._encodedWidth; + stats->height = encoded_image._encodedHeight; + update_times_[ssrc].resolution_update_ms = clock_->TimeInMilliseconds(); + + key_frame_counter_.Add(encoded_image._frameType == kVideoFrameKey); + + if (encoded_image.adapt_reason_.quality_resolution_downscales != -1) { + bool downscaled = + encoded_image.adapt_reason_.quality_resolution_downscales > 0; + quality_limited_frame_counter_.Add(downscaled); + if (downscaled) { + quality_downscales_counter_.Add( + encoded_image.adapt_reason_.quality_resolution_downscales); + } + } + if (encoded_image.adapt_reason_.bw_resolutions_disabled != -1) { + bool bw_limited = encoded_image.adapt_reason_.bw_resolutions_disabled > 0; + bw_limited_frame_counter_.Add(bw_limited); + if (bw_limited) { + bw_resolutions_disabled_counter_.Add( + encoded_image.adapt_reason_.bw_resolutions_disabled); + } + } + + // TODO(asapersson): This is incorrect if simulcast layers are encoded on + // different threads and there is no guarantee that one frame of all layers + // are encoded before the next start. + if (last_sent_frame_timestamp_ > 0 && + encoded_image._timeStamp != last_sent_frame_timestamp_) { + sent_frame_rate_tracker_.AddSamples(1); + sent_width_counter_.Add(max_sent_width_per_timestamp_); + sent_height_counter_.Add(max_sent_height_per_timestamp_); + max_sent_width_per_timestamp_ = 0; + max_sent_height_per_timestamp_ = 0; + } + last_sent_frame_timestamp_ = encoded_image._timeStamp; + max_sent_width_per_timestamp_ = + std::max(max_sent_width_per_timestamp_, + static_cast<int>(encoded_image._encodedWidth)); + max_sent_height_per_timestamp_ = + std::max(max_sent_height_per_timestamp_, + static_cast<int>(encoded_image._encodedHeight)); +} + +void SendStatisticsProxy::OnIncomingFrame(int width, int height) { + rtc::CritScope lock(&crit_); + input_frame_rate_tracker_.AddSamples(1); + input_width_counter_.Add(width); + input_height_counter_.Add(height); +} + +void SendStatisticsProxy::OnEncodedFrame(int encode_time_ms) { + rtc::CritScope lock(&crit_); + encode_time_counter_.Add(encode_time_ms); +} + +void SendStatisticsProxy::RtcpPacketTypesCounterUpdated( + uint32_t ssrc, + const RtcpPacketTypeCounter& packet_counter) { + rtc::CritScope lock(&crit_); + VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc); + if (stats == nullptr) + return; + + stats->rtcp_packet_type_counts = packet_counter; +} + +void SendStatisticsProxy::StatisticsUpdated(const RtcpStatistics& statistics, + uint32_t ssrc) { + rtc::CritScope lock(&crit_); + VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc); + if (stats == nullptr) + return; + + stats->rtcp_stats = statistics; +} + +void SendStatisticsProxy::CNameChanged(const char* cname, uint32_t ssrc) { +} + +void SendStatisticsProxy::DataCountersUpdated( + const StreamDataCounters& counters, + uint32_t ssrc) { + rtc::CritScope lock(&crit_); + VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc); + RTC_DCHECK(stats != nullptr) + << "DataCountersUpdated reported for unknown ssrc: " << ssrc; + + stats->rtp_stats = counters; +} + +void SendStatisticsProxy::Notify(const BitrateStatistics& total_stats, + const BitrateStatistics& retransmit_stats, + uint32_t ssrc) { + rtc::CritScope lock(&crit_); + VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc); + if (stats == nullptr) + return; + + stats->total_bitrate_bps = total_stats.bitrate_bps; + stats->retransmit_bitrate_bps = retransmit_stats.bitrate_bps; +} + +void SendStatisticsProxy::FrameCountUpdated(const FrameCounts& frame_counts, + uint32_t ssrc) { + rtc::CritScope lock(&crit_); + VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc); + if (stats == nullptr) + return; + + stats->frame_counts = frame_counts; +} + +void SendStatisticsProxy::SendSideDelayUpdated(int avg_delay_ms, + int max_delay_ms, + uint32_t ssrc) { + rtc::CritScope lock(&crit_); + VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc); + if (stats == nullptr) + return; + stats->avg_delay_ms = avg_delay_ms; + stats->max_delay_ms = max_delay_ms; +} + +void SendStatisticsProxy::SampleCounter::Add(int sample) { + sum += sample; + ++num_samples; +} + +int SendStatisticsProxy::SampleCounter::Avg(int min_required_samples) const { + if (num_samples < min_required_samples || num_samples == 0) + return -1; + return (sum + (num_samples / 2)) / num_samples; +} + +void SendStatisticsProxy::BoolSampleCounter::Add(bool sample) { + if (sample) + ++sum; + ++num_samples; +} + +int SendStatisticsProxy::BoolSampleCounter::Percent( + int min_required_samples) const { + return Fraction(min_required_samples, 100.0f); +} + +int SendStatisticsProxy::BoolSampleCounter::Permille( + int min_required_samples) const { + return Fraction(min_required_samples, 1000.0f); +} + +int SendStatisticsProxy::BoolSampleCounter::Fraction( + int min_required_samples, float multiplier) const { + if (num_samples < min_required_samples || num_samples == 0) + return -1; + return static_cast<int>((sum * multiplier / num_samples) + 0.5f); +} + +} // namespace webrtc |