Handle scalabilityMode in default VideoEncoderFactory::QueryCodecSupport The default implementation always returned unsupported when a scalability_mode was specified. Fix by looking up the requested mode in the matching format's scalability_modes list from GetSupportedFormats(). Bug: webrtc:496700735 Change-Id: I0835ee7e7111cf181d90dff48e3505c0f97a6365 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/462260 Reviewed-by: Erik Språng <sprang@webrtc.org> Reviewed-by: Johannes Kron <kron@webrtc.org> Commit-Queue: Yury Yarashevich <yura.yaroshevich@gmail.com> Cr-Commit-Position: refs/heads/main@{#47432}
diff --git a/api/video_codecs/BUILD.gn b/api/video_codecs/BUILD.gn index 583ade7..51da74c 100644 --- a/api/video_codecs/BUILD.gn +++ b/api/video_codecs/BUILD.gn
@@ -60,6 +60,7 @@ "video_decoder_factory.h", "video_encoder.cc", "video_encoder.h", + "video_encoder_factory.cc", "video_encoder_factory.h", "vp8_frame_buffer_controller.h", "vp8_frame_config.cc",
diff --git a/api/video_codecs/test/BUILD.gn b/api/video_codecs/test/BUILD.gn index 9c1e795..82fb965 100644 --- a/api/video_codecs/test/BUILD.gn +++ b/api/video_codecs/test/BUILD.gn
@@ -17,6 +17,7 @@ "sdp_video_format_unittest.cc", "video_codec_unittest.cc", "video_decoder_software_fallback_wrapper_unittest.cc", + "video_encoder_factory_unittest.cc", "video_encoder_software_fallback_wrapper_unittest.cc", ] @@ -29,6 +30,7 @@ ":video_encoder_factory_template_tests", "..:builtin_video_encoder_factory", "..:rtc_software_fallback_wrappers", + "..:scalability_mode", "..:video_codecs_api", "../..:fec_controller_api", "../..:field_trials",
diff --git a/api/video_codecs/test/video_encoder_factory_unittest.cc b/api/video_codecs/test/video_encoder_factory_unittest.cc new file mode 100644 index 0000000..2480b56 --- /dev/null +++ b/api/video_codecs/test/video_encoder_factory_unittest.cc
@@ -0,0 +1,126 @@ +/* + * Copyright (c) 2026 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "api/video_codecs/video_encoder_factory.h" + +#include <memory> +#include <optional> +#include <string> +#include <utility> +#include <vector> + +#include "absl/container/inlined_vector.h" +#include "api/environment/environment.h" +#include "api/video_codecs/scalability_mode.h" +#include "api/video_codecs/sdp_video_format.h" +#include "api/video_codecs/video_encoder.h" +#include "api/video_codecs/vp9_profile.h" +#include "test/gmock.h" +#include "test/gtest.h" + +namespace webrtc { +namespace { + +constexpr VideoEncoderFactory::CodecSupport kSupported = { + .is_supported = true, + .is_power_efficient = false}; +constexpr VideoEncoderFactory::CodecSupport kUnsupported = { + .is_supported = false, + .is_power_efficient = false}; + +MATCHER_P(SupportIs, expected, "") { + return arg.is_supported == expected.is_supported && + arg.is_power_efficient == expected.is_power_efficient; +} + +class TestVideoEncoderFactory : public VideoEncoderFactory { + public: + explicit TestVideoEncoderFactory(std::vector<SdpVideoFormat> formats) + : formats_(std::move(formats)) {} + + std::vector<SdpVideoFormat> GetSupportedFormats() const override { + return formats_; + } + + std::unique_ptr<VideoEncoder> Create(const Environment& env, + const SdpVideoFormat& format) override { + return nullptr; + } + + private: + std::vector<SdpVideoFormat> formats_; +}; + +TEST(VideoEncoderFactoryTest, QueryCodecSupportNoScalabilityMode) { + TestVideoEncoderFactory factory({SdpVideoFormat("VP8")}); + EXPECT_THAT(factory.QueryCodecSupport(SdpVideoFormat("VP8"), std::nullopt), + SupportIs(kSupported)); +} + +TEST(VideoEncoderFactoryTest, QueryCodecSupportUnsupportedFormat) { + TestVideoEncoderFactory factory({SdpVideoFormat("VP8")}); + EXPECT_THAT(factory.QueryCodecSupport(SdpVideoFormat("H264"), std::nullopt), + SupportIs(kUnsupported)); +} + +TEST(VideoEncoderFactoryTest, + QueryCodecSupportWithScalabilityModeAndEmptyModeList) { + TestVideoEncoderFactory factory({SdpVideoFormat("VP8")}); + EXPECT_THAT(factory.QueryCodecSupport(SdpVideoFormat("VP8"), "L1T2"), + SupportIs(kUnsupported)); +} + +TEST(VideoEncoderFactoryTest, QueryCodecSupportWithMatchingScalabilityMode) { + SdpVideoFormat format("VP8", {}, {ScalabilityMode::kL1T2}); + TestVideoEncoderFactory factory({format}); + EXPECT_THAT(factory.QueryCodecSupport(SdpVideoFormat("VP8"), "L1T2"), + SupportIs(kSupported)); +} + +TEST(VideoEncoderFactoryTest, QueryCodecSupportWithNonMatchingScalabilityMode) { + SdpVideoFormat format("VP8", {}, {ScalabilityMode::kL1T2}); + TestVideoEncoderFactory factory({format}); + EXPECT_THAT(factory.QueryCodecSupport(SdpVideoFormat("VP8"), "L3T3"), + SupportIs(kUnsupported)); +} + +TEST(VideoEncoderFactoryTest, QueryCodecSupportWithInvalidScalabilityMode) { + SdpVideoFormat format("VP8", {}, {ScalabilityMode::kL1T2}); + TestVideoEncoderFactory factory({format}); + EXPECT_THAT(factory.QueryCodecSupport(SdpVideoFormat("VP8"), "INVALID"), + SupportIs(kUnsupported)); +} + +TEST(VideoEncoderFactoryTest, + QueryCodecSupportScalabilityModeUnsupportedFormat) { + SdpVideoFormat format("VP8", {}, {ScalabilityMode::kL1T2}); + TestVideoEncoderFactory factory({format}); + EXPECT_THAT(factory.QueryCodecSupport(SdpVideoFormat("H264"), "L1T2"), + SupportIs(kUnsupported)); +} + +TEST(VideoEncoderFactoryTest, + QueryCodecSupportDistinguishesDifferentCodecParameters) { + SdpVideoFormat vp9_profile0("VP9", {{kVP9FmtpProfileId, "0"}}, + {ScalabilityMode::kL1T2, ScalabilityMode::kL3T3}); + SdpVideoFormat vp9_profile2("VP9", {{kVP9FmtpProfileId, "2"}}, + {ScalabilityMode::kL1T2}); + TestVideoEncoderFactory factory({vp9_profile0, vp9_profile2}); + + EXPECT_THAT(factory.QueryCodecSupport( + SdpVideoFormat("VP9", {{kVP9FmtpProfileId, "0"}}), "L3T3"), + SupportIs(kSupported)); + EXPECT_THAT(factory.QueryCodecSupport( + SdpVideoFormat("VP9", {{kVP9FmtpProfileId, "2"}}), "L3T3"), + SupportIs(kUnsupported)); +} + +} // namespace +} // namespace webrtc
diff --git a/api/video_codecs/video_encoder_factory.cc b/api/video_codecs/video_encoder_factory.cc new file mode 100644 index 0000000..3ec83b1 --- /dev/null +++ b/api/video_codecs/video_encoder_factory.cc
@@ -0,0 +1,48 @@ +/* + * Copyright (c) 2026 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "api/video_codecs/video_encoder_factory.h" + +#include <optional> +#include <string> + +#include "absl/algorithm/container.h" +#include "api/video_codecs/scalability_mode.h" +#include "api/video_codecs/sdp_video_format.h" + +namespace webrtc { + +VideoEncoderFactory::CodecSupport VideoEncoderFactory::QueryCodecSupport( + const SdpVideoFormat& format, + std::optional<std::string> scalability_mode) const { + // Default implementation, query for supported formats and check if the + // specified format is supported. If a scalability mode is specified, check + // that it is present in the matching format's scalability_modes list. + + for (const auto& supported_format : GetSupportedFormats()) { + if (supported_format.IsSameCodec(format)) { + if (!scalability_mode.has_value()) { + return {.is_supported = true}; + } else { + // Unable to use existing scalability mode parser due to circular + // dependency between api/video_codecs and modules/video_coding. + return {.is_supported = absl::c_any_of( + supported_format.scalability_modes, + [&](ScalabilityMode supported_mode) { + return ScalabilityModeToString(supported_mode) == + *scalability_mode; + })}; + } + } + } + return {.is_supported = false}; +} + +} // namespace webrtc
diff --git a/api/video_codecs/video_encoder_factory.h b/api/video_codecs/video_encoder_factory.h index ebc6e39..a5cf3db 100644 --- a/api/video_codecs/video_encoder_factory.h +++ b/api/video_codecs/video_encoder_factory.h
@@ -22,12 +22,13 @@ #include "api/video/render_resolution.h" #include "api/video_codecs/sdp_video_format.h" #include "api/video_codecs/video_encoder.h" +#include "rtc_base/system/rtc_export.h" namespace webrtc { // A factory that creates VideoEncoders. // NOTE: This class is still under development and may change without notice. -class VideoEncoderFactory { +class RTC_EXPORT VideoEncoderFactory { public: struct CodecSupport { bool is_supported = false; @@ -89,16 +90,7 @@ // subject to change without notice. virtual CodecSupport QueryCodecSupport( const SdpVideoFormat& format, - std::optional<std::string> scalability_mode) const { - // Default implementation, query for supported formats and check if the - // specified format is supported. Returns false if scalability_mode is - // specified. - CodecSupport codec_support; - if (!scalability_mode) { - codec_support.is_supported = format.IsCodecInList(GetSupportedFormats()); - } - return codec_support; - } + std::optional<std::string> scalability_mode) const; // Creates a VideoEncoder for the specified format. virtual std::unique_ptr<VideoEncoder> Create(