Update StreamStats.encode_frame_rate when GetStats is called.

Currently encode_frame_rate is updated (ComputeRate called) when a frame is encoded.

If a stream is stopped, encode_frame_rate will have an old value (the framerate at the time of the last encoded frame) instead of zero.

Bug: webrtc:13037
Change-Id: I1a2122df61e3e8187e57155dda71c0173cda4c5b
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/228220
Commit-Queue: Åsa Persson <asapersson@webrtc.org>
Reviewed-by: Erik Språng <sprang@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#34695}
This commit is contained in:
Åsa Persson
2021-08-10 08:56:36 +02:00
committed by WebRTC LUCI CQ
parent ecc46eff5b
commit 603e6e3ffc
3 changed files with 64 additions and 15 deletions

View File

@ -748,6 +748,14 @@ VideoSendStream::Stats SendStatisticsProxy::GetStats() {
stats_.media_bitrate_bps = media_byte_rate_tracker_.ComputeRate() * 8;
stats_.quality_limitation_durations_ms =
quality_limitation_reason_tracker_.DurationsMs();
for (auto& substream : stats_.substreams) {
uint32_t ssrc = substream.first;
if (encoded_frame_rate_trackers_.count(ssrc) > 0) {
substream.second.encode_frame_rate =
encoded_frame_rate_trackers_[ssrc]->ComputeRate();
}
}
return stats_;
}
@ -963,12 +971,12 @@ void SendStatisticsProxy::OnSendEncodedImage(
VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
if (!stats)
return;
if (encoded_frame_rate_trackers_.count(simulcast_idx) == 0) {
encoded_frame_rate_trackers_[simulcast_idx] =
if (encoded_frame_rate_trackers_.count(ssrc) == 0) {
encoded_frame_rate_trackers_[ssrc] =
std::make_unique<rtc::RateTracker>(kBucketSizeMs, kBucketCount);
}
stats->encode_frame_rate =
encoded_frame_rate_trackers_[simulcast_idx]->ComputeRate();
stats->frames_encoded++;
stats->total_encode_time_ms += encoded_image.timing_.encode_finish_ms -
encoded_image.timing_.encode_start_ms;
@ -1025,7 +1033,7 @@ void SendStatisticsProxy::OnSendEncodedImage(
// is_top_spatial_layer pertains only to SVC, will always be true for
// simulcast.
if (is_top_spatial_layer)
encoded_frame_rate_trackers_[simulcast_idx]->AddSamples(1);
encoded_frame_rate_trackers_[ssrc]->AddSamples(1);
absl::optional<int> downscales =
adaptation_limitations_.MaskedQualityCounts().resolution_adaptations;

View File

@ -284,6 +284,7 @@ class SendStatisticsProxy : public VideoStreamEncoderObserver,
RTC_GUARDED_BY(mutex_);
rtc::RateTracker media_byte_rate_tracker_ RTC_GUARDED_BY(mutex_);
rtc::RateTracker encoded_frame_rate_tracker_ RTC_GUARDED_BY(mutex_);
// Rate trackers mapped by ssrc.
std::map<uint32_t, std::unique_ptr<rtc::RateTracker>>
encoded_frame_rate_trackers_ RTC_GUARDED_BY(mutex_);

View File

@ -456,22 +456,24 @@ TEST_F(SendStatisticsProxyTest,
TEST_F(SendStatisticsProxyTest, EncodeFrameRateInSubStream) {
const int kInterframeDelayMs = 100;
auto ssrc = config_.rtp.ssrcs[0];
const auto ssrc = config_.rtp.ssrcs[0];
rtc::ScopedFakeClock fake_global_clock;
fake_global_clock.SetTime(
Timestamp::Millis(fake_clock_.TimeInMilliseconds()));
EncodedImage encoded_image;
// First frame
EncodedImage encoded_image;
statistics_proxy_->OnSendEncodedImage(encoded_image, nullptr);
// Second frame
fake_clock_.AdvanceTimeMilliseconds(kInterframeDelayMs);
fake_global_clock.SetTime(
Timestamp::Millis(fake_clock_.TimeInMilliseconds()));
// Second frame
encoded_image.SetTimestamp(encoded_image.Timestamp() +
90 * kInterframeDelayMs);
statistics_proxy_->OnSendEncodedImage(encoded_image, nullptr);
fake_clock_.AdvanceTimeMilliseconds(kInterframeDelayMs);
fake_global_clock.SetTime(
Timestamp::Millis(fake_clock_.TimeInMilliseconds()));
auto stats = statistics_proxy_->GetStats();
EXPECT_EQ(stats.substreams[ssrc].encode_frame_rate, 10);
@ -480,39 +482,74 @@ TEST_F(SendStatisticsProxyTest, EncodeFrameRateInSubStream) {
TEST_F(SendStatisticsProxyTest, EncodeFrameRateInSubStreamsVp8Simulcast) {
const int kInterframeDelayMs = 100;
rtc::ScopedFakeClock fake_global_clock;
fake_global_clock.SetTime(
Timestamp::Millis(fake_clock_.TimeInMilliseconds()));
EncodedImage encoded_image;
CodecSpecificInfo codec_info;
codec_info.codecType = kVideoCodecVP8;
for (int i = 0; i < 10; ++i) {
fake_clock_.AdvanceTimeMilliseconds(kInterframeDelayMs);
fake_global_clock.SetTime(
Timestamp::Millis(fake_clock_.TimeInMilliseconds()));
encoded_image.SetTimestamp(encoded_image.Timestamp() +
90 * kInterframeDelayMs);
encoded_image.SetSpatialIndex(0);
statistics_proxy_->OnSendEncodedImage(encoded_image, &codec_info);
encoded_image.SetSpatialIndex(1);
statistics_proxy_->OnSendEncodedImage(encoded_image, &codec_info);
fake_clock_.AdvanceTimeMilliseconds(kInterframeDelayMs);
fake_global_clock.SetTime(
Timestamp::Millis(fake_clock_.TimeInMilliseconds()));
}
VideoSendStream::Stats stats = statistics_proxy_->GetStats();
EXPECT_EQ(2u, stats.substreams.size());
EXPECT_EQ(stats.substreams[config_.rtp.ssrcs[0]].encode_frame_rate, 10);
EXPECT_EQ(stats.substreams[config_.rtp.ssrcs[1]].encode_frame_rate, 10);
// Stop encoding second stream, expect framerate to be zero.
for (int i = 0; i < 10; ++i) {
encoded_image.SetTimestamp(encoded_image.Timestamp() +
90 * kInterframeDelayMs);
encoded_image.SetSpatialIndex(0);
statistics_proxy_->OnSendEncodedImage(encoded_image, &codec_info);
fake_clock_.AdvanceTimeMilliseconds(kInterframeDelayMs);
fake_global_clock.SetTime(
Timestamp::Millis(fake_clock_.TimeInMilliseconds()));
}
stats = statistics_proxy_->GetStats();
EXPECT_EQ(2u, stats.substreams.size());
EXPECT_EQ(stats.substreams[config_.rtp.ssrcs[0]].encode_frame_rate, 10);
EXPECT_EQ(stats.substreams[config_.rtp.ssrcs[1]].encode_frame_rate, 0);
// Start encoding second stream.
for (int i = 0; i < 10; ++i) {
encoded_image.SetTimestamp(encoded_image.Timestamp() +
90 * kInterframeDelayMs);
encoded_image.SetSpatialIndex(0);
statistics_proxy_->OnSendEncodedImage(encoded_image, &codec_info);
encoded_image.SetSpatialIndex(1);
statistics_proxy_->OnSendEncodedImage(encoded_image, &codec_info);
fake_clock_.AdvanceTimeMilliseconds(kInterframeDelayMs);
fake_global_clock.SetTime(
Timestamp::Millis(fake_clock_.TimeInMilliseconds()));
}
stats = statistics_proxy_->GetStats();
EXPECT_EQ(2u, stats.substreams.size());
EXPECT_EQ(stats.substreams[config_.rtp.ssrcs[0]].encode_frame_rate, 10);
EXPECT_EQ(stats.substreams[config_.rtp.ssrcs[1]].encode_frame_rate, 10);
}
TEST_F(SendStatisticsProxyTest, EncodeFrameRateInSubStreamsVp9Svc) {
const int kInterframeDelayMs = 100;
rtc::ScopedFakeClock fake_global_clock;
fake_global_clock.SetTime(
Timestamp::Millis(fake_clock_.TimeInMilliseconds()));
EncodedImage encoded_image;
CodecSpecificInfo codec_info;
codec_info.codecType = kVideoCodecVP9;
for (int i = 0; i < 10; ++i) {
fake_clock_.AdvanceTimeMilliseconds(kInterframeDelayMs);
fake_global_clock.SetTime(
Timestamp::Millis(fake_clock_.TimeInMilliseconds()));
encoded_image.SetTimestamp(encoded_image.Timestamp() +
90 * kInterframeDelayMs);
encoded_image.SetSpatialIndex(0);
@ -521,6 +558,9 @@ TEST_F(SendStatisticsProxyTest, EncodeFrameRateInSubStreamsVp9Svc) {
encoded_image.SetSpatialIndex(1);
codec_info.end_of_picture = true;
statistics_proxy_->OnSendEncodedImage(encoded_image, &codec_info);
fake_clock_.AdvanceTimeMilliseconds(kInterframeDelayMs);
fake_global_clock.SetTime(
Timestamp::Millis(fake_clock_.TimeInMilliseconds()));
}
VideoSendStream::Stats stats = statistics_proxy_->GetStats();