From 76aa330c24f59229bdd3938ee08378457fe5270e Mon Sep 17 00:00:00 2001 From: Danil Chapovalov Date: Mon, 9 Sep 2024 15:02:29 +0200 Subject: [PATCH] Implement ObjCVideoEncoderFactory::QueryCodecSupport MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Reviewed-by: Mirko Bonadei Commit-Queue: Danil Chapovalov Cr-Commit-Position: refs/heads/main@{#42985} --- sdk/BUILD.gn | 1 + sdk/objc/base/RTCVideoEncoderFactory.h | 20 +++++ sdk/objc/base/RTCVideoEncoderFactory.mm | 32 ++++++++ .../native/src/objc_video_encoder_factory.h | 7 +- .../native/src/objc_video_encoder_factory.mm | 20 +++++ .../objc_video_encoder_factory_tests.mm | 79 +++++++++++++++++++ 6 files changed, 158 insertions(+), 1 deletion(-) create mode 100644 sdk/objc/base/RTCVideoEncoderFactory.mm diff --git a/sdk/BUILD.gn b/sdk/BUILD.gn index 0c33be3afd..27fb4adada 100644 --- a/sdk/BUILD.gn +++ b/sdk/BUILD.gn @@ -110,6 +110,7 @@ if (is_ios || is_mac) { "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 a73cd77990..31e469d4ba 100644 --- a/sdk/objc/base/RTCVideoEncoderFactory.h +++ b/sdk/objc/base/RTCVideoEncoderFactory.h @@ -32,6 +32,22 @@ RTC_OBJC_EXPORT @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 @@ RTC_OBJC_EXPORT @optional - (NSArray *)implementations; - (nullable id)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 0000000000..604a52417f --- /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 d74e4933d2..62af09cb8f 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 -#import "base/RTCMacros.h" +#include +#include #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 @@ class ObjCVideoEncoderFactory : public VideoEncoderFactory { std::vector GetSupportedFormats() const override; std::vector GetImplementations() const override; + CodecSupport QueryCodecSupport(const SdpVideoFormat& format, + std::optional scalability_mode) const override; std::unique_ptr Create(const Environment& env, const SdpVideoFormat& format) override; std::unique_ptr 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 e5fb3da79d..bc8c532539 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 #include #import "base/RTCMacros.h" @@ -184,6 +185,25 @@ std::vector ObjCVideoEncoderFactory::GetImplementations() const return GetSupportedFormats(); } +VideoEncoderFactory::CodecSupport ObjCVideoEncoderFactory::QueryCodecSupport( + const SdpVideoFormat &format, std::optional 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 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 a04e797672..2f469bb4a6 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 @@ id CreateErrorEncoderFactory() { return CreateEncoderFactoryReturning(WEBRTC_VIDEO_CODEC_ERROR); } +@interface RTCVideoEncoderFactoryFake : NSObject + +- (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)createEncoder: + (RTC_OBJC_TYPE(RTCVideoCodecInfo) *)info { + return nil; +} + +- (NSArray *)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 GetObjCEncoder( id factory) { webrtc::ObjCVideoEncoderFactory encoder_factory(factory); @@ -132,6 +181,36 @@ std::unique_ptr GetObjCEncoder( 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 supportedFormats = encoder_factory.GetSupportedFormats();