diff --git a/video/send_statistics_proxy.cc b/video/send_statistics_proxy.cc index d3e8774970..efdf03fa14 100644 --- a/video/send_statistics_proxy.cc +++ b/video/send_statistics_proxy.cc @@ -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(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 downscales = adaptation_limitations_.MaskedQualityCounts().resolution_adaptations; diff --git a/video/send_statistics_proxy.h b/video/send_statistics_proxy.h index bfb221f65c..c38488dd84 100644 --- a/video/send_statistics_proxy.h +++ b/video/send_statistics_proxy.h @@ -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> encoded_frame_rate_trackers_ RTC_GUARDED_BY(mutex_); diff --git a/video/send_statistics_proxy_unittest.cc b/video/send_statistics_proxy_unittest.cc index 01dbebdbd9..9c3b2c188b 100644 --- a/video/send_statistics_proxy_unittest.cc +++ b/video/send_statistics_proxy_unittest.cc @@ -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();