Add H.265 to codecs that supports temporal scalability.

Also updated the test to cover IsTemporalLayersSupported() for all types
of codecs.

Bug: chromium:41480904
Change-Id: I25788a87737aba7308b1d6980ad5b2c26b0e225f
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/367570
Reviewed-by: Erik Språng <sprang@webrtc.org>
Commit-Queue: Jianlin Qiu <jianlin.qiu@intel.com>
Reviewed-by: Henrik Boström <hbos@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#43369}
This commit is contained in:
Qiu Jianlin 2024-11-07 12:33:46 +08:00 committed by WebRTC LUCI CQ
parent 7589689774
commit 4405d06b97
4 changed files with 153 additions and 2 deletions

View File

@ -32,6 +32,21 @@
#include "rtc_base/numerics/safe_conversions.h" #include "rtc_base/numerics/safe_conversions.h"
namespace webrtc { namespace webrtc {
namespace {
constexpr ScalabilityMode kH265SupportedScalabilityModes[] = {
ScalabilityMode::kL1T1, ScalabilityMode::kL1T2, ScalabilityMode::kL1T3};
bool H265SupportsScalabilityMode(ScalabilityMode scalability_mode) {
for (const auto& entry : kH265SupportedScalabilityModes) {
if (entry == scalability_mode) {
return true;
}
}
return false;
}
} // namespace
// TODO(sprang): Split this up and separate the codec specific parts. // TODO(sprang): Split this up and separate the codec specific parts.
VideoCodec VideoCodecInitializer::SetupCodec( VideoCodec VideoCodecInitializer::SetupCodec(
@ -328,7 +343,22 @@ VideoCodec VideoCodecInitializer::SetupCodec(
break; break;
} }
case kVideoCodecH265: case kVideoCodecH265:
// TODO(bugs.webrtc.org/13485) RTC_DCHECK(!config.encoder_specific_settings) << "No encoder-specific "
"settings for H.265.";
// Validate specified scalability modes. If some layer has an unsupported
// mode, store it as the top-level scalability mode, which will make
// InitEncode fail with an appropriate error.
for (const auto& stream : streams) {
if (stream.scalability_mode.has_value() &&
!H265SupportsScalabilityMode(*stream.scalability_mode)) {
RTC_LOG(LS_WARNING)
<< "Invalid scalability mode for H.265: "
<< ScalabilityModeToString(*stream.scalability_mode);
video_codec.SetScalabilityMode(*stream.scalability_mode);
break;
}
}
break; break;
default: default:
// TODO(pbos): Support encoder_settings codec-agnostically. // TODO(pbos): Support encoder_settings codec-agnostically.
@ -345,6 +375,9 @@ VideoCodec VideoCodecInitializer::SetupCodec(
video_codec.minBitrate = experimental_min_bitrate_kbps; video_codec.minBitrate = experimental_min_bitrate_kbps;
video_codec.simulcastStream[0].minBitrate = experimental_min_bitrate_kbps; video_codec.simulcastStream[0].minBitrate = experimental_min_bitrate_kbps;
if (video_codec.codecType == kVideoCodecVP9 || if (video_codec.codecType == kVideoCodecVP9 ||
#ifdef RTC_ENABLE_H265
video_codec.codecType == kVideoCodecH265 ||
#endif
video_codec.codecType == kVideoCodecAV1) { video_codec.codecType == kVideoCodecAV1) {
video_codec.spatialLayers[0].minBitrate = experimental_min_bitrate_kbps; video_codec.spatialLayers[0].minBitrate = experimental_min_bitrate_kbps;
} }

View File

@ -689,4 +689,66 @@ TEST_F(VideoCodecInitializerTest, UpdatesVp9SpecificFieldsWithScalabilityMode) {
EXPECT_EQ(codec.VP9()->interLayerPred, InterLayerPredMode::kOff); EXPECT_EQ(codec.VP9()->interLayerPred, InterLayerPredMode::kOff);
} }
#ifdef RTC_ENABLE_H265
TEST_F(VideoCodecInitializerTest, H265SingleSpatialLayerBitratesAreConsistent) {
VideoEncoderConfig config;
config.codec_type = VideoCodecType::kVideoCodecH265;
std::vector<VideoStream> streams = {DefaultStream()};
streams[0].scalability_mode = ScalabilityMode::kL1T2;
VideoCodec codec =
VideoCodecInitializer::SetupCodec(env_.field_trials(), config, streams);
EXPECT_GE(codec.spatialLayers[0].targetBitrate,
codec.spatialLayers[0].minBitrate);
EXPECT_LE(codec.spatialLayers[0].targetBitrate,
codec.spatialLayers[0].maxBitrate);
}
// Test that the H.265 codec initializer carries over invalid simulcast layer
// scalability mode to top level scalability mode setting.
TEST_F(VideoCodecInitializerTest,
H265ScalabilityModeConfiguredToTopLevelWhenNotAllowed) {
VideoEncoderConfig config;
config.codec_type = VideoCodecType::kVideoCodecH265;
std::vector<VideoStream> streams = {DefaultStream()};
streams[0].scalability_mode = ScalabilityMode::kL3T3;
VideoCodec codec =
VideoCodecInitializer::SetupCodec(env_.field_trials(), config, streams);
// Check that an unsupported scalability mode will cause top-level scalability
// to be set to the same unsupported mode.
EXPECT_EQ(codec.GetScalabilityMode(), ScalabilityMode::kL3T3);
EXPECT_EQ(codec.spatialLayers[0].numberOfTemporalLayers, 3);
EXPECT_EQ(codec.simulcastStream[0].numberOfTemporalLayers, 3);
}
// Test that inconistent scalability mode settings in simulcast streams will
// clear top level scalability mode setting.
TEST_F(VideoCodecInitializerTest,
H265InconsistentScalabilityModesWillClearTopLevelScalability) {
VideoEncoderConfig config;
config.simulcast_layers.resize(2);
config.simulcast_layers[0].active = true;
config.simulcast_layers[1].active = true;
config.codec_type = VideoCodecType::kVideoCodecH265;
std::vector<VideoStream> streams = {DefaultStream(), DefaultStream()};
streams[0].scalability_mode = ScalabilityMode::kL1T3;
streams[1].scalability_mode = ScalabilityMode::kL1T1;
VideoCodec codec =
VideoCodecInitializer::SetupCodec(env_.field_trials(), config, streams);
// Top level scalability mode should be cleared if the simulcast streams have
// different per-stream temporal layer settings.
EXPECT_EQ(codec.GetScalabilityMode(), std::nullopt);
EXPECT_EQ(codec.spatialLayers[0].numberOfTemporalLayers, 3);
EXPECT_EQ(codec.simulcastStream[0].numberOfTemporalLayers, 3);
EXPECT_EQ(codec.simulcastStream[1].numberOfTemporalLayers, 1);
}
#endif
} // namespace webrtc } // namespace webrtc

View File

@ -64,7 +64,8 @@ bool IsScaleFactorsPowerOfTwo(const webrtc::VideoEncoderConfig& config) {
bool IsTemporalLayersSupported(webrtc::VideoCodecType codec_type) { bool IsTemporalLayersSupported(webrtc::VideoCodecType codec_type) {
return codec_type == webrtc::VideoCodecType::kVideoCodecVP8 || return codec_type == webrtc::VideoCodecType::kVideoCodecVP8 ||
codec_type == webrtc::VideoCodecType::kVideoCodecVP9 || codec_type == webrtc::VideoCodecType::kVideoCodecVP9 ||
codec_type == webrtc::VideoCodecType::kVideoCodecAV1; codec_type == webrtc::VideoCodecType::kVideoCodecAV1 ||
codec_type == webrtc::VideoCodecType::kVideoCodecH265;
} }
size_t FindRequiredActiveLayers( size_t FindRequiredActiveLayers(

View File

@ -433,4 +433,59 @@ INSTANTIATE_TEST_SUITE_P(
.scalability_mode = ScalabilityMode::kL1T2})}}), .scalability_mode = ScalabilityMode::kL1T2})}}),
Values(VideoCodecType::kVideoCodecVP8, Values(VideoCodecType::kVideoCodecVP8,
VideoCodecType::kVideoCodecAV1))); VideoCodecType::kVideoCodecAV1)));
TEST(EncoderStreamFactory, VP9TemporalLayerCountTransferToStreamSettings) {
VideoEncoderConfig encoder_config;
VideoCodecVP9 vp9_settings = VideoEncoder::GetDefaultVp9Settings();
encoder_config.encoder_specific_settings =
rtc::make_ref_counted<VideoEncoderConfig::Vp9EncoderSpecificSettings>(
vp9_settings);
encoder_config.codec_type = VideoCodecType::kVideoCodecVP9;
encoder_config.number_of_streams = 1;
encoder_config.simulcast_layers.resize(1);
encoder_config.simulcast_layers[0].num_temporal_layers = 3;
auto streams = CreateEncoderStreams(ExplicitKeyValueConfig(""), {1280, 720},
encoder_config);
ASSERT_THAT(streams, SizeIs(1));
EXPECT_EQ(streams[0].num_temporal_layers, 3);
}
TEST(EncoderStreamFactory, AV1TemporalLayerCountTransferToStreamSettings) {
VideoEncoderConfig encoder_config;
encoder_config.codec_type = VideoCodecType::kVideoCodecAV1;
encoder_config.number_of_streams = 1;
encoder_config.simulcast_layers.resize(1);
encoder_config.simulcast_layers[0].num_temporal_layers = 3;
auto streams = CreateEncoderStreams(ExplicitKeyValueConfig(""), {1280, 720},
encoder_config);
ASSERT_THAT(streams, SizeIs(1));
EXPECT_EQ(streams[0].num_temporal_layers, 3);
}
TEST(EncoderStreamFactory, H264TemporalLayerCountTransferToStreamSettings) {
VideoEncoderConfig encoder_config;
encoder_config.codec_type = VideoCodecType::kVideoCodecH264;
encoder_config.number_of_streams = 1;
encoder_config.simulcast_layers.resize(1);
encoder_config.simulcast_layers[0].num_temporal_layers = 3;
auto streams = CreateEncoderStreams(ExplicitKeyValueConfig(""), {1280, 720},
encoder_config);
ASSERT_THAT(streams, SizeIs(1));
EXPECT_EQ(streams[0].num_temporal_layers, std::nullopt);
}
#ifdef RTC_ENABLE_H265
TEST(EncoderStreamFactory, H265TemporalLayerCountTransferToStreamSettings) {
VideoEncoderConfig encoder_config;
encoder_config.codec_type = VideoCodecType::kVideoCodecH265;
encoder_config.number_of_streams = 1;
encoder_config.simulcast_layers.resize(1);
encoder_config.simulcast_layers[0].num_temporal_layers = 3;
auto streams = CreateEncoderStreams(ExplicitKeyValueConfig(""), {1280, 720},
encoder_config);
ASSERT_THAT(streams, SizeIs(1));
EXPECT_EQ(streams[0].num_temporal_layers, 3);
}
#endif
} // namespace webrtc } // namespace webrtc