From d946eb8d46ec108aa9f49724c0ac456c75f6fba0 Mon Sep 17 00:00:00 2001 From: littleGnAl Date: Mon, 26 Dec 2022 19:42:12 +0800 Subject: [PATCH] feat: [ios] Support render mode and mirror mode for AgoraVideoView with flutter texture rendering --- ios/Classes/AgoraTextureViewFactory.h | 14 - ios/Classes/AgoraTextureViewFactory.mm | 191 -------- ios/Classes/TextureRenderer.h | 27 +- ios/Classes/TextureRenderer.mm | 139 +----- ios/Classes/VideoViewController.h | 27 +- ios/Classes/VideoViewController.mm | 138 +----- lib/src/impl/agora_video_view_impl.dart | 6 +- test_shard/rendering_test/README.md | 26 +- .../agora_video_view_render_test.dart | 429 +++++++++++++++--- ...a_video_view_render.platformview.local.png | Bin 1565 -> 1592 bytes ..._video_view_render.platformview.remote.png | Bin 1581 -> 1760 bytes ....texture.local.donot_handle_rendermode.png | Bin 0 -> 1591 bytes ....agora_video_view_render.texture.local.png | Bin 1542 -> 0 bytes ....texture.local.with_default_rendermode.png | Bin 0 -> 1584 bytes ...endermode.with_videomirrormodedisabled.png | Bin 0 -> 1594 bytes ....texture.local.with_rendermodeadaptive.png | Bin 0 -> 1601 bytes ...ender.texture.local.with_rendermodefit.png | Bin 0 -> 1588 bytes ...er.texture.local.with_rendermodehidden.png | Bin 0 -> 1584 bytes ...texture.remote.donot_handle_rendermode.png | Bin 0 -> 1697 bytes ...texture.remote.with_default_rendermode.png | Bin 0 -> 1838 bytes ...dermodede.with_videoMirrorModeEnabled.png} | Bin 1557 -> 1698 bytes ...texture.remote.with_rendermodeadaptive.png | Bin 0 -> 1794 bytes ...nder.texture.remote.with_rendermodefit.png | Bin 0 -> 1911 bytes ...r.texture.remote.with_rendermodehidden.png | Bin 0 -> 1785 bytes 24 files changed, 400 insertions(+), 597 deletions(-) delete mode 100644 ios/Classes/AgoraTextureViewFactory.h delete mode 100644 ios/Classes/AgoraTextureViewFactory.mm mode change 100644 => 120000 ios/Classes/TextureRenderer.h mode change 100644 => 120000 ios/Classes/TextureRenderer.mm mode change 100644 => 120000 ios/Classes/VideoViewController.h mode change 100644 => 120000 ios/Classes/VideoViewController.mm create mode 100644 test_shard/rendering_test/screenshot/ios.agora_video_view_render.texture.local.donot_handle_rendermode.png delete mode 100644 test_shard/rendering_test/screenshot/ios.agora_video_view_render.texture.local.png create mode 100644 test_shard/rendering_test/screenshot/ios.agora_video_view_render.texture.local.with_default_rendermode.png create mode 100644 test_shard/rendering_test/screenshot/ios.agora_video_view_render.texture.local.with_default_rendermode.with_videomirrormodedisabled.png create mode 100644 test_shard/rendering_test/screenshot/ios.agora_video_view_render.texture.local.with_rendermodeadaptive.png create mode 100644 test_shard/rendering_test/screenshot/ios.agora_video_view_render.texture.local.with_rendermodefit.png create mode 100644 test_shard/rendering_test/screenshot/ios.agora_video_view_render.texture.local.with_rendermodehidden.png create mode 100644 test_shard/rendering_test/screenshot/ios.agora_video_view_render.texture.remote.donot_handle_rendermode.png create mode 100644 test_shard/rendering_test/screenshot/ios.agora_video_view_render.texture.remote.with_default_rendermode.png rename test_shard/rendering_test/screenshot/{ios.agora_video_view_render.texture.remote.png => ios.agora_video_view_render.texture.remote.with_default_rendermodede.with_videoMirrorModeEnabled.png} (53%) create mode 100644 test_shard/rendering_test/screenshot/ios.agora_video_view_render.texture.remote.with_rendermodeadaptive.png create mode 100644 test_shard/rendering_test/screenshot/ios.agora_video_view_render.texture.remote.with_rendermodefit.png create mode 100644 test_shard/rendering_test/screenshot/ios.agora_video_view_render.texture.remote.with_rendermodehidden.png diff --git a/ios/Classes/AgoraTextureViewFactory.h b/ios/Classes/AgoraTextureViewFactory.h deleted file mode 100644 index 4539add7a..000000000 --- a/ios/Classes/AgoraTextureViewFactory.h +++ /dev/null @@ -1,14 +0,0 @@ -//#ifndef AgoraTextureViewFactory_h -//#define AgoraTextureViewFactory_h -// -//#import -// -//@interface AgoraTextureViewFactory : NSObject -//- (instancetype)initWithRegistrar:(NSObject *)registrar; -// -//- (int64_t)createTextureRenderer:(void *)renderer; -// -//- (BOOL)destroyTextureRenderer:(int64_t)textureId; -//@end -// -//#endif /* AgoraTextureViewFactory_h */ diff --git a/ios/Classes/AgoraTextureViewFactory.mm b/ios/Classes/AgoraTextureViewFactory.mm deleted file mode 100644 index 99740bf9d..000000000 --- a/ios/Classes/AgoraTextureViewFactory.mm +++ /dev/null @@ -1,191 +0,0 @@ -//#import "AgoraTextureViewFactory.h" -//#import -//#import -//#import -// -//using namespace agora::iris; -//using namespace agora::iris::rtc; -// -//@interface TextureRenderer : NSObject -//@property(nonatomic, weak) NSObject *textureRegistry; -//@property(nonatomic, assign) int64_t textureId; -//@property(nonatomic, strong) FlutterMethodChannel *channel; -//@property(nonatomic) CVPixelBufferRef buffer_cache; -//@property(nonatomic) CVPixelBufferRef buffer_temp; -//@property(nonatomic) IrisVideoFrameBufferManager *renderer; -//@property(nonatomic, strong) dispatch_semaphore_t lock; -// -//- (instancetype) -// initWithTextureRegistry:(NSObject *)textureRegistry -// messenger:(NSObject *)messenger -// renderer:(IrisVideoFrameBufferManager *)renderer; -// -//- (void)destroy; -//@end -// -//namespace { -//class RendererDelegate : public IrisVideoFrameBufferDelegate { -//public: -// RendererDelegate(void *renderer) : renderer_(renderer) {} -// -// void OnVideoFrameReceived(const IrisVideoFrame &video_frame, -// const IrisVideoFrameBufferConfig *config, -// bool resize) override { -// @autoreleasepool { -// TextureRenderer *renderer = (__bridge TextureRenderer *)renderer_; -// CVPixelBufferRef buffer = NULL; -// NSDictionary *dic = [NSDictionary -// dictionaryWithObjectsAndKeys: -// @(YES), kCVPixelBufferCGBitmapContextCompatibilityKey, @(YES), -// kCVPixelBufferCGImageCompatibilityKey, @(YES), -// kCVPixelBufferOpenGLCompatibilityKey, @(YES), -// kCVPixelBufferMetalCompatibilityKey, nil]; -// CVPixelBufferCreate(kCFAllocatorDefault, video_frame.width, -// video_frame.height, kCVPixelFormatType_32BGRA, -// (__bridge CFDictionaryRef)dic, &buffer); -// -// CVPixelBufferLockBaseAddress(buffer, 0); -// void *copyBaseAddress = CVPixelBufferGetBaseAddress(buffer); -// memcpy(copyBaseAddress, video_frame.y_buffer, -// video_frame.y_buffer_length); -// CVPixelBufferUnlockBaseAddress(buffer, 0); -// -// dispatch_semaphore_wait(renderer.lock, DISPATCH_TIME_FOREVER); -// if (renderer.buffer_cache) { -// CVPixelBufferRelease(renderer.buffer_cache); -// } -// renderer.buffer_cache = buffer; -// dispatch_semaphore_signal(renderer.lock); -// -// [renderer.textureRegistry textureFrameAvailable:renderer.textureId]; -// } -// } -// -//public: -// void *renderer_; -//}; -//} -// -//@interface TextureRenderer () -//@property(nonatomic) RendererDelegate *delegate; -//@end -// -//@implementation TextureRenderer -//- (instancetype) -// initWithTextureRegistry:(NSObject *)textureRegistry -// messenger:(NSObject *)messenger -// renderer:(IrisVideoFrameBufferManager *)renderer { -// self = [super init]; -// if (self) { -// self.textureRegistry = textureRegistry; -// self.textureId = [self.textureRegistry registerTexture:self]; -// self.channel = [FlutterMethodChannel -// methodChannelWithName: -// [NSString stringWithFormat:@"agora_rtc_engine/texture_render_%lld", -// self.textureId] -// binaryMessenger:messenger]; -// self.renderer = renderer; -// self.lock = dispatch_semaphore_create(1); -// self.delegate = new ::RendererDelegate((__bridge void *)self); -// __weak __typeof(self) weakSelf = self; -// [self.channel setMethodCallHandler:^(FlutterMethodCall *_Nonnull call, -// FlutterResult _Nonnull result) { -// if (!weakSelf) { -// return; -// } -// if ([@"setData" isEqualToString:call.method]) { -// NSDictionary *data = call.arguments[@"data"]; -// NSNumber *uid = data[@"uid"]; -// NSString *channelId = data[@"channelId"]; -// -// IrisVideoFrameBuffer buffer(kVideoFrameTypeBGRA, -// weakSelf.delegate); -// IrisVideoFrameBufferConfig config; -// -// config.id = [uid unsignedIntValue]; -// if (config.id == 0) { -// config.type = IrisVideoSourceType::kVideoSourceTypeCameraPrimary; -// } else { -// config.type = IrisVideoSourceType::kVideoSourceTypeRemote; -// } -// if (channelId && (NSNull *)channelId != [NSNull null]) { -// strcpy(config.key, [channelId UTF8String]); -// -// } else { -// strcpy(config.key, ""); -// } -// renderer->EnableVideoFrameBuffer(buffer, &config); -// } -// }]; -// } -// return self; -//} -// -//- (void)dealloc { -// [self destroy]; -// if (self.buffer_cache) { -// CVPixelBufferRelease(self.buffer_cache); -// } -//// if (self.buffer_temp) { -//// CVPixelBufferRelease(self.buffer_temp); -//// } -//} -// -//- (void)destroy { -// self.renderer->DisableVideoFrameBuffer(self.delegate); -// [self.textureRegistry unregisterTexture:self.textureId]; -//} -// -//- (CVPixelBufferRef)copyPixelBuffer { -// dispatch_semaphore_wait(self.lock, DISPATCH_TIME_FOREVER); -//// CVPixelBufferRelease(self.buffer_temp); -// self.buffer_temp = self.buffer_cache; -// CVPixelBufferRetain(self.buffer_temp); -// dispatch_semaphore_signal(self.lock); -// return self.buffer_temp; -//// return nil; -//} -// -//- (void)onTextureUnregistered:(NSObject *)texture { -//} -//@end -// -//@interface AgoraTextureViewFactory () -//@property(nonatomic, weak) NSObject *registrar; -//@property(nonatomic, weak) NSObject *messenger; -//@property(nonatomic, strong) -// NSMutableDictionary *renderers; -//@end -// -//@implementation AgoraTextureViewFactory -//- (instancetype)initWithRegistrar: -// (NSObject *)registrar { -// self = [super init]; -// if (self) { -// self.registrar = [registrar textures]; -// self.messenger = [registrar messenger]; -// self.renderers = [NSMutableDictionary new]; -// } -// return self; -//} -// -//- (int64_t)createTextureRenderer:(void *)renderer { -// TextureRenderer *texture = [[TextureRenderer alloc] -// initWithTextureRegistry:self.registrar -// messenger:self.messenger -// renderer:(IrisVideoFrameBufferManager *)renderer]; -// int64_t textureId = [texture textureId]; -// self.renderers[@(textureId)] = texture; -// return textureId; -//} -// -//- (BOOL)destroyTextureRenderer:(int64_t)textureId { -// TextureRenderer *texture = [self.renderers objectForKey:@(textureId)]; -// if (texture != nil) { -// [texture destroy]; -// [self.renderers removeObjectForKey:@(textureId)]; -// return YES; -// } -// return NO; -//} -//@end diff --git a/ios/Classes/TextureRenderer.h b/ios/Classes/TextureRenderer.h deleted file mode 100644 index 3ca463926..000000000 --- a/ios/Classes/TextureRenderer.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef TextureRenderer_h -#define TextureRenderer_h - -#if TARGET_OS_IPHONE -#import -#else -#import -#endif - -@interface TextureRender : NSObject - -- (instancetype) -initWithTextureRegistry:(NSObject *)textureRegistry - messenger:(NSObject *)messenger - videoFrameBufferManager:(void *)manager; - -- (void)updateData:(NSNumber *)uid channelId:(NSString *)channelId videoSourceType:(NSNumber *)videoSourceType; - -- (void)dispose; - -@property(nonatomic, assign) int64_t textureId; - -@end - - -#endif /* TextureRenderer_h */ diff --git a/ios/Classes/TextureRenderer.h b/ios/Classes/TextureRenderer.h new file mode 120000 index 000000000..a38749a1b --- /dev/null +++ b/ios/Classes/TextureRenderer.h @@ -0,0 +1 @@ +../../shared/darwin/TextureRenderer.h \ No newline at end of file diff --git a/ios/Classes/TextureRenderer.mm b/ios/Classes/TextureRenderer.mm deleted file mode 100644 index 293f941b3..000000000 --- a/ios/Classes/TextureRenderer.mm +++ /dev/null @@ -1,138 +0,0 @@ -#import -#import "TextureRenderer.h" -#import -#import -#import - -using namespace agora::iris; - -@interface TextureRender () - -@property(nonatomic, weak) NSObject *textureRegistry; -@property(nonatomic, strong) FlutterMethodChannel *channel; -@property(nonatomic) CVPixelBufferRef buffer_cache; -@property(nonatomic, strong) dispatch_semaphore_t lock; -@property(nonatomic) agora::iris::IrisVideoFrameBufferManager *videoFrameBufferManager; - -@end - -namespace { -class RendererDelegate : public IrisVideoFrameBufferDelegate { -public: - RendererDelegate(void *renderer) : renderer_(renderer) {} - - void OnVideoFrameReceived(const IrisVideoFrame &video_frame, - const IrisVideoFrameBufferConfig *config, - bool resize) override { - @autoreleasepool { - TextureRender *renderer = (__bridge TextureRender *)renderer_; - CVPixelBufferRef buffer = renderer.buffer_cache; - - NSDictionary *cvBufferProperties = @{ - (__bridge NSString *)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA), - (__bridge NSString *)kCVPixelBufferIOSurfacePropertiesKey: @{}, - (__bridge NSString *)kCVPixelBufferOpenGLCompatibilityKey: @YES, - (__bridge NSString *)kCVPixelBufferMetalCompatibilityKey: @YES, - }; - - dispatch_semaphore_wait(renderer.lock, DISPATCH_TIME_FOREVER); - if (renderer.buffer_cache != NULL && resize) { - CVBufferRelease(renderer.buffer_cache); - renderer.buffer_cache = NULL; - } - - if (renderer.buffer_cache == NULL) { - CVPixelBufferCreate(kCFAllocatorDefault, video_frame.width, - video_frame.height, kCVPixelFormatType_32BGRA, - (__bridge CFDictionaryRef)cvBufferProperties, &buffer); - } - - CVPixelBufferLockBaseAddress(buffer, 0); - void *copyBaseAddress = CVPixelBufferGetBaseAddress(buffer); - memcpy(copyBaseAddress, video_frame.y_buffer, - video_frame.y_buffer_length); - CVPixelBufferUnlockBaseAddress(buffer, 0); - - renderer.buffer_cache = buffer; - - dispatch_semaphore_signal(renderer.lock); - - [renderer.textureRegistry textureFrameAvailable:renderer.textureId]; - } - } - -public: - void *renderer_; -}; -} - -@interface TextureRender () - -@property(nonatomic) RendererDelegate *delegate; - -@end - -@implementation TextureRender - -- (instancetype) initWithTextureRegistry:(NSObject *)textureRegistry - messenger:(NSObject *)messenger - videoFrameBufferManager:(void *)manager { - self = [super init]; - if (self) { - self.textureRegistry = textureRegistry; - self.videoFrameBufferManager = (IrisVideoFrameBufferManager *)manager; - self.textureId = [self.textureRegistry registerTexture:self]; - self.channel = [FlutterMethodChannel - methodChannelWithName: - [NSString stringWithFormat:@"agora_rtc_engine/texture_render_%lld", - self.textureId] - binaryMessenger:messenger]; - - self.lock = dispatch_semaphore_create(1); - self.delegate = new ::RendererDelegate((__bridge void *)self); - } - return self; -} - -- (void)updateData:(NSNumber *)uid channelId:(NSString *)channelId videoSourceType:(NSNumber *)videoSourceType { - IrisVideoFrameBuffer buffer(kVideoFrameTypeBGRA, - self.delegate, 16); - IrisVideoFrameBufferConfig config; - - config.id = [uid unsignedIntValue]; - config.type = (IrisVideoSourceType)[videoSourceType intValue]; - - if (channelId && (NSNull *)channelId != [NSNull null]) { - strcpy(config.key, [channelId UTF8String]); - - } else { - strcpy(config.key, ""); - } - - self.videoFrameBufferManager->EnableVideoFrameBuffer(buffer, &config); -} - -- (void)dispose { - self.videoFrameBufferManager->DisableVideoFrameBuffer(self.delegate); - if (self.delegate) { - delete self.delegate; - } - [self.textureRegistry unregisterTexture:self.textureId]; - if (self.buffer_cache) { - CVPixelBufferRelease(self.buffer_cache); - } -} - -- (CVPixelBufferRef _Nullable)copyPixelBuffer { - dispatch_semaphore_wait(self.lock, DISPATCH_TIME_FOREVER); - CVPixelBufferRef buffer_temp = CVPixelBufferRetain(self.buffer_cache); - dispatch_semaphore_signal(self.lock); - - return buffer_temp; -} - -- (void)onTextureUnregistered:(NSObject *)texture { -} - -@end - diff --git a/ios/Classes/TextureRenderer.mm b/ios/Classes/TextureRenderer.mm new file mode 120000 index 000000000..a344f3cdb --- /dev/null +++ b/ios/Classes/TextureRenderer.mm @@ -0,0 +1 @@ +../../shared/darwin/TextureRenderer.mm \ No newline at end of file diff --git a/ios/Classes/VideoViewController.h b/ios/Classes/VideoViewController.h deleted file mode 100644 index d76abc664..000000000 --- a/ios/Classes/VideoViewController.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef VideoViewController_h -#define VideoViewController_h - -#if TARGET_OS_IPHONE -#import -#else -#import -#endif - -@interface VideoViewController : NSObject - -- (instancetype)initWith:(NSObject *)textureRegistry - messenger: (NSObject *)messenger; - -- (int64_t)createPlatformRender; - -- (BOOL)destroyPlatformRender:(int64_t)platformRenderId; - -- (int64_t)createTextureRender:(NSNumber *)uid channelId:(NSString *)channelId videoSourceType:(NSNumber *)videoSourceType; - -- (BOOL)destroyTextureRender:(int64_t)textureId; - -@end - - -#endif /* VideoViewController_h */ diff --git a/ios/Classes/VideoViewController.h b/ios/Classes/VideoViewController.h new file mode 120000 index 000000000..9eeec8f9b --- /dev/null +++ b/ios/Classes/VideoViewController.h @@ -0,0 +1 @@ +../../shared/darwin/VideoViewController.h \ No newline at end of file diff --git a/ios/Classes/VideoViewController.mm b/ios/Classes/VideoViewController.mm deleted file mode 100644 index 35d93f188..000000000 --- a/ios/Classes/VideoViewController.mm +++ /dev/null @@ -1,137 +0,0 @@ -#import -#import "VideoViewController.h" -#import "TextureRenderer.h" -#import -#import - -@interface VideoViewController () -@property(nonatomic, weak) NSObject *textureRegistry; -@property(nonatomic, weak) NSObject *messenger; -@property(nonatomic) NSMutableDictionary *textureRenders; -@property(nonatomic) agora::iris::IrisVideoFrameBufferManager *videoFrameBufferManager; -//@property(nonatomic) intptr_t irisRtcEnginePtr; -@property(nonatomic, strong) FlutterMethodChannel *methodChannel; -@end - -@implementation VideoViewController - -- (instancetype)initWith:(NSObject *)textureRegistry - messenger: (NSObject *)messenger { - self = [super init]; - if (self) { - self.textureRegistry = textureRegistry; - self.messenger = messenger; - self.textureRenders = [NSMutableDictionary new]; -// self.irisRtcEnginePtr = 0; - self.videoFrameBufferManager = new agora::iris::IrisVideoFrameBufferManager; - - self.methodChannel = [FlutterMethodChannel - methodChannelWithName: - @"agora_rtc_ng/video_view_controller" - binaryMessenger:messenger]; - - __weak typeof(self) weakSelf = self; - [self.methodChannel setMethodCallHandler:^(FlutterMethodCall *_Nonnull call, - FlutterResult _Nonnull result) { - if (weakSelf != nil) { - [weakSelf onMethodCall:call result:result]; - } - }]; - - - - } - return self; -} - -- (void)onMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { - if ([@"attachVideoFrameBufferManager" isEqualToString:call.method]) { - NSNumber *enginePtrValue = call.arguments; -// if (self.irisRtcEnginePtr != 0) { -// result(@(YES)); -// return; -// } - intptr_t irisRtcEnginePtr = (intptr_t)[enginePtrValue longLongValue]; - IApiEngineBase *irisApiEngine = reinterpret_cast(irisRtcEnginePtr); -// agora::iris::rtc::IrisRtcRawData *rawData = engine->raw_data(); - irisApiEngine->Attach(self.videoFrameBufferManager); - - result(@((intptr_t)self.videoFrameBufferManager)); - } else if ([@"detachVideoFrameBufferManager" isEqualToString:call.method]) { - NSNumber *enginePtrValue = call.arguments; -// if (self.irisRtcEnginePtr == 0) { -// result(@(NO)); -// return; -// } - intptr_t irisRtcEnginePtr = (intptr_t)[enginePtrValue longLongValue]; - IApiEngineBase *irisApiEngine = reinterpret_cast(irisRtcEnginePtr); -// agora::iris::rtc::IrisRtcRawData *rawData = engine->raw_data(); - irisApiEngine->Detach(self.videoFrameBufferManager); - - result(@(YES)); - } - else if ([@"createTextureRender" isEqualToString:call.method]) { - NSDictionary *data = call.arguments; - NSNumber *uid = data[@"uid"]; - NSString *channelId = data[@"channelId"]; - NSNumber *videoSourceType = data[@"videoSourceType"]; - - int64_t textureId = [self createTextureRender:uid channelId:channelId videoSourceType:videoSourceType]; - result(@(textureId)); - } else if ([@"destroyTextureRender" isEqualToString:call.method]) { - NSNumber *textureIdValue = call.arguments; - BOOL success = [self destroyTextureRender: [textureIdValue longLongValue]]; - result(@(success)); - } else if ([@"updateTextureRenderData" isEqualToString:call.method]) { - NSDictionary *data = call.arguments; - int64_t textureId = [data[@"uid"] longLongValue]; - NSNumber *uid = data[@"uid"]; - NSString *channelId = data[@"channelId"]; - NSNumber *videoSourceType = data[@"videoSourceType"]; - - [self updateTextureRenderData:textureId uid:uid channelId:channelId videoSourceType:videoSourceType]; - result(@(YES)); - } -} - -- (int64_t)createPlatformRender { - return 0; -} - -- (BOOL)destroyPlatformRender:(int64_t)platformRenderId { - return true; -} - -- (int64_t)createTextureRender:(NSNumber *)uid channelId:(NSString *)channelId videoSourceType:(NSNumber *)videoSourceType { - TextureRender *textureRender = [[TextureRender alloc] - initWithTextureRegistry:self.textureRegistry - messenger:self.messenger - videoFrameBufferManager:self.videoFrameBufferManager]; - int64_t textureId = [textureRender textureId]; - [textureRender updateData:uid channelId:channelId videoSourceType:videoSourceType]; - self.textureRenders[@(textureId)] = textureRender; - return textureId; -} - -- (void)updateTextureRenderData:(int64_t)textureId uid:(NSNumber *)uid channelId:(NSString *)channelId videoSourceType:(NSNumber *)videoSourceType { - [self.textureRenders[@(textureId)] updateData:uid channelId:channelId videoSourceType:videoSourceType]; -} - -- (BOOL)destroyTextureRender:(int64_t)textureId { - TextureRender *textureRender = [self.textureRenders objectForKey:@(textureId)]; - if (textureRender != nil) { - [textureRender dispose]; - [self.textureRenders removeObjectForKey:@(textureId)]; - return YES; - } - return NO; -} - -- (void)dealloc { - if (self.videoFrameBufferManager) { - delete self.videoFrameBufferManager; - self.videoFrameBufferManager = nil; - } -} - -@end diff --git a/ios/Classes/VideoViewController.mm b/ios/Classes/VideoViewController.mm new file mode 120000 index 000000000..a73160490 --- /dev/null +++ b/ios/Classes/VideoViewController.mm @@ -0,0 +1 @@ +../../shared/darwin/VideoViewController.mm \ No newline at end of file diff --git a/lib/src/impl/agora_video_view_impl.dart b/lib/src/impl/agora_video_view_impl.dart index 81e4b53f9..96433d656 100644 --- a/lib/src/impl/agora_video_view_impl.dart +++ b/lib/src/impl/agora_video_view_impl.dart @@ -181,7 +181,8 @@ class _AgoraRtcRenderTextureState extends State @override void maybeCreateChannel(int viewId, String viewType) { - if (defaultTargetPlatform != TargetPlatform.macOS) { + if (!(defaultTargetPlatform == TargetPlatform.macOS || + defaultTargetPlatform == TargetPlatform.iOS)) { return; } @@ -256,7 +257,8 @@ class _AgoraRtcRenderTextureState extends State @override Widget build(BuildContext context) { if (widget.controller.getTextureId() != kTextureNotInit) { - if (defaultTargetPlatform != TargetPlatform.macOS) { + if (!(defaultTargetPlatform == TargetPlatform.macOS || + defaultTargetPlatform == TargetPlatform.iOS)) { return buildTexure(widget.controller.getTextureId()); } diff --git a/test_shard/rendering_test/README.md b/test_shard/rendering_test/README.md index 501998b38..1b72f9e90 100644 --- a/test_shard/rendering_test/README.md +++ b/test_shard/rendering_test/README.md @@ -1,14 +1,36 @@ # rendering_test -A new Flutter project. +## Running rendering test +### Android/iOS + +``` +flutter drive --driver=test_driver/integration_test.dart \ + --target=integration_test/agora_video_view_render_test.dart \ + --dart-define=TEST_APP_ID="" +``` + +### macOS/Windows -## Getting Started +``` +flutter test integration_test/agora_video_view_render_test.dart \ + --dart-define=TEST_APP_ID="" +``` + +## Update screenshot ### Android/iOS +To update the screenshot, pass the `export UPDATE_GOLDEN="true"` in command line. + ``` +export UPDATE_GOLDEN="true" +flutter drive --driver=test_driver/integration_test.dart \ + --target=integration_test/agora_video_view_render_test.dart \ + --dart-define=TEST_APP_ID="" ``` ### macOS/Windows +To update the screenshot, pass the `--dart-define=UPDATE_GOLDEN=true` to the `flutter test` command. + ``` flutter test integration_test/agora_video_view_render_test.dart \ --dart-define=TEST_APP_ID="" \ diff --git a/test_shard/rendering_test/integration_test/agora_video_view_render_test.dart b/test_shard/rendering_test/integration_test/agora_video_view_render_test.dart index 2db27a00f..f2d19902b 100644 --- a/test_shard/rendering_test/integration_test/agora_video_view_render_test.dart +++ b/test_shard/rendering_test/integration_test/agora_video_view_render_test.dart @@ -153,15 +153,17 @@ void main() { ); group( - 'AgoraVideoView iOS', + 'AgoraVideoView iOS screenshot test', () { group('platform view', () { testWidgets( - 'local rendering screenshot test', + 'local rendering', (WidgetTester tester) async { final onFrameCompleter = Completer(); await tester.pumpWidget(LocalVideoView( + url: + 'https://download.agora.io/demo/test/agoravideoview_rendering_test_solid_spilt_asymmetrical.mp4', onRendered: (RtcEngineEx rtcEngine) async { if (onFrameCompleter.isCompleted) { return; @@ -186,12 +188,14 @@ void main() { ); testWidgets( - 'remote rendering screenshot test', + 'remote rendering', (WidgetTester tester) async { final onFrameCompleter = Completer(); await tester.pumpWidget(RemoteVideoView( renderModeType: RenderModeType.renderModeFit, + url: + 'https://download.agora.io/demo/test/agoravideoview_rendering_test_solid_spilt_asymmetrical.mp4', onRendered: (RtcEngineEx rtcEngine) async { if (onFrameCompleter.isCompleted) { return; @@ -217,71 +221,374 @@ void main() { group( 'flutter texture', () { - testWidgets( - 'local rendering screenshot test', - (WidgetTester tester) async { - final onFrameCompleter = Completer(); + group('local rendering', () { + testWidgets( + 'do not handle render mode', + (WidgetTester tester) async { + final onFrameCompleter = Completer(); + + await tester.pumpWidget(LocalVideoView( + useFlutterTexture: true, + isRenderModeTest: false, + url: + 'https://download.agora.io/demo/test/agoravideoview_rendering_test_solid_spilt_asymmetrical.mp4', + onRendered: (RtcEngineEx engine) async { + if (onFrameCompleter.isCompleted) { + return; + } + + onFrameCompleter.complete(); + }, + )); + + await tester.pumpAndSettle(const Duration(seconds: 10)); + + await onFrameCompleter.future; + await tester.pumpAndSettle(const Duration(seconds: 10)); + + await binding.takeScreenshot( + 'ios.agora_video_view_render.texture.local.donot_handle_rendermode'); + + await waitDisposed(tester, binding); + }, + ); - await tester.pumpWidget(LocalVideoView( - useFlutterTexture: true, - onRendered: (RtcEngineEx rtcEngine) async { - if (onFrameCompleter.isCompleted) { - return; - } + testWidgets( + 'render mode default', + (WidgetTester tester) async { + final onFrameCompleter = Completer(); - onFrameCompleter.complete(); - }, - )); + await tester.pumpWidget(LocalVideoView( + useFlutterTexture: true, + url: + 'https://download.agora.io/demo/test/agoravideoview_rendering_test_solid_spilt_asymmetrical.mp4', + onRendered: (RtcEngineEx engine) async { + if (onFrameCompleter.isCompleted) { + return; + } - await tester.pumpAndSettle(const Duration(seconds: 10)); + onFrameCompleter.complete(); + }, + )); - await onFrameCompleter.future; - await tester.pumpAndSettle(const Duration(seconds: 10)); + await tester.pumpAndSettle(const Duration(seconds: 10)); - await tester.pumpAndSettle(); + await onFrameCompleter.future; + await tester.pumpAndSettle(const Duration(seconds: 10)); - await binding - .takeScreenshot('ios.agora_video_view_render.texture.local'); + await binding.takeScreenshot( + 'ios.agora_video_view_render.texture.local.with_default_rendermode'); - await waitDisposed(tester, binding); - }, - // TODO(littlegnal): Flutter texture with software rendering on simulator is not supported. - // The log like below: - // Flutter: Attempted to composite external texture sources using the software backend. - // This backend is only used on simulators. This feature is only available - // on actual devices where OpenGL or Metal is used for rendering. - // - // Temporily skip flutter texture rendering test on ios at this time. - skip: true, - ); - - testWidgets( - 'remote rendering screenshot test', - (WidgetTester tester) async { - final onFrameCompleter = Completer(); - - await tester.pumpWidget(RemoteVideoView( - useFlutterTexture: true, - onRendered: (RtcEngineEx rtcEngine) async { - if (onFrameCompleter.isCompleted) { - return; - } - - onFrameCompleter.complete(); - }, - )); - - await tester.pumpAndSettle(const Duration(seconds: 10)); - - await onFrameCompleter.future; - await tester.pumpAndSettle(const Duration(seconds: 10)); - - await binding - .takeScreenshot('ios.agora_video_view_render.texture.remote'); - - await waitDisposed(tester, binding); - }, - ); + await waitDisposed(tester, binding); + }, + ); + + testWidgets( + 'render mode renderModeHidden', + (WidgetTester tester) async { + final onFrameCompleter = Completer(); + + await tester.pumpWidget(LocalVideoView( + useFlutterTexture: true, + url: + 'https://download.agora.io/demo/test/agoravideoview_rendering_test_solid_spilt_asymmetrical.mp4', + renderModeType: RenderModeType.renderModeHidden, + onRendered: (RtcEngineEx engine) async { + if (onFrameCompleter.isCompleted) { + return; + } + + onFrameCompleter.complete(); + }, + )); + + await tester.pumpAndSettle(const Duration(seconds: 10)); + + await onFrameCompleter.future; + await tester.pumpAndSettle(const Duration(seconds: 10)); + + await binding.takeScreenshot( + 'ios.agora_video_view_render.texture.local.with_rendermodehidden'); + + await waitDisposed(tester, binding); + }, + ); + + testWidgets( + 'render mode renderModeFit', + (WidgetTester tester) async { + final onFrameCompleter = Completer(); + + await tester.pumpWidget(LocalVideoView( + useFlutterTexture: true, + url: + 'https://download.agora.io/demo/test/agoravideoview_rendering_test_solid_spilt_asymmetrical.mp4', + renderModeType: RenderModeType.renderModeFit, + onRendered: (RtcEngineEx engine) async { + if (onFrameCompleter.isCompleted) { + return; + } + + onFrameCompleter.complete(); + }, + )); + + await tester.pumpAndSettle(const Duration(seconds: 10)); + + await onFrameCompleter.future; + await tester.pumpAndSettle(const Duration(seconds: 10)); + + await binding.takeScreenshot( + 'ios.agora_video_view_render.texture.local.with_rendermodefit'); + + await waitDisposed(tester, binding); + }, + ); + + testWidgets( + 'render mode renderModeAdaptive', + (WidgetTester tester) async { + final onFrameCompleter = Completer(); + + await tester.pumpWidget(LocalVideoView( + useFlutterTexture: true, + url: + 'https://download.agora.io/demo/test/agoravideoview_rendering_test_solid_spilt_asymmetrical.mp4', + renderModeType: RenderModeType.renderModeAdaptive, + onRendered: (RtcEngineEx engine) async { + if (onFrameCompleter.isCompleted) { + return; + } + + onFrameCompleter.complete(); + }, + )); + + await tester.pumpAndSettle(const Duration(seconds: 10)); + + await onFrameCompleter.future; + await tester.pumpAndSettle(const Duration(seconds: 10)); + + await binding.takeScreenshot( + 'ios.agora_video_view_render.texture.local.with_rendermodeadaptive'); + + await waitDisposed(tester, binding); + }, + ); + + testWidgets( + 'render mode default and videoMirrorModeDisabled', + (WidgetTester tester) async { + final onFrameCompleter = Completer(); + + await tester.pumpWidget(LocalVideoView( + useFlutterTexture: true, + url: + 'https://download.agora.io/demo/test/agoravideoview_rendering_test_solid_spilt_asymmetrical.mp4', + mirrorModeType: VideoMirrorModeType.videoMirrorModeDisabled, + onRendered: (RtcEngineEx engine) async { + if (onFrameCompleter.isCompleted) { + return; + } + + onFrameCompleter.complete(); + }, + )); + + await tester.pumpAndSettle(const Duration(seconds: 10)); + + await onFrameCompleter.future; + await tester.pumpAndSettle(const Duration(seconds: 10)); + + await binding.takeScreenshot( + 'ios.agora_video_view_render.texture.local.with_default_rendermode.with_videomirrormodedisabled'); + + await waitDisposed(tester, binding); + }, + ); + }); + + group('remote rendering', () { + testWidgets( + 'do not handle render mode', + (WidgetTester tester) async { + final onFrameCompleter = Completer(); + + await tester.pumpWidget(RemoteVideoView( + useFlutterTexture: true, + url: + 'https://download.agora.io/demo/test/agoravideoview_rendering_test_solid_spilt_asymmetrical.mp4', + onRendered: (RtcEngineEx engine) async { + if (onFrameCompleter.isCompleted) { + return; + } + + onFrameCompleter.complete(); + }, + )); + + await tester.pumpAndSettle(const Duration(seconds: 10)); + + await onFrameCompleter.future; + await tester.pumpAndSettle(const Duration(seconds: 10)); + + await binding.takeScreenshot( + 'ios.agora_video_view_render.texture.remote.donot_handle_rendermode'); + + await waitDisposed(tester, binding); + }, + ); + + testWidgets( + 'render mode default', + (WidgetTester tester) async { + final onFrameCompleter = Completer(); + + await tester.pumpWidget(RemoteVideoView( + useFlutterTexture: true, + url: + 'https://download.agora.io/demo/test/agoravideoview_rendering_test_solid_spilt_asymmetrical.mp4', + onRendered: (RtcEngineEx engine) async { + if (onFrameCompleter.isCompleted) { + return; + } + + onFrameCompleter.complete(); + }, + )); + + await tester.pumpAndSettle(const Duration(seconds: 10)); + + await onFrameCompleter.future; + await tester.pumpAndSettle(const Duration(seconds: 10)); + + await binding.takeScreenshot( + 'ios.agora_video_view_render.texture.remote.with_default_rendermode'); + + await waitDisposed(tester, binding); + }, + ); + + testWidgets( + 'render mode renderModeHidden', + (WidgetTester tester) async { + final onFrameCompleter = Completer(); + + await tester.pumpWidget(RemoteVideoView( + useFlutterTexture: true, + url: + 'https://download.agora.io/demo/test/agoravideoview_rendering_test_solid_spilt_asymmetrical.mp4', + renderModeType: RenderModeType.renderModeHidden, + onRendered: (RtcEngineEx engine) async { + if (onFrameCompleter.isCompleted) { + return; + } + onFrameCompleter.complete(); + }, + )); + + await tester.pumpAndSettle(const Duration(seconds: 10)); + + await onFrameCompleter.future; + await tester.pumpAndSettle(const Duration(seconds: 10)); + + await binding.takeScreenshot( + 'ios.agora_video_view_render.texture.remote.with_rendermodehidden'); + + await waitDisposed(tester, binding); + }, + ); + + testWidgets( + 'render mode renderModeFit', + (WidgetTester tester) async { + final onFrameCompleter = Completer(); + + await tester.pumpWidget(RemoteVideoView( + useFlutterTexture: true, + url: + 'https://download.agora.io/demo/test/agoravideoview_rendering_test_solid_spilt_asymmetrical.mp4', + renderModeType: RenderModeType.renderModeFit, + onRendered: (RtcEngineEx engine) async { + if (onFrameCompleter.isCompleted) { + return; + } + onFrameCompleter.complete(); + }, + )); + + await tester.pumpAndSettle(const Duration(seconds: 10)); + + await onFrameCompleter.future; + await tester.pumpAndSettle(const Duration(seconds: 10)); + + await binding.takeScreenshot( + 'ios.agora_video_view_render.texture.remote.with_rendermodefit'); + + await waitDisposed(tester, binding); + }, + ); + + testWidgets( + 'render mode renderModeAdaptive', + (WidgetTester tester) async { + final onFrameCompleter = Completer(); + + await tester.pumpWidget(RemoteVideoView( + useFlutterTexture: true, + url: + 'https://download.agora.io/demo/test/agoravideoview_rendering_test_solid_spilt_asymmetrical.mp4', + renderModeType: RenderModeType.renderModeAdaptive, + onRendered: (RtcEngineEx engine) async { + if (onFrameCompleter.isCompleted) { + return; + } + onFrameCompleter.complete(); + }, + )); + + await tester.pumpAndSettle(const Duration(seconds: 10)); + + await onFrameCompleter.future; + await tester.pumpAndSettle(const Duration(seconds: 10)); + + await binding.takeScreenshot( + 'ios.agora_video_view_render.texture.remote.with_rendermodeadaptive'); + + await waitDisposed(tester, binding); + }, + ); + + testWidgets( + 'render mode default and videoMirrorModeDisabled', + (WidgetTester tester) async { + final onFrameCompleter = Completer(); + + await tester.pumpWidget(RemoteVideoView( + useFlutterTexture: true, + url: + 'https://download.agora.io/demo/test/agoravideoview_rendering_test_solid_spilt_asymmetrical.mp4', + mirrorModeType: VideoMirrorModeType.videoMirrorModeEnabled, + onRendered: (RtcEngineEx engine) async { + if (onFrameCompleter.isCompleted) { + return; + } + onFrameCompleter.complete(); + }, + )); + + await tester.pumpAndSettle(const Duration(seconds: 10)); + + await onFrameCompleter.future; + await tester.pumpAndSettle(const Duration(seconds: 10)); + + await binding.takeScreenshot( + 'ios.agora_video_view_render.texture.remote.with_default_rendermodede.with_videoMirrorModeEnabled'); + + await waitDisposed(tester, binding); + }, + ); + }); }, // TODO(littlegnal): Flutter texture with software rendering on simulator is not supported. // The log like below: diff --git a/test_shard/rendering_test/screenshot/ios.agora_video_view_render.platformview.local.png b/test_shard/rendering_test/screenshot/ios.agora_video_view_render.platformview.local.png index 093e10400f7d33bdc3b77c6d42967dd723e558fd..83a7f883db454c032f03aa497d7517382ed39e80 100644 GIT binary patch literal 1592 zcmeAS@N?(olHy`uVBq!ia0y~yV4MKL9Be?5hW%z|7#LXpd%8G=R4~51deG3tk-_!i ze_qcMj%-2`7rL-4I+BoReAcBQI(Wagb^6C^`Q`b?r}Hw@y)662$nZ~%kx@ZVfI(=m zQ8tktjbmlzzu9~?#;4`nuG~Mf z_G#8M&J&M6{{?3`J=MqqMvROPBAA#QloS|LhAnlPC8fvkH`9Urn}$*bJE`uiB`BFm?3D#7Tz)ch*#-Id80W2#y%1IM^vk z;R&B@%KaIBOx$|y9jCvcngTFVdQ&MBb@0N`f4tpET3 diff --git a/test_shard/rendering_test/screenshot/ios.agora_video_view_render.platformview.remote.png b/test_shard/rendering_test/screenshot/ios.agora_video_view_render.platformview.remote.png index b406592fd33dd1e2e4e1b4edd04a2e4210c71de7..e1df1d0fbf137bb7415594798d0a903953f869be 100644 GIT binary patch literal 1760 zcmeAS@N?(olHy`uVBq!ia0y~yV4MKL9Be?5hW%z|7#P@=d%8G=R4~51;g}`kF41uD z-82V_*D*>DnxF94`SwlP{6i(OTF|5E;EgUV-D;gBzHe%Jmajck?R@x3(u4MOk6yog zy)l~Mf&DJKDh7sm>WqvEf&vUegPod`$+T(R>x;?^HB$|bT~wZP)P#}o!6p`#1{Vhg zk6}wK<2>;?e7~$MQ~Za^XJy7cR3m+eF?fJUp^6_mskIN+W>usu*Y@Vs6 zzz{R7fnfp%2Sex3r^Jo>u2TN`wxi! z`FXfKwfcA7&23*Fp1pe6{+B`b@!ysIuNQ9DtIrSr&3xr;(bq?RKRw#8^IZ0CCAe%fHfC`r>mdKI;Vst0P1piivR!s delta 242 zcmaFByOw8yvK{LePZ!6K3dXlrH~O*|inv~s-6Ev8HGnhs0)wf*g#*i`IF!$xsmZv` zEjjTV=bJCC|KpCoV`A7}GS`-&VZY~ON5l-x#Ku*d2ev*&d;$z%a*qvLoxo$qtNs0zk__ lwle$(hZrz9k$Jh;PqybzrR?0VJz2y61fH&bF6*2UngAJPV?_V} diff --git a/test_shard/rendering_test/screenshot/ios.agora_video_view_render.texture.local.donot_handle_rendermode.png b/test_shard/rendering_test/screenshot/ios.agora_video_view_render.texture.local.donot_handle_rendermode.png new file mode 100644 index 0000000000000000000000000000000000000000..513d10dd24ffdb93b39834f6f95b38061d365040 GIT binary patch literal 1591 zcmeAS@N?(olHy`uVBq!ia0y~yV4MKL9Be?5hW%z|7#LXpdAc};R4~51deE2Ikiqrf zU6w#r-3v^u7ZiFgaC~VvqNx@#;X_MOVw%qV{rYv~k8_wCb|r zRLabTH^1$wnGS>=lh~~Orz@3-=|Cw!X*duMwr>c?ez-gQ(GG)^JA2c2QwsY>b-#3jDy8iy|`y9XZ z-1gn)yywNqrTH`LIAt+kbVQ^#U>Vb62=wuf@M*x3WxJ{Zg9;OqgVJE9KCwQnlyg>} TEjb%lAuxEl`njxgN@xNA6VP1O literal 0 HcmV?d00001 diff --git a/test_shard/rendering_test/screenshot/ios.agora_video_view_render.texture.local.png b/test_shard/rendering_test/screenshot/ios.agora_video_view_render.texture.local.png deleted file mode 100644 index f723e159cd42f20cd6b5bc37ef0abfc6072a45c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1542 zcmeAS@N?(olHy`uVBq!ia0y~yV4MKL9Be?5hW%z|7#LX3db&7#g0|Ser z&ct9|P1_4>n!XbzTw+sgicV<#^JwNoz5O}g*I(shu-hVE%g}INMS($uiOE4}uv139 z4tmdjzu@+HvCX<~-+`34*L>$MzmSeM^mSmkF|C1N0tW{}Czv|WHZ4`M_F*;)OM{C8 zgU2W}8U&-MV5DY*Wt=A-SIX8hK3MM~EWof$b+8w6&~o?_+lmbSAD3UmJ_nZX44$rj JF6*2UngB1IB_sd< diff --git a/test_shard/rendering_test/screenshot/ios.agora_video_view_render.texture.local.with_default_rendermode.png b/test_shard/rendering_test/screenshot/ios.agora_video_view_render.texture.local.with_default_rendermode.png new file mode 100644 index 0000000000000000000000000000000000000000..4601b2fbecb2109b2c2a1f5cc9c9c4f26b7505b9 GIT binary patch literal 1584 zcmeAS@N?(olHy`uVBq!ia0y~yV4MKL9Be?5hW%z|7#LW;d%8G=R4~51YN*GO$l&mB zV~0jd(9$OYehVKp={-_gw?Np)S#iHM$3xht4M;$o$ z^TB8w5f?`XuH{avyd|^8?{krbRjX{&S%+usr!R6Im|hvRZ1d$xt4e#hxrg}HrYVSj pzrI+wL0->|eM9k(Dn~vs?p!7t!&QIl9k3=~@O1TaS?83{1ORMZ4|xCp literal 0 HcmV?d00001 diff --git a/test_shard/rendering_test/screenshot/ios.agora_video_view_render.texture.local.with_default_rendermode.with_videomirrormodedisabled.png b/test_shard/rendering_test/screenshot/ios.agora_video_view_render.texture.local.with_default_rendermode.with_videomirrormodedisabled.png new file mode 100644 index 0000000000000000000000000000000000000000..8796b95414063ba49dd214e7383a3f046a08616d GIT binary patch literal 1594 zcmeAS@N?(olHy`uVBq!ia0y~yV4MKL9Be?5hW%z|7#P?XJzX3_Dj45hHS}Um6mh%g z>7x_O6(+WePh7D~?^5Uvj_C3P)g~3U0_HfA{}ywC_kMb8T3&21i{U}L*eGz$Z``yQ~FD>yKXEZPrL@_ZrC@Cr`*Gf7NE#pQ-ohkSl=e=rvl7GUu6c3|*eVQFv~?9>xsCQb3u$1A>F0@eu(p00i_ I>zopr06uU4%K!iX literal 0 HcmV?d00001 diff --git a/test_shard/rendering_test/screenshot/ios.agora_video_view_render.texture.local.with_rendermodeadaptive.png b/test_shard/rendering_test/screenshot/ios.agora_video_view_render.texture.local.with_rendermodeadaptive.png new file mode 100644 index 0000000000000000000000000000000000000000..130c0038e8db152a906903c7fbd6e9fe26ea8c08 GIT binary patch literal 1601 zcmeAS@N?(olHy`uVBq!ia0y~yV4MKL9Be?5hW%z|7#P?%JY5_^Dj45h-RP_0DB^li zDs4l-gaZc;DsdljxYaws=R`xq1c8pc8@>#Myj)f1HlC`9PrY;aS0cj!aoIF}h6m+B z0t`Zoj0%E-ox17cFz=n*vD=%@%wsHGtN!@oo2qYIc7@gZd+l%eI57N(V`6epQeaRS zrAC8bG!=~Sj4+$?#HYejCx01TyDRbfjiJHGI-CB~yXWT}oOr{>;oiTPzPr2r)c^11 zkXd~FZe5J5U;6n69yw|X&+GHk{2SySCVgY*JC?@8bm03?DIF`>H;O7mEVnv$5m+@a Nc)I$ztaD0e0swI$)&&3n literal 0 HcmV?d00001 diff --git a/test_shard/rendering_test/screenshot/ios.agora_video_view_render.texture.local.with_rendermodefit.png b/test_shard/rendering_test/screenshot/ios.agora_video_view_render.texture.local.with_rendermodefit.png new file mode 100644 index 0000000000000000000000000000000000000000..3515dcad36875dcf4f90e95f917bc4961b1bc239 GIT binary patch literal 1588 zcmeAS@N?(olHy`uVBq!ia0y~yV4MKL9Be?5hW%z|7#LW8d%8G=R4~51cF>pEQNZ=! z){nfM0w%=@4|jgy1nw~zpX5F?|4;BciP)fD8<=imPR)25p3jA`HP z-+v#!5xvuq%EWYF9}7!^ivxqlC^Z@c^KuI8PjYeHonD^t1QuJPk}_wZQz=*}yP?W4KZ}!V?US0ljRVIaHRwl{Y YDJY#tt9W?|SQ{{Sy85}Sb4q9e0K+F9IRF3v literal 0 HcmV?d00001 diff --git a/test_shard/rendering_test/screenshot/ios.agora_video_view_render.texture.local.with_rendermodehidden.png b/test_shard/rendering_test/screenshot/ios.agora_video_view_render.texture.local.with_rendermodehidden.png new file mode 100644 index 0000000000000000000000000000000000000000..4601b2fbecb2109b2c2a1f5cc9c9c4f26b7505b9 GIT binary patch literal 1584 zcmeAS@N?(olHy`uVBq!ia0y~yV4MKL9Be?5hW%z|7#LW;d%8G=R4~51YN*GO$l&mB zV~0jd(9$OYehVKp={-_gw?Np)S#iHM$3xht4M;$o$ z^TB8w5f?`XuH{avyd|^8?{krbRjX{&S%+usr!R6Im|hvRZ1d$xt4e#hxrg}HrYVSj pzrI+wL0->|eM9k(Dn~vs?p!7t!&QIl9k3=~@O1TaS?83{1ORMZ4|xCp literal 0 HcmV?d00001 diff --git a/test_shard/rendering_test/screenshot/ios.agora_video_view_render.texture.remote.donot_handle_rendermode.png b/test_shard/rendering_test/screenshot/ios.agora_video_view_render.texture.remote.donot_handle_rendermode.png new file mode 100644 index 0000000000000000000000000000000000000000..87305d2715eedb2a9fb3dbdbabbcde0dde220b4d GIT binary patch literal 1697 zcmeAS@N?(olHy`uVBq!ia0y~yV4MKL9Be?5hW%z|7#P?xJY5_^Dj471a?BD=WN3Rh z(z0_vm!&C>zEmb0`fy%n-(7E~YnQJv zF#ON3{m#Jf&!36OK}mr@WzbXIoF`t3=RcdbVR~P`9q;n|`@H4*;^wSf*8My4PTcPK ztmd~au6|zZzeB8>lY?Qm>QL~~pbzs=2M+#xF&al`aRe+VEP9Lm8FmDU2~Ws%dM3wE zuvkY!;mxg7iO!v?WdbN+oXJ8)lkLhI~fFOQeLd~>SUmQji^a>wSYX*YUiKYU}O zV6R-XQi6#o1ZdXr>i5rqg^u5~__oWp=ePfEXMeswz1BXOljF>*hj*CQolIgX`t#=H z9pP`6ns=uko;;cVw!VD(ex{S6G7^>bP0l+XkKL#7gI literal 0 HcmV?d00001 diff --git a/test_shard/rendering_test/screenshot/ios.agora_video_view_render.texture.remote.with_default_rendermode.png b/test_shard/rendering_test/screenshot/ios.agora_video_view_render.texture.remote.with_default_rendermode.png new file mode 100644 index 0000000000000000000000000000000000000000..ab042700f38d74c7d0f1f2d91ee769b531cd364d GIT binary patch literal 1838 zcmeAS@N?(olHy`uVBq!ia0y~yV4MKL9Be?5hW%z|7#P^Tdb&7(8X0_C%@nhkvt<9ef~I{qNV+JIXa8-JBc@^QSd1OyJ;P=p;m)IOJR27d`vquk#}B z2LXFK5B6B&I{{mUC}=+BdN4 zswq6)GON19cx&wRySIGUy9#uK+Sjq}NH=7hS@TlXgm-qX^Rm~Ezui3k_VvPN_iF8u zJR60@gayt!@a>+puI=j%IkVOE%DZprT}`WA)31B;?9a@_3I__iIZx!Dzjw~|P~}ax zk4zV|&0IR2tTNO?oGI z?sT2FA#c2`Y}@;L{|tM=ci)a!op>kL{+jvqn!+cWfA5Zc#W-i%jE0)uf2SAs7aW`R oshZtFouuNCh!T{hN>1M2I4jBNcf$oWU_Hp->FVdQ&MBb@0L>wbu>b%7 literal 0 HcmV?d00001 diff --git a/test_shard/rendering_test/screenshot/ios.agora_video_view_render.texture.remote.png b/test_shard/rendering_test/screenshot/ios.agora_video_view_render.texture.remote.with_default_rendermodede.with_videoMirrorModeEnabled.png similarity index 53% rename from test_shard/rendering_test/screenshot/ios.agora_video_view_render.texture.remote.png rename to test_shard/rendering_test/screenshot/ios.agora_video_view_render.texture.remote.with_default_rendermodede.with_videoMirrorModeEnabled.png index 11551e960c48726a39bb697eaa95b014f6ef698c..500fa5fc0f0f7bfcde123d71123757e29f6cedfe 100644 GIT binary patch literal 1698 zcmeAS@N?(olHy`uVBq!ia0y~yV4MKL9Be?5hW%z|7#P?xJzX3_Dj471^v}9dz|i(^ z$vLMtZb$7;KhlhODQnZ>{#i!uz~L!Qf*ZFL?wY{o#2Rn)dGXBWjSgy?e~O*lGvTIA zv@pY;1nQDjubC^P@*zxB_4o_ttla3qUq)0zJ}CO7WAT^n6m{cdK={;Moco;|7h!eXJd zeeaFjBWLF~nvPef+Y^{`Cu0pM=}_dvi~J{kdaLjNjttf4VZ6m<}X! zaxio@FihYWrAC8bG!=|y1YlX) diff --git a/test_shard/rendering_test/screenshot/ios.agora_video_view_render.texture.remote.with_rendermodeadaptive.png b/test_shard/rendering_test/screenshot/ios.agora_video_view_render.texture.remote.with_rendermodeadaptive.png new file mode 100644 index 0000000000000000000000000000000000000000..6142dcc5fb399ec2d107c0c4b4f364ee6e656657 GIT binary patch literal 1794 zcmeAS@N?(olHy`uVBq!ia0y~yV4MKL9Be?5hW%z|7#P@2db&71xsw7Uc5xbFKqvm z?D}(Oj~1FT9uVJkqnd%?-+UI91{Vhgk3mnhah_NmAOGH#sl1}<=Wmn$U%!>u$gj7L z=CkHw`Tn3H;@a{`+gZyie;Vwq{+3bo>GtQn$E~^(nV1gz6%t?&Vq{bh9HmBsU^Eqs zpp4M4{>=tk+5Eo#YQLhm!z@nQ6CbXb*6+D{ z*y^r*Z?FL4!+SM0F}|PweEfCxtmPICj>FN{3UcZj)+knPSd;xP>wV+7v>6S5{{Ed` z-G4y#-;N67Si742_a@5vJ-_VcAaQ@wy6>;w-d@cvzwG7i(=U(jb+?+F$n@!S_x$g2 zjn;n*_Ri#cpLc5G-VXNrO}W2cl`P-lmhv+_s!r6M_7R2 m-Sh^A2^<^@or9e^slH1iFtP2+<6vOj#^CAd=d#Wzp$P!}IaPE3 literal 0 HcmV?d00001 diff --git a/test_shard/rendering_test/screenshot/ios.agora_video_view_render.texture.remote.with_rendermodefit.png b/test_shard/rendering_test/screenshot/ios.agora_video_view_render.texture.remote.with_rendermodefit.png new file mode 100644 index 0000000000000000000000000000000000000000..9afa18d7b9921b2bcf6815bf6f1fc2277e1ce612 GIT binary patch literal 1911 zcmeAS@N?(olHy`uVBq!ia0y~yV4MKL9Be?5hW%z|7#P^?JY5_^Dj471NzA*Q!qAXt zeN%u#@n0ePKZSJ%MJ+gwKeXTeKp}>qvE_owsulb)c3ko!J@0Rc1UH^RFoZ2(l4!}h8 zVDH-e#JA6bw=gj|C@Ct2nu|YUo*y>%VYZVW6qn5WAnBY<~Q2To_@DxU$w>j z@}Gw@|6WauEWTBiA--jH!yLZt{Oe=X@_E?T`ksBg`S}LFBR_wd%;qb5v#0v}_qtj6 z)Bo1l3dl}BUY}#Wf%~?vL*L_v_O3SnxThVM|4dcp^d^xMyYRpFUhR)tb2k5u)V!|G zAJaOIzWh4#*r!98ch~ZtzIi+|)>L+z>;u-fY6|-E{{Q=MD%DBe_eA9(d5eOz(spG& zo8MaJ|9+jm-*^6!*`1 literal 0 HcmV?d00001 diff --git a/test_shard/rendering_test/screenshot/ios.agora_video_view_render.texture.remote.with_rendermodehidden.png b/test_shard/rendering_test/screenshot/ios.agora_video_view_render.texture.remote.with_rendermodehidden.png new file mode 100644 index 0000000000000000000000000000000000000000..fd744ede793b857df0c0314245ca854ec3ac80f7 GIT binary patch literal 1785 zcmeAS@N?(olHy`uVBq!ia0y~yV4MKL9Be?5hW%z|7#P?Nc)B=-R4~51Ds$jr z6))RS)?E*T?m3jrIozUM#KHgQ*p3NTRoYk@D?`pZ7zkB;Q1{z<^!w7)t9M_T`zLsI ze4OsRyE~&89?0+7{gr{?k3A;`LuUiS1dhQ@y-`!J|NrImHRU-=<>mK9)cyau{pr8c zsXJv(8(q&%p7-y(#B2WJYkSz^m%N{Ecdx4De3CgMtN;5A0V^U{GOVa!?wjMuT896%6@|P?I4SU;J9L!B}0PCLvCo<-wA> zEKiDJ@@x-P?vgp3D0l5~tuUYYkwm|*40+pTH^>}Id@XqH(wus|klkdMC4Py2A0QIaQC!zdrGDXt)ei$L_!HR#l5}=^p*~d|Q?c&bxsn+;(pV1`igN f2A9E3J>d=C?rUevE06@N(il8l{an^LB{Ts5EHHl| literal 0 HcmV?d00001