Implement ObjCVideoEncoderFactory::QueryCodecSupport

To allow objc video encoders to support scalability modes

Bug: b/299588022
Change-Id: Id58f996b8c48c6688cccdc32caff6adb00370d5c
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/358580
Reviewed-by: Kári Helgason <kthelgason@webrtc.org>
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Commit-Queue: Danil Chapovalov <danilchap@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#42985}
diff --git a/sdk/BUILD.gn b/sdk/BUILD.gn
index 0c33be3..27fb4ad 100644
--- a/sdk/BUILD.gn
+++ b/sdk/BUILD.gn
@@ -110,6 +110,7 @@
       "objc/base/RTCVideoDecoderFactory.h",
       "objc/base/RTCVideoEncoder.h",
       "objc/base/RTCVideoEncoderFactory.h",
+      "objc/base/RTCVideoEncoderFactory.mm",
       "objc/base/RTCVideoEncoderQpThresholds.h",
       "objc/base/RTCVideoEncoderQpThresholds.m",
       "objc/base/RTCVideoEncoderSettings.h",
diff --git a/sdk/objc/base/RTCVideoEncoderFactory.h b/sdk/objc/base/RTCVideoEncoderFactory.h
index a73cd77..31e469d 100644
--- a/sdk/objc/base/RTCVideoEncoderFactory.h
+++ b/sdk/objc/base/RTCVideoEncoderFactory.h
@@ -32,6 +32,22 @@
 
 @end
 
+/** RTCVideoEncoderCodecSupport is an Objective-C version of
+ * webrtc::VideoEncoderFactory::CodecSupport. */
+RTC_OBJC_EXPORT
+@interface RTC_OBJC_TYPE (RTCVideoEncoderCodecSupport) : NSObject
+
+- (instancetype)init NS_UNAVAILABLE;
+
+- (instancetype)initWithSupported:(bool)isSupported;
+- (instancetype)initWithSupported:(bool)isSupported
+                 isPowerEfficient:(bool)isPowerEfficient NS_DESIGNATED_INITIALIZER;
+
+@property(nonatomic, readonly) bool isSupported;
+@property(nonatomic, readonly) bool isPowerEfficient;
+
+@end
+
 /** RTCVideoEncoderFactory is an Objective-C version of webrtc::VideoEncoderFactory.
  */
 RTC_OBJC_EXPORT
@@ -46,6 +62,10 @@
 @optional
 - (NSArray<RTC_OBJC_TYPE(RTCVideoCodecInfo) *> *)implementations;
 - (nullable id<RTC_OBJC_TYPE(RTCVideoEncoderSelector)>)encoderSelector;
+/* TODO: b/299588022 - move to non-optional section when implemented by all derived classes. */
+- (RTC_OBJC_TYPE(RTCVideoEncoderCodecSupport) *)
+    queryCodecSupport:(RTC_OBJC_TYPE(RTCVideoCodecInfo) *)info
+      scalabilityMode:(nullable NSString *)scalabilityMode;
 
 @end
 
diff --git a/sdk/objc/base/RTCVideoEncoderFactory.mm b/sdk/objc/base/RTCVideoEncoderFactory.mm
new file mode 100644
index 0000000..604a524
--- /dev/null
+++ b/sdk/objc/base/RTCVideoEncoderFactory.mm
@@ -0,0 +1,32 @@
+/*
+ *  Copyright 2024 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.
+ */
+
+#import "RTCVideoEncoderFactory.h"
+#import "RTCMacros.h"
+
+@implementation RTC_OBJC_TYPE (RTCVideoEncoderCodecSupport)
+
+@synthesize isSupported = _isSupported;
+@synthesize isPowerEfficient = _isPowerEfficient;
+
+- (instancetype)initWithSupported:(bool)isSupported {
+  return [self initWithSupported:isSupported isPowerEfficient:false];
+}
+
+- (instancetype)initWithSupported:(bool)isSupported isPowerEfficient:(bool)isPowerEfficient {
+  self = [super init];
+  if (self) {
+    _isSupported = isSupported;
+    _isPowerEfficient = isPowerEfficient;
+  }
+  return self;
+}
+
+@end
diff --git a/sdk/objc/native/src/objc_video_encoder_factory.h b/sdk/objc/native/src/objc_video_encoder_factory.h
index d74e493..62af09c 100644
--- a/sdk/objc/native/src/objc_video_encoder_factory.h
+++ b/sdk/objc/native/src/objc_video_encoder_factory.h
@@ -13,10 +13,13 @@
 
 #import <Foundation/Foundation.h>
 
