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(