diff --git a/api/video/builtin_video_bitrate_allocator_factory.cc b/api/video/builtin_video_bitrate_allocator_factory.cc index 81e24e38bf..16f2e69fa0 100644 --- a/api/video/builtin_video_bitrate_allocator_factory.cc +++ b/api/video/builtin_video_bitrate_allocator_factory.cc @@ -36,7 +36,7 @@ class BuiltinVideoBitrateAllocatorFactory case kVideoCodecVP9: // TODO(https://crbug.com/webrtc/14884): Update SvcRateAllocator to // support simulcast and use it for VP9/AV1 simulcast as well. - if (codec.numberOfSimulcastStreams <= 1) { + if (codec.IsSinglecastOrAllNonFirstLayersInactive()) { return std::make_unique(codec); } ABSL_FALLTHROUGH_INTENDED; diff --git a/api/video_codecs/video_codec.cc b/api/video_codecs/video_codec.cc index c6122d3f6a..f21c1a3401 100644 --- a/api/video_codecs/video_codec.cc +++ b/api/video_codecs/video_codec.cc @@ -152,4 +152,13 @@ void VideoCodec::SetFrameDropEnabled(bool enabled) { frame_drop_enabled_ = enabled; } +bool VideoCodec::IsSinglecastOrAllNonFirstLayersInactive() const { + for (int i = 1; i < numberOfSimulcastStreams; ++i) { + if (simulcastStream[i].active) { + return false; + } + } + return true; +} + } // namespace webrtc diff --git a/api/video_codecs/video_codec.h b/api/video_codecs/video_codec.h index 10bceda0d2..a4d19ca613 100644 --- a/api/video_codecs/video_codec.h +++ b/api/video_codecs/video_codec.h @@ -130,6 +130,8 @@ class RTC_EXPORT VideoCodec { bool GetFrameDropEnabled() const; void SetFrameDropEnabled(bool enabled); + bool IsSinglecastOrAllNonFirstLayersInactive() const; + // Public variables. TODO(hta): Make them private with accessors. VideoCodecType codecType; diff --git a/modules/video_coding/video_codec_initializer.cc b/modules/video_coding/video_codec_initializer.cc index 6f5f7d16fe..67a335d63b 100644 --- a/modules/video_coding/video_codec_initializer.cc +++ b/modules/video_coding/video_codec_initializer.cc @@ -211,8 +211,11 @@ VideoCodec VideoCodecInitializer::VideoEncoderConfigToVideoCodec( break; } case kVideoCodecVP9: { - // Force the first stream to always be active. - video_codec.simulcastStream[0].active = codec_active; + // When the SvcRateAllocator is used, "active" is controlled by + // `SpatialLayer::active` instead. + if (video_codec.IsSinglecastOrAllNonFirstLayersInactive()) { + video_codec.simulcastStream[0].active = codec_active; + } if (!config.encoder_specific_settings) { *video_codec.VP9() = VideoEncoder::GetDefaultVp9Settings(); diff --git a/pc/peer_connection_simulcast_unittest.cc b/pc/peer_connection_simulcast_unittest.cc index b8f1a7cdfb..6b11820776 100644 --- a/pc/peer_connection_simulcast_unittest.cc +++ b/pc/peer_connection_simulcast_unittest.cc @@ -1543,6 +1543,66 @@ TEST_F(PeerConnectionSimulcastWithMediaFlowTests, EXPECT_FALSE(parameters.encodings[2].scalability_mode.has_value()); } +TEST_F(PeerConnectionSimulcastWithMediaFlowTests, + SendingThreeEncodings_VP9_AllLayersInactive) { + // TODO(https://crbug.com/webrtc/14884): A field trial shouldn't be needed to + // get spec-compliant behavior! + test::ScopedFieldTrials field_trials( + "WebRTC-AllowDisablingLegacyScalability/Enabled/"); + + rtc::scoped_refptr local_pc_wrapper = CreatePc(); + rtc::scoped_refptr remote_pc_wrapper = CreatePc(); + ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper); + + std::vector layers = + CreateLayers({"f", "h", "q"}, /*active=*/true); + rtc::scoped_refptr transceiver = + AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper, + layers); + std::vector codecs = + GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, "VP9"); + transceiver->SetCodecPreferences(codecs); + + // Legacy SVC mode and all layers inactive. + rtc::scoped_refptr sender = transceiver->sender(); + RtpParameters parameters = sender->GetParameters(); + ASSERT_EQ(parameters.encodings.size(), 3u); + parameters.encodings[0].active = false; + parameters.encodings[1].active = false; + parameters.encodings[2].active = false; + sender->SetParameters(parameters); + + NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper, layers); + local_pc_wrapper->WaitForConnection(); + remote_pc_wrapper->WaitForConnection(); + + // Ensure no media is flowing (1 second should be enough). + rtc::Thread::Current()->SleepMs(1000); + rtc::scoped_refptr report = GetStats(local_pc_wrapper); + std::vector outbound_rtps = + report->GetStatsOfType(); + ASSERT_THAT(outbound_rtps, SizeIs(1u)); + EXPECT_EQ(*outbound_rtps[0]->bytes_sent, 0u); + + // Standard mode and all layers inactive. + parameters = sender->GetParameters(); + ASSERT_EQ(parameters.encodings.size(), 3u); + parameters.encodings[0].scalability_mode = "L1T3"; + parameters.encodings[0].active = false; + parameters.encodings[1].active = false; + parameters.encodings[2].active = false; + sender->SetParameters(parameters); + + // Ensure no media is flowing (1 second should be enough). + rtc::Thread::Current()->SleepMs(1000); + report = GetStats(local_pc_wrapper); + outbound_rtps = report->GetStatsOfType(); + ASSERT_THAT(outbound_rtps, SizeIs(3u)); + EXPECT_EQ(*outbound_rtps[0]->bytes_sent, 0u); + EXPECT_EQ(*outbound_rtps[1]->bytes_sent, 0u); + EXPECT_EQ(*outbound_rtps[2]->bytes_sent, 0u); +} + // TODO(https://crbug.com/webrtc/15005): A field trial shouldn't be needed to // get spec-compliant behavior! The same field trial is also used for VP9 // simulcast (https://crbug.com/webrtc/14884). diff --git a/video/encoder_bitrate_adjuster.cc b/video/encoder_bitrate_adjuster.cc index c672173895..811e0e49dd 100644 --- a/video/encoder_bitrate_adjuster.cc +++ b/video/encoder_bitrate_adjuster.cc @@ -52,7 +52,7 @@ EncoderBitrateAdjuster::EncoderBitrateAdjuster(const VideoCodec& codec_settings) // SVC streams, EncoderBitrateAdjuster needs to be updated to care about both // `simulcastStream` and `spatialLayers` at the same time. if (codec_settings.codecType == VideoCodecType::kVideoCodecVP9 && - codec_settings.numberOfSimulcastStreams <= 1) { + codec_settings.IsSinglecastOrAllNonFirstLayersInactive()) { for (size_t si = 0; si < codec_settings.VP9().numberOfSpatialLayers; ++si) { if (codec_settings.spatialLayers[si].active) { min_bitrates_bps_[si] = diff --git a/video/video_stream_encoder.cc b/video/video_stream_encoder.cc index 39f46b930b..e556ff9b8c 100644 --- a/video/video_stream_encoder.cc +++ b/video/video_stream_encoder.cc @@ -1395,11 +1395,18 @@ void VideoStreamEncoder::ReconfigureEncoder() { pending_encoder_reconfiguration_ = false; bool is_svc = false; + bool single_stream_or_non_first_inactive = true; + for (size_t i = 1; i < encoder_config_.number_of_streams; ++i) { + if (encoder_config_.simulcast_layers[i].active) { + single_stream_or_non_first_inactive = false; + break; + } + } // Set min_bitrate_bps, max_bitrate_bps, and max padding bit rate for VP9 // and AV1 and leave only one stream containing all necessary information. if ((encoder_config_.codec_type == kVideoCodecVP9 || encoder_config_.codec_type == kVideoCodecAV1) && - encoder_config_.number_of_streams == 1) { + single_stream_or_non_first_inactive) { // Lower max bitrate to the level codec actually can produce. streams[0].max_bitrate_bps = std::min(streams[0].max_bitrate_bps,