-#import "base/RTCMacros.h"
+#include <optional>
+#include <string>
 
 #include "api/environment/environment.h"
+#include "api/video_codecs/sdp_video_format.h"
 #include "api/video_codecs/video_encoder_factory.h"
+#import "base/RTCMacros.h"
 
 @protocol RTC_OBJC_TYPE
 (RTCVideoEncoderFactory);
@@ -32,6 +35,8 @@
 
   std::vector<SdpVideoFormat> GetSupportedFormats() const override;
   std::vector<SdpVideoFormat> GetImplementations() const override;
+  CodecSupport QueryCodecSupport(const SdpVideoFormat& format,
+                                 std::optional<std::string> scalability_mode) const override;
   std::unique_ptr<VideoEncoder> Create(const Environment& env,
                                        const SdpVideoFormat& format) override;
   std::unique_ptr<EncoderSelectorInterface> GetEncoderSelector() const override;
diff --git a/sdk/objc/native/src/objc_video_encoder_factory.mm b/sdk/objc/native/src/objc_video_encoder_factory.mm
index e5fb3da..bc8c532 100644
--- a/sdk/objc/native/src/objc_video_encoder_factory.mm
+++ b/sdk/objc/native/src/objc_video_encoder_factory.mm
@@ -10,6 +10,7 @@
 
 #include "sdk/objc/native/src/objc_video_encoder_factory.h"
 
+#include <optional>
 #include <string>
 
 #import "base/RTCMacros.h"
@@ -184,6 +185,25 @@
   return GetSupportedFormats();
 }
 
+VideoEncoderFactory::CodecSupport ObjCVideoEncoderFactory::QueryCodecSupport(
+    const SdpVideoFormat &format, std::optional<std::string> scalability_mode) const {
+  if ([encoder_factory_ respondsToSelector:@selector(queryCodecSupport:scalabilityMode:)]) {
+    RTC_OBJC_TYPE(RTCVideoCodecInfo) *info =
+        [[RTC_OBJC_TYPE(RTCVideoCodecInfo) alloc] initWithNativeSdpVideoFormat:format];
+    NSString *mode;
+    if (scalability_mode.has_value()) {
+      mode = [NSString stringForAbslStringView:*scalability_mode];
+    }
+
+    RTC_OBJC_TYPE(RTCVideoEncoderCodecSupport) *result = [encoder_factory_ queryCodecSupport:info
+                                                                             scalabilityMode:mode];
+    return {.is_supported = result.isSupported, .is_power_efficient = result.isPowerEfficient};
+  }
+
+  // Use default implementation.
+  return VideoEncoderFactory::QueryCodecSupport(format, scalability_mode);
+}
+
 std::unique_ptr<VideoEncoder> ObjCVideoEncoderFactory::Create(const Environment &env,
                                                               const SdpVideoFormat &format) {
   RTC_OBJC_TYPE(RTCVideoCodecInfo) *info =
diff --git a/sdk/objc/unittests/objc_video_encoder_factory_tests.mm b/sdk/objc/unittests/objc_video_encoder_factory_tests.mm
index a04e797..2f469bb 100644
--- a/sdk/objc/unittests/objc_video_encoder_factory_tests.mm
+++ b/sdk/objc/unittests/objc_video_encoder_factory_tests.mm
@@ -52,6 +52,55 @@
   return CreateEncoderFactoryReturning(WEBRTC_VIDEO_CODEC_ERROR);
 }
 
