From 4099203b57a5a8d24787c6c87b3aed9acdf9799c Mon Sep 17 00:00:00 2001 From: xucz Date: Thu, 2 Feb 2023 23:12:46 +0800 Subject: [PATCH 001/154] [Android]fix bytedance beauty mirror bug --- .../example/examples/advanced/beauty/ByteDanceBeauty.java | 2 +- .../io/agora/beauty/bytedance/BeautyByteDanceImpl.java | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/beauty/ByteDanceBeauty.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/beauty/ByteDanceBeauty.java index 7df8c25d8..a70ac1dd5 100644 --- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/beauty/ByteDanceBeauty.java +++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/beauty/ByteDanceBeauty.java @@ -315,7 +315,7 @@ private void joinChannel() { mLocalVideoLayout = new VideoReportLayout(requireContext()); TextureView videoView = new TextureView(requireContext()); - rtcEngine.setupLocalVideo(new VideoCanvas(videoView, Constants.RENDER_MODE_HIDDEN)); + rtcEngine.setupLocalVideo(new VideoCanvas(videoView, Constants.RENDER_MODE_HIDDEN, Constants.VIDEO_MIRROR_MODE_DISABLED, 0)); mLocalVideoLayout.addView(videoView); rtcEngine.startPreview(); diff --git a/Android/APIExample/beauty/bytedance/src/main/java/io/agora/beauty/bytedance/BeautyByteDanceImpl.java b/Android/APIExample/beauty/bytedance/src/main/java/io/agora/beauty/bytedance/BeautyByteDanceImpl.java index ac8e2bf44..7e30f49e4 100644 --- a/Android/APIExample/beauty/bytedance/src/main/java/io/agora/beauty/bytedance/BeautyByteDanceImpl.java +++ b/Android/APIExample/beauty/bytedance/src/main/java/io/agora/beauty/bytedance/BeautyByteDanceImpl.java @@ -73,10 +73,14 @@ public int process(int oesTexId, int width, int height, int rotation) { // 生成目标承载纹理 int dstTexture = mImageUtil.prepareTexture(width, height); // OES 纹理转2D纹理 + ImageUtil.Transition transition = new ImageUtil.Transition(); + if(rotation == 270){ + transition.scale(1.f, -1.0f); + } int texture2d = mImageUtil.transferTextureToTexture(oesTexId, BytedEffectConstants.TextureFormat.Texture_Oes, BytedEffectConstants.TextureFormat.Texure2D, - width, height, new ImageUtil.Transition()); + width, height, transition); // CV SDK 特效处理 boolean process = mEffectManager.process(texture2d, dstTexture, width, height, BytedEffectConstants.Rotation.CLOCKWISE_ROTATE_0, @@ -132,7 +136,7 @@ public void setStickerEnable(boolean enable) { return; } if (enable) { - String stickerPath = new EffectResourceHelper(mContext).getStickerPath("/stickers/zhaocaimao"); + String stickerPath = new EffectResourceHelper(mContext).getStickerPath("/stickers/test_sticker"); mEffectManager.setStickerAbs(stickerPath); } else { mEffectManager.setStickerAbs(null); From 53e7a44a6afce6990a9c994361140264fef1caca Mon Sep 17 00:00:00 2001 From: zhaoyongqiang Date: Mon, 27 Feb 2023 16:08:39 +0800 Subject: [PATCH 002/154] update Scene Beuaty --- .../APIExample.xcodeproj/project.pbxproj | 54 +++++++- .../Base.lproj/SenseBeautify.storyboard | 124 ++++++++++++++---- .../Manager/VideoProcessingManager.h | 5 + .../Manager/VideoProcessingManager.m | 70 +++++++++- .../SenseBeautify/SenseBeautifyVC.m | 22 +++- .../zh-Hans.lproj/SenseBeautify.strings | 15 ++- iOS/APIExample/APIExample/Info.plist | 4 +- 7 files changed, 255 insertions(+), 39 deletions(-) diff --git a/iOS/APIExample/APIExample.xcodeproj/project.pbxproj b/iOS/APIExample/APIExample.xcodeproj/project.pbxproj index e9e1fa16d..bcd865379 100644 --- a/iOS/APIExample/APIExample.xcodeproj/project.pbxproj +++ b/iOS/APIExample/APIExample.xcodeproj/project.pbxproj @@ -203,6 +203,9 @@ E7A49D652909111400F06DD4 /* BEHttpRequestProvider.mm in Sources */ = {isa = PBXBuildFile; fileRef = E7A49D642909111400F06DD4 /* BEHttpRequestProvider.mm */; }; E7A49D682909113200F06DD4 /* BERender.mm in Sources */ = {isa = PBXBuildFile; fileRef = E7A49D672909113200F06DD4 /* BERender.mm */; }; E7A49D6B2909115200F06DD4 /* BEEffectResourceHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = E7A49D6A2909115100F06DD4 /* BEEffectResourceHelper.m */; }; + E7D6E9A229AC9AC800B8787A /* wanneng.zip in Resources */ = {isa = PBXBuildFile; fileRef = E7D6E99E29AC9AC800B8787A /* wanneng.zip */; }; + E7D6E9A329AC9AC800B8787A /* qise.zip in Resources */ = {isa = PBXBuildFile; fileRef = E7D6E99F29AC9AC800B8787A /* qise.zip */; }; + E7D6E9A429AC9AC800B8787A /* lianxingface.zip in Resources */ = {isa = PBXBuildFile; fileRef = E7D6E9A129AC9AC800B8787A /* lianxingface.zip */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -528,6 +531,9 @@ E7A49D692909115100F06DD4 /* BEEffectResourceHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BEEffectResourceHelper.h; sourceTree = ""; }; E7A49D6A2909115100F06DD4 /* BEEffectResourceHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BEEffectResourceHelper.m; sourceTree = ""; }; E7A49D6E290A744400F06DD4 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/ThirdBeautify.strings"; sourceTree = ""; }; + E7D6E99E29AC9AC800B8787A /* wanneng.zip */ = {isa = PBXFileReference; lastKnownFileType = archive.zip; path = wanneng.zip; sourceTree = ""; }; + E7D6E99F29AC9AC800B8787A /* qise.zip */ = {isa = PBXFileReference; lastKnownFileType = archive.zip; path = qise.zip; sourceTree = ""; }; + E7D6E9A129AC9AC800B8787A /* lianxingface.zip */ = {isa = PBXFileReference; lastKnownFileType = archive.zip; path = lianxingface.zip; sourceTree = ""; }; EAD308B056B63304DA681699 /* Pods-Agora-ScreenShare-Extension(Socket).release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Agora-ScreenShare-Extension(Socket).release.xcconfig"; path = "Target Support Files/Pods-Agora-ScreenShare-Extension(Socket)/Pods-Agora-ScreenShare-Extension(Socket).release.xcconfig"; sourceTree = ""; }; EB8CDD3F04870C6A31287732 /* Pods_audioFilter.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_audioFilter.framework; sourceTree = BUILT_PRODUCTS_DIR; }; FAAC2AEE355D103B9E8527B5 /* Pods-Agora-ScreenShare-Extension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Agora-ScreenShare-Extension.debug.xcconfig"; path = "Target Support Files/Pods-Agora-ScreenShare-Extension/Pods-Agora-ScreenShare-Extension.debug.xcconfig"; sourceTree = ""; }; @@ -1236,6 +1242,8 @@ E7A49D0729067F8300F06DD4 /* Manager */ = { isa = PBXGroup; children = ( + E7D6E9A029AC9AC800B8787A /* sticker_face_shape */, + E7D6E99D29AC9AC800B8787A /* style_lightly */, E7A49D352907EB6000F06DD4 /* SENSEME.lic */, E7A49D252907DDBF00F06DD4 /* EffectMacro.h */, E7A49D192907DD7800F06DD4 /* Effects.h */, @@ -1307,6 +1315,23 @@ path = Manager; sourceTree = ""; }; + E7D6E99D29AC9AC800B8787A /* style_lightly */ = { + isa = PBXGroup; + children = ( + E7D6E99E29AC9AC800B8787A /* wanneng.zip */, + E7D6E99F29AC9AC800B8787A /* qise.zip */, + ); + path = style_lightly; + sourceTree = ""; + }; + E7D6E9A029AC9AC800B8787A /* sticker_face_shape */ = { + isa = PBXGroup; + children = ( + E7D6E9A129AC9AC800B8787A /* lianxingface.zip */, + ); + path = sticker_face_shape; + sourceTree = ""; + }; FD17F473C6A05604A44BDDDE /* Pods */ = { isa = PBXGroup; children = ( @@ -1375,6 +1400,7 @@ 03D13BCA2448758900B599B3 /* Resources */, 1B6F6CF9B678035E221EAFDE /* [CP] Embed Pods Frameworks */, 0339BEBA25205B80007D4FDD /* Embed App Extensions */, + 2D7A60E3746CC26A94583758 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -1496,6 +1522,7 @@ 03D13BDC2448758B00B599B3 /* LaunchScreen.storyboard in Resources */, E728B85C28B8971200674A4A /* sample.yuv in Resources */, 8BC751D5273E502700552265 /* LiveStreaming.strings in Resources */, + E7D6E9A329AC9AC800B8787A /* qise.zip in Resources */, 8B349FE32681E2CE007247F2 /* agora-logo.png in Resources */, E728B85928B86B0700674A4A /* CustomVideoSourcePushMulti.storyboard in Resources */, 033A9F23252D70E400BC26E1 /* JoinChannelVideo.storyboard in Resources */, @@ -1510,6 +1537,7 @@ E7A49D40290907E200F06DD4 /* BytedEffect.strings in Resources */, 03B12DAA251125B700E55818 /* VideoView.xib in Resources */, E7163F832964149800EBBD55 /* ARKit.storyboard in Resources */, + E7D6E9A229AC9AC800B8787A /* wanneng.zip in Resources */, 8BA5459526AFEC8D00ED4295 /* SimpleFilter.storyboard in Resources */, E7163F812964149800EBBD55 /* ARKit.strings in Resources */, 67CB2F0D27EB318200CB19D2 /* SpatialAudio.storyboard in Resources */, @@ -1518,6 +1546,7 @@ E7A49CB12900FC6400F06DD4 /* KtvCopyrightMusic.storyboard in Resources */, E7899BDC2861673600851463 /* CreateDataStream.strings in Resources */, E7A49CFD29029E0000F06DD4 /* FUBeautify.storyboard in Resources */, + E7D6E9A429AC9AC800B8787A /* lianxingface.zip in Resources */, 8B5E5B5B274CEFE20040E97D /* RhythmPlayer.storyboard in Resources */, 033A9F84252D8B6400BC26E1 /* StreamEncryption.storyboard in Resources */, 67B8C7B628057D1500195106 /* RawVideoData.storyboard in Resources */, @@ -1605,6 +1634,23 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + 2D7A60E3746CC26A94583758 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-APIExample/Pods-APIExample-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-APIExample/Pods-APIExample-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-APIExample/Pods-APIExample-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; 2E9C38CD8AB71CBDCF3A8D8D /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -2267,7 +2313,7 @@ "$(PROJECT_DIR)/Agora-ScreenShare-Extension", ); MARKETING_VERSION = 4.0.0; - PRODUCT_BUNDLE_IDENTIFIER = "io.agora.api.examples.Agora-ScreenShare-Extension"; + PRODUCT_BUNDLE_IDENTIFIER = "io.agora.rte.extension.sensetime.Agora-ScreenShare-Extension"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; @@ -2304,7 +2350,7 @@ "$(PROJECT_DIR)/Agora-ScreenShare-Extension", ); MARKETING_VERSION = 4.0.0; - PRODUCT_BUNDLE_IDENTIFIER = "io.agora.api.examples.Agora-ScreenShare-Extension"; + PRODUCT_BUNDLE_IDENTIFIER = "io.agora.rte.extension.sensetime.Agora-ScreenShare-Extension"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; @@ -2495,7 +2541,7 @@ "-framework", "\"UIKit\"", ); - PRODUCT_BUNDLE_IDENTIFIER = io.agora.api.examples; + PRODUCT_BUNDLE_IDENTIFIER = io.agora.rte.extension.sensetime; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "APIExample/APIExample-Bridging-Header.h"; @@ -2569,7 +2615,7 @@ "-framework", "\"UIKit\"", ); - PRODUCT_BUNDLE_IDENTIFIER = io.agora.api.examples; + PRODUCT_BUNDLE_IDENTIFIER = io.agora.rte.extension.sensetime; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "APIExample/APIExample-Bridging-Header.h"; diff --git a/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/SenseBeautify/Base.lproj/SenseBeautify.storyboard b/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/SenseBeautify/Base.lproj/SenseBeautify.storyboard index 943b2bf40..51d6a78e6 100644 --- a/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/SenseBeautify/Base.lproj/SenseBeautify.storyboard +++ b/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/SenseBeautify/Base.lproj/SenseBeautify.storyboard @@ -1,9 +1,9 @@ - + - + @@ -23,22 +23,23 @@ You have not enabled the beauty feature of Shang Tang, please follow the steps below: 1: replace SENSEME.lic in SenseBeautify- > Manager- >. 2: replace the Bundle identifier bound by license
3: open the pod 'senseLib' comment in Podfile. -4: add a remoteSourcesLib folder under the iOS- > APIExample- > SenseLib directory. -5: add the st_mobile_sdk folder under the iOS- > APIExample- > SenseLib directory. -6: execute pod install. -7: rerun the project to see the effect +4: Create the SenseLib folder in the iOS ->APIExample directory +5: add a remoteSourcesLib folder under the iOS- > APIExample- > SenseLib directory. +6: add the st_mobile_sdk folder under the iOS- > APIExample- > SenseLib directory. +7: execute pod install. +8: rerun the project to see the effect - - + + - + - - + + - + - - + + + + + + + + + + + + + + + + + + - + + + + + + + + + - + + - + + - - + + + + + + + + - + @@ -93,10 +154,29 @@ + + + + + + + + + + + + + + + + + + + diff --git a/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/SenseBeautify/Manager/VideoProcessingManager.h b/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/SenseBeautify/Manager/VideoProcessingManager.h index ab898f7a9..c757c71b9 100644 --- a/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/SenseBeautify/Manager/VideoProcessingManager.h +++ b/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/SenseBeautify/Manager/VideoProcessingManager.h @@ -13,6 +13,11 @@ NS_ASSUME_NONNULL_BEGIN @interface VideoProcessingManager : NSObject +- (void)setBuauty: (BOOL)isSelected; +- (void)setMakeup: (BOOL)isSelected; +- (void)setSticker: (BOOL)isSelected; +- (void)setFilter: (BOOL)isSelected; + - (CVPixelBufferRef)videoProcessHandler:(CVPixelBufferRef)pixelBuffer; @end diff --git a/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/SenseBeautify/Manager/VideoProcessingManager.m b/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/SenseBeautify/Manager/VideoProcessingManager.m index 337949054..f00c3a05f 100644 --- a/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/SenseBeautify/Manager/VideoProcessingManager.m +++ b/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/SenseBeautify/Manager/VideoProcessingManager.m @@ -26,6 +26,7 @@ @interface VideoProcessingManager () @property (nonatomic) dispatch_queue_t renderQueue; ///贴纸id @property (nonatomic, assign) int stickerId; +@property (nonatomic, assign) int filterId; @end @@ -43,18 +44,75 @@ - (instancetype)init { [self.effectsProcess setModelPath:[bundle pathForResource:@"model" ofType:@"bundle"]]; [EAGLContext setCurrentContext:self.glContext]; self.effectsProcess.detectConfig = ST_MOBILE_FACE_DETECT; - [self.effectsProcess setBeautyParam:EFFECT_BEAUTY_PARAM_ENABLE_WHITEN_SKIN_MASK andVal:0.7]; - [self.effectsProcess setEffectType:EFFECT_BEAUTY_RESHAPE_SHRINK_FACE value:0.8]; - [self.effectsProcess setEffectType:EFFECT_BEAUTY_BASE_WHITTEN value:0.6]; - [self.effectsProcess setEffectType:EFFECT_BEAUTY_RESHAPE_ENLARGE_EYE value:1.0]; - [self.effectsProcess setEffectType:EFFECT_BEAUTY_RESHAPE_ROUND_EYE value:1.0]; - [self.effectsProcess setEffectType:EFFECT_BEAUTY_PLASTIC_OPEN_CANTHUS value:0.7]; + #endif }); } return self; } +- (void)setBuauty: (BOOL)isSelected { +#if __has_include("st_mobile_common.h") + if (isSelected) { + [self.effectsProcess setBeautyParam:EFFECT_BEAUTY_PARAM_ENABLE_WHITEN_SKIN_MASK andVal:0.7]; + [self.effectsProcess setEffectType:EFFECT_BEAUTY_RESHAPE_SHRINK_FACE value:0.8]; + [self.effectsProcess setEffectType:EFFECT_BEAUTY_BASE_WHITTEN value:0.6]; + [self.effectsProcess setEffectType:EFFECT_BEAUTY_RESHAPE_ENLARGE_EYE value:1.0]; + [self.effectsProcess setEffectType:EFFECT_BEAUTY_RESHAPE_ROUND_EYE value:1.0]; + [self.effectsProcess setEffectType:EFFECT_BEAUTY_PLASTIC_OPEN_CANTHUS value:0.7]; + } else { + [self.effectsProcess setBeautyParam:EFFECT_BEAUTY_PARAM_ENABLE_WHITEN_SKIN_MASK andVal:0]; + [self.effectsProcess setEffectType:EFFECT_BEAUTY_RESHAPE_SHRINK_FACE value:0]; + [self.effectsProcess setEffectType:EFFECT_BEAUTY_BASE_WHITTEN value:0]; + [self.effectsProcess setEffectType:EFFECT_BEAUTY_RESHAPE_ENLARGE_EYE value:0]; + [self.effectsProcess setEffectType:EFFECT_BEAUTY_RESHAPE_ROUND_EYE value:0]; + [self.effectsProcess setEffectType:EFFECT_BEAUTY_PLASTIC_OPEN_CANTHUS value:0]; + } +#endif +} +- (void)setMakeup: (BOOL)isSelected { +#if __has_include("st_mobile_common.h") + if (isSelected) { + NSString *path = [[NSBundle mainBundle] pathForResource:@"qise.zip" ofType:nil]; + __weak VideoProcessingManager *weakself = self; + [self.effectsProcess addStickerWithPath:path callBack:^(st_result_t state, int sticker, uint64_t action) { + [weakself.effectsProcess setPackageId:sticker groupType:EFFECT_BEAUTY_GROUP_MAKEUP strength:0.5]; + weakself.stickerId = sticker; + }]; + } else { + [self.effectsProcess removeSticker:self.stickerId]; + self.stickerId = 0; + } +#endif +} +- (void)setSticker: (BOOL)isSelected { +#if __has_include("st_mobile_common.h") + if (isSelected) { + NSString *path = [[NSBundle mainBundle] pathForResource:@"lianxingface.zip" ofType:nil]; + [self.effectsProcess setStickerWithPath:path callBack:^(st_result_t state, int stickerId, uint64_t action) { + + }]; + } else { + [self.effectsProcess cleareStickers]; + } +#endif +} +- (void)setFilter: (BOOL)isSelected { +#if __has_include("st_mobile_common.h") + if (isSelected) { + NSString *path = [[NSBundle mainBundle] pathForResource:@"qise.zip" ofType:nil]; + __weak VideoProcessingManager *weakself = self; + [self.effectsProcess addStickerWithPath:path callBack:^(st_result_t state, int sticker, uint64_t action) { + [weakself.effectsProcess setPackageId:sticker groupType:EFFECT_BEAUTY_GROUP_FILTER strength:0.5]; + weakself.filterId = sticker; + }]; + } else { + [self.effectsProcess removeSticker:self.filterId]; + self.filterId = 0; + } +#endif +} + - (CVPixelBufferRef)videoProcessHandler:(CVPixelBufferRef)pixelBuffer { if (!pixelBuffer) return pixelBuffer; diff --git a/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/SenseBeautify/SenseBeautifyVC.m b/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/SenseBeautify/SenseBeautifyVC.m index ad7531c5c..9ad4b0794 100644 --- a/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/SenseBeautify/SenseBeautifyVC.m +++ b/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/SenseBeautify/SenseBeautifyVC.m @@ -16,7 +16,7 @@ @interface SenseBeautifyVC () @property (weak, nonatomic) IBOutlet UILabel *tipsLabel; -@property (weak, nonatomic) IBOutlet UIStackView *container; +@property (weak, nonatomic) IBOutlet UIView *container; @property (weak, nonatomic) IBOutlet UIView *localVideo; @property (weak, nonatomic) IBOutlet UIView *remoteVideo; @@ -97,6 +97,26 @@ - (void) initSDK { }]; } +- (IBAction)onTapSwitchCameraButton:(id)sender { + [self.rtcEngineKit switchCamera]; +} +- (IBAction)onTapBeautyButton:(UIButton *)sender { + [sender setSelected:!sender.isSelected]; + [self.videoProcessing setBuauty:sender.isSelected]; +} +- (IBAction)onTapMakeupButton:(UIButton *)sender { + [sender setSelected:!sender.isSelected]; + [self.videoProcessing setMakeup:sender.isSelected]; +} +- (IBAction)onTapStickerButton:(UIButton *)sender { + [sender setSelected:!sender.isSelected]; + [self.videoProcessing setSticker:sender.isSelected]; +} +- (IBAction)onTapFilterButton:(UIButton *)sender { + [sender setSelected:!sender.isSelected]; + [self.videoProcessing setFilter:sender.isSelected]; +} + #pragma mark - VideoFrameDelegate - (BOOL)onCaptureVideoFrame:(AgoraOutputVideoFrame *)videoFrame { CVPixelBufferRef pixelBuffer = [self.videoProcessing videoProcessHandler:videoFrame.pixelBuffer]; diff --git a/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/SenseBeautify/zh-Hans.lproj/SenseBeautify.strings b/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/SenseBeautify/zh-Hans.lproj/SenseBeautify.strings index 446492001..298b5f9f6 100644 --- a/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/SenseBeautify/zh-Hans.lproj/SenseBeautify.strings +++ b/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/SenseBeautify/zh-Hans.lproj/SenseBeautify.strings @@ -24,7 +24,14 @@ 1: 在SenseBeautify->Manager->替换SENSEME.lic 2: 替换license绑定的Bundle identifier 3: 打开Podfile中 pod 'senseLib' 注释 -4: 在iOS->APIExample->SenseLib目录下添加remoteSourcesLib文件夹 -5: 在iOS->APIExample->SenseLib目录下添加st_mobile_sdk文件夹 -6: 执行pod install -7: 重新运行项目查看效果"; +4: 在iOS->APIExample目录下创建SenseLib文件夹 +5: 在iOS->APIExample->SenseLib目录下添加remoteSourcesLib文件夹 +6: 在iOS->APIExample->SenseLib目录下添加st_mobile_sdk文件夹 +7: 执行pod install +8: 重新运行项目查看效果"; + +"CrL-Yf-Cev.normalTitle" = "美颜"; +"3hp-ZM-MMW.normalTitle" = "美妆"; +"UdR-D4-uNu.normalTitle" = "贴纸"; +"K3f-4k-VQ1.normalTitle" = "滤镜"; + diff --git a/iOS/APIExample/APIExample/Info.plist b/iOS/APIExample/APIExample/Info.plist index f356acca7..dce12d117 100644 --- a/iOS/APIExample/APIExample/Info.plist +++ b/iOS/APIExample/APIExample/Info.plist @@ -2,8 +2,6 @@ - UIFileSharingEnabled - Application Supports itunes BGTaskSchedulerPermittedIdentifiers @@ -37,6 +35,8 @@ audio processing + UIFileSharingEnabled + UILaunchStoryboardName LaunchScreen UIMainStoryboardFile From 5d7744c766346893bfc50a8380ac1bffbae8a23f Mon Sep 17 00:00:00 2001 From: zhaoyongqiang Date: Mon, 27 Feb 2023 16:59:59 +0800 Subject: [PATCH 003/154] update Byte Beauty --- .../APIExample.xcodeproj/project.pbxproj | 8 +- .../Base.lproj/BytedEffect.storyboard | 138 ++++++++++++++---- .../ByteBeautify/BytedEffectVC.m | 22 ++- .../ByteBeautify/Manager/ByteDanceFilter.h | 7 +- .../ByteBeautify/Manager/ByteDanceFilter.m | 48 +++++- .../ThirdBeautify/ByteBeautify/Manager/Core.h | 2 +- .../zh-Hans.lproj/BytedEffect.strings | 16 +- 7 files changed, 193 insertions(+), 48 deletions(-) diff --git a/iOS/APIExample/APIExample.xcodeproj/project.pbxproj b/iOS/APIExample/APIExample.xcodeproj/project.pbxproj index bcd865379..88454e0c9 100644 --- a/iOS/APIExample/APIExample.xcodeproj/project.pbxproj +++ b/iOS/APIExample/APIExample.xcodeproj/project.pbxproj @@ -2313,7 +2313,7 @@ "$(PROJECT_DIR)/Agora-ScreenShare-Extension", ); MARKETING_VERSION = 4.0.0; - PRODUCT_BUNDLE_IDENTIFIER = "io.agora.rte.extension.sensetime.Agora-ScreenShare-Extension"; + PRODUCT_BUNDLE_IDENTIFIER = "io.agora.api.examples.Agora-ScreenShare-Extension"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; @@ -2350,7 +2350,7 @@ "$(PROJECT_DIR)/Agora-ScreenShare-Extension", ); MARKETING_VERSION = 4.0.0; - PRODUCT_BUNDLE_IDENTIFIER = "io.agora.rte.extension.sensetime.Agora-ScreenShare-Extension"; + PRODUCT_BUNDLE_IDENTIFIER = "io.agora.api.examples.Agora-ScreenShare-Extension"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; @@ -2541,7 +2541,7 @@ "-framework", "\"UIKit\"", ); - PRODUCT_BUNDLE_IDENTIFIER = io.agora.rte.extension.sensetime; + PRODUCT_BUNDLE_IDENTIFIER = io.agora.api.examples; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "APIExample/APIExample-Bridging-Header.h"; @@ -2615,7 +2615,7 @@ "-framework", "\"UIKit\"", ); - PRODUCT_BUNDLE_IDENTIFIER = io.agora.rte.extension.sensetime; + PRODUCT_BUNDLE_IDENTIFIER = io.agora.api.examples; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "APIExample/APIExample-Bridging-Header.h"; diff --git a/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/ByteBeautify/Base.lproj/BytedEffect.storyboard b/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/ByteBeautify/Base.lproj/BytedEffect.storyboard index df5c4fad8..ef0d6de12 100644 --- a/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/ByteBeautify/Base.lproj/BytedEffect.storyboard +++ b/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/ByteBeautify/Base.lproj/BytedEffect.storyboard @@ -1,9 +1,9 @@ - + - + @@ -19,27 +19,28 @@ - - + + - + - - + + - + - - + + + + + + + + + + + + + + + + + + - + + + + + + + + + - + - - - - - - - + + + + + + + + + + + + + + + - + @@ -94,10 +155,29 @@ + + + + + + + + + + + + + + + + + + + diff --git a/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/ByteBeautify/BytedEffectVC.m b/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/ByteBeautify/BytedEffectVC.m index f17d1c9e4..d65d7f4ac 100644 --- a/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/ByteBeautify/BytedEffectVC.m +++ b/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/ByteBeautify/BytedEffectVC.m @@ -15,7 +15,7 @@ @interface BytedEffectVC () @property (weak, nonatomic) IBOutlet UILabel *tipsLabel; -@property (weak, nonatomic) IBOutlet UIStackView *container; +@property (weak, nonatomic) IBOutlet UIView *container; @property (weak, nonatomic) IBOutlet UIView *localVideo; @property (weak, nonatomic) IBOutlet UIView *remoteVideo; @@ -81,6 +81,26 @@ - (void) initSDK { }]; } +- (IBAction)onTapSwitchCameraButton:(id)sender { + [self.rtcEngineKit switchCamera]; +} +- (IBAction)onTapBeautyButton:(UIButton *)sender { + [sender setSelected:!sender.isSelected]; + [self.videoFilter setBuauty:sender.isSelected]; +} +- (IBAction)onTapMakeupButton:(UIButton *)sender { + [sender setSelected:!sender.isSelected]; + [self.videoFilter setMakeup:sender.isSelected]; +} +- (IBAction)onTapStickerButton:(UIButton *)sender { + [sender setSelected:!sender.isSelected]; + [self.videoFilter setSticker:sender.isSelected]; +} +- (IBAction)onTapFilterButton:(UIButton *)sender { + [sender setSelected:!sender.isSelected]; + [self.videoFilter setFilter:sender.isSelected]; +} + #pragma mark - VideoFrameDelegate - (BOOL)onCaptureVideoFrame:(AgoraOutputVideoFrame *)videoFrame { CVPixelBufferRef pixelBuffer = videoFrame.pixelBuffer; diff --git a/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/ByteBeautify/Manager/ByteDanceFilter.h b/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/ByteBeautify/Manager/ByteDanceFilter.h index aff951034..51b548b2a 100755 --- a/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/ByteBeautify/Manager/ByteDanceFilter.h +++ b/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/ByteBeautify/Manager/ByteDanceFilter.h @@ -21,6 +21,11 @@ @property (nonatomic, assign) BOOL enabled; - + (ByteDanceFilter *)shareManager; + +- (void)setBuauty: (BOOL)isSelected; +- (void)setMakeup: (BOOL)isSelected; +- (void)setSticker: (BOOL)isSelected; +- (void)setFilter: (BOOL)isSelected; + @end diff --git a/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/ByteBeautify/Manager/ByteDanceFilter.m b/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/ByteBeautify/Manager/ByteDanceFilter.m index 08c3bcfe7..328190ef3 100755 --- a/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/ByteBeautify/Manager/ByteDanceFilter.m +++ b/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/ByteBeautify/Manager/ByteDanceFilter.m @@ -39,19 +39,53 @@ - (instancetype)init [_processor setEffectOn:YES]; // [_processor setFilterPath:@"Filter_32_Po10"]; - [_processor setStickerPath:@"test_sticker"]; +// [_processor setStickerPath:@"test_sticker"]; [_processor updateComposerNodes:@[@"/beauty_IOS_lite"]]; - [_processor updateComposerNodeIntensity:@"/beauty_IOS_lite" key:@"smooth" intensity:0.8]; - [_processor updateComposerNodeIntensity:@"/beauty_IOS_lite" key:@"whiten" intensity:0.9]; - [_processor updateComposerNodeIntensity:@"/beauty_IOS_lite" key:@"sharp" intensity:0.96]; - [_processor updateComposerNodeIntensity:@"/reshape_lite" key:@"Internal_Deform_Overall" intensity:0.95]; - [_processor updateComposerNodeIntensity:@"/reshape_lite" key:@"Internal_Deform_Eye" intensity:0.95]; - [_processor updateComposerNodeIntensity:@"/reshape_lite" key:@"Internal_Deform_MovNose" intensity:0.0]; } return self; } +- (void)setBuauty: (BOOL)isSelected { +#if __has_include("bef_effect_ai_api.h") + if (isSelected) { + [_processor updateComposerNodeIntensity:@"/beauty_IOS_lite" key:@"whiten" intensity:0.6]; + [_processor updateComposerNodeIntensity:@"/beauty_IOS_lite" key:@"smooth" intensity:0.6]; + } else { + [_processor updateComposerNodeIntensity:@"/beauty_IOS_lite" key:@"whiten" intensity:0]; + [_processor updateComposerNodeIntensity:@"/beauty_IOS_lite" key:@"smooth" intensity:0]; + } +#endif +} +- (void)setMakeup: (BOOL)isSelected { +#if __has_include("bef_effect_ai_api.h") + if (isSelected) { + [_processor updateComposerNodeIntensity:@"/style_makeup/tianmei" key:@"Makeup_ALL" intensity:0.6]; + } else { + [_processor updateComposerNodeIntensity:@"/style_makeup/tianmei" key:@"Makeup_ALL" intensity:0]; + } +#endif +} +- (void)setSticker: (BOOL)isSelected { +#if __has_include("bef_effect_ai_api.h") + if (isSelected) { + [_processor setStickerPath:@"wochaotian"]; + } else { + [_processor setStickerPath:@""]; + } +#endif +} +- (void)setFilter: (BOOL)isSelected { +#if __has_include("bef_effect_ai_api.h") + if (isSelected) { + [_processor setFilterPath:@"Filter_02_14"]; + [_processor setFilterIntensity:0.4]; + } else { + [_processor setFilterIntensity:0]; + } +#endif +} + #pragma mark - VideoFilterDelegate /// process your video frame here diff --git a/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/ByteBeautify/Manager/Core.h b/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/ByteBeautify/Manager/Core.h index 906dcb32b..069aafd07 100644 --- a/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/ByteBeautify/Manager/Core.h +++ b/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/ByteBeautify/Manager/Core.h @@ -1,6 +1,6 @@ #import "macro.h" -#define LICENSE_NAME ((const char *)"labcv_test_20220210_20230210_com.bytedance.labcv.demo_4.2.1.licbag") +#define LICENSE_NAME ((const char *)"agora_test_20220805_20230208_io.agora.entfull_4.2.3.licbag") #define ONLINE_LICENSE_KEY ((const char *)"jiaoyang_test") #define ONLINE_LICENSE_SECRET ((const char *)"04273924-9a77-11eb-94da-0c42a1b32a30") diff --git a/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/ByteBeautify/zh-Hans.lproj/BytedEffect.strings b/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/ByteBeautify/zh-Hans.lproj/BytedEffect.strings index 46475970f..fae52c988 100644 --- a/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/ByteBeautify/zh-Hans.lproj/BytedEffect.strings +++ b/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/ByteBeautify/zh-Hans.lproj/BytedEffect.strings @@ -23,8 +23,14 @@ "j6s-Pm-fSS.text" = "您还没有开启字节美颜功能, 请按如下步骤操作: 1: 在ByteBeautify->Manager->Core.h中填写ONLINE_LICENSE_KEY和ONLINE_LICENSE_SECRET 2: 打开Podfile中 pod 'bytedEffect' 注释 -3: 在iOS->APIExample->ByteEffectLib目录下添加BytedEffectSDK文件夹 -4: 在iOS->APIExample->ByteEffectLib目录下添加Resource文件夹 -5: 在iOS->APIExample->ByteEffectLib目录下添加libeffect-sdk.a库 -6: 执行pod install -7: 重新运行项目查看效果"; +3: 在iOS->APIExample目录下创建ByteEffectLib文件夹 +4: 在iOS->APIExample->ByteEffectLib目录下添加BytedEffectSDK文件夹 +5: 在iOS->APIExample->ByteEffectLib目录下添加Resource文件夹 +6: 在iOS->APIExample->ByteEffectLib目录下添加libeffect-sdk.a库 +7: 执行pod install +8: 重新运行项目查看效果"; + +"8ag-bw-I0V.normalTitle" = "美颜"; +"dow-FW-rpo.normalTitle" = "美妆"; +"dSm-Zl-ccL.normalTitle" = "贴纸"; +"qKk-jv-oyk.normalTitle" = "滤镜"; From 6935ed21099b08c5550df89e0256a5a543a0c576 Mon Sep 17 00:00:00 2001 From: zhaoyongqiang Date: Tue, 7 Mar 2023 11:12:46 +0800 Subject: [PATCH 004/154] update fu Beauty --- .../Base.lproj/FUBeautify.storyboard | 134 ++++++++++++++---- .../ThirdBeautify/FUBeautify/FUBeautifyVC.m | 23 ++- .../FUBeautify/Manager/FUManager.h | 6 +- .../FUBeautify/Manager/FUManager.m | 76 ++++++++-- .../zh-Hans.lproj/FUBeautify.strings | 16 ++- 5 files changed, 205 insertions(+), 50 deletions(-) diff --git a/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/FUBeautify/Base.lproj/FUBeautify.storyboard b/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/FUBeautify/Base.lproj/FUBeautify.storyboard index f8dff7522..53dae9fa9 100644 --- a/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/FUBeautify/Base.lproj/FUBeautify.storyboard +++ b/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/FUBeautify/Base.lproj/FUBeautify.storyboard @@ -1,9 +1,9 @@ - + - + @@ -19,26 +19,28 @@ - - + + - + - - + + - + - - + + + + + + + + + + + + + + + + + + - + + + + + + + + + - + - - - - - - - + + + + + + + + + + + + + + + - @@ -93,10 +154,29 @@ + + + + + + + + + + + + + + + + + + + diff --git a/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/FUBeautify/FUBeautifyVC.m b/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/FUBeautify/FUBeautifyVC.m index c9fa498da..a265620d5 100644 --- a/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/FUBeautify/FUBeautifyVC.m +++ b/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/FUBeautify/FUBeautifyVC.m @@ -52,9 +52,6 @@ - (void) initSDK { // add FaceUnity filter and add to process manager self.videoFilter = [FUManager shareManager]; - // add Sticker - [self.videoFilter setSticker:@"fashi"]; - // set up local video to render your local camera preview AgoraRtcVideoCanvas *videoCanvas = [AgoraRtcVideoCanvas new]; videoCanvas.uid = 0; @@ -80,6 +77,26 @@ - (void) initSDK { }]; }]; } +- (IBAction)onTapCameraSwitch:(id)sender { + [self.rtcEngineKit switchCamera]; +} + +- (IBAction)onTapBeautyButton:(UIButton *)sender { + [sender setSelected:!sender.isSelected]; + [self.videoFilter setBuauty:sender.isSelected]; +} +- (IBAction)onTapMakeupButton:(UIButton *)sender { + [sender setSelected:!sender.isSelected]; + [self.videoFilter setMakeup:sender.isSelected]; +} +- (IBAction)onTapStickerButton:(UIButton *)sender { + [sender setSelected:!sender.isSelected]; + [self.videoFilter setSticker:sender.isSelected]; +} +- (IBAction)onTapFilterButton:(UIButton *)sender { + [sender setSelected:!sender.isSelected]; + [self.videoFilter setFilter:sender.isSelected]; +} #pragma mark - VideoFrameDelegate - (BOOL)onCaptureVideoFrame:(AgoraOutputVideoFrame *)videoFrame { diff --git a/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/FUBeautify/Manager/FUManager.h b/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/FUBeautify/Manager/FUManager.h index f458e23c5..0f6908032 100755 --- a/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/FUBeautify/Manager/FUManager.h +++ b/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/FUBeautify/Manager/FUManager.h @@ -39,7 +39,9 @@ /// 更新美颜磨皮效果(根据人脸检测置信度设置不同磨皮效果) - (void)updateBeautyBlurEffect; -/// 设置贴纸 -- (void)setSticker: (NSString *)stickerName; +- (void)setBuauty: (BOOL)isSelected; +- (void)setMakeup: (BOOL)isSelected; +- (void)setSticker: (BOOL)isSelected; +- (void)setFilter: (BOOL)isSelected; @end diff --git a/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/FUBeautify/Manager/FUManager.m b/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/FUBeautify/Manager/FUManager.m index 782c62d25..827abb55f 100755 --- a/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/FUBeautify/Manager/FUManager.m +++ b/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/FUBeautify/Manager/FUManager.m @@ -66,18 +66,6 @@ - (instancetype)init NSString *bodyAIPath = [bundle pathForResource:@"model/ai_human_processor" ofType:@"bundle"];//[[NSBundle mainBundle] pathForResource:@"ai_human_processor" ofType:@"bundle"]; [FUAIKit loadAIModeWithAIType:FUAITYPE_HUMAN_PROCESSOR dataPath:bodyAIPath]; - // 加载默认美颜效果 - NSString *beautyPath = [bundle pathForResource:@"graphics/face_beautification" ofType:@"bundle"];//[[NSBundle mainBundle] pathForResource:@"face_beautification" ofType:@"bundle"]; - FUBeauty *beauty = [[FUBeauty alloc] initWithPath:beautyPath name:@"FUBeauty"]; - // 默认均匀磨皮 - beauty.heavyBlur = 0; - beauty.blurType = 3; - // 默认自定义脸型 - beauty.faceShape = 4; - beauty.colorLevel = 0.8; - beauty.redLevel = 0.8; - [FURenderKit shareRenderKit].beauty = beauty; - CFAbsoluteTime endTime = (CFAbsoluteTimeGetCurrent() - startTime); NSString *path = [bundle pathForResource:@"graphics/tongue" ofType:@"bundle"];//[[NSBundle mainBundle] pathForResource:@"tongue" ofType:@"bundle"]; [FUAIKit loadTongueMode:path]; @@ -108,13 +96,75 @@ - (void)destoryItems { #endif } +- (void)setBuauty: (BOOL)isSelected { +#if __has_include() + if (isSelected) { + NSBundle *bundle = [BundleUtil bundleWithBundleName:@"FURenderKit" podName:@"fuLib"]; + NSString *beautyPath = [bundle pathForResource:@"graphics/face_beautification" ofType:@"bundle"]; + FUBeauty *beauty = [[FUBeauty alloc] initWithPath:beautyPath name:@"FUBeauty"]; + // 默认均匀磨皮 + beauty.heavyBlur = 0; + beauty.blurType = 3; + [FURenderKit shareRenderKit].beauty = beauty; + } else { + [FURenderKit shareRenderKit].beauty = nil; + } +#endif +} +- (void)setMakeup: (BOOL)isSelected { +#if __has_include() + if (isSelected) { + NSBundle *bundle = [BundleUtil bundleWithBundleName:@"FURenderKit" podName:@"fuLib"]; + NSString *beautyPath = [bundle pathForResource:@"graphics/face_makeup" ofType:@"bundle"]; + FUMakeup *makeup = [[FUMakeup alloc] initWithPath:beautyPath name:@"face_makeup"]; + makeup.isMakeupOn = YES; + [FURenderKit setLogLevel:FU_LOG_LEVEL_DEBUG]; + + [FURenderKit shareRenderKit].makeup = makeup; + [FURenderKit shareRenderKit].makeup.enable = isSelected; + + NSString *makeupPath = [bundle pathForResource:@"美妆/ziyun" ofType:@"bundle"]; + FUItem *makeupItem = [[FUItem alloc] initWithPath:makeupPath name:@"ziyun"]; + [makeup updateMakeupPackage:makeupItem needCleanSubItem:NO]; + makeup.intensity = 0.9; + } else { + [FURenderKit shareRenderKit].makeup.enable = NO; + [FURenderKit shareRenderKit].makeup = nil; + } +#endif +} +- (void)setSticker: (BOOL)isSelected { +#if __has_include() + if (isSelected) { + [self setStickerPath:@"DaisyPig"]; + } else { + [[FURenderKit shareRenderKit].stickerContainer removeAllSticks]; + } +#endif +} +- (void)setFilter: (BOOL)isSelected { +#if __has_include() + if (isSelected) { + NSBundle *bundle = [BundleUtil bundleWithBundleName:@"FURenderKit" podName:@"fuLib"]; + NSString *beautyPath = [bundle pathForResource:@"graphics/face_beautification" ofType:@"bundle"]; + FUBeauty *beauty = [[FUBeauty alloc] initWithPath:beautyPath name:@"FUBeauty"]; + beauty.filterName = FUFilterMiTao1; + beauty.filterLevel = 0.8; + [FURenderKit shareRenderKit].beauty = beauty; + } else { + [FURenderKit shareRenderKit].beauty = nil; + } +#endif +} + + - (void)onCameraChange { #if __has_include() [FUAIKit resetTrackedResult]; #endif } -- (void)setSticker: (NSString *)stickerName { +- (void)setStickerPath: (NSString *)stickerName { NSBundle *bundle = [BundleUtil bundleWithBundleName:@"FURenderKit" podName:@"fuLib"]; NSString *path = [bundle pathForResource:[NSString stringWithFormat:@"贴纸/%@", stickerName] ofType:@"bundle"]; if (!path) { diff --git a/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/FUBeautify/zh-Hans.lproj/FUBeautify.strings b/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/FUBeautify/zh-Hans.lproj/FUBeautify.strings index f07cd2e05..242588805 100644 --- a/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/FUBeautify/zh-Hans.lproj/FUBeautify.strings +++ b/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/FUBeautify/zh-Hans.lproj/FUBeautify.strings @@ -23,8 +23,14 @@ "j6s-Pm-fSS.text" = "您还没有开启相芯美颜功能, 请按如下步骤操作: 1: 在FUBeautify->Manager->authpack中替换license 2: 打开Podfile中 pod 'fuLib' 注释 -3: 在iOS->APIExample->FULib目录下添加FURenderKit.framework -4: 在iOS->APIExample->FULib目录下添加Resources资源文件夹 -5: 在iOS->APIExample->FULib目录下添加Resource->贴纸文件夹 -6: 执行pod install -7: 重新运行项目查看效果"; +3: 在iOS->APIExample目录下创建FULib文件夹 +4: 在iOS->APIExample->FULib目录下添加FURenderKit.framework +5: 在iOS->APIExample->FULib目录下添加Resources资源文件夹 +6: 在iOS->APIExample->FULib目录下添加Resource->贴纸文件夹 +7: 执行pod install +8: 重新运行项目查看效果"; + +"QZu-iN-Fi6.normalTitle" = "美颜"; +"KHn-B1-epr.normalTitle" = "美妆"; +"aoR-43-iFs.normalTitle" = "贴纸"; +"UYi-3l-nYz.normalTitle" = "滤镜"; From be07960a933643031f6d88505a6ab688c33760d2 Mon Sep 17 00:00:00 2001 From: Qianze Zhang Date: Tue, 21 Feb 2023 13:54:11 +0800 Subject: [PATCH 005/154] Update README.zh.md --- windows/README.zh.md | 1 + 1 file changed, 1 insertion(+) diff --git a/windows/README.zh.md b/windows/README.zh.md index 5ad1ffa71..94fb52aa5 100644 --- a/windows/README.zh.md +++ b/windows/README.zh.md @@ -29,6 +29,7 @@ ``` #define APP_ID _T("Your App ID") + #define APP_TOKEN _T("Your Token") ``` 6. (可选)你也可以在Debug/Release目录下创建一个AppId.ini文件以配置你应用程序的AppID, 修改AppId的值为刚才申请的App ID ``` From 3cc8607ae46e33f7c05924973c2530b859eba5d0 Mon Sep 17 00:00:00 2001 From: Qianze Zhang Date: Tue, 21 Feb 2023 17:38:03 +0800 Subject: [PATCH 006/154] Update README.md --- windows/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/windows/README.md b/windows/README.md index af94d017f..d1409a81d 100644 --- a/windows/README.md +++ b/windows/README.md @@ -21,6 +21,7 @@ To build and run the sample application, get an App ID: ``` #define APP_ID _T("Your App ID") + #define APP_TOKEN _T("Your Token") ``` 6. (Optional)Alternate approach to setup your APPID is to create an AppId.ini file under Debug/Release. Modify the appId value to the App ID you just applied. From 853636dfb86b2c7b083d592fa53c6eb4e4fb2466 Mon Sep 17 00:00:00 2001 From: xucz Date: Fri, 24 Feb 2023 19:19:47 +0800 Subject: [PATCH 007/154] [Android]fix faceunity beauty bug(CSSA-1070) --- .../advanced/beauty/FaceUnityBeauty.java | 27 +++++++++-------- .../advanced/beauty/VideoCaptureUtils.java | 29 +++++++++++++++++++ Android/APIExample/beauty/faceunity/README.md | 2 +- .../APIExample/beauty/faceunity/README.zh.md | 2 +- 4 files changed, 46 insertions(+), 14 deletions(-) create mode 100644 Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/beauty/VideoCaptureUtils.java diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/beauty/FaceUnityBeauty.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/beauty/FaceUnityBeauty.java index 6c4681977..c835ef0aa 100644 --- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/beauty/FaceUnityBeauty.java +++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/beauty/FaceUnityBeauty.java @@ -1,7 +1,10 @@ package io.agora.api.example.examples.advanced.beauty; +import static io.agora.rtc2.video.VideoEncoderConfiguration.STANDARD_BITRATE; + import android.os.Bundle; import android.util.Log; +import android.util.Size; import android.view.LayoutInflater; import android.view.TextureView; import android.view.View; @@ -14,6 +17,7 @@ import java.util.Locale; import java.util.Random; +import io.agora.api.example.MainApplication; import io.agora.api.example.R; import io.agora.api.example.common.BaseFragment; import io.agora.api.example.common.widget.VideoReportLayout; @@ -29,6 +33,7 @@ import io.agora.rtc2.RtcEngine; import io.agora.rtc2.video.IVideoFrameObserver; import io.agora.rtc2.video.VideoCanvas; +import io.agora.rtc2.video.VideoEncoderConfiguration; public class FaceUnityBeauty extends BaseFragment { private static final String TAG = "SceneTimeBeauty"; @@ -299,6 +304,13 @@ public int getObservedFramePosition() { } }; rtcEngine.registerVideoFrameObserver(mVideoFrameObserver); + // Setup video encoding configs + rtcEngine.setVideoEncoderConfiguration(new VideoEncoderConfiguration( + ((MainApplication)getActivity().getApplication()).getGlobalSettings().getVideoEncodingDimensionObject(), + VideoEncoderConfiguration.FRAME_RATE.valueOf(((MainApplication)getActivity().getApplication()).getGlobalSettings().getVideoEncodingFrameRate()), + STANDARD_BITRATE, + VideoEncoderConfiguration.ORIENTATION_MODE.valueOf(((MainApplication)getActivity().getApplication()).getGlobalSettings().getVideoEncodingOrientation()) + )); rtcEngine.enableVideo(); rtcEngine.disableAudio(); @@ -308,17 +320,15 @@ public int getObservedFramePosition() { } private VideoFrame.TextureBuffer processSingleInput(VideoFrame.TextureBuffer texBuffer) { - - int width = texBuffer.getWidth(); - int height = texBuffer.getHeight(); + Size captureOriginSize = VideoCaptureUtils.getCaptureOriginSize(texBuffer); Integer processTexId = mTextureBufferHelper.invoke(() -> iBeautyFaceUnity.process( texBuffer.getTextureId(), - width, height + captureOriginSize.getWidth(), captureOriginSize.getHeight() )); return mTextureBufferHelper.wrapTextureBuffer( - width, height, VideoFrame.TextureBuffer.Type.RGB, processTexId, + texBuffer.getWidth(), texBuffer.getHeight(), VideoFrame.TextureBuffer.Type.RGB, processTexId, texBuffer.getTransformMatrix()); } @@ -416,11 +426,4 @@ private void doOnBeautyCreatingEnd() { }); } - private void doOnBeautyReleasingBegin() { - Log.d(TAG, "doOnBeautyReleasingBegin..."); - } - - private void doOnBeautyReleasingEnd() { - Log.d(TAG, "doOnBeautyReleasingEnd."); - } } diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/beauty/VideoCaptureUtils.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/beauty/VideoCaptureUtils.java new file mode 100644 index 000000000..1245a411a --- /dev/null +++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/beauty/VideoCaptureUtils.java @@ -0,0 +1,29 @@ +package io.agora.api.example.examples.advanced.beauty; + +import android.graphics.Matrix; +import android.util.Size; + +import io.agora.base.VideoFrame; +import io.agora.base.internal.video.RendererCommon; + +public class VideoCaptureUtils { + + public static Size getCaptureOriginSize(VideoFrame.TextureBuffer texBuffer){ + int width = texBuffer.getWidth(); + int height = texBuffer.getHeight(); + Matrix texMatrix = texBuffer.getTransformMatrix(); + + // 根据Matrix反算纹理的真实宽高 + Matrix renderMatrix = new Matrix(); + renderMatrix.preTranslate(0.5F, 0.5F); + renderMatrix.preScale(1.0F, -1.0F); + renderMatrix.preTranslate(-0.5F, -0.5F); + Matrix finalMatrix = new Matrix(texMatrix); + finalMatrix.preConcat(renderMatrix); + float[] finalGlMatrix = RendererCommon.convertMatrixFromAndroidGraphicsMatrix(finalMatrix); + int texWidth = (int) (width * 1.0f / finalGlMatrix[0] + 0.5f); + int texHeight = (int) (height * 1.0f / finalGlMatrix[5] + 0.5f); + return new Size(texWidth, texHeight); + } + +} diff --git a/Android/APIExample/beauty/faceunity/README.md b/Android/APIExample/beauty/faceunity/README.md index 448ab2f8f..49859d979 100644 --- a/Android/APIExample/beauty/faceunity/README.md +++ b/Android/APIExample/beauty/faceunity/README.md @@ -7,7 +7,7 @@ This example provides two FaceUnity beauty access methods, single input (NV21 on ## 3 License -Contact sales@agora.io and get a licence file `authpack.java`, then copy this file to project folder `src/main/java/io/agora/rtcwithfu/authpack.java`. Note this licence determines which FaceUnity functions/effects you are allowed to use. +Contact sales@agora.io and get a licence file `authpack.java`, then copy this file to project folder `beauty/faceunity/src/main/java/com/faceunity/nama/authpack.java`. Note this licence determines which FaceUnity functions/effects you are allowed to use. ## 4 Configure compilation diff --git a/Android/APIExample/beauty/faceunity/README.zh.md b/Android/APIExample/beauty/faceunity/README.zh.md index 67d4801b3..45a2467bc 100644 --- a/Android/APIExample/beauty/faceunity/README.zh.md +++ b/Android/APIExample/beauty/faceunity/README.zh.md @@ -7,7 +7,7 @@ ## 1 证书激活 -请联系 sales@agora.io 获取证书文件替换本项目中的 **src/main/java/io/agora/rtcwithfu/authpack.java**。 +请联系 sales@agora.io 获取证书文件替换本项目中的 **beauty/faceunity/src/main/java/com/faceunity/nama/authpack.java**。 ## 2 配置编译 From a45f095eb40e14a73a1c7bce33b34cca5ec86cc2 Mon Sep 17 00:00:00 2001 From: xucz Date: Wed, 8 Mar 2023 16:08:58 +0800 Subject: [PATCH 008/154] [Android]remove inaccurate comment. --- .../io/agora/api/example/examples/advanced/ProcessRawData.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/ProcessRawData.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/ProcessRawData.java index aa8755f0f..8a70b5ef8 100644 --- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/ProcessRawData.java +++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/ProcessRawData.java @@ -239,8 +239,7 @@ private void joinChannel(String channelId) { /**Set up to play remote sound with receiver*/ engine.setDefaultAudioRoutetoSpeakerphone(true); - int ret = engine.registerVideoFrameObserver(iVideoFrameObserver); - // Enable video module should be after calling registerVideoFrameObserver + engine.registerVideoFrameObserver(iVideoFrameObserver); engine.enableVideo(); engine.startPreview(); From 0cbe4b3e363fceeccbceab73fafc137b11ac190d Mon Sep 17 00:00:00 2001 From: xucz Date: Thu, 9 Mar 2023 14:45:57 +0800 Subject: [PATCH 009/154] [Android]perfect sense time beauty implement. --- .../api/example/common/gles/GLTestUtils.java | 125 ++++++++ .../api/example/common/gles/GLThread.java | 11 + .../example/common/model/GlobalSettings.java | 12 +- .../advanced/beauty/SceneTimeBeauty.java | 285 +++++++----------- .../res/layout/fragment_beauty_scenetime.xml | 14 - .../agora/beauty/base/IBeautySenseTime.java | 2 +- .../APIExample/beauty/sense-time/README.md | 2 +- .../APIExample/beauty/sense-time/README.zh.md | 2 +- .../com/sensetime/effects/STRenderer.java | 66 ++-- .../sensetime/effects/display/STGLRender.java | 9 +- .../beauty/sensetime/BeautySenseTimeImpl.java | 10 +- 11 files changed, 304 insertions(+), 234 deletions(-) create mode 100644 Android/APIExample/app/src/main/java/io/agora/api/example/common/gles/GLTestUtils.java create mode 100644 Android/APIExample/app/src/main/java/io/agora/api/example/common/gles/GLThread.java diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/common/gles/GLTestUtils.java b/Android/APIExample/app/src/main/java/io/agora/api/example/common/gles/GLTestUtils.java new file mode 100644 index 000000000..f909044d2 --- /dev/null +++ b/Android/APIExample/app/src/main/java/io/agora/api/example/common/gles/GLTestUtils.java @@ -0,0 +1,125 @@ +package io.agora.api.example.common.gles; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.ImageFormat; +import android.graphics.Rect; +import android.graphics.YuvImage; +import android.opengl.GLES11Ext; +import android.opengl.GLES20; +import android.util.Log; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.IntBuffer; + +public class GLTestUtils { + private static final String TAG = "GLUtils"; + + public static Bitmap getTexture2DImage(int textureID, int width, int height) { + try { + int[] oldFboId = new int[1]; + GLES20.glGetIntegerv(GLES20.GL_FRAMEBUFFER_BINDING, IntBuffer.wrap(oldFboId)); + + int[] framebuffers = new int[1]; + GLES20.glGenFramebuffers(1, framebuffers, 0); + int framebufferId = framebuffers[0]; + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, framebufferId); + + int[] renderbuffers = new int[1]; + GLES20.glGenRenderbuffers(1, renderbuffers, 0); + int renderId = renderbuffers[0]; + GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, renderId); + GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER, GLES20.GL_DEPTH_COMPONENT16, width, height); + + GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, textureID, 0); + GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER, GLES20.GL_DEPTH_ATTACHMENT, GLES20.GL_RENDERBUFFER, renderId); + if (GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER) != GLES20.GL_FRAMEBUFFER_COMPLETE) { + Log.d(TAG, "Framebuffer error"); + } + + ByteBuffer rgbaBuf = ByteBuffer.allocateDirect(width * height * 4); + rgbaBuf.position(0); + GLES20.glReadPixels(0, 0, width, height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, rgbaBuf); + + Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + bitmap.copyPixelsFromBuffer(rgbaBuf); + + GLES20.glDeleteRenderbuffers(1, IntBuffer.wrap(framebuffers)); + GLES20.glDeleteFramebuffers(1, IntBuffer.allocate(framebufferId)); + + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, oldFboId[0]); + + return bitmap; + } catch (Exception e) { + Log.e(TAG, "", e); + } + return null; + } + + public static Bitmap getTextureOESImage(int textureID, int width, int height) { + try { + int[] oldFboId = new int[1]; + GLES20.glGetIntegerv(GLES20.GL_FRAMEBUFFER_BINDING, IntBuffer.wrap(oldFboId)); + + int[] framebuffers = new int[1]; + GLES20.glGenFramebuffers(1, framebuffers, 0); + int framebufferId = framebuffers[0]; + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, framebufferId); + + int[] renderbuffers = new int[1]; + GLES20.glGenRenderbuffers(1, renderbuffers, 0); + int renderId = renderbuffers[0]; + GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, renderId); + GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER, GLES20.GL_DEPTH_COMPONENT16, width, height); + + GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureID, 0); + GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER, GLES20.GL_DEPTH_ATTACHMENT, GLES20.GL_RENDERBUFFER, renderId); + if (GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER) != GLES20.GL_FRAMEBUFFER_COMPLETE) { + Log.d(TAG, "Framebuffer error"); + } + + ByteBuffer rgbaBuf = ByteBuffer.allocateDirect(width * height * 4); + rgbaBuf.position(0); + GLES20.glReadPixels(0, 0, width, height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, rgbaBuf); + + Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + bitmap.copyPixelsFromBuffer(rgbaBuf); + + GLES20.glDeleteRenderbuffers(1, IntBuffer.wrap(framebuffers)); + GLES20.glDeleteFramebuffers(1, IntBuffer.allocate(framebufferId)); + + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, oldFboId[0]); + + return bitmap; + } catch (Exception e) { + Log.e(TAG, "", e); + } + return null; + } + + private static Bitmap nv21ToBitmap(byte[] nv21, int width, int height) { + Bitmap bitmap = null; + try { + YuvImage image = new YuvImage(nv21, ImageFormat.NV21, width, height, null); + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + image.compressToJpeg(new Rect(0, 0, width, height), 80, stream); + bitmap = BitmapFactory.decodeByteArray(stream.toByteArray(), 0, stream.size()); + stream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + return bitmap; + } + + private static Bitmap readBitmap(int width, int height){ + ByteBuffer rgbaBuf = ByteBuffer.allocateDirect(width * height * 4); + rgbaBuf.position(0); + GLES20.glReadPixels(0, 0, width, height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, rgbaBuf); + + Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + bitmap.copyPixelsFromBuffer(rgbaBuf); + return bitmap; + } +} diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/common/gles/GLThread.java b/Android/APIExample/app/src/main/java/io/agora/api/example/common/gles/GLThread.java new file mode 100644 index 000000000..daeb29a38 --- /dev/null +++ b/Android/APIExample/app/src/main/java/io/agora/api/example/common/gles/GLThread.java @@ -0,0 +1,11 @@ +package io.agora.api.example.common.gles; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface GLThread { +} diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/common/model/GlobalSettings.java b/Android/APIExample/app/src/main/java/io/agora/api/example/common/model/GlobalSettings.java index ff2453e02..4ce81f103 100644 --- a/Android/APIExample/app/src/main/java/io/agora/api/example/common/model/GlobalSettings.java +++ b/Android/APIExample/app/src/main/java/io/agora/api/example/common/model/GlobalSettings.java @@ -2,7 +2,7 @@ import static io.agora.rtc2.video.VideoEncoderConfiguration.FRAME_RATE.FRAME_RATE_FPS_15; import static io.agora.rtc2.video.VideoEncoderConfiguration.ORIENTATION_MODE.ORIENTATION_MODE_ADAPTIVE; -import static io.agora.rtc2.video.VideoEncoderConfiguration.VD_640x360; +import static io.agora.rtc2.video.VideoEncoderConfiguration.VD_960x540; import android.text.TextUtils; import android.util.Log; @@ -66,17 +66,15 @@ public LocalAccessPointConfiguration getPrivateCloudConfig() { } public VideoEncoderConfiguration.VideoDimensions getVideoEncodingDimensionObject() { - if (videoEncodingDimension == null) - return VD_640x360; - VideoEncoderConfiguration.VideoDimensions value = VD_640x360; + VideoEncoderConfiguration.VideoDimensions value = VD_960x540; try { - Field tmp = VideoEncoderConfiguration.class.getDeclaredField(videoEncodingDimension); + Field tmp = VideoEncoderConfiguration.class.getDeclaredField(getVideoEncodingDimension()); tmp.setAccessible(true); value = (VideoEncoderConfiguration.VideoDimensions) tmp.get(null); } catch (NoSuchFieldException e) { - Log.e("Field", "Can not find field " + videoEncodingDimension); + Log.e("Field", "Can not find field " + getVideoEncodingDimension()); } catch (IllegalAccessException e) { - Log.e("Field", "Could not access field " + videoEncodingDimension); + Log.e("Field", "Could not access field " + getVideoEncodingDimension()); } return value; } diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/beauty/SceneTimeBeauty.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/beauty/SceneTimeBeauty.java index f4426987a..ea07da468 100644 --- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/beauty/SceneTimeBeauty.java +++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/beauty/SceneTimeBeauty.java @@ -1,5 +1,7 @@ package io.agora.api.example.examples.advanced.beauty; +import static io.agora.rtc2.video.VideoEncoderConfiguration.STANDARD_BITRATE; + import android.graphics.Matrix; import android.opengl.GLES11Ext; import android.opengl.GLES20; @@ -9,6 +11,7 @@ import android.view.TextureView; import android.view.View; import android.view.ViewGroup; +import android.view.ViewParent; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -16,7 +19,9 @@ import java.nio.ByteBuffer; import java.util.Locale; import java.util.Random; +import java.util.concurrent.CountDownLatch; +import io.agora.api.example.MainApplication; import io.agora.api.example.R; import io.agora.api.example.common.BaseFragment; import io.agora.api.example.common.widget.VideoReportLayout; @@ -24,14 +29,17 @@ import io.agora.api.example.utils.TokenUtils; import io.agora.base.TextureBufferHelper; import io.agora.base.VideoFrame; +import io.agora.base.internal.video.RendererCommon; import io.agora.base.internal.video.YuvHelper; import io.agora.beauty.base.IBeautySenseTime; import io.agora.rtc2.ChannelMediaOptions; import io.agora.rtc2.Constants; import io.agora.rtc2.IRtcEngineEventHandler; import io.agora.rtc2.RtcEngine; +import io.agora.rtc2.gl.EglBaseProvider; import io.agora.rtc2.video.IVideoFrameObserver; import io.agora.rtc2.video.VideoCanvas; +import io.agora.rtc2.video.VideoEncoderConfiguration; public class SceneTimeBeauty extends BaseFragment { private static final String TAG = "SceneTimeBeauty"; @@ -40,13 +48,8 @@ public class SceneTimeBeauty extends BaseFragment { private FragmentBeautyScenetimeBinding mBinding; private RtcEngine rtcEngine; private String channelId; - private ByteBuffer nv21ByteBuffer; - private byte[] nv21ByteArray; - private boolean isFrontCamera = true; - private TextureBufferHelper mDoubleTextureBufferHelper; - private TextureBufferHelper mSingleTextureBufferHelper; - private boolean isSingleInput = true; + private boolean isFrontCamera = true; private VideoReportLayout mLocalVideoLayout; private VideoReportLayout mRemoteVideoLayout; @@ -54,8 +57,11 @@ public class SceneTimeBeauty extends BaseFragment { private IVideoFrameObserver mVideoFrameObserver; private IRtcEngineEventHandler mRtcEngineEventHandler; - private volatile boolean isDestroyed = false; - private int mFrameRotation; + // Beauty process require parameters + private TextureBufferHelper mTextureBufferHelper; + private CountDownLatch unInitBeautyLatch; + private ByteBuffer nv21ByteBuffer; + private byte[] nv21ByteArray; @Nullable @Override @@ -81,25 +87,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat @Override public void onDestroyView() { super.onDestroyView(); - isDestroyed = true; - if (mSingleTextureBufferHelper != null) { - mSingleTextureBufferHelper.invoke(() -> { - iBeautySenseTime.release(); - iBeautySenseTime = null; - return null; - }); - mSingleTextureBufferHelper.dispose(); - mSingleTextureBufferHelper = null; - } - if (mDoubleTextureBufferHelper != null) { - mDoubleTextureBufferHelper.invoke(() -> { - iBeautySenseTime.release(); - iBeautySenseTime = null; - return null; - }); - mDoubleTextureBufferHelper.dispose(); - mDoubleTextureBufferHelper = null; - } + unInitBeauty(); if (rtcEngine != null) { rtcEngine.leaveChannel(); } @@ -135,11 +123,6 @@ private void initVideoView() { rtcEngine.switchCamera(); isFrontCamera = !isFrontCamera; }); - mBinding.tvBeautyInput.setText(isSingleInput ? R.string.beauty_input_single : R.string.beauty_input_double); - mBinding.tvBeautyInput.setOnClickListener(v -> { - isSingleInput = !isSingleInput; - mBinding.tvBeautyInput.setText(isSingleInput ? R.string.beauty_input_single : R.string.beauty_input_double); - }); mBinding.smallVideoContainer.setOnClickListener(v -> updateVideoLayouts(!SceneTimeBeauty.this.isLocalFull)); } @@ -177,7 +160,7 @@ public void onUserJoined(int uid, int elapsed) { public void onUserOffline(int uid, int reason) { super.onUserOffline(uid, reason); runOnUIThread(() -> { - if(mRemoteVideoLayout != null && mRemoteVideoLayout.getReportUid() == uid){ + if (mRemoteVideoLayout != null && mRemoteVideoLayout.getReportUid() == uid) { mRemoteVideoLayout.removeAllViews(); mRemoteVideoLayout = null; updateVideoLayouts(isLocalFull); @@ -223,17 +206,7 @@ public void onRemoteVideoStats(RemoteVideoStats stats) { mVideoFrameObserver = new IVideoFrameObserver() { @Override public boolean onCaptureVideoFrame(VideoFrame videoFrame) { - if (isDestroyed) { - return true; - } - if (isSingleInput) { - return processSingleInput(videoFrame); - } else { - if (!processDoubleInput(videoFrame)) { - return processSingleInput(videoFrame); - } - return true; - } + return processBeauty(videoFrame); } @Override @@ -278,7 +251,7 @@ public boolean getRotationApplied() { @Override public boolean getMirrorApplied() { - return false; + return true; } @Override @@ -287,6 +260,13 @@ public int getObservedFramePosition() { } }; rtcEngine.registerVideoFrameObserver(mVideoFrameObserver); + // Setup video encoding configs + rtcEngine.setVideoEncoderConfiguration(new VideoEncoderConfiguration( + ((MainApplication) getActivity().getApplication()).getGlobalSettings().getVideoEncodingDimensionObject(), + VideoEncoderConfiguration.FRAME_RATE.valueOf(((MainApplication) getActivity().getApplication()).getGlobalSettings().getVideoEncodingFrameRate()), + STANDARD_BITRATE, + VideoEncoderConfiguration.ORIENTATION_MODE.valueOf(((MainApplication) getActivity().getApplication()).getGlobalSettings().getVideoEncodingOrientation()) + )); rtcEngine.enableVideo(); rtcEngine.disableAudio(); @@ -295,104 +275,37 @@ public int getObservedFramePosition() { } } - private boolean processSingleInput(VideoFrame videoFrame) { - VideoFrame.Buffer buffer = videoFrame.getBuffer(); - - if (mDoubleTextureBufferHelper != null) { - doOnBeautyReleasingBegin(); - mDoubleTextureBufferHelper.invoke(() -> { - iBeautySenseTime.release(); - return null; - }); - mDoubleTextureBufferHelper.dispose(); - mDoubleTextureBufferHelper = null; - doOnBeautyReleasingEnd(); - } - if (mSingleTextureBufferHelper == null) { - doOnBeautyCreatingBegin(); - mSingleTextureBufferHelper = TextureBufferHelper.create("STRender", null); - mSingleTextureBufferHelper.invoke(() -> { - iBeautySenseTime = IBeautySenseTime.create(getContext()); - return null; - }); - doOnBeautyCreatingEnd(); + private void unInitBeauty() { + unInitBeautyLatch = new CountDownLatch(1); + try { + unInitBeautyLatch.wait(); + } catch (InterruptedException e) { + throw new RuntimeException(e); } + unInitBeautyLatch = null; + } - int width = buffer.getWidth(); - int height = buffer.getHeight(); - - int nv21Size = (int) (width * height * 3.0f / 2.0f + 0.5f); - if (nv21ByteBuffer == null || nv21ByteBuffer.capacity() != nv21Size) { - if (nv21ByteBuffer != null) { - nv21ByteBuffer.clear(); + private boolean processBeauty(VideoFrame videoFrame) { + if (unInitBeautyLatch != null) { + if (mTextureBufferHelper != null) { + mTextureBufferHelper.invoke(() -> { + iBeautySenseTime.release(); + iBeautySenseTime = null; + return null; + }); + mTextureBufferHelper.dispose(); + mTextureBufferHelper = null; } - nv21ByteBuffer = ByteBuffer.allocateDirect(nv21Size); - nv21ByteArray = new byte[nv21Size]; - } - - - VideoFrame.I420Buffer i420Buffer = buffer.toI420(); - YuvHelper.I420ToNV12(i420Buffer.getDataY(), i420Buffer.getStrideY(), - i420Buffer.getDataV(), i420Buffer.getStrideV(), - i420Buffer.getDataU(), i420Buffer.getStrideU(), - nv21ByteBuffer, width, height); - nv21ByteBuffer.position(0); - nv21ByteBuffer.get(nv21ByteArray); - i420Buffer.release(); - - Integer processTexId = mSingleTextureBufferHelper.invoke(() -> iBeautySenseTime.process( - nv21ByteArray, - width, height, mFrameRotation - )); - - // drag one frame to avoid reframe when switching camera. - if(mFrameRotation != videoFrame.getRotation()){ - mFrameRotation = videoFrame.getRotation(); + unInitBeautyLatch.countDown(); return false; } - - VideoFrame.TextureBuffer processBuffer = mSingleTextureBufferHelper.wrapTextureBuffer( - width, height, VideoFrame.TextureBuffer.Type.RGB, processTexId, - buffer instanceof VideoFrame.TextureBuffer ? ((VideoFrame.TextureBuffer) buffer).getTransformMatrix(): new Matrix()); - videoFrame.replaceBuffer(processBuffer, mFrameRotation, videoFrame.getTimestampNs()); - buffer.release(); - - return true; - } - - private boolean processDoubleInput(VideoFrame videoFrame) { VideoFrame.Buffer buffer = videoFrame.getBuffer(); - if (!(buffer instanceof VideoFrame.TextureBuffer)) { - return false; - } - VideoFrame.TextureBuffer texBuffer = (VideoFrame.TextureBuffer) buffer; - if (mSingleTextureBufferHelper != null) { - doOnBeautyReleasingBegin(); - mSingleTextureBufferHelper.invoke(() -> { - iBeautySenseTime.release(); - return null; - }); - mSingleTextureBufferHelper.dispose(); - mSingleTextureBufferHelper = null; - doOnBeautyReleasingEnd(); - } - - if (mDoubleTextureBufferHelper == null) { - doOnBeautyCreatingBegin(); - mDoubleTextureBufferHelper = TextureBufferHelper.create("STRender", texBuffer.getEglBaseContext()); - mDoubleTextureBufferHelper.invoke(() -> { - iBeautySenseTime = IBeautySenseTime.create(getContext()); - return null; - }); - doOnBeautyCreatingEnd(); - } - - int textureId = texBuffer.getTextureId(); - int textureFormat = texBuffer.getType() == VideoFrame.TextureBuffer.Type.OES ? GLES11Ext.GL_TEXTURE_EXTERNAL_OES : GLES20.GL_TEXTURE_2D; int width = buffer.getWidth(); int height = buffer.getHeight(); + + // Obtain nv21 pixel data int nv21Size = (int) (width * height * 3.0f / 2.0f + 0.5f); if (nv21ByteBuffer == null || nv21ByteBuffer.capacity() != nv21Size) { if (nv21ByteBuffer != null) { @@ -401,8 +314,6 @@ private boolean processDoubleInput(VideoFrame videoFrame) { nv21ByteBuffer = ByteBuffer.allocateDirect(nv21Size); nv21ByteArray = new byte[nv21Size]; } - - VideoFrame.I420Buffer i420Buffer = buffer.toI420(); YuvHelper.I420ToNV12(i420Buffer.getDataY(), i420Buffer.getStrideY(), i420Buffer.getDataV(), i420Buffer.getStrideV(), @@ -412,23 +323,44 @@ private boolean processDoubleInput(VideoFrame videoFrame) { nv21ByteBuffer.get(nv21ByteArray); i420Buffer.release(); - Integer processTexId = mDoubleTextureBufferHelper.invoke(() -> iBeautySenseTime.process( - nv21ByteArray, - textureId, textureFormat, - width, height, mFrameRotation - )); + if (mTextureBufferHelper == null) { + Log.d(TAG, "doOnBeautyCreatingBegin..."); + mTextureBufferHelper = TextureBufferHelper.create("STRender", EglBaseProvider.instance().getRootEglBase().getEglBaseContext()); + mTextureBufferHelper.invoke(() -> { + iBeautySenseTime = IBeautySenseTime.create(getContext()); + return null; + }); + Log.d(TAG, "doOnBeautyCreatingEnd."); + runOnUIThread(() -> { + mBinding.cbFilter.setChecked(false); + mBinding.cbFaceBeautify.setChecked(false); + mBinding.cbSticker.setChecked(false); + mBinding.cbMakeup.setChecked(false); + }); + } - // drag one frame to avoid reframe when switching camera. - if(mFrameRotation != videoFrame.getRotation()){ - mFrameRotation = videoFrame.getRotation(); - return false; + int processTexId; + if (buffer instanceof VideoFrame.TextureBuffer) { + VideoFrame.TextureBuffer texBuffer = (VideoFrame.TextureBuffer) buffer; + int textureFormat = texBuffer.getType() == VideoFrame.TextureBuffer.Type.OES ? GLES11Ext.GL_TEXTURE_EXTERNAL_OES : GLES20.GL_TEXTURE_2D; + float[] transformMatrix = RendererCommon.convertMatrixFromAndroidGraphicsMatrix(texBuffer.getTransformMatrix()); + + processTexId = iBeautySenseTime.process( + nv21ByteArray, + texBuffer.getTextureId(), textureFormat, + width, height, videoFrame.getRotation(), transformMatrix + ); + } else { + processTexId = iBeautySenseTime.process( + nv21ByteArray, + width, height, videoFrame.getRotation() + ); } - VideoFrame.TextureBuffer processBuffer = mDoubleTextureBufferHelper.wrapTextureBuffer( - width, height, VideoFrame.TextureBuffer.Type.RGB, processTexId, texBuffer.getTransformMatrix()); - videoFrame.replaceBuffer(processBuffer, mFrameRotation, videoFrame.getTimestampNs()); + VideoFrame.TextureBuffer processBuffer = mTextureBufferHelper.wrapTextureBuffer( + width, height, VideoFrame.TextureBuffer.Type.RGB, processTexId, new Matrix()); + videoFrame.replaceBuffer(processBuffer, videoFrame.getRotation(), videoFrame.getTimestampNs()); buffer.release(); - return true; } @@ -448,54 +380,55 @@ private void joinChannel() { TextureView videoView = new TextureView(requireContext()); rtcEngine.setupLocalVideo(new VideoCanvas(videoView, Constants.RENDER_MODE_HIDDEN)); mLocalVideoLayout.addView(videoView); - rtcEngine.startPreview(); updateVideoLayouts(isLocalFull); } private void updateVideoLayouts(boolean isLocalFull) { this.isLocalFull = isLocalFull; - mBinding.fullVideoContainer.removeAllViews(); - mBinding.smallVideoContainer.removeAllViews(); if (isLocalFull) { if (mLocalVideoLayout != null) { - mBinding.fullVideoContainer.addView(mLocalVideoLayout); + ViewParent parent = mLocalVideoLayout.getParent(); + if (parent instanceof ViewGroup && parent != mBinding.fullVideoContainer) { + ((ViewGroup) parent).removeView(mLocalVideoLayout); + mBinding.fullVideoContainer.addView(mLocalVideoLayout); + } else { + mBinding.fullVideoContainer.addView(mLocalVideoLayout); + } } if (mRemoteVideoLayout != null) { mRemoteVideoLayout.getChildAt(0).setOnClickListener(v -> updateVideoLayouts(!SceneTimeBeauty.this.isLocalFull)); - mBinding.smallVideoContainer.addView(mRemoteVideoLayout); + ViewParent parent = mRemoteVideoLayout.getParent(); + if (parent instanceof ViewGroup && parent != mBinding.smallVideoContainer) { + ((ViewGroup) parent).removeView(mRemoteVideoLayout); + mBinding.smallVideoContainer.addView(mRemoteVideoLayout); + } else { + mBinding.smallVideoContainer.addView(mRemoteVideoLayout); + } } } else { if (mLocalVideoLayout != null) { mLocalVideoLayout.getChildAt(0).setOnClickListener(v -> updateVideoLayouts(!SceneTimeBeauty.this.isLocalFull)); - mBinding.smallVideoContainer.addView(mLocalVideoLayout); + ViewParent parent = mLocalVideoLayout.getParent(); + if (parent instanceof ViewGroup && parent != mBinding.smallVideoContainer) { + ((ViewGroup) parent).removeView(mLocalVideoLayout); + mBinding.smallVideoContainer.addView(mLocalVideoLayout); + } else { + mBinding.smallVideoContainer.addView(mLocalVideoLayout); + } } + if (mRemoteVideoLayout != null) { - mBinding.fullVideoContainer.addView(mRemoteVideoLayout); + ViewParent parent = mRemoteVideoLayout.getParent(); + if (parent instanceof ViewGroup && parent != mBinding.fullVideoContainer) { + ((ViewGroup) parent).removeView(mRemoteVideoLayout); + mBinding.fullVideoContainer.addView(mRemoteVideoLayout); + } else { + mBinding.fullVideoContainer.addView(mRemoteVideoLayout); + } } } } - private void doOnBeautyCreatingBegin() { - Log.d(TAG, "doOnBeautyCreatingBegin..."); - } - - private void doOnBeautyCreatingEnd() { - Log.d(TAG, "doOnBeautyCreatingEnd."); - runOnUIThread(() -> { - mBinding.cbFilter.setChecked(false); - mBinding.cbFaceBeautify.setChecked(false); - mBinding.cbSticker.setChecked(false); - mBinding.cbMakeup.setChecked(false); - }); - } - - private void doOnBeautyReleasingBegin() { - Log.d(TAG, "doOnBeautyReleasingBegin..."); - } - - private void doOnBeautyReleasingEnd() { - Log.d(TAG, "doOnBeautyReleasingEnd."); - } } diff --git a/Android/APIExample/app/src/main/res/layout/fragment_beauty_scenetime.xml b/Android/APIExample/app/src/main/res/layout/fragment_beauty_scenetime.xml index 2154a5cd7..25e7eacc8 100644 --- a/Android/APIExample/app/src/main/res/layout/fragment_beauty_scenetime.xml +++ b/Android/APIExample/app/src/main/res/layout/fragment_beauty_scenetime.xml @@ -47,20 +47,6 @@ app:layout_constraintTop_toTopOf="parent" /> - - The plug-in cloud market provides a more convenient integration method. You can log in to the sound network [console](https://console.agora.io/) to view the [integration tutorial](https://console.agora.io/ marketplace/license/introduce?serviceName=sensetime-ar) ## 1 SenseTime Beauty SDK diff --git a/Android/APIExample/beauty/sense-time/README.zh.md b/Android/APIExample/beauty/sense-time/README.zh.md index 7dfa59656..b02500bb8 100644 --- a/Android/APIExample/beauty/sense-time/README.zh.md +++ b/Android/APIExample/beauty/sense-time/README.zh.md @@ -1,7 +1,7 @@ # 配置指南 *[English](README.md) | 中文* -此示例提供了两种商汤美颜接入方式,单输入(仅NV21)和双输入(NV21和纹理)。双输入比单输入少了 NV21转纹理的这步操作,转换性能比较高,但是在低端机上可能出现贴纸不跟脸等情况,建议根据面向的客户来选择合适的接入方式。 +此示例提供了商汤美颜的最佳接入方式。 > 声网插件云市场上提供了更便捷的集成方式,可以登录声网[控制台](https://console.agora.io/)查看[集成教程](https://console.agora.io/marketplace/license/introduce?serviceName=sensetime-ar) diff --git a/Android/APIExample/beauty/sense-time/src/main/java/com/sensetime/effects/STRenderer.java b/Android/APIExample/beauty/sense-time/src/main/java/com/sensetime/effects/STRenderer.java index 507b606c7..c20e77007 100644 --- a/Android/APIExample/beauty/sense-time/src/main/java/com/sensetime/effects/STRenderer.java +++ b/Android/APIExample/beauty/sense-time/src/main/java/com/sensetime/effects/STRenderer.java @@ -79,6 +79,8 @@ public class STRenderer { private final STEffectParameters mEffectParams = new STEffectParameters(); private STFaceMeshList faceMeshList; + private int mImageWidth; + private int mImageHeight; public STRenderer(Context context) { mContext = context; @@ -262,7 +264,7 @@ private int getCurrentOrientation(int rotation) { } public int preProcess( - int width, int height, int orientation, + int width, int height, int rotation, byte[] cameraPixel, int pixelFormat ) { @@ -270,14 +272,12 @@ public int preProcess( return -1; } - int imageWidth = width; - int imageHeight = height; -// if (orientation == 90 || orientation == 270) { -// imageWidth = height; -// imageHeight = width; -// } + boolean sizeChange = mImageWidth != width || mImageHeight != height; + mImageWidth = width; + mImageHeight = height; - if (mImageDataBuffer == null || mImageDataBuffer.length != cameraPixel.length) { + + if (sizeChange || mImageDataBuffer == null) { mImageDataBuffer = new byte[cameraPixel.length]; } System.arraycopy(cameraPixel, 0, mImageDataBuffer, 0, cameraPixel.length); @@ -293,12 +293,12 @@ public int preProcess( mImageDataBuffer, pixelFormat, mDetectConfig, - getCurrentOrientation(orientation), + getCurrentOrientation(rotation), width, height); if (ret == 0) { if (mNeedAnimalDetect) { - animalDetect(mImageDataBuffer, pixelFormat, getCurrentOrientation(orientation), width, height, 0); + animalDetect(mImageDataBuffer, pixelFormat, getCurrentOrientation(rotation), width, height, 0); } else { mAnimalFaceInfo[0] = new STAnimalFaceInfo(null, 0); } @@ -307,10 +307,14 @@ public int preProcess( // >>>>>> 2. upload nv21 to texture if (mTextureOutId == null) { mTextureOutId = new int[2]; - GlUtil.initEffectTexture(imageWidth, imageHeight, mTextureOutId, GLES20.GL_TEXTURE_2D); + GlUtil.initEffectTexture(mImageWidth, mImageHeight, mTextureOutId, GLES20.GL_TEXTURE_2D); + } else if (sizeChange) { + GLES20.glDeleteTextures(mTextureOutId.length, mTextureOutId, 0); + mTextureOutId = null; + return -1; } - mSTMobileColorConvertNative.setTextureSize(imageWidth, imageHeight); + mSTMobileColorConvertNative.setTextureSize(mImageWidth, mImageHeight); mSTMobileColorConvertNative.nv21BufferToRgbaTexture(width, height, STRotateType.ST_CLOCKWISE_ROTATE_0, false, @@ -321,9 +325,9 @@ public int preProcess( // >>>>>> 3. render texture //输入纹理,纹理只支持2D - STEffectTexture stEffectTexture = new STEffectTexture(textureId, imageWidth, imageHeight, 0); + STEffectTexture stEffectTexture = new STEffectTexture(textureId, mImageWidth, mImageHeight, 0); //输出纹理,需要在上层初始化 - STEffectTexture stEffectTextureOut = new STEffectTexture(mTextureOutId[1], imageWidth, imageHeight, 0); + STEffectTexture stEffectTextureOut = new STEffectTexture(mTextureOutId[1], mImageWidth, mImageHeight, 0); //渲染接口输入参数 STEffectRenderInParam sTEffectRenderInParam = new STEffectRenderInParam( @@ -356,7 +360,7 @@ public int preProcess( /** * @param width camera preview width * @param height camera preview height - * @param orientation camera preview orientation + * @param rotation camera preview orientation * @param cameraPixel camera preview pixel data * @param pixelFormat {@link STCommonNative#ST_PIX_FMT_NV21} and etc. * @param cameraTextureId camera preview texture id @@ -364,32 +368,39 @@ public int preProcess( * @return new Texture ID to render */ public int preProcess( - int width, int height, int orientation, + int width, int height, int rotation, byte[] cameraPixel, int pixelFormat, - int cameraTextureId, int texFormat) { + int cameraTextureId, int texFormat, float[] transformMatrix) { if (!mAuthorized) { return -1; } - int imageWidth = width; - int imageHeight = height; + + boolean sizeChange = mImageWidth != width || mImageHeight != height; + mImageWidth = width; + mImageHeight = height; + // >>>>>> 1. translate oes texture to 2d if (mTextureOutId == null) { mTextureOutId = new int[1]; - GlUtil.initEffectTexture(imageWidth, imageHeight, mTextureOutId, GLES20.GL_TEXTURE_2D); + GlUtil.initEffectTexture(mImageWidth, mImageHeight, mTextureOutId, GLES20.GL_TEXTURE_2D); + } else if (sizeChange) { + GLES20.glDeleteTextures(mTextureOutId.length, mTextureOutId, 0); + mTextureOutId = null; + return -1; } int textureId = cameraTextureId; if (texFormat == GLES11Ext.GL_TEXTURE_EXTERNAL_OES) { - mGLRenderBefore.adjustRenderSize(imageWidth, imageHeight, 0, false, false); - textureId = mGLRenderBefore.process(cameraTextureId, STGLRender.IDENTITY_MATRIX); + mGLRenderBefore.adjustRenderSize(mImageWidth, mImageHeight, 0, false, true); + textureId = mGLRenderBefore.process(cameraTextureId, transformMatrix); } // >>>>>> 2. detect human point info using cameraData if (mIsCreateHumanActionHandleSucceeded) { - if (mImageDataBuffer == null || mImageDataBuffer.length != cameraPixel.length) { + if (sizeChange || mImageDataBuffer == null) { mImageDataBuffer = new byte[cameraPixel.length]; } System.arraycopy(cameraPixel, 0, mImageDataBuffer, 0, cameraPixel.length); @@ -401,14 +412,14 @@ public int preProcess( int ret = mSTHumanActionNative.nativeHumanActionDetectPtr(mImageDataBuffer, pixelFormat, mDetectConfig, - getCurrentOrientation(orientation), + getCurrentOrientation(rotation), width, height); //STHumanAction nativeHumanAction = mSTHumanActionNative.getNativeHumanAction(); //LogUtils.i(TAG, "human action detect cost time: %d, ret: %d", System.currentTimeMillis() - startHumanAction, ret); if (ret == 0) { if (mNeedAnimalDetect) { - animalDetect(mImageDataBuffer, pixelFormat, getCurrentOrientation(orientation), width, height, 0); + animalDetect(mImageDataBuffer, pixelFormat, getCurrentOrientation(rotation), width, height, 0); } else { mAnimalFaceInfo[0] = new STAnimalFaceInfo(null, 0); } @@ -419,9 +430,9 @@ public int preProcess( // >>>>>> 3. render texture //输入纹理,纹理只支持2D - STEffectTexture stEffectTexture = new STEffectTexture(textureId, imageWidth, imageHeight, 0); + STEffectTexture stEffectTexture = new STEffectTexture(textureId, mImageWidth, mImageHeight, 0); //输出纹理,需要在上层初始化 - STEffectTexture stEffectTextureOut = new STEffectTexture(mTextureOutId[0], imageWidth, imageHeight, 0); + STEffectTexture stEffectTextureOut = new STEffectTexture(mTextureOutId[0], mImageWidth, mImageHeight, 0); //渲染接口输入参数 STEffectRenderInParam sTEffectRenderInParam = new STEffectRenderInParam( @@ -585,6 +596,7 @@ public void release() { mChangeStickerManagerThread = null; deleteTextures(); mGLRenderBefore.destroyPrograms(); + mImageWidth = mImageHeight = 0; } private void deleteTextures() { diff --git a/Android/APIExample/beauty/sense-time/src/main/java/com/sensetime/effects/display/STGLRender.java b/Android/APIExample/beauty/sense-time/src/main/java/com/sensetime/effects/display/STGLRender.java index 6a6e44898..80ed1ac5a 100644 --- a/Android/APIExample/beauty/sense-time/src/main/java/com/sensetime/effects/display/STGLRender.java +++ b/Android/APIExample/beauty/sense-time/src/main/java/com/sensetime/effects/display/STGLRender.java @@ -14,14 +14,19 @@ public class STGLRender { private RenderProgram mRenderProgram; + private int mRotation; + private boolean mFlipH, mFlipV; public STGLRender(int textureType) { mRenderProgram = new RenderProgram(textureType); } public void adjustRenderSize(int width, int height, int rotation, boolean flipH, boolean flipV) { - boolean resize = mRenderProgram.resize(width, height); - if (resize) { + boolean change = mRenderProgram.resize(width, height) || mRotation != rotation || mFlipH != flipH && mFlipV != flipV; + if (change) { + mRotation = rotation; + mFlipH = flipH; + mFlipV = flipV; float[] tmp = new float[16]; Matrix.setIdentityM(tmp, 0); diff --git a/Android/APIExample/beauty/sense-time/src/main/java/io/agora/beauty/sensetime/BeautySenseTimeImpl.java b/Android/APIExample/beauty/sense-time/src/main/java/io/agora/beauty/sensetime/BeautySenseTimeImpl.java index 492894583..fb28ca7a1 100644 --- a/Android/APIExample/beauty/sense-time/src/main/java/io/agora/beauty/sensetime/BeautySenseTimeImpl.java +++ b/Android/APIExample/beauty/sense-time/src/main/java/io/agora/beauty/sensetime/BeautySenseTimeImpl.java @@ -23,18 +23,18 @@ public BeautySenseTimeImpl(Context context) { } @Override - public int process(byte[] nv21, int textureId, int texFormat, int width, int height, int orientation) { + public int process(byte[] nv21, int textureId, int texFormat, int width, int height, int rotation, float[] transformMatrix) { return mSTRenderer.preProcess( - width, height, orientation, + width, height, rotation, nv21, STCommonNative.ST_PIX_FMT_NV21, - textureId, texFormat + textureId, texFormat, transformMatrix ); } @Override - public int process(byte[] nv21, int width, int height, int orientation) { + public int process(byte[] nv21, int width, int height, int rotation) { return mSTRenderer.preProcess( - width, height, orientation, + width, height, rotation, nv21, STCommonNative.ST_PIX_FMT_NV21 ); } From ef5f46d6d893c750f3f2a0a80e58617541e7d17d Mon Sep 17 00:00:00 2001 From: xucz Date: Fri, 17 Mar 2023 15:19:47 +0800 Subject: [PATCH 010/154] [Android]adapte to 4.2.0. --- .../example/common/model/GlobalSettings.java | 6 ++--- .../CDNStreaming/AudienceFragment.java | 10 ++++---- .../advanced/CustomRemoteVideoRender.java | 14 ++--------- .../examples/advanced/MediaPlayer.java | 5 ++-- .../examples/advanced/ProcessRawData.java | 14 ++--------- .../advanced/PushExternalVideoYUV.java | 13 ++++------ .../examples/advanced/ScreenSharing.java | 13 ++++------ .../advanced/SwitchCameraScreenShare.java | 17 ++++++------- .../advanced/beauty/ByteDanceBeauty.java | 18 ++++---------- .../advanced/beauty/FaceUnityBeauty.java | 16 +++---------- .../advanced/beauty/SceneTimeBeauty.java | 16 +++---------- .../customaudio/CustomAudioRender.java | 3 ++- .../customaudio/CustomAudioSource.java | 24 ++++++++++++------- .../app/src/main/res/values/arrays.xml | 1 - 14 files changed, 60 insertions(+), 110 deletions(-) diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/common/model/GlobalSettings.java b/Android/APIExample/app/src/main/java/io/agora/api/example/common/model/GlobalSettings.java index 4ce81f103..431be6f1f 100644 --- a/Android/APIExample/app/src/main/java/io/agora/api/example/common/model/GlobalSettings.java +++ b/Android/APIExample/app/src/main/java/io/agora/api/example/common/model/GlobalSettings.java @@ -2,7 +2,7 @@ import static io.agora.rtc2.video.VideoEncoderConfiguration.FRAME_RATE.FRAME_RATE_FPS_15; import static io.agora.rtc2.video.VideoEncoderConfiguration.ORIENTATION_MODE.ORIENTATION_MODE_ADAPTIVE; -import static io.agora.rtc2.video.VideoEncoderConfiguration.VD_960x540; +import static io.agora.rtc2.video.VideoEncoderConfiguration.VD_640x480; import android.text.TextUtils; import android.util.Log; @@ -37,7 +37,7 @@ public class GlobalSettings { public String getVideoEncodingDimension() { if (videoEncodingDimension == null) - return "VD_960x540"; + return "VD_640x480"; else return videoEncodingDimension; } @@ -66,7 +66,7 @@ public LocalAccessPointConfiguration getPrivateCloudConfig() { } public VideoEncoderConfiguration.VideoDimensions getVideoEncodingDimensionObject() { - VideoEncoderConfiguration.VideoDimensions value = VD_960x540; + VideoEncoderConfiguration.VideoDimensions value = VD_640x480; try { Field tmp = VideoEncoderConfiguration.class.getDeclaredField(getVideoEncodingDimension()); tmp.setAccessible(true); diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/CDNStreaming/AudienceFragment.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/CDNStreaming/AudienceFragment.java index 84327a0df..e71b715b6 100644 --- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/CDNStreaming/AudienceFragment.java +++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/CDNStreaming/AudienceFragment.java @@ -157,8 +157,9 @@ public void onActivityCreated(@Nullable Bundle savedInstanceState) { } fl_local.addView(surfaceView); // Setup local video to render your local media player view - VideoCanvas videoCanvas = new VideoCanvas(surfaceView, Constants.RENDER_MODE_HIDDEN, Constants.VIDEO_MIRROR_MODE_AUTO, - Constants.VIDEO_SOURCE_MEDIA_PLAYER, mediaPlayer.getMediaPlayerId(), 0); + VideoCanvas videoCanvas = new VideoCanvas(surfaceView, Constants.RENDER_MODE_HIDDEN, 0); + videoCanvas.sourceType = Constants.VIDEO_SOURCE_MEDIA_PLAYER; + videoCanvas.mediaPlayerId = mediaPlayer.getMediaPlayerId(); engine.setupLocalVideo(videoCanvas); // Your have to call startPreview to see player video engine.startPreview(); @@ -384,8 +385,9 @@ private void toggleVideoLayout(boolean isMultiple) { } fl_local.addView(surfaceView); // Setup local video to render your local media player view - VideoCanvas videoCanvas = new VideoCanvas(surfaceView, Constants.RENDER_MODE_HIDDEN, Constants.VIDEO_MIRROR_MODE_AUTO, - Constants.VIDEO_SOURCE_MEDIA_PLAYER, mediaPlayer.getMediaPlayerId(), 0); + VideoCanvas videoCanvas = new VideoCanvas(surfaceView, Constants.RENDER_MODE_HIDDEN, 0); + videoCanvas.sourceType = Constants.VIDEO_SOURCE_MEDIA_PLAYER; + videoCanvas.mediaPlayerId = mediaPlayer.getMediaPlayerId(); engine.setupLocalVideo(videoCanvas); } engine.startPreview(); diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/CustomRemoteVideoRender.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/CustomRemoteVideoRender.java index 23c4b89b0..1e2290158 100644 --- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/CustomRemoteVideoRender.java +++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/CustomRemoteVideoRender.java @@ -472,22 +472,12 @@ public void onUserOffline(int uid, int reason) { IVideoFrameObserver videoFrameObserver = new IVideoFrameObserver() { @Override - public boolean onCaptureVideoFrame(VideoFrame videoFrame) { + public boolean onCaptureVideoFrame(int sourceType, VideoFrame videoFrame) { return false; } @Override - public boolean onPreEncodeVideoFrame(VideoFrame videoFrame) { - return false; - } - - @Override - public boolean onScreenCaptureVideoFrame(VideoFrame videoFrame) { - return false; - } - - @Override - public boolean onPreEncodeScreenVideoFrame(VideoFrame videoFrame) { + public boolean onPreEncodeVideoFrame(int sourceType, VideoFrame videoFrame) { return false; } diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/MediaPlayer.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/MediaPlayer.java index ae2565a40..5058e0a0d 100644 --- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/MediaPlayer.java +++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/MediaPlayer.java @@ -271,8 +271,9 @@ private void joinChannel(String channelId) { } fl_local.addView(surfaceView); // Setup local video to render your local media player view - VideoCanvas videoCanvas = new VideoCanvas(surfaceView, Constants.RENDER_MODE_HIDDEN, Constants.VIDEO_MIRROR_MODE_AUTO, - Constants.VIDEO_SOURCE_MEDIA_PLAYER, mediaPlayer.getMediaPlayerId(), 0); + VideoCanvas videoCanvas = new VideoCanvas(surfaceView, Constants.RENDER_MODE_HIDDEN, 0); + videoCanvas.sourceType = Constants.VIDEO_SOURCE_MEDIA_PLAYER; + videoCanvas.mediaPlayerId = mediaPlayer.getMediaPlayerId(); engine.setupLocalVideo(videoCanvas); // Set audio route to microPhone engine.setDefaultAudioRoutetoSpeakerphone(true); diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/ProcessRawData.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/ProcessRawData.java index 8a70b5ef8..8884f1a97 100644 --- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/ProcessRawData.java +++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/ProcessRawData.java @@ -273,7 +273,7 @@ private void joinChannel(String channelId) { private final IVideoFrameObserver iVideoFrameObserver = new IVideoFrameObserver() { @Override - public boolean onCaptureVideoFrame(VideoFrame videoFrame) { + public boolean onCaptureVideoFrame(int sourceType, VideoFrame videoFrame) { Log.i(TAG, "OnEncodedVideoImageReceived"+Thread.currentThread().getName()); long startTime = System.currentTimeMillis(); @@ -338,17 +338,7 @@ public boolean onCaptureVideoFrame(VideoFrame videoFrame) { } @Override - public boolean onPreEncodeVideoFrame(VideoFrame videoFrame) { - return false; - } - - @Override - public boolean onScreenCaptureVideoFrame(VideoFrame videoFrame) { - return false; - } - - @Override - public boolean onPreEncodeScreenVideoFrame(VideoFrame videoFrame) { + public boolean onPreEncodeVideoFrame(int sourceType, VideoFrame videoFrame) { return false; } diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/PushExternalVideoYUV.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/PushExternalVideoYUV.java index d8a7d050e..0d0972e56 100644 --- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/PushExternalVideoYUV.java +++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/PushExternalVideoYUV.java @@ -233,9 +233,10 @@ private void joinChannel(String channelId) { engine.setExternalVideoSource(true, false, Constants.ExternalVideoSourceType.VIDEO_FRAME); TextureView textureView = new TextureView(getContext()); - engine.setupLocalVideo(new VideoCanvas(textureView, - Constants.RENDER_MODE_FIT, Constants.VIDEO_MIRROR_MODE_DISABLED, - Constants.VIDEO_SOURCE_CUSTOM, 0)); + VideoCanvas local = new VideoCanvas(textureView, Constants.RENDER_MODE_FIT, 0); + local.mirrorMode = Constants.VIDEO_MIRROR_MODE_DISABLED; + local.sourceType = Constants.VIDEO_SOURCE_CUSTOM; + engine.setupLocalVideo(local); // Add to the local container fl_local.removeAllViews(); fl_local.addView(textureView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, @@ -278,12 +279,6 @@ private void joinChannel(String channelId) { * The SDK uses this class to report to the app on SDK runtime events. */ private final IRtcEngineEventHandler iRtcEngineEventHandler = new IRtcEngineEventHandler() { - /**Reports a warning during SDK runtime. - * Warning code: https://docs.agora.io/en/Voice/API%20Reference/java/classio_1_1agora_1_1rtc_1_1_i_rtc_engine_event_handler_1_1_warn_code.html*/ - @Override - public void onWarning(int warn) { - Log.w(TAG, String.format("onWarning code %d message %s", warn, RtcEngine.getErrorDescription(warn))); - } /**Occurs when a user leaves the channel. * @param stats With this callback, the application retrieves the channel information, diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/ScreenSharing.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/ScreenSharing.java index 1c322d70f..ccd8f1e40 100644 --- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/ScreenSharing.java +++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/ScreenSharing.java @@ -244,20 +244,17 @@ private void startScreenSharePreview() { // Add to the local container fl_local.addView(surfaceView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); // Setup local video to render your local camera preview - engine.setupLocalVideo(new VideoCanvas(surfaceView, Constants.RENDER_MODE_FIT, - Constants.VIDEO_MIRROR_MODE_DISABLED, - Constants.VIDEO_SOURCE_SCREEN_PRIMARY, - 0)); + VideoCanvas local = new VideoCanvas(surfaceView, Constants.RENDER_MODE_FIT, 0); + local.mirrorMode = Constants.VIDEO_MIRROR_MODE_DISABLED; + local.sourceType = Constants.VIDEO_SOURCE_SCREEN_PRIMARY; + engine.setupLocalVideo(local); engine.startPreview(Constants.VideoSourceType.VIDEO_SOURCE_SCREEN_PRIMARY); } private void stopScreenSharePreview() { fl_local.removeAllViews(); - engine.setupLocalVideo(new VideoCanvas(null, Constants.RENDER_MODE_FIT, - Constants.VIDEO_MIRROR_MODE_DISABLED, - Constants.VIDEO_SOURCE_SCREEN_PRIMARY, - 0)); + engine.setupLocalVideo(new VideoCanvas(null)); engine.stopPreview(Constants.VideoSourceType.VIDEO_SOURCE_SCREEN_PRIMARY); } diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/SwitchCameraScreenShare.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/SwitchCameraScreenShare.java index 2f7805e67..0854c2576 100644 --- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/SwitchCameraScreenShare.java +++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/SwitchCameraScreenShare.java @@ -327,10 +327,10 @@ private void addScreenSharePreview() { // Add to the local container fl_screen.addView(surfaceView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); // Setup local video to render your local camera preview - engine.setupLocalVideo(new VideoCanvas(surfaceView, Constants.RENDER_MODE_FIT, - Constants.VIDEO_MIRROR_MODE_DISABLED, - Constants.VIDEO_SOURCE_SCREEN_PRIMARY, - 0)); + VideoCanvas local = new VideoCanvas(surfaceView, Constants.RENDER_MODE_FIT, 0); + local.mirrorMode = Constants.VIDEO_MIRROR_MODE_DISABLED; + local.sourceType = Constants.VIDEO_SOURCE_SCREEN_PRIMARY; + engine.setupLocalVideo(local); engine.startPreview(Constants.VideoSourceType.VIDEO_SOURCE_SCREEN_PRIMARY); } @@ -349,12 +349,9 @@ private void addCameraPreview() { // Add to the local container fl_camera.addView(surfaceView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); // Setup local video to render your local camera preview - engine.setupLocalVideo(new VideoCanvas( - surfaceView, - RENDER_MODE_HIDDEN, - Constants.VIDEO_MIRROR_MODE_AUTO, - Constants.VIDEO_SOURCE_CAMERA_PRIMARY, - 0)); + VideoCanvas local = new VideoCanvas(surfaceView, RENDER_MODE_HIDDEN, 0); + local.sourceType = Constants.VIDEO_SOURCE_CAMERA_PRIMARY; + engine.setupLocalVideo(local); engine.startPreview(Constants.VideoSourceType.VIDEO_SOURCE_CAMERA_PRIMARY); } diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/beauty/ByteDanceBeauty.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/beauty/ByteDanceBeauty.java index a70ac1dd5..4cbef1b98 100644 --- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/beauty/ByteDanceBeauty.java +++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/beauty/ByteDanceBeauty.java @@ -199,7 +199,7 @@ public void onRemoteVideoStats(RemoteVideoStats stats) { mVideoFrameObserver = new IVideoFrameObserver() { @Override - public boolean onCaptureVideoFrame(VideoFrame videoFrame) { + public boolean onCaptureVideoFrame(int sourceType, VideoFrame videoFrame) { if (isDestroyed) { return false; } @@ -243,17 +243,7 @@ public boolean onCaptureVideoFrame(VideoFrame videoFrame) { } @Override - public boolean onPreEncodeVideoFrame(VideoFrame videoFrame) { - return false; - } - - @Override - public boolean onScreenCaptureVideoFrame(VideoFrame videoFrame) { - return false; - } - - @Override - public boolean onPreEncodeScreenVideoFrame(VideoFrame videoFrame) { + public boolean onPreEncodeVideoFrame(int sourceType, VideoFrame videoFrame) { return false; } @@ -315,7 +305,9 @@ private void joinChannel() { mLocalVideoLayout = new VideoReportLayout(requireContext()); TextureView videoView = new TextureView(requireContext()); - rtcEngine.setupLocalVideo(new VideoCanvas(videoView, Constants.RENDER_MODE_HIDDEN, Constants.VIDEO_MIRROR_MODE_DISABLED, 0)); + VideoCanvas local = new VideoCanvas(videoView, Constants.RENDER_MODE_HIDDEN, 0); + local.mirrorMode = Constants.VIDEO_MIRROR_MODE_DISABLED; + rtcEngine.setupLocalVideo(local); mLocalVideoLayout.addView(videoView); rtcEngine.startPreview(); diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/beauty/FaceUnityBeauty.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/beauty/FaceUnityBeauty.java index c835ef0aa..1dbc7d4d9 100644 --- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/beauty/FaceUnityBeauty.java +++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/beauty/FaceUnityBeauty.java @@ -214,7 +214,7 @@ public void onRemoteVideoStats(RemoteVideoStats stats) { mVideoFrameObserver = new IVideoFrameObserver() { @Override - public boolean onCaptureVideoFrame(VideoFrame videoFrame) { + public boolean onCaptureVideoFrame(int sourceType, VideoFrame videoFrame) { if (isDestroyed) { return true; } @@ -254,17 +254,7 @@ public boolean onCaptureVideoFrame(VideoFrame videoFrame) { } @Override - public boolean onPreEncodeVideoFrame(VideoFrame videoFrame) { - return false; - } - - @Override - public boolean onScreenCaptureVideoFrame(VideoFrame videoFrame) { - return false; - } - - @Override - public boolean onPreEncodeScreenVideoFrame(VideoFrame videoFrame) { + public boolean onPreEncodeVideoFrame(int sourceType, VideoFrame videoFrame) { return false; } @@ -381,7 +371,7 @@ private void joinChannel() { mLocalVideoLayout = new VideoReportLayout(requireContext()); TextureView videoView = new TextureView(requireContext()); - rtcEngine.setupLocalVideo(new VideoCanvas(videoView, Constants.RENDER_MODE_HIDDEN)); + rtcEngine.setupLocalVideo(new VideoCanvas(videoView, Constants.RENDER_MODE_HIDDEN, 0)); mLocalVideoLayout.addView(videoView); rtcEngine.startPreview(); diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/beauty/SceneTimeBeauty.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/beauty/SceneTimeBeauty.java index ea07da468..2e406761b 100644 --- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/beauty/SceneTimeBeauty.java +++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/beauty/SceneTimeBeauty.java @@ -205,22 +205,12 @@ public void onRemoteVideoStats(RemoteVideoStats stats) { mVideoFrameObserver = new IVideoFrameObserver() { @Override - public boolean onCaptureVideoFrame(VideoFrame videoFrame) { + public boolean onCaptureVideoFrame(int sourceType, VideoFrame videoFrame) { return processBeauty(videoFrame); } @Override - public boolean onPreEncodeVideoFrame(VideoFrame videoFrame) { - return false; - } - - @Override - public boolean onScreenCaptureVideoFrame(VideoFrame videoFrame) { - return false; - } - - @Override - public boolean onPreEncodeScreenVideoFrame(VideoFrame videoFrame) { + public boolean onPreEncodeVideoFrame(int sourceType, VideoFrame videoFrame) { return false; } @@ -378,7 +368,7 @@ private void joinChannel() { mLocalVideoLayout = new VideoReportLayout(requireContext()); TextureView videoView = new TextureView(requireContext()); - rtcEngine.setupLocalVideo(new VideoCanvas(videoView, Constants.RENDER_MODE_HIDDEN)); + rtcEngine.setupLocalVideo(new VideoCanvas(videoView, Constants.RENDER_MODE_HIDDEN, 0)); mLocalVideoLayout.addView(videoView); updateVideoLayouts(isLocalFull); diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/customaudio/CustomAudioRender.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/customaudio/CustomAudioRender.java index 92c2afb78..e66641d57 100755 --- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/customaudio/CustomAudioRender.java +++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/customaudio/CustomAudioRender.java @@ -236,6 +236,7 @@ public void onClick(View v) { engine.leaveChannel(); pulling = false; join.setText(getString(R.string.join)); + audioSeatManager.downAllSeats(); if(pullingTask != null){ try { pullingTask.join(); @@ -268,7 +269,7 @@ private void joinChannel(String channelId) { * 0: Success. * < 0: Failure. * PS: Ensure that you call this method before the joinChannel method.*/ - engine.setExternalAudioSource(true, SAMPLE_RATE, SAMPLE_NUM_OF_CHANNEL, 2, false, true); + // engine.setExternalAudioSource(true, SAMPLE_RATE, SAMPLE_NUM_OF_CHANNEL, 2, false, true); diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/customaudio/CustomAudioSource.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/customaudio/CustomAudioSource.java index a4403f77a..a94f62cfb 100755 --- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/customaudio/CustomAudioSource.java +++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/customaudio/CustomAudioSource.java @@ -34,6 +34,7 @@ import io.agora.rtc2.RtcEngine; import io.agora.rtc2.RtcEngineConfig; import io.agora.rtc2.RtcEngineEx; +import io.agora.rtc2.audio.AudioTrackConfig; /** * This demo demonstrates how to make a one-to-one voice call @@ -54,10 +55,11 @@ public class CustomAudioSource extends BaseFragment implements View.OnClickListe public static RtcEngineEx engine; private Switch mic, pcm; private ChannelMediaOptions option = new ChannelMediaOptions(); - private volatile int pushTimes = 0; + private int pushTimes = 0; private AudioSeatManager audioSeatManager; private AudioFileReader audioPushingHelper; + private int customAudioTrack = -1; @Override public void onCreate(@Nullable Bundle savedInstanceState) { @@ -155,9 +157,9 @@ public void onActivityCreated(@Nullable Bundle savedInstanceState) { engine.setLocalAccessPoint(((MainApplication) getActivity().getApplication()).getGlobalSettings().getPrivateCloudConfig()); audioPushingHelper = new AudioFileReader(requireContext(), (buffer, timestamp) -> { - if(joined && engine != null){ - Log.i(TAG, "pushExternalAudioFrame times:" + pushTimes++); - engine.pushExternalAudioFrame(buffer, 0); + if(joined && engine != null && customAudioTrack != -1){ + int ret = engine.pushExternalAudioFrame(buffer, timestamp, AudioFileReader.SAMPLE_RATE, AudioFileReader.SAMPLE_NUM_OF_CHANNEL, Constants.BytesPerSample.TWO_BYTES_PER_SAMPLE, customAudioTrack); + Log.i(TAG, "pushExternalAudioFrame times:" + (++pushTimes) + ", ret=" + ret); } }); } catch (Exception e) { @@ -169,6 +171,10 @@ public void onActivityCreated(@Nullable Bundle savedInstanceState) { @Override public void onDestroy() { super.onDestroy(); + if(customAudioTrack != -1){ + engine.destroyCustomAudioTrack(customAudioTrack); + customAudioTrack = -1; + } if(audioPushingHelper != null){ audioPushingHelper.stop(); } @@ -187,9 +193,10 @@ public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { option.publishMicrophoneTrack = checked; engine.updateChannelMediaOptions(option); } else if (compoundButton.getId() == R.id.localAudio) { + option.publishCustomAudioTrackId = customAudioTrack; option.publishCustomAudioTrack = checked; engine.updateChannelMediaOptions(option); - engine.enableCustomAudioLocalPlayback(0, checked); + engine.enableCustomAudioLocalPlayback(customAudioTrack, checked); } } @@ -268,10 +275,9 @@ private void joinChannel(String channelId) { * 0: Success. * < 0: Failure. * PS: Ensure that you call this method before the joinChannel method.*/ - engine.setExternalAudioSource(true, - AudioFileReader.SAMPLE_RATE, AudioFileReader.SAMPLE_NUM_OF_CHANNEL, AudioFileReader.SAMPLE_NUM_OF_CHANNEL, - false, true); - + AudioTrackConfig config = new AudioTrackConfig(); + config.enableLocalPlayback = false; + customAudioTrack = engine.createCustomAudioTrack(Constants.AudioTrackType.AUDIO_TRACK_MIXABLE, config); /**Please configure accessToken in the string_config file. * A temporary token generated in Console. A temporary token is valid for 24 hours. For details, see diff --git a/Android/APIExample/app/src/main/res/values/arrays.xml b/Android/APIExample/app/src/main/res/values/arrays.xml index 6ba3bd2aa..d0c930c33 100644 --- a/Android/APIExample/app/src/main/res/values/arrays.xml +++ b/Android/APIExample/app/src/main/res/values/arrays.xml @@ -188,7 +188,6 @@ VD_480x480 VD_640x480 VD_840x480 - VD_960x540 VD_960x720 VD_1280x720 From 2f6b3227dd1e1377fe007c80acdba829f6bf7bd1 Mon Sep 17 00:00:00 2001 From: zhaoyongqiang Date: Mon, 20 Mar 2023 11:59:05 +0800 Subject: [PATCH 011/154] Adaptive PCM acquisition --- .../Common/ExternalAudio/ExternalAudio.mm | 2 +- .../CustomPcmAudioSource.swift | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/iOS/APIExample/APIExample/Common/ExternalAudio/ExternalAudio.mm b/iOS/APIExample/APIExample/Common/ExternalAudio/ExternalAudio.mm index cefa82035..dc628b7fb 100644 --- a/iOS/APIExample/APIExample/Common/ExternalAudio/ExternalAudio.mm +++ b/iOS/APIExample/APIExample/Common/ExternalAudio/ExternalAudio.mm @@ -303,7 +303,7 @@ - (void)audioController:(AudioController *)controller didCaptureData:(unsigned c } else { // [self.agoraKit pushExternalAudioFrameNSData:[NSData dataWithBytes:data length:bytesLength] sourceId:1 timestamp:0]; - [self.agoraKit pushExternalAudioFrameRawData: data samples: 441 * 10 sourceId:1 timestamp:0]; + [self.agoraKit pushExternalAudioFrameRawData: data samples: 441 * 10 trackId:1 timestamp:0]; } } diff --git a/iOS/APIExample/APIExample/Examples/Advanced/CustomPcmAudioSource/CustomPcmAudioSource.swift b/iOS/APIExample/APIExample/Examples/Advanced/CustomPcmAudioSource/CustomPcmAudioSource.swift index c030bbf27..3c91c6043 100644 --- a/iOS/APIExample/APIExample/Examples/Advanced/CustomPcmAudioSource/CustomPcmAudioSource.swift +++ b/iOS/APIExample/APIExample/Examples/Advanced/CustomPcmAudioSource/CustomPcmAudioSource.swift @@ -42,6 +42,7 @@ class CustomPcmAudioSourceMain: BaseViewController { var audioViews: [UInt:VideoView] = [:] @IBOutlet weak var playAudioView: UIView! @IBOutlet weak var pushPcmSwitch: UISwitch! + private var trackId: Int32 = 0 // indicate if current instance has joined channel var isJoined: Bool = false { @@ -82,7 +83,10 @@ class CustomPcmAudioSourceMain: BaseViewController { // setup external audio source pcmSourcePush = AgoraPcmSourcePush(delegate: self, filePath: filepath, sampleRate: Int(sampleRate), channelsPerFrame: Int(channel), bitPerSample: bitPerSample, samples: samples) - agoraKit.setExternalAudioSource(true, sampleRate: Int(sampleRate), channels: Int(channel), sourceNumber: 2, localPlayback: true, publish: true) + + let trackConfig = AgoraAudioTrackConfig() + trackConfig.enableLocalPlayback = true + trackId = agoraKit.createCustomAudioTrack(.mixable, config: trackConfig) agoraKit.enableCustomAudioLocalPlayback(1, enabled: true) // start joining channel // 1. Users can only see each other after they join the @@ -94,6 +98,7 @@ class CustomPcmAudioSourceMain: BaseViewController { option.publishCameraTrack = false option.publishMicrophoneTrack = true option.publishCustomAudioTrack = true + option.publishCustomAudioTrackId = Int(trackId) option.clientRoleType = GlobalSettings.shared.getUserRole() NetworkManager.shared.generateToken(channelName: channelName, success: { token in let result = self.agoraKit.joinChannel(byToken: token, channelId: channelName, uid: 0, mediaOptions: option) @@ -112,6 +117,7 @@ class CustomPcmAudioSourceMain: BaseViewController { // leave channel when exiting the view pcmSourcePush?.stop() if isJoined { + agoraKit.destroyCustomAudioTrack(Int(trackId)) agoraKit.disableAudio() pcmSourcePush?.stop() agoraKit.leaveChannel { (stats) -> Void in @@ -128,12 +134,20 @@ class CustomPcmAudioSourceMain: BaseViewController { } else { pcmSourcePush?.stop() } + let mediaOption = AgoraRtcChannelMediaOptions() + mediaOption.publishCustomAudioTrack = sender.isOn + agoraKit.updateChannel(with: mediaOption) } } extension CustomPcmAudioSourceMain: AgoraPcmSourcePushDelegate { func onAudioFrame(data: UnsafeMutablePointer) { - agoraKit.pushExternalAudioFrameRawData(data, samples: samples, sourceId: 0, timestamp: 0) + agoraKit.pushExternalAudioFrameRawData(data, + samples: samples, + sampleRate: Int(sampleRate), + channels: Int(channel), + trackId: Int(trackId), + timestamp: 0) } } From 65d399450605bf6d7fab87b1ff21df35f58816b6 Mon Sep 17 00:00:00 2001 From: zhaoyongqiang Date: Mon, 20 Mar 2023 15:33:14 +0800 Subject: [PATCH 012/154] Add Record Moudle --- .../APIExample.xcodeproj/project.pbxproj | 62 ++- .../JoinChannelVideoRecorder.storyboard | 102 +++++ .../JoinChannelVideoRecorder.swift | 381 ++++++++++++++++++ .../JoinChannelVideoRecorder.strings | 21 + .../APIExample/ViewController.swift | 3 +- .../zh-Hans.lproj/Localizable.strings | 2 + 6 files changed, 548 insertions(+), 23 deletions(-) create mode 100644 iOS/APIExample/APIExample/Examples/Basic/JoinChannelVideo(Recorder)/Base.lproj/JoinChannelVideoRecorder.storyboard create mode 100644 iOS/APIExample/APIExample/Examples/Basic/JoinChannelVideo(Recorder)/JoinChannelVideoRecorder.swift create mode 100644 iOS/APIExample/APIExample/Examples/Basic/JoinChannelVideo(Recorder)/zh-Hans.lproj/JoinChannelVideoRecorder.strings diff --git a/iOS/APIExample/APIExample.xcodeproj/project.pbxproj b/iOS/APIExample/APIExample.xcodeproj/project.pbxproj index 88454e0c9..ea37fccf4 100644 --- a/iOS/APIExample/APIExample.xcodeproj/project.pbxproj +++ b/iOS/APIExample/APIExample.xcodeproj/project.pbxproj @@ -83,7 +83,7 @@ 576CA80C25AA0FA90091520B /* AgoraPcmSourcePush.swift in Sources */ = {isa = PBXBuildFile; fileRef = 576CA80B25AA0FA90091520B /* AgoraPcmSourcePush.swift */; }; 576EA54225AC3310000B3D79 /* CustomPcmAudioSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 576EA54125AC3310000B3D79 /* CustomPcmAudioSource.swift */; }; 576EA54825AC3523000B3D79 /* CustomPcmAudioSource.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 576EA54A25AC3523000B3D79 /* CustomPcmAudioSource.storyboard */; }; - 576EA59025AEDD3C000B3D79 /* (null) in Sources */ = {isa = PBXBuildFile; }; + 576EA59025AEDD3C000B3D79 /* BuildFile in Sources */ = {isa = PBXBuildFile; }; 57FE7C4B26B2D103002D9043 /* CircularBuffer.c in Sources */ = {isa = PBXBuildFile; fileRef = 57FE7C4726B2D103002D9043 /* CircularBuffer.c */; }; 670936FD282DFE1600BC3954 /* ContentInspect.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 670936FF282DFE1600BC3954 /* ContentInspect.storyboard */; }; 6709B23B2806B0EA000BCC58 /* RawAudioData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6709B23A2806B0EA000BCC58 /* RawAudioData.swift */; }; @@ -97,7 +97,7 @@ 67B8C7B628057D1500195106 /* RawVideoData.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 67B8C7B828057D1500195106 /* RawVideoData.storyboard */; }; 67CB2F0C27EB318200CB19D2 /* SpatialAudio.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67CB2F0A27EB318100CB19D2 /* SpatialAudio.swift */; }; 67CB2F0D27EB318200CB19D2 /* SpatialAudio.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 67CB2F0B27EB318200CB19D2 /* SpatialAudio.storyboard */; }; - 8407E0942472320800AC5DE8 /* (null) in Sources */ = {isa = PBXBuildFile; }; + 8407E0942472320800AC5DE8 /* BuildFile in Sources */ = {isa = PBXBuildFile; }; 8B10BE1126AFFFA6002E1373 /* SimpleFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B10BE0F26AFFFA6002E1373 /* SimpleFilter.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8B1422C226B50AB500FFF91B /* AudioProcessor.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8B1422BC26B50AB400FFF91B /* AudioProcessor.mm */; }; 8B1422C326B50AB500FFF91B /* ExtensionAudioFilter.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 8B1422BD26B50AB400FFF91B /* ExtensionAudioFilter.hpp */; }; @@ -152,6 +152,9 @@ E728B85928B86B0700674A4A /* CustomVideoSourcePushMulti.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E728B85528B86B0700674A4A /* CustomVideoSourcePushMulti.storyboard */; }; E728B85A28B86B0700674A4A /* CustomVideoSourcePushMulti.swift in Sources */ = {isa = PBXBuildFile; fileRef = E728B85728B86B0700674A4A /* CustomVideoSourcePushMulti.swift */; }; E728B85C28B8971200674A4A /* sample.yuv in Resources */ = {isa = PBXBuildFile; fileRef = E728B85B28B8971200674A4A /* sample.yuv */; }; + E74788AC29C7FB6900CD7415 /* JoinChannelVideoRecorder.strings in Resources */ = {isa = PBXBuildFile; fileRef = E74788A729C7FB6800CD7415 /* JoinChannelVideoRecorder.strings */; }; + E74788AD29C7FB6900CD7415 /* JoinChannelVideoRecorder.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E74788A929C7FB6800CD7415 /* JoinChannelVideoRecorder.storyboard */; }; + E74788AE29C7FB6900CD7415 /* JoinChannelVideoRecorder.swift in Sources */ = {isa = PBXBuildFile; fileRef = E74788AB29C7FB6800CD7415 /* JoinChannelVideoRecorder.swift */; }; E74877B328A23B2F00CA2F58 /* SimpleFilter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B10BE0D26AFFFA6002E1373 /* SimpleFilter.framework */; }; E74877B728A23B8B00CA2F58 /* NetworkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E74877B628A23B8B00CA2F58 /* NetworkManager.swift */; }; E74877BA28A23C1400CA2F58 /* JSONObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = E74877B928A23C1400CA2F58 /* JSONObject.swift */; }; @@ -449,6 +452,9 @@ E728B85628B86B0700674A4A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/CustomVideoSourcePushMulti.storyboard; sourceTree = ""; }; E728B85728B86B0700674A4A /* CustomVideoSourcePushMulti.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomVideoSourcePushMulti.swift; sourceTree = ""; }; E728B85B28B8971200674A4A /* sample.yuv */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = sample.yuv; sourceTree = ""; }; + E74788A829C7FB6800CD7415 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/JoinChannelVideoRecorder.strings"; sourceTree = ""; }; + E74788AA29C7FB6800CD7415 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/JoinChannelVideoRecorder.storyboard; sourceTree = ""; }; + E74788AB29C7FB6800CD7415 /* JoinChannelVideoRecorder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JoinChannelVideoRecorder.swift; sourceTree = ""; }; E74877B628A23B8B00CA2F58 /* NetworkManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkManager.swift; sourceTree = ""; }; E74877B928A23C1400CA2F58 /* JSONObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSONObject.swift; sourceTree = ""; }; E74877C928A2611C00CA2F58 /* ToastView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ToastView.swift; sourceTree = ""; }; @@ -1045,6 +1051,7 @@ A75A56D324A0603000D0089E /* Basic */ = { isa = PBXGroup; children = ( + E74788A629C7FB6800CD7415 /* JoinChannelVideo(Recorder) */, E77D54C128F55E9100D51C1E /* JoinChannelVideo(Token) */, 0385768025224A88003C369A /* JoinChannelVideo */, 0371D8AC250B4A2C00C0DD61 /* JoinChannelAudio */, @@ -1153,6 +1160,16 @@ path = APIExample/Examples/Advanced/CustomVideoSourcePushMulti; sourceTree = SOURCE_ROOT; }; + E74788A629C7FB6800CD7415 /* JoinChannelVideo(Recorder) */ = { + isa = PBXGroup; + children = ( + E74788A729C7FB6800CD7415 /* JoinChannelVideoRecorder.strings */, + E74788A929C7FB6800CD7415 /* JoinChannelVideoRecorder.storyboard */, + E74788AB29C7FB6800CD7415 /* JoinChannelVideoRecorder.swift */, + ); + path = "JoinChannelVideo(Recorder)"; + sourceTree = ""; + }; E74877B528A23B8B00CA2F58 /* NetworkManager */ = { isa = PBXGroup; children = ( @@ -1400,7 +1417,6 @@ 03D13BCA2448758900B599B3 /* Resources */, 1B6F6CF9B678035E221EAFDE /* [CP] Embed Pods Frameworks */, 0339BEBA25205B80007D4FDD /* Embed App Extensions */, - 2D7A60E3746CC26A94583758 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -1492,6 +1508,7 @@ 033A9F8E252D8FF300BC26E1 /* JoinMultiChannel.storyboard in Resources */, 03BEED0B251C4446005E78F4 /* audiomixing.mp3 in Resources */, 8BC751D6273E502700552265 /* LiveStreaming.storyboard in Resources */, + E74788AD29C7FB6900CD7415 /* JoinChannelVideoRecorder.storyboard in Resources */, 03B12DAC251127DC00E55818 /* VideoViewMetal.xib in Resources */, E77D54C828F55E9100D51C1E /* JoinChannelVideoToken.storyboard in Resources */, E7A49D0A29067F8300F06DD4 /* SenseBeautify.storyboard in Resources */, @@ -1518,6 +1535,7 @@ 03BEED0D251CAB9C005E78F4 /* audioeffect.mp3 in Resources */, A7CA48C424553CF700507435 /* Popover.storyboard in Resources */, E7A49D42290907E200F06DD4 /* BytedEffect.storyboard in Resources */, + E74788AC29C7FB6900CD7415 /* JoinChannelVideoRecorder.strings in Resources */, E7A49CFC29029E0000F06DD4 /* FUBeautify.strings in Resources */, 03D13BDC2448758B00B599B3 /* LaunchScreen.storyboard in Resources */, E728B85C28B8971200674A4A /* sample.yuv in Resources */, @@ -1634,23 +1652,6 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 2D7A60E3746CC26A94583758 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-APIExample/Pods-APIExample-resources-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Copy Pods Resources"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-APIExample/Pods-APIExample-resources-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-APIExample/Pods-APIExample-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; 2E9C38CD8AB71CBDCF3A8D8D /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -1720,18 +1721,19 @@ 8B333DA9267B4BC3002A3785 /* SettingsCells.swift in Sources */, E7A49D4829090F8000F06DD4 /* BEFrameProcessor.mm in Sources */, 033A9EFC252D61E200BC26E1 /* CustomVideoRender.swift in Sources */, - 576EA59025AEDD3C000B3D79 /* (null) in Sources */, + 576EA59025AEDD3C000B3D79 /* BuildFile in Sources */, 033A9F09252D61FC00BC26E1 /* RTMPStreaming.swift in Sources */, 6709B23B2806B0EA000BCC58 /* RawAudioData.swift in Sources */, 033A9EEA252D5F5E00BC26E1 /* JoinMultiChannel.swift in Sources */, 0339BE64251DCA3B007D4FDD /* GlobalSettings.swift in Sources */, E728B85A28B86B0700674A4A /* CustomVideoSourcePushMulti.swift in Sources */, E728B84C28B6015800674A4A /* AgoraPictureInPictureController.m in Sources */, - 8407E0942472320800AC5DE8 /* (null) in Sources */, + 8407E0942472320800AC5DE8 /* BuildFile in Sources */, 8B5E5B50274CB68E0040E97D /* RhythmPlayer.swift in Sources */, E7A49D342907E74A00F06DD4 /* BundleUtil.m in Sources */, 036C42B524D2A3C600A59000 /* AgoraMetalRender.swift in Sources */, E72055EA28F943520030E6D1 /* Util.swift in Sources */, + E74788AE29C7FB6900CD7415 /* JoinChannelVideoRecorder.swift in Sources */, E7A49D41290907E200F06DD4 /* BytedEffectVC.m in Sources */, E7A49D142907DC2800F06DD4 /* EffectsProcess.m in Sources */, 03DF1D9324CFC29700DF7151 /* ExternalAudio.mm in Sources */, @@ -2179,6 +2181,22 @@ name = CustomVideoSourcePushMulti.storyboard; sourceTree = ""; }; + E74788A729C7FB6800CD7415 /* JoinChannelVideoRecorder.strings */ = { + isa = PBXVariantGroup; + children = ( + E74788A829C7FB6800CD7415 /* zh-Hans */, + ); + name = JoinChannelVideoRecorder.strings; + sourceTree = ""; + }; + E74788A929C7FB6800CD7415 /* JoinChannelVideoRecorder.storyboard */ = { + isa = PBXVariantGroup; + children = ( + E74788AA29C7FB6800CD7415 /* Base */, + ); + name = JoinChannelVideoRecorder.storyboard; + sourceTree = ""; + }; E77D54C228F55E9100D51C1E /* JoinChannelVideoToken.strings */ = { isa = PBXVariantGroup; children = ( diff --git a/iOS/APIExample/APIExample/Examples/Basic/JoinChannelVideo(Recorder)/Base.lproj/JoinChannelVideoRecorder.storyboard b/iOS/APIExample/APIExample/Examples/Basic/JoinChannelVideo(Recorder)/Base.lproj/JoinChannelVideoRecorder.storyboard new file mode 100644 index 000000000..ad4c20098 --- /dev/null +++ b/iOS/APIExample/APIExample/Examples/Basic/JoinChannelVideo(Recorder)/Base.lproj/JoinChannelVideoRecorder.storyboard @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iOS/APIExample/APIExample/Examples/Basic/JoinChannelVideo(Recorder)/JoinChannelVideoRecorder.swift b/iOS/APIExample/APIExample/Examples/Basic/JoinChannelVideo(Recorder)/JoinChannelVideoRecorder.swift new file mode 100644 index 000000000..67cb92e98 --- /dev/null +++ b/iOS/APIExample/APIExample/Examples/Basic/JoinChannelVideo(Recorder)/JoinChannelVideoRecorder.swift @@ -0,0 +1,381 @@ +// +// JoinChannelVideo.swift +// APIExample +// +// Created by 张乾泽 on 2020/4/17. +// Copyright © 2020 Agora Corp. All rights reserved. +// +import UIKit +import AGEVideoLayout +import AgoraRtcKit + +class JoinChannelVideoRecorderEntry : UIViewController +{ + @IBOutlet weak var joinButton: UIButton! + @IBOutlet weak var channelTextField: UITextField! + let identifier = "JoinChannelVideoRecorder" + @IBOutlet var resolutionBtn: UIButton! + @IBOutlet var fpsBtn: UIButton! + @IBOutlet var orientationBtn: UIButton! + var width:Int = 960, height:Int = 540, orientation:AgoraVideoOutputOrientationMode = .adaptative, fps = 15 + + + override func viewDidLoad() { + super.viewDidLoad() + } + + + func getResolutionAction(width:Int, height:Int) -> UIAlertAction{ + return UIAlertAction(title: "\(width)x\(height)", style: .default, handler: {[unowned self] action in + self.width = width + self.height = height + self.resolutionBtn.setTitle("\(width)x\(height)", for: .normal) + }) + } + + func getFpsAction(_ fps:Int) -> UIAlertAction{ + return UIAlertAction(title: "\(fps)fps", style: .default, handler: {[unowned self] action in + self.fps = fps + self.fpsBtn.setTitle("\(fps)fps", for: .normal) + }) + } + + func getOrientationAction(_ orientation:AgoraVideoOutputOrientationMode) -> UIAlertAction{ + return UIAlertAction(title: "\(orientation.description())", style: .default, handler: {[unowned self] action in + self.orientation = orientation + self.orientationBtn.setTitle("\(orientation.description())", for: .normal) + }) + } + + @IBAction func setResolution(){ + let alert = UIAlertController(title: "Set Resolution".localized, message: nil, preferredStyle: UIDevice.current.userInterfaceIdiom == .pad ? UIAlertController.Style.alert : UIAlertController.Style.actionSheet) + alert.addAction(getResolutionAction(width: 90, height: 90)) + alert.addAction(getResolutionAction(width: 160, height: 120)) + alert.addAction(getResolutionAction(width: 320, height: 240)) + alert.addAction(getResolutionAction(width: 960, height: 540)) + alert.addAction(getResolutionAction(width: 1280, height: 720)) + alert.addCancelAction() + present(alert, animated: true, completion: nil) + } + + @IBAction func setFps(){ + let alert = UIAlertController(title: "Set Fps".localized, message: nil, preferredStyle: UIDevice.current.userInterfaceIdiom == .pad ? UIAlertController.Style.alert : UIAlertController.Style.actionSheet) + alert.addAction(getFpsAction(10)) + alert.addAction(getFpsAction(15)) + alert.addAction(getFpsAction(24)) + alert.addAction(getFpsAction(30)) + alert.addAction(getFpsAction(60)) + alert.addCancelAction() + present(alert, animated: true, completion: nil) + } + + @IBAction func setOrientation(){ + let alert = UIAlertController(title: "Set Orientation".localized, message: nil, preferredStyle: UIDevice.current.userInterfaceIdiom == .pad ? UIAlertController.Style.alert : UIAlertController.Style.actionSheet) + alert.addAction(getOrientationAction(.adaptative)) + alert.addAction(getOrientationAction(.fixedLandscape)) + alert.addAction(getOrientationAction(.fixedPortrait)) + alert.addCancelAction() + present(alert, animated: true, completion: nil) + } + + @IBAction func doJoinPressed(sender: UIButton) { + guard let channelName = channelTextField.text else {return} + //resign channel text field + channelTextField.resignFirstResponder() + + let storyBoard: UIStoryboard = UIStoryboard(name: identifier, bundle: nil) + // create new view controller every time to ensure we get a clean vc + guard let newViewController = storyBoard.instantiateViewController(withIdentifier: identifier) as? BaseViewController else {return} + newViewController.title = channelName + newViewController.configs = ["channelName":channelName, "resolution":CGSize(width: width, height: height), "fps": fps, "orientation": orientation] + navigationController?.pushViewController(newViewController, animated: true) + } +} + +class JoinChannelVideoRecorder: BaseViewController { + private lazy var localRecordButton: UIButton = { + let button = UIButton() + button.translatesAutoresizingMaskIntoConstraints = false + button.setTitle("Recording".localized, for: .normal) + button.setTitle("Stop Recording".localized, for: .selected) + button.setTitleColor(.red, for: .normal) + button.setTitleColor(.red, for: .selected) + button.titleLabel?.font = .systemFont(ofSize: 14) + button.borderColor = UIColor.red.cgColor + button.borderWidth = 1 + button.cornerRadius = 3 + button.addTarget(self, action: #selector(onTapLocalRecordButton(sender:)), for: .touchUpInside) + return button + }() + private lazy var remoteRecordButton: UIButton = { + let button = UIButton() + button.translatesAutoresizingMaskIntoConstraints = false + button.setTitle("Recording".localized, for: .normal) + button.setTitle("Stop Recording".localized, for: .selected) + button.setTitleColor(.red, for: .normal) + button.setTitleColor(.red, for: .selected) + button.titleLabel?.font = .systemFont(ofSize: 14) + button.borderColor = UIColor.red.cgColor + button.borderWidth = 1 + button.cornerRadius = 3 + button.isHidden = true + button.addTarget(self, action: #selector(onTapRemoteRecordButton(sender:)), for: .touchUpInside) + return button + }() + private var storagePath: String { + let documentPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first + return documentPath ?? "" + } + private lazy var localUid: UInt = UInt.random(in: 1...99999) + private var remoteUid: UInt = 0 { + didSet { + remoteRecordButton.isHidden = remoteUid == 0 + } + } + private lazy var localRecord: AgoraMediaRecorder = { + let connection = AgoraRtcConnection(channelId: title ?? "", localUid: Int(localUid)) + let record = agoraKit.createLocalMediaRecorder(connection) + record?.setMediaRecorderDelegate(self) + return record! + }() + private lazy var remoteRecord: AgoraMediaRecorder = { + let record = agoraKit.createRemoteMediaRecorder(title ?? "", uid: remoteUid) + record?.setMediaRecorderDelegate(self) + return record! + }() + var localVideo = Bundle.loadVideoView(type: .local, audioOnly: false) + var remoteVideo = Bundle.loadVideoView(type: .remote, audioOnly: false) + + @IBOutlet weak var container: AGEVideoContainer! + var agoraKit: AgoraRtcEngineKit! + + // indicate if current instance has joined channel + var isJoined: Bool = false + + override func viewDidLoad() { + super.viewDidLoad() + // layout render view + localVideo.setPlaceholder(text: "Local Host".localized) + remoteVideo.setPlaceholder(text: "Remote Host".localized) + container.layoutStream(views: [localVideo, remoteVideo]) + + localVideo.addSubview(localRecordButton) + remoteVideo.addSubview(remoteRecordButton) + localRecordButton.bottomAnchor.constraint(equalTo: localVideo.bottomAnchor, constant: -10).isActive = true + localRecordButton.trailingAnchor.constraint(equalTo: localVideo.trailingAnchor, constant: -10).isActive = true + localRecordButton.widthAnchor.constraint(equalToConstant: 70).isActive = true + localRecordButton.heightAnchor.constraint(equalToConstant: 30).isActive = true + + remoteRecordButton.bottomAnchor.constraint(equalTo: remoteVideo.bottomAnchor, constant: -10).isActive = true + remoteRecordButton.trailingAnchor.constraint(equalTo: remoteVideo.trailingAnchor, constant: -10).isActive = true + remoteRecordButton.widthAnchor.constraint(equalToConstant: 70).isActive = true + remoteRecordButton.heightAnchor.constraint(equalToConstant: 30).isActive = true + + // set up agora instance when view loaded + let config = AgoraRtcEngineConfig() + config.appId = KeyCenter.AppId + config.areaCode = GlobalSettings.shared.area + config.channelProfile = .liveBroadcasting + agoraKit = AgoraRtcEngineKit.sharedEngine(with: config, delegate: self) + // Configuring Privatization Parameters + Util.configPrivatization(agoraKit: agoraKit) + + agoraKit.setLogFile(LogUtils.sdkLogPath()) + + // get channel name from configs + guard let channelName = configs["channelName"] as? String, + let resolution = configs["resolution"] as? CGSize, + let fps = configs["fps"] as? Int, + let orientation = configs["orientation"] as? AgoraVideoOutputOrientationMode else {return} + + // make myself a broadcaster + agoraKit.setClientRole(GlobalSettings.shared.getUserRole()) + // enable video module and set up video encoding configs + agoraKit.enableVideo() + agoraKit.enableAudio() + agoraKit.setVideoEncoderConfiguration(AgoraVideoEncoderConfiguration(size: resolution, + frameRate: AgoraVideoFrameRate(rawValue: fps) ?? .fps15, + bitrate: AgoraVideoBitrateStandard, + orientationMode: orientation, mirrorMode: .auto)) + + // set up local video to render your local camera preview + let videoCanvas = AgoraRtcVideoCanvas() + videoCanvas.uid = 0 + // the view to be binded + videoCanvas.view = localVideo.videoView + videoCanvas.renderMode = .hidden + agoraKit.setupLocalVideo(videoCanvas) + // you have to call startPreview to see local video + agoraKit.startPreview() + + // Set audio route to speaker + agoraKit.setDefaultAudioRouteToSpeakerphone(true) + + // start joining channel + // 1. Users can only see each other after they join the + // same channel successfully using the same app id. + // 2. If app certificate is turned on at dashboard, token is needed + // when joining channel. The channel name and uid used to calculate + // the token has to match the ones used for channel join + let option = AgoraRtcChannelMediaOptions() + option.publishCameraTrack = true + option.publishMicrophoneTrack = true + option.clientRoleType = GlobalSettings.shared.getUserRole() + NetworkManager.shared.generateToken(channelName: channelName, uid: localUid, success: { token in + let result = self.agoraKit.joinChannel(byToken: token, + channelId: channelName, + uid: self.localUid, + mediaOptions: option) + if result != 0 { + // Usually happens with invalid parameters + // Error code description can be found at: + // en: https://api-ref.agora.io/en/voice-sdk/macos/3.x/Constants/AgoraErrorCode.html#content + // cn: https://docs.agora.io/cn/Voice/API%20Reference/oc/Constants/AgoraErrorCode.html + self.showAlert(title: "Error", message: "joinChannel call failed: \(result), please check your params") + } + }) + } + + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + agoraKit.disableAudio() + agoraKit.disableVideo() + if isJoined { + agoraKit.stopPreview() + agoraKit.leaveChannel { (stats) -> Void in + LogUtils.log(message: "left channel, duration: \(stats.duration)", level: .info) + } + } + } + + @objc + private func onTapLocalRecordButton(sender: UIButton) { + sender.isSelected = !sender.isSelected + let path = storagePath + "/\(localUid).mp4" + if sender.isSelected { + let config = AgoraMediaRecorderConfiguration() + config.storagePath = path + config.containerFormat = .MP4 + config.maxDurationMs = 10 * 1000 + localRecord.startRecording(config) + } else { + agoraKit.destroy(localRecord) + ToastView.show(text: path) + } + } + @objc + private func onTapRemoteRecordButton(sender: UIButton) { + sender.isSelected = !sender.isSelected + let path = storagePath + "/\(remoteUid).mp4" + if sender.isSelected { + let config = AgoraMediaRecorderConfiguration() + config.storagePath = path + config.containerFormat = .MP4 + config.maxDurationMs = 10 * 1000 + remoteRecord.startRecording(config) + } else { + agoraKit.destroy(remoteRecord) + ToastView.show(text: path) + } + } +} + +extension JoinChannelVideoRecorder: AgoraMediaRecorderDelegate { + func mediaRecorder(_ recorder: AgoraMediaRecorder, stateDidChanged channelId: String, uid: UInt, state: AgoraMediaRecorderState, error: AgoraMediaRecorderErrorCode) { + LogUtils.log(message: "uid == \(uid) state == \(state.rawValue)", level: .info) + } + + func mediaRecorder(_ recorder: AgoraMediaRecorder, informationDidUpdated channelId: String, uid: UInt, info: AgoraMediaRecorderInfo) { + LogUtils.log(message: "uid == \(uid) info == \(info.recorderFileName)", level: .info) + } +} + +/// agora rtc engine delegate events +extension JoinChannelVideoRecorder: AgoraRtcEngineDelegate { + /// callback when warning occured for agora sdk, warning can usually be ignored, still it's nice to check out + /// what is happening + /// Warning code description can be found at: + /// en: https://api-ref.agora.io/en/voice-sdk/ios/3.x/Constants/AgoraWarningCode.html + /// cn: https://docs.agora.io/cn/Voice/API%20Reference/oc/Constants/AgoraWarningCode.html + /// @param warningCode warning code of the problem + func rtcEngine(_ engine: AgoraRtcEngineKit, didOccurWarning warningCode: AgoraWarningCode) { + LogUtils.log(message: "warning: \(warningCode.description)", level: .warning) + } + + /// callback when error occured for agora sdk, you are recommended to display the error descriptions on demand + /// to let user know something wrong is happening + /// Error code description can be found at: + /// en: https://api-ref.agora.io/en/voice-sdk/macos/3.x/Constants/AgoraErrorCode.html#content + /// cn: https://docs.agora.io/cn/Voice/API%20Reference/oc/Constants/AgoraErrorCode.html + /// @param errorCode error code of the problem + func rtcEngine(_ engine: AgoraRtcEngineKit, didOccurError errorCode: AgoraErrorCode) { + LogUtils.log(message: "error: \(errorCode)", level: .error) + self.showAlert(title: "Error", message: "Error \(errorCode.description) occur") + } + + func rtcEngine(_ engine: AgoraRtcEngineKit, didJoinChannel channel: String, withUid uid: UInt, elapsed: Int) { + self.isJoined = true + LogUtils.log(message: "Join \(channel) with uid \(uid) elapsed \(elapsed)ms", level: .info) + } + + /// callback when a remote user is joinning the channel, note audience in live broadcast mode will NOT trigger this event + /// @param uid uid of remote joined user + /// @param elapsed time elapse since current sdk instance join the channel in ms + func rtcEngine(_ engine: AgoraRtcEngineKit, didJoinedOfUid uid: UInt, elapsed: Int) { + LogUtils.log(message: "remote user join: \(uid) \(elapsed)ms", level: .info) + + // Only one remote video view is available for this + // tutorial. Here we check if there exists a surface + // view tagged as this uid. + let videoCanvas = AgoraRtcVideoCanvas() + videoCanvas.uid = uid + // the view to be binded + videoCanvas.view = remoteVideo.videoView + videoCanvas.renderMode = .hidden + agoraKit.setupRemoteVideo(videoCanvas) + remoteUid = uid + } + + /// callback when a remote user is leaving the channel, note audience in live broadcast mode will NOT trigger this event + /// @param uid uid of remote joined user + /// @param reason reason why this user left, note this event may be triggered when the remote user + /// become an audience in live broadcasting profile + func rtcEngine(_ engine: AgoraRtcEngineKit, didOfflineOfUid uid: UInt, reason: AgoraUserOfflineReason) { + LogUtils.log(message: "remote user left: \(uid) reason \(reason)", level: .info) + + // to unlink your view from sdk, so that your view reference will be released + // note the video will stay at its last frame, to completely remove it + // you will need to remove the EAGL sublayer from your binded view + let videoCanvas = AgoraRtcVideoCanvas() + videoCanvas.uid = uid + // the view to be binded + videoCanvas.view = nil + videoCanvas.renderMode = .hidden + agoraKit.setupRemoteVideo(videoCanvas) + remoteUid = 0 + } + + /// Reports the statistics of the current call. The SDK triggers this callback once every two seconds after the user joins the channel. + /// @param stats stats struct + func rtcEngine(_ engine: AgoraRtcEngineKit, reportRtcStats stats: AgoraChannelStats) { + localVideo.statsInfo?.updateChannelStats(stats) + } + + /// Reports the statistics of the uploading local audio streams once every two seconds. + /// @param stats stats struct + func rtcEngine(_ engine: AgoraRtcEngineKit, localAudioStats stats: AgoraRtcLocalAudioStats) { + localVideo.statsInfo?.updateLocalAudioStats(stats) + } + + /// Reports the statistics of the video stream from each remote user/host. + /// @param stats stats struct + func rtcEngine(_ engine: AgoraRtcEngineKit, remoteVideoStats stats: AgoraRtcRemoteVideoStats) { + remoteVideo.statsInfo?.updateVideoStats(stats) + } + + /// Reports the statistics of the audio stream from each remote user/host. + /// @param stats stats struct for current call statistics + func rtcEngine(_ engine: AgoraRtcEngineKit, remoteAudioStats stats: AgoraRtcRemoteAudioStats) { + remoteVideo.statsInfo?.updateAudioStats(stats) + } +} diff --git a/iOS/APIExample/APIExample/Examples/Basic/JoinChannelVideo(Recorder)/zh-Hans.lproj/JoinChannelVideoRecorder.strings b/iOS/APIExample/APIExample/Examples/Basic/JoinChannelVideo(Recorder)/zh-Hans.lproj/JoinChannelVideoRecorder.strings new file mode 100644 index 000000000..25a97ee8c --- /dev/null +++ b/iOS/APIExample/APIExample/Examples/Basic/JoinChannelVideo(Recorder)/zh-Hans.lproj/JoinChannelVideoRecorder.strings @@ -0,0 +1,21 @@ + +/* Class = "UITextField"; placeholder = "Enter channel name"; ObjectID = "GWc-L5-fZV"; */ +"GWc-L5-fZV.placeholder" = "输入频道名"; + +/* Class = "UINavigationItem"; title = "Join Channel"; ObjectID = "Iy0-Dq-h5x"; */ +"Iy0-Dq-h5x.title" = "加入频道"; + +/* Class = "UIButton"; normalTitle = "Button"; ObjectID = "VpM-9W-auG"; */ +"VpM-9W-auG.normalTitle" = "Button"; + +/* Class = "UIButton"; normalTitle = "Join"; ObjectID = "kbN-ZR-nNn"; */ +"kbN-ZR-nNn.normalTitle" = "加入频道"; + +/* Class = "UIButton"; normalTitle = "Button"; ObjectID = "kf0-3f-UI5"; */ +"kf0-3f-UI5.normalTitle" = "Button"; + +/* Class = "UIViewController"; title = "Join Channel Video"; ObjectID = "p70-sh-D1h"; */ +"p70-sh-D1h.title" = "视频实时通话"; + +/* Class = "UIButton"; normalTitle = "Button"; ObjectID = "wHl-zh-dFe"; */ +"wHl-zh-dFe.normalTitle" = "Button"; diff --git a/iOS/APIExample/APIExample/ViewController.swift b/iOS/APIExample/APIExample/ViewController.swift index 86011f514..d64023016 100644 --- a/iOS/APIExample/APIExample/ViewController.swift +++ b/iOS/APIExample/APIExample/ViewController.swift @@ -27,7 +27,8 @@ class ViewController: AGViewController { MenuSection(name: "Basic", rows: [ MenuItem(name: "Join a channel (Token)".localized, storyboard: "JoinChannelVideoToken", controller: ""), MenuItem(name: "Join a channel (Video)".localized, storyboard: "JoinChannelVideo", controller: ""), - MenuItem(name: "Join a channel (Audio)".localized, storyboard: "JoinChannelAudio", controller: "") + MenuItem(name: "Join a channel (Audio)".localized, storyboard: "JoinChannelAudio", controller: ""), + MenuItem(name: "Join a channel (Recorder)".localized, storyboard: "JoinChannelVideoRecorder", controller: "") ]), MenuSection(name: "Anvanced", rows: [ // MenuItem(name: "Group Video Chat".localized, storyboard: "VideoChat", controller: "VideoChat"), diff --git a/iOS/APIExample/APIExample/zh-Hans.lproj/Localizable.strings b/iOS/APIExample/APIExample/zh-Hans.lproj/Localizable.strings index 2961855f6..1ecfbedea 100644 --- a/iOS/APIExample/APIExample/zh-Hans.lproj/Localizable.strings +++ b/iOS/APIExample/APIExample/zh-Hans.lproj/Localizable.strings @@ -153,3 +153,5 @@ "This app requires world tracking, which is available only on iOS devices with the A9 processor or later." = "AR功能仅在内置A9处理器后的iOS机型支持"; "Move Camera to find a planar\n(Shown as Red Rectangle)" = "移动相机以找到一个平面\n(以红色方块显示)"; "Tap to place remote video canvas" = "点击屏幕以放置视频画布"; +"Recording" = "开始录制"; +"Stop Recording" = "停止录制"; From 5ceed7197a90cc57b4472ea87a5cfafddb738dbc Mon Sep 17 00:00:00 2001 From: zhaoyongqiang Date: Mon, 20 Mar 2023 16:09:52 +0800 Subject: [PATCH 013/154] add voice formant adjust --- .../Base.lproj/VoiceChanger.storyboard | 77 +++++++++++-------- .../Advanced/VoiceChanger/VoiceChanger.swift | 4 + 2 files changed, 51 insertions(+), 30 deletions(-) diff --git a/iOS/APIExample/APIExample/Examples/Advanced/VoiceChanger/Base.lproj/VoiceChanger.storyboard b/iOS/APIExample/APIExample/Examples/Advanced/VoiceChanger/Base.lproj/VoiceChanger.storyboard index 8f241f996..b50768cc2 100644 --- a/iOS/APIExample/APIExample/Examples/Advanced/VoiceChanger/Base.lproj/VoiceChanger.storyboard +++ b/iOS/APIExample/APIExample/Examples/Advanced/VoiceChanger/Base.lproj/VoiceChanger.storyboard @@ -1,9 +1,9 @@ - + - + @@ -18,7 +18,7 @@ - + @@ -70,17 +70,17 @@ - + - + - + - +