| Index: webrtc/video/receive_statistics_proxy.cc
|
| diff --git a/webrtc/video/receive_statistics_proxy.cc b/webrtc/video/receive_statistics_proxy.cc
|
| index 4f231694aa1b5efd7c7daa82f497858fd7153924..6e58e1e86b6418892ab80999227582b0dbd5f89b 100644
|
| --- a/webrtc/video/receive_statistics_proxy.cc
|
| +++ b/webrtc/video/receive_statistics_proxy.cc
|
| @@ -12,13 +12,14 @@
|
|
|
| #include <algorithm>
|
| #include <cmath>
|
| +#include <sstream>
|
| #include <utility>
|
|
|
| +#include "webrtc/modules/pacing/alr_detector.h"
|
| #include "webrtc/modules/video_coding/include/video_codec_interface.h"
|
| #include "webrtc/rtc_base/checks.h"
|
| #include "webrtc/rtc_base/logging.h"
|
| #include "webrtc/system_wrappers/include/clock.h"
|
| -#include "webrtc/system_wrappers/include/field_trial.h"
|
| #include "webrtc/system_wrappers/include/metrics.h"
|
|
|
| namespace webrtc {
|
| @@ -48,6 +49,28 @@ const int kMovingMaxWindowMs = 10000;
|
|
|
| // How large window we use to calculate the framerate/bitrate.
|
| const int kRateStatisticsWindowSizeMs = 1000;
|
| +
|
| +std::string UmaPrefixForContentType(VideoContentType content_type) {
|
| + std::stringstream ss;
|
| + ss << "WebRTC.Video";
|
| + if (videocontenttypehelpers::IsScreenshare(content_type)) {
|
| + ss << ".Screenshare";
|
| + }
|
| + return ss.str();
|
| +}
|
| +
|
| +std::string UmaSuffixForContentType(VideoContentType content_type) {
|
| + std::stringstream ss;
|
| + int simulcast_id = videocontenttypehelpers::GetSimulcastId(content_type);
|
| + if (simulcast_id > 0) {
|
| + ss << ".S" << simulcast_id - 1;
|
| + }
|
| + int experiment_id = videocontenttypehelpers::GetExperimentId(content_type);
|
| + if (experiment_id > 0) {
|
| + ss << ".ExperimentGroup" << experiment_id - 1;
|
| + }
|
| + return ss.str();
|
| +}
|
| } // namespace
|
|
|
| ReceiveStatisticsProxy::ReceiveStatisticsProxy(
|
| @@ -77,10 +100,6 @@ ReceiveStatisticsProxy::ReceiveStatisticsProxy(
|
| render_fps_tracker_(100, 10u),
|
| render_pixel_tracker_(100, 10u),
|
| total_byte_tracker_(100, 10u), // bucket_interval_ms, bucket_count
|
| - e2e_delay_max_ms_video_(-1),
|
| - e2e_delay_max_ms_screenshare_(-1),
|
| - interframe_delay_max_ms_video_(-1),
|
| - interframe_delay_max_ms_screenshare_(-1),
|
| interframe_delay_max_moving_(kMovingMaxWindowMs),
|
| freq_offset_counter_(clock, nullptr, kFreqOffsetProcessIntervalMs),
|
| first_report_block_time_ms_(-1),
|
| @@ -99,9 +118,14 @@ ReceiveStatisticsProxy::~ReceiveStatisticsProxy() {
|
| }
|
|
|
| void ReceiveStatisticsProxy::UpdateHistograms() {
|
| - RTC_HISTOGRAM_COUNTS_100000(
|
| - "WebRTC.Video.ReceiveStreamLifetimeInSeconds",
|
| - (clock_->TimeInMilliseconds() - start_ms_) / 1000);
|
| + int stream_duration_sec = (clock_->TimeInMilliseconds() - start_ms_) / 1000;
|
| + if (stats_.frame_counts.key_frames > 0 ||
|
| + stats_.frame_counts.delta_frames > 0) {
|
| + RTC_HISTOGRAM_COUNTS_100000("WebRTC.Video.ReceiveStreamLifetimeInSeconds",
|
| + stream_duration_sec);
|
| + LOG(LS_INFO) << "WebRTC.Video.ReceiveStreamLifetimeInSeconds "
|
| + << stream_duration_sec;
|
| + }
|
|
|
| if (first_report_block_time_ms_ != -1 &&
|
| ((clock_->TimeInMilliseconds() - first_report_block_time_ms_) / 1000) >=
|
| @@ -124,14 +148,7 @@ void ReceiveStatisticsProxy::UpdateHistograms() {
|
| "WebRTC.Video.RenderSqrtPixelsPerSecond",
|
| round(render_pixel_tracker_.ComputeTotalRate()));
|
| }
|
| - int width = render_width_counter_.Avg(kMinRequiredSamples);
|
| - int height = render_height_counter_.Avg(kMinRequiredSamples);
|
| - if (width != -1) {
|
| - RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.ReceivedWidthInPixels", width);
|
| - RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.ReceivedHeightInPixels", height);
|
| - LOG(LS_INFO) << "WebRTC.Video.ReceivedWidthInPixels " << width;
|
| - LOG(LS_INFO) << "WebRTC.Video.ReceivedHeightInPixels " << height;
|
| - }
|
| +
|
| int sync_offset_ms = sync_offset_counter_.Avg(kMinRequiredSamples);
|
| if (sync_offset_ms != -1) {
|
| RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.AVSyncOffsetInMs", sync_offset_ms);
|
| @@ -189,52 +206,131 @@ void ReceiveStatisticsProxy::UpdateHistograms() {
|
| if (delay_ms != -1)
|
| RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.OnewayDelayInMs", delay_ms);
|
|
|
| - int e2e_delay_ms_video = e2e_delay_counter_video_.Avg(kMinRequiredSamples);
|
| - if (e2e_delay_ms_video != -1) {
|
| - RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.EndToEndDelayInMs",
|
| - e2e_delay_ms_video);
|
| - LOG(LS_INFO) << "WebRTC.Video.EndToEndDelayInMs " << e2e_delay_ms_video;
|
| - }
|
| + // Aggregate content_specific_stats_ by removing experiment or simulcast
|
| + // information;
|
| + std::map<VideoContentType, ContentSpecificStats> aggregated_stats;
|
| + for (auto it : content_specific_stats_) {
|
| + // Calculate simulcast specific metrics (".S0" ... ".S2" suffixes).
|
| + VideoContentType content_type = it.first;
|
| + if (videocontenttypehelpers::GetSimulcastId(content_type) > 0) {
|
| + // Aggregate on experiment id.
|
| + videocontenttypehelpers::SetExperimentId(&content_type, 0);
|
| + aggregated_stats[content_type].Add(it.second);
|
| + }
|
| + // Calculate experiment specific metrics (".ExperimentGroup[0-7]" suffixes).
|
| + content_type = it.first;
|
| + if (videocontenttypehelpers::GetExperimentId(content_type) > 0) {
|
| + // Aggregate on simulcast id.
|
| + videocontenttypehelpers::SetSimulcastId(&content_type, 0);
|
| + aggregated_stats[content_type].Add(it.second);
|
| + }
|
| + // Calculate aggregated metrics (no suffixes. Aggregated on everything).
|
| + content_type = it.first;
|
| + videocontenttypehelpers::SetSimulcastId(&content_type, 0);
|
| + videocontenttypehelpers::SetExperimentId(&content_type, 0);
|
| + aggregated_stats[content_type].Add(it.second);
|
| + }
|
| +
|
| + for (auto it : aggregated_stats) {
|
| + // For the metric Foo we report the following slices:
|
| + // WebRTC.Video.Foo,
|
| + // WebRTC.Video.Screenshare.Foo,
|
| + // WebRTC.Video.Foo.S[0-3],
|
| + // WebRTC.Video.Foo.ExperimentGroup[0-7],
|
| + // WebRTC.Video.Screenshare.Foo.S[0-3],
|
| + // WebRTC.Video.Screenshare.Foo.ExperimentGroup[0-7].
|
| + auto content_type = it.first;
|
| + auto stats = it.second;
|
| + std::string uma_prefix = UmaPrefixForContentType(content_type);
|
| + std::string uma_suffix = UmaSuffixForContentType(content_type);
|
| + // Metrics can be sliced on either simulcast id or experiment id but not
|
| + // both.
|
| + RTC_DCHECK(videocontenttypehelpers::GetExperimentId(content_type) == 0 ||
|
| + videocontenttypehelpers::GetSimulcastId(content_type) == 0);
|
| +
|
| + int e2e_delay_ms = stats.e2e_delay_counter.Avg(kMinRequiredSamples);
|
| + if (e2e_delay_ms != -1) {
|
| + RTC_HISTOGRAM_COUNTS_SPARSE_10000(
|
| + uma_prefix + ".EndToEndDelayInMs" + uma_suffix, e2e_delay_ms);
|
| + LOG(LS_INFO) << uma_prefix << ".EndToEndDelayInMs" << uma_suffix << " "
|
| + << e2e_delay_ms;
|
| + }
|
| + int e2e_delay_max_ms = stats.e2e_delay_counter.Max();
|
| + if (e2e_delay_max_ms != -1 && e2e_delay_ms != -1) {
|
| + RTC_HISTOGRAM_COUNTS_SPARSE_100000(
|
| + uma_prefix + ".EndToEndDelayMaxInMs" + uma_suffix, e2e_delay_max_ms);
|
| + LOG(LS_INFO) << uma_prefix << ".EndToEndDelayMaxInMs" << uma_suffix << " "
|
| + << e2e_delay_max_ms;
|
| + }
|
| + int interframe_delay_ms =
|
| + stats.interframe_delay_counter.Avg(kMinRequiredSamples);
|
| + if (interframe_delay_ms != -1) {
|
| + RTC_HISTOGRAM_COUNTS_SPARSE_10000(
|
| + uma_prefix + ".InterframeDelayInMs" + uma_suffix,
|
| + interframe_delay_ms);
|
| + LOG(LS_INFO) << uma_prefix << ".InterframeDelayInMs" << uma_suffix << " "
|
| + << interframe_delay_ms;
|
| + }
|
| + int interframe_delay_max_ms = stats.interframe_delay_counter.Max();
|
| + if (interframe_delay_max_ms != -1 && interframe_delay_ms != -1) {
|
| + RTC_HISTOGRAM_COUNTS_SPARSE_10000(
|
| + uma_prefix + ".InterframeDelayMaxInMs" + uma_suffix,
|
| + interframe_delay_max_ms);
|
| + LOG(LS_INFO) << uma_prefix << ".InterframeDelayMaxInMs" << uma_suffix
|
| + << " " << interframe_delay_max_ms;
|
| + }
|
|
|
| - int e2e_delay_ms_screenshare =
|
| - e2e_delay_counter_screenshare_.Avg(kMinRequiredSamples);
|
| - if (e2e_delay_ms_screenshare != -1) {
|
| - RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.Screenshare.EndToEndDelayInMs",
|
| - e2e_delay_ms_screenshare);
|
| - }
|
| + int width = stats.received_width.Avg(kMinRequiredSamples);
|
| + if (width != -1) {
|
| + RTC_HISTOGRAM_COUNTS_SPARSE_10000(
|
| + uma_prefix + ".ReceivedWidthInPixels" + uma_suffix, width);
|
| + LOG(LS_INFO) << uma_prefix << ".ReceivedWidthInPixels" << uma_suffix
|
| + << " " << width;
|
| + }
|
|
|
| - int e2e_delay_max_ms_video = e2e_delay_max_ms_video_;
|
| - if (e2e_delay_max_ms_video != -1) {
|
| - RTC_HISTOGRAM_COUNTS_100000("WebRTC.Video.EndToEndDelayMaxInMs",
|
| - e2e_delay_max_ms_video);
|
| - }
|
| + int height = stats.received_height.Avg(kMinRequiredSamples);
|
| + if (height != -1) {
|
| + RTC_HISTOGRAM_COUNTS_SPARSE_10000(
|
| + uma_prefix + ".ReceivedHeightInPixels" + uma_suffix, height);
|
| + LOG(LS_INFO) << uma_prefix << ".ReceivedHeightInPixels" << uma_suffix
|
| + << " " << height;
|
| + }
|
|
|
| - int e2e_delay_max_ms_screenshare = e2e_delay_max_ms_screenshare_;
|
| - if (e2e_delay_max_ms_screenshare != -1) {
|
| - RTC_HISTOGRAM_COUNTS_100000("WebRTC.Video.Screenshare.EndToEndDelayMaxInMs",
|
| - e2e_delay_max_ms_screenshare);
|
| - }
|
| + if (content_type != VideoContentType::UNSPECIFIED) {
|
| + // Don't report these 3 metrics unsliced, as more precise variants
|
| + // are reported separately in this method.
|
| + float flow_duration_sec = stats.flow_duration_ms / 1000.0;
|
| + if (flow_duration_sec >= metrics::kMinRunTimeInSeconds) {
|
| + int media_bitrate_kbps = static_cast<int>(stats.total_media_bytes * 8 /
|
| + flow_duration_sec / 1000);
|
| + RTC_HISTOGRAM_COUNTS_SPARSE_10000(
|
| + uma_prefix + ".MediaBitrateReceivedInKbps" + uma_suffix,
|
| + media_bitrate_kbps);
|
| + LOG(LS_INFO) << uma_prefix << ".MediaBitrateReceivedInKbps"
|
| + << uma_suffix << " " << media_bitrate_kbps;
|
| + }
|
|
|
| - int interframe_delay_ms_screenshare =
|
| - interframe_delay_counter_screenshare_.Avg(kMinRequiredSamples);
|
| - if (interframe_delay_ms_screenshare != -1) {
|
| - RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.Screenshare.InterframeDelayInMs",
|
| - interframe_delay_ms_screenshare);
|
| - RTC_DCHECK_GE(interframe_delay_max_ms_screenshare_,
|
| - interframe_delay_ms_screenshare);
|
| - RTC_HISTOGRAM_COUNTS_10000(
|
| - "WebRTC.Video.Screenshare.InterframeDelayMaxInMs",
|
| - interframe_delay_max_ms_screenshare_);
|
| - }
|
| + int num_total_frames =
|
| + stats.frame_counts.key_frames + stats.frame_counts.delta_frames;
|
| + if (num_total_frames >= kMinRequiredSamples) {
|
| + int num_key_frames = stats.frame_counts.key_frames;
|
| + int key_frames_permille =
|
| + (num_key_frames * 1000 + num_total_frames / 2) / num_total_frames;
|
| + RTC_HISTOGRAM_COUNTS_SPARSE_1000(
|
| + uma_prefix + ".KeyFramesReceivedInPermille" + uma_suffix,
|
| + key_frames_permille);
|
| + LOG(LS_INFO) << uma_prefix << ".KeyFramesReceivedInPermille"
|
| + << uma_suffix << " " << key_frames_permille;
|
| + }
|
|
|
| - int interframe_delay_ms_video =
|
| - interframe_delay_counter_video_.Avg(kMinRequiredSamples);
|
| - if (interframe_delay_ms_video != -1) {
|
| - RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.InterframeDelayInMs",
|
| - interframe_delay_ms_video);
|
| - RTC_DCHECK_GE(interframe_delay_max_ms_video_, interframe_delay_ms_video);
|
| - RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.InterframeDelayMaxInMs",
|
| - interframe_delay_max_ms_video_);
|
| + int qp = stats.qp_counter.Avg(kMinRequiredSamples);
|
| + if (qp != -1) {
|
| + RTC_HISTOGRAM_COUNTS_SPARSE_200(
|
| + uma_prefix + ".Decoded.Vp8.Qp" + uma_suffix, qp);
|
| + LOG(LS_INFO) << uma_prefix << ".Decoded.Vp8.Qp" << uma_suffix << " "
|
| + << qp;
|
| + }
|
| + }
|
| }
|
|
|
| StreamDataCounters rtp = stats_.rtp_stats;
|
| @@ -250,9 +346,12 @@ void ReceiveStatisticsProxy::UpdateHistograms() {
|
| "WebRTC.Video.BitrateReceivedInKbps",
|
| static_cast<int>(rtp_rtx.transmitted.TotalBytes() * 8 / elapsed_sec /
|
| 1000));
|
| - RTC_HISTOGRAM_COUNTS_10000(
|
| - "WebRTC.Video.MediaBitrateReceivedInKbps",
|
| - static_cast<int>(rtp.MediaPayloadBytes() * 8 / elapsed_sec / 1000));
|
| + int media_bitrate_kbs =
|
| + static_cast<int>(rtp.MediaPayloadBytes() * 8 / elapsed_sec / 1000);
|
| + RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.MediaBitrateReceivedInKbps",
|
| + media_bitrate_kbs);
|
| + LOG(LS_INFO) << "WebRTC.Video.MediaBitrateReceivedInKbps "
|
| + << media_bitrate_kbs;
|
| RTC_HISTOGRAM_COUNTS_10000(
|
| "WebRTC.Video.PaddingBitrateReceivedInKbps",
|
| static_cast<int>(rtp_rtx.transmitted.padding_bytes * 8 / elapsed_sec /
|
| @@ -529,6 +628,9 @@ void ReceiveStatisticsProxy::OnDecodedFrame(rtc::Optional<uint8_t> qp,
|
| uint64_t now = clock_->TimeInMilliseconds();
|
|
|
| rtc::CritScope lock(&crit_);
|
| +
|
| + ContentSpecificStats* content_specific_stats =
|
| + &content_specific_stats_[content_type];
|
| ++stats_.frames_decoded;
|
| if (qp) {
|
| if (!stats_.qp_sum) {
|
| @@ -540,6 +642,7 @@ void ReceiveStatisticsProxy::OnDecodedFrame(rtc::Optional<uint8_t> qp,
|
| stats_.qp_sum = rtc::Optional<uint64_t>(0);
|
| }
|
| *stats_.qp_sum += *qp;
|
| + content_specific_stats->qp_counter.Add(*qp);
|
| } else if (stats_.qp_sum) {
|
| LOG(LS_WARNING)
|
| << "QP sum was already set and no QP was given for a frame.";
|
| @@ -551,17 +654,8 @@ void ReceiveStatisticsProxy::OnDecodedFrame(rtc::Optional<uint8_t> qp,
|
| int64_t interframe_delay_ms = now - *last_decoded_frame_time_ms_;
|
| RTC_DCHECK_GE(interframe_delay_ms, 0);
|
| interframe_delay_max_moving_.Add(interframe_delay_ms, now);
|
| - if (last_content_type_ == VideoContentType::SCREENSHARE) {
|
| - interframe_delay_counter_screenshare_.Add(interframe_delay_ms);
|
| - if (interframe_delay_max_ms_screenshare_ < interframe_delay_ms) {
|
| - interframe_delay_max_ms_screenshare_ = interframe_delay_ms;
|
| - }
|
| - } else {
|
| - interframe_delay_counter_video_.Add(interframe_delay_ms);
|
| - if (interframe_delay_max_ms_video_ < interframe_delay_ms) {
|
| - interframe_delay_max_ms_video_ = interframe_delay_ms;
|
| - }
|
| - }
|
| + content_specific_stats->interframe_delay_counter.Add(interframe_delay_ms);
|
| + content_specific_stats->flow_duration_ms += interframe_delay_ms;
|
| }
|
| last_decoded_frame_time_ms_.emplace(now);
|
| }
|
| @@ -572,28 +666,22 @@ void ReceiveStatisticsProxy::OnRenderedFrame(const VideoFrame& frame) {
|
| RTC_DCHECK_GT(width, 0);
|
| RTC_DCHECK_GT(height, 0);
|
| uint64_t now = clock_->TimeInMilliseconds();
|
| -
|
| rtc::CritScope lock(&crit_);
|
| + ContentSpecificStats* content_specific_stats =
|
| + &content_specific_stats_[last_content_type_];
|
| renders_fps_estimator_.Update(1, now);
|
| ++stats_.frames_rendered;
|
| stats_.width = width;
|
| stats_.height = height;
|
| - render_width_counter_.Add(width);
|
| - render_height_counter_.Add(height);
|
| render_fps_tracker_.AddSamples(1);
|
| render_pixel_tracker_.AddSamples(sqrt(width * height));
|
| + content_specific_stats->received_width.Add(width);
|
| + content_specific_stats->received_height.Add(height);
|
|
|
| if (frame.ntp_time_ms() > 0) {
|
| int64_t delay_ms = clock_->CurrentNtpInMilliseconds() - frame.ntp_time_ms();
|
| if (delay_ms >= 0) {
|
| - if (last_content_type_ == VideoContentType::SCREENSHARE) {
|
| - e2e_delay_max_ms_screenshare_ =
|
| - std::max(delay_ms, e2e_delay_max_ms_screenshare_);
|
| - e2e_delay_counter_screenshare_.Add(delay_ms);
|
| - } else {
|
| - e2e_delay_max_ms_video_ = std::max(delay_ms, e2e_delay_max_ms_video_);
|
| - e2e_delay_counter_video_.Add(delay_ms);
|
| - }
|
| + content_specific_stats->e2e_delay_counter.Add(delay_ms);
|
| }
|
| }
|
| }
|
| @@ -618,12 +706,24 @@ void ReceiveStatisticsProxy::OnReceiveRatesUpdated(uint32_t bitRate,
|
| }
|
|
|
| void ReceiveStatisticsProxy::OnCompleteFrame(bool is_keyframe,
|
| - size_t size_bytes) {
|
| + size_t size_bytes,
|
| + VideoContentType content_type) {
|
| rtc::CritScope lock(&crit_);
|
| - if (is_keyframe)
|
| + if (is_keyframe) {
|
| ++stats_.frame_counts.key_frames;
|
| - else
|
| + } else {
|
| ++stats_.frame_counts.delta_frames;
|
| + }
|
| +
|
| + ContentSpecificStats* content_specific_stats =
|
| + &content_specific_stats_[content_type];
|
| +
|
| + content_specific_stats->total_media_bytes += size_bytes;
|
| + if (is_keyframe) {
|
| + ++content_specific_stats->frame_counts.key_frames;
|
| + } else {
|
| + ++content_specific_stats->frame_counts.delta_frames;
|
| + }
|
|
|
| int64_t now_ms = clock_->TimeInMilliseconds();
|
| frame_window_.insert(std::make_pair(now_ms, size_bytes));
|
| @@ -665,6 +765,16 @@ void ReceiveStatisticsProxy::OnStreamInactive() {
|
| void ReceiveStatisticsProxy::SampleCounter::Add(int sample) {
|
| sum += sample;
|
| ++num_samples;
|
| + if (!max || sample > *max) {
|
| + max.emplace(sample);
|
| + }
|
| +}
|
| +
|
| +void ReceiveStatisticsProxy::SampleCounter::Add(const SampleCounter& other) {
|
| + sum += other.sum;
|
| + num_samples += other.num_samples;
|
| + if (other.max && (!max || *max < *other.max))
|
| + max = other.max;
|
| }
|
|
|
| int ReceiveStatisticsProxy::SampleCounter::Avg(
|
| @@ -674,9 +784,14 @@ int ReceiveStatisticsProxy::SampleCounter::Avg(
|
| return static_cast<int>(sum / num_samples);
|
| }
|
|
|
| +int ReceiveStatisticsProxy::SampleCounter::Max() const {
|
| + return max.value_or(-1);
|
| +}
|
| +
|
| void ReceiveStatisticsProxy::SampleCounter::Reset() {
|
| num_samples = 0;
|
| sum = 0;
|
| + max.reset();
|
| }
|
|
|
| void ReceiveStatisticsProxy::OnRttUpdate(int64_t avg_rtt_ms,
|
| @@ -685,4 +800,17 @@ void ReceiveStatisticsProxy::OnRttUpdate(int64_t avg_rtt_ms,
|
| avg_rtt_ms_ = avg_rtt_ms;
|
| }
|
|
|
| +void ReceiveStatisticsProxy::ContentSpecificStats::Add(
|
| + const ContentSpecificStats& other) {
|
| + e2e_delay_counter.Add(other.e2e_delay_counter);
|
| + interframe_delay_counter.Add(other.interframe_delay_counter);
|
| + flow_duration_ms += other.flow_duration_ms;
|
| + total_media_bytes += other.total_media_bytes;
|
| + received_height.Add(other.received_height);
|
| + received_width.Add(other.received_width);
|
| + qp_counter.Add(other.qp_counter);
|
| + frame_counts.key_frames += other.frame_counts.key_frames;
|
| + frame_counts.delta_frames += other.frame_counts.delta_frames;
|
| +}
|
| +
|
| } // namespace webrtc
|
|
|