+@interface RTCVideoEncoderFactoryFake : NSObject <RTC_OBJC_TYPE (RTCVideoEncoderFactory)>
+
+- (instancetype)init NS_UNAVAILABLE;
+- (instancetype)initWithScalabilityMode:(NSString *)scalabilityMode;
+- (instancetype)initWithScalabilityMode:(NSString *)scalabilityMode
+                       isPowerEfficient:(bool)isPowerEfficient NS_DESIGNATED_INITIALIZER;
+@end
+@implementation RTCVideoEncoderFactoryFake
+
+NSString *_scalabilityMode;
+bool _isPowerEfficient;
+
+- (instancetype)initWithScalabilityMode:(NSString *)scalabilityMode {
+  return [self initWithScalabilityMode:scalabilityMode isPowerEfficient:false];
+}
+
+- (instancetype)initWithScalabilityMode:(NSString *)scalabilityMode
+                       isPowerEfficient:(bool)isPowerEfficient {
+  self = [super init];
+  if (self) {
+    _scalabilityMode = scalabilityMode;
+    _isPowerEfficient = isPowerEfficient;
+  }
+  return self;
+}
+
+- (nullable id<RTC_OBJC_TYPE(RTCVideoEncoder)>)createEncoder:
+    (RTC_OBJC_TYPE(RTCVideoCodecInfo) *)info {
+  return nil;
+}
+
+- (NSArray<RTC_OBJC_TYPE(RTCVideoCodecInfo) *> *)supportedCodecs {
+  return @[];
+}
+
+- (RTC_OBJC_TYPE(RTCVideoEncoderCodecSupport) *)
+    queryCodecSupport:(RTC_OBJC_TYPE(RTCVideoCodecInfo) *)info
+      scalabilityMode:(nullable NSString *)scalabilityMode {
+  if (_scalabilityMode ? [_scalabilityMode isEqualToString:scalabilityMode] :
+                         scalabilityMode == nil) {
+    return [[RTC_OBJC_TYPE(RTCVideoEncoderCodecSupport) alloc] initWithSupported:true
+                                                                isPowerEfficient:_isPowerEfficient];
+  } else {
+    return [[RTC_OBJC_TYPE(RTCVideoEncoderCodecSupport) alloc] initWithSupported:false];
+  }
+}
+
+@end
+
 std::unique_ptr<webrtc::VideoEncoder> GetObjCEncoder(
     id<RTC_OBJC_TYPE(RTCVideoEncoderFactory)> factory) {
   webrtc::ObjCVideoEncoderFactory encoder_factory(factory);
@@ -132,6 +181,36 @@
   EXPECT_EQ(encoder->Release(), WEBRTC_VIDEO_CODEC_ERROR);
 }
 
+- (void)testQueryCodecSupportDelegatesToObjcFactoryConvertsNulloptModeToNil {
+  id fakeEncoderFactory = [[RTCVideoEncoderFactoryFake alloc] initWithScalabilityMode:nil];
+  webrtc::SdpVideoFormat codec("VP8");
+  webrtc::ObjCVideoEncoderFactory encoder_factory(fakeEncoderFactory);
+
+  webrtc::VideoEncoderFactory::CodecSupport s =
+      encoder_factory.QueryCodecSupport(codec, std::nullopt);
+
+  EXPECT_TRUE(s.is_supported);
+}
+
+- (void)testQueryCodecSupportDelegatesToObjcFactoryMayReturnUnsupported {
+  id fakeEncoderFactory = [[RTCVideoEncoderFactoryFake alloc] initWithScalabilityMode:@"L1T2"];
+  webrtc::SdpVideoFormat codec("VP8");
+  webrtc::ObjCVideoEncoderFactory encoder_factory(fakeEncoderFactory);
+
+  EXPECT_FALSE(encoder_factory.QueryCodecSupport(codec, "S2T1").is_supported);
+}
+
+- (void)testQueryCodecSupportDelegatesToObjcFactoryIncludesPowerEfficientFlag {
+  id fakeEncoderFactory = [[RTCVideoEncoderFactoryFake alloc] initWithScalabilityMode:@"L1T2"
+                                                                     isPowerEfficient:true];
+  webrtc::SdpVideoFormat codec("VP8");
+  webrtc::ObjCVideoEncoderFactory encoder_factory(fakeEncoderFactory);
+
+  webrtc::VideoEncoderFactory::CodecSupport s = encoder_factory.QueryCodecSupport(codec, "L1T2");
+  EXPECT_TRUE(s.is_supported);
+  EXPECT_TRUE(s.is_power_efficient);
+}
+
 - (void)testGetSupportedFormats {
   webrtc::ObjCVideoEncoderFactory encoder_factory(CreateOKEncoderFactory());
   std::vector<webrtc::SdpVideoFormat> supportedFormats = encoder_factory.GetSupportedFormats();