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:
parent
b396e2b159
commit
4baea5b07f
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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).
|
||||
|
||||
@ -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] =
|
||||
|
||||
@ -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,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user