Make VP9 simulcast behave like singlecast for single active layer cases.

Various "if streams == 1" cases are updated to "if
IsSinglecastOrAllNonFirstLayersInactive()" in order not to cause subtle
differences between VP9 {active} and VP9 {active,inactive,inactive}.

This CL also affects a line that conditionally sets
`simulcastStream[0].active = codec_active` so it seemed fitting to
improve the test coverage of "if all streams are inactive, don't send".

Bug: webrtc:15028
Change-Id: I8872dc8be0f2dfc1d8914bdba5e6433f9ba8cbfd
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/298881
Commit-Queue: Henrik Boström <hbos@webrtc.org>
Reviewed-by: Philip Eliasson <philipel@webrtc.org>
Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org>
Reviewed-by: Erik Språng <sprang@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#39656}
This commit is contained in:
Henrik Boström 2023-03-23 12:46:37 +01:00 committed by WebRTC LUCI CQ
parent b396e2b159
commit 4baea5b07f
7 changed files with 86 additions and 5 deletions

View File

@ -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<SvcRateAllocator>(codec);
}
ABSL_FALLTHROUGH_INTENDED;

View File

@ -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

View File

@ -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;

View File

@ -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();

View File

@ -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<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
std::vector<SimulcastLayer> layers =
CreateLayers({"f", "h", "q"}, /*active=*/true);
rtc::scoped_refptr<RtpTransceiverInterface> transceiver =
AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper,
layers);
std::vector<RtpCodecCapability> codecs =
GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, "VP9");
transceiver->SetCodecPreferences(codecs);
// Legacy SVC mode and all layers inactive.
rtc::scoped_refptr<RtpSenderInterface> 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<const RTCStatsReport> report = GetStats(local_pc_wrapper);
std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
report->GetStatsOfType<RTCOutboundRtpStreamStats>();
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<RTCOutboundRtpStreamStats>();
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).

View File

@ -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] =

View File

@ -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,