Implement RTCOutboundRtpStreamStats.totalEncodedBytesTarget.
This is a standardized metric: https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-totalencodedbytestarget We estimate the target frame size in bytes from the current encoder target bitrate and encoder framerate. We would expect that the average bytes produced by the encoder would over time match the average target, which is calculated by polling getStats() twice and dividing the delta totalEncodedBytesTarget with the delta framesEncoded. This is meant to make googTargetEncBitrate obsolete. Bug: webrtc:10446 Change-Id: Ib10ce236476a2f965582d5c536f419952926d4e6 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/137200 Reviewed-by: Stefan Holmer <stefan@webrtc.org> Commit-Queue: Henrik Boström <hbos@webrtc.org> Cr-Commit-Position: refs/heads/master@{#28022}
This commit is contained in:
parent
04f39242c2
commit
23aff9b737
@ -458,6 +458,7 @@ class RTC_EXPORT RTCOutboundRTPStreamStats final : public RTCRTPStreamStats {
|
||||
RTCStatsMember<double> target_bitrate;
|
||||
RTCStatsMember<uint32_t> frames_encoded;
|
||||
RTCStatsMember<double> total_encode_time;
|
||||
RTCStatsMember<uint64_t> total_encoded_bytes_target;
|
||||
// TODO(https://crbug.com/webrtc/10635): This is only implemented for video;
|
||||
// implement it for audio as well.
|
||||
RTCStatsMember<double> total_packet_send_delay;
|
||||
|
||||
@ -71,6 +71,8 @@ class VideoSendStream {
|
||||
uint32_t frames_encoded = 0;
|
||||
// https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-totalencodetime
|
||||
uint64_t total_encode_time_ms = 0;
|
||||
// https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-totalencodedbytestarget
|
||||
uint64_t total_encoded_bytes_target = 0;
|
||||
uint32_t frames_dropped_by_capturer = 0;
|
||||
uint32_t frames_dropped_by_encoder_queue = 0;
|
||||
uint32_t frames_dropped_by_rate_limiter = 0;
|
||||
|
||||
@ -551,6 +551,8 @@ struct VideoSenderInfo : public MediaSenderInfo {
|
||||
uint32_t frames_encoded = 0;
|
||||
// https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-totalencodetime
|
||||
uint64_t total_encode_time_ms = 0;
|
||||
// https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-totalencodedbytestarget
|
||||
uint64_t total_encoded_bytes_target = 0;
|
||||
uint64_t total_packet_send_delay_ms = 0;
|
||||
bool has_entered_low_resolution = false;
|
||||
absl::optional<uint64_t> qp_sum;
|
||||
|
||||
@ -2261,6 +2261,7 @@ VideoSenderInfo WebRtcVideoChannel::WebRtcVideoSendStream::GetVideoSenderInfo(
|
||||
info.encode_usage_percent = stats.encode_usage_percent;
|
||||
info.frames_encoded = stats.frames_encoded;
|
||||
info.total_encode_time_ms = stats.total_encode_time_ms;
|
||||
info.total_encoded_bytes_target = stats.total_encoded_bytes_target;
|
||||
info.qp_sum = stats.qp_sum;
|
||||
|
||||
info.nominal_bitrate = stats.media_bitrate_bps;
|
||||
|
||||
@ -347,6 +347,8 @@ void SetOutboundRTPStreamStatsFromVideoSenderInfo(
|
||||
outbound_video->total_encode_time =
|
||||
static_cast<double>(video_sender_info.total_encode_time_ms) /
|
||||
rtc::kNumMillisecsPerSec;
|
||||
outbound_video->total_encoded_bytes_target =
|
||||
video_sender_info.total_encoded_bytes_target;
|
||||
outbound_video->total_packet_send_delay =
|
||||
static_cast<double>(video_sender_info.total_packet_send_delay_ms) /
|
||||
rtc::kNumMillisecsPerSec;
|
||||
|
||||
@ -1849,6 +1849,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRTPStreamStats_Video) {
|
||||
video_media_info.senders[0].codec_payload_type = 42;
|
||||
video_media_info.senders[0].frames_encoded = 8;
|
||||
video_media_info.senders[0].total_encode_time_ms = 9000;
|
||||
video_media_info.senders[0].total_encoded_bytes_target = 1234;
|
||||
video_media_info.senders[0].total_packet_send_delay_ms = 10000;
|
||||
video_media_info.senders[0].qp_sum = absl::nullopt;
|
||||
video_media_info.senders[0].content_type = VideoContentType::UNSPECIFIED;
|
||||
@ -1891,6 +1892,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRTPStreamStats_Video) {
|
||||
expected_video.retransmitted_bytes_sent = 60;
|
||||
expected_video.frames_encoded = 8;
|
||||
expected_video.total_encode_time = 9.0;
|
||||
expected_video.total_encoded_bytes_target = 1234;
|
||||
expected_video.total_packet_send_delay = 10.0;
|
||||
// |expected_video.content_type| should be undefined.
|
||||
// |expected_video.qp_sum| should be undefined.
|
||||
|
||||
@ -793,6 +793,8 @@ class RTCStatsReportVerifier {
|
||||
verifier.TestMemberIsDefined(outbound_stream.frames_encoded);
|
||||
verifier.TestMemberIsNonNegative<double>(
|
||||
outbound_stream.total_encode_time);
|
||||
verifier.TestMemberIsNonNegative<uint64_t>(
|
||||
outbound_stream.total_encoded_bytes_target);
|
||||
verifier.TestMemberIsNonNegative<double>(
|
||||
outbound_stream.total_packet_send_delay);
|
||||
// The integration test is not set up to test screen share; don't require
|
||||
@ -801,6 +803,8 @@ class RTCStatsReportVerifier {
|
||||
} else {
|
||||
verifier.TestMemberIsUndefined(outbound_stream.frames_encoded);
|
||||
verifier.TestMemberIsUndefined(outbound_stream.total_encode_time);
|
||||
verifier.TestMemberIsUndefined(
|
||||
outbound_stream.total_encoded_bytes_target);
|
||||
// TODO(https://crbug.com/webrtc/10635): Implement for audio as well.
|
||||
verifier.TestMemberIsUndefined(outbound_stream.total_packet_send_delay);
|
||||
verifier.TestMemberIsUndefined(outbound_stream.content_type);
|
||||
|
||||
@ -675,6 +675,7 @@ WEBRTC_RTCSTATS_IMPL(
|
||||
&target_bitrate,
|
||||
&frames_encoded,
|
||||
&total_encode_time,
|
||||
&total_encoded_bytes_target,
|
||||
&total_packet_send_delay,
|
||||
&content_type)
|
||||
// clang-format on
|
||||
@ -693,6 +694,7 @@ RTCOutboundRTPStreamStats::RTCOutboundRTPStreamStats(std::string&& id,
|
||||
target_bitrate("targetBitrate"),
|
||||
frames_encoded("framesEncoded"),
|
||||
total_encode_time("totalEncodeTime"),
|
||||
total_encoded_bytes_target("totalEncodedBytesTarget"),
|
||||
total_packet_send_delay("totalPacketSendDelay"),
|
||||
content_type("contentType") {}
|
||||
|
||||
@ -706,6 +708,7 @@ RTCOutboundRTPStreamStats::RTCOutboundRTPStreamStats(
|
||||
target_bitrate(other.target_bitrate),
|
||||
frames_encoded(other.frames_encoded),
|
||||
total_encode_time(other.total_encode_time),
|
||||
total_encoded_bytes_target(other.total_encoded_bytes_target),
|
||||
total_packet_send_delay(other.total_packet_send_delay),
|
||||
content_type(other.content_type) {}
|
||||
|
||||
|
||||
@ -548,6 +548,7 @@ if (rtc_include_tests) {
|
||||
"../api/task_queue:default_task_queue_factory",
|
||||
"../api/test/video:function_video_factory",
|
||||
"../api/units:data_rate",
|
||||
"../api/units:timestamp",
|
||||
"../api/video:builtin_video_bitrate_allocator_factory",
|
||||
"../api/video:encoded_image",
|
||||
"../api/video:video_bitrate_allocation",
|
||||
|
||||
@ -896,6 +896,18 @@ void SendStatisticsProxy::OnSendEncodedImage(
|
||||
|
||||
rtc::CritScope lock(&crit_);
|
||||
++stats_.frames_encoded;
|
||||
// The current encode frame rate is based on previously encoded frames.
|
||||
double encode_frame_rate = encoded_frame_rate_tracker_.ComputeRate();
|
||||
// We assume that less than 1 FPS is not a trustworthy estimate - perhaps we
|
||||
// just started encoding for the first time or after a pause. Assuming frame
|
||||
// rate is at least 1 FPS is conservative to avoid too large increments.
|
||||
if (encode_frame_rate < 1.0)
|
||||
encode_frame_rate = 1.0;
|
||||
double target_frame_size_bytes =
|
||||
stats_.target_media_bitrate_bps / (8.0 * encode_frame_rate);
|
||||
// |stats_.target_media_bitrate_bps| is set in
|
||||
// SendStatisticsProxy::OnSetEncoderTargetRate.
|
||||
stats_.total_encoded_bytes_target += round(target_frame_size_bytes);
|
||||
if (codec_info) {
|
||||
UpdateEncoderFallbackStats(
|
||||
codec_info, encoded_image._encodedWidth * encoded_image._encodedHeight,
|
||||
|
||||
@ -17,6 +17,8 @@
|
||||
#include <vector>
|
||||
|
||||
#include "absl/algorithm/container.h"
|
||||
#include "api/units/timestamp.h"
|
||||
#include "rtc_base/fake_clock.h"
|
||||
#include "system_wrappers/include/metrics.h"
|
||||
#include "test/field_trial.h"
|
||||
#include "test/gtest.h"
|
||||
@ -366,6 +368,56 @@ TEST_F(SendStatisticsProxyTest, OnSendEncodedImageWithoutQpQpSumWontExist) {
|
||||
EXPECT_EQ(absl::nullopt, statistics_proxy_->GetStats().qp_sum);
|
||||
}
|
||||
|
||||
TEST_F(SendStatisticsProxyTest, TotalEncodedBytesTargetFirstFrame) {
|
||||
const uint32_t kTargetBytesPerSecond = 100000;
|
||||
statistics_proxy_->OnSetEncoderTargetRate(kTargetBytesPerSecond * 8);
|
||||
EXPECT_EQ(0u, statistics_proxy_->GetStats().total_encoded_bytes_target);
|
||||
|
||||
EncodedImage encoded_image;
|
||||
statistics_proxy_->OnSendEncodedImage(encoded_image, nullptr);
|
||||
// On the first frame we don't know the frame rate yet, calculation yields
|
||||
// zero. Our estimate assumes at least 1 FPS, so we expect the frame size to
|
||||
// increment by a full |kTargetBytesPerSecond|.
|
||||
EXPECT_EQ(kTargetBytesPerSecond,
|
||||
statistics_proxy_->GetStats().total_encoded_bytes_target);
|
||||
}
|
||||
|
||||
TEST_F(SendStatisticsProxyTest,
|
||||
TotalEncodedBytesTargetIncrementsBasedOnFrameRate) {
|
||||
const uint32_t kTargetBytesPerSecond = 100000;
|
||||
const int kInterframeDelayMs = 100;
|
||||
|
||||
// SendStatisticsProxy uses a RateTracker internally. SendStatisticsProxy uses
|
||||
// |fake_clock_| for testing, but the RateTracker relies on a global clock.
|
||||
// This test relies on rtc::ScopedFakeClock to synchronize these two clocks.
|
||||
// TODO(https://crbug.com/webrtc/10640): When the RateTracker uses a Clock
|
||||
// this test can stop relying on rtc::ScopedFakeClock.
|
||||
rtc::ScopedFakeClock fake_global_clock;
|
||||
fake_global_clock.SetTime(Timestamp::ms(fake_clock_.TimeInMilliseconds()));
|
||||
|
||||
statistics_proxy_->OnSetEncoderTargetRate(kTargetBytesPerSecond * 8);
|
||||
EncodedImage encoded_image;
|
||||
|
||||
// First frame
|
||||
statistics_proxy_->OnSendEncodedImage(encoded_image, nullptr);
|
||||
uint64_t first_total_encoded_bytes_target =
|
||||
statistics_proxy_->GetStats().total_encoded_bytes_target;
|
||||
// Second frame
|
||||
fake_clock_.AdvanceTimeMilliseconds(kInterframeDelayMs);
|
||||
fake_global_clock.SetTime(Timestamp::ms(fake_clock_.TimeInMilliseconds()));
|
||||
encoded_image.SetTimestamp(encoded_image.Timestamp() +
|
||||
90 * kInterframeDelayMs);
|
||||
statistics_proxy_->OnSendEncodedImage(encoded_image, nullptr);
|
||||
|
||||
auto stats = statistics_proxy_->GetStats();
|
||||
// By the time the second frame arrives, one frame has previously arrived
|
||||
// during a |kInterframeDelayMs| interval. The estimated encode frame rate at
|
||||
// the second frame's arrival should be 10 FPS.
|
||||
uint64_t delta_encoded_bytes_target =
|
||||
stats.total_encoded_bytes_target - first_total_encoded_bytes_target;
|
||||
EXPECT_EQ(kTargetBytesPerSecond / 10, delta_encoded_bytes_target);
|
||||
}
|
||||
|
||||
TEST_F(SendStatisticsProxyTest, GetCpuAdaptationStats) {
|
||||
SendStatisticsProxy::AdaptationSteps cpu_counts;
|
||||
SendStatisticsProxy::AdaptationSteps quality_counts;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user