diff --git a/.github/ci/build/build_android.groovy b/.github/ci/build/build_android.groovy index d9cfdc887..fc7fc2103 100644 --- a/.github/ci/build/build_android.groovy +++ b/.github/ci/build/build_android.groovy @@ -47,10 +47,21 @@ def doPublish(buildVariables) { "archivePattern": "*.zip", "serverPath": "ApiExample/${shortVersion}/${buildVariables.buildDate}/${env.platform}", "serverRepo": "SDK_repo" + ], + [ + "type": "ARTIFACTORY", + "archivePattern": "*.apk", + "serverPath": "ApiExample/${shortVersion}/${buildVariables.buildDate}/${env.platform}", + "serverRepo": "SDK_repo" ] ] - archive.archiveFiles(archiveInfos) - sh "rm -rf *.zip || true" + archiveUrls = archive.archiveFiles(archiveInfos) ?: [] + archiveUrls = archiveUrls as Set + if (archiveUrls) { + def content = archiveUrls.join("\n") + writeFile(file: 'package_urls', text: content, encoding: "utf-8") + } + sh "rm -rf *.zip *.apk || true" } pipelineLoad(this, "ApiExample", "build", "android", "apiexample_linux") diff --git a/.github/ci/build/build_android.sh b/.github/ci/build/build_android.sh index 053f65542..c803dca02 100644 --- a/.github/ci/build/build_android.sh +++ b/.github/ci/build/build_android.sh @@ -50,8 +50,6 @@ echo short_version: $short_version echo pwd: `pwd` echo sdk_url: $sdk_url -ls ~/.gradle || (mkdir -p /tmp/.gradle && ln -s /tmp/.gradle ~/.gradle && touch ~/.gradle/ln_$(date "+%y%m%d%H") && ls ~/.gradle) - zip_name=${sdk_url##*/} echo zip_name: $zip_name @@ -67,55 +65,21 @@ rm -rf ./$unzip_name/rtc/demo rm ./$unzip_name/rtc/commits rm ./$unzip_name/rtc/package_size_report.txt mkdir ./$unzip_name/rtc/samples -mkdir ./$unzip_name/rtc/samples/API-example - -if [ ! -z "$(echo $sdk_url | grep 'audio')" ] || [ ! -z "$(echo $sdk_url | grep 'VOICE')" ] -then -audio_suffix=-Audio -else -audio_suffix= -fi -echo audio_suffix: $audio_suffix -cp -rf ./Android/APIExample${audio_suffix}/** ./$unzip_name/rtc/samples/API-example +cp -rf ./Android/${android_direction} ./$unzip_name/rtc/samples/API-Example || exit 1 7za a -tzip result.zip -r $unzip_name > log.txt -mv result.zip $WORKSPACE/withAPIExample_$(date "+%d%H%M")_$zip_name +mv result.zip $WORKSPACE/withAPIExample_${BUILD_NUMBER}_$zip_name -# install android sdk -which java -java --version -source ~/.bashrc -export ANDROID_HOME=/usr/lib/android_sdk -echo ANDROID_HOME: $ANDROID_HOME -# compile apk -cd ./$unzip_name/rtc/samples/API-example -pwd - -## config appId -sed -i -e "s#YOUR APP ID#${APP_ID}#g" app/src/main/res/values/string_configs.xml -sed -i -e "s#YOUR APP CERTIFICATE##g" app/src/main/res/values/string_configs.xml -sed -i -e "s#YOUR ACCESS TOKEN##g" app/src/main/res/values/string_configs.xml -rm -f app/src/main/res/values/string_configs.xml-e -cat app/src/main/res/values/string_configs.xml - -## config simple filter -sed -i -e "s#simpleFilter = false#simpleFilter = true#g" gradle.properties -mkdir -p agora-simple-filter/src/main/agoraLibs -cp -r ../../sdk/arm64-v8a agora-simple-filter/src/main/agoraLibs/ -cp -r ../../sdk/armeabi-v7a agora-simple-filter/src/main/agoraLibs/ -curl -o opencv4.zip https://agora-adc-artifacts.s3.cn-north-1.amazonaws.com.cn/androidLibs/opencv4.zip -unzip opencv4.zip -mkdir -p agora-simple-filter/src/main/libs -mv arm64-v8a agora-simple-filter/src/main/libs -mv armeabi-v7a agora-simple-filter/src/main/libs -sed -i -e "s#jniLibs/#libs/#g" agora-simple-filter/src/main/cpp/CMakeLists.txt +if [ $compile_project = true ]; then + # install android sdk + which java + java --version + source ~/.bashrc + export ANDROID_HOME=/usr/lib/android_sdk + echo ANDROID_HOME: $ANDROID_HOME + cd ./$unzip_name/rtc/samples/API-Example || exit 1 + ./cloud_build.sh || exit 1 +fi -./gradlew clean || exit 1 -./gradlew :app:assembleDebug || exit 1 -cp app/build/outputs/apk/debug/app-debug.apk ./APIExample_Android_$(date "+%y%m%d%H").apk -7za a -tzip result.zip -r *.apk > log.txt -mv result.zip $WORKSPACE/APIExample_Android${audio_suffix}_$(date "+%y%m%d%H%M")_apk.zip -ls $WORKSPACE -cd - diff --git a/.github/ci/build/build_ios.groovy b/.github/ci/build/build_ios.groovy index b1faf2479..b5bb5f63f 100644 --- a/.github/ci/build/build_ios.groovy +++ b/.github/ci/build/build_ios.groovy @@ -44,10 +44,16 @@ def doPublish(buildVariables) { "archivePattern": "*.zip", "serverPath": "ApiExample/${shortVersion}/${buildVariables.buildDate}/${env.platform}", "serverRepo": "SDK_repo" // ATTENTIONS: Update the artifactoryRepo if needed. + ], + [ + "type": "ARTIFACTORY", + "archivePattern": "*.ipa", + "serverPath": "ApiExample/${shortVersion}/${buildVariables.buildDate}/${env.platform}", + "serverRepo": "SDK_repo" // ATTENTIONS: Update the artifactoryRepo if needed. ] ] archive.archiveFiles(archiveInfos) - sh "rm -rf *.zip || true" + sh "rm -rf *.zip *.ipa || true" } pipelineLoad(this, "ApiExample", "build", "ios", "apiexample_mac") \ No newline at end of file diff --git a/.github/ci/build/build_ios.sh b/.github/ci/build/build_ios.sh index e39a1f792..7a60cc7ae 100644 --- a/.github/ci/build/build_ios.sh +++ b/.github/ci/build/build_ios.sh @@ -39,7 +39,7 @@ # others: Rename the zip package name yourself, But need copy it to workspace dir ################################## -echo is_generate_validate_app: $is_generate_validate_app +echo ios_direction: $ios_direction echo Package_Publish: $Package_Publish echo is_tag_fetch: $is_tag_fetch echo arch: $arch @@ -55,75 +55,29 @@ echo sdk_url: $sdk_url zip_name=${sdk_url##*/} echo zip_name: $zip_name -python3 $WORKSPACE/artifactory_utils.py --action=download_file --file=$sdk_url -7za x ./$zip_name -y +curl -o $zip_name $sdk_url || exit 1 +7za x ./$zip_name -y > log.txt -unzip_name=`ls -S -d */ | grep Agora` +unzip_name=`ls -S -d */ | grep Agora | sed 's/\///g'` echo unzip_name: $unzip_name rm -rf ./$unzip_name/bin rm ./$unzip_name/commits rm ./$unzip_name/package_size_report.txt mkdir ./$unzip_name/samples -mkdir ./$unzip_name/samples/API-Example -if [ $? -eq 0 ]; then - echo "success" -else - echo "failed" - exit 1 -fi -cp -rf ./iOS/** ./$unzip_name/samples/API-Example +cp -rf ./iOS/${ios_direction} ./$unzip_name/samples/API-Example || exit 1 +ls -al ./$unzip_name/samples/API-Example/ +mv ./$unzip_name/samples/API-Example/sdk.podspec ./$unzip_name/ || exit 1 +python3 ./.github/ci/build/modify_podfile.py ./$unzip_name/samples/API-Example/Podfile || exit 1 + -result=$(echo $sdk_url | grep "VOICE") -if [ ! -z "$result" ] -then - echo "包含" - rm -rf ./$unzip_name/samples/API-Example/APIExample - rm -rf ./$unzip_name/samples/API-Example/APIExample-OC - mv ./$unzip_name/samples/API-Example/APIExample-Audio ./$unzip_name/samples/APIExample-Audio - mv ./$unzip_name/samples/APIExample-Audio/sdk.podspec ./$unzip_name/ - python3 ./.github/ci/build/modify_podfile.py ./$unzip_name/samples/APIExample-Audio/Podfile - if [ $? -eq 0 ]; then - echo "success" - else - echo "failed" - exit 1 - fi - if [ $is_generate_validate_app = true ]; then - ./.github/ci/build/build_ios_ipa.sh ./$unzip_name/samples/APIExample-Audio - fi +7za a -tzip result.zip -r $unzip_name > log.txt +mv result.zip $WORKSPACE/withAPIExample_${BUILD_NUMBER}_$zip_name -else - echo "不包含" - rm -rf ./$unzip_name/samples/API-Example/APIExample-Audio - if [ $is_objective_c = true ]; then - rm -rf ./$unzip_name/samples/API-Example/APIExample - mv ./$unzip_name/samples/API-Example/APIExample-OC ./$unzip_name/samples/APIExample-OC - mv ./$unzip_name/samples/APIExample-OC/sdk.podspec ./$unzip_name/ - python3 ./.github/ci/build/modify_podfile.py ./$unzip_name/samples/APIExample-OC/Podfile - else - rm -rf ./$unzip_name/samples/API-Example/APIExample-OC - mv ./$unzip_name/samples/API-Example/APIExample ./$unzip_name/samples/APIExample - mv ./$unzip_name/samples/APIExample/sdk.podspec ./$unzip_name/ - python3 ./.github/ci/build/modify_podfile.py ./$unzip_name/samples/APIExample/Podfile - fi - - if [ $? -eq 0 ]; then - echo "success" - else - echo "failed" - exit 1 - fi - if [ $is_generate_validate_app = true ]; then - if [ $is_objective_c = true ]; then - ./.github/ci/build/build_ios_ipa.sh ./$unzip_name/samples/APIExample-OC - else - ./.github/ci/build/build_ios_ipa.sh ./$unzip_name/samples/APIExample - fi - fi +if [ $compile_project = true ]; then + cd ./$unzip_name/samples/API-Example + ./cloud_build.sh || exit 1 + cd - fi -rm -rf ./$unzip_name/samples/API-Example -7za a -tzip result.zip -r $unzip_name -cp result.zip $WORKSPACE/withAPIExample_${BUILD_NUMBER}_$zip_name diff --git a/.github/ci/build/build_ios_ipa.sh b/.github/ci/build/build_ios_ipa.sh index 7badd7a4d..e63a4f94f 100755 --- a/.github/ci/build/build_ios_ipa.sh +++ b/.github/ci/build/build_ios_ipa.sh @@ -2,6 +2,10 @@ CURRENT_PATH=$PWD # 获取项目目录 PROJECT_PATH="$( cd "$1" && pwd )" +IS_OBJECTIVE_C=false +if [ "$ios_direction" = "APIExample-OC" ]; then + IS_OBJECTIVE_C=true +fi cd ${PROJECT_PATH} && pod install @@ -13,10 +17,10 @@ else fi # 项目target名 -TARGET_NAME=${PROJECT_PATH##*/} +TARGET_NAME=$ios_direction KEYCENTER_PATH=${PROJECT_PATH}"/"${TARGET_NAME}"/Common/KeyCenter.swift" -if [ $is_objective_c = true ]; then +if [ $IS_OBJECTIVE_C = true ]; then KEYCENTER_PATH=${PROJECT_PATH}"/"${TARGET_NAME}"/Common/KeyCenter.m" fi @@ -31,7 +35,7 @@ PBXPROJ_PATH="${PROJECT_PATH}/${TARGET_NAME}.xcodeproj/project.pbxproj" echo PBXPROJ_PATH: $PBXPROJ_PATH # 主项目工程配置 -if [ $is_objective_c = true ]; then +if [ $IS_OBJECTIVE_C = true ]; then # Debug /usr/libexec/PlistBuddy -c "Set :objects:E70ADE062A5D0050009947CF:buildSettings:CODE_SIGN_STYLE 'Manual'" $PBXPROJ_PATH /usr/libexec/PlistBuddy -c "Set :objects:E70ADE062A5D0050009947CF:buildSettings:DEVELOPMENT_TEAM 'GM72UGLGZW'" $PBXPROJ_PATH diff --git a/.github/ci/build/build_mac.sh b/.github/ci/build/build_mac.sh index 9a2e0deeb..00444a1ee 100644 --- a/.github/ci/build/build_mac.sh +++ b/.github/ci/build/build_mac.sh @@ -38,7 +38,7 @@ # others: Rename the zip package name yourself, But need copy it to workspace dir ################################## -echo is_generate_validate_app:$is_generate_validate_app +echo compile_project:$compile_project echo Package_Publish: $Package_Publish echo is_tag_fetch: $is_tag_fetch echo arch: $arch @@ -54,8 +54,8 @@ echo sdk_url: $sdk_url zip_name=${sdk_url##*/} echo zip_name: $zip_name -python3 $WORKSPACE/artifactory_utils.py --action=download_file --file=$sdk_url -7za x ./$zip_name -y +curl -o $zip_name $sdk_url || exit 1 +7za x ./$zip_name -y > log.txt unzip_name=`ls -S -d */ | grep Agora` echo unzip_name: $unzip_name @@ -64,21 +64,20 @@ rm -rf ./$unzip_name/bin rm ./$unzip_name/commits rm ./$unzip_name/package_size_report.txt mkdir ./$unzip_name/samples -mkdir ./$unzip_name/samples/APIExample -if [ $? -eq 0 ]; then - echo "success" -else - echo "failed" - exit 1 -fi -cp -a ./macOS/** ./$unzip_name/samples/APIExample + + +cp -rf ./macOS ./$unzip_name/samples/APIExample || exit 1 +ls -al ./$unzip_name/samples/API-Example/ mv ./$unzip_name/samples/APIExample/sdk.podspec ./$unzip_name/ python3 ./.github/ci/build/modify_podfile.py ./$unzip_name/samples/APIExample/Podfile +7za a -tzip result.zip -r $unzip_name +cp result.zip $WORKSPACE/withAPIExample_${BUILD_NUMBER}_$zip_name -if [ $is_generate_validate_app = true ]; then - ./.github/ci/build/build_mac_ipa.sh ./$unzip_name/samples/APIExample +if [ $compile_project = true ]; then + cd ./$unzip_name/samples/APIExample + ./cloud_build.sh || exit 1 + cd - fi -7za a -tzip result.zip -r $unzip_name -cp result.zip $WORKSPACE/withAPIExample_${BUILD_NUMBER}_$zip_name + diff --git a/.github/ci/build/build_windows.bat b/.github/ci/build/build_windows.bat index 4c83f4263..0f02ea60c 100644 --- a/.github/ci/build/build_windows.bat +++ b/.github/ci/build/build_windows.bat @@ -38,6 +38,7 @@ REM pr: output test.zip to workspace dir REM others: Rename the zip package name yourself, But need copy it to workspace dir REM ################################## +echo compile_project: %compile_project% echo Package_Publish: %Package_Publish% echo is_tag_fetch: %is_tag_fetch% echo arch: %arch% @@ -84,15 +85,11 @@ xcopy /Y /E windows\README.zh.md Agora_Native_SDK_for_Windows_FULL\samples\API-e rmdir /S /Q Agora_Native_SDK_for_Windows_FULL\samples\API-example\APIExample\APIExample dir Agora_Native_SDK_for_Windows_FULL\samples\API-example\APIExample 7z a -tzip result.zip -r Agora_Native_SDK_for_Windows_FULL -copy result.zip %WORKSPACE%\\withAPIExample_%date:~4,2%%date:~7,2%%time:~0,2%%time:~3,2%_%zip_name% +copy result.zip %WORKSPACE%\\withAPIExample_%BUILD_NUMBER%_%zip_name% del /F result.zip del /F %WORKSPACE%\\%zip_name% +if %compile_project% EQU false goto FINAL cd Agora_Native_SDK_for_Windows_FULL\samples\API-example -echo "compile start..." -call installThirdParty.bat -"C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\MSBuild.exe" "APIExample.sln" /p:platform="Win32" /p:configuration="Release" -7z a -tzip result.zip -r Release -copy result.zip %WORKSPACE%\\APIExample_windows_%date:~4,2%%date:~7,2%%time:~0,2%%time:~3,2%_Release_exe.zip -del /F result.zip -echo "compile done." +call cloud_build.bat +:FINAL diff --git a/.gitignore b/.gitignore index 2785a6808..b27e7ef9b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ xcuserdata .DS_Store AgoraRtcKit.framework +*/libs diff --git a/Android/APIExample-Audio/README.md b/Android/APIExample-Audio/README.md index 23f880c8a..6cbdf868a 100644 --- a/Android/APIExample-Audio/README.md +++ b/Android/APIExample-Audio/README.md @@ -31,6 +31,7 @@ To build and run the sample application, get an App Id: // assign token and certificate to null if you have not enabled app certificate YOUR APP CERTIFICATE // assign token and certificate to null if you have not enabled app certificate or you have set the certificate above. + // PS:It is unsafe to place the App Certificate on the client side, it is recommended to place it on the server side to ensure that the App Certificate is not leaked. YOUR ACCESS TOKEN ``` diff --git a/Android/APIExample-Audio/README.zh.md b/Android/APIExample-Audio/README.zh.md index 84cc668ee..6828cc087 100644 --- a/Android/APIExample-Audio/README.zh.md +++ b/Android/APIExample-Audio/README.zh.md @@ -31,6 +31,7 @@ // 如果你没有打开Token功能,certificate可以直接不填 YOUR APP CERTIFICATE // 如果你没有打开Token功能或者已经配置了certificate,token可以直接不填 + // 注意:App证书放在客户端不安全,推荐放在服务端以确保 App 证书不会泄露。 YOUR ACCESS TOKEN ``` diff --git a/Android/APIExample-Audio/app/build.gradle b/Android/APIExample-Audio/app/build.gradle index 73679fdca..d4d1a595a 100644 --- a/Android/APIExample-Audio/app/build.gradle +++ b/Android/APIExample-Audio/app/build.gradle @@ -1,6 +1,7 @@ apply plugin: 'com.android.application' def localSdkPath= "${rootProject.projectDir.absolutePath}/../../sdk" +def agoraSdkVersion = '4.4.1' android { compileSdkVersion 32 @@ -16,10 +17,28 @@ android { testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } + signingConfigs { + myConfig { + storeFile new File(rootProject.rootDir.absolutePath + "/keystore.key") + storePassword "965606" + keyAlias "agora" + keyPassword "965606" + v1SigningEnabled true + v2SigningEnabled true + } + } + buildTypes { - release { + debug { minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + signingConfig signingConfigs.myConfig + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + + release { + minifyEnabled true + signingConfig signingConfigs.myConfig + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } @@ -28,6 +47,8 @@ android { targetCompatibility JavaVersion.VERSION_1_8 } + + sourceSets { main { jniLibs.srcDirs += 'src/main/jniLibs' @@ -40,6 +61,15 @@ android { buildFeatures{ viewBinding true } + + applicationVariants.all { + variant -> + variant.outputs.all { output -> + outputFileName = new File(rootProject.name + + "_" + agoraSdkVersion + + "_" + new Date().format("yyyyMMddHHmm") + ".apk") + } + } } dependencies { @@ -48,16 +78,15 @@ dependencies { implementation fileTree(dir: "${localSdkPath}", include: ['*.jar', '*.aar']) } else{ - def agora_sdk_version = "4.3.1" // case 1: full single lib with voice only - implementation "io.agora.rtc:voice-sdk:${agora_sdk_version}" + implementation "io.agora.rtc:voice-sdk:${agoraSdkVersion}" // case 2: partial libs with voice only - // implementation "io.agora.rtc:voice-rtc-basic:${agora_sdk_version}" - // implementation "io.agora.rtc:spatial-audio:${agora_sdk_version}" - // implementation "io.agora.rtc:audio-beauty:${agora_sdk_version}" - // implementation "io.agora.rtc:aiaec:${agora_sdk_version}" - // implementation "io.agora.rtc:drm-loader:${agora_sdk_version}" - // implementation "io.agora.rtc:drm:${agora_sdk_version}" + // implementation "io.agora.rtc:voice-rtc-basic:${agoraSdkVersion}" + // implementation "io.agora.rtc:spatial-audio:${agoraSdkVersion}" + // implementation "io.agora.rtc:audio-beauty:${agoraSdkVersion}" + // implementation "io.agora.rtc:aiaec:${agoraSdkVersion}" + // implementation "io.agora.rtc:drm-loader:${agoraSdkVersion}" + // implementation "io.agora.rtc:drm:${agoraSdkVersion}" } implementation 'androidx.appcompat:appcompat:1.5.0' diff --git a/Android/APIExample-Audio/app/src/main/java/io/agora/api/example/examples/advanced/VoiceEffects.java b/Android/APIExample-Audio/app/src/main/java/io/agora/api/example/examples/advanced/VoiceEffects.java index ef1abdbf1..8efd7619d 100644 --- a/Android/APIExample-Audio/app/src/main/java/io/agora/api/example/examples/advanced/VoiceEffects.java +++ b/Android/APIExample-Audio/app/src/main/java/io/agora/api/example/examples/advanced/VoiceEffects.java @@ -89,6 +89,9 @@ import io.agora.rtc2.RtcEngineConfig; import io.agora.rtc2.proxy.LocalAccessPointConfiguration; +/** + * The type Voice effects. + */ @Example( index = 4, group = ADVANCED, @@ -106,7 +109,7 @@ public class VoiceEffects extends BaseFragment implements View.OnClickListener, private EditText et_channel; private Button join; private Spinner audioProfile, audioScenario, - chatBeautifier, timbreTransformation, voiceChanger, styleTransformation, roomAcoustics, pitchCorrection, _pitchModeOption, _pitchValueOption, voiceConversion, ainsMode, + chatBeautifier, timbreTransformation, voiceChanger, styleTransformation, roomAcoustics, pitchCorrection, _pitchModeOption, _pitchValueOption, voiceConversion, ainsMode, voiceAITuner, customBandFreq, customReverbKey; private ViewGroup _voice3DLayout, _pitchModeLayout, _pitchValueLayout; private SeekBar _voice3DCircle, customPitch, customBandGain, customReverbValue, customVoiceFormant; @@ -154,6 +157,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat _pitchValueOption = view.findViewById(R.id.audio_pitch_value_option); voiceConversion = view.findViewById(R.id.audio_voice_conversion); ainsMode = view.findViewById(R.id.audio_ains_mode); + voiceAITuner = view.findViewById(R.id.voice_ai_tuner); chatBeautifier.setOnItemSelectedListener(this); timbreTransformation.setOnItemSelectedListener(this); @@ -166,6 +170,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat _pitchModeOption.setOnItemSelectedListener(this); _pitchValueOption.setOnItemSelectedListener(this); ainsMode.setOnItemSelectedListener(this); + voiceAITuner.setOnItemSelectedListener(this); // Customize Voice Effects Layout customPitch = view.findViewById(R.id.audio_custom_pitch); // engine.setLocalVoicePitch() @@ -205,6 +210,7 @@ private void resetControlLayoutByJoined() { _pitchValueLayout.setVisibility(View.GONE); voiceConversion.setEnabled(joined); ainsMode.setEnabled(joined); + voiceAITuner.setEnabled(joined); customPitch.setEnabled(joined); customBandFreq.setEnabled(joined); @@ -238,30 +244,30 @@ public void onActivityCreated(@Nullable Bundle savedInstanceState) { } try { RtcEngineConfig config = new RtcEngineConfig(); - /** + /* * The context of Android Activity */ config.mContext = context.getApplicationContext(); - /** + /* * The App ID issued to you by Agora. See How to get the App ID */ config.mAppId = getString(R.string.agora_app_id); - /** Sets the channel profile of the Agora RtcEngine. + /* Sets the channel profile of the Agora RtcEngine. CHANNEL_PROFILE_COMMUNICATION(0): (Default) The Communication profile. Use this profile in one-on-one calls or group calls, where all users can talk freely. CHANNEL_PROFILE_LIVE_BROADCASTING(1): The Live-Broadcast profile. Users in a live-broadcast channel have a role as either broadcaster or audience. A broadcaster can both send and receive streams; an audience can only receive streams.*/ config.mChannelProfile = Constants.CHANNEL_PROFILE_LIVE_BROADCASTING; - /** + /* * IRtcEngineEventHandler is an abstract class providing default implementation. * The SDK uses this class to report to the app on SDK runtime events. */ config.mEventHandler = iRtcEngineEventHandler; config.mAudioScenario = Constants.AudioScenario.getValue(Constants.AudioScenario.DEFAULT); - config.mAreaCode = ((MainApplication)getActivity().getApplication()).getGlobalSettings().getAreaCode(); + config.mAreaCode = ((MainApplication) getActivity().getApplication()).getGlobalSettings().getAreaCode(); engine = RtcEngine.create(config); - /** + /* * This parameter is for reporting the usages of APIExample to agora background. * Generally, it is not necessary for you to set this parameter. */ @@ -288,7 +294,7 @@ public void onActivityCreated(@Nullable Bundle savedInstanceState) { @Override public void onDestroy() { super.onDestroy(); - /**leaveChannel and Destroy the RtcEngine instance*/ + /*leaveChannel and Destroy the RtcEngine instance*/ if (engine != null) { engine.leaveChannel(); } @@ -312,15 +318,14 @@ public void onClick(View v) { AndPermission.with(this).runtime().permission( Permission.Group.STORAGE, Permission.Group.MICROPHONE - ).onGranted(permissions -> - { + ).onGranted(permissions -> { // Permissions Granted joinChannel(channelId); }).start(); } else { joined = false; resetControlLayoutByJoined(); - /**After joining a channel, the user must call the leaveChannel method to end the + /*After joining a channel, the user must call the leaveChannel method to end the * call before joining another channel. This method returns 0 if the user leaves the * channel and releases all resources related to the call. This method call is * asynchronous, and the user has not exited the channel when the method call returns. @@ -389,7 +394,7 @@ private int getPitch2Value(String str) { * Users that input the same channel name join the same channel. */ private void joinChannel(String channelId) { - /**In the demo, the default is to enter as the anchor.*/ + /*In the demo, the default is to enter as the anchor.*/ engine.setClientRole(Constants.CLIENT_ROLE_BROADCASTER); // audio config engine.setAudioProfile( @@ -397,13 +402,13 @@ private void joinChannel(String channelId) { Constants.AudioScenario.getValue(Constants.AudioScenario.valueOf(audioScenario.getSelectedItem().toString())) ); - /**Please configure accessToken in the string_config file. + /*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 * https://docs.agora.io/en/Agora%20Platform/token?platform=All%20Platforms#get-a-temporary-token * A token generated at the server. This applies to scenarios with high-security requirements. For details, see * https://docs.agora.io/en/cloud-recording/token_server_java?platform=Java*/ TokenUtils.gen(requireContext(), channelId, 0, accessToken -> { - /** Allows a user to join a channel. + /* Allows a user to join a channel. if you do not specify the uid, we will generate the uid for you*/ ChannelMediaOptions option = new ChannelMediaOptions(); @@ -588,7 +593,7 @@ public void onItemSelected(AdapterView parent, View view, int position, long for (Spinner spinner : voiceBeautifierSpinner) { if (spinner != parent) { - if(spinner.getSelectedItemPosition() != 0){ + if (spinner.getSelectedItemPosition() != 0) { spinner.setTag("reset"); spinner.setSelection(0); } @@ -605,33 +610,33 @@ public void onItemSelected(AdapterView parent, View view, int position, long for (Spinner spinner : audioEffectSpinner) { if (spinner != parent) { - if(spinner.getSelectedItemPosition() != 0){ + if (spinner.getSelectedItemPosition() != 0) { spinner.setTag("reset"); spinner.setSelection(0); } } } - _voice3DLayout.setVisibility(audioEffectPreset == ROOM_ACOUSTICS_3D_VOICE ? View.VISIBLE: View.GONE); + _voice3DLayout.setVisibility(audioEffectPreset == ROOM_ACOUSTICS_3D_VOICE ? View.VISIBLE : View.GONE); _pitchModeLayout.setVisibility(audioEffectPreset == PITCH_CORRECTION ? View.VISIBLE : View.GONE); _pitchValueLayout.setVisibility(audioEffectPreset == PITCH_CORRECTION ? View.VISIBLE : View.GONE); return; } - if(parent == voiceConversion){ + if (parent == voiceConversion) { String item = parent.getSelectedItem().toString(); engine.setVoiceConversionPreset(getVoiceConversionValue(item)); return; } - if(parent == _pitchModeOption || parent == _pitchValueOption){ + if (parent == _pitchModeOption || parent == _pitchValueOption) { int effectOption1 = getPitch1Value(_pitchModeOption.getSelectedItem().toString()); int effectOption2 = getPitch2Value(_pitchValueOption.getSelectedItem().toString()); engine.setAudioEffectParameters(PITCH_CORRECTION, effectOption1, effectOption2); } - if(parent == ainsMode){ + if (parent == ainsMode) { boolean enable = position > 0; /* The AI noise suppression modes: @@ -644,6 +649,12 @@ public void onItemSelected(AdapterView parent, View view, int position, long */ engine.setAINSMode(enable, position - 1); } + + if (parent == voiceAITuner) { + boolean enable = position > 0; + String item = parent.getSelectedItem().toString(); + engine.enableVoiceAITuner(enable, enable ? Constants.VOICE_AI_TUNER_TYPE.valueOf(item) : Constants.VOICE_AI_TUNER_TYPE.VOICE_AI_TUNER_MATURE_MALE); + } } private int getVoiceConversionValue(String label) { @@ -672,7 +683,7 @@ private int getVoiceConversionValue(String label) { return VOICE_CHANGER_DARTH_VADER; case "VOICE_CHANGER_IRON_LADY": return VOICE_CHANGER_IRON_LADY; - case "VOICE_CHANGER_SHIN_CHAN": + case "VOICE_CHANGER_SHIN_CHAN": return VOICE_CHANGER_SHIN_CHAN; case "VOICE_CHANGER_GIRLISH_MAN": return VOICE_CHANGER_GIRLISH_MAN; @@ -807,11 +818,11 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { if (!fromUser) { return; } - if(seekBar == _voice3DCircle){ + if (seekBar == _voice3DCircle) { int cicle = (int) (1 + 59 * progress * 1.0f / seekBar.getMax()); // [1,60], 10 default engine.setAudioEffectParameters(ROOM_ACOUSTICS_3D_VOICE, cicle, 0); - }else if(seekBar == customPitch){ + } else if (seekBar == customPitch) { double pitch = 0.5 + 1.5 * progress * 1.0f / seekBar.getMax(); // pitch: [0.5,2.0], 1.0 default engine.setLocalVoicePitch(pitch); @@ -827,11 +838,11 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { // AUDIO_REVERB_ROOM_SIZE(2):[0, 100] dB // AUDIO_REVERB_WET_DELAY(3):Wet signal, [0, 200] ms // AUDIO_REVERB_STRENGTH(4): [0, 100] - if(reverbKey == Constants.AUDIO_REVERB_TYPE.AUDIO_REVERB_DRY_LEVEL || reverbKey == Constants.AUDIO_REVERB_TYPE.AUDIO_REVERB_WET_LEVEL){ + if (reverbKey == Constants.AUDIO_REVERB_TYPE.AUDIO_REVERB_DRY_LEVEL || reverbKey == Constants.AUDIO_REVERB_TYPE.AUDIO_REVERB_WET_LEVEL) { value = (int) (-20 + 30 * progress * 1.0f / seekBar.getMax()); - }else if(reverbKey == Constants.AUDIO_REVERB_TYPE.AUDIO_REVERB_WET_DELAY){ + } else if (reverbKey == Constants.AUDIO_REVERB_TYPE.AUDIO_REVERB_WET_DELAY) { value = (int) (200 * progress * 1.0f / seekBar.getMax()); - }else { + } else { value = (int) (100 * progress * 1.0f / seekBar.getMax()); } engine.setLocalVoiceReverb(reverbKey, value); diff --git a/Android/APIExample-Audio/app/src/main/java/io/agora/api/example/utils/TokenUtils.java b/Android/APIExample-Audio/app/src/main/java/io/agora/api/example/utils/TokenUtils.java index 65b2ee411..4f378bf22 100644 --- a/Android/APIExample-Audio/app/src/main/java/io/agora/api/example/utils/TokenUtils.java +++ b/Android/APIExample-Audio/app/src/main/java/io/agora/api/example/utils/TokenUtils.java @@ -85,7 +85,7 @@ private static void gen(String appId, String certificate, String channelName, in } Request request = new Request.Builder() - .url("https://test-toolbox.bj2.agoralab.co/v1/token/generate") + .url("https://service.agora.io/toolbox-global/v1/token/generate") .addHeader("Content-Type", "application/json") .post(RequestBody.create(postBody.toString(), null)) .build(); diff --git a/Android/APIExample-Audio/app/src/main/res/layout/fragment_joinchannel_audio_by_token.xml b/Android/APIExample-Audio/app/src/main/res/layout/fragment_joinchannel_audio_by_token.xml index 8b5d3b857..8449bda6a 100644 --- a/Android/APIExample-Audio/app/src/main/res/layout/fragment_joinchannel_audio_by_token.xml +++ b/Android/APIExample-Audio/app/src/main/res/layout/fragment_joinchannel_audio_by_token.xml @@ -7,11 +7,10 @@ + android:layout_marginBottom="16dp"> + + + + + + + + AINS_MODE_AGGRESSIVE AINS_MODE_ULTRALOWLATENCY + + + OFF + VOICE_AI_TUNER_MATURE_MALE + VOICE_AI_TUNER_FRESH_MALE + VOICE_AI_TUNER_ELEGANT_FEMALE + VOICE_AI_TUNER_SWEET_FEMALE + VOICE_AI_TUNER_WARM_MALE_SINGING + VOICE_AI_TUNER_GENTLE_FEMALE_SINGING + VOICE_AI_TUNER_HUSKY_MALE_SINGING + VOICE_AI_TUNER_WARM_ELEGANT_FEMALE_SINGING + VOICE_AI_TUNER_POWERFUL_MALE_SINGING + VOICE_AI_TUNER_DREAMY_FEMALE_SINGING + \ No newline at end of file diff --git a/Android/APIExample-Audio/app/src/main/res/values/string_configs.xml b/Android/APIExample-Audio/app/src/main/res/values/string_configs.xml index 1592e3705..767727190 100644 --- a/Android/APIExample-Audio/app/src/main/res/values/string_configs.xml +++ b/Android/APIExample-Audio/app/src/main/res/values/string_configs.xml @@ -25,14 +25,16 @@ or use the console to generate a temporary token. In order to get the APP ID, you can open the agora console (https://console.agora.io/) to create a project with the App Certificate enabled, - then the APP Certificate can be found in the project detail page. - PS: If the project does not have certificates enabled, leave this field blank. + then the APP Certificate can be found in the project detail page.If the project does not have certificates enabled, leave this field blank. + + PS: It is unsafe to place the App Certificate on the client side, it is recommended to place it on the server side to ensure that the App Certificate is not leaked. 声网APP证书 Agora 提供 App certificate 用以生成 Token。您可以在您的服务器部署并生成 Token,或者使用控制台生成临时的 Token。 - 进入声网控制台(https://console.agora.io/),创建一个带证书鉴权的项目,进入项目配置页,即可看到APP证书。 - 注意:如果项目没有开启证书鉴权,这个字段留空。 + 进入声网控制台(https://console.agora.io/),创建一个带证书鉴权的项目,进入项目配置页,即可看到APP证书。如果项目没有开启证书鉴权,这个字段留空。 + + 注意:App证书放在客户端不安全,推荐放在服务端以确保 App 证书不会泄露。 --> YOUR APP CERTIFICATE diff --git a/Android/APIExample-Audio/cloud_build.sh b/Android/APIExample-Audio/cloud_build.sh new file mode 100755 index 000000000..812a18d85 --- /dev/null +++ b/Android/APIExample-Audio/cloud_build.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env sh + +# cache gradle to /tmp/.gradle +ls ~/.gradle || (mkdir -p /tmp/.gradle && ln -s /tmp/.gradle ~/.gradle && touch ~/.gradle/ln_$(date "+%y%m%d%H") && ls ~/.gradle) + +#change android maven to china repos +sed -ie "s#google()#maven { url \"https\://maven.aliyun.com/repository/public\" }\n google()#g" settings.gradle +sed -ie "s#https://services.gradle.org/distributions#https://mirrors.cloud.tencent.com/gradle#g" gradle/wrapper/gradle-wrapper.properties + +## config appId +sed -i -e "s#YOUR APP ID#${APP_ID}#g" app/src/main/res/values/string_configs.xml +sed -i -e "s#YOUR APP CERTIFICATE##g" app/src/main/res/values/string_configs.xml +sed -i -e "s#YOUR ACCESS TOKEN##g" app/src/main/res/values/string_configs.xml +rm -f app/src/main/res/values/string_configs.xml-e + +./gradlew clean || exit 1 +./gradlew :app:assembleRelease || exit 1 +if [ "$WORKSPACE" != "" ]; then +SDK_VERSION=$(echo $sdk_url | cut -d "/" -f 5) +cp app/build/outputs/apk/release/*.apk $WORKSPACE/APIExample-Audio_${BUILD_NUMBER}_${SDK_VERSION}_$(date "+%Y%m%d%H%M%S").apk +fi \ No newline at end of file diff --git a/Android/APIExample-Audio/keystore.key b/Android/APIExample-Audio/keystore.key new file mode 100644 index 000000000..a5014a522 Binary files /dev/null and b/Android/APIExample-Audio/keystore.key differ diff --git a/Android/APIExample-Compose/README.md b/Android/APIExample-Compose/README.md new file mode 100644 index 000000000..e2ee7b506 --- /dev/null +++ b/Android/APIExample-Compose/README.md @@ -0,0 +1,50 @@ +# API Example Android + +*English | [中文](README.zh.md)* + +This project presents you a set of API examples to help you understand how to use Agora APIs. + +## Prerequisites + +- Android Studio 3.0+ +- Physical Android device +- Android simulator is supported + +## Quick Start + +This section shows you how to prepare, build, and run the sample application. + +### Obtain an App Id + +To build and run the sample application, get an App Id: + +1. Create a developer account at [agora.io](https://dashboard.agora.io/signin/). Once you finish the signup process, you will be redirected to the Dashboard. +2. Navigate in the Dashboard tree on the left to **Projects** > **Project List**. +3. Save the **App Id** from the Dashboard for later use. +4. Save the **App Certificate** from the Dashboard for later use. + +5. Open `Android/APIExample-Compose` and edit the `local.properties` file. Update `YOUR APP ID` with your App Id, update `YOUR APP CERTIFICATE` with the main app certificate from dashboard. Note you can leave the certificate variable `null` if your project has not turned on security token. + + ``` + // Agora APP ID. + AGORA_APP_ID=YOUR APP ID + // Agora APP Certificate. If the project does not have certificates enabled, leave this field blank. + AGORA_APP_CERT=YOUR APP CERTIFICATE + ``` + +You are all set. Now connect your Android device and run the project. + + +## Contact Us + +- For potential issues, take a look at our [FAQ](https://docs.agora.io/en/faq) first +- Dive into [Agora SDK Samples](https://github.com/AgoraIO) to see more tutorials +- Take a look at [Agora Use Case](https://github.com/AgoraIO-usecase) for more complicated real use case +- Repositories managed by developer communities can be found at [Agora Community](https://github.com/AgoraIO-Community) +- You can find full API documentation at [Document Center](https://docs.agora.io/en/) +- If you encounter problems during integration, you can ask question in [Stack Overflow](https://stackoverflow.com/questions/tagged/agora.io) +- You can file bugs about this sample at [issue](https://github.com/AgoraIO/API-Examples/issues) + +## License + +The MIT License (MIT) diff --git a/Android/APIExample-Compose/README.zh.md b/Android/APIExample-Compose/README.zh.md new file mode 100644 index 000000000..21769f0e2 --- /dev/null +++ b/Android/APIExample-Compose/README.zh.md @@ -0,0 +1,50 @@ +# API Example Android + +*[English](README.md) | 中文* + +这个开源示例项目演示了Agora视频SDK的部分API使用示例,以帮助开发者更好地理解和运用Agora视频SDK的API。 + +## 环境准备 + +- Android Studio 3.0+ +- Android 真机设备 +- 支持模拟器 + +## 运行示例程序 + +这个段落主要讲解了如何编译和运行实例程序。 + +### 创建Agora账号并获取AppId + +在编译和启动实例程序前,你需要首先获取一个可用的App Id: + +1. 在[agora.io](https://dashboard.agora.io/signin/)创建一个开发者账号 +2. 前往后台页面,点击左部导航栏的 **项目 > 项目列表** 菜单 +3. 复制后台的 **App Id** 并备注,稍后启动应用时会用到它 +4. 复制后台的 **App 证书** 并备注,稍后启动应用时会用到它 + +5. 打开 `Android/APIExample` 并编辑 `local.properties`,将你的 AppID 、App主证书 分别替换到 `Your App Id` 和 `YOUR APP CERTIFICATE` + + ``` + // 声网APP ID。 + AGORA_APP_ID=YOUR APP ID + // 声网APP证书。如果项目没有开启证书鉴权,这个字段留空。 + AGORA_APP_CERT=YOUR APP CERTIFICATE + ``` + +然后你就可以编译并运行项目了。 + +## 联系我们 + +- 如果你遇到了困难,可以先参阅 [常见问题](https://docs.agora.io/cn/faq) +- 如果你想了解更多官方示例,可以参考 [官方SDK示例](https://github.com/AgoraIO) +- 如果你想了解声网SDK在复杂场景下的应用,可以参考 [官方场景案例](https://github.com/AgoraIO-usecase) +- 如果你想了解声网的一些社区开发者维护的项目,可以查看 [社区](https://github.com/AgoraIO-Community) +- 完整的 API 文档见 [文档中心](https://docs.agora.io/cn/) +- 若遇到问题需要开发者帮助,你可以到 [开发者社区](https://rtcdeveloper.com/) 提问 +- 如果需要售后技术支持, 你可以在 [Agora Dashboard](https://dashboard.agora.io) 提交工单 +- 如果发现了示例代码的 bug,欢迎提交 [issue](https://github.com/AgoraIO/API-Examples/issues) + +## 代码许可 + +The MIT License (MIT) diff --git a/Android/APIExample-Compose/app/build.gradle.kts b/Android/APIExample-Compose/app/build.gradle.kts index 1c5229ad4..fb0485a35 100644 --- a/Android/APIExample-Compose/app/build.gradle.kts +++ b/Android/APIExample-Compose/app/build.gradle.kts @@ -1,3 +1,6 @@ +import com.android.build.gradle.internal.api.ApkVariantOutputImpl +import java.text.SimpleDateFormat +import java.util.Date import java.util.Properties plugins { @@ -5,6 +8,8 @@ plugins { alias(libs.plugins.jetbrainsKotlinAndroid) } +val localSdkPath = "${rootProject.projectDir.absolutePath}/../../sdk" + android { namespace = "io.agora.api.example.compose" compileSdk = 34 @@ -24,7 +29,7 @@ android { val properties = Properties() properties.load(rootProject.file("local.properties").inputStream()) val AGORA_APP_ID = properties.getProperty("AGORA_APP_ID", "") - if(AGORA_APP_ID == ""){ + if (AGORA_APP_ID == "") { throw GradleException("请在项目根目录下local.properties文件里正确配置:AGORA_APP_ID=<您的声网AppId>") } val AGORA_APP_CERT = properties.getProperty("AGORA_APP_CERT", "") @@ -32,12 +37,26 @@ android { buildConfigField("String", "AGORA_APP_CERT", "\"$AGORA_APP_CERT\"") } + signingConfigs(Action { + create("myConfig") { + storeFile = file(rootProject.rootDir.absolutePath + "/keystore.key") + storePassword = "965606" + keyAlias = "agora" + keyPassword = "965606" + } + }) + buildFeatures { buildConfig = true } buildTypes { - release { + debug { isMinifyEnabled = false + signingConfig = signingConfigs.getByName("myConfig") + } + release { + isMinifyEnabled = true + signingConfig = signingConfigs.getByName("myConfig") proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" @@ -62,10 +81,29 @@ android { excludes += "/META-INF/{AL2.0,LGPL2.1}" } } + android.applicationVariants.all { + outputs.all { + if (this is ApkVariantOutputImpl) { + this.outputFileName = + "${rootProject.name}_${libs.versions.agoraSdk.get()}_${ + SimpleDateFormat("yyyyMMddHHmm").format( + Date() + ) + }.apk" + } + } + } + sourceSets { + getByName("main") { + if (File(localSdkPath).exists()) { + jniLibs.srcDirs(localSdkPath) + } + } + + } } dependencies { - implementation(libs.androidx.core.ktx) implementation(libs.androidx.lifecycle.runtime.ktx) implementation(libs.androidx.activity.compose) @@ -77,8 +115,6 @@ dependencies { implementation(libs.androidx.navigation.compose) implementation(libs.androidx.datastore) implementation(libs.androidx.datastore.preferences) - implementation(libs.agora.full.sdk) - implementation(libs.agora.full.screen.sharing) testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) @@ -90,4 +126,11 @@ dependencies { implementation(libs.okhttp) implementation(libs.logging.interceptor) + if (File(localSdkPath).exists()) { + implementation(fileTree(localSdkPath).include("*.jar", "*.aar")) + } else { + implementation(libs.agora.full.sdk) + implementation(libs.agora.full.screen.sharing) + } + } \ No newline at end of file diff --git a/Android/APIExample-Compose/app/proguard-rules.pro b/Android/APIExample-Compose/app/proguard-rules.pro index 481bb4348..a2b8a71d6 100644 --- a/Android/APIExample-Compose/app/proguard-rules.pro +++ b/Android/APIExample-Compose/app/proguard-rules.pro @@ -18,4 +18,12 @@ # If you keep the line number information, uncomment this to # hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file +#-renamesourcefileattribute SourceFile + +-keep class io.agora.**{*;} +-dontwarn javax.** +-dontwarn com.google.devtools.build.android.** + +-dontwarn org.bouncycastle.** +-dontwarn org.conscrypt.** +-dontwarn org.openjsse.** \ No newline at end of file diff --git a/Android/APIExample-Compose/app/src/main/AndroidManifest.xml b/Android/APIExample-Compose/app/src/main/AndroidManifest.xml index e392d9d14..d05457e9e 100644 --- a/Android/APIExample-Compose/app/src/main/AndroidManifest.xml +++ b/Android/APIExample-Compose/app/src/main/AndroidManifest.xml @@ -5,12 +5,13 @@ + - + - - + + @@ -27,13 +28,21 @@ android:name=".MainActivity" android:exported="true" android:label="@string/app_name" - android:theme="@style/Theme.APIExampleCompose"> + android:theme="@style/Theme.APIExampleCompose" + android:configChanges="screenSize|screenLayout|orientation|smallestScreenSize"> + + + \ No newline at end of file diff --git a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/model/Examples.kt b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/model/Examples.kt index de56bab84..47abfdc76 100644 --- a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/model/Examples.kt +++ b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/model/Examples.kt @@ -20,19 +20,21 @@ import io.agora.api.example.compose.samples.MediaPlayer import io.agora.api.example.compose.samples.MediaRecorder import io.agora.api.example.compose.samples.OriginAudioData import io.agora.api.example.compose.samples.OriginVideoData +import io.agora.api.example.compose.samples.PictureInPictureEntrance import io.agora.api.example.compose.samples.PlayAudioFiles import io.agora.api.example.compose.samples.PreCallTest import io.agora.api.example.compose.samples.RTMPStreaming import io.agora.api.example.compose.samples.RhythmPlayer import io.agora.api.example.compose.samples.ScreenSharing import io.agora.api.example.compose.samples.SendDataStream +import io.agora.api.example.compose.samples.SpatialSound import io.agora.api.example.compose.samples.VideoProcessExtension import io.agora.api.example.compose.samples.VoiceEffects data class Example( @StringRes val name: Int, val description: String = "", - val content: @Composable () -> Unit + val content: @Composable (back: () -> Unit) -> Unit ) val BasicExampleList = listOf( @@ -52,6 +54,7 @@ val AdvanceExampleList = listOf( Example(R.string.example_originvideodata) { OriginVideoData() }, Example(R.string.example_customvideosource) { CustomVideoSource() }, Example(R.string.example_customvideorender) { CustomVideoRender() }, + Example(R.string.example_pictureinpicture) { PictureInPictureEntrance(it) }, Example(R.string.example_joinmultichannel) { JoinMultiChannel() }, Example(R.string.example_channelencryption) { ChannelEncryption() }, Example(R.string.example_playaudiofiles) { PlayAudioFiles() }, @@ -64,4 +67,5 @@ val AdvanceExampleList = listOf( Example(R.string.example_localvideotranscoding) { LocalVideoTranscoding() }, Example(R.string.example_senddatastream) { SendDataStream() }, Example(R.string.example_hostacrosschannel) { HostAcrossChannel() }, + Example(R.string.example_spatialsound) { SpatialSound() }, ) \ No newline at end of file diff --git a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/ChannelEncryption.kt b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/ChannelEncryption.kt index e4b496d55..198acabb0 100644 --- a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/ChannelEncryption.kt +++ b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/ChannelEncryption.kt @@ -24,8 +24,10 @@ import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.platform.LocalSoftwareKeyboardController +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import io.agora.api.example.compose.BuildConfig +import io.agora.api.example.compose.R import io.agora.api.example.compose.data.SettingPreferences import io.agora.api.example.compose.ui.common.ChannelNameInput import io.agora.api.example.compose.ui.common.DropdownMenuRaw @@ -235,7 +237,7 @@ fun ChannelEncryption() { } @Composable -fun ChannelEncryptionView( +private fun ChannelEncryptionView( rtcEngine: RtcEngine? = null, channelName: String, isJoined: Boolean, @@ -274,7 +276,7 @@ fun ChannelEncryptionView( } ) DropdownMenuRaw( - title = "加密方式", + title = stringResource(id = R.string.encryption_mode), options = listOf( "AES_128_XTS" to EncryptionConfig.EncryptionMode.AES_128_XTS, "AES_128_ECB" to EncryptionConfig.EncryptionMode.AES_128_ECB, @@ -298,7 +300,7 @@ fun ChannelEncryptionView( value = encryptionKey, enabled = !isJoined, onValueChange = { onEncryptionKeyChanged(it) }, - label = { Text("加密密钥") }, + label = { Text(stringResource(id = R.string.encryption_key)) }, singleLine = true, shape = RectangleShape, colors = TextFieldDefaults.colors( diff --git a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/CustomAudioRender.kt b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/CustomAudioRender.kt index 05a7839cd..b3d6d9f98 100644 --- a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/CustomAudioRender.kt +++ b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/CustomAudioRender.kt @@ -205,7 +205,7 @@ fun CustomAudioRender() { @Composable -fun CustomAudioRenderView( +private fun CustomAudioRenderView( videoIdList: List, statsMap: Map = mapOf(), channelName: String = "", diff --git a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/CustomAudioSource.kt b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/CustomAudioSource.kt index f9f469013..e9c4e949e 100644 --- a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/CustomAudioSource.kt +++ b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/CustomAudioSource.kt @@ -23,8 +23,10 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.platform.LocalSoftwareKeyboardController +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import io.agora.api.example.compose.BuildConfig +import io.agora.api.example.compose.R import io.agora.api.example.compose.data.SettingPreferences import io.agora.api.example.compose.ui.common.AudioGrid import io.agora.api.example.compose.ui.common.AudioStatsInfo @@ -198,7 +200,7 @@ fun CustomAudioSource() { @Composable -fun CustomAudioSourceView( +private fun CustomAudioSourceView( videoIdList: List, statsMap: Map = mapOf(), channelName: String = "", @@ -221,8 +223,8 @@ fun CustomAudioSourceView( Spacer(modifier = Modifier.weight(1.0f)) - SwitchRaw(title = "发布本地音频", enable = isJoined, onCheckedChange = onLocalAudioPublish) - SwitchRaw(title = "发布麦克风", enable = isJoined, onCheckedChange = onMicrophonePublish) + SwitchRaw(title = stringResource(id = R.string.publish_local_audio), enable = isJoined, onCheckedChange = onLocalAudioPublish) + SwitchRaw(title = stringResource(id = R.string.publish_microphone), enable = isJoined, onCheckedChange = onMicrophonePublish) ChannelNameInput( channelName = channelName, diff --git a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/CustomVideoRender.kt b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/CustomVideoRender.kt index 3f6153430..ef78e9e69 100644 --- a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/CustomVideoRender.kt +++ b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/CustomVideoRender.kt @@ -278,13 +278,15 @@ class CustomVideoRenderRender( try { drawer.drawYuv( yuvUploader.yuvTextures, + 0, RendererCommon.convertMatrixFromAndroidGraphicsMatrix(renderMatrix), frame.rotatedWidth, frame.rotatedHeight, 0, 0, viewportWidth, - viewportHeight + viewportHeight, + 0 ) } catch (exception: NullPointerException) { Log.e(TAG, "skip empty buffer!") diff --git a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/CustomVideoSource.kt b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/CustomVideoSource.kt index 975defac5..56a58dd61 100644 --- a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/CustomVideoSource.kt +++ b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/CustomVideoSource.kt @@ -221,7 +221,7 @@ fun CustomVideoSource() { } @Composable -fun CustomVideoSourceView( +private fun CustomVideoSourceView( rtcEngine: RtcEngine? = null, channelName: String, isJoined: Boolean, @@ -292,7 +292,7 @@ fun CustomVideoSourceView( } } -class ExternalVideoFramePusher( +private class ExternalVideoFramePusher( private val context: Context, private val engine: RtcEngine, private val trackId: Int = 0 @@ -325,7 +325,7 @@ class ExternalVideoFramePusher( } fun dispose() { - videoFileReader.start() + videoFileReader.stop() textureBufferHelper?.invoke { yuvFboProgram?.release() yuvFboProgram = null diff --git a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/HostAcrossChannel.kt b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/HostAcrossChannel.kt index e8b4fe469..024344fd7 100644 --- a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/HostAcrossChannel.kt +++ b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/HostAcrossChannel.kt @@ -19,8 +19,10 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.platform.LocalSoftwareKeyboardController +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import io.agora.api.example.compose.BuildConfig +import io.agora.api.example.compose.R import io.agora.api.example.compose.data.SettingPreferences import io.agora.api.example.compose.ui.common.ChannelNameInput import io.agora.api.example.compose.ui.common.InputRaw @@ -247,7 +249,7 @@ fun HostAcrossChannel() { } @Composable -fun HostAcrossChannelView( +private fun HostAcrossChannelView( rtcEngine: RtcEngine?, channelName: String, isJoined: Boolean, @@ -285,13 +287,15 @@ fun HostAcrossChannelView( text = "", label = "Across Channel Name", editable = !isAcrossStarted, - btnText = if (isAcrossStarted) "退出" else "加入", + btnText = if (isAcrossStarted) stringResource(id = R.string.exit) else stringResource(id = R.string.join), enable = isJoined, onBtnClick = { onAcrossClick(it, isAcrossStarted) }, secondVisible = true, - secondBtnText = if (isAcrossRunning) "暂停" else "恢复", + secondBtnText = if (isAcrossRunning) stringResource(id = R.string.pause) else stringResource( + id = R.string.resume + ), secondEnable = isAcrossStarted, onSecondBtnClick = { onAcrossRunningClick(isAcrossRunning) diff --git a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/JoinChannelAudio.kt b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/JoinChannelAudio.kt new file mode 100644 index 000000000..47b7a8c2b --- /dev/null +++ b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/JoinChannelAudio.kt @@ -0,0 +1,321 @@ +package io.agora.api.example.compose.samples + +import android.Manifest +import android.os.Build +import android.widget.Toast +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateMapOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalLifecycleOwner +import androidx.compose.ui.platform.LocalSoftwareKeyboardController +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import io.agora.api.example.compose.BuildConfig +import io.agora.api.example.compose.data.SettingPreferences +import io.agora.api.example.compose.ui.common.AudioGrid +import io.agora.api.example.compose.ui.common.AudioStatsInfo +import io.agora.api.example.compose.ui.common.ChannelNameInput +import io.agora.api.example.compose.ui.common.DropdownMenuRaw +import io.agora.api.example.compose.ui.common.SliderRaw +import io.agora.api.example.compose.ui.common.SwitchRaw +import io.agora.api.example.compose.utils.TokenUtils +import io.agora.rtc2.ChannelMediaOptions +import io.agora.rtc2.Constants +import io.agora.rtc2.IRtcEngineEventHandler +import io.agora.rtc2.RtcEngine +import io.agora.rtc2.RtcEngineConfig + +@Composable +fun JoinChannelAudio() { + val context = LocalContext.current + val lifecycleOwner = LocalLifecycleOwner.current + val keyboard = LocalSoftwareKeyboardController.current + var isJoined by rememberSaveable { mutableStateOf(false) } + var channelName by rememberSaveable { mutableStateOf("") } + var localUid by rememberSaveable { mutableIntStateOf(0) } + var videoIdList by rememberSaveable { mutableStateOf(listOf()) } + val statsMap = remember { mutableStateMapOf(0 to AudioStatsInfo()) } + var audioRoute by rememberSaveable { mutableIntStateOf(Constants.AUDIO_ROUTE_SPEAKERPHONE) } + + val rtcEngine = remember { + RtcEngine.create(RtcEngineConfig().apply { + mAreaCode = SettingPreferences.getArea() + mContext = context + mAppId = BuildConfig.AGORA_APP_ID + mEventHandler = object : IRtcEngineEventHandler() { + + override fun onAudioRouteChanged(routing: Int) { + super.onAudioRouteChanged(routing) + audioRoute = routing + } + + override fun onJoinChannelSuccess(channel: String?, uid: Int, elapsed: Int) { + super.onJoinChannelSuccess(channel, uid, elapsed) + isJoined = true + localUid = uid + videoIdList = videoIdList + uid + statsMap[uid] = AudioStatsInfo() + } + + override fun onLeaveChannel(stats: RtcStats?) { + super.onLeaveChannel(stats) + isJoined = false + videoIdList = emptyList() + statsMap.clear() + } + + override fun onUserJoined(uid: Int, elapsed: Int) { + super.onUserJoined(uid, elapsed) + videoIdList = videoIdList + uid + statsMap[uid] = AudioStatsInfo() + } + + override fun onUserOffline(uid: Int, reason: Int) { + super.onUserOffline(uid, reason) + videoIdList = videoIdList - uid + statsMap.remove(uid) + } + + override fun onLocalAudioStats(stats: LocalAudioStats?) { + super.onLocalAudioStats(stats) + statsMap[localUid]?.copy(localAudioStats = stats)?.let { + statsMap[localUid] = it + } + } + + override fun onRemoteAudioStats(stats: RemoteAudioStats?) { + super.onRemoteAudioStats(stats) + val uid = stats?.uid ?: return + statsMap[uid]?.copy(remoteAudioStats = stats)?.let { + statsMap[uid] = it + } + } + } + }).apply { + enableAudio() + setChannelProfile(Constants.CHANNEL_PROFILE_COMMUNICATION) + } + } + DisposableEffect(lifecycleOwner) { + onDispose { + if (isJoined) { + rtcEngine.leaveChannel() + } + RtcEngine.destroy() + } + } + val permissionLauncher = + rememberLauncherForActivityResult(contract = ActivityResultContracts.RequestMultiplePermissions()) { grantedMap -> + val allGranted = grantedMap.values.all { it } + if (allGranted) { + // Permission is granted + Toast.makeText(context, "Permission Granted", Toast.LENGTH_LONG).show() + val mediaOptions = ChannelMediaOptions() + mediaOptions.publishCameraTrack = false + mediaOptions.publishMicrophoneTrack = true + mediaOptions.autoSubscribeAudio = true + mediaOptions.autoSubscribeVideo = false + TokenUtils.gen(channelName, 0) { + rtcEngine.joinChannel(it, channelName, 0, mediaOptions) + } + + } else { + // Permission is denied + Toast.makeText(context, "Permission Denied", Toast.LENGTH_LONG).show() + } + } + + JoinChannelAudioView( + rtcEngine = rtcEngine, + videoIdList = videoIdList, + statsMap = statsMap, + audioRoute = audioRoute, + channelName = channelName, + isJoined = isJoined, + onJoinClick = { + keyboard?.hide() + channelName = it + permissionLauncher.launch( + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { + arrayOf( + Manifest.permission.RECORD_AUDIO, + ) + } else { + arrayOf( + Manifest.permission.RECORD_AUDIO, + Manifest.permission.READ_PHONE_STATE, + Manifest.permission.BLUETOOTH_CONNECT, + ) + } + ) + }, + onLeaveClick = { + rtcEngine.leaveChannel() + } + ) +} + +@Composable +private fun JoinChannelAudioView( + videoIdList: List, + statsMap: Map = mapOf(), + rtcEngine: RtcEngine? = null, + channelName: String = "", + audioRoute: Int = Constants.AUDIO_ROUTE_SPEAKERPHONE, + isJoined: Boolean = false, + onJoinClick: (String) -> Unit = {}, + onLeaveClick: () -> Unit = {} +) { + Column( + Modifier + .fillMaxHeight() + .fillMaxWidth() + ) { + AudioGrid( + modifier = Modifier.height(380.dp), + videoIdList = videoIdList, + statsMap = statsMap + ) + val lazyListState = rememberLazyListState(Int.MAX_VALUE) + + LazyColumn( + modifier = Modifier.weight(1.0f), + verticalArrangement = Arrangement.Bottom, + state = lazyListState + ) { + item { + DropdownMenuRaw( + title = "Audio Route", + options = listOf( + "Speaker" to Constants.AUDIO_ROUTE_SPEAKERPHONE, + "Headset" to Constants.AUDIO_ROUTE_HEADSET, + "Earphone" to Constants.AUDIO_ROUTE_EARPIECE, + "Bluetooth" to Constants.AUDIO_ROUTE_BLUETOOTH_DEVICE_HFP + ), + selectedValue = audioRoute + ) { _, option -> + val ret = rtcEngine?.setRouteInCommunicationMode(option.second) + if (ret != Constants.ERR_OK) { + // no in communication mode + val isSpeakerPhone = option.second == Constants.AUDIO_ROUTE_SPEAKERPHONE + rtcEngine?.setEnableSpeakerphone(isSpeakerPhone) + } + } + } + item { + Column { + var inEarOpen by remember { mutableStateOf(false) } + SwitchRaw(title = "InEar Monitor", checked = inEarOpen) { + inEarOpen = it + rtcEngine?.enableInEarMonitoring(it) + } + if (inEarOpen) { + SliderRaw(title = "InEar Monitor Vol") { + rtcEngine?.setInEarMonitoringVolume((it * 100).toInt()) + } + } + } + } + item { + SliderRaw(title = "Playout Vol"){ + rtcEngine?.adjustPlaybackSignalVolume((it * 100).toInt()) + } + } + item { + SliderRaw(title = "Recording Vol"){ + rtcEngine?.adjustRecordingSignalVolume((it * 100).toInt()) + } + } + item { + DropdownMenuRaw( + title = "Scenario", + options = listOf( + "Default" to Constants.AUDIO_SCENARIO_DEFAULT, + "Game Streaming" to Constants.AUDIO_SCENARIO_GAME_STREAMING, + "Chatroom" to Constants.AUDIO_SCENARIO_CHATROOM, + "Chorus" to Constants.AUDIO_SCENARIO_CHORUS, + "Meeting" to Constants.AUDIO_SCENARIO_MEETING, + ), + selected = 0 + ) { _, option -> + rtcEngine?.setAudioScenario(option.second) + } + } + item { + DropdownMenuRaw( + title = "Audio Profile", + options = listOf( + "Default" to Constants.AUDIO_PROFILE_DEFAULT, + "Speech Standard" to Constants.AUDIO_PROFILE_SPEECH_STANDARD, + "Music Standard" to Constants.AUDIO_PROFILE_MUSIC_STANDARD, + "Music Standard Stereo" to Constants.AUDIO_PROFILE_MUSIC_STANDARD_STEREO, + "Music High Quality" to Constants.AUDIO_PROFILE_MUSIC_HIGH_QUALITY, + "Music High Quality Stereo" to Constants.AUDIO_PROFILE_MUSIC_HIGH_QUALITY_STEREO, + ), + selected = 0 + ) { _, option -> + rtcEngine?.setAudioScenario(option.second) + } + } + item { + DropdownMenuRaw( + title = "Channel Profile", + options = listOf( + "Communication" to Constants.CHANNEL_PROFILE_COMMUNICATION, + "Live Broadcasting" to Constants.CHANNEL_PROFILE_LIVE_BROADCASTING, + "Game" to Constants.CHANNEL_PROFILE_GAME, + "Cloud Gaming" to Constants.CHANNEL_PROFILE_CLOUD_GAMING, + "Communication 1v1" to Constants.CHANNEL_PROFILE_COMMUNICATION_1v1, + "Communication 1v1" to Constants.CHANNEL_PROFILE_COMMUNICATION_1v1, + ), + ) { _, option -> + rtcEngine?.setChannelProfile(option.second) + } + } + } + + ChannelNameInput( + channelName = channelName, + isJoined = isJoined, + onJoinClick = onJoinClick, + onLeaveClick = onLeaveClick + ) + } +} + +@Preview +@Composable +private fun JoinChannelAudioViewPreview() { + JoinChannelAudioView( + listOf(0, 1, 2, 3), + mapOf( + 0 to AudioStatsInfo( + localAudioStats = IRtcEngineEventHandler.LocalAudioStats().apply { + numChannels = 2 + sentSampleRate = 48000 + sentBitrate = 128 + }, + ), + 1 to AudioStatsInfo(), + 2 to AudioStatsInfo(), + 3 to AudioStatsInfo() + ) + ) +} diff --git a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/JoinChannelVideo.kt b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/JoinChannelVideo.kt index 85f7c9fbe..8aeeac6c9 100644 --- a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/JoinChannelVideo.kt +++ b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/JoinChannelVideo.kt @@ -195,7 +195,7 @@ fun JoinChannelVideo() { @Preview @Composable -fun JoinChannelVideoPreview() { +private fun JoinChannelVideoPreview() { JoinChannelVideoView( channelName = "Channel Name", isJoined = false, @@ -207,7 +207,7 @@ fun JoinChannelVideoPreview() { } @Composable -fun JoinChannelVideoView( +private fun JoinChannelVideoView( channelName: String, isJoined: Boolean, onJoinClick: (String) -> Unit, diff --git a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/JoinChannelVideoToken.kt b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/JoinChannelVideoToken.kt index ef86d0338..0322d2823 100644 --- a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/JoinChannelVideoToken.kt +++ b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/JoinChannelVideoToken.kt @@ -198,7 +198,7 @@ fun JoinChannelVideoToken() { @Preview @Composable -fun JoinChannelVideoTokenPreview() { +private fun JoinChannelVideoTokenPreview() { JoinChannelVideoTokenView( channelName = "Channel Name", token = "", @@ -211,7 +211,7 @@ fun JoinChannelVideoTokenPreview() { } @Composable -fun JoinChannelVideoTokenView( +private fun JoinChannelVideoTokenView( channelName: String, token: String, isJoined: Boolean, diff --git a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/JoinMultiChannel.kt b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/JoinMultiChannel.kt index 0ea56263d..1f3a8d7a2 100644 --- a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/JoinMultiChannel.kt +++ b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/JoinMultiChannel.kt @@ -24,7 +24,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.platform.LocalSoftwareKeyboardController +import androidx.compose.ui.res.stringResource import io.agora.api.example.compose.BuildConfig +import io.agora.api.example.compose.R import io.agora.api.example.compose.data.SettingPreferences import io.agora.api.example.compose.ui.common.ChannelNameInput import io.agora.api.example.compose.ui.common.SwitchRaw @@ -244,7 +246,7 @@ fun JoinMultiChannel() { } @Composable -fun JoinMultiChannelView( +private fun JoinMultiChannelView( rtcEngine: RtcEngineEx?, channelName: String, videoList: List, @@ -299,7 +301,7 @@ fun JoinMultiChannelView( }, enabled = exVideoList.isNotEmpty() ) { - Text(text = "Ex频道截图") + Text(text = stringResource(id = R.string.snapshot_ex)) } Button( onClick = { @@ -311,7 +313,9 @@ fun JoinMultiChannelView( }, enabled = isJoin ) { - Text(text = if (isExJoin) "离开Ex频道" else "加入Ex频道") + Text(text = if (isExJoin) stringResource(id = R.string.leave_ex) else stringResource( + id = R.string.join_ex + )) } } } @@ -326,9 +330,9 @@ fun JoinMultiChannelView( if (exWillLeave) { var stopMic by rememberSaveable { mutableStateOf(false) } AlertDialog( - title = { Text("离开选项") }, + title = { Text(stringResource(id = R.string.leave_option)) }, text = { - SwitchRaw(title = "是否停止录音", checked = stopMic) { + SwitchRaw(title = stringResource(id = R.string.stop_recording_or_not), checked = stopMic) { stopMic = it } }, @@ -338,12 +342,12 @@ fun JoinMultiChannelView( exWillLeave = false onExLeaveClick(stopMic) }) { - Text("确定") + Text(stringResource(id = R.string.confirm)) } }, dismissButton = { TextButton(onClick = { exWillLeave = false }) { - Text("取消") + Text(stringResource(id = R.string.cancel)) } } ) diff --git a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/LiveStreaming.kt b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/LiveStreaming.kt index c383b5836..d827cb97e 100644 --- a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/LiveStreaming.kt +++ b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/LiveStreaming.kt @@ -30,10 +30,12 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.platform.LocalSoftwareKeyboardController +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import io.agora.api.example.compose.BuildConfig +import io.agora.api.example.compose.R import io.agora.api.example.compose.data.SettingPreferences import io.agora.api.example.compose.ui.common.ChannelNameInput import io.agora.api.example.compose.ui.common.DropdownMenuRaw @@ -236,7 +238,7 @@ fun LiveStreaming() { @OptIn(ExperimentalMaterial3Api::class) @Composable -fun LiveStreamingView( +private fun LiveStreamingView( rtcEngine: RtcEngine? = null, channelName: String, isJoined: Boolean, @@ -360,7 +362,7 @@ fun LiveStreamingView( @Preview @Composable -fun LiveStreamingViewPreview() { +private fun LiveStreamingViewPreview() { Box { LiveStreamingView( channelName = "Channel Name", @@ -379,7 +381,7 @@ fun LiveStreamingViewPreview() { @Composable -fun LiveStreamingSettingView( +private fun LiveStreamingSettingView( modifier: Modifier = Modifier, rtcEngine: RtcEngine? = null, role: Int = Constants.CLIENT_ROLE_AUDIENCE, @@ -395,7 +397,7 @@ fun LiveStreamingSettingView( ) { if (role == Constants.CLIENT_ROLE_AUDIENCE) { SwitchRaw( - title = "开启极速直播", + title = stringResource(id = R.string.open_low_latency_live), checked = values["living"] as? Boolean ?: false ) { enable -> onValueChanged("living", enable) @@ -409,7 +411,7 @@ fun LiveStreamingSettingView( if (role == Constants.CLIENT_ROLE_BROADCASTER) { Divider(modifier = Modifier.padding(horizontal = 16.dp)) SwitchRaw( - title = "水印", + title = stringResource(id = R.string.watermark), checked = values["watermark"] as? Boolean ?: false ) { enable -> onValueChanged("watermark", enable) @@ -430,7 +432,7 @@ fun LiveStreamingSettingView( Divider(modifier = Modifier.padding(horizontal = 16.dp)) SwitchRaw( - title = "小流", + title = stringResource(id = R.string.low_stream), checked = values["stream"] as? Boolean ?: false ) { enable -> onValueChanged("stream", enable) @@ -446,11 +448,11 @@ fun LiveStreamingSettingView( Divider(modifier = Modifier.padding(horizontal = 16.dp)) DropdownMenuRaw( - title = "编码类型", + title = stringResource(id = R.string.encoder_type), options = listOf( - "自动选择" to VideoEncoderConfiguration.ENCODING_PREFERENCE.PREFER_AUTO, - "硬编" to VideoEncoderConfiguration.ENCODING_PREFERENCE.PREFER_HARDWARE, - "软编" to VideoEncoderConfiguration.ENCODING_PREFERENCE.PREFER_SOFTWARE, + stringResource(id = R.string.encoder_auto) to VideoEncoderConfiguration.ENCODING_PREFERENCE.PREFER_AUTO, + stringResource(id = R.string.encoder_hardware) to VideoEncoderConfiguration.ENCODING_PREFERENCE.PREFER_HARDWARE, + stringResource(id = R.string.encoder_software) to VideoEncoderConfiguration.ENCODING_PREFERENCE.PREFER_SOFTWARE, ), selected = values["encoder"] as? Int ?: 0 ) { index, option -> @@ -465,7 +467,7 @@ fun LiveStreamingSettingView( Divider(modifier = Modifier.padding(horizontal = 16.dp)) SwitchRaw( - title = "B帧", + title = stringResource(id = R.string.b_frame), checked = values["BFrame"] as? Boolean ?: false ) { enable -> onValueChanged("BFrame", enable) @@ -484,7 +486,7 @@ fun LiveStreamingSettingView( Divider(modifier = Modifier.padding(horizontal = 16.dp)) SwitchRaw( - title = "垫片", + title = stringResource(id = R.string.video_image), checked = values["padding"] as? Boolean ?: false ) { enable -> onValueChanged("padding", enable) diff --git a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/LocalVideoTranscoding.kt b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/LocalVideoTranscoding.kt index 041a0fbec..8996bcd38 100644 --- a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/LocalVideoTranscoding.kt +++ b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/LocalVideoTranscoding.kt @@ -21,8 +21,10 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.platform.LocalSoftwareKeyboardController +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import io.agora.api.example.compose.BuildConfig +import io.agora.api.example.compose.R import io.agora.api.example.compose.data.SettingPreferences import io.agora.api.example.compose.ui.common.ChannelNameInput import io.agora.api.example.compose.ui.common.SwitchRaw @@ -279,7 +281,7 @@ fun LocalVideoTranscoding() { } @Composable -fun LocalVideoTranscodingView( +private fun LocalVideoTranscodingView( channelName: String, isJoined: Boolean, localUid: Int = 0, @@ -304,7 +306,7 @@ fun LocalVideoTranscodingView( remoteRender = { view, id, _ -> remoteRender(view, id) } ) Spacer(modifier = Modifier.weight(1.0f)) - SwitchRaw(title = "透明背景"){ + SwitchRaw(title = stringResource(id = R.string.alpha_background)){ onAlphaBackground(it) } ChannelNameInput( diff --git a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/MediaMetadata.kt b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/MediaMetadata.kt index 18f3f4eeb..5c431f8bc 100644 --- a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/MediaMetadata.kt +++ b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/MediaMetadata.kt @@ -21,7 +21,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.platform.LocalSoftwareKeyboardController +import androidx.compose.ui.res.stringResource import io.agora.api.example.compose.BuildConfig +import io.agora.api.example.compose.R import io.agora.api.example.compose.data.SettingPreferences import io.agora.api.example.compose.ui.common.ChannelNameInput import io.agora.api.example.compose.ui.common.InputRaw @@ -35,6 +37,7 @@ import io.agora.rtc2.IMetadataObserver import io.agora.rtc2.IRtcEngineEventHandler import io.agora.rtc2.RtcEngine import io.agora.rtc2.RtcEngineConfig +import io.agora.rtc2.video.AgoraMetadata import io.agora.rtc2.video.VideoCanvas import io.agora.rtc2.video.VideoEncoderConfiguration @@ -168,8 +171,8 @@ fun MediaMetadata() { return data } - override fun onMetadataReceived(buffer: ByteArray?, uid: Int, timeStampMs: Long) { - receivedMetadata = buffer?.toString(Charsets.UTF_8) ?: "" + override fun onMetadataReceived(metadata: AgoraMetadata) { + receivedMetadata = metadata.data?.toString(Charsets.UTF_8) ?: "" } }, IMetadataObserver.VIDEO_METADATA) @@ -256,7 +259,7 @@ fun MediaMetadata() { @Composable -fun MediaMetadataView( +private fun MediaMetadataView( rtcEngine: RtcEngine? = null, channelName: String, isJoined: Boolean, @@ -306,8 +309,8 @@ fun MediaMetadataView( InputRaw( text = "", - label = "视频元数据", - btnText = "发送", + label = stringResource(id = R.string.metadata_video), + btnText = stringResource(id = R.string.send), enable = isJoined ) { onVideoMetadataSend(it) diff --git a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/MediaPlayer.kt b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/MediaPlayer.kt index 9017098b5..106c0b5bf 100644 --- a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/MediaPlayer.kt +++ b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/MediaPlayer.kt @@ -23,9 +23,11 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.platform.LocalSoftwareKeyboardController +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import io.agora.api.example.compose.BuildConfig +import io.agora.api.example.compose.R import io.agora.api.example.compose.data.SettingPreferences import io.agora.api.example.compose.ui.common.ChannelNameInput import io.agora.api.example.compose.ui.common.InputRaw @@ -330,7 +332,7 @@ fun MediaPlayer() { } @Composable -fun MediaPlayerView( +private fun MediaPlayerView( channelName: String, isJoined: Boolean, localUid: Int = 0, @@ -375,7 +377,7 @@ fun MediaPlayerView( enabled = isJoined && playerReady, onClick = onPlayClick ) { - Text(text = "播放") + Text(text = stringResource(id = R.string.play)) } Button( modifier = Modifier @@ -384,7 +386,7 @@ fun MediaPlayerView( enabled = isJoined && playerReady, onClick = onStopClick ) { - Text(text = "停止") + Text(text = stringResource(id = R.string.stop)) } Button( modifier = Modifier @@ -393,7 +395,7 @@ fun MediaPlayerView( enabled = isJoined && playerReady, onClick = onPauseClick ) { - Text(text = "暂停") + Text(text = stringResource(id = R.string.pause)) } Button( modifier = Modifier @@ -402,7 +404,7 @@ fun MediaPlayerView( enabled = isJoined && playerReady, onClick = onPushClick ) { - Text(text = "推送") + Text(text = stringResource(id = R.string.publish)) } } @@ -434,7 +436,7 @@ fun MediaPlayerView( @Preview @Composable -fun MediaPlayerViewPreview() { +private fun MediaPlayerViewPreview() { MediaPlayerView(channelName = "", isJoined = false, onJoinClick = {}) { } diff --git a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/MediaRecorder.kt b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/MediaRecorder.kt index ef809a1ec..c8514fe42 100644 --- a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/MediaRecorder.kt +++ b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/MediaRecorder.kt @@ -26,9 +26,11 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.platform.LocalSoftwareKeyboardController +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import io.agora.api.example.compose.BuildConfig +import io.agora.api.example.compose.R import io.agora.api.example.compose.data.SettingPreferences import io.agora.api.example.compose.ui.common.ChannelNameInput import io.agora.api.example.compose.ui.common.VideoGrid @@ -264,7 +266,7 @@ fun MediaRecorder() { TextButton(onClick = { recoderResult = "" }) { - Text("确定") + Text(stringResource(id = R.string.confirm)) } }, title = { Text(text = "Recorder Result") }, @@ -274,7 +276,7 @@ fun MediaRecorder() { } @Composable -fun MediaRecorderView( +private fun MediaRecorderView( channelName: String, isJoined: Boolean, onJoinClick: (String) -> Unit, @@ -301,7 +303,9 @@ fun MediaRecorderView( onRecorderClick(id, isRecording) }) { - Text(text = if (!isRecording) "Start Recording" else "Stop Recording") + Text(text = if (!isRecording) stringResource(id = R.string.start_recording) else stringResource( + id = R.string.stop_recording + )) } } ) @@ -312,7 +316,7 @@ fun MediaRecorderView( onClick = onCameraSwitchClick, enabled = isJoined ) { - Text(text = "Switch Camera") + Text(text = stringResource(id = R.string.switch_camera)) } ChannelNameInput( channelName = channelName, @@ -325,7 +329,7 @@ fun MediaRecorderView( @Preview @Composable -fun MediaRecorderViewPreview() { +private fun MediaRecorderViewPreview() { MediaRecorderView( channelName = "", isJoined = false, diff --git a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/OriginAudioData.kt b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/OriginAudioData.kt index cc3696c30..dce16b412 100644 --- a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/OriginAudioData.kt +++ b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/OriginAudioData.kt @@ -25,8 +25,10 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.platform.LocalSoftwareKeyboardController +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import io.agora.api.example.compose.BuildConfig +import io.agora.api.example.compose.R import io.agora.api.example.compose.data.SettingPreferences import io.agora.api.example.compose.ui.common.AudioGrid import io.agora.api.example.compose.ui.common.AudioStatsInfo @@ -171,7 +173,7 @@ fun OriginAudioData() { } @Composable -fun OriginAudioDataView( +private fun OriginAudioDataView( videoIdList: List, statsMap: Map = mapOf(), channelName: String = "", @@ -193,7 +195,7 @@ fun OriginAudioDataView( Spacer(modifier = Modifier.weight(1.0f)) - SwitchRaw(title = "音频回写", enable = isJoined, onCheckedChange = onRewriteOpen) + SwitchRaw(title = stringResource(id = R.string.audio_rewrite), enable = isJoined, onCheckedChange = onRewriteOpen) ChannelNameInput( channelName = channelName, @@ -204,7 +206,7 @@ fun OriginAudioDataView( } } -class OriginAudioDataRewriter( +private class OriginAudioDataRewriter( private val context: Context, private val rtcEngine: RtcEngine ) : IAudioFrameObserver { @@ -351,7 +353,8 @@ class OriginAudioDataRewriter( samplesPerSec: Int, buffer: ByteBuffer?, renderTimeMs: Long, - avsync_type: Int + avsync_type: Int, + rtpTimestamp: Int ) = false override fun getObservedAudioFramePosition() = diff --git a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/OriginVideoData.kt b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/OriginVideoData.kt index a75c784bc..14fab7bac 100644 --- a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/OriginVideoData.kt +++ b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/OriginVideoData.kt @@ -233,7 +233,7 @@ fun OriginVideoData() { } @Composable -fun OriginVideoDataView( +private fun OriginVideoDataView( channelName: String, isJoined: Boolean, rtcEngine: RtcEngine? = null, @@ -298,7 +298,7 @@ fun OriginVideoDataView( } } -class OriginVideoDataScreenshotTaker( +private class OriginVideoDataScreenshotTaker( private val context: Context, private val rtcEngine: RtcEngine ) : IVideoFrameObserver { diff --git a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/PictureInPicture.kt b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/PictureInPicture.kt new file mode 100644 index 000000000..06d045b00 --- /dev/null +++ b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/PictureInPicture.kt @@ -0,0 +1,340 @@ +package io.agora.api.example.compose.samples + +import android.app.AppOpsManager +import android.app.PictureInPictureParams +import android.content.Intent +import android.graphics.RectF +import android.os.Build +import android.os.Bundle +import android.os.Process +import android.util.Rational +import android.widget.Toast +import androidx.activity.ComponentActivity +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.compose.setContent +import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.consumeWindowInsets +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.safeDrawing +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.toAndroidRectF +import androidx.compose.ui.layout.boundsInWindow +import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalLifecycleOwner +import androidx.compose.ui.platform.LocalSoftwareKeyboardController +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.lifecycle.DefaultLifecycleObserver +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleOwner +import io.agora.api.example.compose.BuildConfig +import io.agora.api.example.compose.R +import io.agora.api.example.compose.data.SettingPreferences +import io.agora.api.example.compose.ui.common.APIExampleScaffold +import io.agora.api.example.compose.ui.common.ChannelNameInput +import io.agora.api.example.compose.ui.common.TwoVideoView +import io.agora.api.example.compose.ui.common.TwoVideoViewType +import io.agora.api.example.compose.ui.common.VideoStatsInfo +import io.agora.api.example.compose.ui.theme.APIExampleComposeTheme +import io.agora.api.example.compose.utils.TokenUtils +import io.agora.rtc2.ChannelMediaOptions +import io.agora.rtc2.Constants +import io.agora.rtc2.IRtcEngineEventHandler +import io.agora.rtc2.RtcEngine +import io.agora.rtc2.RtcEngineConfig +import io.agora.rtc2.video.VideoCanvas +import io.agora.rtc2.video.VideoEncoderConfiguration + + +@Composable +fun PictureInPictureEntrance(back: () -> Unit) { + val context = LocalContext.current + val intent = Intent(context, PictureInPictureActivity::class.java) + context.startActivity(intent) + back() +} + +@Composable +private fun PictureInPicture() { + val context = LocalContext.current as ComponentActivity + var isPipOn by rememberSaveable { mutableStateOf(false) } + val lifecycleOwner = LocalLifecycleOwner.current + val keyboard = LocalSoftwareKeyboardController.current + var isJoined by rememberSaveable { mutableStateOf(false) } + var channelName by rememberSaveable { mutableStateOf("") } + var localUid by rememberSaveable { mutableIntStateOf(0) } + var remoteUid by rememberSaveable { mutableIntStateOf(0) } + var localStats by remember { mutableStateOf(VideoStatsInfo()) } + var remoteStats by remember { mutableStateOf(VideoStatsInfo()) } + val videoViewBound = remember { RectF() } + + val rtcEngine = remember { + RtcEngine.create(RtcEngineConfig().apply { + mAreaCode = SettingPreferences.getArea() + mContext = context + mAppId = BuildConfig.AGORA_APP_ID + mEventHandler = object : IRtcEngineEventHandler() { + override fun onJoinChannelSuccess(channel: String?, uid: Int, elapsed: Int) { + super.onJoinChannelSuccess(channel, uid, elapsed) + isJoined = true + localUid = uid + } + + override fun onLeaveChannel(stats: RtcStats?) { + super.onLeaveChannel(stats) + isJoined = false + localUid = 0 + localStats = VideoStatsInfo() + remoteUid = 0 + remoteStats = VideoStatsInfo() + } + + override fun onUserJoined(uid: Int, elapsed: Int) { + super.onUserJoined(uid, elapsed) + remoteUid = uid + remoteStats = VideoStatsInfo() + } + + override fun onUserOffline(uid: Int, reason: Int) { + super.onUserOffline(uid, reason) + if (remoteUid == uid) { + remoteUid = 0 + remoteStats = VideoStatsInfo() + } + } + + override fun onRtcStats(stats: RtcStats?) { + super.onRtcStats(stats) + localStats.copy(rtcStats = stats).let { + localStats = it + } + } + + override fun onLocalVideoStats( + source: Constants.VideoSourceType?, + stats: LocalVideoStats? + ) { + super.onLocalVideoStats(source, stats) + localStats.copy(localVideoStats = stats).let { + localStats = it + } + } + + override fun onLocalAudioStats(stats: LocalAudioStats?) { + super.onLocalAudioStats(stats) + localStats.copy(localAudioStats = stats).let { + localStats = it + } + } + + override fun onRemoteVideoStats(stats: RemoteVideoStats?) { + super.onRemoteVideoStats(stats) + val uid = stats?.uid ?: return + if (remoteUid == uid) { + remoteStats.copy(remoteVideoStats = stats).let { + remoteStats = it + } + } + } + + override fun onRemoteAudioStats(stats: RemoteAudioStats?) { + super.onRemoteAudioStats(stats) + val uid = stats?.uid ?: return + if (remoteUid == uid) { + remoteStats.copy(remoteAudioStats = stats).let { + remoteStats = it + } + } + } + } + }).apply { + setVideoEncoderConfiguration( + VideoEncoderConfiguration( + SettingPreferences.getVideoDimensions(), + SettingPreferences.getVideoFrameRate(), + VideoEncoderConfiguration.STANDARD_BITRATE, + SettingPreferences.getOrientationMode() + ) + ) + enableVideo() + } + } + LaunchedEffect(lifecycleOwner) { + context.addOnPictureInPictureModeChangedListener { info -> + isPipOn = info.isInPictureInPictureMode + if (lifecycleOwner.lifecycle.currentState < Lifecycle.State.STARTED) { + context.finish() + } + } + lifecycleOwner.lifecycle.addObserver(object: DefaultLifecycleObserver { + override fun onDestroy(owner: LifecycleOwner) { + rtcEngine.stopPreview() + rtcEngine.leaveChannel() + RtcEngine.destroy() + } + }) + } + val permissionLauncher = + rememberLauncherForActivityResult(contract = ActivityResultContracts.RequestMultiplePermissions()) { grantedMap -> + val allGranted = grantedMap.values.all { it } + if (allGranted) { + // Permission is granted + Toast.makeText(context, "Permission Granted", Toast.LENGTH_LONG).show() + val mediaOptions = ChannelMediaOptions() + mediaOptions.channelProfile = Constants.CHANNEL_PROFILE_LIVE_BROADCASTING + mediaOptions.clientRoleType = Constants.CLIENT_ROLE_BROADCASTER + mediaOptions.autoSubscribeAudio = true + mediaOptions.autoSubscribeAudio = true + mediaOptions.publishCameraTrack = true + mediaOptions.publishMicrophoneTrack = true + TokenUtils.gen(channelName, 0) { + rtcEngine.joinChannel(it, channelName, 0, mediaOptions) + } + } else { + // Permission is denied + Toast.makeText(context, "Permission Denied", Toast.LENGTH_LONG).show() + } + } + + val videoView: @Composable () -> Unit = { + TwoVideoView( + modifier = Modifier + .height(350.dp) + .onGloballyPositioned { + videoViewBound.set( + it + .boundsInWindow() + .toAndroidRectF() + ) + }, + type = TwoVideoViewType.Row, + localUid = localUid, + remoteUid = remoteUid, + localStats = localStats, + remoteStats = remoteStats, + localRender = { view, id, _ -> + rtcEngine.setupLocalVideo( + VideoCanvas( + view, + Constants.RENDER_MODE_HIDDEN, + id + ) + ) + rtcEngine.startPreview() + }, + remoteRender = { view, id, _ -> + rtcEngine.setupRemoteVideo( + VideoCanvas( + view, + Constants.RENDER_MODE_HIDDEN, + id + ) + ) + }) + } + + if (isPipOn) { + videoView() + } else { + APIExampleComposeTheme { + APIExampleScaffold( + topBarTitle = stringResource(id = R.string.example_pictureinpicture), + showSettingIcon = false, + showBackNavigationIcon = true, + onBackClick = { context.finish() }, + ) { paddingValues -> + Column( + modifier = Modifier + .fillMaxSize() + .consumeWindowInsets(WindowInsets.safeDrawing) + .padding(paddingValues) + ) { + videoView() + Spacer(modifier = Modifier.weight(1f)) + + Button( + modifier = Modifier.padding(16.dp, 8.dp), + enabled = isJoined, + onClick = { + if (Build.VERSION.SDK_INT >= 26) { + val appOpsManager: AppOpsManager = + context.getSystemService(AppOpsManager::class.java) + if (appOpsManager.checkOpNoThrow( + AppOpsManager.OPSTR_PICTURE_IN_PICTURE, + Process.myUid(), + context.packageName + ) == AppOpsManager.MODE_ALLOWED + ) { + context.enterPictureInPictureMode( + PictureInPictureParams.Builder() + .setAspectRatio( + Rational( + videoViewBound.width().toInt(), + videoViewBound.height().toInt() + ) + ) + .build() + ) + val homeIntent = Intent(Intent.ACTION_MAIN) + homeIntent.addCategory(Intent.CATEGORY_HOME) + context.startActivity(homeIntent) + isPipOn = true + } + } + } + ) { + Text(text = "Enter Picture-in-Picture Mode") + } + + ChannelNameInput( + channelName = channelName, + isJoined = isJoined, + onJoinClick = { + channelName = it + keyboard?.hide() + permissionLauncher.launch( + arrayOf( + android.Manifest.permission.RECORD_AUDIO, + android.Manifest.permission.CAMERA + ) + ) + }, + onLeaveClick = { + rtcEngine.stopPreview() + rtcEngine.leaveChannel() + } + ) + } + } + } + } + +} + + +class PictureInPictureActivity : ComponentActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContent { + PictureInPicture() + } + } + +} \ No newline at end of file diff --git a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/PlayAudioFiles.kt b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/PlayAudioFiles.kt index f1e249fcf..a567c743e 100644 --- a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/PlayAudioFiles.kt +++ b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/PlayAudioFiles.kt @@ -26,10 +26,12 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.platform.LocalSoftwareKeyboardController +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import io.agora.api.example.compose.BuildConfig +import io.agora.api.example.compose.R import io.agora.api.example.compose.data.SettingPreferences import io.agora.api.example.compose.ui.common.AudioGrid import io.agora.api.example.compose.ui.common.AudioStatsInfo @@ -160,7 +162,7 @@ fun PlayAudioFiles() { @Composable -fun PlayAudioFilesView( +private fun PlayAudioFilesView( videoIdList: List, statsMap: Map = mapOf(), rtcEngine: RtcEngine? = null, @@ -192,7 +194,7 @@ fun PlayAudioFilesView( .padding(16.dp, 0.dp) ) { Text( - text = "开始", + text = stringResource(id = R.string.start), style = MaterialTheme.typography.bodySmall, color = Color.Blue, textAlign = TextAlign.Center, @@ -209,7 +211,7 @@ fun PlayAudioFilesView( } ) Text( - text = "恢复播放", + text = stringResource(id = R.string.resume), style = MaterialTheme.typography.bodySmall, color = Color.Blue, textAlign = TextAlign.Center, @@ -221,7 +223,7 @@ fun PlayAudioFilesView( } ) Text( - text = "暂停", + text = stringResource(id = R.string.pause), style = MaterialTheme.typography.bodySmall, color = Color.Blue, textAlign = TextAlign.Center, @@ -233,7 +235,7 @@ fun PlayAudioFilesView( } ) Text( - text = "停止", + text = stringResource(id = R.string.stop), style = MaterialTheme.typography.bodySmall, color = Color.Blue, textAlign = TextAlign.Center, @@ -246,13 +248,13 @@ fun PlayAudioFilesView( ) } - SliderRaw(title = "混音音量", value = 1.0f) { + SliderRaw(title = stringResource(id = R.string.audio_mixing_volume), value = 1.0f) { rtcEngine?.adjustAudioMixingVolume((100 * it).toInt()) } - SliderRaw(title = "混音播放音量", value = 1.0f) { + SliderRaw(title = stringResource(id = R.string.audio_mixing_playout_volume), value = 1.0f) { rtcEngine?.adjustAudioMixingPlayoutVolume((100 * it).toInt()) } - SliderRaw(title = "混音发布音量", value = 1.0f) { + SliderRaw(title = stringResource(id = R.string.audio_mixing_publish_volume), value = 1.0f) { rtcEngine?.adjustAudioMixingPublishVolume((100 * it).toInt()) } } @@ -270,7 +272,7 @@ fun PlayAudioFilesView( .fillMaxWidth() .padding(16.dp, 0.dp) ) { - Text(text = "开始", + Text(text = stringResource(id = R.string.start), style = MaterialTheme.typography.bodySmall, color = Color.Blue, textAlign = TextAlign.Center, @@ -288,7 +290,7 @@ fun PlayAudioFilesView( true // Sets whether to publish the audio effect. ); }) - Text(text = "恢复播放", + Text(text = stringResource(id = R.string.resume), style = MaterialTheme.typography.bodySmall, color = Color.Blue, textAlign = TextAlign.Center, @@ -298,7 +300,7 @@ fun PlayAudioFilesView( .clickable { rtcEngine?.resumeEffect(EFFECT_SOUND_ID) }) - Text(text = "暂停", + Text(text = stringResource(id = R.string.pause), style = MaterialTheme.typography.bodySmall, color = Color.Blue, textAlign = TextAlign.Center, @@ -308,7 +310,7 @@ fun PlayAudioFilesView( .clickable { rtcEngine?.pauseEffect(EFFECT_SOUND_ID) }) - Text(text = "停止", + Text(text = stringResource(id = R.string.stop), style = MaterialTheme.typography.bodySmall, color = Color.Blue, textAlign = TextAlign.Center, @@ -320,7 +322,7 @@ fun PlayAudioFilesView( }) } - SliderRaw(title = "音效音量", value = 1.0f) { + SliderRaw(title = stringResource(id = R.string.effects_volume), value = 1.0f) { rtcEngine?.setEffectsVolume((100 * it).toDouble()) } } @@ -365,6 +367,6 @@ fun PlayAudioFilesView( @Preview @Composable -fun PlayAudioFilesViewPreview() { +private fun PlayAudioFilesViewPreview() { PlayAudioFilesView(videoIdList = listOf(1, 2)) } \ No newline at end of file diff --git a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/PreCallTest.kt b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/PreCallTest.kt index 2f47bd60a..f91338fe2 100644 --- a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/PreCallTest.kt +++ b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/PreCallTest.kt @@ -31,9 +31,11 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalLifecycleOwner +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import io.agora.api.example.compose.BuildConfig +import io.agora.api.example.compose.R import io.agora.api.example.compose.data.SettingPreferences import io.agora.api.example.compose.ui.common.VideoCell import io.agora.rtc2.Constants @@ -184,7 +186,7 @@ fun PreCallTest() { } @Composable -fun PreCallTestView( +private fun PreCallTestView( isAudioEchoPretesting: Boolean = false, isVideoEchoPretesting: Boolean = false, isNetworkTesting: Boolean = false, @@ -241,7 +243,7 @@ fun PreCallTestView( } Text( modifier = Modifier.padding(0.dp, 8.dp), - text = "Tip:开始测试后请对着麦克风讲话,讲话声音在${ECHO_TEST_INTERVAL_IN_SECONDS}秒后播放则测试正常。", + text = stringResource(id = R.string.audio_echo_pretest_tip, ECHO_TEST_INTERVAL_IN_SECONDS), style = MaterialTheme.typography.bodySmall ) @@ -274,7 +276,7 @@ fun PreCallTestView( @Preview @Composable -fun PreCallTestViewPreview() { +private fun PreCallTestViewPreview() { PreCallTestView( isVideoEchoPretesting = true ) diff --git a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/RTMPStreaming.kt b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/RTMPStreaming.kt index a8ea49125..dd9f718cd 100644 --- a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/RTMPStreaming.kt +++ b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/RTMPStreaming.kt @@ -1,5 +1,7 @@ package io.agora.api.example.compose.samples +import android.os.Handler +import android.util.Log import android.widget.Toast import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts @@ -16,8 +18,10 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.platform.LocalSoftwareKeyboardController +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import io.agora.api.example.compose.BuildConfig +import io.agora.api.example.compose.R import io.agora.api.example.compose.data.SettingPreferences import io.agora.api.example.compose.ui.common.ChannelNameInput import io.agora.api.example.compose.ui.common.InputRaw @@ -31,7 +35,6 @@ import io.agora.rtc2.Constants import io.agora.rtc2.IRtcEngineEventHandler import io.agora.rtc2.RtcEngine import io.agora.rtc2.RtcEngineConfig -import io.agora.rtc2.SimulcastStreamConfig import io.agora.rtc2.live.LiveTranscoding import io.agora.rtc2.video.VideoCanvas import io.agora.rtc2.video.VideoEncoderConfiguration @@ -41,6 +44,7 @@ fun RTMPStreaming() { val context = LocalContext.current val lifecycleOwner = LocalLifecycleOwner.current val keyboard = LocalSoftwareKeyboardController.current + val mainHandler = remember { Handler(context.mainLooper) } var isJoined by rememberSaveable { mutableStateOf(false) } var localLarge by rememberSaveable { mutableStateOf(true) } var channelName by rememberSaveable { mutableStateOf("") } @@ -147,6 +151,25 @@ fun RTMPStreaming() { super.onClientRoleChanged(oldRole, newRole, newRoleOptions) clientRole = newRole } + + override fun onRtmpStreamingStateChanged(url: String?, state: Int, reason: Int) { + super.onRtmpStreamingStateChanged(url, state, reason) + Log.d("RTMPStreaming", "onRtmpStreamingStateChanged: $url, $state, $reason") + if (state == Constants.RTMP_STREAM_PUBLISH_STATE_IDLE) { + pushing = false + } else if (state == Constants.RTMP_STREAM_PUBLISH_STATE_RUNNING) { + pushing = true + } + } + + override fun onRtmpStreamingEvent(url: String?, event: Int) { + super.onRtmpStreamingEvent(url, event) + if (event == Constants.RTMP_STREAMING_EVENT_URL_ALREADY_IN_USE) { + mainHandler.post { + Toast.makeText(context, "URL already in use: $url", Toast.LENGTH_LONG).show() + } + } + } } }).apply { engine = this @@ -159,14 +182,6 @@ fun RTMPStreaming() { ) ) enableVideo() - setDualStreamMode( - Constants.SimulcastStreamMode.ENABLE_SIMULCAST_STREAM, - SimulcastStreamConfig( - VideoEncoderConfiguration.VideoDimensions( - 100, 100 - ), 100, 15 - ) - ) } } DisposableEffect(lifecycleOwner) { @@ -222,7 +237,6 @@ fun RTMPStreaming() { pushing = !pushing if (pushing) { - rtcEngine?.stopRtmpStream(url) if (transcoding) { rtcEngine?.startRtmpStreamWithTranscoding(url, getRtmpStreamTranscoding(localUid, remoteUid) @@ -256,7 +270,7 @@ fun RTMPStreaming() { } @Composable -fun RTMPStreamingView( +private fun RTMPStreamingView( rtcEngine: RtcEngine? = null, channelName: String, isJoined: Boolean, @@ -307,7 +321,7 @@ fun RTMPStreamingView( } ) - SwitchRaw(title = "是否转码", checked = transcoding) { + SwitchRaw(title = stringResource(id = R.string.transcoding_or_not), checked = transcoding) { onTranscoding() } ChannelNameInput( @@ -318,8 +332,10 @@ fun RTMPStreamingView( ) InputRaw( text = url, - label = "推流地址", - btnText = if (pushing) "关闭推流" else "开始推流", + label = stringResource(id = R.string.rtmp_url), + btnText = if (pushing) stringResource(id = R.string.stop_publish_stream) else stringResource( + id = R.string.start_publish_stream + ), enable = isJoined, editable = !pushing ) { u -> @@ -333,25 +349,25 @@ private fun getRtmpStreamTranscoding( remoteUid: Int ): LiveTranscoding { return LiveTranscoding().apply { - width = 640 - height = 360 + width = 360 + height = 640 videoBitrate = 400 videoFramerate = 15 addUser(LiveTranscoding.TranscodingUser().apply { uid = localUid x = 0 y = 0 - width = 640 - height = 180 + width = 360 + height = 320 zOrder = 1 }) if (remoteUid != 0) { addUser(LiveTranscoding.TranscodingUser().apply { uid = remoteUid x = 0 - y = 180 - width = 640 - height = 180 + y = 320 + width = 360 + height = 320 zOrder = 2 }) } @@ -360,7 +376,7 @@ private fun getRtmpStreamTranscoding( @Preview @Composable -fun RTMPStreamingViewPreview() { +private fun RTMPStreamingViewPreview() { RTMPStreamingView( channelName = "test", isJoined = true diff --git a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/RhythmPlayer.kt b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/RhythmPlayer.kt index f91be3f69..2d0baab9f 100644 --- a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/RhythmPlayer.kt +++ b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/RhythmPlayer.kt @@ -18,8 +18,10 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.platform.LocalSoftwareKeyboardController +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import io.agora.api.example.compose.BuildConfig +import io.agora.api.example.compose.R import io.agora.api.example.compose.data.SettingPreferences import io.agora.api.example.compose.ui.common.ChannelNameInput import io.agora.api.example.compose.ui.common.SliderRaw @@ -102,8 +104,8 @@ fun RhythmPlayer() { mediaOptions.clientRoleType = Constants.CLIENT_ROLE_BROADCASTER mediaOptions.autoSubscribeAudio = true mediaOptions.autoSubscribeVideo = true - mediaOptions.publishRhythmPlayerTrack = true - mediaOptions.publishMicrophoneTrack = false + mediaOptions.publishRhythmPlayerTrack = isPlaying + mediaOptions.publishMicrophoneTrack = true mediaOptions.publishCameraTrack = false TokenUtils.gen(channelName, 0) { rtcEngine.joinChannel(it, channelName, 0, mediaOptions) @@ -115,7 +117,7 @@ fun RhythmPlayer() { } @Composable -fun RhythmPlayerView( +private fun RhythmPlayerView( rtcEngine: RtcEngine? = null, channelName: String, isJoined: Boolean, @@ -151,9 +153,14 @@ fun RhythmPlayerView( .padding(8.dp, 0.dp), onClick = { rtcEngine?.startRhythmPlayer(URL_UPBEAT, URL_DOWNBEAT, config) + if(isJoined){ + rtcEngine?.updateChannelMediaOptions(ChannelMediaOptions().apply { + publishRhythmPlayerTrack = true + }) + } } ) { - Text(text = "Play") + Text(text = stringResource(id = R.string.play)) } Button( modifier = Modifier @@ -161,10 +168,15 @@ fun RhythmPlayerView( .padding(8.dp, 0.dp), onClick = { rtcEngine?.stopRhythmPlayer() + if(isJoined){ + rtcEngine?.updateChannelMediaOptions(ChannelMediaOptions().apply { + publishRhythmPlayerTrack = false + }) + } onPlayStopClick() } ) { - Text(text = "Stop") + Text(text = stringResource(id = R.string.stop)) } } } diff --git a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/ScreenSharing.kt b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/ScreenSharing.kt index 31f604304..1c769762d 100644 --- a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/ScreenSharing.kt +++ b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/ScreenSharing.kt @@ -20,9 +20,11 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.platform.LocalSoftwareKeyboardController +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import io.agora.api.example.compose.BuildConfig +import io.agora.api.example.compose.R import io.agora.api.example.compose.data.SettingPreferences import io.agora.api.example.compose.ui.common.ChannelNameInput import io.agora.api.example.compose.ui.common.DropdownMenuRaw @@ -278,7 +280,7 @@ fun ScreenSharing() { @Composable -fun ScreenSharingView( +private fun ScreenSharingView( channelName: String, isJoined: Boolean, localUid: Int = 0, @@ -311,14 +313,14 @@ fun ScreenSharingView( Spacer(modifier = Modifier.weight(1.0f)) SwitchRaw( - title = "屏幕共享本地预览", + title = stringResource(id = R.string.screen_sharing_local_preview), checked = isScreenPreview, enable = isJoined && isScreenSharing ) { onScreenPreview(it) } SwitchRaw( - title = "屏幕共享本地音频", + title = stringResource(id = R.string.screen_sharing_audio), enable = isJoined && isScreenSharing ) { onScreenAudio(it) @@ -353,7 +355,7 @@ fun ScreenSharingView( @Preview @Composable -fun ScreenSharingViewPreview() { +private fun ScreenSharingViewPreview() { ScreenSharingView( channelName = "test", isJoined = false, diff --git a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/SendDataStream.kt b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/SendDataStream.kt index 7be151991..9ce1aeb8b 100644 --- a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/SendDataStream.kt +++ b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/SendDataStream.kt @@ -224,7 +224,7 @@ fun SendDataStream() { } @Composable -fun SendDataStreamView( +private fun SendDataStreamView( rtcEngine: RtcEngine?, channelName: String, isJoined: Boolean, diff --git a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/SpatialSound.kt b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/SpatialSound.kt new file mode 100644 index 000000000..2e5b55e09 --- /dev/null +++ b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/SpatialSound.kt @@ -0,0 +1,655 @@ +package io.agora.api.example.compose.samples + +import android.util.Log +import android.widget.Toast +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts +import androidx.annotation.DrawableRes +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.gestures.detectDragGestures +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableFloatStateOf +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.platform.LocalLifecycleOwner +import androidx.compose.ui.platform.LocalSoftwareKeyboardController +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.unit.dp +import io.agora.api.example.compose.BuildConfig +import io.agora.api.example.compose.R +import io.agora.api.example.compose.data.SettingPreferences +import io.agora.api.example.compose.ui.common.ChannelNameInput +import io.agora.api.example.compose.ui.common.SliderRaw +import io.agora.api.example.compose.ui.common.SwitchRaw +import io.agora.api.example.compose.utils.TokenUtils +import io.agora.mediaplayer.data.MediaPlayerSource +import io.agora.rtc2.ChannelMediaOptions +import io.agora.rtc2.Constants +import io.agora.rtc2.IRtcEngineEventHandler +import io.agora.rtc2.RtcEngine +import io.agora.rtc2.RtcEngineConfig +import io.agora.rtc2.SpatialAudioParams +import io.agora.rtc2.video.VideoEncoderConfiguration +import io.agora.spatialaudio.ILocalSpatialAudioEngine +import io.agora.spatialaudio.LocalSpatialAudioConfig +import io.agora.spatialaudio.RemoteVoicePositionInfo +import kotlin.math.roundToInt + + +private const val AXIS_MAX_DISTANCE = 10.0f + +@Composable +fun SpatialSound() { + val context = LocalContext.current + val lifecycleOwner = LocalLifecycleOwner.current + val keyboard = LocalSoftwareKeyboardController.current + var isJoined by rememberSaveable { mutableStateOf(false) } + var channelName by rememberSaveable { mutableStateOf("") } + var localUid by rememberSaveable { mutableIntStateOf(0) } + var remoteUidList by rememberSaveable { mutableStateOf(listOf()) } + val settingCache = remember { mutableMapOf() } + + val rtcEngine = remember { + RtcEngine.create(RtcEngineConfig().apply { + mAreaCode = SettingPreferences.getArea() + mContext = context + mAppId = BuildConfig.AGORA_APP_ID + mEventHandler = object : IRtcEngineEventHandler() { + override fun onJoinChannelSuccess(channel: String?, uid: Int, elapsed: Int) { + super.onJoinChannelSuccess(channel, uid, elapsed) + isJoined = true + localUid = uid + } + + override fun onLeaveChannel(stats: RtcStats?) { + super.onLeaveChannel(stats) + isJoined = false + localUid = 0 + remoteUidList = emptyList() + } + + override fun onUserJoined(uid: Int, elapsed: Int) { + super.onUserJoined(uid, elapsed) + remoteUidList += uid + settingCache[uid] = SettingInfo( + mute = false, + voiceBlur = false, + airborneSimulation = false, + attenuation = 0.5f + ) + } + + override fun onUserOffline(uid: Int, reason: Int) { + super.onUserOffline(uid, reason) + remoteUidList -= uid + settingCache.remove(uid) + } + + } + }).apply { + setVideoEncoderConfiguration( + VideoEncoderConfiguration( + SettingPreferences.getVideoDimensions(), + SettingPreferences.getVideoFrameRate(), + VideoEncoderConfiguration.STANDARD_BITRATE, + SettingPreferences.getOrientationMode() + ) + ) + enableAudio() + } + } + + val localSpatial = remember { + ILocalSpatialAudioEngine.create().apply { + val localSpatialAudioConfig = LocalSpatialAudioConfig() + localSpatialAudioConfig.mRtcEngine = rtcEngine + initialize(localSpatialAudioConfig) + + setMaxAudioRecvCount(2) + setAudioRecvRange(AXIS_MAX_DISTANCE) + setDistanceUnit(1f) + } + } + + val playerLeft = remember { + rtcEngine.createMediaPlayer().apply { + setLoopCount(-1) + settingCache[mediaPlayerId] = SettingInfo( + mute = true, voiceBlur = false, airborneSimulation = false, attenuation = 0.5f + ) + } + } + val playerRight = remember { + rtcEngine.createMediaPlayer().apply { + setLoopCount(-1) + settingCache[mediaPlayerId] = SettingInfo( + mute = true, voiceBlur = false, airborneSimulation = false, attenuation = 0.5f + ) + } + } + + DisposableEffect(lifecycleOwner) { + onDispose { + playerLeft.destroy() + playerRight.destroy() + rtcEngine.leaveChannel() + RtcEngine.destroy() + } + } + val permissionLauncher = + rememberLauncherForActivityResult(contract = ActivityResultContracts.RequestMultiplePermissions()) { grantedMap -> + val allGranted = grantedMap.values.all { it } + if (allGranted) { + // Permission is granted + Toast.makeText(context, "Permission Granted", Toast.LENGTH_LONG).show() + val mediaOptions = ChannelMediaOptions() + mediaOptions.channelProfile = Constants.CHANNEL_PROFILE_LIVE_BROADCASTING + mediaOptions.clientRoleType = Constants.CLIENT_ROLE_BROADCASTER + mediaOptions.autoSubscribeAudio = true + mediaOptions.autoSubscribeVideo = false + mediaOptions.publishMicrophoneTrack = true + mediaOptions.publishCameraTrack = false + TokenUtils.gen(channelName, 0) { + rtcEngine.joinChannel(it, channelName, 0, mediaOptions) + } + } else { + // Permission is denied + Toast.makeText(context, "Permission Denied", Toast.LENGTH_LONG).show() + } + } + + SpatialSoundView(channelName = channelName, + isJoined = isJoined, + remoteUidList = remoteUidList, + onJoinClick = { + channelName = it + keyboard?.hide() + settingCache[playerLeft.mediaPlayerId] = + settingCache[playerLeft.mediaPlayerId]?.copy(mute = false)!! + settingCache[playerRight.mediaPlayerId] = + settingCache[playerRight.mediaPlayerId]?.copy(mute = false)!! + playerLeft.openWithMediaSource(MediaPlayerSource().apply { + url = "https://webdemo.agora.io/audiomixing.mp3" + isAutoPlay = true + }) + playerRight.openWithMediaSource(MediaPlayerSource().apply { + url = "https://webdemo.agora.io/dang.mp3" + isAutoPlay = true + }) + + permissionLauncher.launch( + arrayOf( + android.Manifest.permission.RECORD_AUDIO, + ) + ) + }, + onLeaveClick = { + playerLeft.stop() + playerRight.stop() + rtcEngine.leaveChannel() + }, + onPlayerLeftPositionUpdated = { x, y -> + localSpatial.updatePlayerPositionInfo(playerLeft.mediaPlayerId, + RemoteVoicePositionInfo().apply { + forward = floatArrayOf(1.0f, 0f, 0f) + position = floatArrayOf(x * AXIS_MAX_DISTANCE, y * AXIS_MAX_DISTANCE, 0f) + }) + }, + onPlayerRightPositionUpdated = { x, y -> + localSpatial.updatePlayerPositionInfo(playerRight.mediaPlayerId, + RemoteVoicePositionInfo().apply { + forward = floatArrayOf(1.0f, 0f, 0f) + position = floatArrayOf(x * AXIS_MAX_DISTANCE, y * AXIS_MAX_DISTANCE, 0f) + }) + }, + onRemotePositionUpdated = { uid, x, y -> + localSpatial.updateRemotePosition(uid, RemoteVoicePositionInfo().apply { + forward = floatArrayOf(1.0f, 0f, 0f) + position = floatArrayOf(x * AXIS_MAX_DISTANCE, y * AXIS_MAX_DISTANCE, 0f) + }) + }, + onLocalPositionUpdated = { x, y -> + localSpatial.updateSelfPosition( + floatArrayOf(x * AXIS_MAX_DISTANCE, y * AXIS_MAX_DISTANCE, 0f), + floatArrayOf(1.0f, 0f, 0f), + floatArrayOf(0f, 1.0f, 0f), + floatArrayOf(0f, 0f, 1.0f) + ) + }, + onMute = { settingType, enable, uid -> + when (settingType) { + SETTING_TYPE_PLAYER_LEFT -> { + settingCache[playerLeft.mediaPlayerId] = + settingCache[playerLeft.mediaPlayerId]?.copy(mute = enable)!! + if (enable) { + playerLeft.pause() + } else { + playerLeft.resume() + } + } + + SETTING_TYPE_PLAYER_RIGHT -> { + settingCache[playerRight.mediaPlayerId] = + settingCache[playerRight.mediaPlayerId]?.copy(mute = enable)!! + if (enable) { + playerRight.pause() + } else { + playerRight.resume() + } + } + + SETTING_TYPE_REMOTE -> { + settingCache[uid]?.copy(mute = enable)?.let { + settingCache[uid] = it + } + localSpatial.muteRemoteAudioStream(uid, enable) + } + } + }, + onVoiceBlur = { settingType, enable, uid -> + when (settingType) { + SETTING_TYPE_PLAYER_LEFT -> { + settingCache[playerLeft.mediaPlayerId] = + settingCache[playerLeft.mediaPlayerId]?.copy(voiceBlur = enable)!! + playerLeft.setSpatialAudioParams(SpatialAudioParams().apply { + enable_blur = enable + }) + } + + SETTING_TYPE_PLAYER_RIGHT -> { + settingCache[playerRight.mediaPlayerId] = + settingCache[playerRight.mediaPlayerId]?.copy(voiceBlur = enable)!! + playerRight.setSpatialAudioParams(SpatialAudioParams().apply { + enable_blur = enable + }) + } + + SETTING_TYPE_REMOTE -> { + settingCache[uid]?.copy(voiceBlur = enable)?.let { + settingCache[uid] = it + } + rtcEngine.setRemoteUserSpatialAudioParams(uid, SpatialAudioParams().apply { + enable_blur = enable + }) + } + } + }, + onAirborneSimulation = { settingType, enable, uid -> + when (settingType) { + SETTING_TYPE_PLAYER_LEFT -> { + settingCache[playerLeft.mediaPlayerId] = + settingCache[playerLeft.mediaPlayerId]?.copy(airborneSimulation = enable)!! + playerLeft.setSpatialAudioParams(SpatialAudioParams().apply { + enable_air_absorb = enable + }) + } + + SETTING_TYPE_PLAYER_RIGHT -> { + settingCache[playerRight.mediaPlayerId] = + settingCache[playerRight.mediaPlayerId]?.copy(airborneSimulation = enable)!! + playerRight.setSpatialAudioParams(SpatialAudioParams().apply { + enable_air_absorb = enable + }) + } + + SETTING_TYPE_REMOTE -> { + settingCache[uid]?.copy(airborneSimulation = enable)?.let { + settingCache[uid] = it + } + rtcEngine.setRemoteUserSpatialAudioParams(uid, SpatialAudioParams().apply { + enable_air_absorb = enable + }) + } + } + }, + onAttenuation = { settingType, value, uid -> + when (settingType) { + SETTING_TYPE_PLAYER_LEFT -> { + settingCache[playerLeft.mediaPlayerId] = + settingCache[playerLeft.mediaPlayerId]?.copy(attenuation = value)!! + localSpatial.setRemoteAudioAttenuation( + playerLeft.mediaPlayerId, value.toDouble(), false + ) + } + + SETTING_TYPE_PLAYER_RIGHT -> { + settingCache[playerRight.mediaPlayerId] = + settingCache[playerRight.mediaPlayerId]?.copy(attenuation = value)!! + localSpatial.setRemoteAudioAttenuation( + playerRight.mediaPlayerId, value.toDouble(), false + ) + } + + SETTING_TYPE_REMOTE -> { + settingCache[uid]?.copy(attenuation = value)?.let { + settingCache[uid] = it + } + localSpatial.setRemoteAudioAttenuation(uid, value.toDouble(), false) + } + } + }) +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +private fun SpatialSoundView( + channelName: String, + isJoined: Boolean, + remoteUidList: List = emptyList(), + settingCache: Map = emptyMap(), + onJoinClick: (channelName: String) -> Unit = { _ -> }, + onLeaveClick: () -> Unit = {}, + onMute: (settingType: Int, enable: Boolean, uid: Int) -> Unit = { _, _, _ -> }, + onVoiceBlur: (settingType: Int, enable: Boolean, uid: Int) -> Unit = { _, _, _ -> }, + onAirborneSimulation: (settingType: Int, enable: Boolean, uid: Int) -> Unit = { _, _, _ -> }, + onAttenuation: (settingType: Int, value: Float, uid: Int) -> Unit = { _, _, _ -> }, + onPlayerLeftPositionUpdated: (x: Float, y: Float) -> Unit = { _, _ -> }, + onPlayerRightPositionUpdated: (x: Float, y: Float) -> Unit = { _, _ -> }, + onRemotePositionUpdated: (uid: Int, x: Float, y: Float) -> Unit = { _, _, _ -> }, + onLocalPositionUpdated: (x: Float, y: Float) -> Unit = { _, _ -> }, +) { + var showSetting by rememberSaveable { mutableStateOf(false) } + var settingUid by rememberSaveable { mutableIntStateOf(0) } + var settingType by rememberSaveable { mutableIntStateOf(SETTING_TYPE_PLAYER_LEFT) } + + + Column { + Box(modifier = Modifier.weight(1.0f)) { + DraggableIcon(iconId = R.drawable.ic_speaker, + iconSize = 60.dp, + axisX = -0.5f, + axisY = 0.5f, + draggable = false, + clickable = isJoined, + onClick = { + settingUid = 0 + settingType = SETTING_TYPE_PLAYER_LEFT + showSetting = true + }) { x, y -> + Log.i("SpatialSoundView", "Speaker1 >> x=$x, y=$y") + onPlayerLeftPositionUpdated(x, y) + } + DraggableIcon(iconId = R.drawable.ic_speaker, + iconSize = 60.dp, + axisX = 0.5f, + axisY = 0.5f, + draggable = false, + clickable = isJoined, + onClick = { + settingUid = 1 + settingType = SETTING_TYPE_PLAYER_RIGHT + showSetting = true + }) { x, y -> + Log.i("SpatialSoundView", "Speaker2 >> x=$x, y=$y") + onPlayerRightPositionUpdated(x, y) + } + + Text( + modifier = Modifier + .align(Alignment.TopCenter) + .padding(top = 50.dp), + text = stringResource(id = R.string.spatial_sound_experience_tip), + style = MaterialTheme.typography.titleMedium + ) + DraggableIcon( + iconId = R.drawable.ic_local, + iconSize = 50.dp, + axisX = 0.0f, + axisY = 0.0f, + showGuides = false, + clickable = false, + draggable = isJoined + ) { x, y -> + Log.i("SpatialSoundView", "Local >> x=$x, y=$y") + onLocalPositionUpdated(x, y) + } + + remoteUidList.getOrNull(0)?.let { + DraggableIcon(iconId = R.drawable.ic_remote, + iconSize = 60.dp, + axisX = -0.5f, + axisY = -0.5f, + draggable = false, + clickable = true, + onClick = { + settingUid = it + settingType = SETTING_TYPE_REMOTE + showSetting = true + }) { x, y -> + Log.i("SpatialSoundView", "Remote1 >> x=$x, y=$y") + onRemotePositionUpdated(it, x, y) + } + } + + remoteUidList.getOrNull(1)?.let { + DraggableIcon(iconId = R.drawable.ic_remote, + iconSize = 60.dp, + axisX = 0.5f, + axisY = -0.5f, + draggable = false, + clickable = true, + onClick = { + settingUid = it + settingType = SETTING_TYPE_REMOTE + showSetting = true + }) { x, y -> + Log.i("SpatialSoundView", "Remote2 >> x=$x, y=$y") + onRemotePositionUpdated(it, x, y) + } + } + + + } + + ChannelNameInput( + channelName = channelName, + isJoined = isJoined, + onJoinClick = onJoinClick, + onLeaveClick = onLeaveClick + ) + } + + if (showSetting) { + ModalBottomSheet( + onDismissRequest = { + showSetting = false + }, + windowInsets = WindowInsets(0), + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 16.dp) + ) { + SwitchRaw(title = stringResource(id = R.string.mute_audio), settingCache[settingUid]?.mute ?: false) { + onMute(settingType, it, settingUid) + } + SwitchRaw(title = "VoiceBlur", settingCache[settingUid]?.voiceBlur ?: false) { + onVoiceBlur(settingType, it, settingUid) + } + SwitchRaw( + title = "Airborne Simulation", + settingCache[settingUid]?.airborneSimulation ?: false + ) { + onAirborneSimulation(settingType, it, settingUid) + } + SliderRaw( + title = "Attenuation", + value = settingCache[settingUid]?.attenuation ?: 0.5f + ) { + onAttenuation(settingType, it, settingUid) + } + } + } + } + +} + +private const val SETTING_TYPE_PLAYER_LEFT = 0 +private const val SETTING_TYPE_PLAYER_RIGHT = 1 +private const val SETTING_TYPE_REMOTE = 2 + +private data class SettingInfo( + val mute: Boolean, + val voiceBlur: Boolean, + val airborneSimulation: Boolean, + val attenuation: Float +) + +@Composable +private fun DraggableIcon( + @DrawableRes iconId: Int, + iconSize: Dp, + axisX: Float, + axisY: Float, + draggable: Boolean = true, + showGuides: Boolean = false, + clickable: Boolean = true, + onClick: () -> Unit = { }, + onAxisPositionChanged: (axisX: Float, axisY: Float) -> Unit = { _, _ -> } +) { + var width by remember { mutableIntStateOf(0) } + var height by remember { mutableIntStateOf(0) } + var offsetX by remember { mutableFloatStateOf(0.0f) } + var offsetY by remember { mutableFloatStateOf(0.0f) } + val density = LocalDensity.current.density + + Box(modifier = Modifier + .fillMaxSize() + .onGloballyPositioned { coordinates -> + width = coordinates.size.width + height = coordinates.size.height + offsetX = width * 1.0f / 2 - (iconSize.value * density) / 2 + width * 1.0f / 2 * axisX + offsetY = height * 1.0f / 2 - (iconSize.value * density) / 2 - height * 1.0f / 2 * axisY + onAxisPositionChanged( + (offsetX - (width * 1.0f / 2 - (iconSize.value * density) / 2)) / (width * 1.0f / 2), + -1 * (offsetY - (height * 1.0f / 2 - (iconSize.value * density) / 2)) / (height * 1.0f / 2) + ) + }) { + if (showGuides) { + val row = 4 + val col = 4 + Column(modifier = Modifier.fillMaxSize()) { + for (i in 0 until row) { + if (i == 0) { + Box( + modifier = Modifier + .height(1.dp) + .fillMaxWidth() + .background(Color.Red) + .alpha(0.5f) + ) + } + Spacer(modifier = Modifier.weight(1.0f)) + Box( + modifier = Modifier + .height(1.dp) + .fillMaxWidth() + .background(Color.Red) + .alpha(0.5f) + ) + } + } + + Row(modifier = Modifier.fillMaxSize()) { + for (i in 0 until col) { + if (i == 0) { + Box( + modifier = Modifier + .width(1.dp) + .fillMaxHeight() + .background(Color.Red) + .alpha(0.5f) + ) + } + Spacer(modifier = Modifier.weight(1.0f)) + Box( + modifier = Modifier + .width(1.dp) + .fillMaxHeight() + .background(Color.Red) + .alpha(0.5f) + ) + } + } + } + + var modifier = Modifier + .offset { IntOffset(offsetX.roundToInt(), offsetY.roundToInt()) } + .size(iconSize) + if (draggable) { + modifier = modifier.pointerInput(Unit) { + detectDragGestures { change, dragAmount -> + change.consume() + + var x = offsetX + dragAmount.x + var y = offsetY + dragAmount.y + if (x < 0) { + x = 0f + } + if (x > (width - iconSize.toPx())) { + x = width - iconSize.toPx() + } + if (y < 0) { + y = 0f + } + if (y > (height - iconSize.toPx())) { + y = height - iconSize.toPx() + } + offsetX = x + offsetY = y + + onAxisPositionChanged( + (offsetX - (width * 1.0f / 2 - (iconSize.value * density) / 2)) / (width * 1.0f / 2), + -1 * (offsetY - (height * 1.0f / 2 - (iconSize.value * density) / 2)) / (height * 1.0f / 2) + ) + } + } + } + + if (clickable) { + modifier = modifier.clickable { + onClick() + } + } + + Image( + modifier = modifier, painter = painterResource(id = iconId), contentDescription = null + ) + } +} diff --git a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/VideoProcessExtension.kt b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/VideoProcessExtension.kt index 19f7dd758..27ea702f5 100644 --- a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/VideoProcessExtension.kt +++ b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/VideoProcessExtension.kt @@ -19,9 +19,11 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.platform.LocalSoftwareKeyboardController +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import io.agora.api.example.compose.BuildConfig +import io.agora.api.example.compose.R import io.agora.api.example.compose.data.SettingPreferences import io.agora.api.example.compose.ui.common.ChannelNameInput import io.agora.api.example.compose.ui.common.RadioGroup @@ -201,7 +203,7 @@ fun VideoProcessExtension() { } @Composable -fun VideoProcessExtensionView( +private fun VideoProcessExtensionView( rtcEngine: RtcEngine? = null, channelName: String, isJoined: Boolean, @@ -235,24 +237,24 @@ fun VideoProcessExtensionView( val beautyOptions = remember { BeautyOptions() } var isOpen by remember { mutableStateOf(false) } Column { - SwitchRaw(title = "美颜", checked = isOpen) { + SwitchRaw(title = stringResource(id = R.string.beauty_face), checked = isOpen) { isOpen = it rtcEngine?.setBeautyEffectOptions(isOpen, beautyOptions) } Column(modifier = Modifier.padding(16.dp, 0.dp)) { - SliderRaw(title = "美白", value = beautyOptions.lighteningLevel) { + SliderRaw(title = stringResource(id = R.string.beauty_lightening), value = beautyOptions.lighteningLevel) { beautyOptions.lighteningLevel = it rtcEngine?.setBeautyEffectOptions(isOpen, beautyOptions) } - SliderRaw(title = "红润", value = beautyOptions.rednessLevel) { + SliderRaw(title = stringResource(id = R.string.beauty_redness), value = beautyOptions.rednessLevel) { beautyOptions.rednessLevel = it rtcEngine?.setBeautyEffectOptions(isOpen, beautyOptions) } - SliderRaw(title = "锐利", value = beautyOptions.sharpnessLevel) { + SliderRaw(title = stringResource(id = R.string.beauty_sharpness), value = beautyOptions.sharpnessLevel) { beautyOptions.sharpnessLevel = it rtcEngine?.setBeautyEffectOptions(isOpen, beautyOptions) } - SliderRaw(title = "平滑", value = beautyOptions.smoothnessLevel) { + SliderRaw(title = stringResource(id = R.string.beauty_smoothness), value = beautyOptions.smoothnessLevel) { beautyOptions.smoothnessLevel = it rtcEngine?.setBeautyEffectOptions(isOpen, beautyOptions) } @@ -260,7 +262,7 @@ fun VideoProcessExtensionView( } } item { - SwitchRaw(title = "暗光增强") { + SwitchRaw(title = stringResource(id = R.string.low_light_enhance)) { val options = LowLightEnhanceOptions() options.lowlightEnhanceLevel = LowLightEnhanceOptions.LOW_LIGHT_ENHANCE_LEVEL_FAST @@ -272,17 +274,17 @@ fun VideoProcessExtensionView( val colorEnhanceOptions = remember { ColorEnhanceOptions() } var isOpen by remember { mutableStateOf(false) } Column { - SwitchRaw(title = "色彩增强", checked = isOpen) { + SwitchRaw(title = stringResource(id = R.string.color_enhance), checked = isOpen) { isOpen = it rtcEngine?.setColorEnhanceOptions(isOpen, colorEnhanceOptions) } Column(modifier = Modifier.padding(16.dp, 0.dp)) { - SliderRaw(title = "强度", value = colorEnhanceOptions.strengthLevel) { + SliderRaw(title = stringResource(id = R.string.strength), value = colorEnhanceOptions.strengthLevel) { colorEnhanceOptions.strengthLevel = it rtcEngine?.setColorEnhanceOptions(isOpen, colorEnhanceOptions) } SliderRaw( - title = "肤色保护", + title = stringResource(id = R.string.skin_protect), value = colorEnhanceOptions.skinProtectLevel ) { colorEnhanceOptions.skinProtectLevel = it @@ -292,7 +294,7 @@ fun VideoProcessExtensionView( } } item { - SwitchRaw(title = "视频降噪") { + SwitchRaw(title = stringResource(id = R.string.video_denoiser)) { val options = VideoDenoiserOptions() options.denoiserLevel = VideoDenoiserOptions.VIDEO_DENOISER_LEVEL_HIGH_QUALITY options.denoiserMode = VideoDenoiserOptions.VIDEO_DENOISER_AUTO @@ -309,16 +311,16 @@ fun VideoProcessExtensionView( val segproperty = remember { SegmentationProperty() } Column { - SwitchRaw(title = "虚拟背景", checked = isOpen) { + SwitchRaw(title = stringResource(id = R.string.virtual_background), checked = isOpen) { isOpen = it rtcEngine?.enableVirtualBackground(isOpen, backgroundSource, segproperty) } RadioGroup( options = listOf( - "图片" to VirtualBackgroundSource.BACKGROUND_IMG, - "颜色" to VirtualBackgroundSource.BACKGROUND_COLOR, - "毛玻璃" to VirtualBackgroundSource.BACKGROUND_BLUR, - "视频" to VirtualBackgroundSource.BACKGROUND_VIDEO, + stringResource(id = R.string.picture) to VirtualBackgroundSource.BACKGROUND_IMG, + stringResource(id = R.string.color) to VirtualBackgroundSource.BACKGROUND_COLOR, + stringResource(id = R.string.blur) to VirtualBackgroundSource.BACKGROUND_BLUR, + stringResource(id = R.string.video) to VirtualBackgroundSource.BACKGROUND_VIDEO, ), selectedValue = backgroundSource.backgroundSourceType ) { _, option -> @@ -357,7 +359,7 @@ fun VideoProcessExtensionView( @Preview @Composable -fun VideoProcessExtensionViewPreview() { +private fun VideoProcessExtensionViewPreview() { VideoProcessExtensionView(channelName = "", isJoined = true, onJoinClick = {}) { } diff --git a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/VoiceEffects.kt b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/VoiceEffects.kt index 4dd92a518..4df78c431 100644 --- a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/VoiceEffects.kt +++ b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/VoiceEffects.kt @@ -25,9 +25,11 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.platform.LocalSoftwareKeyboardController +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import io.agora.api.example.compose.BuildConfig +import io.agora.api.example.compose.R import io.agora.api.example.compose.data.SettingPreferences import io.agora.api.example.compose.ui.common.AudioGrid import io.agora.api.example.compose.ui.common.AudioStatsInfo @@ -151,7 +153,7 @@ fun VoiceEffects() { } @Composable -fun VoiceEffectsView( +private fun VoiceEffectsView( videoIdList: List, statsMap: Map = mapOf(), rtcEngine: RtcEngine? = null, @@ -326,9 +328,9 @@ fun VoiceEffectsView( title = "AINS Mode", options = listOf( "Off" to 0, - "均衡降噪模式" to 0, - "强降噪模式" to 1, - "低延时强降噪模式" to 2, + stringResource(id = R.string.ains_mode_0) to 0, + stringResource(id = R.string.ains_mode_1) to 1, + stringResource(id = R.string.ains_mode_2) to 2, ) ) { index, option -> rtcEngine?.setAINSMode(index != 0, option.second) @@ -380,6 +382,6 @@ fun VoiceEffectsView( @Preview @Composable -fun VoiceEffectsViewPreview() { +private fun VoiceEffectsViewPreview() { VoiceEffectsView(videoIdList = listOf(0, 1, 2)) } \ No newline at end of file diff --git a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/ui/common/Widgets.kt b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/ui/common/Widgets.kt index 366497db3..67fa8df16 100644 --- a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/ui/common/Widgets.kt +++ b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/ui/common/Widgets.kt @@ -41,6 +41,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.RectangleShape +import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.Role import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.TextUnit @@ -48,6 +49,7 @@ import androidx.compose.ui.unit.TextUnitType import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.viewinterop.AndroidView +import io.agora.api.example.compose.R import io.agora.rtc2.IRtcEngineEventHandler @Composable @@ -62,8 +64,8 @@ fun ChannelNameInput( InputRaw( modifier = modifier, text = text, - btnText = if (isJoined) "Leave" else "Join", - label = "Channel Name", + btnText = if (isJoined) stringResource(id = R.string.leave) else stringResource(id = R.string.join), + label = stringResource(id = R.string.channel_name), ) { text = it if (isJoined) onLeaveClick() else onJoinClick(it) diff --git a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/ui/example/Example.kt b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/ui/example/Example.kt index 81a4bef1f..8e8e1e5e5 100644 --- a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/ui/example/Example.kt +++ b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/ui/example/Example.kt @@ -30,7 +30,7 @@ fun Example( .padding(paddingValues), contentAlignment = Alignment.Center ) { - example.content() + example.content(onBackClick) } } } \ No newline at end of file diff --git a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/ui/settings/Settings.kt b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/ui/settings/Settings.kt index 53f31c05f..ba6d4ade5 100644 --- a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/ui/settings/Settings.kt +++ b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/ui/settings/Settings.kt @@ -19,9 +19,11 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import io.agora.api.example.compose.R import io.agora.api.example.compose.data.SettingPreferences import io.agora.api.example.compose.ui.common.APIExampleScaffold import io.agora.api.example.compose.ui.common.DropdownMenuRaw @@ -32,7 +34,7 @@ import io.agora.rtc2.video.VideoEncoderConfiguration @Composable fun Settings(onBackClick: () -> Unit) { APIExampleScaffold( - topBarTitle = "Settings", + topBarTitle = stringResource(id = R.string.settings), showBackNavigationIcon = true, onBackClick = onBackClick ) { paddingValues -> @@ -69,7 +71,7 @@ fun Settings(onBackClick: () -> Unit) { VideoEncoderConfiguration.VD_3840x2160, ) DropdownMenuRaw( - title = "Dimension", + title = stringResource(id = R.string.resolution), options = dimensions.map { it.toText() to it }, @@ -89,7 +91,7 @@ fun Settings(onBackClick: () -> Unit) { VideoEncoderConfiguration.FRAME_RATE.FRAME_RATE_FPS_60, ) DropdownMenuRaw( - title = "FrameRate", + title = stringResource(id = R.string.frame_rate), options = frameRates.map { it.toText() to it}, selectedValue = SettingPreferences.getVideoFrameRate(), ) { _, option -> @@ -103,7 +105,7 @@ fun Settings(onBackClick: () -> Unit) { VideoEncoderConfiguration.ORIENTATION_MODE.ORIENTATION_MODE_FIXED_PORTRAIT, ) DropdownMenuRaw( - title = "Orientation", + title = stringResource(id = R.string.orientation), options = orientationMode.map { it.toText() to it}, selectedValue = SettingPreferences.getOrientationMode(), ) { _, option -> @@ -113,7 +115,7 @@ fun Settings(onBackClick: () -> Unit) { Divider(modifier = Modifier.padding(horizontal = 16.dp)) DropdownMenuRaw( - title = "Area", + title = stringResource(id = R.string.area), options = listOf( "Glob" to RtcEngineConfig.AreaCode.AREA_CODE_GLOB, "China" to RtcEngineConfig.AreaCode.AREA_CODE_CN, diff --git a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/utils/TokenUtils.java b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/utils/TokenUtils.java index 8788511e5..a4f83346b 100644 --- a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/utils/TokenUtils.java +++ b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/utils/TokenUtils.java @@ -98,7 +98,7 @@ private static void gen(String appId, String certificate, String channelName, in } Request request = new Request.Builder() - .url("https://test-toolbox.bj2.agoralab.co/v1/token/generate") + .url("https://service.agora.io/toolbox-global/v1/token/generate") .addHeader("Content-Type", "application/json") .post(RequestBody.create(postBody.toString(), null)) .build(); diff --git a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/utils/YuvFboProgram.java b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/utils/YuvFboProgram.java index bf3d6dfb4..c534c3d67 100644 --- a/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/utils/YuvFboProgram.java +++ b/Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/utils/YuvFboProgram.java @@ -96,7 +96,7 @@ public Integer drawYuv(byte[] yuv, int width, int height) { matrix.preTranslate(0.5f, 0.5f); matrix.preScale(1f, -1f); // I420-frames are upside down matrix.preTranslate(-0.5f, -0.5f); - glRectDrawer.drawYuv(yuvUploader.getYuvTextures(), RendererCommon.convertMatrixFromAndroidGraphicsMatrix(matrix), width, height, 0, 0, width, height); + glRectDrawer.drawYuv(yuvUploader.getYuvTextures(), 0, RendererCommon.convertMatrixFromAndroidGraphicsMatrix(matrix), width, height, 0, 0, width, height, 0); GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); GLES20.glFlush(); diff --git a/Android/APIExample-Compose/app/src/main/res/drawable/ic_local.png b/Android/APIExample-Compose/app/src/main/res/drawable/ic_local.png new file mode 100644 index 000000000..4968510c0 Binary files /dev/null and b/Android/APIExample-Compose/app/src/main/res/drawable/ic_local.png differ diff --git a/Android/APIExample-Compose/app/src/main/res/drawable/ic_remote.png b/Android/APIExample-Compose/app/src/main/res/drawable/ic_remote.png new file mode 100644 index 000000000..99c244ef7 Binary files /dev/null and b/Android/APIExample-Compose/app/src/main/res/drawable/ic_remote.png differ diff --git a/Android/APIExample-Compose/app/src/main/res/drawable/ic_speaker.png b/Android/APIExample-Compose/app/src/main/res/drawable/ic_speaker.png new file mode 100644 index 000000000..b1a45ff89 Binary files /dev/null and b/Android/APIExample-Compose/app/src/main/res/drawable/ic_speaker.png differ diff --git a/Android/APIExample-Compose/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/Android/APIExample-Compose/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..64c410326 Binary files /dev/null and b/Android/APIExample-Compose/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/Android/APIExample-Compose/app/src/main/res/values-zh/strings.xml b/Android/APIExample-Compose/app/src/main/res/values-zh/strings.xml index 0e3ec3f13..2c733c923 100644 --- a/Android/APIExample-Compose/app/src/main/res/values-zh/strings.xml +++ b/Android/APIExample-Compose/app/src/main/res/values-zh/strings.xml @@ -1,5 +1,4 @@ - APIExample-Compose 视频互动直播 音频互动直播 RTC实时直播 @@ -25,4 +24,80 @@ 创建数据流 虚拟节拍器 跨频道媒体流转发 + 画中画 + 空间音频 + + 加密方式 + 加密密钥 + 发布本地音频 + 发布麦克风 + 退出 + 加入 + 暂停 + 恢复 + Ex频道截图 + 离开Ex频道 + 加入Ex频道 + 离开选项 + 是否停止录音 + 确定 + 取消 + 开启极速直播 + 水印 + 小流 + 编码类型 + 自动选择 + 硬编 + 软编 + B帧 + 垫片 + 透明背景 + 视频元数据 + 发送 + 推送 + 停止 + 播放 + 开始录制 + 停止录制 + 切换摄像头 + 音频回写 + 开始 + 音效音量 + 混音音量 + 混音播放音量 + 混音发布音量 + \"Tip:开始测试后请对着麦克风讲话,讲话声音在%d秒后播放则测试正常。 + 推流地址 + 关闭推流 + 开始推流 + 是否转码 + 屏幕共享本地预览 + 屏幕共享本地音频 + 请移动红色图标体验3d音频效果 + 静音 + 美颜 + 美白 + 红润 + 锐利 + 平滑 + 暗光增强 + 色彩增强 + 强度 + 肤色保护 + 视频降噪 + 虚拟背景 + 图片 + 颜色 + 毛玻璃 + 视频 + 均衡降噪模式 + 强降噪模式 + 低延时强降噪模式 + 频道名 + 离开 + 设置 + 分辨率 + 帧率 + 方向 + 区域 \ No newline at end of file diff --git a/Android/APIExample-Compose/app/src/main/res/values/strings.xml b/Android/APIExample-Compose/app/src/main/res/values/strings.xml index 31da5a3b9..ab5edbb19 100644 --- a/Android/APIExample-Compose/app/src/main/res/values/strings.xml +++ b/Android/APIExample-Compose/app/src/main/res/values/strings.xml @@ -1,28 +1,104 @@ - APIExample-Compose + APIExample-Compose Join Video Channel Join Audio Channel Live Streaming Join Video Channel (With Token) - 旁路推流CDN - 音视频元数据 - 美声与音效 - 自定义音频采集 - 自定义音频渲染 - 自定义视频采集 - 自定义视频渲染 - 原始音频数据 - 原始视频数据 - 加入多频道 - 媒体流加密 - 音频文件混音 - 通话前质量检测 - 本地/远端录制 - 媒体播放器 - 屏幕共享 - 视频增强组件 - 本地合图 - 创建数据流 - 虚拟节拍器 - 跨频道媒体流转发 + RTMP Streaming + Media Metadata + Voice Effects + Custom Audio Source + Custom Audio Render + Custom Video Source + Custom Video Render + Origin Audio Data + Origin Video Data + Join Multi Channel + Channel Encryption + Play Audio Files + Pre Call Test + Media Recorder + Media Player + Screen Sharing + Video Process Extension + Local Video Transcoding + Send Data Stream + Rhythm Player + Host Across Channel + Picture In Picture + Spaital Sound + + Encryption Mode + Encryption Key + Publish Local Audio + Publish microphone + Exit + Join + Pause + Resume + Ex Channel Snapshot + Leave Ex Channel + Join Ex Channel + Leave Option + Stop Recording Or Not + Confirm + Cancel + Open Low Latency live + Watermark + Low Stream + Encoder Type + Auto + Hardware + Software + B Frame + Video Image + Alpha Background + Video Metadata + Send + Publish + Stop + Play + Start Recording + Stop Recording + Switch Camera + Audio Rewrite + Start + Effects Volume + Mixing + Mixing Playout Volume + Mixing Publish Volume + Tip:After starting the test, please speak into the microphone. If the speech sound plays after %d seconds, the test will be normal. + RTMP Url + Stop Publishing + Start Publishing + Transcoding Or Not + Screen Sharing Local Preview + Screen Sharing Audio + Please move the red icon to experience the 3D audio effect + Mute Audio + Beauty + Lightening + Redness + Sharpness + Smoothness + Low Light Enhance + Color Enhance + Strength + Skin Protext + Video Denoiser + Virtual Background + Picture + Color + Blur + Video + Balanced noise reduction mode + Strong noise reduction mode + ow latency and strong noise reduction mode + Channel Name + Leave + Settings + Resolution + Frame Rate + Orientation + Area \ No newline at end of file diff --git a/Android/APIExample-Compose/cloud_build.sh b/Android/APIExample-Compose/cloud_build.sh new file mode 100755 index 000000000..66e456cb4 --- /dev/null +++ b/Android/APIExample-Compose/cloud_build.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env sh + +# cache gradle to /tmp/.gradle +ls ~/.gradle || (mkdir -p /tmp/.gradle && ln -s /tmp/.gradle ~/.gradle && touch ~/.gradle/ln_$(date "+%y%m%d%H") && ls ~/.gradle) + +## use open jdk 17 +SYSTEM=$(uname -s) +if [ "$SYSTEM" = "Linux" ];then +if [ ! -d "/tmp/jdk-17.0.2" ];then + curl -O https://download.java.net/java/GA/jdk17.0.2/dfd4a8d0985749f896bed50d7138ee7f/8/GPL/openjdk-17.0.2_linux-x64_bin.tar.gz + tar zxf openjdk-17.0.2_linux-x64_bin.tar.gz + mv jdk-17.0.2 /tmp/ +fi +export JAVA_HOME=/tmp/jdk-17.0.2 +export PATH=$JAVA_HOME/bin:$PATH +java --version +fi + +## config appId +if [ ! -f "local.properties" ];then + touch local.properties + echo "AGORA_APP_ID=${APP_ID}" >> local.properties +fi + +./gradlew clean || exit 1 +./gradlew :app:assembleRelease || exit 1 +if [ "$WORKSPACE" != "" ]; then +SDK_VERSION=$(echo $sdk_url | cut -d "/" -f 5) +cp app/build/outputs/apk/release/*.apk $WORKSPACE/APIExample-Compose_${BUILD_NUMBER}_${SDK_VERSION}_$(date "+%Y%m%d%H%M%S").apk +fi \ No newline at end of file diff --git a/Android/APIExample-Compose/gradle/libs.versions.toml b/Android/APIExample-Compose/gradle/libs.versions.toml index 699c40d4f..5e7d7eb30 100644 --- a/Android/APIExample-Compose/gradle/libs.versions.toml +++ b/Android/APIExample-Compose/gradle/libs.versions.toml @@ -12,7 +12,7 @@ composeBom = "2023.08.00" loggingInterceptor = "4.10.0" materialIconsExtended = "1.6.0" navigationCompose = "2.7.7" -agoraSdk = "4.3.0" +agoraSdk = "4.4.1" okhttp = "4.10.0" [libraries] diff --git a/Android/APIExample-Compose/keystore.key b/Android/APIExample-Compose/keystore.key new file mode 100644 index 000000000..a5014a522 Binary files /dev/null and b/Android/APIExample-Compose/keystore.key differ diff --git a/Android/APIExample/README.md b/Android/APIExample/README.md index 56c2c20f2..37c26351a 100644 --- a/Android/APIExample/README.md +++ b/Android/APIExample/README.md @@ -29,6 +29,7 @@ To build and run the sample application, get an App Id: // Agora APP ID. YOUR APP ID // Agora APP Certificate. If the project does not have certificates enabled, leave this field blank. + // PS:It is unsafe to place the App Certificate on the client side, it is recommended to place it on the server side to ensure that the App Certificate is not leaked. YOUR APP CERTIFICATE ``` diff --git a/Android/APIExample/README.zh.md b/Android/APIExample/README.zh.md index 876de706e..8e9142829 100644 --- a/Android/APIExample/README.zh.md +++ b/Android/APIExample/README.zh.md @@ -29,6 +29,7 @@ // 声网APP ID。 YOUR APP ID // 声网APP证书。如果项目没有开启证书鉴权,这个字段留空。 + // 注意:App证书放在客户端不安全,推荐放在服务端以确保 App 证书不会泄露。 YOUR APP CERTIFICATE ``` diff --git a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/AgoraBase.h b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/AgoraBase.h index a3b4647a6..792137209 100644 --- a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/AgoraBase.h +++ b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/AgoraBase.h @@ -33,31 +33,42 @@ #if defined(AGORARTC_EXPORT) #define AGORA_API extern "C" __declspec(dllexport) +#define AGORA_CPP_API __declspec(dllexport) #else #define AGORA_API extern "C" __declspec(dllimport) +#define AGORA_CPP_API __declspec(dllimport) #endif // AGORARTC_EXPORT #define AGORA_CALL __cdecl #define __deprecated +#define AGORA_CPP_INTERNAL_API extern + #elif defined(__APPLE__) #include #define AGORA_API extern "C" __attribute__((visibility("default"))) +#define AGORA_CPP_API __attribute__((visibility("default"))) #define AGORA_CALL +#define AGORA_CPP_INTERNAL_API __attribute__((visibility("hidden"))) + #elif defined(__ANDROID__) || defined(__linux__) #define AGORA_API extern "C" __attribute__((visibility("default"))) +#define AGORA_CPP_API __attribute__((visibility("default"))) #define AGORA_CALL #define __deprecated +#define AGORA_CPP_INTERNAL_API __attribute__((visibility("hidden"))) + #else // !_WIN32 && !__APPLE__ && !(__ANDROID__ || __linux__) #define AGORA_API extern "C" +#define AGORA_CPP_API #define AGORA_CALL #define __deprecated @@ -631,7 +642,7 @@ enum ERROR_CODE_TYPE { */ ERR_SET_CLIENT_ROLE_NOT_AUTHORIZED = 119, /** - * 120: Decryption fails. The user may have tried to join the channel with a wrong + * 120: MediaStream decryption fails. The user may have tried to join the channel with a wrong * password. Check your settings or try rejoining the channel. */ ERR_DECRYPTION_FAILED = 120, @@ -639,6 +650,11 @@ enum ERROR_CODE_TYPE { * 121: The user ID is invalid. */ ERR_INVALID_USER_ID = 121, + /** + * 122: DataStream decryption fails. The peer may have tried to join the channel with a wrong + * password, or did't enable datastream encryption + */ + ERR_DATASTREAM_DECRYPTION_FAILED = 122, /** * 123: The app is banned by the server. */ @@ -850,7 +866,6 @@ enum INTERFACE_ID_TYPE { AGORA_IID_RTC_CONNECTION = 7, AGORA_IID_SIGNALING_ENGINE = 8, AGORA_IID_MEDIA_ENGINE_REGULATOR = 9, - AGORA_IID_CLOUD_SPATIAL_AUDIO = 10, AGORA_IID_LOCAL_SPATIAL_AUDIO = 11, AGORA_IID_STATE_SYNC = 13, AGORA_IID_META_SERVICE = 14, @@ -1163,11 +1178,12 @@ enum VIDEO_CODEC_TYPE { */ VIDEO_CODEC_GENERIC_H264 = 7, /** - * 12: AV1. - */ + * 12: AV1. + * @technical preview + */ VIDEO_CODEC_AV1 = 12, /** - * 5: VP9. + * 13: VP9. */ VIDEO_CODEC_VP9 = 13, /** @@ -1176,6 +1192,28 @@ enum VIDEO_CODEC_TYPE { VIDEO_CODEC_GENERIC_JPEG = 20, }; +/** + * Camera focal length type. + */ +enum CAMERA_FOCAL_LENGTH_TYPE { + /** + * By default, there are no wide-angle and ultra-wide-angle properties. + */ + CAMERA_FOCAL_LENGTH_DEFAULT = 0, + /** + * Lens with focal length from 24mm to 35mm. + */ + CAMERA_FOCAL_LENGTH_WIDE_ANGLE = 1, + /** + * Lens with focal length of less than 24mm. + */ + CAMERA_FOCAL_LENGTH_ULTRA_WIDE = 2, + /** + * Telephoto lens. + */ + CAMERA_FOCAL_LENGTH_TELEPHOTO = 3, +}; + /** * The CC (Congestion Control) mode options. */ @@ -1311,6 +1349,10 @@ enum AUDIO_CODEC_TYPE { * 12: LPCNET. */ AUDIO_CODEC_LPCNET = 12, + /** + * 13: Opus codec, supporting 3 to 8 channels audio. + */ + AUDIO_CODEC_OPUSMC = 13, }; /** @@ -1511,13 +1553,38 @@ enum H264PacketizeMode { */ enum VIDEO_STREAM_TYPE { /** - * 0: The high-quality video stream, which has a higher resolution and bitrate. + * 0: The high-quality video stream, which has the highest resolution and bitrate. */ VIDEO_STREAM_HIGH = 0, /** - * 1: The low-quality video stream, which has a lower resolution and bitrate. + * 1: The low-quality video stream, which has the lowest resolution and bitrate. */ VIDEO_STREAM_LOW = 1, + /** + * 4: The video stream of layer_1, which has a lower resolution and bitrate than VIDEO_STREAM_HIGH. + */ + VIDEO_STREAM_LAYER_1 = 4, + /** + * 5: The video stream of layer_2, which has a lower resolution and bitrate than VIDEO_STREAM_LAYER_1. + */ + VIDEO_STREAM_LAYER_2 = 5, + /** + * 6: The video stream of layer_3, which has a lower resolution and bitrate than VIDEO_STREAM_LAYER_2. + */ + VIDEO_STREAM_LAYER_3 = 6, + /** + * 7: The video stream of layer_4, which has a lower resolution and bitrate than VIDEO_STREAM_LAYER_3. + */ + VIDEO_STREAM_LAYER_4 = 7, + /** + * 8: The video stream of layer_5, which has a lower resolution and bitrate than VIDEO_STREAM_LAYER_4. + */ + VIDEO_STREAM_LAYER_5 = 8, + /** + * 9: The video stream of layer_6, which has a lower resolution and bitrate than VIDEO_STREAM_LAYER_5. + */ + VIDEO_STREAM_LAYER_6 = 9, + }; struct VideoSubscriptionOptions { @@ -1563,7 +1630,8 @@ struct EncodedVideoFrameInfo { trackId(0), captureTimeMs(0), decodeTimeMs(0), - streamType(VIDEO_STREAM_HIGH) {} + streamType(VIDEO_STREAM_HIGH), + presentationMs(-1) {} EncodedVideoFrameInfo(const EncodedVideoFrameInfo& rhs) : uid(rhs.uid), @@ -1576,7 +1644,8 @@ struct EncodedVideoFrameInfo { trackId(rhs.trackId), captureTimeMs(rhs.captureTimeMs), decodeTimeMs(rhs.decodeTimeMs), - streamType(rhs.streamType) {} + streamType(rhs.streamType), + presentationMs(rhs.presentationMs) {} EncodedVideoFrameInfo& operator=(const EncodedVideoFrameInfo& rhs) { if (this == &rhs) return *this; @@ -1591,6 +1660,7 @@ struct EncodedVideoFrameInfo { captureTimeMs = rhs.captureTimeMs; decodeTimeMs = rhs.decodeTimeMs; streamType = rhs.streamType; + presentationMs = rhs.presentationMs; return *this; } @@ -1642,6 +1712,8 @@ struct EncodedVideoFrameInfo { */ VIDEO_STREAM_TYPE streamType; + // @technical preview + int64_t presentationMs; }; /** @@ -1691,17 +1763,27 @@ struct AdvanceOptions { */ COMPRESSION_PREFERENCE compressionPreference; + /** + * Whether to encode and send the alpha data to the remote when alpha data is present. + * The default value is false. + */ + bool encodeAlpha; + AdvanceOptions() : encodingPreference(PREFER_AUTO), - compressionPreference(PREFER_LOW_LATENCY) {} + compressionPreference(PREFER_LOW_LATENCY), + encodeAlpha(false) {} AdvanceOptions(ENCODING_PREFERENCE encoding_preference, - COMPRESSION_PREFERENCE compression_preference) : + COMPRESSION_PREFERENCE compression_preference, + bool encode_alpha) : encodingPreference(encoding_preference), - compressionPreference(compression_preference) {} + compressionPreference(compression_preference), + encodeAlpha(encode_alpha) {} bool operator==(const AdvanceOptions& rhs) const { return encodingPreference == rhs.encodingPreference && - compressionPreference == rhs.compressionPreference; + compressionPreference == rhs.compressionPreference && + encodeAlpha == rhs.encodeAlpha; } }; @@ -1724,6 +1806,17 @@ enum VIDEO_MIRROR_MODE_TYPE { VIDEO_MIRROR_MODE_DISABLED = 2, }; +#if defined(__APPLE__) && TARGET_OS_IOS +/** + * Camera capturer configuration for format type. + */ +enum CAMERA_FORMAT_TYPE { + /** 0: (Default) NV12. */ + CAMERA_FORMAT_NV12, + /** 1: BGRA. */ + CAMERA_FORMAT_BGRA, +}; +#endif /** Supported codec type bit mask. */ enum CODEC_CAP_MASK { @@ -1762,6 +1855,14 @@ struct CodecCapInfo { CodecCapInfo(): codecType(VIDEO_CODEC_NONE), codecCapMask(0) {} }; +/** FocalLengthInfo contains the IDs of the front and rear cameras, along with the wide-angle types. */ +struct FocalLengthInfo { + /** The camera direction. */ + int cameraDirection; + /** Camera focal segment type. */ + CAMERA_FOCAL_LENGTH_TYPE focalLengthType; +}; + /** * The definition of the VideoEncoderConfiguration struct. */ @@ -1873,7 +1974,7 @@ struct VideoEncoderConfiguration { AdvanceOptions advanceOptions; VideoEncoderConfiguration(const VideoDimensions& d, int f, int b, ORIENTATION_MODE m, VIDEO_MIRROR_MODE_TYPE mirror = VIDEO_MIRROR_MODE_DISABLED) - : codecType(VIDEO_CODEC_H265), + : codecType(VIDEO_CODEC_NONE), dimensions(d), frameRate(f), bitrate(b), @@ -1881,9 +1982,9 @@ struct VideoEncoderConfiguration { orientationMode(m), degradationPreference(MAINTAIN_QUALITY), mirrorMode(mirror), - advanceOptions(PREFER_AUTO, PREFER_LOW_LATENCY) {} + advanceOptions(PREFER_AUTO, PREFER_LOW_LATENCY, false) {} VideoEncoderConfiguration(int width, int height, int f, int b, ORIENTATION_MODE m, VIDEO_MIRROR_MODE_TYPE mirror = VIDEO_MIRROR_MODE_DISABLED) - : codecType(VIDEO_CODEC_H265), + : codecType(VIDEO_CODEC_NONE), dimensions(width, height), frameRate(f), bitrate(b), @@ -1891,7 +1992,7 @@ struct VideoEncoderConfiguration { orientationMode(m), degradationPreference(MAINTAIN_QUALITY), mirrorMode(mirror), - advanceOptions(PREFER_AUTO, PREFER_LOW_LATENCY) {} + advanceOptions(PREFER_AUTO, PREFER_LOW_LATENCY, false) {} VideoEncoderConfiguration(const VideoEncoderConfiguration& config) : codecType(config.codecType), dimensions(config.dimensions), @@ -1903,7 +2004,7 @@ struct VideoEncoderConfiguration { mirrorMode(config.mirrorMode), advanceOptions(config.advanceOptions) {} VideoEncoderConfiguration() - : codecType(VIDEO_CODEC_H265), + : codecType(VIDEO_CODEC_NONE), dimensions(FRAME_WIDTH_960, FRAME_HEIGHT_540), frameRate(FRAME_RATE_FPS_15), bitrate(STANDARD_BITRATE), @@ -1911,7 +2012,7 @@ struct VideoEncoderConfiguration { orientationMode(ORIENTATION_MODE_ADAPTIVE), degradationPreference(MAINTAIN_QUALITY), mirrorMode(VIDEO_MIRROR_MODE_DISABLED), - advanceOptions(PREFER_AUTO, PREFER_LOW_LATENCY) {} + advanceOptions(PREFER_AUTO, PREFER_LOW_LATENCY, false) {} VideoEncoderConfiguration& operator=(const VideoEncoderConfiguration& rhs) { if (this == &rhs) return *this; @@ -1985,15 +2086,78 @@ struct SimulcastStreamConfig { */ int kBitrate; /** - * he capture frame rate (fps) of the local video. The default value is 5. + * The capture frame rate (fps) of the local video. The default value is 5. */ int framerate; SimulcastStreamConfig() : dimensions(160, 120), kBitrate(65), framerate(5) {} + SimulcastStreamConfig(const SimulcastStreamConfig& other) : dimensions(other.dimensions), kBitrate(other.kBitrate), framerate(other.framerate) {} bool operator==(const SimulcastStreamConfig& rhs) const { return dimensions == rhs.dimensions && kBitrate == rhs.kBitrate && framerate == rhs.framerate; } }; +/** + * The configuration of the multi-layer video stream. + */ +struct SimulcastConfig { + /** + * The index of multi-layer video stream + */ + enum StreamLayerIndex { + /** + * 0: video stream index of layer_1 + */ + STREAM_LAYER_1 = 0, + /** + * 1: video stream index of layer_2 + */ + STREAM_LAYER_2 = 1, + /** + * 2: video stream index of layer_3 + */ + STREAM_LAYER_3 = 2, + /** + * 3: video stream index of layer_4 + */ + STREAM_LAYER_4 = 3, + /** + * 4: video stream index of layer_5 + */ + STREAM_LAYER_5 = 4, + /** + * 5: video stream index of layer_6 + */ + STREAM_LAYER_6 = 5, + /** + * 6: video stream index of low + */ + STREAM_LOW = 6, + /** + * 7: max count of video stream layers + */ + STREAM_LAYER_COUNT_MAX = 7 + }; + struct StreamLayerConfig { + /** + * The video frame dimension. The default value is 0. + */ + VideoDimensions dimensions; + /** + * The capture frame rate (fps) of the local video. The default value is 0. + */ + int framerate; + /** + * Whether to enable the corresponding layer of video stream. The default value is false. + */ + bool enable; + StreamLayerConfig() : dimensions(0, 0), framerate(0), enable(false) {} + }; + + /** + * The array of StreamLayerConfig, which contains STREAM_LAYER_COUNT_MAX layers of video stream at most. + */ + StreamLayerConfig configs[STREAM_LAYER_COUNT_MAX]; +}; /** * The location of the target area relative to the screen or window. If you do not set this parameter, * the SDK selects the whole screen or window. @@ -2591,6 +2755,10 @@ enum VIDEO_APPLICATION_SCENARIO_TYPE { * 1: Meeting Scenario. This scenario is the best QoE practice of meeting application. */ APPLICATION_SCENARIO_MEETING = 1, + /** + * 2: Video Call Scenario. This scenario is used to optimize the video experience in video application, like 1v1 video call. + */ + APPLICATION_SCENARIO_1V1 = 2, }; /** @@ -2635,6 +2803,27 @@ enum CAPTURE_BRIGHTNESS_LEVEL_TYPE { CAPTURE_BRIGHTNESS_LEVEL_DARK = 2, }; +enum CAMERA_STABILIZATION_MODE { + /** The camera stabilization mode is disabled. + */ + CAMERA_STABILIZATION_MODE_OFF = -1, + /** device choose stabilization mode automatically. + */ + CAMERA_STABILIZATION_MODE_AUTO = 0, + /** stabilization mode level 1. + */ + CAMERA_STABILIZATION_MODE_LEVEL_1 = 1, + /** stabilization mode level 2. + */ + CAMERA_STABILIZATION_MODE_LEVEL_2 = 2, + /** stabilization mode level 3. + */ + CAMERA_STABILIZATION_MODE_LEVEL_3 = 3, + /** The maximum level of the camera stabilization mode. + */ + CAMERA_STABILIZATION_MODE_MAX_LEVEL = CAMERA_STABILIZATION_MODE_LEVEL_3, +}; + /** * Local audio states. */ @@ -2788,6 +2977,16 @@ enum LOCAL_VIDEO_STREAM_REASON { * Check whether the ID of the video device is valid. */ LOCAL_VIDEO_STREAM_REASON_DEVICE_INVALID_ID = 10, + /** + * 14: (Android only) Video capture was interrupted, possibly due to the camera being occupied + * or some policy reasons such as background termination. + */ + LOCAL_VIDEO_STREAM_REASON_DEVICE_INTERRUPT = 14, + /** + * 15: (Android only) The device may need to be shut down and restarted to restore camera function, + * or there may be a persistent hardware problem. + */ + LOCAL_VIDEO_STREAM_REASON_DEVICE_FATAL_ERROR = 15, /** * 101: The current video capture device is unavailable due to excessive system pressure. */ @@ -2831,7 +3030,7 @@ enum LOCAL_VIDEO_STREAM_REASON { LOCAL_VIDEO_STREAM_REASON_SCREEN_CAPTURE_WINDOW_HIDDEN = 25, /** 26: (Windows only) The local screen capture window is recovered from its hidden state. */ LOCAL_VIDEO_STREAM_REASON_SCREEN_CAPTURE_WINDOW_RECOVER_FROM_HIDDEN = 26, - /** 27:(Windows only) The window is recovered from miniminzed */ + /** 27: (Windows and macOS only) The window is recovered from miniminzed */ LOCAL_VIDEO_STREAM_REASON_SCREEN_CAPTURE_WINDOW_RECOVER_FROM_MINIMIZED = 27, /** * 28: The screen capture paused. @@ -2843,6 +3042,8 @@ enum LOCAL_VIDEO_STREAM_REASON { LOCAL_VIDEO_STREAM_REASON_SCREEN_CAPTURE_PAUSED = 28, /** 29: The screen capture is resumed. */ LOCAL_VIDEO_STREAM_REASON_SCREEN_CAPTURE_RESUMED = 29, + /** 30: The shared display has been disconnected */ + LOCAL_VIDEO_STREAM_REASON_SCREEN_CAPTURE_DISPLAY_DISCONNECTED = 30, }; @@ -2920,6 +3121,14 @@ enum REMOTE_AUDIO_STATE_REASON * 7: The remote user leaves the channel. */ REMOTE_AUDIO_REASON_REMOTE_OFFLINE = 7, + /** + * 8: The local user does not receive any audio packet from remote user. + */ + REMOTE_AUDIO_REASON_NO_PACKET_RECEIVE = 8, + /** + * 9: The local user receives remote audio packet but fails to play. + */ + REMOTE_AUDIO_REASON_LOCAL_PLAY_FAILED = 9, }; /** @@ -3040,7 +3249,7 @@ enum REMOTE_USER_STATE { struct VideoTrackInfo { VideoTrackInfo() : isLocal(false), ownerUid(0), trackId(0), channelId(OPTIONAL_NULLPTR) - , streamType(VIDEO_STREAM_HIGH), codecType(VIDEO_CODEC_H265) + , codecType(VIDEO_CODEC_H265) , encodedFrameOnly(false), sourceType(VIDEO_SOURCE_CAMERA_PRIMARY) , observationPosition(agora::media::base::POSITION_POST_CAPTURER) {} /** @@ -3061,10 +3270,6 @@ struct VideoTrackInfo { * The channel ID of the video track. */ const char* channelId; - /** - * The video stream type: #VIDEO_STREAM_TYPE. - */ - VIDEO_STREAM_TYPE streamType; /** * The video codec type: #VIDEO_CODEC_TYPE. */ @@ -4112,12 +4317,14 @@ enum CLIENT_ROLE_CHANGE_FAILED_REASON { CLIENT_ROLE_CHANGE_FAILED_NOT_AUTHORIZED = 2, /** * 3: The operation of changing role is timeout. + * @deprecated This reason is deprecated. */ - CLIENT_ROLE_CHANGE_FAILED_REQUEST_TIME_OUT = 3, + CLIENT_ROLE_CHANGE_FAILED_REQUEST_TIME_OUT __deprecated = 3, /** * 4: The operation of changing role is interrupted since we lost connection with agora service. + * @deprecated This reason is deprecated. */ - CLIENT_ROLE_CHANGE_FAILED_CONNECTION_FAILED = 4, + CLIENT_ROLE_CHANGE_FAILED_CONNECTION_FAILED __deprecated = 4, }; /** @@ -4359,6 +4566,85 @@ struct BeautyOptions { BeautyOptions() : lighteningContrastLevel(LIGHTENING_CONTRAST_NORMAL), lighteningLevel(0), smoothnessLevel(0), rednessLevel(0), sharpnessLevel(0) {} }; +/** Face shape area options. This structure defines options for facial adjustments on different facial areas. + * + * @technical preview + */ +struct FaceShapeAreaOptions { + /** The specific facial area to be adjusted. + */ + enum FACE_SHAPE_AREA { + /** (Default) Invalid area. */ + FACE_SHAPE_AREA_NONE = -1, + /** Head Scale, reduces the size of head. */ + FACE_SHAPE_AREA_HEADSCALE = 0, + /** Forehead, adjusts the size of forehead. */ + FACE_SHAPE_AREA_FOREHEAD = 1, + /** Face Contour, slims the facial contour. */ + FACE_SHAPE_AREA_FACECONTOUR = 2, + /** Face Length, adjusts the length of face. */ + FACE_SHAPE_AREA_FACELENGTH = 3, + /** Face Width, narrows the width of face. */ + FACE_SHAPE_AREA_FACEWIDTH = 4, + /** Cheekbone, adjusts the size of cheekbone. */ + FACE_SHAPE_AREA_CHEEKBONE = 5, + /** Cheek, adjusts the size of cheek. */ + FACE_SHAPE_AREA_CHEEK = 6, + /** Chin, adjusts the length of chin. */ + FACE_SHAPE_AREA_CHIN = 7, + /** Eye Scale, adjusts the size of eyes. */ + FACE_SHAPE_AREA_EYESCALE = 8, + /** Nose Length, adjusts the length of nose. */ + FACE_SHAPE_AREA_NOSELENGTH = 9, + /** Nose Width, adjusts the width of nose. */ + FACE_SHAPE_AREA_NOSEWIDTH = 10, + /** Mouth Scale, adjusts the size of mouth. */ + FACE_SHAPE_AREA_MOUTHSCALE = 11, + }; + + /** The specific facial area to be adjusted, See #FACE_SHAPE_AREA. + */ + FACE_SHAPE_AREA shapeArea; + + /** The intensity of the pinching effect applied to the specified facial area. + * For the following area values: #FACE_SHAPE_AREA_FOREHEAD, #FACE_SHAPE_AREA_FACELENGTH, #FACE_SHAPE_AREA_CHIN, #FACE_SHAPE_AREA_NOSELENGTH, #FACE_SHAPE_AREA_NOSEWIDTH, #FACE_SHAPE_AREA_MOUTHSCALE, the value ranges from -100 to 100. + * The default value is 0. The greater the absolute value, the stronger the intensity applied to the specified facial area, and negative values indicate the opposite direction. + * For enumeration values other than the above, the value ranges from 0 to 100. The default value is 0. The greater the value, the stronger the intensity applied to the specified facial area. + */ + int shapeIntensity; + + FaceShapeAreaOptions(FACE_SHAPE_AREA shapeArea, int areaIntensity) : shapeArea(shapeArea), shapeIntensity(areaIntensity) {} + + FaceShapeAreaOptions() : shapeArea(FACE_SHAPE_AREA_NONE), shapeIntensity(0) {} +}; + +/** Face shape beauty options. This structure defines options for facial adjustments of different facial styles. + * + * @technical preview + */ +struct FaceShapeBeautyOptions { + /** The face shape style. + */ + enum FACE_SHAPE_BEAUTY_STYLE { + /** (Default) Female face shape style. */ + FACE_SHAPE_BEAUTY_STYLE_FEMALE = 0, + /** Male face shape style. */ + FACE_SHAPE_BEAUTY_STYLE_MALE = 1, + }; + + /** The face shape style, See #FACE_SHAPE_BEAUTY_STYLE. + */ + FACE_SHAPE_BEAUTY_STYLE shapeStyle; + + /** The intensity of the pinching effect applied to the specified facial style. The value ranges from 0 (original) to 100. The default value is 0. The greater the value, the stronger the intensity applied to face pinching. + */ + int styleIntensity; + + FaceShapeBeautyOptions(FACE_SHAPE_BEAUTY_STYLE shapeStyle, int styleIntensity) : shapeStyle(shapeStyle), styleIntensity(styleIntensity) {} + + FaceShapeBeautyOptions() : shapeStyle(FACE_SHAPE_BEAUTY_STYLE_FEMALE), styleIntensity(50) {} +}; + struct LowlightEnhanceOptions { /** * The low-light enhancement mode. @@ -4682,6 +4968,7 @@ enum VOICE_BEAUTIFIER_PRESET { * - `ROOM_ACOUSTICS_PHONOGRAPH` * - `ROOM_ACOUSTICS_SPACIAL` * - `ROOM_ACOUSTICS_ETHEREAL` + * - `ROOM_ACOUSTICS_CHORUS` * - `VOICE_CHANGER_EFFECT_UNCLE` * - `VOICE_CHANGER_EFFECT_OLDMAN` * - `VOICE_CHANGER_EFFECT_BOY` @@ -4743,6 +5030,14 @@ enum AUDIO_EFFECT_PRESET { * setting this enumerator. */ ROOM_ACOUSTICS_VIRTUAL_SURROUND_SOUND = 0x02010900, + /** The voice effect for chorus. + * + * @note: To achieve better audio effect quality, Agora recommends calling \ref + * IRtcEngine::setAudioProfile "setAudioProfile" and setting the `profile` parameter to + * `AUDIO_PROFILE_MUSIC_HIGH_QUALITY(4)` or `AUDIO_PROFILE_MUSIC_HIGH_QUALITY_STEREO(5)` before + * setting this enumerator. + */ + ROOM_ACOUSTICS_CHORUS = 0x02010D00, /** A middle-aged man's voice. * * @note @@ -4875,6 +5170,41 @@ enum HEADPHONE_EQUALIZER_PRESET { HEADPHONE_EQUALIZER_INEAR = 0x04000002 }; +/** The options for SDK voice AI tuner. + */ +enum VOICE_AI_TUNER_TYPE { + /** Uncle, deep and magnetic male voice. + */ + VOICE_AI_TUNER_MATURE_MALE, + /** Fresh male, refreshing and sweet male voice. + */ + VOICE_AI_TUNER_FRESH_MALE, + /** Big sister, deep and charming female voice. + */ + VOICE_AI_TUNER_ELEGANT_FEMALE, + /** Lolita, high-pitched and cute female voice. + */ + VOICE_AI_TUNER_SWEET_FEMALE, + /** Warm man singing, warm and melodic male voice that is suitable for male lyrical songs. + */ + VOICE_AI_TUNER_WARM_MALE_SINGING, + /** Gentle female singing, soft and delicate female voice that is suitable for female lyrical songs. + */ + VOICE_AI_TUNER_GENTLE_FEMALE_SINGING, + /** Smoky uncle singing, unique husky male voice that is suitable for rock or blues songs. + */ + VOICE_AI_TUNER_HUSKY_MALE_SINGING, + /** Warm big sister singing, warm and mature female voice that is suitable for emotionally powerful songs. + */ + VOICE_AI_TUNER_WARM_ELEGANT_FEMALE_SINGING, + /** Forceful male singing, strong and powerful male voice that is suitable for passionate songs. + */ + VOICE_AI_TUNER_POWERFUL_MALE_SINGING, + /** Dreamy female singing, dreamlike and soft female voice that is suitable for airy and dream-like songs. + */ + VOICE_AI_TUNER_DREAMY_FEMALE_SINGING, +}; + /** * Screen sharing configurations. */ @@ -5187,6 +5517,10 @@ enum AREA_CODE { AREA_CODE_GLOB = (0xFFFFFFFF) }; +/** + Extra region code + @technical preview +*/ enum AREA_CODE_EX { /** * Oceania @@ -5212,6 +5546,10 @@ enum AREA_CODE_EX { * United States */ AREA_CODE_US = 0x00000800, + /** + * Russia + */ + AREA_CODE_RU = 0x00001000, /** * The global area (except China) */ @@ -5530,10 +5868,13 @@ struct EncryptionConfig { * In this case, ensure that this parameter is not 0. */ uint8_t encryptionKdfSalt[32]; + + bool datastreamEncryptionEnabled; EncryptionConfig() : encryptionMode(AES_128_GCM2), - encryptionKey(OPTIONAL_NULLPTR) + encryptionKey(OPTIONAL_NULLPTR), + datastreamEncryptionEnabled(false) { memset(encryptionKdfSalt, 0, sizeof(encryptionKdfSalt)); } @@ -5573,13 +5914,21 @@ enum ENCRYPTION_ERROR_TYPE { */ ENCRYPTION_ERROR_INTERNAL_FAILURE = 0, /** - * 1: Decryption errors. Ensure that the receiver and the sender use the same encryption mode and key. + * 1: MediaStream decryption errors. Ensure that the receiver and the sender use the same encryption mode and key. */ ENCRYPTION_ERROR_DECRYPTION_FAILURE = 1, /** - * 2: Encryption errors. + * 2: MediaStream encryption errors. */ ENCRYPTION_ERROR_ENCRYPTION_FAILURE = 2, + /** + * 3: DataStream decryption errors. Ensure that the receiver and the sender use the same encryption mode and key. + */ + ENCRYPTION_ERROR_DATASTREAM_DECRYPTION_FAILURE = 3, + /** + * 4: DataStream encryption errors. + */ + ENCRYPTION_ERROR_DATASTREAM_ENCRYPTION_FAILURE = 4, }; enum UPLOAD_ERROR_REASON @@ -5621,8 +5970,8 @@ enum STREAM_SUBSCRIBE_STATE { * - Calls `enableLocalAudio(false)` or `enableLocalVideo(false)` to disable the local audio or video capture. * - The role of the remote user is audience. * - The local user calls the following methods to stop receiving remote streams: - * - Calls `muteRemoteAudioStream(true)`, `muteAllRemoteAudioStreams(true)` or `setDefaultMuteAllRemoteAudioStreams(true)` to stop receiving the remote audio streams. - * - Calls `muteRemoteVideoStream(true)`, `muteAllRemoteVideoStreams(true)` or `setDefaultMuteAllRemoteVideoStreams(true)` to stop receiving the remote video streams. + * - Calls `muteRemoteAudioStream(true)`, `muteAllRemoteAudioStreams(true)` to stop receiving the remote audio streams. + * - Calls `muteRemoteVideoStream(true)`, `muteAllRemoteVideoStreams(true)` to stop receiving the remote video streams. */ SUB_STATE_NO_SUBSCRIBED = 1, /** @@ -5713,7 +6062,12 @@ enum EAR_MONITORING_FILTER_TYPE { /** * 4: Enable noise suppression to the in-ear monitor. */ - EAR_MONITORING_FILTER_NOISE_SUPPRESSION = (1<<2) + EAR_MONITORING_FILTER_NOISE_SUPPRESSION = (1<<2), + /** + * 32768: Enable audio filters by reuse post-processing filter to the in-ear monitor. + * This bit is intended to be used in exclusive mode, which means, if this bit is set, all other bits will be disregarded. + */ + EAR_MONITORING_FILTER_REUSE_POST_PROCESSING_FILTER = (1<<15), }; /** @@ -5993,7 +6347,13 @@ struct LocalAccessPointConfiguration { /** Local proxy connection, advanced Config info. */ AdvancedConfigInfo advancedConfig; - LocalAccessPointConfiguration() : ipList(NULL), ipListSize(0), domainList(NULL), domainListSize(0), verifyDomainName(NULL), mode(ConnectivityFirst) {} + /** + * Whether to disable vos-aut: + - true: (Default)disable vos-aut. + - false: not disable vos-aut + */ + bool disableAut; + LocalAccessPointConfiguration() : ipList(NULL), ipListSize(0), domainList(NULL), domainListSize(0), verifyDomainName(NULL), mode(ConnectivityFirst), disableAut(true) {} }; /** diff --git a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/AgoraExtensionVersion.h b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/AgoraExtensionVersion.h index 988e5ecc8..da4d563a3 100644 --- a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/AgoraExtensionVersion.h +++ b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/AgoraExtensionVersion.h @@ -58,6 +58,7 @@ class IExtensionProvider; class IExtensionProviderV2; class IExtensionProviderV3; class IAudioFilter; +class IAudioFilterV2; class IExtensionVideoFilter; class IScreenCaptureSource; @@ -85,6 +86,13 @@ struct ExtensionInterfaceVersion { } }; +template <> +struct ExtensionInterfaceVersion { + static ExtensionVersion Version() { + return BUMP_MAJOR_VERSION(ExtensionInterfaceVersion::Version()); + } +}; + template <> struct ExtensionInterfaceVersion { static ExtensionVersion Version() { diff --git a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/AgoraMediaBase.h b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/AgoraMediaBase.h index 15dfd4b38..8120acb3f 100644 --- a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/AgoraMediaBase.h +++ b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/AgoraMediaBase.h @@ -35,6 +35,32 @@ static const unsigned int DEFAULT_CONNECTION_ID = 0; static const unsigned int DUMMY_CONNECTION_ID = (std::numeric_limits::max)(); struct EncodedVideoFrameInfo; +/** +* The definition of extension context types. +**/ +struct ExtensionContext { + /** + * Whether the uid is valid. + * - true: The uid is valid. + * - false: The uid is invalid. + */ + bool isValid; + /** + * The ID of the user. + * A uid of 0 indicates the local user, and a uid greater than 0 represents a remote user. + */ + uid_t uid; + /** + * The provider name of the current extension. + */ + const char *providerName; + /** + * The extension name of the current extension. + */ + const char *extensionName; + ExtensionContext():isValid(false), uid(0), providerName(NULL), extensionName(NULL) {} +}; + /** * Video source types definition. @@ -88,6 +114,9 @@ enum VIDEO_SOURCE_TYPE { /** Video for fourth screen sharing. */ VIDEO_SOURCE_SCREEN_FOURTH = 14, + /** Video for voice drive. + */ + VIDEO_SOURCE_SPEECH_DRIVEN = 15, VIDEO_SOURCE_UNKNOWN = 100 }; @@ -122,9 +151,9 @@ enum AudioRoute */ ROUTE_LOUDSPEAKER = 4, /** - * The Bluetooth Headset via HFP. + * The Bluetooth Device via HFP. */ - ROUTE_HEADSETBLUETOOTH = 5, + ROUTE_BLUETOOTH_DEVICE_HFP = 5, /** * The USB. */ @@ -142,9 +171,9 @@ enum AudioRoute */ ROUTE_AIRPLAY = 9, /** - * The Bluetooth Speaker via A2DP. + * The Bluetooth Device via A2DP. */ - ROUTE_BLUETOOTH_SPEAKER = 10, + ROUTE_BLUETOOTH_DEVICE_A2DP = 10, }; /** @@ -242,6 +271,10 @@ enum MEDIA_SOURCE_TYPE { * 12: Video for transcoded. */ TRANSCODED_VIDEO_SOURCE = 12, + /** + * 13: Video for voice drive. + */ + SPEECH_DRIVEN_VIDEO_SOURCE = 13, /** * 100: Internal Usage only. */ @@ -395,24 +428,31 @@ struct AudioPcmFrame { rtc::BYTES_PER_SAMPLE bytes_per_sample; /** The audio frame data. */ int16_t data_[kMaxDataSizeSamples]; + + /** + * @technical preview + * data_[kMaxDataSizeSamples] is real stereo data + */ + bool is_stereo_; AudioPcmFrame& operator=(const AudioPcmFrame& src) { - if(this == &src) { + if (this == &src) { return *this; } - this->capture_timestamp = src.capture_timestamp; - this->samples_per_channel_ = src.samples_per_channel_; - this->sample_rate_hz_ = src.sample_rate_hz_; - this->bytes_per_sample = src.bytes_per_sample; - this->num_channels_ = src.num_channels_; + capture_timestamp = src.capture_timestamp; + samples_per_channel_ = src.samples_per_channel_; + sample_rate_hz_ = src.sample_rate_hz_; + bytes_per_sample = src.bytes_per_sample; + num_channels_ = src.num_channels_; + is_stereo_ = src.is_stereo_; size_t length = src.samples_per_channel_ * src.num_channels_; if (length > kMaxDataSizeSamples) { length = kMaxDataSizeSamples; } - memcpy(this->data_, src.data_, length * sizeof(int16_t)); + memcpy(data_, src.data_, length * sizeof(int16_t)); return *this; } @@ -422,7 +462,8 @@ struct AudioPcmFrame { samples_per_channel_(0), sample_rate_hz_(0), num_channels_(0), - bytes_per_sample(rtc::TWO_BYTES_PER_SAMPLE) { + bytes_per_sample(rtc::TWO_BYTES_PER_SAMPLE), + is_stereo_(false) { memset(data_, 0, sizeof(data_)); } @@ -431,7 +472,8 @@ struct AudioPcmFrame { samples_per_channel_(src.samples_per_channel_), sample_rate_hz_(src.sample_rate_hz_), num_channels_(src.num_channels_), - bytes_per_sample(src.bytes_per_sample) { + bytes_per_sample(src.bytes_per_sample), + is_stereo_(src.is_stereo_) { size_t length = src.samples_per_channel_ * src.num_channels_; if (length > kMaxDataSizeSamples) { length = kMaxDataSizeSamples; @@ -502,6 +544,10 @@ enum VIDEO_PIXEL_FORMAT { 14: pixel format for iOS CVPixelBuffer BGRA */ VIDEO_CVPIXEL_BGRA = 14, + /** + 15: pixel format for iOS CVPixelBuffer P010(10bit NV12) + */ + VIDEO_CVPIXEL_P010 = 15, /** * 16: I422. */ @@ -510,6 +556,11 @@ enum VIDEO_PIXEL_FORMAT { * 17: ID3D11Texture2D, only support DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B8G8R8A8_TYPELESS, DXGI_FORMAT_NV12 texture format */ VIDEO_TEXTURE_ID3D11TEXTURE2D = 17, + /** + * 18: I010. 10bit I420 data. + * @technical preview + */ + VIDEO_PIXEL_I010 = 18, }; /** @@ -565,6 +616,191 @@ class IVideoFrameMetaInfo { virtual const char* getMetaInfoStr(META_INFO_KEY key) const = 0; }; +struct ColorSpace { + enum PrimaryID { + // The indices are equal to the values specified in T-REC H.273 Table 2. + PRIMARYID_BT709 = 1, + PRIMARYID_UNSPECIFIED = 2, + PRIMARYID_BT470M = 4, + PRIMARYID_BT470BG = 5, + PRIMARYID_SMPTE170M = 6, // Identical to BT601 + PRIMARYID_SMPTE240M = 7, + PRIMARYID_FILM = 8, + PRIMARYID_BT2020 = 9, + PRIMARYID_SMPTEST428 = 10, + PRIMARYID_SMPTEST431 = 11, + PRIMARYID_SMPTEST432 = 12, + PRIMARYID_JEDECP22 = 22, // Identical to EBU3213-E + }; + + enum RangeID { + // The indices are equal to the values specified at + // https://www.webmproject.org/docs/container/#colour for the element Range. + RANGEID_INVALID = 0, + // Limited Rec. 709 color range with RGB values ranging from 16 to 235. + RANGEID_LIMITED = 1, + // Full RGB color range with RGB valees from 0 to 255. + RANGEID_FULL = 2, + // Range is defined by MatrixCoefficients/TransferCharacteristics. + RANGEID_DERIVED = 3, + }; + + enum MatrixID { + // The indices are equal to the values specified in T-REC H.273 Table 4. + MATRIXID_RGB = 0, + MATRIXID_BT709 = 1, + MATRIXID_UNSPECIFIED = 2, + MATRIXID_FCC = 4, + MATRIXID_BT470BG = 5, + MATRIXID_SMPTE170M = 6, + MATRIXID_SMPTE240M = 7, + MATRIXID_YCOCG = 8, + MATRIXID_BT2020_NCL = 9, + MATRIXID_BT2020_CL = 10, + MATRIXID_SMPTE2085 = 11, + MATRIXID_CDNCLS = 12, + MATRIXID_CDCLS = 13, + MATRIXID_BT2100_ICTCP = 14, + }; + + enum TransferID { + // The indices are equal to the values specified in T-REC H.273 Table 3. + TRANSFERID_BT709 = 1, + TRANSFERID_UNSPECIFIED = 2, + TRANSFERID_GAMMA22 = 4, + TRANSFERID_GAMMA28 = 5, + TRANSFERID_SMPTE170M = 6, + TRANSFERID_SMPTE240M = 7, + TRANSFERID_LINEAR = 8, + TRANSFERID_LOG = 9, + TRANSFERID_LOG_SQRT = 10, + TRANSFERID_IEC61966_2_4 = 11, + TRANSFERID_BT1361_ECG = 12, + TRANSFERID_IEC61966_2_1 = 13, + TRANSFERID_BT2020_10 = 14, + TRANSFERID_BT2020_12 = 15, + TRANSFERID_SMPTEST2084 = 16, + TRANSFERID_SMPTEST428 = 17, + TRANSFERID_ARIB_STD_B67 = 18, + }; + + PrimaryID primaries; + TransferID transfer; + MatrixID matrix; + RangeID range; + + ColorSpace() + : primaries(PRIMARYID_UNSPECIFIED), transfer(TRANSFERID_UNSPECIFIED), + matrix(MATRIXID_UNSPECIFIED), range(RANGEID_INVALID) {} + + bool validate() const { + return primaries != PRIMARYID_UNSPECIFIED || transfer != TRANSFERID_UNSPECIFIED || + matrix != MATRIXID_UNSPECIFIED || + range != RANGEID_INVALID; + } +}; + +/** + * The definition of the Hdr10MetadataInfo struct. + */ +struct Hdr10MetadataInfo { + /** + * The x coordinates of the red value in the CIE1931 color space. The values need to normalized to 50,000. + */ + uint16_t redPrimaryX; + /** + * The y coordinates of the red value in the CIE1931 color space. The values need to normalized to 50,000. + */ + uint16_t redPrimaryY; + /** + * The x coordinates of the green value in the CIE1931 color space. The values need to normalized to 50,000. + */ + uint16_t greenPrimaryX; + /** + * The y coordinates of the green value in the CIE1931 color space. The values need to normalized to 50,000. + */ + uint16_t greenPrimaryY; + /** + * The x coordinates of the blue value in the CIE1931 color space. The values need to normalized to 50,000. + */ + uint16_t bluePrimaryX; + /** + * The y coordinates of the blue value in the CIE1931 color space. The values need to normalized to 50,000. + */ + uint16_t bluePrimaryY; + /** + * The x coordinates of the white point in the CIE1931 color space.The values need to normalized to 50,000. + */ + uint16_t whitePointX; + /** + * The y coordinates of the white point in the CIE1931 color space.The values need to normalized to 50,000. + */ + uint16_t whitePointY; + /** + * The maximum number of nits of the display used to master the content. The values need to normalized to 10,000. + */ + unsigned int maxMasteringLuminance; + /** + * The minimum number of nits of the display used to master the content. The values need to normalized to 10,000. + */ + unsigned int minMasteringLuminance; + /** + * The maximum content light level (MaxCLL). This is the nit value corresponding to the brightest pixel used anywhere in the content. + */ + uint16_t maxContentLightLevel; + /** + * The maximum frame average light level (MaxFALL). This is the nit value corresponding to the average luminance of the frame which has the brightest average luminance anywhere in the content. + */ + uint16_t maxFrameAverageLightLevel; + + Hdr10MetadataInfo() + : redPrimaryX(0), + redPrimaryY(0), + greenPrimaryX(0), + greenPrimaryY(0), + bluePrimaryX(0), + bluePrimaryY(0), + whitePointX(0), + whitePointY(0), + maxMasteringLuminance(0), + minMasteringLuminance(0), + maxContentLightLevel(0), + maxFrameAverageLightLevel(0){} + + bool validate() const { + return maxContentLightLevel >= 0 && maxContentLightLevel <= 20000 && + maxFrameAverageLightLevel >= 0 && + maxFrameAverageLightLevel <= 20000; + } +}; + +/** + * The relative position between alphabuffer and the frame. + */ +enum ALPHA_STITCH_MODE { + /** + * 0: Normal frame without alphabuffer stitched + */ + NO_ALPHA_STITCH = 0, + /** + * 1: Alphabuffer is above the frame + */ + ALPHA_STITCH_UP = 1, + /** + * 2: Alphabuffer is below the frame + */ + ALPHA_STITCH_BELOW = 2, + /** + * 3: Alphabuffer is on the left of frame + */ + ALPHA_STITCH_LEFT = 3, + /** + * 4: Alphabuffer is on the right of frame + */ + ALPHA_STITCH_RIGHT = 4, +}; + + /** * The definition of the ExternalVideoFrame struct. */ @@ -584,11 +820,14 @@ struct ExternalVideoFrame { eglContext(NULL), eglType(EGL_CONTEXT10), textureId(0), - metadata_buffer(NULL), - metadata_size(0), + fenceObject(0), + metadataBuffer(NULL), + metadataSize(0), alphaBuffer(NULL), - d3d11_texture_2d(NULL), - texture_slice_index(0){} + fillAlphaBuffer(false), + alphaStitchMode(NO_ALPHA_STITCH), + d3d11Texture2d(NULL), + textureSliceIndex(0){} /** * The EGL context type. @@ -690,6 +929,11 @@ struct ExternalVideoFrame { * [Texture related parameter] Incoming 4 × 4 transformational matrix. The typical value is a unit matrix. */ int textureId; + /** + * [Texture related parameter] The fence object related to the textureId parameter, indicating the synchronization status of the video data in Texture format. + * The default value is 0 + */ + long long fenceObject; /** * [Texture related parameter] Incoming 4 × 4 transformational matrix. The typical value is a unit matrix. */ @@ -698,28 +942,53 @@ struct ExternalVideoFrame { * [Texture related parameter] The MetaData buffer. * The default value is NULL */ - uint8_t* metadata_buffer; + uint8_t* metadataBuffer; /** * [Texture related parameter] The MetaData size. * The default value is 0 */ - int metadata_size; + int metadataSize; /** - * Indicates the output data of the portrait segmentation algorithm, which is consistent with the size of the video frame. - * The value range of each pixel is [0,255], where 0 represents the background; 255 represents the foreground (portrait). - * The default value is NULL + * Indicates the alpha channel of current frame, which is consistent with the dimension of the video frame. + * The value range of each pixel is [0,255], where 0 represents the background; 255 represents the foreground. + * The default value is NULL. */ uint8_t* alphaBuffer; + /** + * [For bgra or rgba only] Extract alphaBuffer from bgra or rgba data. Set it true if you do not explicitly specify the alphabuffer. + * The default value is false + */ + bool fillAlphaBuffer; + /** + * The relative position between alphabuffer and the frame. + * 0: Normal frame; + * 1: Alphabuffer is above the frame; + * 2: Alphabuffer is below the frame; + * 3: Alphabuffer is on the left of frame; + * 4: Alphabuffer is on the right of frame; + * The default value is 0. + */ + ALPHA_STITCH_MODE alphaStitchMode; /** * [For Windows only] The pointer of ID3D11Texture2D used by the video frame. */ - void *d3d11_texture_2d; + void *d3d11Texture2d; /** * [For Windows only] The index of ID3D11Texture2D array used by the video frame. */ - int texture_slice_index; + int textureSliceIndex; + + /** + * metadata info used for hdr video data + */ + Hdr10MetadataInfo hdr10MetadataInfo; + + /** + * The ColorSpace of the video frame. + */ + ColorSpace colorSpace; }; /** @@ -745,6 +1014,7 @@ struct VideoFrame { textureId(0), d3d11Texture2d(NULL), alphaBuffer(NULL), + alphaStitchMode(NO_ALPHA_STITCH), pixelBuffer(NULL), metaInfo(NULL){ memset(matrix, 0, sizeof(matrix)); @@ -827,11 +1097,21 @@ struct VideoFrame { */ float matrix[16]; /** - * Indicates the output data of the portrait segmentation algorithm, which is consistent with the size of the video frame. - * The value range of each pixel is [0,255], where 0 represents the background; 255 represents the foreground (portrait). - * The default value is NULL + * Indicates the alpha channel of current frame, which is consistent with the dimension of the video frame. + * The value range of each pixel is [0,255], where 0 represents the background; 255 represents the foreground. + * The default value is NULL. */ uint8_t* alphaBuffer; + /** + * The relative position between alphabuffer and the frame. + * 0: Normal frame; + * 1: Alphabuffer is above the frame; + * 2: Alphabuffer is below the frame; + * 3: Alphabuffer is on the left of frame; + * 4: Alphabuffer is on the right of frame; + * The default value is 0. + */ + ALPHA_STITCH_MODE alphaStitchMode; /** *The type of CVPixelBufferRef, for iOS and macOS only. */ @@ -840,6 +1120,16 @@ struct VideoFrame { * The pointer to IVideoFrameMetaInfo, which is the interface to get metainfo contents from VideoFrame. */ IVideoFrameMetaInfo* metaInfo; + + /** + * metadata info used for hdr video data + */ + Hdr10MetadataInfo hdr10MetadataInfo; + + /** + * The ColorSpace of the video frame + */ + ColorSpace colorSpace; }; /** @@ -981,6 +1271,10 @@ class IAudioFrameObserverBase { * The number of the audio track. */ int audioTrackNumber; + /** + * RTP timestamp of the first sample in the audio frame + */ + uint32_t rtpTimestamp; AudioFrame() : type(FRAME_TYPE_PCM16), samplesPerChannel(0), @@ -991,7 +1285,8 @@ class IAudioFrameObserverBase { renderTimeMs(0), avsync_type(0), presentationMs(0), - audioTrackNumber(0) {} + audioTrackNumber(0), + rtpTimestamp(0) {} }; enum AUDIO_FRAME_POSITION { @@ -1609,6 +1904,21 @@ struct MediaRecorderConfiguration { MediaRecorderConfiguration() : storagePath(NULL), containerFormat(FORMAT_MP4), streamType(STREAM_TYPE_BOTH), maxDurationMs(120000), recorderInfoUpdateInterval(0) {} MediaRecorderConfiguration(const char* path, MediaRecorderContainerFormat format, MediaRecorderStreamType type, int duration, int interval) : storagePath(path), containerFormat(format), streamType(type), maxDurationMs(duration), recorderInfoUpdateInterval(interval) {} }; + +class IFaceInfoObserver { +public: + /** + * Occurs when the face info is received. + * @param outFaceInfo The output face info. + * @return + * - true: The face info is valid. + * - false: The face info is invalid. + */ + virtual bool onFaceInfo(const char* outFaceInfo) = 0; + + virtual ~IFaceInfoObserver() {} +}; + /** * Information for the recording file. * diff --git a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/IAgoraFileUploader.h b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/IAgoraFileUploader.h index f0611fe38..f531e4853 100644 --- a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/IAgoraFileUploader.h +++ b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/IAgoraFileUploader.h @@ -9,7 +9,7 @@ #pragma once // NOLINT(build/header_guard) #include "AgoraRefPtr.h" -#include +#include namespace agora { namespace rtc { @@ -38,8 +38,8 @@ struct ImagePayloadData { class IFileUploaderService : public RefCountInterface { public: virtual ~IFileUploaderService() {} - virtual int startImageUpload(const ImagePayloadData* imgData, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; - virtual int stopImageUpload(ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int startImageUpload(const ImagePayloadData* imgData, aosl_ref_t ares = AOSL_REF_INVALID) = 0; + virtual int stopImageUpload(aosl_ref_t ares = AOSL_REF_INVALID) = 0; }; } // namespace rtc } // namespace agora diff --git a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/IAgoraLog.h b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/IAgoraLog.h index 2fae3aa13..20b6416ef 100644 --- a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/IAgoraLog.h +++ b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/IAgoraLog.h @@ -37,6 +37,7 @@ OPTIONAL_ENUM_CLASS LOG_LEVEL { LOG_LEVEL_ERROR = 0x0004, LOG_LEVEL_FATAL = 0x0008, LOG_LEVEL_API_CALL = 0x0010, + LOG_LEVEL_DEBUG = 0x0020, }; /* diff --git a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/IAgoraMediaPlayerSource.h b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/IAgoraMediaPlayerSource.h index 00be02233..99da405bc 100644 --- a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/IAgoraMediaPlayerSource.h +++ b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/IAgoraMediaPlayerSource.h @@ -42,17 +42,6 @@ class IMediaPlayerSource : public RefCountInterface { * - < 0: Failure. */ virtual int open(const char* url, int64_t startPos) = 0; - - /** - * @deprecated - * @brief Open media file or stream with custom soucrce. - * @param startPos Set the starting position for playback, in seconds - * @param observer dataProvider object - * @return - * - 0: Success. - * - < 0: Failure. - */ - virtual int openWithCustomSource(int64_t startPos, media::base::IMediaPlayerCustomDataProvider* provider) __deprecated = 0; /** * Opens a media file with a media file source. diff --git a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/IAgoraParameter.h b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/IAgoraParameter.h index b88969e1d..f50afe9b5 100644 --- a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/IAgoraParameter.h +++ b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/IAgoraParameter.h @@ -39,8 +39,7 @@ * set the video encoder mode (hardware or software) */ #define KEY_RTC_VIDEO_ENABLED_HW_ENCODER "engine.video.enable_hw_encoder" -#define KEY_RTC_VIDEO_HARDWARE_ENCODEING "che.hardware_encoding" -#define KEY_RTC_VIDEO_H264_HWENC "che.video.h264.hwenc" +#define KEY_RTC_VIDEO_HARDWARE_ENCODEING "che.hardware_encoding" // deprecated, please use engine.video.enable_hw_encoder /** * set the hardware video encoder provider (nv for nvidia or qsv for intel) */ @@ -50,7 +49,7 @@ * set the video decoder mode (hardware or software) */ #define KEY_RTC_VIDEO_ENABLED_HW_DECODER "engine.video.enable_hw_decoder" -#define KEY_RTC_VIDEO_HARDWARE_DECODING "che.hardware_decoding" +#define KEY_RTC_VIDEO_HARDWARE_DECODING "che.hardware_decoding" // deprecated, please use engine.video.enable_hw_decoder /** * set the hardware video decoder provider (h264_cuvid(default) or h264_qsv) @@ -115,8 +114,7 @@ /** * set the video codec type, such as "H264", "JPEG" */ -#define KEY_RTC_VIDEO_CODEC_TYPE "engine.video.codec_type" -#define KEY_RTC_VIDEO_MINOR_STREAM_CODEC_TYPE "engine.video.minor_stream_codec_type" +#define KEY_RTC_VIDEO_MINOR_STREAM_CODEC_INDEX "engine.video.minor_stream_codec_index" #define KEY_RTC_VIDEO_CODEC_INDEX "che.video.videoCodecIndex" /** * only use average QP for quality scaling diff --git a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/IAgoraRtmService.h b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/IAgoraRtmService.h index 1ce7c5f91..580790c30 100644 --- a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/IAgoraRtmService.h +++ b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/IAgoraRtmService.h @@ -7,7 +7,7 @@ #pragma once #include -#include +#include namespace agora { @@ -520,7 +520,7 @@ class IChannel { * Sets an event handler for IChannel. * @param eventHandler The pointer to the event handler of IChannel: IChannelEventHandler. */ - virtual int setEventHandler(IChannelEventHandler *eventHandler, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setEventHandler(IChannelEventHandler *eventHandler, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Joins the current channel. * @@ -530,7 +530,7 @@ class IChannel { * - 0: Success. * - < 0: Failure. */ - virtual int join(ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int join(aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Leaves the current channel. * @@ -539,7 +539,7 @@ class IChannel { * - 0: Success. * - < 0: Failure. */ - virtual int leave(ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int leave(aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Sends a channel message. * @@ -550,7 +550,7 @@ class IChannel { * - 0: Success. * - < 0: Failure. */ - virtual int sendMessage(const IMessage *message, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int sendMessage(const IMessage *message, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Updates the channel attributes. * @@ -561,7 +561,7 @@ class IChannel { * - 0: Success. * - < 0: Failure. */ - virtual int updateAttributes(IChannelAttributes *attributes, int64_t &requestId, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int updateAttributes(IChannelAttributes *attributes, int64_t &requestId, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Removes the channel attributes. * @@ -572,7 +572,7 @@ class IChannel { * - 0: Success. * - < 0: Failure. */ - virtual int deleteAttributes(IChannelAttributes *attributes, int64_t &requestId, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int deleteAttributes(IChannelAttributes *attributes, int64_t &requestId, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Gets the current request ID. * @return @@ -675,14 +675,14 @@ class IRtmService { * - 0: Success. * - < 0: Failure. */ - virtual int login(const char *token, const char *userId, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int login(const char *token, const char *userId, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Logs out of the RTM service. * @return * - 0: Success. * - < 0: Failure. */ - virtual int logout(ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int logout(aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Sends a peer message to a specified remote user. * @@ -692,7 +692,7 @@ class IRtmService { * - 0: Success. * - < 0: Failure. */ - virtual int sendMessageToPeer(const char *peerId, const IMessage *message, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int sendMessageToPeer(const char *peerId, const IMessage *message, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Creates an RTM channel. * diff --git a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/IAgoraRtmpStreamingService.h b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/IAgoraRtmpStreamingService.h index 0f6a1eec8..e9c3568c2 100644 --- a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/IAgoraRtmpStreamingService.h +++ b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/IAgoraRtmpStreamingService.h @@ -12,7 +12,7 @@ #include "AgoraRefPtr.h" #include "IAgoraService.h" #include "NGIAgoraRtcConnection.h" -#include +#include namespace agora { namespace rtc { @@ -110,7 +110,7 @@ class IRtmpStreamingService : public RefCountInterface { * - #ERR_NOT_INITIALIZED (7): You have not initialized the RTC engine when publishing the stream. * - #ERR_ALREADY_IN_USE (19): This streaming URL is already in use. Use a new streaming URL for CDN streaming. */ - virtual int startRtmpStreamWithoutTranscoding(const char* url, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int startRtmpStreamWithoutTranscoding(const char* url, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** Publishes the local stream with transcoding to a specified CDN live RTMP address. (CDN live only.) @@ -133,7 +133,7 @@ class IRtmpStreamingService : public RefCountInterface { * - #ERR_NOT_INITIALIZED (7): You have not initialized the RTC engine when publishing the stream. * - #ERR_ALREADY_IN_USE (19): This streaming URL is already in use. Use a new streaming URL for CDN streaming. */ - virtual int startRtmpStreamWithTranscoding(const char* url, const LiveTranscoding& transcoding, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int startRtmpStreamWithTranscoding(const char* url, const LiveTranscoding& transcoding, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** Update the video layout and audio settings for CDN live. (CDN live only.) * @note This method applies to Live Broadcast only. @@ -144,7 +144,7 @@ class IRtmpStreamingService : public RefCountInterface { * - 0: Success. * - < 0: Failure. */ - virtual int updateRtmpTranscoding(const LiveTranscoding& transcoding, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int updateRtmpTranscoding(const LiveTranscoding& transcoding, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** Stop an RTMP stream with transcoding or without transcoding from the CDN. (CDN live only.) * This method removes the RTMP URL address (added by the \ref IRtcEngine::startRtmpStreamWithoutTranscoding "startRtmpStreamWithoutTranscoding" method @@ -163,7 +163,7 @@ class IRtmpStreamingService : public RefCountInterface { * - 0: Success. * - < 0: Failure. */ - virtual int stopRtmpStream(const char* url, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int stopRtmpStream(const char* url, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Registers an RTMP streaming observer. @@ -172,7 +172,7 @@ class IRtmpStreamingService : public RefCountInterface { * - 0: Success. * - < 0: Failure. */ - virtual int registerObserver(IRtmpStreamingObserver* observer, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int registerObserver(IRtmpStreamingObserver* observer, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Releases the RTMP streaming observer created by registerObserver(). * @param observer The pointer to the RTMP streaming observer that you want to release. See \ref agora::rtc::IRtmpStreamingObserver "IRtmpStreamingObserver". diff --git a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/IAgoraService.h b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/IAgoraService.h index 9feb0c914..200807f3b 100644 --- a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/IAgoraService.h +++ b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/IAgoraService.h @@ -8,7 +8,7 @@ #include "IAgoraLog.h" #include "AgoraBase.h" #include "AgoraOptional.h" -#include +#include namespace agora { class ILocalDataChannel; @@ -71,6 +71,8 @@ class IRtmService; namespace base { class IServiceObserver; +class ISyncClient; +struct SyncConfig; /** * The global configurations for \ref agora::base::IAgoraService "AgoraService". @@ -144,9 +146,9 @@ struct AgoraServiceConfiguration { IServiceObserver* serviceObserver; /** - * Thread priority for SDK common threads + * @deprecated Thread priority for SDK common threads */ - Optional threadPriority; + Optional threadPriority __deprecated; /** * Whether use egl context in current thread as sdk‘s root egl context * which shared by all egl related modules. eg. camera capture, video renderer. @@ -286,6 +288,13 @@ struct AudioSessionConfiguration { */ Optional outputNumberOfChannels; +#if defined(WEBRTC_IOS) + /** + * Initialize the AudioSession with the value for category. (iOS only) + */ + Optional category; +#endif + void SetAll(const AudioSessionConfiguration& change) { SetFrom(&playbackAndRecord, change.playbackAndRecord); SetFrom(&chatMode, change.chatMode); @@ -299,6 +308,9 @@ struct AudioSessionConfiguration { SetFrom(&ioBufferDuration, change.ioBufferDuration); SetFrom(&inputNumberOfChannels, change.inputNumberOfChannels); SetFrom(&outputNumberOfChannels, change.outputNumberOfChannels); +#if defined(WEBRTC_IOS) + SetFrom(&category, change.category); +#endif } bool operator==(const AudioSessionConfiguration& o) const { @@ -308,7 +320,12 @@ struct AudioSessionConfiguration { allowBluetooth == o.allowBluetooth && allowBluetoothA2DP == o.allowBluetoothA2DP && sampleRate == o.sampleRate && ioBufferDuration == o.ioBufferDuration && inputNumberOfChannels == o.inputNumberOfChannels && +#if defined(WEBRTC_IOS) + outputNumberOfChannels == o.outputNumberOfChannels && + category == o.category; +#else outputNumberOfChannels == o.outputNumberOfChannels; +#endif } bool operator!=(const AudioSessionConfiguration& o) const { return !(*this == o); } @@ -393,7 +410,7 @@ class IAgoraService { /** * Flush log & cache before exit */ - virtual int atExit(ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int atExit(aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Releases the \ref agora::base::IAgoraService "AgoraService" object. @@ -413,7 +430,7 @@ class IAgoraService { * - 0: Success. * - < 0: Failure. */ - virtual int setAudioSessionPreset(agora::rtc::AUDIO_SCENARIO_TYPE scenario, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setAudioSessionPreset(agora::rtc::AUDIO_SCENARIO_TYPE scenario, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Customizes the audio session configuration. @@ -423,7 +440,7 @@ class IAgoraService { * - 0: Success. * - < 0: Failure. */ - virtual int setAudioSessionConfiguration(const AudioSessionConfiguration& config, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setAudioSessionConfiguration(const AudioSessionConfiguration& config, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Gets the audio session configuration. @@ -454,7 +471,7 @@ class IAgoraService { * - 0: Success. * - < 0: Failure. */ - virtual int setLogFile(const char* filePath, unsigned int fileSize, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setLogFile(const char* filePath, unsigned int fileSize, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Sets the SDK log output filter. * @@ -473,7 +490,7 @@ class IAgoraService { * - 0: Success. * - < 0: Failure. */ - virtual int setLogFilter(unsigned int filters, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setLogFilter(unsigned int filters, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Creates an \ref agora::rtc::IRtcConnection "RtcConnection" object and returns the pointer. @@ -635,12 +652,13 @@ class IAgoraService { * @param audioSource The pointer to the recording device source. See \ref agora::rtc::IRecordingDeviceSource "IRecordingDeviceSource". * @param enableAec Whether enable audio echo cancellation for loopback recording. If loopback * recording device is a virtual sound card, it should be false, or it should be true. + * @param overlap Whether overlap playout signal. * @return * - The pointer to \ref rtc::ILocalAudioTrack "ILocalAudioTrack": Success. * - A null pointer: Failure. */ virtual agora_refptr createRecordingDeviceAudioTrack( - agora_refptr audioSource, bool enableAec) = 0; + agora_refptr audioSource, bool enableAec, bool overlap) = 0; /** * Creates an audio device manager object and returns the pointer. @@ -866,9 +884,9 @@ class IAgoraService { */ virtual rtm::IRtmService* createRtmService() = 0; - virtual int addExtensionObserver(agora::agora_refptr observer, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int addExtensionObserver(agora::agora_refptr observer, aosl_ref_t ares = AOSL_REF_INVALID) = 0; - virtual int removeExtensionObserver(agora::agora_refptr observer, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int removeExtensionObserver(agora::agora_refptr observer, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Creates an audio device manager and returns the pointer. @@ -924,7 +942,7 @@ class IAgoraService { */ virtual int enableExtension( const char* provider_name, const char* extension_name, const char* track_id = NULL, - bool auto_enable_on_track = false, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + bool auto_enable_on_track = false, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Disable extension. * @@ -937,7 +955,7 @@ class IAgoraService { * - < 0: Failure. */ virtual int disableExtension( - const char* provider_name, const char* extension_name, const char* track_id = NULL, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + const char* provider_name, const char* extension_name, const char* track_id = NULL, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Gets the IAgoraParameter object. @@ -956,6 +974,32 @@ class IAgoraService { * - A null pointer: Failure. */ virtual agora_refptr getConfigCenter() = 0; + + /** + * Get the \ref agora::rtc::ISyncClient "ISyncClient" object and return the pointer. + * + * @return + * - The pointer to \ref rtc::ISyncClient "ISyncClient": Success. + * - A null pointer: Failure. + */ + virtual agora_refptr createSyncClient(const base::SyncConfig& config) = 0; + + /** + * Set the logWriter for the sdk log. + * @param logWriter the log writer + * @return + * - 0: Success. + * - < 0: Failure. + */ + virtual int32_t setLogWriter(agora::commons::ILogWriter* logWriter) = 0; + + /** + * Release logWriter for the sdk log. + * @return + * -The pointer to \ref agora::commons::ILogWriter + * - A null pointer: Failure. + */ + virtual agora::commons::ILogWriter* releaseLogWriter() = 0; protected: virtual ~IAgoraService() {} diff --git a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraAudioDeviceManager.h b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraAudioDeviceManager.h index 6374ca11c..621b5a077 100644 --- a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraAudioDeviceManager.h +++ b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraAudioDeviceManager.h @@ -10,7 +10,7 @@ #include "AgoraBase.h" #include "AgoraRefPtr.h" -#include +#include namespace agora { namespace media { namespace base { @@ -37,6 +37,11 @@ struct AudioDeviceInfo { * The name of the device. The maximum name size is 128 bytes. The default value is 0. */ char deviceName[kAdmMaxDeviceNameSize]; + /** + * The type name of the device. such as Built-in, USB, HDMI, etc. The maximum size is 128 bytes. The default value is 0. + * @note This member applies to macOS only. + */ + char deviceTypeName[kAdmMaxDeviceNameSize]; /** * The ID of the device. The maximum size is 128 bytes. The default value is 0. */ @@ -57,6 +62,7 @@ struct AudioDeviceInfo { AudioDeviceInfo() : isCurrentSelected(false), isPlayoutDevice(true) { memset(deviceName, 0, sizeof(deviceName)); + memset(deviceTypeName, 0, sizeof(deviceTypeName)); memset(deviceId, 0, sizeof(deviceId)); } }; @@ -147,7 +153,7 @@ class IRecordingDeviceSource : public RefCountInterface { * - 0: Success. * - < 0: Failure. */ - virtual int startRecording(ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int startRecording(aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Stop the recording device. @@ -155,7 +161,7 @@ class IRecordingDeviceSource : public RefCountInterface { * - 0: Success. * - < 0: Failure. */ - virtual int stopRecording(ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int stopRecording(aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Registers an audio frame observer. @@ -165,7 +171,7 @@ class IRecordingDeviceSource : public RefCountInterface { * - 0: Success. * - < 0: Failure. */ - virtual int registerAudioFrameObserver(media::IAudioPcmFrameSink* observer, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int registerAudioFrameObserver(media::IAudioPcmFrameSink* observer, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Releases the registered IAudioFrameObserver object. @@ -183,7 +189,7 @@ class IRecordingDeviceSource : public RefCountInterface { * - 0: Success. * - < 0: Failure. */ - virtual int setLoopbackDeviceParameter(const LoopbackRecordingOption &option, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setLoopbackDeviceParameter(const LoopbackRecordingOption &option, aosl_ref_t ares = AOSL_REF_INVALID) = 0; virtual ~IRecordingDeviceSource() {} }; @@ -213,7 +219,7 @@ class INGAudioDeviceManager : public RefCountInterface { * - 0: Success. * - < 0: Failure. */ - virtual int setMicrophoneVolume(unsigned int volume, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setMicrophoneVolume(unsigned int volume, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Gets the volume of the microphone. * @param volume The volume of the microphone. @@ -229,7 +235,7 @@ class INGAudioDeviceManager : public RefCountInterface { * - 0: Success. * - < 0: Failure. */ - virtual int setSpeakerVolume(unsigned int volume, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setSpeakerVolume(unsigned int volume, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Gets the volume of the speaker. * @param volume The volume of the speaker. @@ -247,7 +253,7 @@ class INGAudioDeviceManager : public RefCountInterface { * - 0: Success. * - < 0: Failure. */ - virtual int setMicrophoneMute(bool mute, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setMicrophoneMute(bool mute, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Gets the mute state of the microphone. * @param mute The mute state of the microphone. @@ -265,7 +271,7 @@ class INGAudioDeviceManager : public RefCountInterface { * - 0: Success. * - < 0: Failure. */ - virtual int setSpeakerMute(bool mute, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setSpeakerMute(bool mute, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Gets the mute state of the speaker. * @param mute A reference to the mute state of the speaker. @@ -309,7 +315,7 @@ class INGAudioDeviceManager : public RefCountInterface { * - 0: Success. * - < 0: Failure. */ - virtual int setDefaultAudioRouting(AudioRoute route, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setDefaultAudioRouting(AudioRoute route, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Changes the current audio routing. * @@ -321,7 +327,7 @@ class INGAudioDeviceManager : public RefCountInterface { * - 0: Success. * - < 0: Failure. */ - virtual int changeAudioRouting(AudioRoute route, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int changeAudioRouting(AudioRoute route, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Changes the speaker status on/off. * @@ -333,7 +339,7 @@ class INGAudioDeviceManager : public RefCountInterface { * - 0: Success. * - < 0: Failure. */ - virtual int setAudioRoutingSpeakerOn(bool enable, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setAudioRoutingSpeakerOn(bool enable, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Gets the current audio routing. * @@ -405,7 +411,7 @@ class INGAudioDeviceManager : public RefCountInterface { * - 0: Success. * - < 0: Failure. */ - virtual int setPlayoutDevice(int index, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setPlayoutDevice(int index, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Sets the recording device. * @@ -417,7 +423,7 @@ class INGAudioDeviceManager : public RefCountInterface { * - 0: Success. * - < 0: Failure. */ - virtual int setRecordingDevice(int index, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setRecordingDevice(int index, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** The status of following system default playback device. @note The status of following system default playback device. @@ -429,7 +435,7 @@ class INGAudioDeviceManager : public RefCountInterface { - 0: Success. - < 0: Failure. */ - virtual int followSystemPlaybackDevice(bool enable, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int followSystemPlaybackDevice(bool enable, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** The status of following system default recording device. @@ -442,7 +448,7 @@ class INGAudioDeviceManager : public RefCountInterface { - 0: Success. - < 0: Failure. */ - virtual int followSystemRecordingDevice(bool enable, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int followSystemRecordingDevice(bool enable, aosl_ref_t ares = AOSL_REF_INVALID) = 0; #endif // _WIN32 || (TARGET_OS_MAC && !TARGET_OS_IPHONE) #if defined(_WIN32) @@ -457,7 +463,7 @@ class INGAudioDeviceManager : public RefCountInterface { * - 0: Success. * - < 0: Failure. */ - virtual int setApplicationVolume(unsigned int volume, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setApplicationVolume(unsigned int volume, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Gets the volume of the app. * @@ -483,7 +489,7 @@ class INGAudioDeviceManager : public RefCountInterface { * - 0: Success. * - < 0: Failure. */ - virtual int setApplicationMuteState(bool mute, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setApplicationMuteState(bool mute, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Gets the mute state of the app. * @@ -518,7 +524,7 @@ class INGAudioDeviceManager : public RefCountInterface { * - 0: Success. * - < 0: Failure. */ - virtual int setLoopbackDevice(int index, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setLoopbackDevice(int index, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** The status of following system default loopback device. @note The status of following system default loopback device. @@ -530,7 +536,7 @@ class INGAudioDeviceManager : public RefCountInterface { - 0: Success. - < 0: Failure. */ - virtual int followSystemLoopbackDevice(bool enable, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int followSystemLoopbackDevice(bool enable, aosl_ref_t ares = AOSL_REF_INVALID) = 0; #endif // _WIN32 /** @@ -544,7 +550,7 @@ class INGAudioDeviceManager : public RefCountInterface { * - 0: Success. * - < 0: Failure. */ - virtual int registerObserver(IAudioDeviceManagerObserver* observer, void(*safeDeleter)(IAudioDeviceManagerObserver*) = NULL, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int registerObserver(IAudioDeviceManagerObserver* observer, void(*safeDeleter)(IAudioDeviceManagerObserver*) = NULL, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Releases the IAudioDeviceManagerObserver object. * @param observer The pointer to the IAudioDeviceManagerObserver class registered using #registerObserver. @@ -554,7 +560,7 @@ class INGAudioDeviceManager : public RefCountInterface { */ virtual int unregisterObserver(IAudioDeviceManagerObserver* observer) = 0; - virtual int setupAudioAttributeContext(void* audioAttr, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setupAudioAttributeContext(void* audioAttr, aosl_ref_t ares = AOSL_REF_INVALID) = 0; protected: ~INGAudioDeviceManager() {} diff --git a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraAudioTrack.h b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraAudioTrack.h index 1d24933ed..e877e370e 100644 --- a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraAudioTrack.h +++ b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraAudioTrack.h @@ -9,7 +9,7 @@ #pragma once // NOLINT(build/header_guard) #include "AgoraBase.h" -#include +#include // FIXME(Ender): use this class instead of AudioSendStream as local track namespace agora { @@ -87,7 +87,7 @@ class IAudioTrack : public RefCountInterface { * - 0: Success. * - < 0: Failure. */ - virtual int adjustPlayoutVolume(int volume, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int adjustPlayoutVolume(int volume, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Gets the current playback volume. @@ -104,11 +104,12 @@ class IAudioTrack : public RefCountInterface { * By adding an audio filter, you can apply various audio effects to the audio, for example, voice change. * @param filter A pointer to the audio filter. See \ref agora::rtc::IAudioFilter "IAudioFilter". * @param position The position of the audio filter. See \ref agora::rtc::IAudioTrack::AudioFilterPosition "AudioFilterPosition". + * @param extContext The context of current filter. See \ref agora::rtc::ExtensionContext "ExtensionContext". * @return * - `true`: Success. * - `false`: Failure. */ - virtual bool addAudioFilter(agora_refptr filter, AudioFilterPosition position, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual bool addAudioFilter(agora_refptr filter, AudioFilterPosition position, ExtensionContext *extContext = NULL, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Removes the audio filter added by callling `addAudioFilter`. * @@ -118,7 +119,7 @@ class IAudioTrack : public RefCountInterface { * - `true`: Success. * - `false`: Failure. */ - virtual bool removeAudioFilter(agora_refptr filter, AudioFilterPosition position, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual bool removeAudioFilter(agora_refptr filter, AudioFilterPosition position, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Enable / Disable specified audio filter @@ -129,7 +130,7 @@ class IAudioTrack : public RefCountInterface { * - 0: success * - <0: failure */ - virtual int enableAudioFilter(const char* id, bool enable, AudioFilterPosition position, ahpl_ref_t ares = AHPL_REF_INVALID) { + virtual int enableAudioFilter(const char* id, bool enable, AudioFilterPosition position, aosl_ref_t ares = AOSL_REF_INVALID) { (void)id; (void)enable; (void)position; @@ -146,7 +147,7 @@ class IAudioTrack : public RefCountInterface { * - 0: success * - <0: failure */ - virtual int setFilterProperty(const char* id, const char* key, const char* jsonValue, AudioFilterPosition position, ahpl_ref_t ares = AHPL_REF_INVALID) { + virtual int setFilterProperty(const char* id, const char* key, const char* jsonValue, AudioFilterPosition position, aosl_ref_t ares = AOSL_REF_INVALID) { (void)id; (void)key; (void)jsonValue; @@ -194,7 +195,7 @@ class IAudioTrack : public RefCountInterface { * - `true`: Success. * - `false`: Failure. */ - virtual bool addAudioSink(agora_refptr sink, const AudioSinkWants& wants, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual bool addAudioSink(agora_refptr sink, const AudioSinkWants& wants, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Removes an audio sink. @@ -204,7 +205,7 @@ class IAudioTrack : public RefCountInterface { * - `true`: Success. * - `false`: Failure. */ - virtual bool removeAudioSink(agora_refptr sink, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual bool removeAudioSink(agora_refptr sink, aosl_ref_t ares = AOSL_REF_INVALID) = 0; }; /** @@ -315,7 +316,7 @@ class ILocalAudioTrack : public IAudioTrack { * - `true`: Enable the local audio track. * - `false`: Disable the local audio track. */ - virtual int setEnabled(bool enable, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setEnabled(bool enable, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Gets whether the local audio track is enabled. @@ -345,7 +346,7 @@ class ILocalAudioTrack : public IAudioTrack { * - 0: Success. * - < 0: Failure. */ - virtual int adjustPublishVolume(int volume, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int adjustPublishVolume(int volume, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Gets the current volume for publishing. @@ -368,7 +369,7 @@ class ILocalAudioTrack : public IAudioTrack { * - 0: Success. * - < 0: Failure. */ - virtual int enableLocalPlayback(bool enable, bool sync = true, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int enableLocalPlayback(bool enable, bool sync = true, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Enables in-ear monitoring (for Android and iOS only). @@ -381,7 +382,7 @@ class ILocalAudioTrack : public IAudioTrack { * - 0: Success. * - < 0: Failure. */ - virtual int enableEarMonitor(bool enable, int includeAudioFilters, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int enableEarMonitor(bool enable, int includeAudioFilters, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** Register an local audio track observer * * @param observer A pointer to the local audio track observer: \ref agora::rtc::ILocalAudioTrackObserver @@ -390,7 +391,7 @@ class ILocalAudioTrack : public IAudioTrack { * - 0: Success. * - < 0: Failure. */ - virtual int registerTrackObserver(ILocalAudioTrackObserver* observer, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int registerTrackObserver(ILocalAudioTrackObserver* observer, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** Releases the local audio track observer * * @param observer A pointer to the local audio track observer: \ref agora::rtc::ILocalAudioTrackObserver @@ -401,6 +402,21 @@ class ILocalAudioTrack : public IAudioTrack { */ virtual int unregisterTrackObserver(ILocalAudioTrackObserver* observer) = 0; + /** set Max buffered audio frame number + * + * @param number : the buffer number set,unit is 10ms + * + */ + virtual void setMaxBufferedAudioFrameNumber(int number) = 0; + + /** clear sender buffer + * + * @return + * - >= 0: Frame number in sender buffer. + * - < 0: Failure. + */ + virtual int ClearSenderBuffer() = 0; + protected: ~ILocalAudioTrack() {} }; @@ -535,6 +551,14 @@ struct RemoteAudioTrackStats { * The time of 200 ms frozen in 2 seconds */ uint16_t frozen_time_200_ms; + /** + * The full time of 80 ms frozen in 2 seconds + */ + uint16_t full_frozen_time_80_ms; + /** + * The full time of 200 ms frozen in 2 seconds + */ + uint16_t full_frozen_time_200_ms; /** * The estimate delay */ @@ -617,6 +641,8 @@ struct RemoteAudioTrackStats { frozen_time_80_ms(0), frozen_count_200_ms(0), frozen_time_200_ms(0), + full_frozen_time_80_ms(0), + full_frozen_time_200_ms(0), delay_estimate_ms(0), mos_value(0), frozen_rate_by_custom_plc_count(0), @@ -663,7 +689,7 @@ class IRemoteAudioTrack : public IAudioTrack { * - 0: Success. * - < 0: Failure. */ - virtual int registerMediaPacketReceiver(IMediaPacketReceiver* packetReceiver, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int registerMediaPacketReceiver(IMediaPacketReceiver* packetReceiver, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Releases the `IMediaPacketReceiver` object. @@ -686,7 +712,7 @@ class IRemoteAudioTrack : public IAudioTrack { * - 0: Success. * - < 0: Failure. */ - virtual int registerAudioEncodedFrameReceiver(IAudioEncodedFrameReceiver* packetReceiver, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int registerAudioEncodedFrameReceiver(IAudioEncodedFrameReceiver* packetReceiver, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Releases the `IAudioEncodedFrameReceiver` object. @@ -709,8 +735,45 @@ class IRemoteAudioTrack : public IAudioTrack { - 0: Success. - < 0: Failure. */ - virtual int setRemoteVoicePosition(float pan, float gain, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setRemoteVoicePosition(float pan, float gain, aosl_ref_t ares = AOSL_REF_INVALID) = 0; + + /** Sets the volume of each audio decoded channel + + @param decoded_index The channel index of the remote user. The value ranges from 0 to 100: + @param volume The channel index of the remote user. The value ranges from 0 to 100. + - 0: mute the channel. + - 100: keep the origin volume of the channel. + + @return + - 0: Success. + - < 0: Failure. + */ + virtual int adjustDecodedAudioVolume(int decoded_index, int volume, aosl_ref_t ares = AOSL_REF_INVALID) = 0; + + /** mute remote stream from timestamp + + @note + - unmuteRemoteFromTimestamp should be called after muteRemoteFromTimestamp, othewise this stream will be muted all time + + @param timestamp The rtp timestamp of start mute + @return + - 0: Success. + - < 0: Failure. + */ + virtual int muteRemoteFromTimestamp(uint32_t timestamp) = 0; + + /** unmute remote stream from timestamp + + @note + - unmuteRemoteFromTimestamp should be called after muteRemoteFromTimestamp, othewise this stream will be muted all time + @param timestamp The rtp timestamp of start unmute + @return + - 0: Success. + - < 0: Failure. + */ + virtual int unmuteRemoteFromTimestamp(uint32_t timestamp) = 0; + /** set percentage of audio acceleration during poor network @note @@ -724,7 +787,7 @@ class IRemoteAudioTrack : public IAudioTrack { - 0: Success. - < 0: Failure. */ - virtual int adjustAudioAcceleration(int percentage, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int adjustAudioAcceleration(int percentage, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** set percentage of audio deceleration during poor network @@ -739,7 +802,7 @@ class IRemoteAudioTrack : public IAudioTrack { - 0: Success. - < 0: Failure. */ - virtual int adjustAudioDeceleration(int percentage, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int adjustAudioDeceleration(int percentage, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** enable spatial audio @@ -750,7 +813,7 @@ class IRemoteAudioTrack : public IAudioTrack { - 0: Success. - < 0: Failure. */ - virtual int enableSpatialAudio(bool enabled, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int enableSpatialAudio(bool enabled, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** Sets remote user parameters for spatial audio @@ -760,7 +823,7 @@ class IRemoteAudioTrack : public IAudioTrack { - 0: Success. - < 0: Failure. */ - virtual int setRemoteUserSpatialAudioParams(const agora::SpatialAudioParams& params, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setRemoteUserSpatialAudioParams(const agora::SpatialAudioParams& params, aosl_ref_t ares = AOSL_REF_INVALID) = 0; }; } // namespace rtc diff --git a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraCameraCapturer.h b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraCameraCapturer.h index 09da1d422..5b089a441 100644 --- a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraCameraCapturer.h +++ b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraCameraCapturer.h @@ -8,7 +8,7 @@ #include "AgoraBase.h" #include "AgoraRefPtr.h" -#include +#include namespace agora { namespace rtc { @@ -32,6 +32,10 @@ class ICameraCapturer : public RefCountInterface { * The camera source is the front camera. */ CAMERA_FRONT, + /** + * The camera source is the extra camera. + */ + CAMERA_EXTRA, }; /** @@ -121,7 +125,7 @@ class ICameraCapturer : public RefCountInterface { * - 0: Success. * - < 0: Failure. */ - virtual int setCameraSource(CAMERA_SOURCE source, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setCameraSource(CAMERA_SOURCE source, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Gets the camera source. * @@ -137,7 +141,7 @@ class ICameraCapturer : public RefCountInterface { * @note * This method applies to Android and iOS only. */ - virtual int switchCamera(ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int switchCamera(aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Returns whether zooming is supported by the current device. * @note @@ -161,7 +165,7 @@ class ICameraCapturer : public RefCountInterface { * - 0: Success. * - < 0: Failure. */ - virtual int32_t setCameraZoom(float zoomValue, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int32_t setCameraZoom(float zoomValue, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Gets the max zooming factor of the device. * @@ -192,7 +196,7 @@ class ICameraCapturer : public RefCountInterface { * - 0: Success. * - < 0: Failure. */ - virtual int32_t setCameraFocus(float x, float y, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int32_t setCameraFocus(float x, float y, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Returns whether auto face focus is supported by the current device. * @note @@ -214,7 +218,7 @@ class ICameraCapturer : public RefCountInterface { * - 0: Success. * - < 0: Failure. */ - virtual int32_t setCameraAutoFaceFocus(bool enable, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int32_t setCameraAutoFaceFocus(bool enable, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Enables or disables auto face detection. * @note @@ -225,7 +229,7 @@ class ICameraCapturer : public RefCountInterface { * - 0: Success. * - < 0: Failure. */ - virtual int32_t enableFaceDetection(bool enable, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int32_t enableFaceDetection(bool enable, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Checks whether the camera face detect is supported. @@ -274,7 +278,7 @@ class ICameraCapturer : public RefCountInterface { * - 0: Success * - < 0: Failure */ - virtual int setCameraTorchOn(bool on, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setCameraTorchOn(bool on, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** Checks whether the camera exposure function is supported. * @@ -304,7 +308,7 @@ class ICameraCapturer : public RefCountInterface { *
  • < 0: Failure.
  • * */ - virtual int setCameraExposurePosition(float positionXinView, float positionYinView, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setCameraExposurePosition(float positionXinView, float positionYinView, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Returns whether exposure value adjusting is supported by the current device. @@ -330,7 +334,7 @@ class ICameraCapturer : public RefCountInterface { * - 0: Success. * - < 0: Failure. */ - virtual int setCameraExposureFactor(float value, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setCameraExposureFactor(float value, aosl_ref_t ares = AOSL_REF_INVALID) = 0; #if (defined(__APPLE__) && TARGET_OS_IOS) /** @@ -344,7 +348,7 @@ class ICameraCapturer : public RefCountInterface { * - 0: Success. * - < 0: Failure. */ - virtual bool enableMultiCamera(bool enable, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual bool enableMultiCamera(bool enable, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Checks whether the camera auto exposure function is supported. * @@ -368,7 +372,14 @@ class ICameraCapturer : public RefCountInterface { *
  • < 0: Failure.
  • * */ - virtual int setCameraAutoExposureFaceModeEnabled(bool enabled, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setCameraAutoExposureFaceModeEnabled(bool enabled, aosl_ref_t ares = AOSL_REF_INVALID) = 0; + + /** + * set camera stabilization mode.If open stabilization mode, fov will be smaller and capture latency will be longer. + * + * @param mode specifies the camera stabilization mode. + */ + virtual int setCameraStabilizationMode(CAMERA_STABILIZATION_MODE mode) = 0; #endif #elif defined(_WIN32) || (defined(__linux__) && !defined(__ANDROID__)) || \ @@ -410,11 +421,32 @@ class ICameraCapturer : public RefCountInterface { virtual int initWithDeviceName(const char* deviceName) = 0; #endif +#if defined(__APPLE__) + /** + * Checks whether the center stage is supported. Use this method after starting the camera. + * + * @return + * - true: The center stage is supported. + * - false: The center stage is not supported. + */ + virtual bool isCenterStageSupported() = 0; + + /** Enables the camera Center Stage. + * @param enabled enable Center Stage: + * - true: Enable Center Stage. + * - false: Disable Center Stage. + * @return + * - 0: Success. + * - < 0: Failure. + */ + virtual int enableCenterStage(bool enabled) = 0; +#endif + /** * Set the device orientation of the capture device * @param VIDEO_ORIENTATION orientaion of the device 0(by default), 90, 180, 270 */ - virtual int setDeviceOrientation(VIDEO_ORIENTATION orientation, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setDeviceOrientation(VIDEO_ORIENTATION orientation, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Sets the format of the video captured by the camera. @@ -423,7 +455,7 @@ class ICameraCapturer : public RefCountInterface { * * @param capture_format The reference to the video format: VideoFormat. */ - virtual int setCaptureFormat(const VideoFormat& capture_format, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setCaptureFormat(const VideoFormat& capture_format, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Gets the format of the video captured by the camera. * @return @@ -435,7 +467,7 @@ class ICameraCapturer : public RefCountInterface { * * @param observer Instance of the capture observer. */ - virtual int registerCameraObserver(ICameraCaptureObserver* observer, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int registerCameraObserver(ICameraCaptureObserver* observer, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Unregisters the camera observer. * diff --git a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraDataChannel.h b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraDataChannel.h index a79b2c79e..d62a64ee1 100644 --- a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraDataChannel.h +++ b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraDataChannel.h @@ -9,7 +9,7 @@ #include "AgoraRefPtr.h" #include "AgoraBase.h" -#include +#include namespace agora { /** @@ -92,7 +92,7 @@ class ILocalDataChannel : public RefCountInterface { * - 0: Success. * - < 0: Failure. */ - virtual int sendDataPacket(const char* packet, size_t length, uint64_t capture_time_ms, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int sendDataPacket(const char* packet, size_t length, uint64_t capture_time_ms, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Send meta data to this data channel before publishing. * @@ -102,7 +102,7 @@ class ILocalDataChannel : public RefCountInterface { * - 0: Success. * - < 0: Failure. */ - virtual int setMetaData(const char* metaData, size_t length, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setMetaData(const char* metaData, size_t length, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * return configured channel id diff --git a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraExtensionProvider.h b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraExtensionProvider.h index 9a1a54141..4dff21248 100644 --- a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraExtensionProvider.h +++ b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraExtensionProvider.h @@ -106,7 +106,7 @@ class IExtensionProvider : public RefCountInterface { virtual agora_refptr createVideoSink(const char* name) { return NULL; } - + virtual void setProperty(const char* key, const char* value) {} protected: diff --git a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraLocalUser.h b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraLocalUser.h index 8a9d885bb..2db3c9dcf 100644 --- a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraLocalUser.h +++ b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraLocalUser.h @@ -11,7 +11,7 @@ #include #include "AgoraBase.h" #include "AgoraOptional.h" -#include +#include namespace agora { namespace media { @@ -41,6 +41,7 @@ struct LocalVideoTrackStats; struct RemoteVideoTrackStats; class IMediaPacketReceiver; class IVideoSinkBase; + /** * The ILocalUser class defines the behavior and state of a local user. * @@ -280,7 +281,7 @@ class ILocalUser { * as `role`, the connection fails with the \ref IRtcConnectionObserver::onConnectionFailure "onConnectionFailure" callback. * @param role The role of the user. See \ref rtc::CLIENT_ROLE_TYPE "CLIENT_ROLE_TYPE". */ - virtual int setUserRole(rtc::CLIENT_ROLE_TYPE role, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setUserRole(rtc::CLIENT_ROLE_TYPE role, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Gets the role of the user. @@ -296,7 +297,7 @@ class ILocalUser { * @param level The latency level of an audience member in interactive live streaming. See AUDIENCE_LATENCY_LEVEL_TYPE. * @param role The user role determined by the config. If it's -1, it means there is no configured role. */ - virtual int setAudienceLatencyLevel(AUDIENCE_LATENCY_LEVEL_TYPE level, int role, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setAudienceLatencyLevel(AUDIENCE_LATENCY_LEVEL_TYPE level, int role, aosl_ref_t ares = AOSL_REF_INVALID) = 0; virtual AUDIENCE_LATENCY_LEVEL_TYPE getAudienceLatencyLevel() = 0; @@ -310,7 +311,7 @@ class ILocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int setAudioEncoderConfiguration(const rtc::AudioEncoderConfiguration& config, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setAudioEncoderConfiguration(const rtc::AudioEncoderConfiguration& config, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Sets the audio parameters and application scenarios. @@ -321,7 +322,7 @@ class ILocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int setAudioScenario(AUDIO_SCENARIO_TYPE scenario, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setAudioScenario(AUDIO_SCENARIO_TYPE scenario, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * You can call this method to set the expected video scenario. @@ -333,7 +334,7 @@ class ILocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int setVideoScenario(VIDEO_APPLICATION_SCENARIO_TYPE scenarioType, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setVideoScenario(VIDEO_APPLICATION_SCENARIO_TYPE scenarioType, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Sets the Video qoe preference. @@ -348,7 +349,7 @@ class ILocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int setVideoQoEPreference(VIDEO_QOE_PREFERENCE_TYPE qoePreference, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setVideoQoEPreference(VIDEO_QOE_PREFERENCE_TYPE qoePreference, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Gets the detailed statistics of the local audio. @@ -371,7 +372,7 @@ class ILocalUser { * - < 0: Failure. * - -5(ERR_REFUSED), if the role of the local user is not broadcaster. */ - virtual int publishAudio(agora_refptr audioTrack, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int publishAudio(agora_refptr audioTrack, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Stops publishing the local audio track to the channel. @@ -381,7 +382,7 @@ class ILocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int unpublishAudio(agora_refptr audioTrack, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int unpublishAudio(agora_refptr audioTrack, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Publishes a local video track to the channel. @@ -391,7 +392,7 @@ class ILocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int publishVideo(agora_refptr videoTrack, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int publishVideo(agora_refptr videoTrack, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Stops publishing the local video track to the channel. @@ -401,7 +402,7 @@ class ILocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int unpublishVideo(agora_refptr videoTrack, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int unpublishVideo(agora_refptr videoTrack, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Subscribes to the audio of a specified remote user in channel. @@ -412,7 +413,7 @@ class ILocalUser { * - < 0: Failure. * - -2(ERR_INVALID_ARGUMENT), if no such user exists or `userId` is invalid. */ - virtual int subscribeAudio(user_id_t userId, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int subscribeAudio(user_id_t userId, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Subscribes to the audio of all remote users in the channel. @@ -423,7 +424,7 @@ class ILocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int subscribeAllAudio(ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int subscribeAllAudio(aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Stops subscribing to the audio of a specified remote user in the channel. @@ -434,7 +435,7 @@ class ILocalUser { * - < 0: Failure. * - -2(ERR_INVALID_ARGUMENT), if no such user exists or `userId` is invalid. */ - virtual int unsubscribeAudio(user_id_t userId, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int unsubscribeAudio(user_id_t userId, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Stops subscribing to the audio of all remote users in the channel. @@ -446,7 +447,7 @@ class ILocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int unsubscribeAllAudio(ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int unsubscribeAllAudio(aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Adjusts the playback signal volume. @@ -458,7 +459,7 @@ class ILocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int adjustPlaybackSignalVolume(int volume, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int adjustPlaybackSignalVolume(int volume, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Gets the current playback signal volume. @@ -489,7 +490,7 @@ class ILocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int adjustUserPlaybackSignalVolume(user_id_t userId, int volume, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int adjustUserPlaybackSignalVolume(user_id_t userId, int volume, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Gets the current playback signal volume of specified user. @@ -513,7 +514,7 @@ class ILocalUser { - 0: Success. - < 0: Failure. */ - virtual int enableSoundPositionIndication(bool enabled, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int enableSoundPositionIndication(bool enabled, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** Sets the sound position and gain of a remote user. @@ -535,7 +536,7 @@ class ILocalUser { - 0: Success. - < 0: Failure. */ - virtual int setRemoteVoicePosition(user_id_t userId, double pan, double gain, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setRemoteVoicePosition(user_id_t userId, double pan, double gain, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** enable spatial audio @@ -546,7 +547,7 @@ class ILocalUser { - 0: Success. - < 0: Failure. */ - virtual int enableSpatialAudio(bool enabled, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int enableSpatialAudio(bool enabled, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** Sets remote user parameters for spatial audio @@ -557,7 +558,7 @@ class ILocalUser { - 0: Success. - < 0: Failure. */ - virtual int setRemoteUserSpatialAudioParams(user_id_t userId, const agora::SpatialAudioParams& param, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setRemoteUserSpatialAudioParams(user_id_t userId, const agora::SpatialAudioParams& param, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Sets the audio frame parameters for the \ref agora::media::IAudioFrameObserver::onPlaybackAudioFrame @@ -578,7 +579,7 @@ class ILocalUser { virtual int setPlaybackAudioFrameParameters(size_t numberOfChannels, uint32_t sampleRateHz, RAW_AUDIO_FRAME_OP_MODE_TYPE mode = RAW_AUDIO_FRAME_OP_MODE_READ_ONLY, - int samplesPerCall = 0, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + int samplesPerCall = 0, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Sets the audio frame parameters for the \ref agora::media::IAudioFrameObserver::onRecordAudioFrame * "onRecordAudioFrame" callback. @@ -598,7 +599,7 @@ class ILocalUser { virtual int setRecordingAudioFrameParameters(size_t numberOfChannels, uint32_t sampleRateHz, RAW_AUDIO_FRAME_OP_MODE_TYPE mode = RAW_AUDIO_FRAME_OP_MODE_READ_ONLY, - int samplesPerCall = 0, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + int samplesPerCall = 0, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Sets the audio frame parameters for the \ref agora::media::IAudioFrameObserver::onMixedAudioFrame * "onMixedAudioFrame" callback. @@ -615,7 +616,7 @@ class ILocalUser { */ virtual int setMixedAudioFrameParameters(size_t numberOfChannels, uint32_t sampleRateHz, - int samplesPerCall = 0, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + int samplesPerCall = 0, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Sets the audio frame parameters for the \ref agora::media::IAudioFrameObserver::onEarMonitoringAudioFrame @@ -639,7 +640,7 @@ class ILocalUser { size_t numberOfChannels, uint32_t sampleRateHz, RAW_AUDIO_FRAME_OP_MODE_TYPE mode = RAW_AUDIO_FRAME_OP_MODE_READ_ONLY, - int samplesPerCall = 0, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + int samplesPerCall = 0, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Sets the audio frame parameters for the \ref agora::media::IAudioFrameObserver::onPlaybackAudioFrameBeforeMixing @@ -656,7 +657,7 @@ class ILocalUser { * - < 0: Failure. */ virtual int setPlaybackAudioFrameBeforeMixingParameters(size_t numberOfChannels, - uint32_t sampleRateHz, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + uint32_t sampleRateHz, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Registers an audio frame observer. @@ -674,7 +675,7 @@ class ILocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int registerAudioFrameObserver(agora::media::IAudioFrameObserverBase* observer, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int registerAudioFrameObserver(agora::media::IAudioFrameObserverBase* observer, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Releases the audio frame observer. * @@ -695,7 +696,7 @@ class ILocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int enableAudioSpectrumMonitor(int intervalInMS = 100, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int enableAudioSpectrumMonitor(int intervalInMS = 100, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Disalbe the audio spectrum monitor. * @@ -703,7 +704,7 @@ class ILocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int disableAudioSpectrumMonitor(ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int disableAudioSpectrumMonitor(aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Registers an audio spectrum observer. @@ -719,7 +720,7 @@ class ILocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int registerAudioSpectrumObserver(agora::media::IAudioSpectrumObserver * observer, void (*safeDeleter)(agora::media::IAudioSpectrumObserver*), ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int registerAudioSpectrumObserver(agora::media::IAudioSpectrumObserver * observer, void (*safeDeleter)(agora::media::IAudioSpectrumObserver*), aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Releases the audio spectrum observer. * @@ -743,7 +744,7 @@ class ILocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int registerLocalVideoEncodedFrameObserver(agora::media::IVideoEncodedFrameObserver* observer, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int registerLocalVideoEncodedFrameObserver(agora::media::IVideoEncodedFrameObserver* observer, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Releases the \ref agora::media::IVideoEncodedFrameObserver "IVideoEncodedFrameObserver" object. * @param observer The pointer to the `IVideoEncodedFrameObserver` object. @@ -759,7 +760,7 @@ class ILocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int forceNextIntraFrame(ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int forceNextIntraFrame(aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Registers an \ref agora::media::IVideoEncodedFrameObserver "IVideoEncodedFrameObserver" object. * @@ -772,7 +773,7 @@ class ILocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int registerVideoEncodedFrameObserver(agora::media::IVideoEncodedFrameObserver* observer, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int registerVideoEncodedFrameObserver(agora::media::IVideoEncodedFrameObserver* observer, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Releases the \ref agora::media::IVideoEncodedFrameObserver "IVideoEncodedFrameObserver" object. * @param observer The pointer to the `IVideoEncodedFrameObserver` object. @@ -794,7 +795,7 @@ class ILocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int registerVideoFrameObserver(IVideoFrameObserver2* observer, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int registerVideoFrameObserver(IVideoFrameObserver2* observer, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Releases the \ref agora::rtc::IVideoFrameObserver2 "IVideoFrameObserver2" object. * @param observer The pointer to the `IVideoFrameObserver2` object. @@ -805,12 +806,14 @@ class ILocalUser { virtual int unregisterVideoFrameObserver(IVideoFrameObserver2* observer) = 0; virtual int setVideoSubscriptionOptions(user_id_t userId, - const VideoSubscriptionOptions& options, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + const VideoSubscriptionOptions& options, aosl_ref_t ares = AOSL_REF_INVALID) = 0; - virtual int setHighPriorityUserList(uid_t* vipList, int uidNum, int option, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setHighPriorityUserList(uid_t* vipList, int uidNum, int option, aosl_ref_t ares = AOSL_REF_INVALID) = 0; virtual int getHighPriorityUserList(std::vector& vipList, int& option) = 0; + virtual int setRemoteSubscribeFallbackOption(int option, aosl_ref_t ares = AOSL_REF_INVALID) = 0; + /** * Sets the blocklist of subscribe remote stream audio. * @@ -825,7 +828,7 @@ class ILocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int setSubscribeAudioBlocklist(user_id_t* userList, int userNumber, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setSubscribeAudioBlocklist(user_id_t* userList, int userNumber, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Sets the allowlist of subscribe remote stream audio. @@ -843,7 +846,7 @@ class ILocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int setSubscribeAudioAllowlist(user_id_t* userList, int userNumber, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setSubscribeAudioAllowlist(user_id_t* userList, int userNumber, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Sets the blocklist of subscribe remote stream video. @@ -859,7 +862,7 @@ class ILocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int setSubscribeVideoBlocklist(user_id_t* userList, int userNumber, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setSubscribeVideoBlocklist(user_id_t* userList, int userNumber, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Sets the allowlist of subscribe remote stream video. @@ -877,7 +880,7 @@ class ILocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int setSubscribeVideoAllowlist(user_id_t* userList, int userNumber, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setSubscribeVideoAllowlist(user_id_t* userList, int userNumber, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Subscribes to the video of a specified remote user in the channel. @@ -892,7 +895,7 @@ class ILocalUser { * - -2(ERR_INVALID_ARGUMENT), if `userId` is invalid. */ virtual int subscribeVideo(user_id_t userId, - const VideoSubscriptionOptions &subscriptionOptions, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + const VideoSubscriptionOptions &subscriptionOptions, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Subscribes to the video of all remote users in the channel. @@ -904,7 +907,7 @@ class ILocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int subscribeAllVideo(const VideoSubscriptionOptions &subscriptionOptions, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int subscribeAllVideo(const VideoSubscriptionOptions &subscriptionOptions, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Stops subscribing to the video of a specified remote user in the channel. @@ -915,7 +918,7 @@ class ILocalUser { * - < 0: Failure. * - -2(ERR_INVALID_ARGUMENT), if `userId` is invalid. */ - virtual int unsubscribeVideo(user_id_t userId, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int unsubscribeVideo(user_id_t userId, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Stops subscribing to the video of all remote users in the channel. @@ -927,7 +930,7 @@ class ILocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int unsubscribeAllVideo(ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int unsubscribeAllVideo(aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Sets the time interval and the volume smoothing factor of the \ref agora::rtc::ILocalUserObserver::onAudioVolumeIndication "onAudioVolumeIndication" callback. @@ -948,7 +951,7 @@ class ILocalUser { * - < 0: Failure. * - -2(ERR_INVALID_ARGUMENT), if `intervalInMS` or `smooth` is out of range. */ - virtual int setAudioVolumeIndicationParameters(int intervalInMS, int smooth, bool reportVad, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setAudioVolumeIndicationParameters(int intervalInMS, int smooth, bool reportVad, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Registers a local user observer object. @@ -963,7 +966,7 @@ class ILocalUser { */ virtual int registerLocalUserObserver( ILocalUserObserver* observer, - void(*safeDeleter)(ILocalUserObserver*) = NULL, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + void(*safeDeleter)(ILocalUserObserver*) = NULL, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Releases the \ref agora::rtc::ILocalUserObserver "ILocalUserObserver" object. @@ -996,7 +999,7 @@ class ILocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int registerMediaControlPacketReceiver(IMediaControlPacketReceiver* ctrlPacketReceiver, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int registerMediaControlPacketReceiver(IMediaControlPacketReceiver* ctrlPacketReceiver, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Releases the media control packet receiver. @@ -1017,7 +1020,7 @@ class ILocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int sendIntraRequest(user_id_t userId, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int sendIntraRequest(user_id_t userId, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Set local audio filterable by topn @@ -1030,7 +1033,7 @@ class ILocalUser { * - < 0: Failure. */ - virtual int setAudioFilterable(bool filterable, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setAudioFilterable(bool filterable, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Enable / Disable specified audio filter @@ -1041,7 +1044,7 @@ class ILocalUser { * - 0: success * - <0: failure */ - virtual int enableRemoteAudioTrackFilter(user_id_t userId, const char* id, bool enable, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int enableRemoteAudioTrackFilter(user_id_t userId, const char* id, bool enable, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * set the properties of the specified audio filter @@ -1053,7 +1056,7 @@ class ILocalUser { * - 0: success * - <0: failure */ - virtual int setRemoteAudioTrackFilterProperty(user_id_t userId, const char* id, const char* key, const char* jsonValue, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setRemoteAudioTrackFilterProperty(user_id_t userId, const char* id, const char* key, const char* jsonValue, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * get the properties of the specified audio filter @@ -1075,7 +1078,7 @@ class ILocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int publishDataChannel(agora_refptr channel, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int publishDataChannel(agora_refptr channel, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Stops publishing the data channel to the channel. * @@ -1084,7 +1087,7 @@ class ILocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int unpublishDataChannel(agora_refptr channel, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int unpublishDataChannel(agora_refptr channel, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Subscribes to a specified data channel of a specified remote user in channel. * @@ -1094,7 +1097,7 @@ class ILocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int subscribeDataChannel(user_id_t userId, int channelId, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int subscribeDataChannel(user_id_t userId, int channelId, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Stops subscribing to the data channel of a specified remote user in the channel. * @@ -1105,7 +1108,7 @@ class ILocalUser { * - < 0: Failure. * - -2(ERR_INVALID_ARGUMENT), if no such user exists or `userId` is invalid. */ - virtual int unsubscribeDataChannel(user_id_t userId, int channelId, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int unsubscribeDataChannel(user_id_t userId, int channelId, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Registers an data channel observer. * @@ -1116,7 +1119,7 @@ class ILocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int registerDataChannelObserver(IDataChannelObserver * observer, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int registerDataChannelObserver(IDataChannelObserver * observer, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Releases the data channel observer. * @@ -1137,7 +1140,7 @@ class ILocalUser { * - 0: success * - <0: failure */ - virtual int SetAudioNsMode(bool NsEnable, NS_MODE NsMode, NS_LEVEL NsLevel, NS_DELAY NsDelay, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int SetAudioNsMode(bool NsEnable, NS_MODE NsMode, NS_LEVEL NsLevel, NS_DELAY NsDelay, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * enable the mix track that mix special track * @@ -1149,7 +1152,7 @@ class ILocalUser { * - 0: success * - <0: failure */ - virtual int EnableLocalMixedAudioTrack(agora_refptr& track, bool enable, bool MixLocal, bool MixRemote, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int EnableLocalMixedAudioTrack(agora_refptr& track, bool enable, bool MixLocal, bool MixRemote, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Trigger data channel update callback with all data channel infos. * @@ -1157,7 +1160,18 @@ class ILocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int takeDataChannelSnapshot(ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int takeDataChannelSnapshot(aosl_ref_t ares = AOSL_REF_INVALID) = 0; + /** + * send audio metadata + * + * @param metadata The pointer of metadata + * @param length Size of metadata + * @return + * - 0: success + * - <0: failure + * @technical preview + */ + virtual int sendAudioMetadata(const char* metadata, size_t length, aosl_ref_t ares = AOSL_REF_INVALID) = 0; }; /** @@ -1494,6 +1508,20 @@ class ILocalUserObserver { */ virtual void onVideoSizeChanged(user_id_t userId, int width, int height, int rotation) = 0; + /** + * The audio metadata received. + * + * @param userId ID of the remote user. + * @param metadata The pointer of metadata + * @param length Size of metadata + * @technical preview + */ + virtual void onAudioMetadataReceived(user_id_t userId, const char* metadata, size_t length) { + (void)userId; + (void)metadata; + (void)length; + } + /** * The media information of a specified user. */ diff --git a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraMediaNode.h b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraMediaNode.h index f74fb00ed..0b2bc0152 100644 --- a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraMediaNode.h +++ b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraMediaNode.h @@ -4,7 +4,7 @@ #include "IAgoraLog.h" #include "NGIAgoraVideoFrame.h" #include "AgoraExtensionVersion.h" -#include +#include #ifndef OPTIONAL_PROCESSRESULT_SPECIFIER #if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800) @@ -140,6 +140,32 @@ class IAudioFilter : public IAudioFilterBase { ~IAudioFilter() {} }; +class IAudioFilterV2 : public IAudioFilter { +public: + class Control : public RefCountInterface { + public: + /** + * @brief Post an event and notify the end users. + * @param key '\0' ended string that describes the key of the event + * @param value '\0' ended string that describes the value of the event + */ + virtual int postEvent(const char* key, const char* value) = 0; + /** + * @brief print log to the SDK. + * @param level Log level @ref agora::commons::LOG_LEVEL + * @param format log formatter string + * @param ... variadic arguments + */ + virtual void printLog(commons::LOG_LEVEL level, const char* format, ...) = 0; + }; +public: + /** + * @brief AgoraSDK set IAudioFilterV2::Control to filter + * @param control IAudioFilterV2::Control + */ + virtual void setExtensionControl(agora::agora_refptr control) = 0; +}; + /** * The `IVideoFilterBase` class is the base class for video filters. You can use this class to implement your own filter * and add the filter to a video track. @@ -365,6 +391,64 @@ class IExtensionVideoFilter : public IVideoFilter { } }; +class ILipSyncFilter : public RefCountInterface { + public: + enum ProcessResult { + kSuccess, // Video frame data is successfully processed + kBypass, // Video frame data should bypass the current filter and flow to its successsors + kDrop, // Video Frame data should be discarded + }; + + class Control : public RefCountInterface { + public: + /** + * @brief Post an event and notify the end users. + * @param key '\0' ended string that describes the key of the event + * @param value '\0' ended string that describes the value of the event + */ + virtual int postEvent(const char* key, const char* value) = 0; + /** + * @brief print log to the SDK. + * @param level Log level @ref agora::commons::LOG_LEVEL + * @param format log formatter string + * @param ... variadic arguments + */ + virtual void printLog(commons::LOG_LEVEL level, const char* format, ...) = 0; + /** + * @brief Ask SDK to disable the current filter if a fatal error is detected + * @param error error code + * @param msg error message + */ + virtual void disableMe(int error, const char* msg) = 0; + /** + * @brief report counter to the SDK. + * @param counter_id counter id + * @param value counter value + */ + virtual void ReportCounter(int32_t counter_id, int32_t value) = 0; + /** + * @brief get stats to the SDK. + * @param counter_id counter id + */ + virtual int GetStats(int32_t counter_id) = 0; + }; + + virtual int start(agora::agora_refptr control) = 0; + + virtual int stop() = 0; + + virtual int setProperty(const char* key, const void* buf, size_t buf_size) { return -1; } + /** + * Convert the audio frame to face info. + * @param inAudioFrame The reference to the audio frame that you want to convert. + * @param outFaceInfo The reference to the face info. + * @return see @ref ProcessResult + */ + virtual ProcessResult convertAudioFrameToFaceInfo(const agora::media::base::AudioPcmFrame& inAudioFrame, char* outFaceInfo) { + return kBypass; + } +}; + /** * The `IVideoSinkBase` class is the base class for the custom video sink. */ @@ -434,10 +518,10 @@ class IVideoSinkBase : public RefCountInterface { class IMediaExtensionObserver : public RefCountInterface { public: virtual ~IMediaExtensionObserver() {} - virtual void onEvent(const char* provider, const char* extension, const char* key, const char* json_value) {} - virtual void onExtensionStopped(const char* provider, const char* extension) {} - virtual void onExtensionStarted(const char* provider, const char* extension) {} - virtual void onExtensionError(const char* provider, const char* extension, int error, const char* message) {} + virtual void onEventWithContext(const ExtensionContext& context, const char* key, const char* json_value) {} + virtual void onExtensionStoppedWithContext(const ExtensionContext& context) {} + virtual void onExtensionStartedWithContext(const ExtensionContext& context) {} + virtual void onExtensionErrorWithContext(const ExtensionContext& context, int error, const char* message) {} }; /** @@ -465,7 +549,7 @@ class IAudioPcmDataSender : public RefCountInterface { const size_t samples_per_channel, // for 10ms Data, number_of_samples * 100 = sample_rate const agora::rtc::BYTES_PER_SAMPLE bytes_per_sample, // 2 const size_t number_of_channels, - const uint32_t sample_rate, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; // sample_rate > 8000) + const uint32_t sample_rate, aosl_ref_t ares = AOSL_REF_INVALID) = 0; // sample_rate > 8000) protected: ~IAudioPcmDataSender() {} @@ -493,7 +577,7 @@ class IAudioEncodedFrameSender : public RefCountInterface { * - `false`: Failure. */ virtual bool sendEncodedAudioFrame(const uint8_t* payload_data, size_t payload_size, - const EncodedAudioFrameInfo& audioFrameInfo, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + const EncodedAudioFrameInfo& audioFrameInfo, aosl_ref_t ares = AOSL_REF_INVALID) = 0; protected: ~IAudioEncodedFrameSender() {} @@ -576,7 +660,7 @@ class IMediaPacketSender : public RefCountInterface { * - `false`: Failure. */ virtual int sendMediaPacket(const uint8_t *packet, size_t length, - const media::base::PacketOptions &options, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + const media::base::PacketOptions &options, aosl_ref_t ares = AOSL_REF_INVALID) = 0; protected: ~IMediaPacketSender() {} }; @@ -604,7 +688,7 @@ class IMediaControlPacketSender { */ virtual int sendPeerMediaControlPacket(media::base::user_id_t userId, const uint8_t *packet, - size_t length, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + size_t length, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Sends the media transport control packet to all users. @@ -616,7 +700,7 @@ class IMediaControlPacketSender { * - `true`: Success. * - `false`: Failure. */ - virtual int sendBroadcastMediaControlPacket(const uint8_t *packet, size_t length, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int sendBroadcastMediaControlPacket(const uint8_t *packet, size_t length, aosl_ref_t ares = AOSL_REF_INVALID) = 0; virtual ~IMediaControlPacketSender() {} }; @@ -658,7 +742,7 @@ class IVideoFrameSender : public RefCountInterface { * - 0: Success. * - < 0: Failure. */ - virtual int sendVideoFrame(const media::base::ExternalVideoFrame& videoFrame, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int sendVideoFrame(const media::base::ExternalVideoFrame& videoFrame, aosl_ref_t ares = AOSL_REF_INVALID) = 0; protected: ~IVideoFrameSender() {} @@ -685,7 +769,7 @@ class IVideoEncodedImageSender : public RefCountInterface { * - `false`: Failure. */ virtual bool sendEncodedVideoImage(const uint8_t* imageBuffer, size_t length, - const EncodedVideoFrameInfo& videoEncodedFrameInfo, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + const EncodedVideoFrameInfo& videoEncodedFrameInfo, aosl_ref_t ares = AOSL_REF_INVALID) = 0; protected: ~IVideoEncodedImageSender() {} @@ -779,7 +863,7 @@ class IVideoRenderer : public IVideoSinkBase { * - 0: Success. * - < 0: Failure. */ - virtual int setRenderMode(media::base::RENDER_MODE_TYPE renderMode, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setRenderMode(media::base::RENDER_MODE_TYPE renderMode, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Sets the render mode of the view. * @param view the view to set render mode. @@ -788,7 +872,7 @@ class IVideoRenderer : public IVideoSinkBase { * - 0: Success. * - < 0: Failure. */ - virtual int setRenderMode(void* view, media::base::RENDER_MODE_TYPE renderMode, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setRenderMode(void* view, media::base::RENDER_MODE_TYPE renderMode, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Sets whether to mirror the video. * @param mirror Whether to mirror the video: @@ -798,7 +882,7 @@ class IVideoRenderer : public IVideoSinkBase { * - 0: Success. * - < 0: Failure. */ - virtual int setMirror(bool mirror, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setMirror(bool mirror, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Sets whether to mirror the video. * @param view the view to set mirror mode. @@ -809,7 +893,7 @@ class IVideoRenderer : public IVideoSinkBase { * - 0: Success. * - < 0: Failure. */ - virtual int setMirror(void* view, bool mirror, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setMirror(void* view, bool mirror, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Sets the video display window. * @param view The pointer to the video display window. @@ -817,7 +901,7 @@ class IVideoRenderer : public IVideoSinkBase { * - 0: Success. * - < 0: Failure. */ - virtual int setView(void* view, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setView(void* view, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Sets the video display window. * @param view The pointer to the video display window. @@ -826,14 +910,14 @@ class IVideoRenderer : public IVideoSinkBase { * - 0: Success. * - < 0: Failure. */ - virtual int addView(void* view, const Rectangle& cropArea, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int addView(void* view, const Rectangle& cropArea, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Stops rendering the video view on the window. * @return * - 0: Success. * - < 0: Failure. */ - virtual int unsetView(ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int unsetView(aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * remove rendering the video view on the window. * @return @@ -852,8 +936,8 @@ class IVideoTrack; class IVideoFrameTransceiver : public RefCountInterface { public: virtual int getTranscodingDelayMs() = 0; - virtual int addVideoTrack(agora_refptr track, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; - virtual int removeVideoTrack(agora_refptr track, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int addVideoTrack(agora_refptr track, aosl_ref_t ares = AOSL_REF_INVALID) = 0; + virtual int removeVideoTrack(agora_refptr track, aosl_ref_t ares = AOSL_REF_INVALID) = 0; }; } diff --git a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraMediaNodeFactory.h b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraMediaNodeFactory.h index fb57b852e..a22242b23 100644 --- a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraMediaNodeFactory.h +++ b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraMediaNodeFactory.h @@ -7,7 +7,7 @@ #pragma once // NOLINT(build/header_guard) #include "AgoraBase.h" -#include +#include namespace agora { namespace rtc { diff --git a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraRemoteAudioMixerSource.h b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraRemoteAudioMixerSource.h index 12aa0d80d..98bdf2bd2 100644 --- a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraRemoteAudioMixerSource.h +++ b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraRemoteAudioMixerSource.h @@ -7,7 +7,7 @@ #pragma once #include "AgoraRefPtr.h" -#include +#include namespace agora { namespace rtc { @@ -27,13 +27,13 @@ class IRemoteAudioMixerSource : public RefCountInterface { * Add a audio track for mixing. Automatically starts mixing if add audio track * @param track The instance of the audio track that you want mixer to receive its audio stream. */ - virtual int addAudioTrack(agora_refptr track, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int addAudioTrack(agora_refptr track, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Remove a audio track for mixing. Automatically stops the mixed stream if all audio tracks are removed * @param track The instance of the audio track that you want to remove from the mixer. */ - virtual int removeAudioTrack(agora_refptr track, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int removeAudioTrack(agora_refptr track, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Gets the delay time for mix. diff --git a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraRtcConnection.h b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraRtcConnection.h index 4085bd1d6..2959d06da 100644 --- a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraRtcConnection.h +++ b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraRtcConnection.h @@ -9,7 +9,7 @@ #include "AgoraBase.h" #include "time_utils.h" -#include +#include namespace agora { namespace rtc { @@ -212,7 +212,7 @@ class IRtcConnection : public RefCountInterface { * - -2(ERR_INVALID_ARGUMENT): The argument that you pass is invalid. * - -8(ERR_INVALID_STATE): The current connection state is not CONNECTION_STATE_DISCONNECTED(1). */ - virtual int connect(const char* token, const char* channelId, user_id_t userId, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int connect(const char* token, const char* channelId, user_id_t userId, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Connects to an Agora channel. @@ -225,7 +225,7 @@ class IRtcConnection : public RefCountInterface { * The SDK also triggers `onConnected` or `onDisconnected` to notify you of the state change. * @param settings The settings of connecting. */ - virtual int connect(const TConnectSettings& settings, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int connect(const TConnectSettings& settings, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Disconnects from the Agora channel. @@ -238,7 +238,7 @@ class IRtcConnection : public RefCountInterface { * - 0: Success. * - < 0: Failure. */ - virtual int disconnect(ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int disconnect(aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Starts the last-mile network probe test. @@ -267,7 +267,7 @@ class IRtcConnection : public RefCountInterface { * - 0: Success. * - < 0: Failure. */ - virtual int startLastmileProbeTest(const LastmileProbeConfig& config, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int startLastmileProbeTest(const LastmileProbeConfig& config, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Stops the last-mile network probe test. @@ -275,7 +275,7 @@ class IRtcConnection : public RefCountInterface { * - 0: Success. * - < 0: Failure. */ - virtual int stopLastmileProbeTest(ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int stopLastmileProbeTest(aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Renews the token. @@ -286,7 +286,7 @@ class IRtcConnection : public RefCountInterface { * * @param token The pointer to the new token. */ - virtual int renewToken(const char* token, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int renewToken(const char* token, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Gets the connection information. @@ -340,7 +340,7 @@ class IRtcConnection : public RefCountInterface { * - 0: Success. * - < 0: Failure. */ - virtual int registerObserver(IRtcConnectionObserver* observer, void(*safeDeleter)(IRtcConnectionObserver*) = NULL, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int registerObserver(IRtcConnectionObserver* observer, void(*safeDeleter)(IRtcConnectionObserver*) = NULL, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Releases the registered IRtcConnectionObserver object. @@ -361,7 +361,7 @@ class IRtcConnection : public RefCountInterface { * - 0: Success. * - < 0: Failure. */ - virtual int registerNetworkObserver(INetworkObserver* observer, void(*safeDeleter)(INetworkObserver*) = NULL, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int registerNetworkObserver(INetworkObserver* observer, void(*safeDeleter)(INetworkObserver*) = NULL, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Releases the registered INetworkObserver object. @@ -437,7 +437,7 @@ class IRtcConnection : public RefCountInterface { * - 0: Success. * - < 0: Failure. */ - virtual int sendStreamMessage(int streamId, const char* data, size_t length, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int sendStreamMessage(int streamId, const char* data, size_t length, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** Enables/Disables the built-in encryption. * @@ -457,7 +457,7 @@ class IRtcConnection : public RefCountInterface { * - 0: Success. * - < 0: Failure. */ - virtual int enableEncryption(bool enabled, const EncryptionConfig& config, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int enableEncryption(bool enabled, const EncryptionConfig& config, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Reports a custom event to Agora. @@ -472,7 +472,7 @@ class IRtcConnection : public RefCountInterface { * - 0: Success. * - < 0: Failure. */ - virtual int sendCustomReportMessage(const char* id, const char* category, const char* event, const char* label, int value, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int sendCustomReportMessage(const char* id, const char* category, const char* event, const char* label, int value, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** Gets the user information by user account, which is in string format. * * @param userAccount The user account of the user. diff --git a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraRtmpConnection.h b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraRtmpConnection.h index d0516cb6d..1fabd7339 100644 --- a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraRtmpConnection.h +++ b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraRtmpConnection.h @@ -9,7 +9,7 @@ #include "AgoraBase.h" #include "AgoraRefPtr.h" -#include +#include namespace agora { namespace rtc { @@ -231,10 +231,8 @@ enum RTMP_CONNECTION_STATE { struct RtmpConnectionConfiguration { RtmpStreamingAudioConfiguration audioConfig; RtmpStreamingVideoConfiguration videoConfig; - bool enableWriteFlvFile; bool audioOnly; - RtmpConnectionConfiguration() : enableWriteFlvFile(false), - audioOnly(false) {} + RtmpConnectionConfiguration() : audioOnly(false) {} }; /** @@ -339,7 +337,7 @@ class IRtmpConnection : public RefCountInterface { * - ERR_INVALID_ARGUMENT: The passed in argument is invalid. * - ERR_INVALID_STATE: The current connection state is not STATE_DISCONNECTED(3). */ - virtual int connect(const char* url, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int connect(const char* url, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Disconnects from the RTMP server. @@ -348,7 +346,7 @@ class IRtmpConnection : public RefCountInterface { * STATE_DISCONNECTED(4). You will be notified with the callback * \ref onDisconnected "onDisconnected". */ - virtual int disconnect(ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int disconnect(aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Gets the current connection information. @@ -376,7 +374,7 @@ class IRtmpConnection : public RefCountInterface { * - 0: Success. * - < 0: Failure. */ - virtual int registerObserver(IRtmpConnectionObserver* observer, void(*safeDeleter)(IRtmpConnectionObserver*) = NULL, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int registerObserver(IRtmpConnectionObserver* observer, void(*safeDeleter)(IRtmpConnectionObserver*) = NULL, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Releases the registered IRtmpConnectionObserver object. diff --git a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraRtmpLocalUser.h b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraRtmpLocalUser.h index 4b2531f47..e5ee27f0c 100644 --- a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraRtmpLocalUser.h +++ b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraRtmpLocalUser.h @@ -134,7 +134,7 @@ class IRtmpLocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int setAudioStreamConfiguration(const RtmpStreamingAudioConfiguration& config, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setAudioStreamConfiguration(const RtmpStreamingAudioConfiguration& config, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Set the parameters of the video encoder when pushing the stream @@ -145,7 +145,7 @@ class IRtmpLocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int setVideoStreamConfiguration(const RtmpStreamingVideoConfiguration& config, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setVideoStreamConfiguration(const RtmpStreamingVideoConfiguration& config, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Adjusts the audio volume for publishing. @@ -156,7 +156,7 @@ class IRtmpLocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int adjustRecordingSignalVolume(int volume, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int adjustRecordingSignalVolume(int volume, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Gets the current volume for publishing. @@ -182,7 +182,7 @@ class IRtmpLocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int adjustVideoBitrate(VideoBitrateAdjustType type, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int adjustVideoBitrate(VideoBitrateAdjustType type, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Set whether to enable local video @@ -195,7 +195,7 @@ class IRtmpLocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int setVideoEnabled(bool enabled, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setVideoEnabled(bool enabled, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Publishes a local audio track to the RTMP connection. @@ -205,7 +205,7 @@ class IRtmpLocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int publishAudio(agora_refptr audioTrack, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int publishAudio(agora_refptr audioTrack, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Stops publishing the local audio track to the RTMP connection. @@ -215,7 +215,7 @@ class IRtmpLocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int unpublishAudio(agora_refptr audioTrack, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int unpublishAudio(agora_refptr audioTrack, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Publishes a local video track to the RTMP connection. @@ -225,7 +225,7 @@ class IRtmpLocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int publishVideo(agora_refptr videoTrack, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int publishVideo(agora_refptr videoTrack, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Stops publishing the local video track to the RTMP connection. @@ -234,7 +234,7 @@ class IRtmpLocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int unpublishVideo(agora_refptr videoTrack, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int unpublishVideo(agora_refptr videoTrack, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Registers a RTMP user observer object. @@ -247,7 +247,7 @@ class IRtmpLocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int registerRtmpUserObserver(IRtmpLocalUserObserver* observer, void(*safeDeleter)(IRtmpLocalUserObserver*) = NULL, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int registerRtmpUserObserver(IRtmpLocalUserObserver* observer, void(*safeDeleter)(IRtmpLocalUserObserver*) = NULL, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Releases the IRtmpLocalUserObserver object previously registered using registerRtmpUserObserver(). @@ -267,7 +267,7 @@ class IRtmpLocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int registerAudioFrameObserver(media::IAudioPcmFrameSink* observer, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int registerAudioFrameObserver(media::IAudioPcmFrameSink* observer, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Unregisters an audio frame observer object. @@ -285,7 +285,7 @@ class IRtmpLocalUser { * - 0: Success. * - < 0: Failure. */ - virtual int registerVideoFrameObserver(media::base::IVideoFrameObserver* observer, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int registerVideoFrameObserver(media::base::IVideoFrameObserver* observer, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Unregisters a video frame observer object. diff --git a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraScreenCapturer.h b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraScreenCapturer.h index ee1633fb6..3204f9916 100644 --- a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraScreenCapturer.h +++ b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraScreenCapturer.h @@ -8,7 +8,7 @@ #include "AgoraBase.h" #include "AgoraRefPtr.h" -#include +#include namespace agora { namespace rtc { @@ -91,7 +91,7 @@ class IScreenCapturer : public RefCountInterface { * - < 0: Failure. * - ERR_NOT_READY: No screen or window is being shared. */ - virtual int setContentHint(VIDEO_CONTENT_HINT contentHint, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setContentHint(VIDEO_CONTENT_HINT contentHint, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Updates the screen capture region. @@ -103,19 +103,19 @@ class IScreenCapturer : public RefCountInterface { * - < 0: Failure. * - No screen or window is being shared. */ - virtual int updateScreenCaptureRegion(const Rectangle& regionRect, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int updateScreenCaptureRegion(const Rectangle& regionRect, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Set orientation of the captured screen image * @param VIDEO_ORIENTATION orientaion of the device 0(by default), 90, 180, 270 */ - virtual int setScreenOrientation(VIDEO_ORIENTATION orientation, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setScreenOrientation(VIDEO_ORIENTATION orientation, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Set frame rate of the screen capture source * @param rate frame rate (in fps) */ - virtual int setFrameRate(int rate, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setFrameRate(int rate, aosl_ref_t ares = AOSL_REF_INVALID) = 0; #if defined(__ANDROID__) /** @@ -155,7 +155,7 @@ class IScreenCapturer2 : public RefCountInterface { * - < 0: Failure. * - ERR_INVALID_ARGUMENT if data is null. */ - virtual int setScreenCaptureDimensions(const VideoDimensions& dimensions, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setScreenCaptureDimensions(const VideoDimensions& dimensions, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Updates the screen capture region. @@ -167,13 +167,13 @@ class IScreenCapturer2 : public RefCountInterface { * - < 0: Failure. * - No screen or window is being shared. */ - virtual int updateScreenCaptureRegion(const Rectangle& regionRect, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int updateScreenCaptureRegion(const Rectangle& regionRect, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Set frame rate of the screen capture source * @param rate frame rate (in fps) */ - virtual int setFrameRate(int rate, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setFrameRate(int rate, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Set channels and sample rate of screen audio capturing @@ -183,7 +183,7 @@ class IScreenCapturer2 : public RefCountInterface { * - 0: Sucess. * - < 0: Failure */ - virtual int setAudioRecordConfig(int channels, int sampleRate, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setAudioRecordConfig(int channels, int sampleRate, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Set volume of screen audio capturing @@ -192,7 +192,7 @@ class IScreenCapturer2 : public RefCountInterface { * - 0: Sucess. * - < 0: Failure */ - virtual int setAudioVolume(uint32_t volume, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setAudioVolume(uint32_t volume, aosl_ref_t ares = AOSL_REF_INVALID) = 0; protected: virtual ~IScreenCapturer2() {} diff --git a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraSyncClient.h b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraSyncClient.h new file mode 100644 index 000000000..7c8880de2 --- /dev/null +++ b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraSyncClient.h @@ -0,0 +1,139 @@ +// Copyright (c) 2020 Agora.io. All rights reserved + +// This program is confidential and proprietary to Agora.io. +// And may not be copied, reproduced, modified, disclosed to others, published +// or used, in whole or in part, without the express prior written permission +// of Agora.io. + +#pragma once // NOLINT(build/header_guard) +#include "AgoraRefPtr.h" +#include "AgoraBase.h" +#include +#include + +namespace agora { +namespace base { + +enum SyncClientError { + kOk = 0, + kFail = -1, + kConnectSyncFailed = -2, + kConnectDatabaseFailed = -3, + kDisconnectDatabaseFailed = -4, + kDatabaseNotConnected = -5, + kCreateCollectionFailed = -6, + kCollectionNotCreated = -7, + kCollectionExisted = -8, + kInvalidParams = -9, + kNotLoggedIn = -10, + kQueryDocFailed = -11, + kDocNotCreated = -12, +}; + +typedef void(*syncClientCallback)(SyncClientError, void*); +typedef void(*dataBaseOpCallback)(SyncClientError, const char*, void*); +typedef void(*collectionOpCallback)(SyncClientError, const char*, const char*, void*); +typedef void(*queryDocCallback)(SyncClientError error, const char* resultJson, size_t count, bool more, void* userData); + +enum SyncEventType { + kInserted = 0, + kPut = 1, + kDeleted = 2, + + kToBeInserted = 3, + kToBePut = 4, + kToBeDeleted = 5, + + kTransactionBegin = 6, + kTransactionEnd = 7, + kDocSyncEnd = 8, +}; + +/** + * sync client observer + */ +class ISyncClientObserver { + public: + struct CollectionEvent { + SyncEventType type; + const char* path; + const char* value; + }; + virtual void onCollectionEvent(const char* previousJson, const char* curJson, const char* collection, const char* docName, + const CollectionEvent* events, int eventSize) = 0; + virtual void onDatabaseEvent(const char* databaseName, SyncClientError error) = 0; + virtual void onDataException(const char* databaseName, const char* collectionName) = 0; + virtual ~ISyncClientObserver() {}; +}; + +/** + * sync configuration + */ +struct SyncConfig { + const char* appId; + /* shakehand interval in seconds, 0 means enable manual shake hand */ + uint32_t shakehand_interval; + /* connection timeout in seconds */ + uint32_t connection_timeout; + /* compact interval in seconds */ + uint32_t compact_interval; + SyncConfig() : shakehand_interval(1), connection_timeout(10), compact_interval(3600 * 1000) {} +}; + +class ISyncClient : public RefCountInterface { +protected: + virtual ~ISyncClient() {} +public: + + virtual int32_t registerSyncClientObserver(ISyncClientObserver* observer, void(*safeDeleter)(ISyncClientObserver*) = OPTIONAL_NULLPTR, aosl_ref_t ares = AOSL_REF_INVALID) = 0; + virtual int32_t unregisterSyncClientObserver(ISyncClientObserver* observer) = 0; + // client operations + virtual int32_t login(const char* token, const char* channelName, user_id_t userId, syncClientCallback callback, void* userData, aosl_ref_t ares = AOSL_REF_INVALID) = 0; + virtual int32_t queryDoc(const char* database, const char* coll, const char* range_start, const char* range_end, int64_t limits, bool doc_only, bool count_only, queryDocCallback callback, void* userData, aosl_ref_t ares = AOSL_REF_INVALID) = 0; + virtual int32_t logout(aosl_ref_t ares = AOSL_REF_INVALID) = 0; + virtual int32_t renewToken(const char* token, aosl_ref_t ares = AOSL_REF_INVALID) = 0; + + // database operations + virtual int32_t connectDatabase(const char* database, dataBaseOpCallback callback, void* userData, aosl_ref_t ares = AOSL_REF_INVALID) = 0; + virtual int32_t disconnectDatabase(const char* database, + dataBaseOpCallback callback, void* userData, aosl_ref_t ares = AOSL_REF_INVALID) = 0; + virtual int32_t createCollection(const char* database, const char* collection, + const char** readable, int readSize, + collectionOpCallback callback, void* userData, aosl_ref_t ares = AOSL_REF_INVALID) = 0; + virtual int32_t deleteCollection(const char* database, const char* collection, + collectionOpCallback callback, void* userData, aosl_ref_t ares = AOSL_REF_INVALID) = 0; + + // collection operations + virtual int32_t subscribe(const char* database, const char* collection, + util::AString& snapshotJson) = 0; + virtual int32_t unsubscribe(const char* database, const char* collection, aosl_ref_t ares = AOSL_REF_INVALID) = 0; + virtual int32_t putDoc(const char* database, const char* collection, + const char* docName, aosl_ref_t ares = AOSL_REF_INVALID) = 0; + virtual int32_t deleteDoc(const char* database, const char* collection, + const char* docName, aosl_ref_t ares = AOSL_REF_INVALID) = 0; + virtual int32_t getDocs(const char* database, const char* collection, + util::AString* docNames, uint32_t docSize) = 0; + + // document operations + virtual int32_t putDocValue(const char* database, const char* collection, + const char* docName, const char* jsonValue, aosl_ref_t ares = AOSL_REF_INVALID) = 0; + virtual int32_t updateDocValue(const char* database, const char* collection, + const char* docName, const char* path, + const char* jsonValue, aosl_ref_t ares = AOSL_REF_INVALID) = 0; + virtual int32_t deleteDocValue(const char* database, const char* collection, + const char* docName, const char* path, aosl_ref_t ares = AOSL_REF_INVALID) = 0; + virtual int32_t deleteDocValues(const char* database, const char* collection, const char* docName, + const char** path, uint32_t pathSize, + aosl_ref_t ares = AOSL_REF_INVALID) = 0; + virtual int32_t getDocValue(const char* database, const char* collection, + const char* docName, util::AString& jsonValue) = 0; + virtual int32_t hasPath(const char* database, const char* collection, + const char* docName, const char* path, bool& result) = 0; + virtual int32_t keepAliveDoc(const char* database, const char* collection, + const char* docName, uint32_t ttl, aosl_ref_t ares = AOSL_REF_INVALID) = 0; + + // sync operations + virtual int32_t shakehand(aosl_ref_t ares = AOSL_REF_INVALID) = 0; +}; +}// namespace base +}// namespace agora diff --git a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraVideoFrame.h b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraVideoFrame.h index 9b114002f..3823ec28b 100644 --- a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraVideoFrame.h +++ b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraVideoFrame.h @@ -40,6 +40,7 @@ struct TextureInfo { EglContextType context_type; void* shared_context; int texture_id; + int64_t fence_object; float transform_matrix[16]; }; @@ -73,84 +74,6 @@ struct PaddedRawPixelBuffer { : data(NULL), size(0), stride(0) {} }; -struct ColorSpace { - enum PrimaryID { - // The indices are equal to the values specified in T-REC H.273 Table 2. - PRIMARYID_BT709 = 1, - PRIMARYID_UNSPECIFIED = 2, - PRIMARYID_BT470M = 4, - PRIMARYID_BT470BG = 5, - PRIMARYID_SMPTE170M = 6, // Identical to BT601 - PRIMARYID_SMPTE240M = 7, - PRIMARYID_FILM = 8, - PRIMARYID_BT2020 = 9, - PRIMARYID_SMPTEST428 = 10, - PRIMARYID_SMPTEST431 = 11, - PRIMARYID_SMPTEST432 = 12, - PRIMARYID_JEDECP22 = 22, // Identical to EBU3213-E - }; - - enum RangeID { - // The indices are equal to the values specified at - // https://www.webmproject.org/docs/container/#colour for the element Range. - RANGEID_INVALID = 0, - // Limited Rec. 709 color range with RGB values ranging from 16 to 235. - RANGEID_LIMITED = 1, - // Full RGB color range with RGB valees from 0 to 255. - RANGEID_FULL = 2, - // Range is defined by MatrixCoefficients/TransferCharacteristics. - RANGEID_DERIVED = 3, - }; - - enum MatrixID { - // The indices are equal to the values specified in T-REC H.273 Table 4. - MATRIXID_RGB = 0, - MATRIXID_BT709 = 1, - MATRIXID_UNSPECIFIED = 2, - MATRIXID_FCC = 4, - MATRIXID_BT470BG = 5, - MATRIXID_SMPTE170M = 6, - MATRIXID_SMPTE240M = 7, - MATRIXID_YCOCG = 8, - MATRIXID_BT2020_NCL = 9, - MATRIXID_BT2020_CL = 10, - MATRIXID_SMPTE2085 = 11, - MATRIXID_CDNCLS = 12, - MATRIXID_CDCLS = 13, - MATRIXID_BT2100_ICTCP = 14, - }; - - enum TransferID { - // The indices are equal to the values specified in T-REC H.273 Table 3. - TRANSFERID_BT709 = 1, - TRANSFERID_UNSPECIFIED = 2, - TRANSFERID_GAMMA22 = 4, - TRANSFERID_GAMMA28 = 5, - TRANSFERID_SMPTE170M = 6, - TRANSFERID_SMPTE240M = 7, - TRANSFERID_LINEAR = 8, - TRANSFERID_LOG = 9, - TRANSFERID_LOG_SQRT = 10, - TRANSFERID_IEC61966_2_4 = 11, - TRANSFERID_BT1361_ECG = 12, - TRANSFERID_IEC61966_2_1 = 13, - TRANSFERID_BT2020_10 = 14, - TRANSFERID_BT2020_12 = 15, - TRANSFERID_SMPTEST2084 = 16, - TRANSFERID_SMPTEST428 = 17, - TRANSFERID_ARIB_STD_B67 = 18, - }; - - PrimaryID primaries; - TransferID transfer; - MatrixID matrix; - RangeID range; - - ColorSpace() - : primaries(PRIMARYID_UNSPECIFIED), transfer(TRANSFERID_UNSPECIFIED), - matrix(MATRIXID_UNSPECIFIED), range(RANGEID_INVALID) {} -}; - /** * This structure defines underlying detailed video frame data of @ref agora::rtc::IVideoFrame * @@ -173,7 +96,7 @@ struct VideoFrameData { int width; int height; int rotation; - ColorSpace color_space; + agora::media::base::ColorSpace color_space; int64_t timestamp_ms; // Capture time in milli-seconds }; diff --git a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraVideoMixerSource.h b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraVideoMixerSource.h index 7cbe7183c..58d085b6b 100644 --- a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraVideoMixerSource.h +++ b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraVideoMixerSource.h @@ -51,7 +51,7 @@ class IVideoMixerSource : public RefCountInterface { * 0 - Success * <0 - Failure */ - virtual int addVideoTrack(const char* id, agora_refptr track, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int addVideoTrack(const char* id, agora_refptr track, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Remove the video track. * @param id The unique id of the stream. @@ -60,7 +60,7 @@ class IVideoMixerSource : public RefCountInterface { * 0 - Success * <0 - Failure */ - virtual int removeVideoTrack(const char* id, agora_refptr track, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int removeVideoTrack(const char* id, agora_refptr track, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Configures the layout of video frames comming from a specific track (indicated by uid) * on the mixer canvas. @@ -70,7 +70,7 @@ class IVideoMixerSource : public RefCountInterface { * 0 - Success * <0 - Failure */ - virtual int setStreamLayout(const char* id, const MixerLayoutConfig& config, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setStreamLayout(const char* id, const MixerLayoutConfig& config, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Remove the user layout on the mixer canvas * @param id The unique id of the stream. @@ -79,7 +79,7 @@ class IVideoMixerSource : public RefCountInterface { * 0 - Success * <0 - Failure */ - virtual int delStreamLayout(const char* id, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int delStreamLayout(const char* id, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Add a image source to the mixer with its layout configuration on the mixer canvas. * @param id The unique id of the image. @@ -88,7 +88,7 @@ class IVideoMixerSource : public RefCountInterface { * 0 - Success * <0 - Failure */ - virtual int addImageSource(const char* id, const MixerLayoutConfig& config, ImageType type = kPng, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int addImageSource(const char* id, const MixerLayoutConfig& config, ImageType type = kPng, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Delete a image source to the mixer. * @param id The unique id of the image. @@ -96,18 +96,18 @@ class IVideoMixerSource : public RefCountInterface { * 0 - Success * <0 - Failure */ - virtual int delImageSource(const char* id, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int delImageSource(const char* id, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Clear all the layout settings set previously */ - virtual int clearLayout(ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int clearLayout(aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Refresh the user layout on the mixer canvas * @return * 0 - Success * <0 - Failure */ - virtual int refresh(ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int refresh(aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Set the mixer canvas background to override the default configuration * @param width width of the canvas @@ -118,7 +118,7 @@ class IVideoMixerSource : public RefCountInterface { * 0 - Success * <0 - Failure */ - virtual int setBackground(uint32_t width, uint32_t height, int fps, uint32_t color_argb = 0, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setBackground(uint32_t width, uint32_t height, int fps, uint32_t color_argb = 0, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Set the mixer canvas background to override the default configuration * @param width width of the canvas @@ -129,7 +129,7 @@ class IVideoMixerSource : public RefCountInterface { * 0 - Success * <0 - Failure */ - virtual int setBackground(uint32_t width, uint32_t height, int fps, const char* url, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setBackground(uint32_t width, uint32_t height, int fps, const char* url, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Set the rotation of the mixed video stream * @param rotation:0:none, 1:90°, 2:180°, 3:270° @@ -137,7 +137,7 @@ class IVideoMixerSource : public RefCountInterface { * 0 - Success * <0 - Failure */ - virtual int setRotation(uint8_t rotation, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setRotation(uint8_t rotation, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Get the average delay in ms introduced by the mixer module, which includes the average * mixing delay plus the encoder delay. @@ -152,7 +152,7 @@ class IVideoMixerSource : public RefCountInterface { * 0 - Success * <0 - Failure */ - virtual int setMasterClockSource(const char* id = NULL, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setMasterClockSource(const char* id = NULL, aosl_ref_t ares = AOSL_REF_INVALID) = 0; }; } //namespace rtc diff --git a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraVideoTrack.h b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraVideoTrack.h index a79cb5e45..ea36ee93e 100644 --- a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraVideoTrack.h +++ b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/NGIAgoraVideoTrack.h @@ -9,7 +9,7 @@ #pragma once // NOLINT(build/header_guard) #include "AgoraBase.h" -#include +#include #ifndef OPTIONAL_OVERRIDE #if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800) @@ -26,6 +26,65 @@ class IVideoEncodedFrameObserver; class IMediaPacketReceiver; class IVideoSinkBase; +enum StreamLayerIndexInternal { + STREAM_LAYER_1 = 1, + STREAM_LAYER_2 = 2, + STREAM_LAYER_3 = 3, + STREAM_LAYER_4 = 4, + STREAM_LAYER_5 = 5, + STREAM_LAYER_6 = 6, + STREAM_LOW = 7, + STREAM_LAYER_COUNT_MAX = 8 + }; + +struct StreamLayerConfigInternal { + VideoDimensions dimensions; + int framerate; + int bitrate_kbps; + bool enable; + StreamLayerConfigInternal() : dimensions(0, 0), framerate(0), bitrate_kbps(STANDARD_BITRATE), enable(false) {} + StreamLayerConfigInternal(const StreamLayerConfigInternal& other) : dimensions(other.dimensions), framerate(other.framerate), bitrate_kbps(other.bitrate_kbps), enable(other.enable) {} + bool operator==(const StreamLayerConfigInternal& rhs) const { + return dimensions == rhs.dimensions && bitrate_kbps == rhs.bitrate_kbps && framerate == rhs.framerate && enable == rhs.enable; + } + + StreamLayerConfigInternal& operator=(const SimulcastConfig::StreamLayerConfig& slc) { + dimensions = slc.dimensions; + framerate = slc.framerate; + enable = slc.enable; + return *this; + } + + void reset() { + dimensions.width = 0; + dimensions.height = 0; + framerate = 0; + bitrate_kbps = STANDARD_BITRATE; + enable = false; + } +}; + +struct SimulcastConfigInternal { + StreamLayerConfigInternal simulcastlayerConfigs[STREAM_LAYER_COUNT_MAX]; + + void reset() { + for (int i = STREAM_LAYER_1; i < STREAM_LAYER_COUNT_MAX; i++) { + simulcastlayerConfigs[i].reset(); + } + } + + bool operator==(const SimulcastConfigInternal& rhs) const { + for (int i = 0; i < STREAM_LAYER_COUNT_MAX; i++) { + if (simulcastlayerConfigs[i] == rhs.simulcastlayerConfigs[i]) { + continue; + } else { + return false; + } + } + return true; + } +}; + enum VideoTrackType { LOCAL_VIDEO_TRACK, REMOTE_VIDEO_TRACK, @@ -56,7 +115,7 @@ class IVideoTrack : public RefCountInterface { */ virtual bool addVideoFilter( agora_refptr filter, media::base::VIDEO_MODULE_POSITION position = media::base::POSITION_POST_CAPTURER, - const char* id = NULL, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + const char* id = NULL, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Removes the video filter added by `addVideoFilter` from the video track. @@ -70,7 +129,7 @@ class IVideoTrack : public RefCountInterface { */ virtual bool removeVideoFilter( agora_refptr filter, media::base::VIDEO_MODULE_POSITION position = media::base::POSITION_POST_CAPTURER, - const char* id = NULL, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + const char* id = NULL, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Whether a video filter exists @@ -95,7 +154,7 @@ class IVideoTrack : public RefCountInterface { * - `true`: The video renderer is added successfully. * - `false`: The video renderer fails to be added. */ - virtual bool addRenderer(agora_refptr videoRenderer, media::base::VIDEO_MODULE_POSITION position, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual bool addRenderer(agora_refptr videoRenderer, media::base::VIDEO_MODULE_POSITION position, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Removes the video renderer added by `addRenderer` from the video track. * @@ -105,7 +164,7 @@ class IVideoTrack : public RefCountInterface { * - `true`: The video renderer is removed successfully. * - `false`: The video renderer fails to be removed. */ - virtual bool removeRenderer(agora_refptr videoRenderer, media::base::VIDEO_MODULE_POSITION position, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual bool removeRenderer(agora_refptr videoRenderer, media::base::VIDEO_MODULE_POSITION position, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Get the track type of the video track * @return @@ -121,7 +180,7 @@ class IVideoTrack : public RefCountInterface { * - 0: success * - <0: failure */ - virtual int enableVideoFilter(const char* id, bool enable, ahpl_ref_t ares = AHPL_REF_INVALID) { return -1; } + virtual int enableVideoFilter(const char* id, bool enable, aosl_ref_t ares = AOSL_REF_INVALID) { return -1; } /** * set the properties of the specified video filter @@ -132,7 +191,7 @@ class IVideoTrack : public RefCountInterface { * - 0: success * - <0: failure */ - virtual int setFilterProperty(const char* id, const char* key, const char* json_value, ahpl_ref_t ares = AHPL_REF_INVALID) { return -1; } + virtual int setFilterProperty(const char* id, const char* key, const char* json_value, aosl_ref_t ares = AOSL_REF_INVALID) { return -1; } /** * get the properties of the specified video filter @@ -143,12 +202,18 @@ class IVideoTrack : public RefCountInterface { * - 0: success * - <0: failure */ - virtual int getFilterProperty(const char* id, const char* key, char* json_value, size_t buf_size, ahpl_ref_t ares = AHPL_REF_INVALID) { return -1; } + virtual int getFilterProperty(const char* id, const char* key, char* json_value, size_t buf_size, aosl_ref_t ares = AOSL_REF_INVALID) { return -1; } protected: ~IVideoTrack() {} }; +struct SimulcastStreamProfile { + int width; + int height; + int framerate; + int bitrate; +}; /** * The statistics of the local video track. */ @@ -255,6 +320,8 @@ struct LocalVideoTrackStats { /** The brightness level of the video image captured by the local camera. See #CAPTURE_BRIGHTNESS_LEVEL_TYPE. */ CAPTURE_BRIGHTNESS_LEVEL_TYPE capture_brightness_level; + + SimulcastStreamProfile simulcast_stream_profile[STREAM_LAYER_COUNT_MAX]; LocalVideoTrackStats() : number_of_streams(0), bytes_major_stream(0), @@ -307,7 +374,7 @@ class ILocalVideoTrack : public IVideoTrack { * - `true`: Enable the local video track. * - `false`: Disable the local video track. */ - virtual int setEnabled(bool enable, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setEnabled(bool enable, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Sets the video encoder configuration. @@ -325,7 +392,7 @@ class ILocalVideoTrack : public IVideoTrack { * - 0: Success. * - < 0: Failure. */ - virtual int setVideoEncoderConfiguration(const VideoEncoderConfiguration& config, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setVideoEncoderConfiguration(const VideoEncoderConfiguration& config, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Set simulcast stream mode, enable, disable or auto enable @@ -336,7 +403,7 @@ class ILocalVideoTrack : public IVideoTrack { * - 0: Success. * - < 0: Failure. */ - virtual int setSimulcastStreamMode(SIMULCAST_STREAM_MODE mode, const SimulcastStreamConfig& config, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int setSimulcastStreamMode(SIMULCAST_STREAM_MODE mode, const SimulcastConfigInternal& config, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Gets the state of the local video stream. @@ -388,6 +455,9 @@ struct RemoteVideoTrackStats { * The bitrate (Kbps) received in the reported interval. */ int receivedBitrate; + /** The decoder input frame rate (fps) of the remote video track. + */ + int decoderInputFrameRate; /** The decoder output frame rate (fps) of the remote video track. */ int decoderOutputFrameRate; @@ -457,13 +527,17 @@ struct RemoteVideoTrackStats { decoder vender id, VideoCodecVenderId */ uint32_t decoder_vender_id; + /** + The decoder codec type of the remote video track + */ + uint32_t decoder_type; RemoteVideoTrackStats() : uid(0), delay(0), width(0), height(0), - receivedBitrate(0), decoderOutputFrameRate(0), rendererOutputFrameRate(0), + receivedBitrate(0), decoderInputFrameRate(0), decoderOutputFrameRate(0), rendererOutputFrameRate(0), frameLossRate(0), packetLossRate(0), rxStreamType(VIDEO_STREAM_HIGH), totalFrozenTime(0), frozenRate(0), received_bytes(0), totalDecodedFrames(0), avSyncTimeMs(0), downlink_process_time_ms(0), frame_render_delay_ms(0), totalActiveTime(0), - publishDuration(0), vqa_mos(0), vqa_avg_cost_ms(0), decoder_vender_id(0) {} + publishDuration(0), vqa_mos(0), vqa_avg_cost_ms(0), decoder_vender_id(0), decoder_type(0) {} }; /** @@ -504,7 +578,7 @@ class IRemoteVideoTrack : public IVideoTrack { * - 0: Success. * - < 0: Failure. */ - virtual int registerVideoEncodedFrameObserver(agora::media::IVideoEncodedFrameObserver* encodedObserver, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int registerVideoEncodedFrameObserver(agora::media::IVideoEncodedFrameObserver* encodedObserver, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Releases the \ref agora::media::IVideoEncodedFrameObserver "IVideoEncodedFrameObserver" object. * @param encodedObserver The pointer to the `IVideoEncodedFrameObserver` object. @@ -526,7 +600,7 @@ class IRemoteVideoTrack : public IVideoTrack { * - 0: Success. * - < 0: Failure. */ - virtual int registerMediaPacketReceiver(IMediaPacketReceiver* videoReceiver, ahpl_ref_t ares = AHPL_REF_INVALID) = 0; + virtual int registerMediaPacketReceiver(IMediaPacketReceiver* videoReceiver, aosl_ref_t ares = AOSL_REF_INVALID) = 0; /** * Releases the \ref agora::rtc::IMediaPacketReceiver "IMediaPacketReceiver" object. * @param videoReceiver The pointer to the `IMediaPacketReceiver` object. diff --git a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/ahpl_ares.h b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/aosl_ares.h similarity index 60% rename from Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/ahpl_ares.h rename to Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/aosl_ares.h index 92c409bb3..9b5ba7b1f 100644 --- a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/ahpl_ares.h +++ b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/aosl_ares.h @@ -1,22 +1,22 @@ /************************************************************* * Author: Lionfore Hao (haolianfu@agora.io) * Date : May 30th, 2023 - * Module: AHPL async result object header file + * Module: AOSL async result object header file * * - * This is a part of the Advanced High Performance Library. + * This is a part of the Advanced Operating System Layer. * Copyright (C) 2018 ~ 2023 Agora IO * All rights reserved. * *************************************************************/ -#ifndef __AHPL_ARES_H__ -#define __AHPL_ARES_H__ +#ifndef __AOSL_ARES_H__ +#define __AOSL_ARES_H__ -#include -#include -#include -#include +#include +#include +#include +#include #ifdef __cplusplus extern "C" { @@ -28,9 +28,9 @@ extern "C" { * Parameter: * arg: the parameter attached with the async result object; * Return value: - * the async result object ref id just created, AHPL_REF_INVALID when failed. + * the async result object ref id just created, AOSL_REF_INVALID when failed. **/ -extern __ahpl_api__ ahpl_ref_t ahpl_ares_create (void *arg); +extern __aosl_api__ aosl_ref_t aosl_ares_create (void *arg); /** * Complete the specified async result object. @@ -38,10 +38,10 @@ extern __ahpl_api__ ahpl_ref_t ahpl_ares_create (void *arg); * ref: the async result object ref id; * result: a result value which can be retrieved by wait function; * Return value: - * <0: error occured, and ahpl_errno indicates which error; + * <0: error occured, and aosl_errno indicates which error; * >=0: successful; **/ -extern __ahpl_api__ int ahpl_ares_complete (ahpl_ref_t ref, intptr_t result); +extern __aosl_api__ int aosl_ares_complete (aosl_ref_t ref, intptr_t result); /** * Wait the specified async result object to complete. @@ -50,23 +50,23 @@ extern __ahpl_api__ int ahpl_ares_complete (ahpl_ref_t ref, intptr_t result); * timeo: maximum waiting time in milliseconds; * result: variable address for the value which was set by complete function, * NOTE: the *result only will be set when the return value of wait - * function is AHPL_POLL_ST_SIGNALED and result != NULL, if you + * function is AOSL_POLL_ST_SIGNALED and result != NULL, if you * do not care the complete result, just passing NULL to it; * Return value: - * <0: error occured, and ahpl_errno indicates which error; - * >=0: AHPL_POLL_ST_* macros value; + * <0: error occured, and aosl_errno indicates which error; + * >=0: AOSL_POLL_ST_* macros value; **/ -extern __ahpl_api__ int ahpl_ares_wait (ahpl_ref_t ref, intptr_t timeo, intptr_t *result); +extern __aosl_api__ int aosl_ares_wait (aosl_ref_t ref, intptr_t timeo, intptr_t *result); /** * Reset the specified async result object to non signaled state. * Parameters: * ref: the async result object ref id * Return value: - * <0: error occured, and ahpl_errno indicates which error; + * <0: error occured, and aosl_errno indicates which error; * >=0: successful; **/ -extern __ahpl_api__ int ahpl_ares_reset (ahpl_ref_t ref); +extern __aosl_api__ int aosl_ares_reset (aosl_ref_t ref); @@ -74,4 +74,4 @@ extern __ahpl_api__ int ahpl_ares_reset (ahpl_ref_t ref); } #endif -#endif /* __AHPL_ARES_H__ */ \ No newline at end of file +#endif /* __AOSL_ARES_H__ */ \ No newline at end of file diff --git a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/ahpl_defs.h b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/aosl_defs.h similarity index 70% rename from Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/ahpl_defs.h rename to Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/aosl_defs.h index 3f1cb6a02..ce2386549 100644 --- a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/ahpl_defs.h +++ b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/aosl_defs.h @@ -1,21 +1,21 @@ /************************************************************* * Author: Lionfore Hao (haolianfu@agora.io) * Date : Jul 21st, 2018 - * Module: AHPL common definitions header file + * Module: AOSL common definitions header file * * - * This is a part of the Advanced High Performance Library. + * This is a part of the Advanced Operating System Layer. * Copyright (C) 2018 Agora IO * All rights reserved. * *************************************************************/ -#ifndef __AHPL_DEFS_H__ -#define __AHPL_DEFS_H__ +#ifndef __AOSL_DEFS_H__ +#define __AOSL_DEFS_H__ -#define ahpl_stringify_1(x) #x -#define ahpl_stringify(x) ahpl_stringify_1(x) +#define aosl_stringify_1(x) #x +#define aosl_stringify(x) aosl_stringify_1(x) #ifndef __MAKERCORE_ASSEMBLY__ @@ -42,17 +42,17 @@ extern "C" { #endif #endif -#define ahpl_rela_addr(type, field) (&((type *)0)->field) -#define ahpl_base_addr(ptr, type, field) \ +#define aosl_rela_addr(type, field) (&((type *)0)->field) +#define aosl_base_addr(ptr, type, field) \ ((type *)((uintptr_t)(ptr) - (uintptr_t)(&((type *)0)->field))) -#define ahpl_min(x, y) ((x) < (y) ? (x) : (y)) -#define ahpl_max(x, y) ((x) > (y) ? (x) : (y)) +#define aosl_min(x, y) ((x) < (y) ? (x) : (y)) +#define aosl_max(x, y) ((x) > (y) ? (x) : (y)) /* I think 64 args is big enough */ -#define AHPL_VAR_ARGS_MAX 64 +#define AOSL_VAR_ARGS_MAX 64 #ifdef BUILD_TARGET_SHARED @@ -66,17 +66,17 @@ extern "C" { #endif -#ifndef __ahpl_api__ +#ifndef __aosl_api__ #if defined (_MSC_VER) && defined (BUILDING_API_IMPL_SOURCE) && defined (BUILD_TARGET_SHARED) -#define __ahpl_api__ __declspec (dllexport) +#define __aosl_api__ __declspec (dllexport) #elif defined (_MSC_VER) && !defined (BUILDING_API_IMPL_SOURCE) -#define __ahpl_api__ __declspec (dllimport) +#define __aosl_api__ __declspec (dllimport) #else -#define __ahpl_api__ +#define __aosl_api__ #endif #endif -#if defined (BUILDING_API_IMPL_SOURCE) || defined (STATIC_LINKING_AHPL) +#if defined (BUILDING_API_IMPL_SOURCE) || defined (STATIC_LINKING_AOSL) #if defined (__GNUC__) #define __so_api__ __attribute__ ((visibility ("default"))) @@ -99,34 +99,34 @@ extern "C" { #ifdef __GNUC__ #ifndef __MACH__ -#define AHPL_DEFINE_BIN(v, f) \ +#define AOSL_DEFINE_BIN(v, f) \ __asm__ (".section .rodata\n\t" \ ".globl "#v"_bin_begin\n\t" \ ".hidden "#v"_bin_begin\n\t" \ ".align 4\n\t" \ #v"_bin_begin:\n\t" \ - ".incbin \"" ahpl_stringify(MAKERCORE_THIS_FILE_DIR/f)"\"\n\t" \ + ".incbin \"" aosl_stringify(MAKERCORE_THIS_FILE_DIR/f)"\"\n\t" \ ".globl "#v"_bin_end\n\t" \ ".hidden "#v"_bin_end\n\t" \ #v"_bin_end:\n\t" \ ".previous\n\t") #else -#define AHPL_DEFINE_BIN(v, f) \ +#define AOSL_DEFINE_BIN(v, f) \ __asm__ (".section __TEXT,__const\n\t" \ ".globl _"#v"_bin_begin\n\t" \ ".private_extern _"#v"_bin_begin\n\t" \ ".align 4\n\t" \ "_"#v"_bin_begin:\n\t" \ - ".incbin \"" ahpl_stringify (MAKERCORE_THIS_FILE_DIR/f) "\"\n\t" \ + ".incbin \"" aosl_stringify (MAKERCORE_THIS_FILE_DIR/f) "\"\n\t" \ ".globl _"#v"_bin_end\n\t" \ ".private_extern _"#v"_bin_end\n\t" \ "_"#v"_bin_end:\n\t" \ ".previous\n\t") #endif -#define AHPL_DECLARE_BIN(v) extern unsigned char v##_bin_begin, v##_bin_end -#define AHPL_BIN_ADDR(v) ((void *)&v##_bin_begin) -#define AHPL_BIN_SIZE(v) ((size_t)((unsigned char *)&v##_bin_end - (unsigned char *)&v##_bin_begin)) +#define AOSL_DECLARE_BIN(v) extern unsigned char v##_bin_begin, v##_bin_end +#define AOSL_BIN_ADDR(v) ((void *)&v##_bin_begin) +#define AOSL_BIN_SIZE(v) ((size_t)((unsigned char *)&v##_bin_end - (unsigned char *)&v##_bin_begin)) #endif @@ -138,26 +138,26 @@ __asm__ (".section __TEXT,__const\n\t" \ #ifdef __GNUC__ #ifndef __MACH__ -.macro AHPL_DEFINE_BIN_S v, f +.macro AOSL_DEFINE_BIN_S v, f .section .rodata .globl \v\()_bin_begin .hidden \v\()_bin_begin .align 4 \v\()_bin_begin: - .incbin ahpl_stringify (MAKERCORE_THIS_FILE_DIR/\f) + .incbin aosl_stringify (MAKERCORE_THIS_FILE_DIR/\f) .globl \v\()_bin_end .hidden \v\()_bin_end \v\()_bin_end: .previous .endm #else -.macro AHPL_DEFINE_BIN_S v, f +.macro AOSL_DEFINE_BIN_S v, f .section __TEXT,__const .globl _\()\v\()_bin_begin .private_extern _\()\v\()_bin_begin .align 4 _\()\v\()_bin_begin: - .incbin ahpl_stringify (MAKERCORE_THIS_FILE_DIR/\f) + .incbin aosl_stringify (MAKERCORE_THIS_FILE_DIR/\f) .globl _\()\v\()_bin_end .private_extern _\()\v\()_bin_end _\()\v\()_bin_end: @@ -168,4 +168,4 @@ __asm__ (".section __TEXT,__const\n\t" \ #endif /* __MAKERCORE_ASSEMBLY__ */ -#endif /* __AHPL_DEFS_H__ */ \ No newline at end of file +#endif /* __AOSL_DEFS_H__ */ \ No newline at end of file diff --git a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/ahpl_poll.h b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/aosl_poll.h similarity index 65% rename from Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/ahpl_poll.h rename to Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/aosl_poll.h index d8d1e7785..4eac74a0d 100644 --- a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/ahpl_poll.h +++ b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/aosl_poll.h @@ -1,21 +1,21 @@ /************************************************************* * Author: Lionfore Hao (haolianfu@agora.io) * Date : May 30th, 2023 - * Module: AHPL poll functionality definition header file + * Module: AOSL poll functionality definition header file * * - * This is a part of the Advanced High Performance Library. + * This is a part of the Advanced Operating System Layer. * Copyright (C) 2018 ~ 2023 Agora IO * All rights reserved. * *************************************************************/ -#ifndef __AHPL_POLL_H__ -#define __AHPL_POLL_H__ +#ifndef __AOSL_POLL_H__ +#define __AOSL_POLL_H__ -#include -#include -#include +#include +#include +#include #ifdef __cplusplus extern "C" { @@ -23,9 +23,9 @@ extern "C" { -#define AHPL_POLL_ST_NONE 0 -#define AHPL_POLL_ST_SIGNALED 1 -#define AHPL_POLL_ST_DESTROY 2 +#define AOSL_POLL_ST_NONE 0 +#define AOSL_POLL_ST_SIGNALED 1 +#define AOSL_POLL_ST_DESTROY 2 /** * Poll the objects specified in refs, return their states. @@ -37,10 +37,10 @@ extern "C" { * then wake up immediately regardless min parameter. * timeo: maximum waiting time in milliseconds; * Return value: - * <0: error occured, and ahpl_errno indicates which error; + * <0: error occured, and aosl_errno indicates which error; * >=0: the signaled refs count before timeout; **/ -extern __ahpl_api__ ssize_t ahpl_poll (ahpl_ref_t refs [], size_t count, size_t min, intptr_t timeo); +extern __aosl_api__ ssize_t aosl_poll (aosl_ref_t refs [], size_t count, size_t min, intptr_t timeo); @@ -48,4 +48,4 @@ extern __ahpl_api__ ssize_t ahpl_poll (ahpl_ref_t refs [], size_t count, size_t } #endif -#endif /* __AHPL_POLL_H__ */ \ No newline at end of file +#endif /* __AOSL_POLL_H__ */ \ No newline at end of file diff --git a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/ahpl_ref.h b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/aosl_ref.h similarity index 51% rename from Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/ahpl_ref.h rename to Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/aosl_ref.h index b5264468b..4a2f439fa 100644 --- a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/ahpl_ref.h +++ b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/aosl_ref.h @@ -1,21 +1,21 @@ /************************************************************* * Author: Lionfore Hao (haolianfu@agora.io) * Date : Nov 19th, 2018 - * Module: AHPL reference object definition file + * Module: AOSL reference object definition file * * - * This is a part of the Advanced High Performance Library. + * This is a part of the Advanced Operating System Layer. * Copyright (C) 2018 Agora IO * All rights reserved. * *************************************************************/ -#ifndef __AHPL_REF_H__ -#define __AHPL_REF_H__ +#ifndef __AOSL_REF_H__ +#define __AOSL_REF_H__ -#include -#include +#include +#include #ifdef __cplusplus @@ -24,22 +24,22 @@ extern "C" { -typedef struct _internal_ref_od_ *ahpl_ref_t; +typedef struct _internal_ref_od_ *aosl_ref_t; -#define AHPL_REF_INVALID ((ahpl_ref_t)(intptr_t)-1) +#define AOSL_REF_INVALID ((aosl_ref_t)(intptr_t)NULL) -#define ahpl_ref_invalid(ref) (((int)(intptr_t)(ref)) < 0) +#define aosl_ref_invalid(ref) ((int)(intptr_t)(ref) <= 0) /** * The reference object destructor function prototype, which invoked when application - * calling ahpl_ref_destroy functions to release resources. + * calling aosl_ref_destroy functions to release resources. * Parameter: * arg: the parameter passed in when creating the reference object; * Return value: * none. **/ -typedef void (*ahpl_ref_dtor_t) (void *arg); +typedef void (*aosl_ref_dtor_t) (void *arg); /** * The reference object creating function prototype, which is used to create a ref object. @@ -51,9 +51,9 @@ typedef void (*ahpl_ref_dtor_t) (void *arg); * none-0 guarantee the ref object relatives must be freed in the caller thread * 0 the ref object relatives could be freed in any thread * Return value: - * the ref object id, please use ahpl_ref_invalid macro to check whether failed. + * the ref object id, please use aosl_ref_invalid macro to check whether failed. **/ -extern __ahpl_api__ ahpl_ref_t ahpl_ref_create (void *arg, ahpl_ref_dtor_t dtor, int caller_free); +extern __aosl_api__ aosl_ref_t aosl_ref_create (void *arg, aosl_ref_dtor_t dtor, int caller_free); /** @@ -61,13 +61,13 @@ extern __ahpl_api__ ahpl_ref_t ahpl_ref_create (void *arg, ahpl_ref_dtor_t dtor, * Parameter: * arg: the ref object argument which was passed in when creating; * argc: specify the argv array elements count, the same as the argc - * when invoking ahpl_ref_[get|read|write] functions; + * when invoking aosl_ref_[get|read|write] functions; * argv: array for passing variable args, the same as the args - * when invoking ahpl_task_exec_* functions; + * when invoking aosl_task_exec_* functions; * Return value: * none. **/ -typedef void (*ahpl_ref_func_t) (void *arg, uintptr_t argc, uintptr_t argv []); +typedef void (*aosl_ref_func_t) (void *arg, uintptr_t argc, uintptr_t argv []); /** * Hold the ref object, and invoke the specified callback function. @@ -78,11 +78,11 @@ typedef void (*ahpl_ref_func_t) (void *arg, uintptr_t argc, uintptr_t argv []); * ...: variable args * Return value: * 0: success - * <0: failure with ahpl_errno set + * <0: failure with aosl_errno set **/ -extern __ahpl_api__ int ahpl_ref_hold (ahpl_ref_t ref, ahpl_ref_func_t f, uintptr_t argc, ...); -extern __ahpl_api__ int ahpl_ref_hold_args (ahpl_ref_t ref, ahpl_ref_func_t f, uintptr_t argc, va_list args); -extern __ahpl_api__ int ahpl_ref_hold_argv (ahpl_ref_t ref, ahpl_ref_func_t f, uintptr_t argc, uintptr_t argv []); +extern __aosl_api__ int aosl_ref_hold (aosl_ref_t ref, aosl_ref_func_t f, uintptr_t argc, ...); +extern __aosl_api__ int aosl_ref_hold_args (aosl_ref_t ref, aosl_ref_func_t f, uintptr_t argc, va_list args); +extern __aosl_api__ int aosl_ref_hold_argv (aosl_ref_t ref, aosl_ref_func_t f, uintptr_t argc, uintptr_t argv []); /** * Hold the ref object and read lock it, then invoke the specified callback function. @@ -93,11 +93,11 @@ extern __ahpl_api__ int ahpl_ref_hold_argv (ahpl_ref_t ref, ahpl_ref_func_t f, u * ...: variable args * Return value: * 0: success - * <0: failure with ahpl_errno set + * <0: failure with aosl_errno set **/ -extern __ahpl_api__ int ahpl_ref_read (ahpl_ref_t ref, ahpl_ref_func_t f, uintptr_t argc, ...); -extern __ahpl_api__ int ahpl_ref_read_args (ahpl_ref_t ref, ahpl_ref_func_t f, uintptr_t argc, va_list args); -extern __ahpl_api__ int ahpl_ref_read_argv (ahpl_ref_t ref, ahpl_ref_func_t f, uintptr_t argc, uintptr_t argv []); +extern __aosl_api__ int aosl_ref_read (aosl_ref_t ref, aosl_ref_func_t f, uintptr_t argc, ...); +extern __aosl_api__ int aosl_ref_read_args (aosl_ref_t ref, aosl_ref_func_t f, uintptr_t argc, va_list args); +extern __aosl_api__ int aosl_ref_read_argv (aosl_ref_t ref, aosl_ref_func_t f, uintptr_t argc, uintptr_t argv []); /** * Hold the ref object and write lock it, then invoke the specified callback function. @@ -108,11 +108,26 @@ extern __ahpl_api__ int ahpl_ref_read_argv (ahpl_ref_t ref, ahpl_ref_func_t f, u * ...: variable args * Return value: * 0: success - * <0: failure with ahpl_errno set + * <0: failure with aosl_errno set **/ -extern __ahpl_api__ int ahpl_ref_write (ahpl_ref_t ref, ahpl_ref_func_t f, uintptr_t argc, ...); -extern __ahpl_api__ int ahpl_ref_write_args (ahpl_ref_t ref, ahpl_ref_func_t f, uintptr_t argc, va_list args); -extern __ahpl_api__ int ahpl_ref_write_argv (ahpl_ref_t ref, ahpl_ref_func_t f, uintptr_t argc, uintptr_t argv []); +extern __aosl_api__ int aosl_ref_write (aosl_ref_t ref, aosl_ref_func_t f, uintptr_t argc, ...); +extern __aosl_api__ int aosl_ref_write_args (aosl_ref_t ref, aosl_ref_func_t f, uintptr_t argc, va_list args); +extern __aosl_api__ int aosl_ref_write_argv (aosl_ref_t ref, aosl_ref_func_t f, uintptr_t argc, uintptr_t argv []); + +/** + * Hold the ref object and set it unsafe, then invoke the specified callback function. + * Parameter: + * ref: the ref object id; + * f: the callback function; + * argc: the args count + * ...: variable args + * Return value: + * 0: success + * <0: failure with aosl_errno set + **/ +extern __aosl_api__ int aosl_ref_unsafe (aosl_ref_t ref, aosl_ref_func_t f, uintptr_t argc, ...); +extern __aosl_api__ int aosl_ref_unsafe_args (aosl_ref_t ref, aosl_ref_func_t f, uintptr_t argc, va_list args); +extern __aosl_api__ int aosl_ref_unsafe_argv (aosl_ref_t ref, aosl_ref_func_t f, uintptr_t argc, uintptr_t argv []); /** * Hold the ref object and set it maystall, then invoke the specified callback function. @@ -123,17 +138,17 @@ extern __ahpl_api__ int ahpl_ref_write_argv (ahpl_ref_t ref, ahpl_ref_func_t f, * ...: variable args * Return value: * 0: success - * <0: failure with ahpl_errno set + * <0: failure with aosl_errno set **/ -extern __ahpl_api__ int ahpl_ref_maystall (ahpl_ref_t ref, ahpl_ref_func_t f, uintptr_t argc, ...); -extern __ahpl_api__ int ahpl_ref_maystall_args (ahpl_ref_t ref, ahpl_ref_func_t f, uintptr_t argc, va_list args); -extern __ahpl_api__ int ahpl_ref_maystall_argv (ahpl_ref_t ref, ahpl_ref_func_t f, uintptr_t argc, uintptr_t argv []); +extern __aosl_api__ int aosl_ref_maystall (aosl_ref_t ref, aosl_ref_func_t f, uintptr_t argc, ...); +extern __aosl_api__ int aosl_ref_maystall_args (aosl_ref_t ref, aosl_ref_func_t f, uintptr_t argc, va_list args); +extern __aosl_api__ int aosl_ref_maystall_argv (aosl_ref_t ref, aosl_ref_func_t f, uintptr_t argc, uintptr_t argv []); -typedef void *ahpl_refobj_t; +typedef void *aosl_refobj_t; -#define AHPL_FREE_ONLY_OBJ ((ahpl_refobj_t)(uintptr_t)1) -#define ahpl_is_free_only(robj) ((int)((ahpl_refobj_t)(robj) == AHPL_FREE_ONLY_OBJ)) +#define AOSL_FREE_ONLY_OBJ ((aosl_refobj_t)(uintptr_t)1) +#define aosl_is_free_only(robj) ((int)((aosl_refobj_t)(robj) == AOSL_FREE_ONLY_OBJ)) /** * Retrieve the ref object arg. @@ -142,7 +157,7 @@ typedef void *ahpl_refobj_t; * Return value: * the ref object arg; **/ -extern __ahpl_api__ void *ahpl_refobj_arg (ahpl_refobj_t robj); +extern __aosl_api__ void *aosl_refobj_arg (aosl_refobj_t robj); /** * Get the ref id of the specified ref object. @@ -151,7 +166,7 @@ extern __ahpl_api__ void *ahpl_refobj_arg (ahpl_refobj_t robj); * Return value: * the ref id. **/ -extern __ahpl_api__ ahpl_ref_t ahpl_refobj_id (ahpl_refobj_t robj); +extern __aosl_api__ aosl_ref_t aosl_refobj_id (aosl_refobj_t robj); /** * Make sure read lock the ref object specified by robj, then invoke the specified callback function. @@ -162,11 +177,26 @@ extern __ahpl_api__ ahpl_ref_t ahpl_refobj_id (ahpl_refobj_t robj); * ...: variable args * Return value: * 0: success - * <0: failure with ahpl_errno set + * <0: failure with aosl_errno set + **/ +extern __aosl_api__ int aosl_refobj_read (aosl_refobj_t robj, aosl_ref_func_t f, uintptr_t argc, ...); +extern __aosl_api__ int aosl_refobj_read_args (aosl_refobj_t robj, aosl_ref_func_t f, uintptr_t argc, va_list args); +extern __aosl_api__ int aosl_refobj_read_argv (aosl_refobj_t robj, aosl_ref_func_t f, uintptr_t argc, uintptr_t argv []); + +/** + * Make sure set the ref object specified by robj unsafe, then invoke the specified callback function. + * Parameter: + * robj: the ref object itself; + * f: the callback function; + * argc: the args count + * ...: variable args + * Return value: + * 0: success + * <0: failure with aosl_errno set **/ -extern __ahpl_api__ int ahpl_refobj_read (ahpl_refobj_t robj, ahpl_ref_func_t f, uintptr_t argc, ...); -extern __ahpl_api__ int ahpl_refobj_read_args (ahpl_refobj_t robj, ahpl_ref_func_t f, uintptr_t argc, va_list args); -extern __ahpl_api__ int ahpl_refobj_read_argv (ahpl_refobj_t robj, ahpl_ref_func_t f, uintptr_t argc, uintptr_t argv []); +extern __aosl_api__ int aosl_refobj_unsafe (aosl_refobj_t robj, aosl_ref_func_t f, uintptr_t argc, ...); +extern __aosl_api__ int aosl_refobj_unsafe_args (aosl_refobj_t robj, aosl_ref_func_t f, uintptr_t argc, va_list args); +extern __aosl_api__ int aosl_refobj_unsafe_argv (aosl_refobj_t robj, aosl_ref_func_t f, uintptr_t argc, uintptr_t argv []); /** * Make sure set the ref object specified by robj maystall, then invoke the specified callback function. @@ -177,11 +207,11 @@ extern __ahpl_api__ int ahpl_refobj_read_argv (ahpl_refobj_t robj, ahpl_ref_func * ...: variable args * Return value: * 0: success - * <0: failure with ahpl_errno set + * <0: failure with aosl_errno set **/ -extern __ahpl_api__ int ahpl_refobj_maystall (ahpl_refobj_t robj, ahpl_ref_func_t f, uintptr_t argc, ...); -extern __ahpl_api__ int ahpl_refobj_maystall_args (ahpl_refobj_t robj, ahpl_ref_func_t f, uintptr_t argc, va_list args); -extern __ahpl_api__ int ahpl_refobj_maystall_argv (ahpl_refobj_t robj, ahpl_ref_func_t f, uintptr_t argc, uintptr_t argv []); +extern __aosl_api__ int aosl_refobj_maystall (aosl_refobj_t robj, aosl_ref_func_t f, uintptr_t argc, ...); +extern __aosl_api__ int aosl_refobj_maystall_args (aosl_refobj_t robj, aosl_ref_func_t f, uintptr_t argc, va_list args); +extern __aosl_api__ int aosl_refobj_maystall_argv (aosl_refobj_t robj, aosl_ref_func_t f, uintptr_t argc, uintptr_t argv []); /** @@ -193,7 +223,7 @@ extern __ahpl_api__ int ahpl_refobj_maystall_argv (ahpl_refobj_t robj, ahpl_ref_ * 0: not read locked * none zero: read locked by calling thread **/ -extern __ahpl_api__ int ahpl_ref_locked (ahpl_ref_t ref); +extern __aosl_api__ int aosl_ref_locked (aosl_ref_t ref); /** * Set the living scope ref object of the specified ref object. @@ -202,10 +232,10 @@ extern __ahpl_api__ int ahpl_ref_locked (ahpl_ref_t ref); * scope_ref: the living scope ref, the ref object will be destroyed * when the object specified by scope_ref was destroyed; * Return value: - * <0: error occured, and ahpl_errno indicates which error; + * <0: error occured, and aosl_errno indicates which error; * >=0: successful; **/ -extern __ahpl_api__ int ahpl_ref_set_scope (ahpl_ref_t ref, ahpl_ref_t scope_ref); +extern __aosl_api__ int aosl_ref_set_scope (aosl_ref_t ref, aosl_ref_t scope_ref); /** * Destroy the reference object specified by ref. @@ -215,9 +245,9 @@ extern __ahpl_api__ int ahpl_ref_set_scope (ahpl_ref_t ref, ahpl_ref_t scope_ref * non-0 value for deleting it * Return value: * 0: success - * <0: failed, and ahpl_errno indicates what error occurs + * <0: failed, and aosl_errno indicates what error occurs **/ -extern __ahpl_api__ int ahpl_ref_destroy (ahpl_ref_t ref, int do_delete); +extern __aosl_api__ int aosl_ref_destroy (aosl_ref_t ref, int do_delete); @@ -227,4 +257,4 @@ extern __ahpl_api__ int ahpl_ref_destroy (ahpl_ref_t ref, int do_delete); -#endif /* __AHPL_REF_H__ */ \ No newline at end of file +#endif /* __AOSL_REF_H__ */ \ No newline at end of file diff --git a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/ahpl_types.h b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/aosl_types.h similarity index 61% rename from Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/ahpl_types.h rename to Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/aosl_types.h index 0617d3326..6afa42c98 100644 --- a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/ahpl_types.h +++ b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/aosl_types.h @@ -1,17 +1,17 @@ /************************************************************* * Author: Lionfore Hao (haolianfu@agora.io) * Date : Jul 27th, 2020 - * Module: AHPL POSIX definitions header file + * Module: AOSL POSIX definitions header file * * - * This is a part of the Advanced High Performance Library. + * This is a part of the Advanced Operating System Layer. * Copyright (C) 2020 Agora IO * All rights reserved. * *************************************************************/ -#ifndef __AHPL_TYPES_H__ -#define __AHPL_TYPES_H__ +#ifndef __AOSL_TYPES_H__ +#define __AOSL_TYPES_H__ #include #include @@ -21,7 +21,7 @@ #include #endif -#include +#include #ifdef __cplusplus extern "C" { @@ -41,21 +41,21 @@ typedef intptr_t ssize_t; #endif -/* The AHPL timestamp type */ -typedef unsigned long long ahpl_ts_t; +/* The AOSL timestamp type */ +typedef unsigned long long aosl_ts_t; -/* The proto for a general ahpl var args function with argc & argv. */ -typedef void (*ahpl_argv_f) (uintptr_t argc, uintptr_t argv []); +/* The proto for a general aosl var args function with argc & argv. */ +typedef void (*aosl_argv_f) (uintptr_t argc, uintptr_t argv []); -/* The proto for a general ahpl object destructor function. */ -typedef ahpl_argv_f ahpl_obj_dtor_t; +/* The proto for a general aosl object destructor function. */ +typedef aosl_argv_f aosl_obj_dtor_t; #if !defined (_WIN32) && !defined (__kspreadtrum__) -typedef int ahpl_fd_t; -#define AHPL_INVALID_FD ((ahpl_fd_t)-1) +typedef int aosl_fd_t; +#define AOSL_INVALID_FD ((aosl_fd_t)-1) -static __inline__ int ahpl_fd_invalid (ahpl_fd_t fd) +static __inline__ int aosl_fd_invalid (aosl_fd_t fd) { return (int)(fd < 0); } @@ -71,19 +71,19 @@ static __inline__ int ahpl_fd_invalid (ahpl_fd_t fd) #include #include -typedef HANDLE ahpl_fd_t; -#define AHPL_INVALID_FD ((ahpl_fd_t)INVALID_HANDLE_VALUE) +typedef HANDLE aosl_fd_t; +#define AOSL_INVALID_FD ((aosl_fd_t)INVALID_HANDLE_VALUE) #elif defined (__kspreadtrum__) #include #include -typedef TCPIP_SOCKET_T ahpl_fd_t; -#define AHPL_INVALID_FD ((ahpl_fd_t)-1) +typedef TCPIP_SOCKET_T aosl_fd_t; +#define AOSL_INVALID_FD ((aosl_fd_t)-1) #endif -static __inline__ int ahpl_fd_invalid (ahpl_fd_t fd) +static __inline__ int aosl_fd_invalid (aosl_fd_t fd) { - return (int)(fd == AHPL_INVALID_FD); + return (int)(fd == AOSL_INVALID_FD); } #endif @@ -93,4 +93,4 @@ static __inline__ int ahpl_fd_invalid (ahpl_fd_t fd) #endif -#endif /* __AHPL_TYPES_H__ */ \ No newline at end of file +#endif /* __AOSL_TYPES_H__ */ \ No newline at end of file diff --git a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/cpp/ahpl_ares_class.h b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/cpp/ahpl_ares_class.h deleted file mode 100644 index 8643c0b78..000000000 --- a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/cpp/ahpl_ares_class.h +++ /dev/null @@ -1,91 +0,0 @@ -/************************************************************* - * Author: Lionfore Hao (haolianfu@agora.io) - * Date : Jun 23rd, 2023 - * Module: AHPL async result object for C++ definition file - * - * - * This is a part of the Advanced High Performance Library. - * Copyright (C) 2018 ~ 2023 Agora IO - * All rights reserved. - * - *************************************************************/ - -#ifndef __AHPL_ARES_CLASS_H__ -#define __AHPL_ARES_CLASS_H__ - - -#include - -#include -#include -#include -#include -#include - - -class ahpl_ares_class: public ahpl_ref_class { -public: - ahpl_ares_class (): ahpl_ref_class (ahpl_ares_create (this)) - { - if (ahpl_ref_invalid (ref ())) - abort (); - } - - /** - * The destructor of this class is very different with - * base class and other derivatives, destroy the ref - * in the destructor and the destructor is public. - **/ - virtual ~ahpl_ares_class () - { - ahpl_ref_t refid = ref (); - if (!ahpl_ref_invalid (refid)) - ahpl_ref_destroy (refid, true); - } - - /* complete the async result */ - int complete (intptr_t result = 0) - { - return ahpl_ares_complete (ref (), result); - } - - /* wait the async result to be completed */ - int wait (intptr_t timeo, intptr_t *result = NULL) - { - return ahpl_ares_wait (ref (), timeo, result); - } - - /* reset the signaled state */ - int reset (void) - { - return ahpl_ares_reset (ref ()); - } - - operator ahpl_ref_t () const - { - return ref (); - } - -private: - /* we do not allow invoke the destroy function of base class */ - int destroy (bool do_delete = true) - { - abort (); - return 0; - } - -#if (__cplusplus >= 201103) || defined (_MSC_VER) -private: - ahpl_ares_class (const ahpl_ares_class &) = delete; - ahpl_ares_class (ahpl_ares_class &&) = delete; - ahpl_ares_class &operator = (const ahpl_ares_class &) = delete; - ahpl_ares_class &operator = (ahpl_ares_class &&) = delete; -#else -private: - ahpl_ares_class (const ahpl_ares_class &); - ahpl_ares_class &operator = (const ahpl_ares_class &); -#endif /* C++11 */ -}; - - -#endif /* __AHPL_ARES_CLASS_H__ */ \ No newline at end of file diff --git a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/cpp/ahpl_ref_class.h b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/cpp/ahpl_ref_class.h deleted file mode 100644 index 85958718e..000000000 --- a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/cpp/ahpl_ref_class.h +++ /dev/null @@ -1,1002 +0,0 @@ -/************************************************************* - * Author: Lionfore Hao (haolianfu@agora.io) - * Date : Nov 19th, 2018 - * Module: AHPL reference object for C++ definition file - * - * - * This is a part of the Advanced High Performance Library. - * Copyright (C) 2018 Agora IO - * All rights reserved. - * - *************************************************************/ - -#ifndef __AHPL_REF_OBJ_CPP_H__ -#define __AHPL_REF_OBJ_CPP_H__ - - -#include - -#include -#include -#include - -#ifdef COMPILING_WITH_MPQ_H -#include -#ifdef COMPILING_WITH_MPQP_H -#include -#endif -#endif - -#ifdef COMPILING_WITH_ASYNC_H -#include -#endif - -#if (__cplusplus >= 201103) || defined (_MSC_VER) -#include -#include -#endif - -class ahpl_ref_class { -private: - ahpl_ref_t ref_id; - -public: - ahpl_ref_class (bool caller_free = true) - { - ref_id = ahpl_ref_create (this, __dtor, (int)caller_free); - if (ahpl_ref_invalid (ref_id)) - abort (); - } - - ahpl_ref_class (ahpl_ref_t ref) - { - ref_id = ref; - } - - ahpl_ref_t ref () const - { - return ref_id; - } - - int hold (ahpl_ref_func_t f, uintptr_t argc, ...) - { - va_list args; - int err; - - va_start (args, argc); - err = ahpl_ref_hold_args (ref (), f, argc, args); - va_end (args); - - return err; - } - - int hold_args (ahpl_ref_func_t f, uintptr_t argc, va_list args) - { - return ahpl_ref_hold_args (ref (), f, argc, args); - } - - int hold_argv (ahpl_ref_func_t f, uintptr_t argc, uintptr_t argv []) - { - return ahpl_ref_hold_argv (ref (), f, argc, argv); - } - - int read (ahpl_ref_func_t f, uintptr_t argc, ...) - { - va_list args; - int err; - - va_start (args, argc); - err = ahpl_ref_read_args (ref (), f, argc, args); - va_end (args); - - return err; - } - - int read_args (ahpl_ref_func_t f, uintptr_t argc, va_list args) - { - return ahpl_ref_read_args (ref (), f, argc, args); - } - - int read_argv (ahpl_ref_func_t f, uintptr_t argc, uintptr_t argv []) - { - return ahpl_ref_read_argv (ref (), f, argc, argv); - } - - int write (ahpl_ref_func_t f, uintptr_t argc, ...) - { - va_list args; - int err; - - va_start (args, argc); - err = ahpl_ref_write_args (ref (), f, argc, args); - va_end (args); - - return err; - } - - int write_args (ahpl_ref_func_t f, uintptr_t argc, va_list args) - { - return ahpl_ref_write_args (ref (), f, argc, args); - } - - int write_argv (ahpl_ref_func_t f, uintptr_t argc, uintptr_t argv []) - { - return ahpl_ref_write_argv (ref (), f, argc, argv); - } - - int maystall (ahpl_ref_func_t f, uintptr_t argc, ...) - { - va_list args; - int err; - - va_start (args, argc); - err = ahpl_ref_maystall_args (ref (), f, argc, args); - va_end (args); - - return err; - } - - int maystall_args (ahpl_ref_func_t f, uintptr_t argc, va_list args) - { - return ahpl_ref_maystall_args (ref (), f, argc, args); - } - - int maystall_argv (ahpl_ref_func_t f, uintptr_t argc, uintptr_t argv []) - { - return ahpl_ref_maystall_argv (ref (), f, argc, argv); - } - - /* The static version of member functions */ - static int hold (ahpl_ref_t ref, ahpl_ref_func_t f, uintptr_t argc, ...) - { - va_list args; - int err; - - va_start (args, argc); - err = ahpl_ref_hold_args (ref, f, argc, args); - va_end (args); - - return err; - } - - static int hold_args (ahpl_ref_t ref, ahpl_ref_func_t f, uintptr_t argc, va_list args) - { - return ahpl_ref_hold_args (ref, f, argc, args); - } - - static int hold_argv (ahpl_ref_t ref, ahpl_ref_func_t f, uintptr_t argc, uintptr_t argv []) - { - return ahpl_ref_hold_argv (ref, f, argc, argv); - } - - static int read (ahpl_ref_t ref, ahpl_ref_func_t f, uintptr_t argc, ...) - { - va_list args; - int err; - - va_start (args, argc); - err = ahpl_ref_read_args (ref, f, argc, args); - va_end (args); - - return err; - } - - static int read_args (ahpl_ref_t ref, ahpl_ref_func_t f, uintptr_t argc, va_list args) - { - return ahpl_ref_read_args (ref, f, argc, args); - } - - static int read_argv (ahpl_ref_t ref, ahpl_ref_func_t f, uintptr_t argc, uintptr_t argv []) - { - return ahpl_ref_read_argv (ref, f, argc, argv); - } - - static int write (ahpl_ref_t ref, ahpl_ref_func_t f, uintptr_t argc, ...) - { - va_list args; - int err; - - va_start (args, argc); - err = ahpl_ref_write_args (ref, f, argc, args); - va_end (args); - - return err; - } - - static int write_args (ahpl_ref_t ref, ahpl_ref_func_t f, uintptr_t argc, va_list args) - { - return ahpl_ref_write_args (ref, f, argc, args); - } - - static int write_argv (ahpl_ref_t ref, ahpl_ref_func_t f, uintptr_t argc, uintptr_t argv []) - { - return ahpl_ref_write_argv (ref, f, argc, argv); - } - - static int maystall (ahpl_ref_t ref, ahpl_ref_func_t f, uintptr_t argc, ...) - { - va_list args; - int err; - - va_start (args, argc); - err = ahpl_ref_maystall_args (ref, f, argc, args); - va_end (args); - - return err; - } - - static int maystall_args (ahpl_ref_t ref, ahpl_ref_func_t f, uintptr_t argc, va_list args) - { - return ahpl_ref_maystall_args (ref, f, argc, args); - } - - static int maystall_argv (ahpl_ref_t ref, ahpl_ref_func_t f, uintptr_t argc, uintptr_t argv []) - { - return ahpl_ref_maystall_argv (ref, f, argc, argv); - } - - static int read (ahpl_refobj_t robj, ahpl_ref_func_t f, uintptr_t argc, ...) - { - va_list args; - int err; - - va_start (args, argc); - err = ahpl_refobj_read_args (robj, f, argc, args); - va_end (args); - - return err; - } - - static int read_args (ahpl_refobj_t robj, ahpl_ref_func_t f, uintptr_t argc, va_list args) - { - return ahpl_refobj_read_args (robj, f, argc, args); - } - - static int read_argv (ahpl_refobj_t robj, ahpl_ref_func_t f, uintptr_t argc, uintptr_t argv []) - { - return ahpl_refobj_read_argv (robj, f, argc, argv); - } - - static int maystall (ahpl_refobj_t robj, ahpl_ref_func_t f, uintptr_t argc, ...) - { - va_list args; - int err; - - va_start (args, argc); - err = ahpl_refobj_maystall_args (robj, f, argc, args); - va_end (args); - - return err; - } - - static int maystall_args (ahpl_refobj_t robj, ahpl_ref_func_t f, uintptr_t argc, va_list args) - { - return ahpl_refobj_maystall_args (robj, f, argc, args); - } - - static int maystall_argv (ahpl_refobj_t robj, ahpl_ref_func_t f, uintptr_t argc, uintptr_t argv []) - { - return ahpl_refobj_maystall_argv (robj, f, argc, argv); - } - - static ahpl_ref_class *from_refobj (ahpl_refobj_t robj) - { - return (ahpl_ref_class *)ahpl_refobj_arg (robj); - } - - /* set the living scope ref object of this ref object */ - int set_scope (ahpl_ref_t scope_ref) - { - return ahpl_ref_set_scope (ref (), scope_ref); - } - - int destroy (bool do_delete = true) - { - if (!ahpl_ref_invalid (ref_id)) - return ahpl_ref_destroy (ref_id, (int)do_delete); - - if (do_delete) - delete this; - - return 0; - } - -public: - class deleter { - public: - void operator () (ahpl_ref_class *obj_ptr) const - { - if (obj_ptr != NULL) - obj_ptr->destroy (); - } - }; - -protected: - /* We do not allow delete this object directly. */ - virtual ~ahpl_ref_class () - { - } - -private: - static void __dtor (void *arg) - { - ahpl_ref_class *__this = (ahpl_ref_class *)arg; - ::delete __this; - } - -#ifdef __AHPL_MPQ_H__ - /* MPQ relative encapsulations */ -public: - int queue (ahpl_mpq_t tq, ahpl_mpq_t dq, const char *f_name, ahpl_mpq_func_argv_t f, uintptr_t argc, ...) - { - va_list args; - int err; - - va_start (args, argc); - err = ahpl_mpq_queue_args (tq, dq, ref (), f_name, f, argc, args); - va_end (args); - - return err; - } - - int queue_args (ahpl_mpq_t tq, ahpl_mpq_t dq, const char *f_name, ahpl_mpq_func_argv_t f, uintptr_t argc, va_list args) - { - return ahpl_mpq_queue_args (tq, dq, ref (), f_name, f, argc, args); - } - - int queue_argv (ahpl_mpq_t tq, ahpl_mpq_t dq, const char *f_name, ahpl_mpq_func_argv_t f, uintptr_t argc, uintptr_t *argv) - { - return ahpl_mpq_queue_argv (tq, dq, ref (), f_name, f, argc, argv); - } - - int queue_data (ahpl_mpq_t tq, ahpl_mpq_t dq, const char *f_name, ahpl_mpq_func_data_t f, size_t len, void *data) - { - return ahpl_mpq_queue_data (tq, dq, ref (), f_name, f, len, data); - } - - int call (ahpl_mpq_t q, const char *f_name, ahpl_mpq_func_argv_t f, uintptr_t argc, ...) - { - va_list args; - int err; - - va_start (args, argc); - err = ahpl_mpq_call_args (q, ref (), f_name, f, argc, args); - va_end (args); - - return err; - } - - int call_args (ahpl_mpq_t q, const char *f_name, ahpl_mpq_func_argv_t f, uintptr_t argc, va_list args) - { - return ahpl_mpq_call_args (q, ref (), f_name, f, argc, args); - } - - int call_argv (ahpl_mpq_t q, const char *f_name, ahpl_mpq_func_argv_t f, uintptr_t argc, uintptr_t *argv) - { - return ahpl_mpq_call_argv (q, ref (), f_name, f, argc, argv); - } - - int call_data (ahpl_mpq_t q, const char *f_name, ahpl_mpq_func_data_t f, size_t len, void *data) - { - return ahpl_mpq_call_data (q, ref (), f_name, f, len, data); - } - - int run (ahpl_mpq_t q, ahpl_mpq_t dq, const char *f_name, ahpl_mpq_func_argv_t f, uintptr_t argc, ...) - { - va_list args; - int err; - - va_start (args, argc); - err = ahpl_mpq_run_args (q, dq, ref (), f_name, f, argc, args); - va_end (args); - - return err; - } - - int run_args (ahpl_mpq_t q, ahpl_mpq_t dq, const char *f_name, ahpl_mpq_func_argv_t f, uintptr_t argc, va_list args) - { - return ahpl_mpq_run_args (q, dq, ref (), f_name, f, argc, args); - } - - int run_argv (ahpl_mpq_t q, ahpl_mpq_t dq, const char *f_name, ahpl_mpq_func_argv_t f, uintptr_t argc, uintptr_t *argv) - { - return ahpl_mpq_run_argv (q, dq, ref (), f_name, f, argc, argv); - } - - int run_data (ahpl_mpq_t q, ahpl_mpq_t dq, const char *f_name, ahpl_mpq_func_data_t f, size_t len, void *data) - { - return ahpl_mpq_run_data (q, dq, ref (), f_name, f, len, data); - } - -#ifdef __AHPL_MPQP_H__ - /* MPQP relative encapsulations */ - ahpl_mpq_t queue (ahpl_mpqp_t qp, ahpl_mpq_t dq, const char *f_name, ahpl_mpq_func_argv_t f, uintptr_t argc, ...) - { - va_list args; - ahpl_mpq_t qid; - - va_start (args, argc); - qid = ahpl_mpqp_queue_args (qp, dq, ref (), f_name, f, argc, args); - va_end (args); - - return qid; - } - - ahpl_mpq_t queue_args (ahpl_mpqp_t qp, ahpl_mpq_t dq, const char *f_name, ahpl_mpq_func_argv_t f, uintptr_t argc, va_list args) - { - return ahpl_mpqp_queue_args (qp, dq, ref (), f_name, f, argc, args); - } - - ahpl_mpq_t queue_argv (ahpl_mpqp_t qp, ahpl_mpq_t dq, const char *f_name, ahpl_mpq_func_argv_t f, uintptr_t argc, uintptr_t *argv) - { - return ahpl_mpqp_queue_argv (qp, dq, ref (), f_name, f, argc, argv); - } - - ahpl_mpq_t queue_data (ahpl_mpqp_t qp, ahpl_mpq_t dq, const char *f_name, ahpl_mpq_func_data_t f, size_t len, void *data) - { - return ahpl_mpqp_queue_data (qp, dq, ref (), f_name, f, len, data); - } - - ahpl_mpq_t call (ahpl_mpqp_t qp, const char *f_name, ahpl_mpq_func_argv_t f, uintptr_t argc, ...) - { - va_list args; - ahpl_mpq_t qid; - - va_start (args, argc); - qid = ahpl_mpqp_call_args (qp, ref (), f_name, f, argc, args); - va_end (args); - - return qid; - } - - ahpl_mpq_t call_args (ahpl_mpqp_t qp, const char *f_name, ahpl_mpq_func_argv_t f, uintptr_t argc, va_list args) - { - return ahpl_mpqp_call_args (qp, ref (), f_name, f, argc, args); - } - - ahpl_mpq_t call_argv (ahpl_mpqp_t qp, const char *f_name, ahpl_mpq_func_argv_t f, uintptr_t argc, uintptr_t *argv) - { - return ahpl_mpqp_call_argv (qp, ref (), f_name, f, argc, argv); - } - - ahpl_mpq_t call_data (ahpl_mpqp_t qp, const char *f_name, ahpl_mpq_func_data_t f, size_t len, void *data) - { - return ahpl_mpqp_call_data (qp, ref (), f_name, f, len, data); - } - - ahpl_mpq_t run (ahpl_mpqp_t qp, ahpl_mpq_t dq, const char *f_name, ahpl_mpq_func_argv_t f, uintptr_t argc, ...) - { - va_list args; - ahpl_mpq_t qid; - - va_start (args, argc); - qid = ahpl_mpqp_run_args (qp, dq, ref (), f_name, f, argc, args); - va_end (args); - - return qid; - } - - ahpl_mpq_t run_args (ahpl_mpqp_t qp, ahpl_mpq_t dq, const char *f_name, ahpl_mpq_func_argv_t f, uintptr_t argc, va_list args) - { - return ahpl_mpqp_run_args (qp, dq, ref (), f_name, f, argc, args); - } - - ahpl_mpq_t run_argv (ahpl_mpqp_t qp, ahpl_mpq_t dq, const char *f_name, ahpl_mpq_func_argv_t f, uintptr_t argc, uintptr_t *argv) - { - return ahpl_mpqp_run_argv (qp, dq, ref (), f_name, f, argc, argv); - } - - ahpl_mpq_t run_data (ahpl_mpqp_t qp, ahpl_mpq_t dq, const char *f_name, ahpl_mpq_func_data_t f, size_t len, void *data) - { - return ahpl_mpqp_run_data (qp, dq, ref (), f_name, f, len, data); - } - - int pool_tail_queue (ahpl_mpqp_t qp, ahpl_mpq_t dq, const char *f_name, ahpl_mpq_func_argv_t f, uintptr_t argc, ...) - { - va_list args; - int err; - - va_start (args, argc); - err = ahpl_mpqp_pool_tail_queue_args (qp, dq, ref (), f_name, f, argc, args); - va_end (args); - - return err; - } - - int pool_tail_queue_args (ahpl_mpqp_t qp, ahpl_mpq_t dq, const char *f_name, ahpl_mpq_func_argv_t f, uintptr_t argc, va_list args) - { - return ahpl_mpqp_pool_tail_queue_args (qp, dq, ref (), f_name, f, argc, args); - } - - int pool_tail_queue_argv (ahpl_mpqp_t qp, ahpl_mpq_t dq, const char *f_name, ahpl_mpq_func_argv_t f, uintptr_t argc, uintptr_t *argv) - { - return ahpl_mpqp_pool_tail_queue_argv (qp, dq, ref (), f_name, f, argc, argv); - } -#endif /* __AHPL_MPQP_H__ */ -#endif /* __AHPL_MPQ_H__ */ - - /* C++11 lambda encapsulations */ -#if (__cplusplus >= 201103) || defined (_MSC_VER) -public: - typedef std::function ahpl_ref_lambda_f; - - int hold (ahpl_ref_lambda_f &&lambda_f) - { - ahpl_ref_lambda_f lambda_obj (std::move (lambda_f)); - return ahpl_ref_class::hold (____ref_f, 1, &lambda_obj); - } - - int read (ahpl_ref_lambda_f &&lambda_f) - { - ahpl_ref_lambda_f lambda_obj (std::move (lambda_f)); - return ahpl_ref_class::read (____ref_f, 1, &lambda_obj); - } - - int write (ahpl_ref_lambda_f &&lambda_f) - { - ahpl_ref_lambda_f lambda_obj (std::move (lambda_f)); - return ahpl_ref_class::write (____ref_f, 1, &lambda_obj); - } - - int maystall (ahpl_ref_lambda_f &&lambda_f) - { - ahpl_ref_lambda_f lambda_obj (std::move (lambda_f)); - return ahpl_ref_class::maystall (____ref_f, 1, &lambda_obj); - } - - static int hold (ahpl_ref_t ref, ahpl_ref_lambda_f &&lambda_f) - { - ahpl_ref_lambda_f lambda_obj (std::move (lambda_f)); - return ahpl_ref_class::hold (ref, ____ref_f, 1, &lambda_obj); - } - - static int read (ahpl_ref_t ref, ahpl_ref_lambda_f &&lambda_f) - { - ahpl_ref_lambda_f lambda_obj (std::move (lambda_f)); - return ahpl_ref_class::read (ref, ____ref_f, 1, &lambda_obj); - } - - static int write (ahpl_ref_t ref, ahpl_ref_lambda_f &&lambda_f) - { - ahpl_ref_lambda_f lambda_obj (std::move (lambda_f)); - return ahpl_ref_class::write (ref, ____ref_f, 1, &lambda_obj); - } - - static int maystall (ahpl_ref_t ref, ahpl_ref_lambda_f &&lambda_f) - { - ahpl_ref_lambda_f lambda_obj (std::move (lambda_f)); - return ahpl_ref_class::maystall (ref, ____ref_f, 1, &lambda_obj); - } - - static int read (ahpl_refobj_t robj, ahpl_ref_lambda_f &&lambda_f) - { - ahpl_ref_lambda_f lambda_obj (std::move (lambda_f)); - return ahpl_ref_class::read (robj, ____ref_f, 1, &lambda_obj); - } - - static int maystall (ahpl_refobj_t robj, ahpl_ref_lambda_f &&lambda_f) - { - ahpl_ref_lambda_f lambda_obj (std::move (lambda_f)); - return ahpl_ref_class::maystall (robj, ____ref_f, 1, &lambda_obj); - } - -private: - static void ____ref_f (void *arg, uintptr_t argc, uintptr_t argv []) - { - ahpl_ref_lambda_f *lambda_obj = reinterpret_cast(argv [0]); - (*lambda_obj) ((ahpl_ref_class *)arg); - } - -#ifdef __AHPL_MPQ_H__ -public: - typedef std::function ahpl_ref_mpq_lambda_f; - - /* MPQ encapsulations */ - int queue (ahpl_mpq_t tq, ahpl_mpq_t dq, const char *f_name, ahpl_ref_mpq_lambda_f&& task) - { - ahpl_ref_mpq_lambda_f *task_obj = new ahpl_ref_mpq_lambda_f (std::move (task)); - int err = ahpl_ref_class::queue (tq, dq, f_name, ____mpq_f, 1, task_obj); - if (err < 0) - delete task_obj; - - return err; - } - - int call (ahpl_mpq_t q, const char *f_name, ahpl_ref_mpq_lambda_f&& task, void *task_result = NULL) - { - ahpl_ref_mpq_lambda_f *task_obj = new ahpl_ref_mpq_lambda_f (std::move (task)); - int err = ahpl_ref_class::call (q, f_name, ____mpq_f, 2, task_obj, task_result); - if (err < 0) - delete task_obj; - - return err; - } - - int run (ahpl_mpq_t q, const char *f_name, ahpl_ref_mpq_lambda_f&& task) - { - ahpl_ref_mpq_lambda_f *task_obj = new ahpl_ref_mpq_lambda_f (std::move (task)); - int err = ahpl_ref_class::run (q, AHPL_MPQ_INVALID, f_name, ____mpq_f, 1, task_obj); - if (err < 0) - delete task_obj; - - return err; - } - -#ifdef __AHPL_MPQP_H__ - /* MPQP encapsulations */ - ahpl_mpq_t queue (ahpl_mpqp_t qp, ahpl_mpq_t dq, const char *f_name, ahpl_ref_mpq_lambda_f&& task) - { - ahpl_ref_mpq_lambda_f *task_obj = new ahpl_ref_mpq_lambda_f (std::move (task)); - ahpl_mpq_t qid = ahpl_ref_class::queue (qp, dq, f_name, ____mpq_f, 1, task_obj); - if (ahpl_mpq_invalid (qid)) - delete task_obj; - - return qid; - } - - ahpl_mpq_t call (ahpl_mpqp_t qp, const char *f_name, ahpl_ref_mpq_lambda_f&& task, void *task_result = NULL) - { - ahpl_ref_mpq_lambda_f *task_obj = new ahpl_ref_mpq_lambda_f (std::move (task)); - ahpl_mpq_t qid = ahpl_ref_class::call (qp, f_name, ____mpq_f, 2, task_obj, task_result); - if (ahpl_mpq_invalid (qid)) - delete task_obj; - - return qid; - } - - ahpl_mpq_t run (ahpl_mpqp_t qp, const char *f_name, ahpl_ref_mpq_lambda_f&& task) - { - ahpl_ref_mpq_lambda_f *task_obj = new ahpl_ref_mpq_lambda_f (std::move (task)); - ahpl_mpq_t qid = ahpl_ref_class::run (qp, AHPL_MPQ_INVALID, f_name, ____mpq_f, 1, task_obj); - if (ahpl_mpq_invalid (qid)) - delete task_obj; - - return qid; - } - - int pool_tail_queue (ahpl_mpqp_t qp, ahpl_mpq_t dq, const char *f_name, ahpl_ref_mpq_lambda_f&& task) - { - ahpl_ref_mpq_lambda_f *task_obj = new ahpl_ref_mpq_lambda_f (std::move (task)); - int err = ahpl_ref_class::pool_tail_queue (qp, dq, f_name, ____mpq_f, 1, task_obj); - if (err < 0) - delete task_obj; - - return err; - } -#endif /* __AHPL_MPQP_H__ */ - - /* MPQ with specified ref encapsulations */ - static int queue (ahpl_mpq_t tq, ahpl_mpq_t dq, ahpl_ref_t ref, const char *f_name, ahpl_ref_mpq_lambda_f&& task) - { - ahpl_ref_mpq_lambda_f *task_obj = new ahpl_ref_mpq_lambda_f (std::move (task)); - int err = ahpl_mpq_queue (tq, dq, ref, f_name, ____mpq_f, 1, task_obj); - if (err < 0) - delete task_obj; - - return err; - } - - static int call (ahpl_mpq_t q, ahpl_ref_t ref, const char *f_name, ahpl_ref_mpq_lambda_f&& task, void *task_result = NULL) - { - ahpl_ref_mpq_lambda_f *task_obj = new ahpl_ref_mpq_lambda_f (std::move (task)); - int err = ahpl_mpq_call (q, ref, f_name, ____mpq_f, 2, task_obj, task_result); - if (err < 0) - delete task_obj; - - return err; - } - - static int run (ahpl_mpq_t q, ahpl_ref_t ref, const char *f_name, ahpl_ref_mpq_lambda_f&& task) - { - ahpl_ref_mpq_lambda_f *task_obj = new ahpl_ref_mpq_lambda_f (std::move (task)); - int err = ahpl_mpq_run (q, AHPL_MPQ_INVALID, ref, f_name, ____mpq_f, 1, task_obj); - if (err < 0) - delete task_obj; - - return err; - } - -#ifdef __AHPL_MPQP_H__ - /* MPQP with specified ref encapsulations */ - static ahpl_mpq_t queue (ahpl_mpqp_t qp, ahpl_mpq_t dq, ahpl_ref_t ref, const char *f_name, ahpl_ref_mpq_lambda_f&& task) - { - ahpl_ref_mpq_lambda_f *task_obj = new ahpl_ref_mpq_lambda_f (std::move (task)); - ahpl_mpq_t qid = ahpl_mpqp_queue (qp, dq, ref, f_name, ____mpq_f, 1, task_obj); - if (ahpl_mpq_invalid (qid)) - delete task_obj; - - return qid; - } - - static ahpl_mpq_t call (ahpl_mpqp_t qp, ahpl_ref_t ref, const char *f_name, ahpl_ref_mpq_lambda_f&& task, void *task_result = NULL) - { - ahpl_ref_mpq_lambda_f *task_obj = new ahpl_ref_mpq_lambda_f (std::move (task)); - ahpl_mpq_t qid = ahpl_mpqp_call (qp, ref, f_name, ____mpq_f, 2, task_obj, task_result); - if (ahpl_mpq_invalid (qid)) - delete task_obj; - - return qid; - } - - static ahpl_mpq_t run (ahpl_mpqp_t qp, ahpl_ref_t ref, const char *f_name, ahpl_ref_mpq_lambda_f&& task) - { - ahpl_ref_mpq_lambda_f *task_obj = new ahpl_ref_mpq_lambda_f (std::move (task)); - ahpl_mpq_t qid = ahpl_mpqp_run (qp, AHPL_MPQ_INVALID, ref, f_name, ____mpq_f, 1, task_obj); - if (ahpl_mpq_invalid (qid)) - delete task_obj; - - return qid; - } - - static int pool_tail_queue (ahpl_mpqp_t qp, ahpl_mpq_t dq, ahpl_ref_t ref, const char *f_name, ahpl_ref_mpq_lambda_f&& task) - { - ahpl_ref_mpq_lambda_f *task_obj = new ahpl_ref_mpq_lambda_f (std::move (task)); - int err = ahpl_mpqp_pool_tail_queue (qp, dq, ref, f_name, ____mpq_f, 1, task_obj); - if (err < 0) - delete task_obj; - - return err; - } -#endif /* __AHPL_MPQP_H__ */ - - static void *call_result_var_addr (void) - { - void *var_addr; - - if (ahpl_mpq_run_func_arg (1, (uintptr_t *)&var_addr) < 0) - return NULL; - - return var_addr; - } - -private: - static void ____mpq_f (const ahpl_ts_t *queued_ts_p, ahpl_refobj_t robj, uintptr_t argc, uintptr_t argv []) - { - ahpl_ref_mpq_lambda_f *task_obj = reinterpret_cast(argv [0]); - ahpl_mpq_t done_qid = ahpl_mpq_run_func_done_qid (); - (*task_obj) (*queued_ts_p, robj); - if (ahpl_mpq_invalid (done_qid) || ahpl_is_free_only (robj)) { - /** - * We only free the task object when the running function has no - * done mpq id, due to the task object would be still in use if - * the function has a done mpq id when queuing back to the done - * mpq. - * -- Lionfore Hao Nov 19th, 2018 - **/ - delete task_obj; - } - } -#endif /* __AHPL_MPQ_H__ */ - -#ifdef __AHPL_ASYNC_H__ - /** - * The stackless coroutine like implementation in AHPL. We could not - * support the real stackless coroutine except in the language level, - * so we just provide similar equivalent functionals here. - **/ -public: - typedef std::function ahpl_async_prepare_lambda_f; - - int prepare (ahpl_stack_id_t stack_id, const char *f_name, ahpl_async_prepare_lambda_f&& task) - { - ahpl_async_prepare_lambda_f *prepare_f = new ahpl_async_prepare_lambda_f (std::move (task)); - int err = ahpl_async_prepare (stack_id, ref (), f_name, ____async_prepare_f, 1, prepare_f); - if (err < 0) - delete prepare_f; - - return err; - } - - static int prepare (ahpl_stack_id_t stack_id, ahpl_ref_t ref, const char *f_name, ahpl_async_prepare_lambda_f&& task) - { - ahpl_async_prepare_lambda_f *prepare_f = new ahpl_async_prepare_lambda_f (std::move (task)); - int err = ahpl_async_prepare (stack_id, ref, f_name, ____async_prepare_f, 1, prepare_f); - if (err < 0) - delete prepare_f; - - return err; - } - -private: - static int ____async_prepare_f (int free_only, uintptr_t argc, uintptr_t argv []) - { - ahpl_async_prepare_lambda_f *prepare_f = reinterpret_cast(argv [0]); - int err; - err = (*prepare_f) (free_only); - delete prepare_f; - return err; - } - -public: - typedef std::function ahpl_async_resume_lambda_f; - - int resume (ahpl_stack_id_t stack_id, const char *f_name, ahpl_async_resume_lambda_f&& task) - { - ahpl_async_resume_lambda_f *resume_f = new ahpl_async_resume_lambda_f (std::move (task)); - int err = ahpl_async_resume (stack_id, ref (), f_name, ____async_resume_f, 1, resume_f); - if (err < 0) - delete resume_f; - - return err; - } - - static int resume (ahpl_stack_id_t stack_id, ahpl_ref_t ref, const char *f_name, ahpl_async_resume_lambda_f&& task) - { - ahpl_async_resume_lambda_f *resume_f = new ahpl_async_resume_lambda_f (std::move (task)); - int err = ahpl_async_resume (stack_id, ref, f_name, ____async_resume_f, 1, resume_f); - if (err < 0) - delete resume_f; - - return err; - } - -private: - static void ____async_resume_f (int free_only, uintptr_t argc, uintptr_t argv []) - { - ahpl_async_resume_lambda_f *resume_f = reinterpret_cast(argv [0]); - (*resume_f) (free_only); - delete resume_f; - } -#endif /* __AHPL_ASYNC_H__ */ -#endif /* C++11 */ - -#if (__cplusplus >= 201103) || defined (_MSC_VER) -private: - ahpl_ref_class (const ahpl_ref_class &) = delete; - ahpl_ref_class (ahpl_ref_class &&) = delete; - ahpl_ref_class &operator = (const ahpl_ref_class &) = delete; - ahpl_ref_class &operator = (ahpl_ref_class &&) = delete; -#else -private: - ahpl_ref_class (const ahpl_ref_class &); - ahpl_ref_class &operator = (const ahpl_ref_class &); -#endif /* C++11 */ -}; - - -/** - * The T_ref_cls argument of this template must be - * ahpl_ref_class or its derivatives. - **/ -template -class ahpl_ref_unique_ptr { -private: - T_ref_cls *_ptr; - -public: - ahpl_ref_unique_ptr (): _ptr (NULL) {} - ahpl_ref_unique_ptr (T_ref_cls *p): _ptr (p) {} - - ahpl_ref_unique_ptr &operator = (T_ref_cls *p) - { - reset (); - _ptr = p; - return *this; - } - - T_ref_cls *operator -> () const - { - return _ptr; - } - - T_ref_cls *get () const - { - return _ptr; - } - - operator bool () const - { - return _ptr != NULL; - } - - T_ref_cls *release () - { - T_ref_cls *p = _ptr; - _ptr = NULL; - return p; - } - - void reset (T_ref_cls *p = NULL) - { - T_ref_cls *old = _ptr; - - /** - * We do the destroy and not delete the object - * before we set the pointer to the new value, - * this is very important to make sure that no - * any async operation is executing. - **/ - if (old != NULL) - old->destroy (false/* not delete */); - - _ptr = p; - - /** - * The destroy with delete operation must be - * the last action, and don't touch any member - * of this object anymore after it. - **/ - if (old != NULL) - old->destroy (true/* do delete */); - } - - ~ahpl_ref_unique_ptr () - { - reset (); - } - -#if (__cplusplus >= 201103) || defined (_MSC_VER) -private: - ahpl_ref_unique_ptr (const ahpl_ref_unique_ptr &) = delete; - ahpl_ref_unique_ptr &operator = (const ahpl_ref_unique_ptr &) = delete; - -public: - ahpl_ref_unique_ptr (ahpl_ref_unique_ptr &&src): _ptr (src.release ()) {} - ahpl_ref_unique_ptr &operator = (ahpl_ref_unique_ptr &&ptr) - { - reset (ptr.release ()); - return *this; - } -#else -private: - ahpl_ref_unique_ptr (const ahpl_ref_unique_ptr &); - ahpl_ref_unique_ptr &operator = (const ahpl_ref_unique_ptr &); -#endif /* C++11 */ -}; - - -template -inline bool operator == (const ahpl_ref_unique_ptr &ptr, intptr_t _null) -{ - return ptr.get () == (T_ref_cls *)_null; -} - -template -inline bool operator != (const ahpl_ref_unique_ptr &ptr, intptr_t _null) -{ - return ptr.get () != (T_ref_cls *)_null; -} - -template -inline bool operator == (intptr_t _null, const ahpl_ref_unique_ptr &ptr) -{ - return (T_ref_cls *)_null == ptr.get (); -} - -template -inline bool operator != (intptr_t _null, const ahpl_ref_unique_ptr &ptr) -{ - return (T_ref_cls *)_null != ptr.get (); -} - -#if (__cplusplus >= 201103) || defined (_MSC_VER) -template -inline bool operator == (const ahpl_ref_unique_ptr &ptr, nullptr_t) -{ - return !ptr; -} - -template -inline bool operator != (const ahpl_ref_unique_ptr &ptr, nullptr_t) -{ - return ptr; -} - -template -inline bool operator == (nullptr_t, const ahpl_ref_unique_ptr &ptr) -{ - return !ptr; -} - -template -inline bool operator != (nullptr_t, const ahpl_ref_unique_ptr &ptr) -{ - return ptr; -} -#endif /* C++11 */ - - -typedef ahpl_ref_unique_ptr ahpl_ref_class_unique_ptr; - - -#endif /* __AHPL_REF_OBJ_CPP_H__ */ \ No newline at end of file diff --git a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/cpp/aosl_ares_class.h b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/cpp/aosl_ares_class.h new file mode 100644 index 000000000..634096300 --- /dev/null +++ b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/cpp/aosl_ares_class.h @@ -0,0 +1,91 @@ +/************************************************************* + * Author: Lionfore Hao (haolianfu@agora.io) + * Date : Jun 23rd, 2023 + * Module: AOSL async result object for C++ definition file + * + * + * This is a part of the Advanced Operating System Layer. + * Copyright (C) 2018 ~ 2023 Agora IO + * All rights reserved. + * + *************************************************************/ + +#ifndef __AOSL_ARES_CLASS_H__ +#define __AOSL_ARES_CLASS_H__ + + +#include + +#include +#include +#include +#include +#include + + +class aosl_ares_class: public aosl_ref_class { +public: + aosl_ares_class (): aosl_ref_class (aosl_ares_create (this)) + { + if (aosl_ref_invalid (ref ())) + abort (); + } + + /** + * The destructor of this class is very different with + * base class and other derivatives, destroy the ref + * in the destructor and the destructor is public. + **/ + virtual ~aosl_ares_class () + { + aosl_ref_t refid = ref (); + if (!aosl_ref_invalid (refid)) + aosl_ref_destroy (refid, true); + } + + /* complete the async result */ + int complete (intptr_t result = 0) + { + return aosl_ares_complete (ref (), result); + } + + /* wait the async result to be completed */ + int wait (intptr_t timeo, intptr_t *result = NULL) + { + return aosl_ares_wait (ref (), timeo, result); + } + + /* reset the signaled state */ + int reset (void) + { + return aosl_ares_reset (ref ()); + } + + operator aosl_ref_t () const + { + return ref (); + } + +private: + /* we do not allow invoke the destroy function of base class */ + int destroy (bool do_delete = true) + { + abort (); + return 0; + } + +#if (__cplusplus >= 201103) || defined (_MSC_VER) +private: + aosl_ares_class (const aosl_ares_class &) = delete; + aosl_ares_class (aosl_ares_class &&) = delete; + aosl_ares_class &operator = (const aosl_ares_class &) = delete; + aosl_ares_class &operator = (aosl_ares_class &&) = delete; +#else +private: + aosl_ares_class (const aosl_ares_class &); + aosl_ares_class &operator = (const aosl_ares_class &); +#endif /* C++11 */ +}; + + +#endif /* __AOSL_ARES_CLASS_H__ */ \ No newline at end of file diff --git a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/cpp/ahpl_poll_class.h b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/cpp/aosl_poll_class.h similarity index 55% rename from Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/cpp/ahpl_poll_class.h rename to Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/cpp/aosl_poll_class.h index 2253b5d34..54d1a0560 100644 --- a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/cpp/ahpl_poll_class.h +++ b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/cpp/aosl_poll_class.h @@ -1,24 +1,24 @@ /************************************************************* * Author: Lionfore Hao (haolianfu@agora.io) * Date : Jun 23rd, 2023 - * Module: AHPL poll functionality for C++ definition file + * Module: AOSL poll functionality for C++ definition file * * - * This is a part of the Advanced High Performance Library. + * This is a part of the Advanced Operating System Layer. * Copyright (C) 2018 ~ 2023 Agora IO * All rights reserved. * *************************************************************/ -#ifndef __AHPL_POLL_CLASS_H__ -#define __AHPL_POLL_CLASS_H__ +#ifndef __AOSL_POLL_CLASS_H__ +#define __AOSL_POLL_CLASS_H__ -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include #if (__cplusplus >= 201103) || defined (_MSC_VER) #include @@ -29,46 +29,52 @@ #include -class ahpl_poll_class { +class aosl_poll_class { private: - std::map poll_refs; - std::vector signaled_refs; + std::map poll_refs; + std::vector signaled_refs; public: - int add (const ahpl_ares_class &tail) + void add (const aosl_ares_class &tail) { poll_refs [tail.ref ()] = &tail; - return 0; } #if (__cplusplus >= 201103) || defined (_MSC_VER) template - int add (const T &head, const Targs&... rest) + void add (const T &head, const Targs&... rest) { poll_refs [head.ref ()] = &head; - return add (rest...); + add (rest...); } /* constructor with variable args */ template - ahpl_poll_class (Targs&... args) + aosl_poll_class (Targs&... args) { add (args...); } #endif /* C++11 */ + aosl_poll_class (const aosl_ares_class * const areses [], size_t count) + { + size_t i; + for (i = 0; i < count; i++) + add (*areses [i]); + } + /* poll the constructed async results */ int poll (size_t min, intptr_t timeo) { - ahpl_ref_t local_refs [32]; - ahpl_ref_t *refs = local_refs; + aosl_ref_t local_refs [32]; + aosl_ref_t *refs = local_refs; size_t count = poll_refs.size (); - std::map::iterator it; + std::map::iterator it; int i; int err; if (count > sizeof local_refs / sizeof local_refs [0]) { - refs = new ahpl_ref_t [count]; + refs = new aosl_ref_t [count]; if (refs == NULL) return -1; } @@ -77,7 +83,7 @@ class ahpl_poll_class { for (it = poll_refs.begin (); it != poll_refs.end (); it++) refs [i++] = it->first; - err = ahpl_poll (refs, count, min, timeo); + err = aosl_poll (refs, count, min, timeo); signaled_refs.clear (); for (i = 0; i < err; i++) { it = poll_refs.find (refs [i]); @@ -104,7 +110,7 @@ class ahpl_poll_class { } /* operator for accessing the signaled async results */ - const ahpl_ares_class *operator [] (size_t idx) + const aosl_ares_class *operator [] (size_t idx) { if (idx < signaled_refs.size ()) return signaled_refs [idx]; @@ -114,16 +120,16 @@ class ahpl_poll_class { #if (__cplusplus >= 201103) || defined (_MSC_VER) private: - ahpl_poll_class (const ahpl_poll_class &) = delete; - ahpl_poll_class (ahpl_poll_class &&) = delete; - ahpl_poll_class &operator = (const ahpl_poll_class &) = delete; - ahpl_poll_class &operator = (ahpl_poll_class &&) = delete; + aosl_poll_class (const aosl_poll_class &) = delete; + aosl_poll_class (aosl_poll_class &&) = delete; + aosl_poll_class &operator = (const aosl_poll_class &) = delete; + aosl_poll_class &operator = (aosl_poll_class &&) = delete; #else private: - ahpl_poll_class (const ahpl_poll_class &); - ahpl_poll_class &operator = (const ahpl_poll_class &); + aosl_poll_class (const aosl_poll_class &); + aosl_poll_class &operator = (const aosl_poll_class &); #endif }; -#endif /* __AHPL_POLL_CLASS_H__ */ \ No newline at end of file +#endif /* __AOSL_POLL_CLASS_H__ */ \ No newline at end of file diff --git a/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/cpp/aosl_ref_class.h b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/cpp/aosl_ref_class.h new file mode 100644 index 000000000..4dab3878b --- /dev/null +++ b/Android/APIExample/agora-simple-filter/src/main/cpp/AgoraRtcKit/api/cpp/aosl_ref_class.h @@ -0,0 +1,1956 @@ +/************************************************************* + * Author: Lionfore Hao (haolianfu@agora.io) + * Date : Nov 19th, 2018 + * Module: AOSL reference object for C++ definition file + * + * + * This is a part of the Advanced Operating System Layer. + * Copyright (C) 2018 Agora IO + * All rights reserved. + * + *************************************************************/ + +#ifndef __AOSL_REF_OBJ_CPP_H__ +#define __AOSL_REF_OBJ_CPP_H__ + + +#include + +#include +#include +#include + +#ifdef COMPILING_WITH_MPQ_H +#include +#ifdef COMPILING_WITH_MPQP_H +#include +#endif +#endif + +#ifdef COMPILING_WITH_ASYNC_H +#include +#endif + +#if (__cplusplus >= 201103) || defined (_MSC_VER) +#include +#include +typedef std::function aosl_ref_lambda_f; +typedef std::function aosl_ref_mpq_lambda_f; +typedef std::function aosl_ref_mpq_lambda_0arg_f; +typedef std::function aosl_async_prepare_lambda_f; +typedef std::function aosl_async_resume_lambda_f; +#endif + +class aosl_ref_class { +public: + class aosl_ref_t_oop { + private: + /** + * We do not allow create any object of this class directly, + * only the pointer of this class is significant. + **/ + aosl_ref_t_oop (); + + public: + static aosl_ref_t_oop *create (void *arg = NULL, aosl_ref_dtor_t dtor = NULL, bool caller_free = true) + { + return (aosl_ref_t_oop *)aosl_ref_create (arg, dtor, (int)caller_free); + } + + static aosl_ref_t_oop *from_aosl_ref_t (aosl_ref_t ref) + { + return (aosl_ref_t_oop *)ref; + } + + static void operator delete (void *ptr) + { + ((aosl_ref_t_oop *)ptr)->destroy (true); + } + + aosl_ref_t ref () const + { + return (aosl_ref_t)this; + } + + int hold (aosl_ref_func_t f, uintptr_t argc, ...) + { + va_list args; + int err; + + va_start (args, argc); + err = aosl_ref_hold_args (ref (), f, argc, args); + va_end (args); + + return err; + } + + int hold_args (aosl_ref_func_t f, uintptr_t argc, va_list args) + { + return aosl_ref_hold_args (ref (), f, argc, args); + } + + int hold_argv (aosl_ref_func_t f, uintptr_t argc, uintptr_t argv []) + { + return aosl_ref_hold_argv (ref (), f, argc, argv); + } + + int read (aosl_ref_func_t f, uintptr_t argc, ...) + { + va_list args; + int err; + + va_start (args, argc); + err = aosl_ref_read_args (ref (), f, argc, args); + va_end (args); + + return err; + } + + int read_args (aosl_ref_func_t f, uintptr_t argc, va_list args) + { + return aosl_ref_read_args (ref (), f, argc, args); + } + + int read_argv (aosl_ref_func_t f, uintptr_t argc, uintptr_t argv []) + { + return aosl_ref_read_argv (ref (), f, argc, argv); + } + + int write (aosl_ref_func_t f, uintptr_t argc, ...) + { + va_list args; + int err; + + va_start (args, argc); + err = aosl_ref_write_args (ref (), f, argc, args); + va_end (args); + + return err; + } + + int write_args (aosl_ref_func_t f, uintptr_t argc, va_list args) + { + return aosl_ref_write_args (ref (), f, argc, args); + } + + int write_argv (aosl_ref_func_t f, uintptr_t argc, uintptr_t argv []) + { + return aosl_ref_write_argv (ref (), f, argc, argv); + } + + int unsafe (aosl_ref_func_t f, uintptr_t argc, ...) + { + va_list args; + int err; + + va_start (args, argc); + err = aosl_ref_unsafe_args (ref (), f, argc, args); + va_end (args); + + return err; + } + + int unsafe_args (aosl_ref_func_t f, uintptr_t argc, va_list args) + { + return aosl_ref_unsafe_args (ref (), f, argc, args); + } + + int unsafe_argv (aosl_ref_func_t f, uintptr_t argc, uintptr_t argv []) + { + return aosl_ref_unsafe_argv (ref (), f, argc, argv); + } + + /* The static version of member functions */ + static int hold (aosl_ref_t ref, aosl_ref_func_t f, uintptr_t argc, ...) + { + va_list args; + int err; + + va_start (args, argc); + err = aosl_ref_hold_args (ref, f, argc, args); + va_end (args); + + return err; + } + + static int hold_args (aosl_ref_t ref, aosl_ref_func_t f, uintptr_t argc, va_list args) + { + return aosl_ref_hold_args (ref, f, argc, args); + } + + static int hold_argv (aosl_ref_t ref, aosl_ref_func_t f, uintptr_t argc, uintptr_t argv []) + { + return aosl_ref_hold_argv (ref, f, argc, argv); + } + + static int read (aosl_ref_t ref, aosl_ref_func_t f, uintptr_t argc, ...) + { + va_list args; + int err; + + va_start (args, argc); + err = aosl_ref_read_args (ref, f, argc, args); + va_end (args); + + return err; + } + + static int read_args (aosl_ref_t ref, aosl_ref_func_t f, uintptr_t argc, va_list args) + { + return aosl_ref_read_args (ref, f, argc, args); + } + + static int read_argv (aosl_ref_t ref, aosl_ref_func_t f, uintptr_t argc, uintptr_t argv []) + { + return aosl_ref_read_argv (ref, f, argc, argv); + } + + static int write (aosl_ref_t ref, aosl_ref_func_t f, uintptr_t argc, ...) + { + va_list args; + int err; + + va_start (args, argc); + err = aosl_ref_write_args (ref, f, argc, args); + va_end (args); + + return err; + } + + static int write_args (aosl_ref_t ref, aosl_ref_func_t f, uintptr_t argc, va_list args) + { + return aosl_ref_write_args (ref, f, argc, args); + } + + static int write_argv (aosl_ref_t ref, aosl_ref_func_t f, uintptr_t argc, uintptr_t argv []) + { + return aosl_ref_write_argv (ref, f, argc, argv); + } + + static int unsafe (aosl_ref_t ref, aosl_ref_func_t f, uintptr_t argc, ...) + { + va_list args; + int err; + + va_start (args, argc); + err = aosl_ref_unsafe_args (ref, f, argc, args); + va_end (args); + + return err; + } + + static int unsafe_args (aosl_ref_t ref, aosl_ref_func_t f, uintptr_t argc, va_list args) + { + return aosl_ref_unsafe_args (ref, f, argc, args); + } + + static int unsafe_argv (aosl_ref_t ref, aosl_ref_func_t f, uintptr_t argc, uintptr_t argv []) + { + return aosl_ref_unsafe_argv (ref, f, argc, argv); + } + + static int read (aosl_refobj_t robj, aosl_ref_func_t f, uintptr_t argc, ...) + { + va_list args; + int err; + + va_start (args, argc); + err = aosl_refobj_read_args (robj, f, argc, args); + va_end (args); + + return err; + } + + static int read_args (aosl_refobj_t robj, aosl_ref_func_t f, uintptr_t argc, va_list args) + { + return aosl_refobj_read_args (robj, f, argc, args); + } + + static int read_argv (aosl_refobj_t robj, aosl_ref_func_t f, uintptr_t argc, uintptr_t argv []) + { + return aosl_refobj_read_argv (robj, f, argc, argv); + } + + static int unsafe (aosl_refobj_t robj, aosl_ref_func_t f, uintptr_t argc, ...) + { + va_list args; + int err; + + va_start (args, argc); + err = aosl_refobj_unsafe_args (robj, f, argc, args); + va_end (args); + + return err; + } + + static int unsafe_args (aosl_refobj_t robj, aosl_ref_func_t f, uintptr_t argc, va_list args) + { + return aosl_refobj_unsafe_args (robj, f, argc, args); + } + + static int unsafe_argv (aosl_refobj_t robj, aosl_ref_func_t f, uintptr_t argc, uintptr_t argv []) + { + return aosl_refobj_unsafe_argv (robj, f, argc, argv); + } + + /* set the living scope ref object of this ref object */ + int set_scope (aosl_ref_t scope_ref) + { + return aosl_ref_set_scope (ref (), scope_ref); + } + + int destroy (bool do_delete = true) + { + if (!aosl_ref_invalid (ref ())) + return aosl_ref_destroy (ref (), (int)do_delete); + + return -1; + } + + #ifdef __AOSL_MPQ_H__ + /* MPQ relative encapsulations */ + public: + int queue (aosl_mpq_t tq, aosl_mpq_t dq, const char *f_name, aosl_mpq_func_argv_t f, uintptr_t argc, ...) + { + va_list args; + int err; + + va_start (args, argc); + err = aosl_mpq_queue_args (tq, dq, ref (), f_name, f, argc, args); + va_end (args); + + return err; + } + + int queue_args (aosl_mpq_t tq, aosl_mpq_t dq, const char *f_name, aosl_mpq_func_argv_t f, uintptr_t argc, va_list args) + { + return aosl_mpq_queue_args (tq, dq, ref (), f_name, f, argc, args); + } + + int queue_argv (aosl_mpq_t tq, aosl_mpq_t dq, const char *f_name, aosl_mpq_func_argv_t f, uintptr_t argc, uintptr_t *argv) + { + return aosl_mpq_queue_argv (tq, dq, ref (), f_name, f, argc, argv); + } + + int queue_data (aosl_mpq_t tq, aosl_mpq_t dq, const char *f_name, aosl_mpq_func_data_t f, size_t len, void *data) + { + return aosl_mpq_queue_data (tq, dq, ref (), f_name, f, len, data); + } + + int call (aosl_mpq_t q, const char *f_name, aosl_mpq_func_argv_t f, uintptr_t argc, ...) + { + va_list args; + int err; + + va_start (args, argc); + err = aosl_mpq_call_args (q, ref (), f_name, f, argc, args); + va_end (args); + + return err; + } + + int call_args (aosl_mpq_t q, const char *f_name, aosl_mpq_func_argv_t f, uintptr_t argc, va_list args) + { + return aosl_mpq_call_args (q, ref (), f_name, f, argc, args); + } + + int call_argv (aosl_mpq_t q, const char *f_name, aosl_mpq_func_argv_t f, uintptr_t argc, uintptr_t *argv) + { + return aosl_mpq_call_argv (q, ref (), f_name, f, argc, argv); + } + + int call_data (aosl_mpq_t q, const char *f_name, aosl_mpq_func_data_t f, size_t len, void *data) + { + return aosl_mpq_call_data (q, ref (), f_name, f, len, data); + } + + int run (aosl_mpq_t q, aosl_mpq_t dq, const char *f_name, aosl_mpq_func_argv_t f, uintptr_t argc, ...) + { + va_list args; + int err; + + va_start (args, argc); + err = aosl_mpq_run_args (q, dq, ref (), f_name, f, argc, args); + va_end (args); + + return err; + } + + int run_args (aosl_mpq_t q, aosl_mpq_t dq, const char *f_name, aosl_mpq_func_argv_t f, uintptr_t argc, va_list args) + { + return aosl_mpq_run_args (q, dq, ref (), f_name, f, argc, args); + } + + int run_argv (aosl_mpq_t q, aosl_mpq_t dq, const char *f_name, aosl_mpq_func_argv_t f, uintptr_t argc, uintptr_t *argv) + { + return aosl_mpq_run_argv (q, dq, ref (), f_name, f, argc, argv); + } + + int run_data (aosl_mpq_t q, aosl_mpq_t dq, const char *f_name, aosl_mpq_func_data_t f, size_t len, void *data) + { + return aosl_mpq_run_data (q, dq, ref (), f_name, f, len, data); + } + + #ifdef __AOSL_MPQP_H__ + /* MPQP relative encapsulations */ + aosl_mpq_t queue (aosl_mpqp_t qp, aosl_mpq_t dq, const char *f_name, aosl_mpq_func_argv_t f, uintptr_t argc, ...) + { + va_list args; + aosl_mpq_t qid; + + va_start (args, argc); + qid = aosl_mpqp_queue_args (qp, dq, ref (), f_name, f, argc, args); + va_end (args); + + return qid; + } + + aosl_mpq_t queue_args (aosl_mpqp_t qp, aosl_mpq_t dq, const char *f_name, aosl_mpq_func_argv_t f, uintptr_t argc, va_list args) + { + return aosl_mpqp_queue_args (qp, dq, ref (), f_name, f, argc, args); + } + + aosl_mpq_t queue_argv (aosl_mpqp_t qp, aosl_mpq_t dq, const char *f_name, aosl_mpq_func_argv_t f, uintptr_t argc, uintptr_t *argv) + { + return aosl_mpqp_queue_argv (qp, dq, ref (), f_name, f, argc, argv); + } + + aosl_mpq_t queue_data (aosl_mpqp_t qp, aosl_mpq_t dq, const char *f_name, aosl_mpq_func_data_t f, size_t len, void *data) + { + return aosl_mpqp_queue_data (qp, dq, ref (), f_name, f, len, data); + } + + aosl_mpq_t call (aosl_mpqp_t qp, const char *f_name, aosl_mpq_func_argv_t f, uintptr_t argc, ...) + { + va_list args; + aosl_mpq_t qid; + + va_start (args, argc); + qid = aosl_mpqp_call_args (qp, ref (), f_name, f, argc, args); + va_end (args); + + return qid; + } + + aosl_mpq_t call_args (aosl_mpqp_t qp, const char *f_name, aosl_mpq_func_argv_t f, uintptr_t argc, va_list args) + { + return aosl_mpqp_call_args (qp, ref (), f_name, f, argc, args); + } + + aosl_mpq_t call_argv (aosl_mpqp_t qp, const char *f_name, aosl_mpq_func_argv_t f, uintptr_t argc, uintptr_t *argv) + { + return aosl_mpqp_call_argv (qp, ref (), f_name, f, argc, argv); + } + + aosl_mpq_t call_data (aosl_mpqp_t qp, const char *f_name, aosl_mpq_func_data_t f, size_t len, void *data) + { + return aosl_mpqp_call_data (qp, ref (), f_name, f, len, data); + } + + aosl_mpq_t run (aosl_mpqp_t qp, aosl_mpq_t dq, const char *f_name, aosl_mpq_func_argv_t f, uintptr_t argc, ...) + { + va_list args; + aosl_mpq_t qid; + + va_start (args, argc); + qid = aosl_mpqp_run_args (qp, dq, ref (), f_name, f, argc, args); + va_end (args); + + return qid; + } + + aosl_mpq_t run_args (aosl_mpqp_t qp, aosl_mpq_t dq, const char *f_name, aosl_mpq_func_argv_t f, uintptr_t argc, va_list args) + { + return aosl_mpqp_run_args (qp, dq, ref (), f_name, f, argc, args); + } + + aosl_mpq_t run_argv (aosl_mpqp_t qp, aosl_mpq_t dq, const char *f_name, aosl_mpq_func_argv_t f, uintptr_t argc, uintptr_t *argv) + { + return aosl_mpqp_run_argv (qp, dq, ref (), f_name, f, argc, argv); + } + + aosl_mpq_t run_data (aosl_mpqp_t qp, aosl_mpq_t dq, const char *f_name, aosl_mpq_func_data_t f, size_t len, void *data) + { + return aosl_mpqp_run_data (qp, dq, ref (), f_name, f, len, data); + } + + int pool_tail_queue (aosl_mpqp_t qp, aosl_mpq_t dq, const char *f_name, aosl_mpq_func_argv_t f, uintptr_t argc, ...) + { + va_list args; + int err; + + va_start (args, argc); + err = aosl_mpqp_pool_tail_queue_args (qp, dq, ref (), f_name, f, argc, args); + va_end (args); + + return err; + } + + int pool_tail_queue_args (aosl_mpqp_t qp, aosl_mpq_t dq, const char *f_name, aosl_mpq_func_argv_t f, uintptr_t argc, va_list args) + { + return aosl_mpqp_pool_tail_queue_args (qp, dq, ref (), f_name, f, argc, args); + } + + int pool_tail_queue_argv (aosl_mpqp_t qp, aosl_mpq_t dq, const char *f_name, aosl_mpq_func_argv_t f, uintptr_t argc, uintptr_t *argv) + { + return aosl_mpqp_pool_tail_queue_argv (qp, dq, ref (), f_name, f, argc, argv); + } + #endif /* __AOSL_MPQP_H__ */ + #endif /* __AOSL_MPQ_H__ */ + + /* C++11 lambda encapsulations */ + #if (__cplusplus >= 201103) || defined (_MSC_VER) + public: + int hold (aosl_ref_lambda_f &&lambda_f) + { + aosl_ref_lambda_f lambda_obj (std::move (lambda_f)); + return aosl_ref_t_oop::hold (____ref_f, 1, &lambda_obj); + } + + int read (aosl_ref_lambda_f &&lambda_f) + { + aosl_ref_lambda_f lambda_obj (std::move (lambda_f)); + return aosl_ref_t_oop::read (____ref_f, 1, &lambda_obj); + } + + int write (aosl_ref_lambda_f &&lambda_f) + { + aosl_ref_lambda_f lambda_obj (std::move (lambda_f)); + return aosl_ref_t_oop::write (____ref_f, 1, &lambda_obj); + } + + int unsafe (aosl_ref_lambda_f &&lambda_f) + { + aosl_ref_lambda_f lambda_obj (std::move (lambda_f)); + return aosl_ref_t_oop::unsafe (____ref_f, 1, &lambda_obj); + } + + static int hold (aosl_ref_t ref, aosl_ref_lambda_f &&lambda_f) + { + aosl_ref_lambda_f lambda_obj (std::move (lambda_f)); + return aosl_ref_t_oop::hold (ref, ____ref_f, 1, &lambda_obj); + } + + static int read (aosl_ref_t ref, aosl_ref_lambda_f &&lambda_f) + { + aosl_ref_lambda_f lambda_obj (std::move (lambda_f)); + return aosl_ref_t_oop::read (ref, ____ref_f, 1, &lambda_obj); + } + + static int write (aosl_ref_t ref, aosl_ref_lambda_f &&lambda_f) + { + aosl_ref_lambda_f lambda_obj (std::move (lambda_f)); + return aosl_ref_t_oop::write (ref, ____ref_f, 1, &lambda_obj); + } + + static int unsafe (aosl_ref_t ref, aosl_ref_lambda_f &&lambda_f) + { + aosl_ref_lambda_f lambda_obj (std::move (lambda_f)); + return aosl_ref_t_oop::unsafe (ref, ____ref_f, 1, &lambda_obj); + } + + static int read (aosl_refobj_t robj, aosl_ref_lambda_f &&lambda_f) + { + aosl_ref_lambda_f lambda_obj (std::move (lambda_f)); + return aosl_ref_t_oop::read (robj, ____ref_f, 1, &lambda_obj); + } + + static int unsafe (aosl_refobj_t robj, aosl_ref_lambda_f &&lambda_f) + { + aosl_ref_lambda_f lambda_obj (std::move (lambda_f)); + return aosl_ref_t_oop::unsafe (robj, ____ref_f, 1, &lambda_obj); + } + + private: + static void ____ref_f (void *arg, uintptr_t argc, uintptr_t argv []) + { + aosl_ref_lambda_f *lambda_obj = reinterpret_cast(argv [0]); + (*lambda_obj) (arg); + } + + #ifdef __AOSL_MPQ_H__ + public: + /* MPQ encapsulations */ + int queue (aosl_mpq_t tq, aosl_mpq_t dq, const char *f_name, aosl_ref_mpq_lambda_f&& task) + { + aosl_ref_mpq_lambda_f *task_obj = new aosl_ref_mpq_lambda_f (std::move (task)); + int err = aosl_ref_t_oop::queue (tq, dq, f_name, ____mpq_f, 1, task_obj); + if (err < 0) + delete task_obj; + + return err; + } + + int call (aosl_mpq_t q, const char *f_name, aosl_ref_mpq_lambda_f&& task, void *task_result = NULL) + { + aosl_ref_mpq_lambda_f *task_obj = new aosl_ref_mpq_lambda_f (std::move (task)); + int err = aosl_ref_t_oop::call (q, f_name, ____mpq_f, 2, task_obj, task_result); + if (err < 0) + delete task_obj; + + return err; + } + + int run (aosl_mpq_t q, const char *f_name, aosl_ref_mpq_lambda_f&& task) + { + aosl_ref_mpq_lambda_f *task_obj = new aosl_ref_mpq_lambda_f (std::move (task)); + int err = aosl_ref_t_oop::run (q, AOSL_MPQ_INVALID, f_name, ____mpq_f, 1, task_obj); + if (err < 0) + delete task_obj; + + return err; + } + + int queue (aosl_mpq_t tq, const char *f_name, aosl_ref_mpq_lambda_0arg_f&& task) + { + aosl_ref_mpq_lambda_0arg_f *task_obj = new aosl_ref_mpq_lambda_0arg_f (std::move (task)); + int err = aosl_ref_t_oop::queue (tq, AOSL_MPQ_INVALID, f_name, ____mpq_0arg_f, 1, task_obj); + if (err < 0) + delete task_obj; + + return err; + } + + int call (aosl_mpq_t q, const char *f_name, aosl_ref_mpq_lambda_0arg_f&& task, void *task_result = NULL) + { + aosl_ref_mpq_lambda_0arg_f *task_obj = new aosl_ref_mpq_lambda_0arg_f (std::move (task)); + int err = aosl_ref_t_oop::call (q, f_name, ____mpq_0arg_f, 2, task_obj, task_result); + if (err < 0) + delete task_obj; + + return err; + } + + int run (aosl_mpq_t q, const char *f_name, aosl_ref_mpq_lambda_0arg_f&& task) + { + aosl_ref_mpq_lambda_0arg_f *task_obj = new aosl_ref_mpq_lambda_0arg_f (std::move (task)); + int err = aosl_ref_t_oop::run (q, AOSL_MPQ_INVALID, f_name, ____mpq_0arg_f, 1, task_obj); + if (err < 0) + delete task_obj; + + return err; + } + + #ifdef __AOSL_MPQP_H__ + /* MPQP encapsulations */ + aosl_mpq_t queue (aosl_mpqp_t qp, aosl_mpq_t dq, const char *f_name, aosl_ref_mpq_lambda_f&& task) + { + aosl_ref_mpq_lambda_f *task_obj = new aosl_ref_mpq_lambda_f (std::move (task)); + aosl_mpq_t qid = aosl_ref_t_oop::queue (qp, dq, f_name, ____mpq_f, 1, task_obj); + if (aosl_mpq_invalid (qid)) + delete task_obj; + + return qid; + } + + aosl_mpq_t call (aosl_mpqp_t qp, const char *f_name, aosl_ref_mpq_lambda_f&& task, void *task_result = NULL) + { + aosl_ref_mpq_lambda_f *task_obj = new aosl_ref_mpq_lambda_f (std::move (task)); + aosl_mpq_t qid = aosl_ref_t_oop::call (qp, f_name, ____mpq_f, 2, task_obj, task_result); + if (aosl_mpq_invalid (qid)) + delete task_obj; + + return qid; + } + + aosl_mpq_t run (aosl_mpqp_t qp, const char *f_name, aosl_ref_mpq_lambda_f&& task) + { + aosl_ref_mpq_lambda_f *task_obj = new aosl_ref_mpq_lambda_f (std::move (task)); + aosl_mpq_t qid = aosl_ref_t_oop::run (qp, AOSL_MPQ_INVALID, f_name, ____mpq_f, 1, task_obj); + if (aosl_mpq_invalid (qid)) + delete task_obj; + + return qid; + } + + int pool_tail_queue (aosl_mpqp_t qp, aosl_mpq_t dq, const char *f_name, aosl_ref_mpq_lambda_f&& task) + { + aosl_ref_mpq_lambda_f *task_obj = new aosl_ref_mpq_lambda_f (std::move (task)); + int err = aosl_ref_t_oop::pool_tail_queue (qp, dq, f_name, ____mpq_f, 1, task_obj); + if (err < 0) + delete task_obj; + + return err; + } + + aosl_mpq_t queue (aosl_mpqp_t qp, const char *f_name, aosl_ref_mpq_lambda_0arg_f&& task) + { + aosl_ref_mpq_lambda_0arg_f *task_obj = new aosl_ref_mpq_lambda_0arg_f (std::move (task)); + aosl_mpq_t qid = aosl_ref_t_oop::queue (qp, AOSL_MPQ_INVALID, f_name, ____mpq_0arg_f, 1, task_obj); + if (aosl_mpq_invalid (qid)) + delete task_obj; + + return qid; + } + + aosl_mpq_t call (aosl_mpqp_t qp, const char *f_name, aosl_ref_mpq_lambda_0arg_f&& task, void *task_result = NULL) + { + aosl_ref_mpq_lambda_0arg_f *task_obj = new aosl_ref_mpq_lambda_0arg_f (std::move (task)); + aosl_mpq_t qid = aosl_ref_t_oop::call (qp, f_name, ____mpq_0arg_f, 2, task_obj, task_result); + if (aosl_mpq_invalid (qid)) + delete task_obj; + + return qid; + } + + aosl_mpq_t run (aosl_mpqp_t qp, const char *f_name, aosl_ref_mpq_lambda_0arg_f&& task) + { + aosl_ref_mpq_lambda_0arg_f *task_obj = new aosl_ref_mpq_lambda_0arg_f (std::move (task)); + aosl_mpq_t qid = aosl_ref_t_oop::run (qp, AOSL_MPQ_INVALID, f_name, ____mpq_0arg_f, 1, task_obj); + if (aosl_mpq_invalid (qid)) + delete task_obj; + + return qid; + } + + int pool_tail_queue (aosl_mpqp_t qp, const char *f_name, aosl_ref_mpq_lambda_0arg_f&& task) + { + aosl_ref_mpq_lambda_0arg_f *task_obj = new aosl_ref_mpq_lambda_0arg_f (std::move (task)); + int err = aosl_ref_t_oop::pool_tail_queue (qp, AOSL_MPQ_INVALID, f_name, ____mpq_0arg_f, 1, task_obj); + if (err < 0) + delete task_obj; + + return err; + } + #endif /* __AOSL_MPQP_H__ */ + + /* MPQ with specified ref encapsulations */ + static int queue (aosl_mpq_t tq, aosl_mpq_t dq, aosl_ref_t ref, const char *f_name, aosl_ref_mpq_lambda_f&& task) + { + aosl_ref_mpq_lambda_f *task_obj = new aosl_ref_mpq_lambda_f (std::move (task)); + int err = aosl_mpq_queue (tq, dq, ref, f_name, ____mpq_f, 1, task_obj); + if (err < 0) + delete task_obj; + + return err; + } + + static int call (aosl_mpq_t q, aosl_ref_t ref, const char *f_name, aosl_ref_mpq_lambda_f&& task, void *task_result = NULL) + { + aosl_ref_mpq_lambda_f *task_obj = new aosl_ref_mpq_lambda_f (std::move (task)); + int err = aosl_mpq_call (q, ref, f_name, ____mpq_f, 2, task_obj, task_result); + if (err < 0) + delete task_obj; + + return err; + } + + static int run (aosl_mpq_t q, aosl_ref_t ref, const char *f_name, aosl_ref_mpq_lambda_f&& task) + { + aosl_ref_mpq_lambda_f *task_obj = new aosl_ref_mpq_lambda_f (std::move (task)); + int err = aosl_mpq_run (q, AOSL_MPQ_INVALID, ref, f_name, ____mpq_f, 1, task_obj); + if (err < 0) + delete task_obj; + + return err; + } + + static int queue (aosl_mpq_t tq, aosl_ref_t ref, const char *f_name, aosl_ref_mpq_lambda_0arg_f&& task) + { + aosl_ref_mpq_lambda_0arg_f *task_obj = new aosl_ref_mpq_lambda_0arg_f (std::move (task)); + int err = aosl_mpq_queue (tq, AOSL_MPQ_INVALID, ref, f_name, ____mpq_0arg_f, 1, task_obj); + if (err < 0) + delete task_obj; + + return err; + } + + static int call (aosl_mpq_t q, aosl_ref_t ref, const char *f_name, aosl_ref_mpq_lambda_0arg_f&& task, void *task_result = NULL) + { + aosl_ref_mpq_lambda_0arg_f *task_obj = new aosl_ref_mpq_lambda_0arg_f (std::move (task)); + int err = aosl_mpq_call (q, ref, f_name, ____mpq_0arg_f, 2, task_obj, task_result); + if (err < 0) + delete task_obj; + + return err; + } + + static int run (aosl_mpq_t q, aosl_ref_t ref, const char *f_name, aosl_ref_mpq_lambda_0arg_f&& task) + { + aosl_ref_mpq_lambda_0arg_f *task_obj = new aosl_ref_mpq_lambda_0arg_f (std::move (task)); + int err = aosl_mpq_run (q, AOSL_MPQ_INVALID, ref, f_name, ____mpq_0arg_f, 1, task_obj); + if (err < 0) + delete task_obj; + + return err; + } + + #ifdef __AOSL_MPQP_H__ + /* MPQP with specified ref encapsulations */ + static aosl_mpq_t queue (aosl_mpqp_t qp, aosl_mpq_t dq, aosl_ref_t ref, const char *f_name, aosl_ref_mpq_lambda_f&& task) + { + aosl_ref_mpq_lambda_f *task_obj = new aosl_ref_mpq_lambda_f (std::move (task)); + aosl_mpq_t qid = aosl_mpqp_queue (qp, dq, ref, f_name, ____mpq_f, 1, task_obj); + if (aosl_mpq_invalid (qid)) + delete task_obj; + + return qid; + } + + static aosl_mpq_t call (aosl_mpqp_t qp, aosl_ref_t ref, const char *f_name, aosl_ref_mpq_lambda_f&& task, void *task_result = NULL) + { + aosl_ref_mpq_lambda_f *task_obj = new aosl_ref_mpq_lambda_f (std::move (task)); + aosl_mpq_t qid = aosl_mpqp_call (qp, ref, f_name, ____mpq_f, 2, task_obj, task_result); + if (aosl_mpq_invalid (qid)) + delete task_obj; + + return qid; + } + + static aosl_mpq_t run (aosl_mpqp_t qp, aosl_ref_t ref, const char *f_name, aosl_ref_mpq_lambda_f&& task) + { + aosl_ref_mpq_lambda_f *task_obj = new aosl_ref_mpq_lambda_f (std::move (task)); + aosl_mpq_t qid = aosl_mpqp_run (qp, AOSL_MPQ_INVALID, ref, f_name, ____mpq_f, 1, task_obj); + if (aosl_mpq_invalid (qid)) + delete task_obj; + + return qid; + } + + static int pool_tail_queue (aosl_mpqp_t qp, aosl_mpq_t dq, aosl_ref_t ref, const char *f_name, aosl_ref_mpq_lambda_f&& task) + { + aosl_ref_mpq_lambda_f *task_obj = new aosl_ref_mpq_lambda_f (std::move (task)); + int err = aosl_mpqp_pool_tail_queue (qp, dq, ref, f_name, ____mpq_f, 1, task_obj); + if (err < 0) + delete task_obj; + + return err; + } + + static aosl_mpq_t queue (aosl_mpqp_t qp, aosl_ref_t ref, const char *f_name, aosl_ref_mpq_lambda_0arg_f&& task) + { + aosl_ref_mpq_lambda_0arg_f *task_obj = new aosl_ref_mpq_lambda_0arg_f (std::move (task)); + aosl_mpq_t qid = aosl_mpqp_queue (qp, AOSL_MPQ_INVALID, ref, f_name, ____mpq_0arg_f, 1, task_obj); + if (aosl_mpq_invalid (qid)) + delete task_obj; + + return qid; + } + + static aosl_mpq_t call (aosl_mpqp_t qp, aosl_ref_t ref, const char *f_name, aosl_ref_mpq_lambda_0arg_f&& task, void *task_result = NULL) + { + aosl_ref_mpq_lambda_0arg_f *task_obj = new aosl_ref_mpq_lambda_0arg_f (std::move (task)); + aosl_mpq_t qid = aosl_mpqp_call (qp, ref, f_name, ____mpq_0arg_f, 2, task_obj, task_result); + if (aosl_mpq_invalid (qid)) + delete task_obj; + + return qid; + } + + static aosl_mpq_t run (aosl_mpqp_t qp, aosl_ref_t ref, const char *f_name, aosl_ref_mpq_lambda_0arg_f&& task) + { + aosl_ref_mpq_lambda_0arg_f *task_obj = new aosl_ref_mpq_lambda_0arg_f (std::move (task)); + aosl_mpq_t qid = aosl_mpqp_run (qp, AOSL_MPQ_INVALID, ref, f_name, ____mpq_0arg_f, 1, task_obj); + if (aosl_mpq_invalid (qid)) + delete task_obj; + + return qid; + } + + static int pool_tail_queue (aosl_mpqp_t qp, aosl_ref_t ref, const char *f_name, aosl_ref_mpq_lambda_0arg_f&& task) + { + aosl_ref_mpq_lambda_0arg_f *task_obj = new aosl_ref_mpq_lambda_0arg_f (std::move (task)); + int err = aosl_mpqp_pool_tail_queue (qp, AOSL_MPQ_INVALID, ref, f_name, ____mpq_0arg_f, 1, task_obj); + if (err < 0) + delete task_obj; + + return err; + } + #endif /* __AOSL_MPQP_H__ */ + + static void *call_result_var_addr (void) + { + void *var_addr; + + if (aosl_mpq_run_func_arg (1, (uintptr_t *)&var_addr) < 0) + return NULL; + + return var_addr; + } + + private: + static void ____mpq_f (const aosl_ts_t *queued_ts_p, aosl_refobj_t robj, uintptr_t argc, uintptr_t argv []) + { + aosl_ref_mpq_lambda_f *task_obj = reinterpret_cast(argv [0]); + aosl_mpq_t done_qid = aosl_mpq_run_func_done_qid (); + (*task_obj) (*queued_ts_p, robj); + if (aosl_mpq_invalid (done_qid) || aosl_is_free_only (robj)) { + /** + * We only free the task object when the running function has no + * done mpq id, due to the task object would be still in use if + * the function has a done mpq id when queuing back to the done + * mpq. + * -- Lionfore Hao Nov 19th, 2018 + **/ + delete task_obj; + } + } + + static void ____mpq_0arg_f (const aosl_ts_t *queued_ts_p, aosl_refobj_t robj, uintptr_t argc, uintptr_t argv []) + { + aosl_ref_mpq_lambda_0arg_f *task_obj = reinterpret_cast(argv [0]); + aosl_mpq_t done_qid = aosl_mpq_run_func_done_qid (); + if (!aosl_is_free_only (robj)) + (*task_obj) (); + if (aosl_mpq_invalid (done_qid) || aosl_is_free_only (robj)) { + /** + * We only free the task object when the running function has no + * done mpq id, due to the task object would be still in use if + * the function has a done mpq id when queuing back to the done + * mpq. + * -- Lionfore Hao Nov 19th, 2018 + **/ + delete task_obj; + } + } + #endif /* __AOSL_MPQ_H__ */ + + #ifdef __AOSL_ASYNC_H__ + /** + * The stackless coroutine like implementation in AOSL. We could not + * support the real stackless coroutine except in the language level, + * so we just provide similar equivalent functionals here. + **/ + public: + int prepare (aosl_stack_id_t stack_id, const char *f_name, aosl_async_prepare_lambda_f&& task) + { + aosl_async_prepare_lambda_f *prepare_f = new aosl_async_prepare_lambda_f (std::move (task)); + int err = aosl_async_prepare (stack_id, ref (), f_name, ____async_prepare_f, 1, prepare_f); + if (err < 0) + delete prepare_f; + + return err; + } + + static int prepare (aosl_stack_id_t stack_id, aosl_ref_t ref, const char *f_name, aosl_async_prepare_lambda_f&& task) + { + aosl_async_prepare_lambda_f *prepare_f = new aosl_async_prepare_lambda_f (std::move (task)); + int err = aosl_async_prepare (stack_id, ref, f_name, ____async_prepare_f, 1, prepare_f); + if (err < 0) + delete prepare_f; + + return err; + } + + private: + static int ____async_prepare_f (int free_only, uintptr_t argc, uintptr_t argv []) + { + aosl_async_prepare_lambda_f *prepare_f = reinterpret_cast(argv [0]); + int err; + err = (*prepare_f) (free_only); + delete prepare_f; + return err; + } + + public: + int resume (aosl_stack_id_t stack_id, const char *f_name, aosl_async_resume_lambda_f&& task) + { + aosl_async_resume_lambda_f *resume_f = new aosl_async_resume_lambda_f (std::move (task)); + int err = aosl_async_resume (stack_id, ref (), f_name, ____async_resume_f, 1, resume_f); + if (err < 0) + delete resume_f; + + return err; + } + + static int resume (aosl_stack_id_t stack_id, aosl_ref_t ref, const char *f_name, aosl_async_resume_lambda_f&& task) + { + aosl_async_resume_lambda_f *resume_f = new aosl_async_resume_lambda_f (std::move (task)); + int err = aosl_async_resume (stack_id, ref, f_name, ____async_resume_f, 1, resume_f); + if (err < 0) + delete resume_f; + + return err; + } + + private: + static void ____async_resume_f (int free_only, uintptr_t argc, uintptr_t argv []) + { + aosl_async_resume_lambda_f *resume_f = reinterpret_cast(argv [0]); + (*resume_f) (free_only); + delete resume_f; + } + #endif /* __AOSL_ASYNC_H__ */ + #endif /* C++11 */ + + #if (__cplusplus >= 201103) || defined (_MSC_VER) + private: + aosl_ref_t_oop (const aosl_ref_t_oop &) = delete; + aosl_ref_t_oop (aosl_ref_t_oop &&) = delete; + aosl_ref_t_oop &operator = (const aosl_ref_t_oop &) = delete; + aosl_ref_t_oop &operator = (aosl_ref_t_oop &&) = delete; + #else + private: + aosl_ref_t_oop (const aosl_ref_t_oop &); + aosl_ref_t_oop &operator = (const aosl_ref_t_oop &); + #endif /* C++11 */ + }; + +private: + aosl_ref_t_oop *refoop; + +public: + aosl_ref_class (bool caller_free = true) + { + refoop = aosl_ref_t_oop::create (this, __dtor, caller_free); + if (aosl_ref_invalid (refoop)) + abort (); + } + + aosl_ref_class (aosl_ref_t_oop *obj) + { + refoop = obj; + } + + aosl_ref_class (aosl_ref_t ref) + { + refoop = aosl_ref_t_oop::from_aosl_ref_t (ref); + } + + aosl_ref_t_oop *ref_oop () const + { + return refoop; + } + + aosl_ref_t ref () const + { + return refoop->ref (); + } + + int hold (aosl_ref_func_t f, uintptr_t argc, ...) + { + va_list args; + int err; + + va_start (args, argc); + err = refoop->hold_args (f, argc, args); + va_end (args); + + return err; + } + + int hold_args (aosl_ref_func_t f, uintptr_t argc, va_list args) + { + return refoop->hold_args (f, argc, args); + } + + int hold_argv (aosl_ref_func_t f, uintptr_t argc, uintptr_t argv []) + { + return refoop->hold_argv (f, argc, argv); + } + + int read (aosl_ref_func_t f, uintptr_t argc, ...) + { + va_list args; + int err; + + va_start (args, argc); + err = refoop->read_args (f, argc, args); + va_end (args); + + return err; + } + + int read_args (aosl_ref_func_t f, uintptr_t argc, va_list args) + { + return refoop->read_args (f, argc, args); + } + + int read_argv (aosl_ref_func_t f, uintptr_t argc, uintptr_t argv []) + { + return refoop->read_argv (f, argc, argv); + } + + int write (aosl_ref_func_t f, uintptr_t argc, ...) + { + va_list args; + int err; + + va_start (args, argc); + err = refoop->write_args (f, argc, args); + va_end (args); + + return err; + } + + int write_args (aosl_ref_func_t f, uintptr_t argc, va_list args) + { + return refoop->write_args (f, argc, args); + } + + int write_argv (aosl_ref_func_t f, uintptr_t argc, uintptr_t argv []) + { + return refoop->write_argv (f, argc, argv); + } + + int unsafe (aosl_ref_func_t f, uintptr_t argc, ...) + { + va_list args; + int err; + + va_start (args, argc); + err = refoop->unsafe_args (f, argc, args); + va_end (args); + + return err; + } + + int unsafe_args (aosl_ref_func_t f, uintptr_t argc, va_list args) + { + return refoop->unsafe_args (f, argc, args); + } + + int unsafe_argv (aosl_ref_func_t f, uintptr_t argc, uintptr_t argv []) + { + return refoop->unsafe_argv (f, argc, argv); + } + + int maystall (aosl_ref_func_t f, uintptr_t argc, ...) + { + va_list args; + int err; + + va_start (args, argc); + err = refoop->unsafe_args (f, argc, args); + va_end (args); + + return err; + } + + int maystall_args (aosl_ref_func_t f, uintptr_t argc, va_list args) + { + return refoop->unsafe_args (f, argc, args); + } + + int maystall_argv (aosl_ref_func_t f, uintptr_t argc, uintptr_t argv []) + { + return refoop->unsafe_argv (f, argc, argv); + } + + /* The static version of member functions */ + static int hold (aosl_ref_t ref, aosl_ref_func_t f, uintptr_t argc, ...) + { + va_list args; + int err; + + va_start (args, argc); + err = aosl_ref_t_oop::hold_args (ref, f, argc, args); + va_end (args); + + return err; + } + + static int hold_args (aosl_ref_t ref, aosl_ref_func_t f, uintptr_t argc, va_list args) + { + return aosl_ref_t_oop::hold_args (ref, f, argc, args); + } + + static int hold_argv (aosl_ref_t ref, aosl_ref_func_t f, uintptr_t argc, uintptr_t argv []) + { + return aosl_ref_t_oop::hold_argv (ref, f, argc, argv); + } + + static int read (aosl_ref_t ref, aosl_ref_func_t f, uintptr_t argc, ...) + { + va_list args; + int err; + + va_start (args, argc); + err = aosl_ref_t_oop::read_args (ref, f, argc, args); + va_end (args); + + return err; + } + + static int read_args (aosl_ref_t ref, aosl_ref_func_t f, uintptr_t argc, va_list args) + { + return aosl_ref_t_oop::read_args (ref, f, argc, args); + } + + static int read_argv (aosl_ref_t ref, aosl_ref_func_t f, uintptr_t argc, uintptr_t argv []) + { + return aosl_ref_t_oop::read_argv (ref, f, argc, argv); + } + + static int write (aosl_ref_t ref, aosl_ref_func_t f, uintptr_t argc, ...) + { + va_list args; + int err; + + va_start (args, argc); + err = aosl_ref_t_oop::write_args (ref, f, argc, args); + va_end (args); + + return err; + } + + static int write_args (aosl_ref_t ref, aosl_ref_func_t f, uintptr_t argc, va_list args) + { + return aosl_ref_t_oop::write_args (ref, f, argc, args); + } + + static int write_argv (aosl_ref_t ref, aosl_ref_func_t f, uintptr_t argc, uintptr_t argv []) + { + return aosl_ref_t_oop::write_argv (ref, f, argc, argv); + } + + static int unsafe (aosl_ref_t ref, aosl_ref_func_t f, uintptr_t argc, ...) + { + va_list args; + int err; + + va_start (args, argc); + err = aosl_ref_t_oop::unsafe_args (ref, f, argc, args); + va_end (args); + + return err; + } + + static int unsafe_args (aosl_ref_t ref, aosl_ref_func_t f, uintptr_t argc, va_list args) + { + return aosl_ref_t_oop::unsafe_args (ref, f, argc, args); + } + + static int unsafe_argv (aosl_ref_t ref, aosl_ref_func_t f, uintptr_t argc, uintptr_t argv []) + { + return aosl_ref_t_oop::unsafe_argv (ref, f, argc, argv); + } + + static int maystall (aosl_ref_t ref, aosl_ref_func_t f, uintptr_t argc, ...) + { + va_list args; + int err; + + va_start (args, argc); + err = aosl_ref_t_oop::unsafe_args (ref, f, argc, args); + va_end (args); + + return err; + } + + static int maystall_args (aosl_ref_t ref, aosl_ref_func_t f, uintptr_t argc, va_list args) + { + return aosl_ref_t_oop::unsafe_args (ref, f, argc, args); + } + + static int maystall_argv (aosl_ref_t ref, aosl_ref_func_t f, uintptr_t argc, uintptr_t argv []) + { + return aosl_ref_t_oop::unsafe_argv (ref, f, argc, argv); + } + + static int read (aosl_refobj_t robj, aosl_ref_func_t f, uintptr_t argc, ...) + { + va_list args; + int err; + + va_start (args, argc); + err = aosl_ref_t_oop::read_args (robj, f, argc, args); + va_end (args); + + return err; + } + + static int read_args (aosl_refobj_t robj, aosl_ref_func_t f, uintptr_t argc, va_list args) + { + return aosl_ref_t_oop::read_args (robj, f, argc, args); + } + + static int read_argv (aosl_refobj_t robj, aosl_ref_func_t f, uintptr_t argc, uintptr_t argv []) + { + return aosl_ref_t_oop::read_argv (robj, f, argc, argv); + } + + static int unsafe (aosl_refobj_t robj, aosl_ref_func_t f, uintptr_t argc, ...) + { + va_list args; + int err; + + va_start (args, argc); + err = aosl_ref_t_oop::unsafe_args (robj, f, argc, args); + va_end (args); + + return err; + } + + static int unsafe_args (aosl_refobj_t robj, aosl_ref_func_t f, uintptr_t argc, va_list args) + { + return aosl_ref_t_oop::unsafe_args (robj, f, argc, args); + } + + static int unsafe_argv (aosl_refobj_t robj, aosl_ref_func_t f, uintptr_t argc, uintptr_t argv []) + { + return aosl_ref_t_oop::unsafe_argv (robj, f, argc, argv); + } + + static int maystall (aosl_refobj_t robj, aosl_ref_func_t f, uintptr_t argc, ...) + { + va_list args; + int err; + + va_start (args, argc); + err = aosl_ref_t_oop::unsafe_args (robj, f, argc, args); + va_end (args); + + return err; + } + + static int maystall_args (aosl_refobj_t robj, aosl_ref_func_t f, uintptr_t argc, va_list args) + { + return aosl_ref_t_oop::unsafe_args (robj, f, argc, args); + } + + static int maystall_argv (aosl_refobj_t robj, aosl_ref_func_t f, uintptr_t argc, uintptr_t argv []) + { + return aosl_ref_t_oop::unsafe_argv (robj, f, argc, argv); + } + + static aosl_ref_class *from_refobj (aosl_refobj_t robj) + { + return (aosl_ref_class *)aosl_refobj_arg (robj); + } + + /* set the living scope ref object of this ref object */ + int set_scope (aosl_ref_t scope_ref) + { + return refoop->set_scope (scope_ref); + } + + int destroy (bool do_delete = true) + { + int err = refoop->destroy (do_delete); + if (err < 0 && do_delete) { + ::delete this; + return 0; + } + + return err; + } + +public: + class deleter { + public: + void operator () (aosl_ref_class *obj_ptr) const + { + if (obj_ptr != NULL) + obj_ptr->destroy (); + } + }; + +protected: + /* We do not allow delete this object directly. */ + virtual ~aosl_ref_class () + { + } + +private: + static void __dtor (void *arg) + { + aosl_ref_class *__this = (aosl_ref_class *)arg; + ::delete __this; + } + +#ifdef __AOSL_MPQ_H__ + /* MPQ relative encapsulations */ +public: + int queue (aosl_mpq_t tq, aosl_mpq_t dq, const char *f_name, aosl_mpq_func_argv_t f, uintptr_t argc, ...) + { + va_list args; + int err; + + va_start (args, argc); + err = refoop->queue_args (tq, dq, f_name, f, argc, args); + va_end (args); + + return err; + } + + int queue_args (aosl_mpq_t tq, aosl_mpq_t dq, const char *f_name, aosl_mpq_func_argv_t f, uintptr_t argc, va_list args) + { + return refoop->queue_args (tq, dq, f_name, f, argc, args); + } + + int queue_argv (aosl_mpq_t tq, aosl_mpq_t dq, const char *f_name, aosl_mpq_func_argv_t f, uintptr_t argc, uintptr_t *argv) + { + return refoop->queue_argv (tq, dq, f_name, f, argc, argv); + } + + int queue_data (aosl_mpq_t tq, aosl_mpq_t dq, const char *f_name, aosl_mpq_func_data_t f, size_t len, void *data) + { + return refoop->queue_data (tq, dq, f_name, f, len, data); + } + + int call (aosl_mpq_t q, const char *f_name, aosl_mpq_func_argv_t f, uintptr_t argc, ...) + { + va_list args; + int err; + + va_start (args, argc); + err = refoop->call_args (q, f_name, f, argc, args); + va_end (args); + + return err; + } + + int call_args (aosl_mpq_t q, const char *f_name, aosl_mpq_func_argv_t f, uintptr_t argc, va_list args) + { + return refoop->call_args (q, f_name, f, argc, args); + } + + int call_argv (aosl_mpq_t q, const char *f_name, aosl_mpq_func_argv_t f, uintptr_t argc, uintptr_t *argv) + { + return refoop->call_argv (q, f_name, f, argc, argv); + } + + int call_data (aosl_mpq_t q, const char *f_name, aosl_mpq_func_data_t f, size_t len, void *data) + { + return refoop->call_data (q, f_name, f, len, data); + } + + int run (aosl_mpq_t q, aosl_mpq_t dq, const char *f_name, aosl_mpq_func_argv_t f, uintptr_t argc, ...) + { + va_list args; + int err; + + va_start (args, argc); + err = refoop->run_args (q, dq, f_name, f, argc, args); + va_end (args); + + return err; + } + + int run_args (aosl_mpq_t q, aosl_mpq_t dq, const char *f_name, aosl_mpq_func_argv_t f, uintptr_t argc, va_list args) + { + return refoop->run_args (q, dq, f_name, f, argc, args); + } + + int run_argv (aosl_mpq_t q, aosl_mpq_t dq, const char *f_name, aosl_mpq_func_argv_t f, uintptr_t argc, uintptr_t *argv) + { + return refoop->run_argv (q, dq, f_name, f, argc, argv); + } + + int run_data (aosl_mpq_t q, aosl_mpq_t dq, const char *f_name, aosl_mpq_func_data_t f, size_t len, void *data) + { + return refoop->run_data (q, dq, f_name, f, len, data); + } + +#ifdef __AOSL_MPQP_H__ + /* MPQP relative encapsulations */ + aosl_mpq_t queue (aosl_mpqp_t qp, aosl_mpq_t dq, const char *f_name, aosl_mpq_func_argv_t f, uintptr_t argc, ...) + { + va_list args; + aosl_mpq_t qid; + + va_start (args, argc); + qid = refoop->queue_args (qp, dq, f_name, f, argc, args); + va_end (args); + + return qid; + } + + aosl_mpq_t queue_args (aosl_mpqp_t qp, aosl_mpq_t dq, const char *f_name, aosl_mpq_func_argv_t f, uintptr_t argc, va_list args) + { + return refoop->queue_args (qp, dq, f_name, f, argc, args); + } + + aosl_mpq_t queue_argv (aosl_mpqp_t qp, aosl_mpq_t dq, const char *f_name, aosl_mpq_func_argv_t f, uintptr_t argc, uintptr_t *argv) + { + return refoop->queue_argv (qp, dq, f_name, f, argc, argv); + } + + aosl_mpq_t queue_data (aosl_mpqp_t qp, aosl_mpq_t dq, const char *f_name, aosl_mpq_func_data_t f, size_t len, void *data) + { + return refoop->queue_data (qp, dq, f_name, f, len, data); + } + + aosl_mpq_t call (aosl_mpqp_t qp, const char *f_name, aosl_mpq_func_argv_t f, uintptr_t argc, ...) + { + va_list args; + aosl_mpq_t qid; + + va_start (args, argc); + qid = refoop->call_args (qp, f_name, f, argc, args); + va_end (args); + + return qid; + } + + aosl_mpq_t call_args (aosl_mpqp_t qp, const char *f_name, aosl_mpq_func_argv_t f, uintptr_t argc, va_list args) + { + return refoop->call_args (qp, f_name, f, argc, args); + } + + aosl_mpq_t call_argv (aosl_mpqp_t qp, const char *f_name, aosl_mpq_func_argv_t f, uintptr_t argc, uintptr_t *argv) + { + return refoop->call_argv (qp, f_name, f, argc, argv); + } + + aosl_mpq_t call_data (aosl_mpqp_t qp, const char *f_name, aosl_mpq_func_data_t f, size_t len, void *data) + { + return refoop->call_data (qp, f_name, f, len, data); + } + + aosl_mpq_t run (aosl_mpqp_t qp, aosl_mpq_t dq, const char *f_name, aosl_mpq_func_argv_t f, uintptr_t argc, ...) + { + va_list args; + aosl_mpq_t qid; + + va_start (args, argc); + qid = refoop->run_args (qp, dq, f_name, f, argc, args); + va_end (args); + + return qid; + } + + aosl_mpq_t run_args (aosl_mpqp_t qp, aosl_mpq_t dq, const char *f_name, aosl_mpq_func_argv_t f, uintptr_t argc, va_list args) + { + return refoop->run_args (qp, dq, f_name, f, argc, args); + } + + aosl_mpq_t run_argv (aosl_mpqp_t qp, aosl_mpq_t dq, const char *f_name, aosl_mpq_func_argv_t f, uintptr_t argc, uintptr_t *argv) + { + return refoop->run_argv (qp, dq, f_name, f, argc, argv); + } + + aosl_mpq_t run_data (aosl_mpqp_t qp, aosl_mpq_t dq, const char *f_name, aosl_mpq_func_data_t f, size_t len, void *data) + { + return refoop->run_data (qp, dq, f_name, f, len, data); + } + + int pool_tail_queue (aosl_mpqp_t qp, aosl_mpq_t dq, const char *f_name, aosl_mpq_func_argv_t f, uintptr_t argc, ...) + { + va_list args; + int err; + + va_start (args, argc); + err = refoop->pool_tail_queue_args (qp, dq, f_name, f, argc, args); + va_end (args); + + return err; + } + + int pool_tail_queue_args (aosl_mpqp_t qp, aosl_mpq_t dq, const char *f_name, aosl_mpq_func_argv_t f, uintptr_t argc, va_list args) + { + return refoop->pool_tail_queue_args (qp, dq, f_name, f, argc, args); + } + + int pool_tail_queue_argv (aosl_mpqp_t qp, aosl_mpq_t dq, const char *f_name, aosl_mpq_func_argv_t f, uintptr_t argc, uintptr_t *argv) + { + return refoop->pool_tail_queue_argv (qp, dq, f_name, f, argc, argv); + } +#endif /* __AOSL_MPQP_H__ */ +#endif /* __AOSL_MPQ_H__ */ + + /* C++11 lambda encapsulations */ +#if (__cplusplus >= 201103) || defined (_MSC_VER) +public: + int hold (aosl_ref_lambda_f &&lambda_f) + { + return refoop->hold (std::move (lambda_f)); + } + + int read (aosl_ref_lambda_f &&lambda_f) + { + return refoop->read (std::move (lambda_f)); + } + + int write (aosl_ref_lambda_f &&lambda_f) + { + return refoop->write (std::move (lambda_f)); + } + + int unsafe (aosl_ref_lambda_f &&lambda_f) + { + return refoop->unsafe (std::move (lambda_f)); + } + + int maystall (aosl_ref_lambda_f &&lambda_f) + { + return refoop->unsafe (std::move (lambda_f)); + } + + static int hold (aosl_ref_t ref, aosl_ref_lambda_f &&lambda_f) + { + return aosl_ref_t_oop::hold (ref, std::move (lambda_f)); + } + + static int read (aosl_ref_t ref, aosl_ref_lambda_f &&lambda_f) + { + return aosl_ref_t_oop::read (ref, std::move (lambda_f)); + } + + static int write (aosl_ref_t ref, aosl_ref_lambda_f &&lambda_f) + { + return aosl_ref_t_oop::write (ref, std::move (lambda_f)); + } + + static int unsafe (aosl_ref_t ref, aosl_ref_lambda_f &&lambda_f) + { + return aosl_ref_t_oop::unsafe (ref, std::move (lambda_f)); + } + + static int maystall (aosl_ref_t ref, aosl_ref_lambda_f &&lambda_f) + { + return aosl_ref_t_oop::unsafe (ref, std::move (lambda_f)); + } + + static int read (aosl_refobj_t robj, aosl_ref_lambda_f &&lambda_f) + { + return aosl_ref_t_oop::read (robj, std::move (lambda_f)); + } + + static int unsafe (aosl_refobj_t robj, aosl_ref_lambda_f &&lambda_f) + { + return aosl_ref_t_oop::unsafe (robj, std::move (lambda_f)); + } + + static int maystall (aosl_refobj_t robj, aosl_ref_lambda_f &&lambda_f) + { + return aosl_ref_t_oop::unsafe (robj, std::move (lambda_f)); + } + +#ifdef __AOSL_MPQ_H__ +public: + typedef std::function aosl_ref_mpq_lambda_f; + typedef std::function aosl_ref_mpq_lambda_0arg_f; + + /* MPQ encapsulations */ + int queue (aosl_mpq_t tq, aosl_mpq_t dq, const char *f_name, aosl_ref_mpq_lambda_f&& task) + { + return refoop->queue (tq, dq, f_name, std::move (task)); + } + + int call (aosl_mpq_t q, const char *f_name, aosl_ref_mpq_lambda_f&& task, void *task_result = NULL) + { + return refoop->call (q, f_name, std::move (task), task_result); + } + + int run (aosl_mpq_t q, const char *f_name, aosl_ref_mpq_lambda_f&& task) + { + return refoop->run (q, f_name, std::move (task)); + } + + int queue (aosl_mpq_t tq, const char *f_name, aosl_ref_mpq_lambda_0arg_f&& task) + { + return refoop->queue (tq, f_name, std::move (task)); + } + + int call (aosl_mpq_t q, const char *f_name, aosl_ref_mpq_lambda_0arg_f&& task, void *task_result = NULL) + { + return refoop->call (q, f_name, std::move (task), task_result); + } + + int run (aosl_mpq_t q, const char *f_name, aosl_ref_mpq_lambda_0arg_f&& task) + { + return refoop->run (q, f_name, std::move (task)); + } + +#ifdef __AOSL_MPQP_H__ + /* MPQP encapsulations */ + aosl_mpq_t queue (aosl_mpqp_t qp, aosl_mpq_t dq, const char *f_name, aosl_ref_mpq_lambda_f&& task) + { + return refoop->queue (qp, dq, f_name, std::move (task)); + } + + aosl_mpq_t call (aosl_mpqp_t qp, const char *f_name, aosl_ref_mpq_lambda_f&& task, void *task_result = NULL) + { + return refoop->call (qp, f_name, std::move (task), task_result); + } + + aosl_mpq_t run (aosl_mpqp_t qp, const char *f_name, aosl_ref_mpq_lambda_f&& task) + { + return refoop->run (qp, f_name, std::move (task)); + } + + int pool_tail_queue (aosl_mpqp_t qp, aosl_mpq_t dq, const char *f_name, aosl_ref_mpq_lambda_f&& task) + { + return refoop->pool_tail_queue (qp, dq, f_name, std::move (task)); + } + + aosl_mpq_t queue (aosl_mpqp_t qp, const char *f_name, aosl_ref_mpq_lambda_0arg_f&& task) + { + return refoop->queue (qp, f_name, std::move (task)); + } + + aosl_mpq_t call (aosl_mpqp_t qp, const char *f_name, aosl_ref_mpq_lambda_0arg_f&& task, void *task_result = NULL) + { + return refoop->call (qp, f_name, std::move (task), task_result); + } + + aosl_mpq_t run (aosl_mpqp_t qp, const char *f_name, aosl_ref_mpq_lambda_0arg_f&& task) + { + return refoop->run (qp, f_name, std::move (task)); + } + + int pool_tail_queue (aosl_mpqp_t qp, const char *f_name, aosl_ref_mpq_lambda_0arg_f&& task) + { + return refoop->pool_tail_queue (qp, f_name, std::move (task)); + } +#endif /* __AOSL_MPQP_H__ */ + + /* MPQ with specified ref encapsulations */ + static int queue (aosl_mpq_t tq, aosl_mpq_t dq, aosl_ref_t ref, const char *f_name, aosl_ref_mpq_lambda_f&& task) + { + return aosl_ref_t_oop::queue (tq, dq, ref, f_name, std::move (task)); + } + + static int call (aosl_mpq_t q, aosl_ref_t ref, const char *f_name, aosl_ref_mpq_lambda_f&& task, void *task_result = NULL) + { + return aosl_ref_t_oop::call (q, ref, f_name, std::move (task), task_result); + } + + static int run (aosl_mpq_t q, aosl_ref_t ref, const char *f_name, aosl_ref_mpq_lambda_f&& task) + { + return aosl_ref_t_oop::run (q, ref, f_name, std::move (task)); + } + + static int queue (aosl_mpq_t tq, aosl_ref_t ref, const char *f_name, aosl_ref_mpq_lambda_0arg_f&& task) + { + return aosl_ref_t_oop::queue (tq, ref, f_name, std::move (task)); + } + + static int call (aosl_mpq_t q, aosl_ref_t ref, const char *f_name, aosl_ref_mpq_lambda_0arg_f&& task, void *task_result = NULL) + { + return aosl_ref_t_oop::call (q, ref, f_name, std::move (task), task_result); + } + + static int run (aosl_mpq_t q, aosl_ref_t ref, const char *f_name, aosl_ref_mpq_lambda_0arg_f&& task) + { + return aosl_ref_t_oop::run (q, ref, f_name, std::move (task)); + } + +#ifdef __AOSL_MPQP_H__ + /* MPQP with specified ref encapsulations */ + static aosl_mpq_t queue (aosl_mpqp_t qp, aosl_mpq_t dq, aosl_ref_t ref, const char *f_name, aosl_ref_mpq_lambda_f&& task) + { + return aosl_ref_t_oop::queue (qp, dq, ref, f_name, std::move (task)); + } + + static aosl_mpq_t call (aosl_mpqp_t qp, aosl_ref_t ref, const char *f_name, aosl_ref_mpq_lambda_f&& task, void *task_result = NULL) + { + return aosl_ref_t_oop::call (qp, ref, f_name, std::move (task), task_result); + } + + static aosl_mpq_t run (aosl_mpqp_t qp, aosl_ref_t ref, const char *f_name, aosl_ref_mpq_lambda_f&& task) + { + return aosl_ref_t_oop::run (qp, ref, f_name, std::move (task)); + } + + static int pool_tail_queue (aosl_mpqp_t qp, aosl_mpq_t dq, aosl_ref_t ref, const char *f_name, aosl_ref_mpq_lambda_f&& task) + { + return aosl_ref_t_oop::pool_tail_queue (qp, dq, ref, f_name, std::move (task)); + } + + static aosl_mpq_t queue (aosl_mpqp_t qp, aosl_ref_t ref, const char *f_name, aosl_ref_mpq_lambda_0arg_f&& task) + { + return aosl_ref_t_oop::queue (qp, ref, f_name, std::move (task)); + } + + static aosl_mpq_t call (aosl_mpqp_t qp, aosl_ref_t ref, const char *f_name, aosl_ref_mpq_lambda_0arg_f&& task, void *task_result = NULL) + { + return aosl_ref_t_oop::call (qp, ref, f_name, std::move (task), task_result); + } + + static aosl_mpq_t run (aosl_mpqp_t qp, aosl_ref_t ref, const char *f_name, aosl_ref_mpq_lambda_0arg_f&& task) + { + return aosl_ref_t_oop::run (qp, ref, f_name, std::move (task)); + } + + static int pool_tail_queue (aosl_mpqp_t qp, aosl_ref_t ref, const char *f_name, aosl_ref_mpq_lambda_0arg_f&& task) + { + return aosl_ref_t_oop::pool_tail_queue (qp, ref, f_name, std::move (task)); + } +#endif /* __AOSL_MPQP_H__ */ +#endif /* __AOSL_MPQ_H__ */ + +#ifdef __AOSL_ASYNC_H__ + /** + * The stackless coroutine like implementation in AOSL. We could not + * support the real stackless coroutine except in the language level, + * so we just provide similar equivalent functionals here. + **/ +public: + int prepare (aosl_stack_id_t stack_id, const char *f_name, aosl_async_prepare_lambda_f&& task) + { + return refoop->prepare (stack_id, f_name, std::move (task)); + } + + static int prepare (aosl_stack_id_t stack_id, aosl_ref_t ref, const char *f_name, aosl_async_prepare_lambda_f&& task) + { + return aosl_ref_t_oop::prepare (stack_id, ref, f_name, std::move (task)); + } + +public: + typedef std::function aosl_async_resume_lambda_f; + + int resume (aosl_stack_id_t stack_id, const char *f_name, aosl_async_resume_lambda_f&& task) + { + return refoop->resume (stack_id, f_name, std::move (task)); + } + + static int resume (aosl_stack_id_t stack_id, aosl_ref_t ref, const char *f_name, aosl_async_resume_lambda_f&& task) + { + return aosl_ref_t_oop::resume (stack_id, ref, f_name, std::move (task)); + } +#endif /* __AOSL_ASYNC_H__ */ +#endif /* C++11 */ + +#if (__cplusplus >= 201103) || defined (_MSC_VER) +private: + aosl_ref_class (const aosl_ref_class &) = delete; + aosl_ref_class (aosl_ref_class &&) = delete; + aosl_ref_class &operator = (const aosl_ref_class &) = delete; + aosl_ref_class &operator = (aosl_ref_class &&) = delete; +#else +private: + aosl_ref_class (const aosl_ref_class &); + aosl_ref_class &operator = (const aosl_ref_class &); +#endif /* C++11 */ +}; + + +/** + * The T_ref_cls argument of this template must be + * aosl_ref_class or its derivatives. + **/ +template +class aosl_ref_unique_ptr { +private: + T_ref_cls *_ptr; + +public: + aosl_ref_unique_ptr (): _ptr (NULL) {} + aosl_ref_unique_ptr (T_ref_cls *p): _ptr (p) {} + + aosl_ref_unique_ptr &operator = (T_ref_cls *p) + { + reset (); + _ptr = p; + return *this; + } + + T_ref_cls *operator -> () const + { + return _ptr; + } + + T_ref_cls *get () const + { + return _ptr; + } + + operator bool () const + { + return _ptr != NULL; + } + + T_ref_cls *release () + { + T_ref_cls *p = _ptr; + _ptr = NULL; + return p; + } + + void reset (T_ref_cls *p = NULL) + { + T_ref_cls *old = _ptr; + + /** + * We do the destroy and not delete the object + * before we set the pointer to the new value, + * this is very important to make sure that no + * any async operation is executing. + **/ + if (old != NULL) + old->destroy (false/* not delete */); + + _ptr = p; + + /** + * The destroy with delete operation must be + * the last action, and don't touch any member + * of this object anymore after it. + **/ + if (old != NULL) + old->destroy (true/* do delete */); + } + + ~aosl_ref_unique_ptr () + { + reset (); + } + +#if (__cplusplus >= 201103) || defined (_MSC_VER) +private: + aosl_ref_unique_ptr (const aosl_ref_unique_ptr &) = delete; + aosl_ref_unique_ptr &operator = (const aosl_ref_unique_ptr &) = delete; + +public: + aosl_ref_unique_ptr (aosl_ref_unique_ptr &&src): _ptr (src.release ()) {} + aosl_ref_unique_ptr &operator = (aosl_ref_unique_ptr &&ptr) + { + reset (ptr.release ()); + return *this; + } +#else +private: + aosl_ref_unique_ptr (const aosl_ref_unique_ptr &); + aosl_ref_unique_ptr &operator = (const aosl_ref_unique_ptr &); +#endif /* C++11 */ +}; + + +template +inline bool operator == (const aosl_ref_unique_ptr &ptr, intptr_t _null) +{ + return ptr.get () == (T_ref_cls *)_null; +} + +template +inline bool operator != (const aosl_ref_unique_ptr &ptr, intptr_t _null) +{ + return ptr.get () != (T_ref_cls *)_null; +} + +template +inline bool operator == (intptr_t _null, const aosl_ref_unique_ptr &ptr) +{ + return (T_ref_cls *)_null == ptr.get (); +} + +template +inline bool operator != (intptr_t _null, const aosl_ref_unique_ptr &ptr) +{ + return (T_ref_cls *)_null != ptr.get (); +} + +#if (__cplusplus >= 201103) || defined (_MSC_VER) +template +inline bool operator == (const aosl_ref_unique_ptr &ptr, std::nullptr_t) +{ + return !ptr; +} + +template +inline bool operator != (const aosl_ref_unique_ptr &ptr, std::nullptr_t) +{ + return ptr; +} + +template +inline bool operator == (std::nullptr_t, const aosl_ref_unique_ptr &ptr) +{ + return !ptr; +} + +template +inline bool operator != (std::nullptr_t, const aosl_ref_unique_ptr &ptr) +{ + return ptr; +} +#endif /* C++11 */ + + +typedef aosl_ref_unique_ptr aosl_ref_class_unique_ptr; + + +#endif /* __AOSL_REF_OBJ_CPP_H__ */ \ No newline at end of file diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/AgoraBase.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/AgoraBase.h index a3b4647a6..792137209 100644 --- a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/AgoraBase.h +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/AgoraBase.h @@ -33,31 +33,42 @@ #if defined(AGORARTC_EXPORT) #define AGORA_API extern "C" __declspec(dllexport) +#define AGORA_CPP_API __declspec(dllexport) #else #define AGORA_API extern "C" __declspec(dllimport) +#define AGORA_CPP_API __declspec(dllimport) #endif // AGORARTC_EXPORT #define AGORA_CALL __cdecl #define __deprecated +#define AGORA_CPP_INTERNAL_API extern + #elif defined(__APPLE__) #include #define AGORA_API extern "C" __attribute__((visibility("default"))) +#define AGORA_CPP_API __attribute__((visibility("default"))) #define AGORA_CALL +#define AGORA_CPP_INTERNAL_API __attribute__((visibility("hidden"))) + #elif defined(__ANDROID__) || defined(__linux__) #define AGORA_API extern "C" __attribute__((visibility("default"))) +#define AGORA_CPP_API __attribute__((visibility("default"))) #define AGORA_CALL #define __deprecated +#define AGORA_CPP_INTERNAL_API __attribute__((visibility("hidden"))) + #else // !_WIN32 && !__APPLE__ && !(__ANDROID__ || __linux__) #define AGORA_API extern "C" +#define AGORA_CPP_API #define AGORA_CALL #define __deprecated @@ -631,7 +642,7 @@ enum ERROR_CODE_TYPE { */ ERR_SET_CLIENT_ROLE_NOT_AUTHORIZED = 119, /** - * 120: Decryption fails. The user may have tried to join the channel with a wrong + * 120: MediaStream decryption fails. The user may have tried to join the channel with a wrong * password. Check your settings or try rejoining the channel. */ ERR_DECRYPTION_FAILED = 120, @@ -639,6 +650,11 @@ enum ERROR_CODE_TYPE { * 121: The user ID is invalid. */ ERR_INVALID_USER_ID = 121, + /** + * 122: DataStream decryption fails. The peer may have tried to join the channel with a wrong + * password, or did't enable datastream encryption + */ + ERR_DATASTREAM_DECRYPTION_FAILED = 122, /** * 123: The app is banned by the server. */ @@ -850,7 +866,6 @@ enum INTERFACE_ID_TYPE { AGORA_IID_RTC_CONNECTION = 7, AGORA_IID_SIGNALING_ENGINE = 8, AGORA_IID_MEDIA_ENGINE_REGULATOR = 9, - AGORA_IID_CLOUD_SPATIAL_AUDIO = 10, AGORA_IID_LOCAL_SPATIAL_AUDIO = 11, AGORA_IID_STATE_SYNC = 13, AGORA_IID_META_SERVICE = 14, @@ -1163,11 +1178,12 @@ enum VIDEO_CODEC_TYPE { */ VIDEO_CODEC_GENERIC_H264 = 7, /** - * 12: AV1. - */ + * 12: AV1. + * @technical preview + */ VIDEO_CODEC_AV1 = 12, /** - * 5: VP9. + * 13: VP9. */ VIDEO_CODEC_VP9 = 13, /** @@ -1176,6 +1192,28 @@ enum VIDEO_CODEC_TYPE { VIDEO_CODEC_GENERIC_JPEG = 20, }; +/** + * Camera focal length type. + */ +enum CAMERA_FOCAL_LENGTH_TYPE { + /** + * By default, there are no wide-angle and ultra-wide-angle properties. + */ + CAMERA_FOCAL_LENGTH_DEFAULT = 0, + /** + * Lens with focal length from 24mm to 35mm. + */ + CAMERA_FOCAL_LENGTH_WIDE_ANGLE = 1, + /** + * Lens with focal length of less than 24mm. + */ + CAMERA_FOCAL_LENGTH_ULTRA_WIDE = 2, + /** + * Telephoto lens. + */ + CAMERA_FOCAL_LENGTH_TELEPHOTO = 3, +}; + /** * The CC (Congestion Control) mode options. */ @@ -1311,6 +1349,10 @@ enum AUDIO_CODEC_TYPE { * 12: LPCNET. */ AUDIO_CODEC_LPCNET = 12, + /** + * 13: Opus codec, supporting 3 to 8 channels audio. + */ + AUDIO_CODEC_OPUSMC = 13, }; /** @@ -1511,13 +1553,38 @@ enum H264PacketizeMode { */ enum VIDEO_STREAM_TYPE { /** - * 0: The high-quality video stream, which has a higher resolution and bitrate. + * 0: The high-quality video stream, which has the highest resolution and bitrate. */ VIDEO_STREAM_HIGH = 0, /** - * 1: The low-quality video stream, which has a lower resolution and bitrate. + * 1: The low-quality video stream, which has the lowest resolution and bitrate. */ VIDEO_STREAM_LOW = 1, + /** + * 4: The video stream of layer_1, which has a lower resolution and bitrate than VIDEO_STREAM_HIGH. + */ + VIDEO_STREAM_LAYER_1 = 4, + /** + * 5: The video stream of layer_2, which has a lower resolution and bitrate than VIDEO_STREAM_LAYER_1. + */ + VIDEO_STREAM_LAYER_2 = 5, + /** + * 6: The video stream of layer_3, which has a lower resolution and bitrate than VIDEO_STREAM_LAYER_2. + */ + VIDEO_STREAM_LAYER_3 = 6, + /** + * 7: The video stream of layer_4, which has a lower resolution and bitrate than VIDEO_STREAM_LAYER_3. + */ + VIDEO_STREAM_LAYER_4 = 7, + /** + * 8: The video stream of layer_5, which has a lower resolution and bitrate than VIDEO_STREAM_LAYER_4. + */ + VIDEO_STREAM_LAYER_5 = 8, + /** + * 9: The video stream of layer_6, which has a lower resolution and bitrate than VIDEO_STREAM_LAYER_5. + */ + VIDEO_STREAM_LAYER_6 = 9, + }; struct VideoSubscriptionOptions { @@ -1563,7 +1630,8 @@ struct EncodedVideoFrameInfo { trackId(0), captureTimeMs(0), decodeTimeMs(0), - streamType(VIDEO_STREAM_HIGH) {} + streamType(VIDEO_STREAM_HIGH), + presentationMs(-1) {} EncodedVideoFrameInfo(const EncodedVideoFrameInfo& rhs) : uid(rhs.uid), @@ -1576,7 +1644,8 @@ struct EncodedVideoFrameInfo { trackId(rhs.trackId), captureTimeMs(rhs.captureTimeMs), decodeTimeMs(rhs.decodeTimeMs), - streamType(rhs.streamType) {} + streamType(rhs.streamType), + presentationMs(rhs.presentationMs) {} EncodedVideoFrameInfo& operator=(const EncodedVideoFrameInfo& rhs) { if (this == &rhs) return *this; @@ -1591,6 +1660,7 @@ struct EncodedVideoFrameInfo { captureTimeMs = rhs.captureTimeMs; decodeTimeMs = rhs.decodeTimeMs; streamType = rhs.streamType; + presentationMs = rhs.presentationMs; return *this; } @@ -1642,6 +1712,8 @@ struct EncodedVideoFrameInfo { */ VIDEO_STREAM_TYPE streamType; + // @technical preview + int64_t presentationMs; }; /** @@ -1691,17 +1763,27 @@ struct AdvanceOptions { */ COMPRESSION_PREFERENCE compressionPreference; + /** + * Whether to encode and send the alpha data to the remote when alpha data is present. + * The default value is false. + */ + bool encodeAlpha; + AdvanceOptions() : encodingPreference(PREFER_AUTO), - compressionPreference(PREFER_LOW_LATENCY) {} + compressionPreference(PREFER_LOW_LATENCY), + encodeAlpha(false) {} AdvanceOptions(ENCODING_PREFERENCE encoding_preference, - COMPRESSION_PREFERENCE compression_preference) : + COMPRESSION_PREFERENCE compression_preference, + bool encode_alpha) : encodingPreference(encoding_preference), - compressionPreference(compression_preference) {} + compressionPreference(compression_preference), + encodeAlpha(encode_alpha) {} bool operator==(const AdvanceOptions& rhs) const { return encodingPreference == rhs.encodingPreference && - compressionPreference == rhs.compressionPreference; + compressionPreference == rhs.compressionPreference && + encodeAlpha == rhs.encodeAlpha; } }; @@ -1724,6 +1806,17 @@ enum VIDEO_MIRROR_MODE_TYPE { VIDEO_MIRROR_MODE_DISABLED = 2, }; +#if defined(__APPLE__) && TARGET_OS_IOS +/** + * Camera capturer configuration for format type. + */ +enum CAMERA_FORMAT_TYPE { + /** 0: (Default) NV12. */ + CAMERA_FORMAT_NV12, + /** 1: BGRA. */ + CAMERA_FORMAT_BGRA, +}; +#endif /** Supported codec type bit mask. */ enum CODEC_CAP_MASK { @@ -1762,6 +1855,14 @@ struct CodecCapInfo { CodecCapInfo(): codecType(VIDEO_CODEC_NONE), codecCapMask(0) {} }; +/** FocalLengthInfo contains the IDs of the front and rear cameras, along with the wide-angle types. */ +struct FocalLengthInfo { + /** The camera direction. */ + int cameraDirection; + /** Camera focal segment type. */ + CAMERA_FOCAL_LENGTH_TYPE focalLengthType; +}; + /** * The definition of the VideoEncoderConfiguration struct. */ @@ -1873,7 +1974,7 @@ struct VideoEncoderConfiguration { AdvanceOptions advanceOptions; VideoEncoderConfiguration(const VideoDimensions& d, int f, int b, ORIENTATION_MODE m, VIDEO_MIRROR_MODE_TYPE mirror = VIDEO_MIRROR_MODE_DISABLED) - : codecType(VIDEO_CODEC_H265), + : codecType(VIDEO_CODEC_NONE), dimensions(d), frameRate(f), bitrate(b), @@ -1881,9 +1982,9 @@ struct VideoEncoderConfiguration { orientationMode(m), degradationPreference(MAINTAIN_QUALITY), mirrorMode(mirror), - advanceOptions(PREFER_AUTO, PREFER_LOW_LATENCY) {} + advanceOptions(PREFER_AUTO, PREFER_LOW_LATENCY, false) {} VideoEncoderConfiguration(int width, int height, int f, int b, ORIENTATION_MODE m, VIDEO_MIRROR_MODE_TYPE mirror = VIDEO_MIRROR_MODE_DISABLED) - : codecType(VIDEO_CODEC_H265), + : codecType(VIDEO_CODEC_NONE), dimensions(width, height), frameRate(f), bitrate(b), @@ -1891,7 +1992,7 @@ struct VideoEncoderConfiguration { orientationMode(m), degradationPreference(MAINTAIN_QUALITY), mirrorMode(mirror), - advanceOptions(PREFER_AUTO, PREFER_LOW_LATENCY) {} + advanceOptions(PREFER_AUTO, PREFER_LOW_LATENCY, false) {} VideoEncoderConfiguration(const VideoEncoderConfiguration& config) : codecType(config.codecType), dimensions(config.dimensions), @@ -1903,7 +2004,7 @@ struct VideoEncoderConfiguration { mirrorMode(config.mirrorMode), advanceOptions(config.advanceOptions) {} VideoEncoderConfiguration() - : codecType(VIDEO_CODEC_H265), + : codecType(VIDEO_CODEC_NONE), dimensions(FRAME_WIDTH_960, FRAME_HEIGHT_540), frameRate(FRAME_RATE_FPS_15), bitrate(STANDARD_BITRATE), @@ -1911,7 +2012,7 @@ struct VideoEncoderConfiguration { orientationMode(ORIENTATION_MODE_ADAPTIVE), degradationPreference(MAINTAIN_QUALITY), mirrorMode(VIDEO_MIRROR_MODE_DISABLED), - advanceOptions(PREFER_AUTO, PREFER_LOW_LATENCY) {} + advanceOptions(PREFER_AUTO, PREFER_LOW_LATENCY, false) {} VideoEncoderConfiguration& operator=(const VideoEncoderConfiguration& rhs) { if (this == &rhs) return *this; @@ -1985,15 +2086,78 @@ struct SimulcastStreamConfig { */ int kBitrate; /** - * he capture frame rate (fps) of the local video. The default value is 5. + * The capture frame rate (fps) of the local video. The default value is 5. */ int framerate; SimulcastStreamConfig() : dimensions(160, 120), kBitrate(65), framerate(5) {} + SimulcastStreamConfig(const SimulcastStreamConfig& other) : dimensions(other.dimensions), kBitrate(other.kBitrate), framerate(other.framerate) {} bool operator==(const SimulcastStreamConfig& rhs) const { return dimensions == rhs.dimensions && kBitrate == rhs.kBitrate && framerate == rhs.framerate; } }; +/** + * The configuration of the multi-layer video stream. + */ +struct SimulcastConfig { + /** + * The index of multi-layer video stream + */ + enum StreamLayerIndex { + /** + * 0: video stream index of layer_1 + */ + STREAM_LAYER_1 = 0, + /** + * 1: video stream index of layer_2 + */ + STREAM_LAYER_2 = 1, + /** + * 2: video stream index of layer_3 + */ + STREAM_LAYER_3 = 2, + /** + * 3: video stream index of layer_4 + */ + STREAM_LAYER_4 = 3, + /** + * 4: video stream index of layer_5 + */ + STREAM_LAYER_5 = 4, + /** + * 5: video stream index of layer_6 + */ + STREAM_LAYER_6 = 5, + /** + * 6: video stream index of low + */ + STREAM_LOW = 6, + /** + * 7: max count of video stream layers + */ + STREAM_LAYER_COUNT_MAX = 7 + }; + struct StreamLayerConfig { + /** + * The video frame dimension. The default value is 0. + */ + VideoDimensions dimensions; + /** + * The capture frame rate (fps) of the local video. The default value is 0. + */ + int framerate; + /** + * Whether to enable the corresponding layer of video stream. The default value is false. + */ + bool enable; + StreamLayerConfig() : dimensions(0, 0), framerate(0), enable(false) {} + }; + + /** + * The array of StreamLayerConfig, which contains STREAM_LAYER_COUNT_MAX layers of video stream at most. + */ + StreamLayerConfig configs[STREAM_LAYER_COUNT_MAX]; +}; /** * The location of the target area relative to the screen or window. If you do not set this parameter, * the SDK selects the whole screen or window. @@ -2591,6 +2755,10 @@ enum VIDEO_APPLICATION_SCENARIO_TYPE { * 1: Meeting Scenario. This scenario is the best QoE practice of meeting application. */ APPLICATION_SCENARIO_MEETING = 1, + /** + * 2: Video Call Scenario. This scenario is used to optimize the video experience in video application, like 1v1 video call. + */ + APPLICATION_SCENARIO_1V1 = 2, }; /** @@ -2635,6 +2803,27 @@ enum CAPTURE_BRIGHTNESS_LEVEL_TYPE { CAPTURE_BRIGHTNESS_LEVEL_DARK = 2, }; +enum CAMERA_STABILIZATION_MODE { + /** The camera stabilization mode is disabled. + */ + CAMERA_STABILIZATION_MODE_OFF = -1, + /** device choose stabilization mode automatically. + */ + CAMERA_STABILIZATION_MODE_AUTO = 0, + /** stabilization mode level 1. + */ + CAMERA_STABILIZATION_MODE_LEVEL_1 = 1, + /** stabilization mode level 2. + */ + CAMERA_STABILIZATION_MODE_LEVEL_2 = 2, + /** stabilization mode level 3. + */ + CAMERA_STABILIZATION_MODE_LEVEL_3 = 3, + /** The maximum level of the camera stabilization mode. + */ + CAMERA_STABILIZATION_MODE_MAX_LEVEL = CAMERA_STABILIZATION_MODE_LEVEL_3, +}; + /** * Local audio states. */ @@ -2788,6 +2977,16 @@ enum LOCAL_VIDEO_STREAM_REASON { * Check whether the ID of the video device is valid. */ LOCAL_VIDEO_STREAM_REASON_DEVICE_INVALID_ID = 10, + /** + * 14: (Android only) Video capture was interrupted, possibly due to the camera being occupied + * or some policy reasons such as background termination. + */ + LOCAL_VIDEO_STREAM_REASON_DEVICE_INTERRUPT = 14, + /** + * 15: (Android only) The device may need to be shut down and restarted to restore camera function, + * or there may be a persistent hardware problem. + */ + LOCAL_VIDEO_STREAM_REASON_DEVICE_FATAL_ERROR = 15, /** * 101: The current video capture device is unavailable due to excessive system pressure. */ @@ -2831,7 +3030,7 @@ enum LOCAL_VIDEO_STREAM_REASON { LOCAL_VIDEO_STREAM_REASON_SCREEN_CAPTURE_WINDOW_HIDDEN = 25, /** 26: (Windows only) The local screen capture window is recovered from its hidden state. */ LOCAL_VIDEO_STREAM_REASON_SCREEN_CAPTURE_WINDOW_RECOVER_FROM_HIDDEN = 26, - /** 27:(Windows only) The window is recovered from miniminzed */ + /** 27: (Windows and macOS only) The window is recovered from miniminzed */ LOCAL_VIDEO_STREAM_REASON_SCREEN_CAPTURE_WINDOW_RECOVER_FROM_MINIMIZED = 27, /** * 28: The screen capture paused. @@ -2843,6 +3042,8 @@ enum LOCAL_VIDEO_STREAM_REASON { LOCAL_VIDEO_STREAM_REASON_SCREEN_CAPTURE_PAUSED = 28, /** 29: The screen capture is resumed. */ LOCAL_VIDEO_STREAM_REASON_SCREEN_CAPTURE_RESUMED = 29, + /** 30: The shared display has been disconnected */ + LOCAL_VIDEO_STREAM_REASON_SCREEN_CAPTURE_DISPLAY_DISCONNECTED = 30, }; @@ -2920,6 +3121,14 @@ enum REMOTE_AUDIO_STATE_REASON * 7: The remote user leaves the channel. */ REMOTE_AUDIO_REASON_REMOTE_OFFLINE = 7, + /** + * 8: The local user does not receive any audio packet from remote user. + */ + REMOTE_AUDIO_REASON_NO_PACKET_RECEIVE = 8, + /** + * 9: The local user receives remote audio packet but fails to play. + */ + REMOTE_AUDIO_REASON_LOCAL_PLAY_FAILED = 9, }; /** @@ -3040,7 +3249,7 @@ enum REMOTE_USER_STATE { struct VideoTrackInfo { VideoTrackInfo() : isLocal(false), ownerUid(0), trackId(0), channelId(OPTIONAL_NULLPTR) - , streamType(VIDEO_STREAM_HIGH), codecType(VIDEO_CODEC_H265) + , codecType(VIDEO_CODEC_H265) , encodedFrameOnly(false), sourceType(VIDEO_SOURCE_CAMERA_PRIMARY) , observationPosition(agora::media::base::POSITION_POST_CAPTURER) {} /** @@ -3061,10 +3270,6 @@ struct VideoTrackInfo { * The channel ID of the video track. */ const char* channelId; - /** - * The video stream type: #VIDEO_STREAM_TYPE. - */ - VIDEO_STREAM_TYPE streamType; /** * The video codec type: #VIDEO_CODEC_TYPE. */ @@ -4112,12 +4317,14 @@ enum CLIENT_ROLE_CHANGE_FAILED_REASON { CLIENT_ROLE_CHANGE_FAILED_NOT_AUTHORIZED = 2, /** * 3: The operation of changing role is timeout. + * @deprecated This reason is deprecated. */ - CLIENT_ROLE_CHANGE_FAILED_REQUEST_TIME_OUT = 3, + CLIENT_ROLE_CHANGE_FAILED_REQUEST_TIME_OUT __deprecated = 3, /** * 4: The operation of changing role is interrupted since we lost connection with agora service. + * @deprecated This reason is deprecated. */ - CLIENT_ROLE_CHANGE_FAILED_CONNECTION_FAILED = 4, + CLIENT_ROLE_CHANGE_FAILED_CONNECTION_FAILED __deprecated = 4, }; /** @@ -4359,6 +4566,85 @@ struct BeautyOptions { BeautyOptions() : lighteningContrastLevel(LIGHTENING_CONTRAST_NORMAL), lighteningLevel(0), smoothnessLevel(0), rednessLevel(0), sharpnessLevel(0) {} }; +/** Face shape area options. This structure defines options for facial adjustments on different facial areas. + * + * @technical preview + */ +struct FaceShapeAreaOptions { + /** The specific facial area to be adjusted. + */ + enum FACE_SHAPE_AREA { + /** (Default) Invalid area. */ + FACE_SHAPE_AREA_NONE = -1, + /** Head Scale, reduces the size of head. */ + FACE_SHAPE_AREA_HEADSCALE = 0, + /** Forehead, adjusts the size of forehead. */ + FACE_SHAPE_AREA_FOREHEAD = 1, + /** Face Contour, slims the facial contour. */ + FACE_SHAPE_AREA_FACECONTOUR = 2, + /** Face Length, adjusts the length of face. */ + FACE_SHAPE_AREA_FACELENGTH = 3, + /** Face Width, narrows the width of face. */ + FACE_SHAPE_AREA_FACEWIDTH = 4, + /** Cheekbone, adjusts the size of cheekbone. */ + FACE_SHAPE_AREA_CHEEKBONE = 5, + /** Cheek, adjusts the size of cheek. */ + FACE_SHAPE_AREA_CHEEK = 6, + /** Chin, adjusts the length of chin. */ + FACE_SHAPE_AREA_CHIN = 7, + /** Eye Scale, adjusts the size of eyes. */ + FACE_SHAPE_AREA_EYESCALE = 8, + /** Nose Length, adjusts the length of nose. */ + FACE_SHAPE_AREA_NOSELENGTH = 9, + /** Nose Width, adjusts the width of nose. */ + FACE_SHAPE_AREA_NOSEWIDTH = 10, + /** Mouth Scale, adjusts the size of mouth. */ + FACE_SHAPE_AREA_MOUTHSCALE = 11, + }; + + /** The specific facial area to be adjusted, See #FACE_SHAPE_AREA. + */ + FACE_SHAPE_AREA shapeArea; + + /** The intensity of the pinching effect applied to the specified facial area. + * For the following area values: #FACE_SHAPE_AREA_FOREHEAD, #FACE_SHAPE_AREA_FACELENGTH, #FACE_SHAPE_AREA_CHIN, #FACE_SHAPE_AREA_NOSELENGTH, #FACE_SHAPE_AREA_NOSEWIDTH, #FACE_SHAPE_AREA_MOUTHSCALE, the value ranges from -100 to 100. + * The default value is 0. The greater the absolute value, the stronger the intensity applied to the specified facial area, and negative values indicate the opposite direction. + * For enumeration values other than the above, the value ranges from 0 to 100. The default value is 0. The greater the value, the stronger the intensity applied to the specified facial area. + */ + int shapeIntensity; + + FaceShapeAreaOptions(FACE_SHAPE_AREA shapeArea, int areaIntensity) : shapeArea(shapeArea), shapeIntensity(areaIntensity) {} + + FaceShapeAreaOptions() : shapeArea(FACE_SHAPE_AREA_NONE), shapeIntensity(0) {} +}; + +/** Face shape beauty options. This structure defines options for facial adjustments of different facial styles. + * + * @technical preview + */ +struct FaceShapeBeautyOptions { + /** The face shape style. + */ + enum FACE_SHAPE_BEAUTY_STYLE { + /** (Default) Female face shape style. */ + FACE_SHAPE_BEAUTY_STYLE_FEMALE = 0, + /** Male face shape style. */ + FACE_SHAPE_BEAUTY_STYLE_MALE = 1, + }; + + /** The face shape style, See #FACE_SHAPE_BEAUTY_STYLE. + */ + FACE_SHAPE_BEAUTY_STYLE shapeStyle; + + /** The intensity of the pinching effect applied to the specified facial style. The value ranges from 0 (original) to 100. The default value is 0. The greater the value, the stronger the intensity applied to face pinching. + */ + int styleIntensity; + + FaceShapeBeautyOptions(FACE_SHAPE_BEAUTY_STYLE shapeStyle, int styleIntensity) : shapeStyle(shapeStyle), styleIntensity(styleIntensity) {} + + FaceShapeBeautyOptions() : shapeStyle(FACE_SHAPE_BEAUTY_STYLE_FEMALE), styleIntensity(50) {} +}; + struct LowlightEnhanceOptions { /** * The low-light enhancement mode. @@ -4682,6 +4968,7 @@ enum VOICE_BEAUTIFIER_PRESET { * - `ROOM_ACOUSTICS_PHONOGRAPH` * - `ROOM_ACOUSTICS_SPACIAL` * - `ROOM_ACOUSTICS_ETHEREAL` + * - `ROOM_ACOUSTICS_CHORUS` * - `VOICE_CHANGER_EFFECT_UNCLE` * - `VOICE_CHANGER_EFFECT_OLDMAN` * - `VOICE_CHANGER_EFFECT_BOY` @@ -4743,6 +5030,14 @@ enum AUDIO_EFFECT_PRESET { * setting this enumerator. */ ROOM_ACOUSTICS_VIRTUAL_SURROUND_SOUND = 0x02010900, + /** The voice effect for chorus. + * + * @note: To achieve better audio effect quality, Agora recommends calling \ref + * IRtcEngine::setAudioProfile "setAudioProfile" and setting the `profile` parameter to + * `AUDIO_PROFILE_MUSIC_HIGH_QUALITY(4)` or `AUDIO_PROFILE_MUSIC_HIGH_QUALITY_STEREO(5)` before + * setting this enumerator. + */ + ROOM_ACOUSTICS_CHORUS = 0x02010D00, /** A middle-aged man's voice. * * @note @@ -4875,6 +5170,41 @@ enum HEADPHONE_EQUALIZER_PRESET { HEADPHONE_EQUALIZER_INEAR = 0x04000002 }; +/** The options for SDK voice AI tuner. + */ +enum VOICE_AI_TUNER_TYPE { + /** Uncle, deep and magnetic male voice. + */ + VOICE_AI_TUNER_MATURE_MALE, + /** Fresh male, refreshing and sweet male voice. + */ + VOICE_AI_TUNER_FRESH_MALE, + /** Big sister, deep and charming female voice. + */ + VOICE_AI_TUNER_ELEGANT_FEMALE, + /** Lolita, high-pitched and cute female voice. + */ + VOICE_AI_TUNER_SWEET_FEMALE, + /** Warm man singing, warm and melodic male voice that is suitable for male lyrical songs. + */ + VOICE_AI_TUNER_WARM_MALE_SINGING, + /** Gentle female singing, soft and delicate female voice that is suitable for female lyrical songs. + */ + VOICE_AI_TUNER_GENTLE_FEMALE_SINGING, + /** Smoky uncle singing, unique husky male voice that is suitable for rock or blues songs. + */ + VOICE_AI_TUNER_HUSKY_MALE_SINGING, + /** Warm big sister singing, warm and mature female voice that is suitable for emotionally powerful songs. + */ + VOICE_AI_TUNER_WARM_ELEGANT_FEMALE_SINGING, + /** Forceful male singing, strong and powerful male voice that is suitable for passionate songs. + */ + VOICE_AI_TUNER_POWERFUL_MALE_SINGING, + /** Dreamy female singing, dreamlike and soft female voice that is suitable for airy and dream-like songs. + */ + VOICE_AI_TUNER_DREAMY_FEMALE_SINGING, +}; + /** * Screen sharing configurations. */ @@ -5187,6 +5517,10 @@ enum AREA_CODE { AREA_CODE_GLOB = (0xFFFFFFFF) }; +/** + Extra region code + @technical preview +*/ enum AREA_CODE_EX { /** * Oceania @@ -5212,6 +5546,10 @@ enum AREA_CODE_EX { * United States */ AREA_CODE_US = 0x00000800, + /** + * Russia + */ + AREA_CODE_RU = 0x00001000, /** * The global area (except China) */ @@ -5530,10 +5868,13 @@ struct EncryptionConfig { * In this case, ensure that this parameter is not 0. */ uint8_t encryptionKdfSalt[32]; + + bool datastreamEncryptionEnabled; EncryptionConfig() : encryptionMode(AES_128_GCM2), - encryptionKey(OPTIONAL_NULLPTR) + encryptionKey(OPTIONAL_NULLPTR), + datastreamEncryptionEnabled(false) { memset(encryptionKdfSalt, 0, sizeof(encryptionKdfSalt)); } @@ -5573,13 +5914,21 @@ enum ENCRYPTION_ERROR_TYPE { */ ENCRYPTION_ERROR_INTERNAL_FAILURE = 0, /** - * 1: Decryption errors. Ensure that the receiver and the sender use the same encryption mode and key. + * 1: MediaStream decryption errors. Ensure that the receiver and the sender use the same encryption mode and key. */ ENCRYPTION_ERROR_DECRYPTION_FAILURE = 1, /** - * 2: Encryption errors. + * 2: MediaStream encryption errors. */ ENCRYPTION_ERROR_ENCRYPTION_FAILURE = 2, + /** + * 3: DataStream decryption errors. Ensure that the receiver and the sender use the same encryption mode and key. + */ + ENCRYPTION_ERROR_DATASTREAM_DECRYPTION_FAILURE = 3, + /** + * 4: DataStream encryption errors. + */ + ENCRYPTION_ERROR_DATASTREAM_ENCRYPTION_FAILURE = 4, }; enum UPLOAD_ERROR_REASON @@ -5621,8 +5970,8 @@ enum STREAM_SUBSCRIBE_STATE { * - Calls `enableLocalAudio(false)` or `enableLocalVideo(false)` to disable the local audio or video capture. * - The role of the remote user is audience. * - The local user calls the following methods to stop receiving remote streams: - * - Calls `muteRemoteAudioStream(true)`, `muteAllRemoteAudioStreams(true)` or `setDefaultMuteAllRemoteAudioStreams(true)` to stop receiving the remote audio streams. - * - Calls `muteRemoteVideoStream(true)`, `muteAllRemoteVideoStreams(true)` or `setDefaultMuteAllRemoteVideoStreams(true)` to stop receiving the remote video streams. + * - Calls `muteRemoteAudioStream(true)`, `muteAllRemoteAudioStreams(true)` to stop receiving the remote audio streams. + * - Calls `muteRemoteVideoStream(true)`, `muteAllRemoteVideoStreams(true)` to stop receiving the remote video streams. */ SUB_STATE_NO_SUBSCRIBED = 1, /** @@ -5713,7 +6062,12 @@ enum EAR_MONITORING_FILTER_TYPE { /** * 4: Enable noise suppression to the in-ear monitor. */ - EAR_MONITORING_FILTER_NOISE_SUPPRESSION = (1<<2) + EAR_MONITORING_FILTER_NOISE_SUPPRESSION = (1<<2), + /** + * 32768: Enable audio filters by reuse post-processing filter to the in-ear monitor. + * This bit is intended to be used in exclusive mode, which means, if this bit is set, all other bits will be disregarded. + */ + EAR_MONITORING_FILTER_REUSE_POST_PROCESSING_FILTER = (1<<15), }; /** @@ -5993,7 +6347,13 @@ struct LocalAccessPointConfiguration { /** Local proxy connection, advanced Config info. */ AdvancedConfigInfo advancedConfig; - LocalAccessPointConfiguration() : ipList(NULL), ipListSize(0), domainList(NULL), domainListSize(0), verifyDomainName(NULL), mode(ConnectivityFirst) {} + /** + * Whether to disable vos-aut: + - true: (Default)disable vos-aut. + - false: not disable vos-aut + */ + bool disableAut; + LocalAccessPointConfiguration() : ipList(NULL), ipListSize(0), domainList(NULL), domainListSize(0), verifyDomainName(NULL), mode(ConnectivityFirst), disableAut(true) {} }; /** diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/AgoraMediaBase.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/AgoraMediaBase.h index 15dfd4b38..8120acb3f 100644 --- a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/AgoraMediaBase.h +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/AgoraMediaBase.h @@ -35,6 +35,32 @@ static const unsigned int DEFAULT_CONNECTION_ID = 0; static const unsigned int DUMMY_CONNECTION_ID = (std::numeric_limits::max)(); struct EncodedVideoFrameInfo; +/** +* The definition of extension context types. +**/ +struct ExtensionContext { + /** + * Whether the uid is valid. + * - true: The uid is valid. + * - false: The uid is invalid. + */ + bool isValid; + /** + * The ID of the user. + * A uid of 0 indicates the local user, and a uid greater than 0 represents a remote user. + */ + uid_t uid; + /** + * The provider name of the current extension. + */ + const char *providerName; + /** + * The extension name of the current extension. + */ + const char *extensionName; + ExtensionContext():isValid(false), uid(0), providerName(NULL), extensionName(NULL) {} +}; + /** * Video source types definition. @@ -88,6 +114,9 @@ enum VIDEO_SOURCE_TYPE { /** Video for fourth screen sharing. */ VIDEO_SOURCE_SCREEN_FOURTH = 14, + /** Video for voice drive. + */ + VIDEO_SOURCE_SPEECH_DRIVEN = 15, VIDEO_SOURCE_UNKNOWN = 100 }; @@ -122,9 +151,9 @@ enum AudioRoute */ ROUTE_LOUDSPEAKER = 4, /** - * The Bluetooth Headset via HFP. + * The Bluetooth Device via HFP. */ - ROUTE_HEADSETBLUETOOTH = 5, + ROUTE_BLUETOOTH_DEVICE_HFP = 5, /** * The USB. */ @@ -142,9 +171,9 @@ enum AudioRoute */ ROUTE_AIRPLAY = 9, /** - * The Bluetooth Speaker via A2DP. + * The Bluetooth Device via A2DP. */ - ROUTE_BLUETOOTH_SPEAKER = 10, + ROUTE_BLUETOOTH_DEVICE_A2DP = 10, }; /** @@ -242,6 +271,10 @@ enum MEDIA_SOURCE_TYPE { * 12: Video for transcoded. */ TRANSCODED_VIDEO_SOURCE = 12, + /** + * 13: Video for voice drive. + */ + SPEECH_DRIVEN_VIDEO_SOURCE = 13, /** * 100: Internal Usage only. */ @@ -395,24 +428,31 @@ struct AudioPcmFrame { rtc::BYTES_PER_SAMPLE bytes_per_sample; /** The audio frame data. */ int16_t data_[kMaxDataSizeSamples]; + + /** + * @technical preview + * data_[kMaxDataSizeSamples] is real stereo data + */ + bool is_stereo_; AudioPcmFrame& operator=(const AudioPcmFrame& src) { - if(this == &src) { + if (this == &src) { return *this; } - this->capture_timestamp = src.capture_timestamp; - this->samples_per_channel_ = src.samples_per_channel_; - this->sample_rate_hz_ = src.sample_rate_hz_; - this->bytes_per_sample = src.bytes_per_sample; - this->num_channels_ = src.num_channels_; + capture_timestamp = src.capture_timestamp; + samples_per_channel_ = src.samples_per_channel_; + sample_rate_hz_ = src.sample_rate_hz_; + bytes_per_sample = src.bytes_per_sample; + num_channels_ = src.num_channels_; + is_stereo_ = src.is_stereo_; size_t length = src.samples_per_channel_ * src.num_channels_; if (length > kMaxDataSizeSamples) { length = kMaxDataSizeSamples; } - memcpy(this->data_, src.data_, length * sizeof(int16_t)); + memcpy(data_, src.data_, length * sizeof(int16_t)); return *this; } @@ -422,7 +462,8 @@ struct AudioPcmFrame { samples_per_channel_(0), sample_rate_hz_(0), num_channels_(0), - bytes_per_sample(rtc::TWO_BYTES_PER_SAMPLE) { + bytes_per_sample(rtc::TWO_BYTES_PER_SAMPLE), + is_stereo_(false) { memset(data_, 0, sizeof(data_)); } @@ -431,7 +472,8 @@ struct AudioPcmFrame { samples_per_channel_(src.samples_per_channel_), sample_rate_hz_(src.sample_rate_hz_), num_channels_(src.num_channels_), - bytes_per_sample(src.bytes_per_sample) { + bytes_per_sample(src.bytes_per_sample), + is_stereo_(src.is_stereo_) { size_t length = src.samples_per_channel_ * src.num_channels_; if (length > kMaxDataSizeSamples) { length = kMaxDataSizeSamples; @@ -502,6 +544,10 @@ enum VIDEO_PIXEL_FORMAT { 14: pixel format for iOS CVPixelBuffer BGRA */ VIDEO_CVPIXEL_BGRA = 14, + /** + 15: pixel format for iOS CVPixelBuffer P010(10bit NV12) + */ + VIDEO_CVPIXEL_P010 = 15, /** * 16: I422. */ @@ -510,6 +556,11 @@ enum VIDEO_PIXEL_FORMAT { * 17: ID3D11Texture2D, only support DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B8G8R8A8_TYPELESS, DXGI_FORMAT_NV12 texture format */ VIDEO_TEXTURE_ID3D11TEXTURE2D = 17, + /** + * 18: I010. 10bit I420 data. + * @technical preview + */ + VIDEO_PIXEL_I010 = 18, }; /** @@ -565,6 +616,191 @@ class IVideoFrameMetaInfo { virtual const char* getMetaInfoStr(META_INFO_KEY key) const = 0; }; +struct ColorSpace { + enum PrimaryID { + // The indices are equal to the values specified in T-REC H.273 Table 2. + PRIMARYID_BT709 = 1, + PRIMARYID_UNSPECIFIED = 2, + PRIMARYID_BT470M = 4, + PRIMARYID_BT470BG = 5, + PRIMARYID_SMPTE170M = 6, // Identical to BT601 + PRIMARYID_SMPTE240M = 7, + PRIMARYID_FILM = 8, + PRIMARYID_BT2020 = 9, + PRIMARYID_SMPTEST428 = 10, + PRIMARYID_SMPTEST431 = 11, + PRIMARYID_SMPTEST432 = 12, + PRIMARYID_JEDECP22 = 22, // Identical to EBU3213-E + }; + + enum RangeID { + // The indices are equal to the values specified at + // https://www.webmproject.org/docs/container/#colour for the element Range. + RANGEID_INVALID = 0, + // Limited Rec. 709 color range with RGB values ranging from 16 to 235. + RANGEID_LIMITED = 1, + // Full RGB color range with RGB valees from 0 to 255. + RANGEID_FULL = 2, + // Range is defined by MatrixCoefficients/TransferCharacteristics. + RANGEID_DERIVED = 3, + }; + + enum MatrixID { + // The indices are equal to the values specified in T-REC H.273 Table 4. + MATRIXID_RGB = 0, + MATRIXID_BT709 = 1, + MATRIXID_UNSPECIFIED = 2, + MATRIXID_FCC = 4, + MATRIXID_BT470BG = 5, + MATRIXID_SMPTE170M = 6, + MATRIXID_SMPTE240M = 7, + MATRIXID_YCOCG = 8, + MATRIXID_BT2020_NCL = 9, + MATRIXID_BT2020_CL = 10, + MATRIXID_SMPTE2085 = 11, + MATRIXID_CDNCLS = 12, + MATRIXID_CDCLS = 13, + MATRIXID_BT2100_ICTCP = 14, + }; + + enum TransferID { + // The indices are equal to the values specified in T-REC H.273 Table 3. + TRANSFERID_BT709 = 1, + TRANSFERID_UNSPECIFIED = 2, + TRANSFERID_GAMMA22 = 4, + TRANSFERID_GAMMA28 = 5, + TRANSFERID_SMPTE170M = 6, + TRANSFERID_SMPTE240M = 7, + TRANSFERID_LINEAR = 8, + TRANSFERID_LOG = 9, + TRANSFERID_LOG_SQRT = 10, + TRANSFERID_IEC61966_2_4 = 11, + TRANSFERID_BT1361_ECG = 12, + TRANSFERID_IEC61966_2_1 = 13, + TRANSFERID_BT2020_10 = 14, + TRANSFERID_BT2020_12 = 15, + TRANSFERID_SMPTEST2084 = 16, + TRANSFERID_SMPTEST428 = 17, + TRANSFERID_ARIB_STD_B67 = 18, + }; + + PrimaryID primaries; + TransferID transfer; + MatrixID matrix; + RangeID range; + + ColorSpace() + : primaries(PRIMARYID_UNSPECIFIED), transfer(TRANSFERID_UNSPECIFIED), + matrix(MATRIXID_UNSPECIFIED), range(RANGEID_INVALID) {} + + bool validate() const { + return primaries != PRIMARYID_UNSPECIFIED || transfer != TRANSFERID_UNSPECIFIED || + matrix != MATRIXID_UNSPECIFIED || + range != RANGEID_INVALID; + } +}; + +/** + * The definition of the Hdr10MetadataInfo struct. + */ +struct Hdr10MetadataInfo { + /** + * The x coordinates of the red value in the CIE1931 color space. The values need to normalized to 50,000. + */ + uint16_t redPrimaryX; + /** + * The y coordinates of the red value in the CIE1931 color space. The values need to normalized to 50,000. + */ + uint16_t redPrimaryY; + /** + * The x coordinates of the green value in the CIE1931 color space. The values need to normalized to 50,000. + */ + uint16_t greenPrimaryX; + /** + * The y coordinates of the green value in the CIE1931 color space. The values need to normalized to 50,000. + */ + uint16_t greenPrimaryY; + /** + * The x coordinates of the blue value in the CIE1931 color space. The values need to normalized to 50,000. + */ + uint16_t bluePrimaryX; + /** + * The y coordinates of the blue value in the CIE1931 color space. The values need to normalized to 50,000. + */ + uint16_t bluePrimaryY; + /** + * The x coordinates of the white point in the CIE1931 color space.The values need to normalized to 50,000. + */ + uint16_t whitePointX; + /** + * The y coordinates of the white point in the CIE1931 color space.The values need to normalized to 50,000. + */ + uint16_t whitePointY; + /** + * The maximum number of nits of the display used to master the content. The values need to normalized to 10,000. + */ + unsigned int maxMasteringLuminance; + /** + * The minimum number of nits of the display used to master the content. The values need to normalized to 10,000. + */ + unsigned int minMasteringLuminance; + /** + * The maximum content light level (MaxCLL). This is the nit value corresponding to the brightest pixel used anywhere in the content. + */ + uint16_t maxContentLightLevel; + /** + * The maximum frame average light level (MaxFALL). This is the nit value corresponding to the average luminance of the frame which has the brightest average luminance anywhere in the content. + */ + uint16_t maxFrameAverageLightLevel; + + Hdr10MetadataInfo() + : redPrimaryX(0), + redPrimaryY(0), + greenPrimaryX(0), + greenPrimaryY(0), + bluePrimaryX(0), + bluePrimaryY(0), + whitePointX(0), + whitePointY(0), + maxMasteringLuminance(0), + minMasteringLuminance(0), + maxContentLightLevel(0), + maxFrameAverageLightLevel(0){} + + bool validate() const { + return maxContentLightLevel >= 0 && maxContentLightLevel <= 20000 && + maxFrameAverageLightLevel >= 0 && + maxFrameAverageLightLevel <= 20000; + } +}; + +/** + * The relative position between alphabuffer and the frame. + */ +enum ALPHA_STITCH_MODE { + /** + * 0: Normal frame without alphabuffer stitched + */ + NO_ALPHA_STITCH = 0, + /** + * 1: Alphabuffer is above the frame + */ + ALPHA_STITCH_UP = 1, + /** + * 2: Alphabuffer is below the frame + */ + ALPHA_STITCH_BELOW = 2, + /** + * 3: Alphabuffer is on the left of frame + */ + ALPHA_STITCH_LEFT = 3, + /** + * 4: Alphabuffer is on the right of frame + */ + ALPHA_STITCH_RIGHT = 4, +}; + + /** * The definition of the ExternalVideoFrame struct. */ @@ -584,11 +820,14 @@ struct ExternalVideoFrame { eglContext(NULL), eglType(EGL_CONTEXT10), textureId(0), - metadata_buffer(NULL), - metadata_size(0), + fenceObject(0), + metadataBuffer(NULL), + metadataSize(0), alphaBuffer(NULL), - d3d11_texture_2d(NULL), - texture_slice_index(0){} + fillAlphaBuffer(false), + alphaStitchMode(NO_ALPHA_STITCH), + d3d11Texture2d(NULL), + textureSliceIndex(0){} /** * The EGL context type. @@ -690,6 +929,11 @@ struct ExternalVideoFrame { * [Texture related parameter] Incoming 4 × 4 transformational matrix. The typical value is a unit matrix. */ int textureId; + /** + * [Texture related parameter] The fence object related to the textureId parameter, indicating the synchronization status of the video data in Texture format. + * The default value is 0 + */ + long long fenceObject; /** * [Texture related parameter] Incoming 4 × 4 transformational matrix. The typical value is a unit matrix. */ @@ -698,28 +942,53 @@ struct ExternalVideoFrame { * [Texture related parameter] The MetaData buffer. * The default value is NULL */ - uint8_t* metadata_buffer; + uint8_t* metadataBuffer; /** * [Texture related parameter] The MetaData size. * The default value is 0 */ - int metadata_size; + int metadataSize; /** - * Indicates the output data of the portrait segmentation algorithm, which is consistent with the size of the video frame. - * The value range of each pixel is [0,255], where 0 represents the background; 255 represents the foreground (portrait). - * The default value is NULL + * Indicates the alpha channel of current frame, which is consistent with the dimension of the video frame. + * The value range of each pixel is [0,255], where 0 represents the background; 255 represents the foreground. + * The default value is NULL. */ uint8_t* alphaBuffer; + /** + * [For bgra or rgba only] Extract alphaBuffer from bgra or rgba data. Set it true if you do not explicitly specify the alphabuffer. + * The default value is false + */ + bool fillAlphaBuffer; + /** + * The relative position between alphabuffer and the frame. + * 0: Normal frame; + * 1: Alphabuffer is above the frame; + * 2: Alphabuffer is below the frame; + * 3: Alphabuffer is on the left of frame; + * 4: Alphabuffer is on the right of frame; + * The default value is 0. + */ + ALPHA_STITCH_MODE alphaStitchMode; /** * [For Windows only] The pointer of ID3D11Texture2D used by the video frame. */ - void *d3d11_texture_2d; + void *d3d11Texture2d; /** * [For Windows only] The index of ID3D11Texture2D array used by the video frame. */ - int texture_slice_index; + int textureSliceIndex; + + /** + * metadata info used for hdr video data + */ + Hdr10MetadataInfo hdr10MetadataInfo; + + /** + * The ColorSpace of the video frame. + */ + ColorSpace colorSpace; }; /** @@ -745,6 +1014,7 @@ struct VideoFrame { textureId(0), d3d11Texture2d(NULL), alphaBuffer(NULL), + alphaStitchMode(NO_ALPHA_STITCH), pixelBuffer(NULL), metaInfo(NULL){ memset(matrix, 0, sizeof(matrix)); @@ -827,11 +1097,21 @@ struct VideoFrame { */ float matrix[16]; /** - * Indicates the output data of the portrait segmentation algorithm, which is consistent with the size of the video frame. - * The value range of each pixel is [0,255], where 0 represents the background; 255 represents the foreground (portrait). - * The default value is NULL + * Indicates the alpha channel of current frame, which is consistent with the dimension of the video frame. + * The value range of each pixel is [0,255], where 0 represents the background; 255 represents the foreground. + * The default value is NULL. */ uint8_t* alphaBuffer; + /** + * The relative position between alphabuffer and the frame. + * 0: Normal frame; + * 1: Alphabuffer is above the frame; + * 2: Alphabuffer is below the frame; + * 3: Alphabuffer is on the left of frame; + * 4: Alphabuffer is on the right of frame; + * The default value is 0. + */ + ALPHA_STITCH_MODE alphaStitchMode; /** *The type of CVPixelBufferRef, for iOS and macOS only. */ @@ -840,6 +1120,16 @@ struct VideoFrame { * The pointer to IVideoFrameMetaInfo, which is the interface to get metainfo contents from VideoFrame. */ IVideoFrameMetaInfo* metaInfo; + + /** + * metadata info used for hdr video data + */ + Hdr10MetadataInfo hdr10MetadataInfo; + + /** + * The ColorSpace of the video frame + */ + ColorSpace colorSpace; }; /** @@ -981,6 +1271,10 @@ class IAudioFrameObserverBase { * The number of the audio track. */ int audioTrackNumber; + /** + * RTP timestamp of the first sample in the audio frame + */ + uint32_t rtpTimestamp; AudioFrame() : type(FRAME_TYPE_PCM16), samplesPerChannel(0), @@ -991,7 +1285,8 @@ class IAudioFrameObserverBase { renderTimeMs(0), avsync_type(0), presentationMs(0), - audioTrackNumber(0) {} + audioTrackNumber(0), + rtpTimestamp(0) {} }; enum AUDIO_FRAME_POSITION { @@ -1609,6 +1904,21 @@ struct MediaRecorderConfiguration { MediaRecorderConfiguration() : storagePath(NULL), containerFormat(FORMAT_MP4), streamType(STREAM_TYPE_BOTH), maxDurationMs(120000), recorderInfoUpdateInterval(0) {} MediaRecorderConfiguration(const char* path, MediaRecorderContainerFormat format, MediaRecorderStreamType type, int duration, int interval) : storagePath(path), containerFormat(format), streamType(type), maxDurationMs(duration), recorderInfoUpdateInterval(interval) {} }; + +class IFaceInfoObserver { +public: + /** + * Occurs when the face info is received. + * @param outFaceInfo The output face info. + * @return + * - true: The face info is valid. + * - false: The face info is invalid. + */ + virtual bool onFaceInfo(const char* outFaceInfo) = 0; + + virtual ~IFaceInfoObserver() {} +}; + /** * Information for the recording file. * diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/IAgoraLog.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/IAgoraLog.h index 2fae3aa13..20b6416ef 100644 --- a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/IAgoraLog.h +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/IAgoraLog.h @@ -37,6 +37,7 @@ OPTIONAL_ENUM_CLASS LOG_LEVEL { LOG_LEVEL_ERROR = 0x0004, LOG_LEVEL_FATAL = 0x0008, LOG_LEVEL_API_CALL = 0x0010, + LOG_LEVEL_DEBUG = 0x0020, }; /* diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/IAgoraMediaEngine.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/IAgoraMediaEngine.h index e57404a22..b3b92e9e4 100644 --- a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/IAgoraMediaEngine.h +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/IAgoraMediaEngine.h @@ -72,6 +72,21 @@ class IMediaEngine { * - < 0: Failure. */ virtual int registerVideoEncodedFrameObserver(IVideoEncodedFrameObserver* observer) = 0; + + /** + * Registers a face info observer object. + * + * @note + * Ensure that you call this method before \ref IRtcEngine::joinChannel "joinChannel". + * + * @param observer A pointer to the face info observer object: IFaceInfoObserver. + * + * @return + * - 0: Success. + * - < 0: Failure. + */ + virtual int registerFaceInfoObserver(IFaceInfoObserver* observer) = 0; + /** * Pushes the external audio data to the app. * diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/IAgoraMediaPlayer.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/IAgoraMediaPlayer.h index bd3c7597c..25f48a4a2 100644 --- a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/IAgoraMediaPlayer.h +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/IAgoraMediaPlayer.h @@ -49,17 +49,6 @@ class IMediaPlayer : public RefCountInterface { */ virtual int open(const char* url, int64_t startPos) = 0; - /** - * @deprecated - * @brief Open media file or stream with custom soucrce. - * @param startPos Set the starting position for playback, in seconds - * @param observer dataProvider object - * @return - * - 0: Success. - * - < 0: Failure. - */ - virtual int openWithCustomSource(int64_t startPos, media::base::IMediaPlayerCustomDataProvider* provider) __deprecated = 0; - /** * @brief Open a media file with a media file source. * @param source Media file source that you want to play, see `MediaSource` diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/IAgoraMediaPlayerSource.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/IAgoraMediaPlayerSource.h index 00be02233..99da405bc 100644 --- a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/IAgoraMediaPlayerSource.h +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/IAgoraMediaPlayerSource.h @@ -42,17 +42,6 @@ class IMediaPlayerSource : public RefCountInterface { * - < 0: Failure. */ virtual int open(const char* url, int64_t startPos) = 0; - - /** - * @deprecated - * @brief Open media file or stream with custom soucrce. - * @param startPos Set the starting position for playback, in seconds - * @param observer dataProvider object - * @return - * - 0: Success. - * - < 0: Failure. - */ - virtual int openWithCustomSource(int64_t startPos, media::base::IMediaPlayerCustomDataProvider* provider) __deprecated = 0; /** * Opens a media file with a media file source. diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/IAgoraMusicContentCenter.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/IAgoraMusicContentCenter.h index d5ed99ef8..ebb4d5237 100644 --- a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/IAgoraMusicContentCenter.h +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/IAgoraMusicContentCenter.h @@ -13,68 +13,90 @@ namespace agora { namespace rtc { +/** + * Modes for playing songs. + */ +typedef enum +{ + /** + * 0: The music player is in the origin mode, which means playing the original song. + */ + kMusicPlayModeOriginal = 0, + + /** + * 1: The music player is in the accompany mode, which means playing the accompaniment only. + */ + kMusicPlayModeAccompany = 1, + + /** + * 2: The music player is in the lead sing mode, which means playing the lead vocals. + */ + kMusicPlayModeLeadSing = 2, + +} MusicPlayMode; + typedef enum { /** * 0: No error occurs and preload succeeds. */ - kPreloadStatusCompleted = 0, + kPreloadStateCompleted = 0, /** * 1: A general error occurs. */ - kPreloadStatusFailed = 1, + kPreloadStateFailed = 1, /** * 2: The media file is preloading. */ - kPreloadStatusPreloading = 2, + kPreloadStatePreloading = 2, /** * 3: The media file is removed. */ - kPreloadStatusRemoved = 3, -} PreloadStatusCode; + kPreloadStateRemoved = 3, +} PreloadState; typedef enum { /** * 0: No error occurs and request succeeds. */ - kMusicContentCenterStatusOk = 0, + kMusicContentCenterReasonOk = 0, /** * 1: A general error occurs. */ - kMusicContentCenterStatusError = 1, + kMusicContentCenterReasonError = 1, /** * 2: The gateway error. There are several possible reasons: * - Token is expired. Check if your token is expired. * - Token is invalid. Check the type of token you passed in. * - Network error. Check your network. */ - kMusicContentCenterStatusGateway = 2, + kMusicContentCenterReasonGateway = 2, /** * 3: Permission and resource error. There are several possible reasons: * - Your appid may not have the mcc permission. Please contact technical support * - The resource may not exist. Please contact technical support */ - kMusicContentCenterStatusPermissionAndResource = 3, + kMusicContentCenterReasonPermissionAndResource = 3, /** * 4: Internal data parse error. Please contact technical support */ - kMusicContentCenterStatusInternalDataParse = 4, + kMusicContentCenterReasonInternalDataParse = 4, /** * 5: Music loading error. Please contact technical support */ - kMusicContentCenterStatusMusicLoading = 5, + kMusicContentCenterReasonMusicLoading = 5, /** * 6: Music decryption error. Please contact technical support */ - kMusicContentCenterStatusMusicDecryption = 6, + kMusicContentCenterReasonMusicDecryption = 6, /** * 7: Http internal error. Please retry later. */ - kMusicContentCenterStatusHttpInternalError = 7, -} MusicContentCenterStatusCode; + kMusicContentCenterReasonHttpInternalError = 7, +} MusicContentCenterStateReason; typedef struct { @@ -234,18 +256,18 @@ class IMusicContentCenterEventHandler { * * @param requestId The request id is same as that returned by getMusicCharts. * @param result The result of music chart collection - * @param status The status of the request. See MusicContentCenterStatusCode + * @param reason The status of the request. See MusicContentCenterStateReason */ - virtual void onMusicChartsResult(const char* requestId, agora_refptr result, MusicContentCenterStatusCode status) = 0; + virtual void onMusicChartsResult(const char* requestId, agora_refptr result, MusicContentCenterStateReason reason) = 0; /** * Music collection, occurs when getMusicCollectionByMusicChartId or searchMusic method is called. * * @param requestId The request id is same as that returned by getMusicCollectionByMusicChartId or searchMusic * @param result The result of music collection - * @param status The status of the request. See MusicContentCenterStatusCode + * @param reason The status of the request. See MusicContentCenterStateReason */ - virtual void onMusicCollectionResult(const char* requestId, agora_refptr result, MusicContentCenterStatusCode status) = 0; + virtual void onMusicCollectionResult(const char* requestId, agora_refptr result, MusicContentCenterStateReason reason) = 0; /** * Lyric url callback of getLyric, occurs when getLyric is called @@ -253,9 +275,9 @@ class IMusicContentCenterEventHandler { * @param requestId The request id is same as that returned by getLyric * @param songCode Song code * @param lyricUrl The lyric url of this music - * @param status The status of the request. See MusicContentCenterStatusCode + * @param reason The status of the request. See MusicContentCenterStateReason */ - virtual void onLyricResult(const char* requestId, int64_t songCode, const char* lyricUrl, MusicContentCenterStatusCode status) = 0; + virtual void onLyricResult(const char* requestId, int64_t songCode, const char* lyricUrl, MusicContentCenterStateReason reason) = 0; /** * Simple info callback of getSongSimpleInfo, occurs when getSongSimpleInfo is called @@ -263,9 +285,9 @@ class IMusicContentCenterEventHandler { * @param requestId The request id is same as that returned by getSongSimpleInfo. * @param songCode Song code * @param simpleInfo The metadata of the music. - * @param status The status of the request. See MusicContentCenterStatusCode + * @param reason The status of the request. See MusicContentCenterStateReason */ - virtual void onSongSimpleInfoResult(const char* requestId, int64_t songCode, const char* simpleInfo, MusicContentCenterStatusCode status) = 0; + virtual void onSongSimpleInfoResult(const char* requestId, int64_t songCode, const char* simpleInfo, MusicContentCenterStateReason reason) = 0; /** * Preload process callback, occurs when preload is called @@ -274,10 +296,10 @@ class IMusicContentCenterEventHandler { * @param songCode Song code * @param percent Preload progress (0 ~ 100) * @param lyricUrl The lyric url of this music - * @param preloadStatus Preload status; see PreloadStatusCode. - * @param mccStatus The status of the request. See MusicContentCenterStatusCode + * @param state Preload state; see PreloadState. + * @param reason The status of the request. See MusicContentCenterStateReason */ - virtual void onPreLoadEvent(const char* requestId, int64_t songCode, int percent, const char* lyricUrl, PreloadStatusCode preloadStatus, MusicContentCenterStatusCode mccStatus) = 0; + virtual void onPreLoadEvent(const char* requestId, int64_t songCode, int percent, const char* lyricUrl, PreloadState state, MusicContentCenterStateReason reason) = 0; virtual ~IMusicContentCenterEventHandler() {}; }; @@ -329,6 +351,18 @@ class IMusicPlayer : public IMediaPlayer { * - < 0: Failure. */ virtual int open(int64_t songCode, int64_t startPos = 0) = 0; + + /** + * Set the mode for playing songs. + * You can call this method to switch from original to accompaniment or lead vocals. + * If you do not call this method to set the mode, the SDK plays the accompaniment by default. + * + * @param model The playing mode. + * @return + * - 0: Success. + * - < 0: Failure. + */ + virtual int setPlayMode(MusicPlayMode mode) = 0; }; class IMusicContentCenter @@ -383,6 +417,15 @@ class IMusicContentCenter * - The empty pointer NULL, if the method call fails. */ virtual agora_refptr createMusicPlayer() = 0; + + /** + * Destroy a music player source object and return result. + * @param music_player The pointer to \ref rtc::IMusicPlayer "IMusicPlayer". + * @return + * - 0: Success. + * - < 0: Failure. + */ + virtual int destroyMusicPlayer(agora_refptr music_player) = 0; /** * Get music chart collection of music. @@ -501,12 +544,12 @@ class IMusicContentCenter * * @param requestId The request id you will get of this query, format is uuid. * @param songCode The identifier of the media file that you want to play. - * @param LyricType The type of the lyric file. 0:xml or 1:lrc. + * @param lyricType The type of the lyric file. 0:xml or 1:lrc. * @return * - 0: Success. * - < 0: Failure. */ - virtual int getLyric(agora::util::AString& requestId, int64_t songCode, int32_t LyricType = 0) = 0; + virtual int getLyric(agora::util::AString& requestId, int64_t songCode, int32_t lyricType = 0) = 0; /** * Gets the metadata of a specific music. Once this method is called, the SDK triggers the onSongSimpleInfoResult callback to report the metadata of the music. diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/IAgoraParameter.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/IAgoraParameter.h index b88969e1d..f50afe9b5 100644 --- a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/IAgoraParameter.h +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/IAgoraParameter.h @@ -39,8 +39,7 @@ * set the video encoder mode (hardware or software) */ #define KEY_RTC_VIDEO_ENABLED_HW_ENCODER "engine.video.enable_hw_encoder" -#define KEY_RTC_VIDEO_HARDWARE_ENCODEING "che.hardware_encoding" -#define KEY_RTC_VIDEO_H264_HWENC "che.video.h264.hwenc" +#define KEY_RTC_VIDEO_HARDWARE_ENCODEING "che.hardware_encoding" // deprecated, please use engine.video.enable_hw_encoder /** * set the hardware video encoder provider (nv for nvidia or qsv for intel) */ @@ -50,7 +49,7 @@ * set the video decoder mode (hardware or software) */ #define KEY_RTC_VIDEO_ENABLED_HW_DECODER "engine.video.enable_hw_decoder" -#define KEY_RTC_VIDEO_HARDWARE_DECODING "che.hardware_decoding" +#define KEY_RTC_VIDEO_HARDWARE_DECODING "che.hardware_decoding" // deprecated, please use engine.video.enable_hw_decoder /** * set the hardware video decoder provider (h264_cuvid(default) or h264_qsv) @@ -115,8 +114,7 @@ /** * set the video codec type, such as "H264", "JPEG" */ -#define KEY_RTC_VIDEO_CODEC_TYPE "engine.video.codec_type" -#define KEY_RTC_VIDEO_MINOR_STREAM_CODEC_TYPE "engine.video.minor_stream_codec_type" +#define KEY_RTC_VIDEO_MINOR_STREAM_CODEC_INDEX "engine.video.minor_stream_codec_index" #define KEY_RTC_VIDEO_CODEC_INDEX "che.video.videoCodecIndex" /** * only use average QP for quality scaling diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/IAgoraRtcEngine.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/IAgoraRtcEngine.h index f7e8999b2..5e075327f 100644 --- a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/IAgoraRtcEngine.h +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/IAgoraRtcEngine.h @@ -230,19 +230,30 @@ enum AUDIO_REVERB_TYPE { }; enum STREAM_FALLBACK_OPTIONS { - /** 0: No fallback operation for the stream when the network - condition is poor. The stream quality cannot be guaranteed. */ - + /** 0: No fallback operation to a lower resolution stream when the network + condition is poor. Fallback to Scalable Video Coding (e.g. SVC) + is still possible, but the resolution remains in high stream. + The stream quality cannot be guaranteed. */ STREAM_FALLBACK_OPTION_DISABLED = 0, - /** 1: (Default) Under poor network conditions, the SDK will send or receive + /** 1: (Default) Under poor network conditions, the receiver SDK will receive agora::rtc::VIDEO_STREAM_LOW. You can only set this option in RtcEngineParameters::setRemoteSubscribeFallbackOption. Nothing happens when you set this in RtcEngineParameters::setLocalPublishFallbackOption. */ STREAM_FALLBACK_OPTION_VIDEO_STREAM_LOW = 1, - /** 2: Under poor network conditions, the SDK may receive - agora::rtc::VIDEO_STREAM_LOW first, but if the network still does - not allow displaying the video, the SDK will send or receive audio only. */ + /** 2: Under poor network conditions, the SDK may receive agora::rtc::VIDEO_STREAM_LOW first, + then agora::rtc::VIDEO_STREAM_LAYER_1 to agora::rtc::VIDEO_STREAM_LAYER_6 if the related layer exists. + If the network still does not allow displaying the video, the SDK will receive audio only. */ STREAM_FALLBACK_OPTION_AUDIO_ONLY = 2, + /** 3~8: If the receiver SDK uses RtcEngineParameters::setRemoteSubscribeFallbackOption,it will receive + one of the streams from agora::rtc::VIDEO_STREAM_LAYER_1 to agora::rtc::VIDEO_STREAM_LAYER_6 + if the related layer exists when the network condition is poor. The lower bound of fallback depends on + the STREAM_FALLBACK_OPTION_VIDEO_STREAM_LAYER_X. */ + STREAM_FALLBACK_OPTION_VIDEO_STREAM_LAYER_1 = 3, + STREAM_FALLBACK_OPTION_VIDEO_STREAM_LAYER_2 = 4, + STREAM_FALLBACK_OPTION_VIDEO_STREAM_LAYER_3 = 5, + STREAM_FALLBACK_OPTION_VIDEO_STREAM_LAYER_4 = 6, + STREAM_FALLBACK_OPTION_VIDEO_STREAM_LAYER_5 = 7, + STREAM_FALLBACK_OPTION_VIDEO_STREAM_LAYER_6 = 8, }; enum PRIORITY_TYPE { @@ -341,6 +352,9 @@ struct LocalVideoStats * - hardware = 1. */ int hwEncoderAccelerating; + /** The dimensions of the simulcast streams's encoding frame. + */ + VideoDimensions simulcastDimensions[SimulcastConfig::STREAM_LAYER_COUNT_MAX]; }; /** @@ -443,6 +457,10 @@ struct RemoteAudioStats * The total number of audio bytes received (bytes), inluding the FEC bytes, represented by an aggregate value. */ unsigned int rxAudioBytes; + /** + * The end-to-end delay (ms) from the sender to the receiver. + */ + int e2eDelay; RemoteAudioStats() : uid(0), @@ -462,7 +480,8 @@ struct RemoteAudioStats publishDuration(0), qoeQuality(0), qualityChangedReason(0), - rxAudioBytes(0) {} + rxAudioBytes(0), + e2eDelay(0) {} }; /** @@ -497,6 +516,9 @@ struct RemoteVideoStats { * Bitrate (Kbps) received since the last count. */ int receivedBitrate; + /** The decoder input frame rate (fps) of the remote video. + */ + int decoderInputFrameRate; /** The decoder output frame rate (fps) of the remote video. */ int decoderOutputFrameRate; @@ -792,21 +814,32 @@ struct CameraCapturerConfiguration { /** * The camera direction. */ - CAMERA_DIRECTION cameraDirection; + Optional cameraDirection; + + /*- CAMERA_FOCAL_LENGTH_TYPE.CAMERA_FOCAL_LENGTH_DEFAULT: + For iOS, if iPhone/iPad has 3 or 2 back camera, it means combination of triple (wide + ultra wide + telephoto) camera + or dual wide(wide + ultra wide) camera.In this situation, you can apply for ultra wide len by set smaller zoom fator + and bigger zoom fator for telephoto len.Otherwise, it always means wide back/front camera. + + - CAMERA_FOCAL_LENGTH_TYPE.CAMERA_FOCAL_LENGTH_WIDE_ANGLE:wide camera + - CAMERA_FOCAL_LENGTH_TYPE.CAMERA_FOCAL_LENGTH_ULTRA_WIDE:ultra wide camera + - CAMERA_FOCAL_LENGTH_TYPE.CAMERA_FOCAL_LENGTH_TELEPHOTO:telephoto camera*/ + Optional cameraFocalLengthType; #else - /** For windows. The device ID of the playback device. The maximum length is #MAX_DEVICE_ID_LENGTH. */ - char deviceId[MAX_DEVICE_ID_LENGTH]; + /** For windows. The device ID of the playback device. */ + Optional deviceId; #endif - /** The video format. See VideoFormat. */ - VideoFormat format; - bool followEncodeDimensionRatio; - CameraCapturerConfiguration() : followEncodeDimensionRatio(true) { -#if defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IOS) - cameraDirection = CAMERA_REAR; -#else - memset(deviceId, 0, sizeof(deviceId)); + +#if defined(__ANDROID__) + /** + * The camera id. + */ + Optional cameraId; #endif - } + Optional followEncodeDimensionRatio; + /** The video format. See VideoFormat. */ + VideoFormat format; + CameraCapturerConfiguration() : format(VideoFormat(0, 0, 0)) {} }; /** * The configuration of the captured screen. @@ -1140,6 +1173,12 @@ struct ChannelMediaOptions { * - `false`: (Default) Do not publish the local mixed track. */ Optional publishMixedAudioTrack; + /** + * Whether to publish the local lip sync video track. + * - `true`: Publish the video track of local lip sync video track. + * - `false`: (Default) Do not publish the local lip sync video track. + */ + Optional publishLipSyncTrack; /** * Whether to automatically subscribe to all remote audio streams when the user joins a channel: * - `true`: (Default) Subscribe to all remote audio streams. @@ -1234,6 +1273,12 @@ struct ChannelMediaOptions { */ Optional isAudioFilterable; + /** Provides the technical preview functionalities or special customizations by configuring the SDK with JSON options. + Pointer to the set parameters in a JSON string. + * @technical preview + */ + Optional parameters; + ChannelMediaOptions() {} ~ChannelMediaOptions() {} @@ -1256,6 +1301,7 @@ struct ChannelMediaOptions { #endif SET_FROM(publishTranscodedVideoTrack); SET_FROM(publishMixedAudioTrack); + SET_FROM(publishLipSyncTrack); SET_FROM(publishCustomAudioTrack); SET_FROM(publishCustomAudioTrackId); SET_FROM(publishCustomVideoTrack); @@ -1278,6 +1324,7 @@ struct ChannelMediaOptions { SET_FROM(customVideoTrackId); SET_FROM(isAudioFilterable); SET_FROM(isInteractiveAudience); + SET_FROM(parameters); #undef SET_FROM } @@ -1303,6 +1350,7 @@ struct ChannelMediaOptions { #endif ADD_COMPARE(publishTranscodedVideoTrack); ADD_COMPARE(publishMixedAudioTrack); + ADD_COMPARE(publishLipSyncTrack); ADD_COMPARE(publishCustomAudioTrack); ADD_COMPARE(publishCustomAudioTrackId); ADD_COMPARE(publishCustomVideoTrack); @@ -1325,6 +1373,7 @@ struct ChannelMediaOptions { ADD_COMPARE(customVideoTrackId); ADD_COMPARE(isAudioFilterable); ADD_COMPARE(isInteractiveAudience); + ADD_COMPARE(parameters); END_COMPARE(); #undef BEGIN_COMPARE @@ -1353,6 +1402,7 @@ struct ChannelMediaOptions { #endif REPLACE_BY(publishTranscodedVideoTrack); REPLACE_BY(publishMixedAudioTrack); + REPLACE_BY(publishLipSyncTrack); REPLACE_BY(publishCustomAudioTrack); REPLACE_BY(publishCustomAudioTrackId); REPLACE_BY(publishCustomVideoTrack); @@ -1375,6 +1425,7 @@ struct ChannelMediaOptions { REPLACE_BY(customVideoTrackId); REPLACE_BY(isAudioFilterable); REPLACE_BY(isInteractiveAudience); + REPLACE_BY(parameters); #undef REPLACE_BY } return *this; @@ -2826,63 +2877,69 @@ class IRtcEngineEventHandler { (void)layoutlist; } + /** + * Occurs when the SDK receives audio metadata. + * @since v4.3.1 + * @param uid ID of the remote user. + * @param metadata The pointer of metadata + * @param length Size of metadata + * @technical preview + */ + virtual void onAudioMetadataReceived(uid_t uid, const char* metadata, size_t length) { + (void)uid; + (void)metadata; + (void)length; + } + /** * The event callback of the extension. * * To listen for events while the extension is running, you need to register this callback. - * - * @param provider The name of the extension provider. - * @param extension The name of the extension. + * + * @param context The context of the extension. * @param key The key of the extension. * @param value The value of the extension key. */ - virtual void onExtensionEvent(const char* provider, const char* extension, const char* key, const char* value) { - (void)provider; - (void)extension; + virtual void onExtensionEventWithContext(const ExtensionContext &context, const char* key, const char* value) { + (void)context; (void)key; (void)value; } /** * Occurs when the extension is enabled. - * - * After a successful call of `enableExtension(true)`, the extension triggers this callback. - * - * @param provider The name of the extension provider. - * @param extension The name of the extension. + * + * After a successful creation of filter , the extension triggers this callback. + * + * @param context The context of the extension. */ - virtual void onExtensionStarted(const char* provider, const char* extension) { - (void)provider; - (void)extension; + virtual void onExtensionStartedWithContext(const ExtensionContext &context) { + (void)context; } /** * Occurs when the extension is disabled. - * - * After a successful call of `enableExtension(false)`, the extension triggers this callback. - * - * @param provider The name of the extension provider. - * @param extension The name of the extension. + * + * After a successful destroy filter, the extension triggers this callback. + * + * @param context The context of the extension. */ - virtual void onExtensionStopped(const char* provider, const char* extension) { - (void)provider; - (void)extension; + virtual void onExtensionStoppedWithContext(const ExtensionContext &context) { + (void)context; } /** * Occurs when the extension runs incorrectly. - * - * When calling `enableExtension(true)` fails or the extension runs in error, the extension triggers + * + * When the extension runs in error, the extension triggers * this callback and reports the error code and reason. * - * @param provider The name of the extension provider. - * @param extension The name of the extension. + * @param context The context of the extension. * @param error The error code. For details, see the extension documentation provided by the extension provider. * @param message The error message. For details, see the extension documentation provided by the extension provider. */ - virtual void onExtensionError(const char* provider, const char* extension, int error, const char* message) { - (void)provider; - (void)extension; + virtual void onExtensionErrorWithContext(const ExtensionContext &context, int error, const char* message) { + (void)context; (void)error; (void)message; } @@ -3185,23 +3242,26 @@ class IMetadataObserver { */ struct Metadata { - /** The User ID that sent the metadata. - * - For the receiver: The user ID of the user who sent the `metadata`. - * - For the sender: Ignore this value. - */ - unsigned int uid; - /** The buffer size of the sent or received `metadata`. - */ - unsigned int size; - /** The buffer address of the sent or received `metadata`. - */ - unsigned char* buffer; - /** The timestamp (ms) of the `metadata`. - * - */ - long long timeStampMs; - - Metadata() : uid(0), size(0), buffer(NULL), timeStampMs(0) {} + /** The channel ID of the `metadata`. + */ + const char* channelId; + /** The User ID that sent the metadata. + * - For the receiver: The user ID of the user who sent the `metadata`. + * - For the sender: Ignore this value. + */ + unsigned int uid; + /** The buffer size of the sent or received `metadata`. + */ + unsigned int size; + /** The buffer address of the sent or received `metadata`. + */ + unsigned char *buffer; + /** The NTP timestamp (ms) when the metadata is sent. + * @note If the receiver is audience, the receiver cannot get the NTP timestamp (ms). + */ + long long timeStampMs; + + Metadata() : channelId(NULL), uid(0), size(0), buffer(NULL), timeStampMs(0) {} }; /** Occurs when the SDK requests the maximum size of the metadata. @@ -3488,14 +3548,12 @@ class IRtcEngine : public agora::base::IEngineBase { * @param sync Determines whether this method is a synchronous call. * - `true`: This method is a synchronous call, which means that the result of this method call * returns after the IRtcEngine object resources are released. Do not call this method - * in any callback generated by the SDK, or it may result in a deadlock. The SDK automatically - * detects the deadlock and turns this method into an asynchronous call, but the test itself takes - * extra time. + * in any callback generated by the SDK, or it may result in a deadlock. * - `false`: This method is an asynchronous call. The result returns immediately even when the * IRtcEngine object resources are not released. * */ - virtual void release(bool sync = false) = 0; + AGORA_CPP_API static void release(bool sync = false); /** * Initializes `IRtcEngine`. @@ -3877,6 +3935,7 @@ class IRtcEngine : public agora::base::IEngineBase { * @return * - 0: Success. * - < 0: Failure. + * - -8(ERR_INVALID_STATE): The current status is invalid, only allowed to be called when the connection is disconnected. */ virtual int setChannelProfile(CHANNEL_PROFILE_TYPE profile) = 0; @@ -3925,48 +3984,10 @@ class IRtcEngine : public agora::base::IEngineBase { * - -1(ERR_FAILED): A general error occurs (no specified reason). * - -2(ERR_INALID_ARGUMENT): The parameter is invalid. * - -7(ERR_NOT_INITIALIZED): The SDK is not initialized. + * - -8(ERR_INVALID_STATE): The channel profile is not `LIVE_BROADCASTING`. */ virtual int setClientRole(CLIENT_ROLE_TYPE role, const ClientRoleOptions& options) = 0; - /** Starts an audio call test. - - This method launches an audio call test to determine whether the audio devices - (for example, headset and speaker) and the network connection are working - properly. - - In the test, the user first speaks, and the recording is played back - in 10 seconds. If the user can hear the recording in 10 seconds, it indicates - that the audio devices and network connection work properly. - - @note - After calling the startEchoTest() method, always call stopEchoTest() to end - the test. Otherwise, the app cannot run the next echo test, nor can - it call the joinChannel() method. - - @return - - 0: Success. - - < 0: Failure. - */ - virtual int startEchoTest() = 0; - - /** Starts an audio call test. - - This method starts an audio call test to determine whether the audio devices (for example, headset and speaker) and the network connection are working properly. - - In the audio call test, you record your voice. If the recording plays back within the set time interval, the audio devices and the network connection are working properly. - - @note - - Call this method before joining a channel. - - After calling this method, call the \ref IRtcEngine::stopEchoTest "stopEchoTest" method to end the test. Otherwise, the app cannot run the next echo test, or call the \ref IRtcEngine::joinChannel "joinChannel" method. - - In the `LIVE_BROADCASTING` profile, only a host can call this method. - @param intervalInSeconds The time interval (s) between when you speak and when the recording plays back. - - @return - - 0: Success. - - < 0: Failure. - */ - virtual int startEchoTest(int intervalInSeconds) = 0; - /** Starts a video call test. * * @param config: configuration for video call test. @@ -4131,6 +4152,41 @@ class IRtcEngine : public agora::base::IEngineBase { * @param options Sets the image enhancement option. See BeautyOptions. */ virtual int setBeautyEffectOptions(bool enabled, const BeautyOptions& options, agora::media::MEDIA_SOURCE_TYPE type = agora::media::PRIMARY_CAMERA_SOURCE) = 0; + /** Enables/Disables face shape and sets the beauty options. + * + * @note Call this method after calling the \ref IRtcEngine::enableVideo "enableVideo" method. + * + * @param enabled Sets whether or not to enable face shape: + * - true: enables face shape. + * - false: disables face shape. + * @param options Sets the face shape beauty option. See FaceShapeBeautyOptions. + */ + virtual int setFaceShapeBeautyOptions(bool enabled, const FaceShapeBeautyOptions& options, agora::media::MEDIA_SOURCE_TYPE type = agora::media::PRIMARY_CAMERA_SOURCE) = 0; + /** Enables/Disables face shape and sets the area options. + * + * @note Call this method after calling the \ref IRtcEngine::setFaceShapeBeautyOptions "setFaceShapeBeautyOptions" method. + * + * @param options Sets the face shape area option. See FaceShapeAreaOptions. + */ + virtual int setFaceShapeAreaOptions(const FaceShapeAreaOptions& options, agora::media::MEDIA_SOURCE_TYPE type = agora::media::PRIMARY_CAMERA_SOURCE) = 0; + + /** Gets the face shape beauty options. + * + * @note Call this method after calling the \ref IRtcEngine::enableVideo "enableVideo" method. + * + * @param options Gets the face shape beauty option. See FaceShapeBeautyOptions. + */ + virtual int getFaceShapeBeautyOptions(FaceShapeBeautyOptions& options, agora::media::MEDIA_SOURCE_TYPE type = agora::media::PRIMARY_CAMERA_SOURCE) = 0; + + /** Gets the face shape area options. + * + * @note Call this method after calling the \ref IRtcEngine::enableVideo "enableVideo" method. + * + * @param shapeArea The face area. See FaceShapeAreaOptions::FACE_SHAPE_AREA. + * @param options Gets the face area beauty option. See FaceShapeAreaOptions. + */ + virtual int getFaceShapeAreaOptions(agora::rtc::FaceShapeAreaOptions::FACE_SHAPE_AREA shapeArea, FaceShapeAreaOptions& options, agora::media::MEDIA_SOURCE_TYPE type = agora::media::PRIMARY_CAMERA_SOURCE) = 0; + /** * Sets low-light enhancement. * @@ -4499,29 +4555,6 @@ class IRtcEngine : public agora::base::IEngineBase { */ virtual int muteAllRemoteAudioStreams(bool mute) = 0; - /** - * Determines whether to receive all remote audio streams by default. - * - * @deprecated This method is deprecated. To set whether to receive remote - * audio streams by default, call - * \ref IRtcEngine::muteAllRemoteAudioStreams "muteAllRemoteAudioStreams" - * before calling `joinChannel` - * - * Use this method to set whether to receive audio streams of subsequent peer - * users. Agora recommends calling it before joining a channel. - * - * A successful call of setDefaultMuteAllRemoteAudioStreams(true) results in - * that the local user not receiving any audio stream after joining a channel. - * @param mute Whether to receive remote audio streams by default: - * - true: Do not receive any remote audio stream by default. - * - false: (Default) Receive remote audio streams by default. - * - * @return int - * - 0: Success. - * - < 0: Failure. - */ - virtual int setDefaultMuteAllRemoteAudioStreams(bool mute) __deprecated = 0; - /** * Stops or resumes receiving the audio stream of a specified user. * @@ -4620,29 +4653,6 @@ class IRtcEngine : public agora::base::IEngineBase { */ virtual int muteAllRemoteVideoStreams(bool mute) = 0; - /** - Determines whether to receive all remote video streams by default. - - @deprecated This method is deprecated. To set whether to receive remote - video streams by default, call - \ref IRtcEngine::muteAllRemoteVideoStreams "muteAllRemoteVideoStreams" - before calling `joinChannel`. - - Use this method to set whether to receive video streams of subsequent peer - users. Agora recommends calling it before joining a channel. - - A successful call of setDefaultMuteAllRemoteVideoStreams(true) results in - that the local user not receiving any video stream after joining a channel. - - @param mute Whether to receive remote video streams by default: - - true: Do not receive any remote video stream by default. - - false: (Default) Receive remote video streams by default. - @return int - - 0: Success. - - < 0: Failure. - */ - virtual int setDefaultMuteAllRemoteVideoStreams(bool mute) __deprecated = 0; - /** * Sets the default stream type of the remote video if the remote user has enabled dual-stream. * @@ -5181,6 +5191,24 @@ class IRtcEngine : public agora::base::IEngineBase { * - < 0: Failure. */ virtual int setAudioMixingPitch(int pitch) = 0; + + /** + * Sets the playback speed of the current music file. + * + * @note Call this method after calling \ref IRtcEngine::startAudioMixing(const char*,bool,bool,int,int) "startAudioMixing" [2/2] + * and receiving the \ref IRtcEngineEventHandler::onAudioMixingStateChanged "onAudioMixingStateChanged" (AUDIO_MIXING_STATE_PLAYING) callback. + * + * @param speed The playback speed. Agora recommends that you limit this value to between 50 and 400, defined as follows: + * - 50: Half the original speed. + * - 100: The original speed. + * - 400: 4 times the original speed. + * + * @return + * - 0: Success. + * - < 0: Failure. + */ + virtual int setAudioMixingPlaybackSpeed(int speed) = 0; + /** * Gets the volume of audio effects. * @@ -5815,6 +5843,19 @@ class IRtcEngine : public agora::base::IEngineBase { */ virtual int setHeadphoneEQParameters(int lowGain, int highGain) = 0; + /** Enables or disables the voice AI tuner. + * + * @param enabled Determines whether to enable the voice AI tuner: + * - true: Enable the voice AI tuner + * - false: (default) Disable the voice AI tuner. + * + * @param type. The options for SDK voice AI tuner types. See #VOICE_AI_TUNER_TYPE. + * @return + * - 0: Success. + * - < 0: Failure. + */ + virtual int enableVoiceAITuner(bool enabled, VOICE_AI_TUNER_TYPE type) = 0; + /** **DEPRECATED** Specifies an SDK output log file. * * The log file records all log data for the SDK’s operation. Ensure that the @@ -5900,6 +5941,23 @@ class IRtcEngine : public agora::base::IEngineBase { */ virtual int uploadLogFile(agora::util::AString& requestId) = 0; + /** * Write the log to SDK . @technical preview + * + * You can Write the log to SDK log files of the specified level. + * + * @param level The log level: + * - `LOG_LEVEL_NONE (0x0000)`: Do not output any log file. + * - `LOG_LEVEL_INFO (0x0001)`: (Recommended) Output log files of the INFO level. + * - `LOG_LEVEL_WARN (0x0002)`: Output log files of the WARN level. + * - `LOG_LEVEL_ERROR (0x0004)`: Output log files of the ERROR level. + * - `LOG_LEVEL_FATAL (0x0008)`: Output log files of the FATAL level. + * + * @return + * - 0: Success. + * - < 0: Failure. + */ + virtual int writeLog(commons::LOG_LEVEL level, const char* fmt, ...) = 0; + /** * Updates the display mode of the local video view. * @@ -5962,7 +6020,7 @@ class IRtcEngine : public agora::base::IEngineBase { * - 0: Success. * - < 0: Failure. */ - virtual int setLocalRenderMode(media::base::RENDER_MODE_TYPE renderMode) = 0; + virtual int setLocalRenderMode(media::base::RENDER_MODE_TYPE renderMode) __deprecated = 0; /** * Sets the local video mirror mode. @@ -5975,7 +6033,7 @@ class IRtcEngine : public agora::base::IEngineBase { * - 0: Success. * - < 0: Failure. */ - virtual int setLocalVideoMirrorMode(VIDEO_MIRROR_MODE_TYPE mirrorMode) = 0; + virtual int setLocalVideoMirrorMode(VIDEO_MIRROR_MODE_TYPE mirrorMode) __deprecated = 0; /** * Enables or disables the dual video stream mode. @@ -6027,6 +6085,23 @@ class IRtcEngine : public agora::base::IEngineBase { */ virtual int setDualStreamMode(SIMULCAST_STREAM_MODE mode) = 0; + /** + * Sets the multi-layer video stream configuration. + * + * If multi-layer is configured, the subscriber can choose to receive the coresponding layer + * of video stream using {@link setRemoteVideoStreamType setRemoteVideoStreamType}. + * + * @param simulcastConfig + * - The configuration for multi-layer video stream. It includes seven layers, ranging from + * STREAM_LAYER_1 to STREAM_LOW. A maximum of 3 layers can be enabled simultaneously. + * + * @return + * - 0: Success. + * - < 0: Failure. + * @technical preview + */ + virtual int setSimulcastConfig(const SimulcastConfig& simulcastConfig) = 0; + /** * Enables, disables or auto enable the dual video stream mode. * @@ -6771,6 +6846,13 @@ class IRtcEngine : public agora::base::IEngineBase { * - false: Do not enable the auto exposure face function. */ virtual int setCameraAutoExposureFaceModeEnabled(bool enabled) = 0; + + /** + * set camera stabilization mode.If open stabilization mode, fov will be smaller and capture latency will be longer. + * + * @param mode specifies the camera stabilization mode. + */ + virtual int setCameraStabilizationMode(CAMERA_STABILIZATION_MODE mode) = 0; #endif /** Sets the default audio route (for Android and iOS only). @@ -6855,6 +6937,27 @@ class IRtcEngine : public agora::base::IEngineBase { #endif // __ANDROID__ || (__APPLE__ && TARGET_OS_IOS) +#if defined(__APPLE__) + /** + * Checks whether the center stage is supported. Use this method after starting the camera. + * + * @return + * - true: The center stage is supported. + * - false: The center stage is not supported. + */ + virtual bool isCameraCenterStageSupported() = 0; + + /** Enables the camera Center Stage. + * @param enabled enable Center Stage: + * - true: Enable Center Stage. + * - false: Disable Center Stage. + * @return + * - 0: Success. + * - < 0: Failure. + */ + virtual int enableCameraCenterStage(bool enabled) = 0; +#endif + #if defined(_WIN32) || (defined(__APPLE__) && TARGET_OS_MAC && !TARGET_OS_IPHONE) /** Get \ref ScreenCaptureSourceInfo list including available windows and screens. * @@ -6960,7 +7063,6 @@ class IRtcEngine : public agora::base::IEngineBase { * - < 0: Failure.. */ virtual int getAudioDeviceInfo(DeviceInfo& deviceInfo) = 0; - #endif // __ANDROID__ #if defined(_WIN32) || (defined(__APPLE__) && TARGET_OS_MAC && !TARGET_OS_IPHONE) @@ -7071,6 +7173,19 @@ class IRtcEngine : public agora::base::IEngineBase { * - < 0: Failure. */ virtual int queryScreenCaptureCapability() = 0; + + /** + * Query all focal attributes supported by the camera. + * + * @param focalLengthInfos The camera supports the collection of focal segments.Ensure the size of array is not less than 8. + * + * @param size The camera supports the size of the focal segment set. Ensure the size is not less than 8. + * + * @return + * - 0: Success. + * - < 0: Failure.. + */ + virtual int queryCameraFocalLengthCapability(agora::rtc::FocalLengthInfo* focalLengthInfos, int& size) = 0; #endif #if defined(_WIN32) || defined(__APPLE__) || defined(__ANDROID__) @@ -7325,47 +7440,6 @@ class IRtcEngine : public agora::base::IEngineBase { */ virtual int registerPacketObserver(IPacketObserver* observer) = 0; - /** - * Sets the built-in encryption mode. - * - * @deprecated This method is deprecated. Use enableEncryption(bool enabled, const EncryptionConfig&) instead. - * - * The Agora Native SDK supports built-in encryption. - * Call this API to set the encryption mode. - * - * All users in the same channel must use the same encryption mode and password. - * Refer to information related to the encryption algorithm on the differences - * between encryption modes. - * - * @note - * Call \ref setEncryptionSecret "setEncryptionSecret" to enable the built-in encryption function - * before calling this API. - * @param encryptionMode Encryption mode: - * - "sm4-128-ecb": 128-bit SM4 encryption, ECB mode. - * - * @return - * - 0: Success. - * - < 0: Failure. - */ - virtual int setEncryptionMode(const char* encryptionMode) __deprecated = 0; - - /** - * Enables built-in encryption. - * - * @deprecated This method is deprecated. Use enableEncryption(bool enabled, const EncryptionConfig&) instead. - * - * Use this method to specify an encryption password to enable built-in - * encryption before joining a channel. All users in a channel must set the same - * encryption password. The encryption password is automatically cleared once a - * user has left the channel. If the encryption password is not specified or set to - * empty, the encryption function will be disabled. - * - * @param secret The encryption password. - * @return - * - 0: Success. - * - < 0: Failure. - */ - virtual int setEncryptionSecret(const char* secret) __deprecated = 0; /** Enables/Disables the built-in encryption. * @@ -8246,6 +8320,18 @@ class IRtcEngine : public agora::base::IEngineBase { * - false: not available. */ virtual bool isFeatureAvailableOnDevice(FeatureType type) = 0; + + /** + * @brief send audio metadata + * @since v4.3.1 + * @param metadata The pointer of metadata + * @param length Size of metadata + * @return + * - 0: success + * - <0: failure + * @technical preview + */ + virtual int sendAudioMetadata(const char* metadata, size_t length) = 0; }; // The following types are either deprecated or not implmented yet. diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/IAgoraRtcEngineEx.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/IAgoraRtcEngineEx.h index e9826d78f..a3d7f9858 100644 --- a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/IAgoraRtcEngineEx.h +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/IAgoraRtcEngineEx.h @@ -101,6 +101,7 @@ class IRtcEngineEventHandlerEx : public IRtcEngineEventHandler { using IRtcEngineEventHandler::onVideoRenderingTracingResult; using IRtcEngineEventHandler::onSetRtmFlagResult; using IRtcEngineEventHandler::onTranscodedStreamLayoutInfo; + using IRtcEngineEventHandler::onAudioMetadataReceived; virtual const char* eventHandlerType() const { return "event_handler_ex"; } @@ -317,40 +318,6 @@ class IRtcEngineEventHandlerEx : public IRtcEngineEventHandler { (void)rotation; } - /** Occurs when the local video stream state changes. - * - * When the state of the local video stream changes (including the state of the video capture and - * encoding), the SDK triggers this callback to report the current state. This callback indicates - * the state of the local video stream, including camera capturing and video encoding, and allows - * you to troubleshoot issues when exceptions occur. - * - * The SDK triggers the onLocalVideoStateChanged callback with the state code of `LOCAL_VIDEO_STREAM_STATE_FAILED` - * and error code of `LOCAL_VIDEO_STREAM_REASON_CAPTURE_FAILURE` in the following situations: - * - The app switches to the background, and the system gets the camera resource. - * - The camera starts normally, but does not output video for four consecutive seconds. - * - * When the camera outputs the captured video frames, if the video frames are the same for 15 - * consecutive frames, the SDK triggers the `onLocalVideoStateChanged` callback with the state code - * of `LOCAL_VIDEO_STREAM_STATE_CAPTURING` and error code of `LOCAL_VIDEO_STREAM_REASON_CAPTURE_FAILURE`. - * Note that the video frame duplication detection is only available for video frames with a resolution - * greater than 200 × 200, a frame rate greater than or equal to 10 fps, and a bitrate less than 20 Kbps. - * - * @note For some device models, the SDK does not trigger this callback when the state of the local - * video changes while the local video capturing device is in use, so you have to make your own - * timeout judgment. - * - * @param connection The RtcConnection object. - * @param state The state of the local video. See #LOCAL_VIDEO_STREAM_STATE. - * @param reason The detailed error information. See #LOCAL_VIDEO_STREAM_REASON. - */ - virtual void onLocalVideoStateChanged(const RtcConnection& connection, - LOCAL_VIDEO_STREAM_STATE state, - LOCAL_VIDEO_STREAM_REASON reason) { - (void)connection; - (void)state; - (void)reason; - } - /** * Occurs when the remote video state changes. * @@ -1059,6 +1026,20 @@ class IRtcEngineEventHandlerEx : public IRtcEngineEventHandler { (void)layoutCount; (void)layoutlist; } + + /** + * The audio metadata received. + * + * @param connection The RtcConnection object. + * @param uid ID of the remote user. + * @param metadata The pointer of metadata + * @param length Size of metadata + * @technical preview + */ + virtual void onAudioMetadataReceived(const RtcConnection& connection, uid_t uid, const char* metadata, size_t length) { + (void)metadata; + (void)length; + } }; class IRtcEngineEx : public IRtcEngine { @@ -1864,6 +1845,24 @@ class IRtcEngineEx : public IRtcEngine { virtual int setDualStreamModeEx(SIMULCAST_STREAM_MODE mode, const SimulcastStreamConfig& streamConfig, const RtcConnection& connection) = 0; + + /** + * Set the multi-layer video stream configuration. + * + * If multi-layer is configed, the subscriber can choose to receive the coresponding layer + * of video stream using {@link setRemoteVideoStreamType setRemoteVideoStreamType}. + * + * @param simulcastConfig + * - The configuration for multi-layer video stream. It includes seven layers, ranging from + * STREAM_LAYER_1 to STREAM_LOW. A maximum of 3 layers can be enabled simultaneously. + * @param connection The RtcConnection object. + * @return + * - 0: Success. + * - < 0: Failure. + * @technical preview + */ + virtual int setSimulcastConfigEx(const SimulcastConfig& simulcastConfig, + const RtcConnection& connection) = 0; /** * Set the high priority user list and their fallback level in weak network condition. @@ -1955,6 +1954,38 @@ class IRtcEngineEx : public IRtcEngine { - < 0: Failure. */ virtual int setParametersEx(const RtcConnection& connection, const char* parameters) = 0; + + /** + * Gets the current call ID. + * + * When a user joins a channel on a client, a `callId` is generated to identify + * the call. + * + * After a call ends, you can call `rate` or `complain` to gather feedback from the customer. + * These methods require a `callId` parameter. To use these feedback methods, call the this + * method first to retrieve the `callId` during the call, and then pass the value as an + * argument in the `rate` or `complain` method after the call ends. + * + * @param callId The reference to the call ID. + * @param connection The RtcConnection object. + * @return + * - The call ID if the method call is successful. + * - < 0: Failure. + */ + virtual int getCallIdEx(agora::util::AString& callId, const RtcConnection& connection) = 0; + + /** + * send audio metadata + * @since v4.3.1 + * @param connection The RtcConnection object. + * @param metadata The pointer of metadata + * @param length Size of metadata + * @return + * - 0: success + * - <0: failure + * @technical preview + */ + virtual int sendAudioMetadataEx(const RtcConnection& connection, const char* metadata, size_t length) = 0; }; } // namespace rtc diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/IAudioDeviceManager.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/IAudioDeviceManager.h index 77667b827..6537f0990 100644 --- a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/IAudioDeviceManager.h +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/IAudioDeviceManager.h @@ -50,6 +50,20 @@ class IAudioDeviceCollection { virtual int getDevice(int index, char deviceName[MAX_DEVICE_ID_LENGTH], char deviceId[MAX_DEVICE_ID_LENGTH]) = 0; + /** + * Gets the information of a specified audio device. + * @note + * @param index An input parameter that specifies the audio device. + * @param deviceName An output parameter that indicates the device name. + * @param deviceTypeName An output parameter that indicates the device type name. such as Built-in, USB, HDMI, etc. (MacOS only) + * @param deviceId An output parameter that indicates the device ID. + * @return + * - 0: Success. + * - < 0: Failure. + */ + virtual int getDevice(int index, char deviceName[MAX_DEVICE_ID_LENGTH], char deviceTypeName[MAX_DEVICE_ID_LENGTH], + char deviceId[MAX_DEVICE_ID_LENGTH]) = 0; + /** * Specifies a device with the device ID. * @param deviceId The device ID. @@ -71,6 +85,19 @@ class IAudioDeviceCollection { */ virtual int getDefaultDevice(char deviceName[MAX_DEVICE_ID_LENGTH], char deviceId[MAX_DEVICE_ID_LENGTH]) = 0; + /** + * Gets the default audio device of the system (for macOS and Windows only). + * + * @param deviceName The name of the system default audio device. + * @param deviceTypeName The device type name of the the system default audio device, such as Built-in, USB, HDMI, etc. (MacOS only) + * @param deviceId The device ID of the the system default audio device. + * + * @return + * - 0: Success. + * - < 0: Failure. + */ + virtual int getDefaultDevice(char deviceName[MAX_DEVICE_ID_LENGTH], char deviceTypeName[MAX_DEVICE_ID_LENGTH], char deviceId[MAX_DEVICE_ID_LENGTH]) = 0; + /** * Sets the volume of the app. * @@ -199,6 +226,17 @@ class IAudioDeviceManager : public RefCountInterface { virtual int getPlaybackDeviceInfo(char deviceId[MAX_DEVICE_ID_LENGTH], char deviceName[MAX_DEVICE_ID_LENGTH]) = 0; + /** + * Gets the device ID and device name and device type name of the audio playback device. + * @param deviceId An output parameter that specifies the ID of the audio playback device. + * @param deviceName An output parameter that specifies the name of the audio playback device. + * @param deviceTypeName An output parameter that specifies the device type name. such as Built-in, USB, HDMI, etc. (MacOS only) + * @return + * - 0: Success. + * - < 0: Failure. + */ + virtual int getPlaybackDeviceInfo(char deviceId[MAX_DEVICE_ID_LENGTH], char deviceName[MAX_DEVICE_ID_LENGTH], char deviceTypeName[MAX_DEVICE_ID_LENGTH]) = 0; + /** * Sets the volume of the audio playback device. * @param volume The volume of the audio playing device. The value range is @@ -254,6 +292,18 @@ class IAudioDeviceManager : public RefCountInterface { virtual int getRecordingDeviceInfo(char deviceId[MAX_DEVICE_ID_LENGTH], char deviceName[MAX_DEVICE_ID_LENGTH]) = 0; + /** + * Gets the device ID and device name and device type name of the audio recording device. + * + * @param deviceId An output parameter that indicates the device id. + * @param deviceName An output parameter that indicates the device name. + * @param deviceTypeName An output parameter that indicates the device type name. such as Built-in, USB, HDMI, etc. (MacOS only) + * @return + * - 0: Success. + * - < 0: Failure. + */ + virtual int getRecordingDeviceInfo(char deviceId[MAX_DEVICE_ID_LENGTH], char deviceName[MAX_DEVICE_ID_LENGTH], char deviceTypeName[MAX_DEVICE_ID_LENGTH]) = 0; + /** * Sets the volume of the recording device. * @param volume The volume of the recording device. The value range is [0, diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/bridge.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/bridge.h new file mode 100644 index 000000000..1ad36416f --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/bridge.h @@ -0,0 +1,25 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include "handle.h" +#include "common.h" +#include "stream/stream.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +AGORA_RTE_API_C Rte RteGetFromBridge(RteError *err); + +AGORA_RTE_API_C void RteChannelAndStreamGetFromBridge( + Rte *rte, const char *channel_name, int uid, RteChannel *channel, + RteStream *stream, RteError *error); + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/c_error.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/c_error.h new file mode 100644 index 000000000..686c6603d --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/c_error.h @@ -0,0 +1,48 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include + +#include "common.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef struct RteString RteString; + +typedef enum RteErrorCode { + kRteOk, + kRteErrorDefault, + kRteErrorInvalidArgument, + kRteErrorInvalidOperation, + kRteErrorNetworkError, + kRteErrorAuthenticationFailed, + kRteErrorStreamNotFound, +} RteErrorCode; + +typedef struct RteError { + RteErrorCode code; + RteString *message; +} RteError; + +AGORA_RTE_API_C RteError *RteErrorCreate(); +AGORA_RTE_API_C bool RteErrorDestroy(RteError *err); + +AGORA_RTE_API_C bool RteErrorInit(RteError *err); +AGORA_RTE_API_C bool RteErrorDeinit(RteError *err); + +AGORA_RTE_API_C bool RteErrorCopy(RteError *dest, RteError *src); + +AGORA_RTE_API_C bool RteErrorSet(RteError *err, RteErrorCode code, + const char *fmt, ...); +AGORA_RTE_API_C bool RteErrorOccurred(RteError *err); + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/c_player.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/c_player.h new file mode 100644 index 000000000..b00e5a321 --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/c_player.h @@ -0,0 +1,386 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include + +#include "handle.h" +#include "observer.h" +#include "utils/string.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef struct Rte Rte; +typedef struct RteStream RteStream; +typedef struct RtePlayerInternal RtePlayerInternal; + +typedef enum RtePlayerState { + kRtePlayerStateIdle, + kRtePlayerStateOpening, + kRtePlayerStateOpenCompleted, + kRtePlayerStatePlaying, + kRtePlayerStatePaused, + kRtePlayerStatePlaybackCompleted, + kRtePlayerStateStopped, + kRtePlayerStateFailed +} RtePlayerState; + +typedef enum RtePlayerEvent { + kRtePlayerEventSeekBegin, + kRtePlayerEventSeekComplete, + kRtePlayerEventSeekError, + kRtePlayerEventBufferLow, + kRtePlayerEventBufferRecover, + kRtePlayerEventFreezeStart, + kRtePlayerEventFreezeStop, + kRtePlayerEventOneLoopPlaybackCompleted, + kRtePlayerEventAuthenticationWillExpire +} RtePlayerEvent; + +typedef struct RtePlayerInfo { + RtePlayerState state; + size_t duration; + size_t stream_count; + bool has_audio; + bool has_video; + bool is_audio_muted; + bool is_video_muted; + int video_height; + int video_width; + int audio_sample_rate; + int audio_channels; + int audio_bits_per_sample; +} RtePlayerInfo; + + +typedef struct RtePlayerStats { + int video_decode_frame_rate; + int video_render_frame_rate; + int video_bitrate; + + int audio_bitrate; +} RtePlayerStats; + +typedef struct RteMediaTrackInfo { + void *placeholder; +} RteMediaTrackInfo; + +typedef enum RtePlayerMetadataType { + kRtePlayerMetadataTypeSei +} RtePlayerMetadataType; + +typedef enum RteAudioDualMonoMode { + RteAudioDualMonoStereo, + RteAudioDualMonoLeft, + RteAudioDualMonoRight, + RteAudioDualMonoMix, +} RteAudioDualMonoMode; + +typedef struct RtePlayerInitialConfig { + bool enable_cache; + bool _enable_cache_is_set; + + bool enable_multiple_audio_track; + bool _enable_multiple_audio_track_is_set; + + bool is_agora_source; + bool _is_agora_source_is_set; + + bool is_live_source; + bool _is_live_source_is_set; +} RtePlayerInitialConfig; + +typedef struct RtePlayerConfig { + bool auto_play; + bool _auto_play_is_set; + + int32_t speed; + bool _speed_is_set; + + int32_t playout_audio_track_idx; + bool _playout_audio_track_idx_is_set; + + int32_t publish_audio_track_idx; + bool _publish_audio_track_idx_is_set; + + int32_t subtitle_track_idx; + bool _subtitle_track_idx_is_set; + + int32_t external_subtitle_track_idx; + bool _external_subtitle_track_idx_is_set; + + int32_t audio_pitch; + bool _audio_pitch_is_set; + + int32_t playout_volume; + bool _playout_volume_is_set; + + int32_t audio_playback_delay; + bool _audio_playback_delay_is_set; + + RteAudioDualMonoMode audio_dual_mono_mode; + bool _audio_dual_mono_mode_is_set; + + int32_t publish_volume; + bool _publish_volume_is_set; + + int32_t loop_count; + bool _loop_count_is_set; + + RteString *json_parameter; + bool _json_parameter_is_set; +} RtePlayerConfig; + +typedef struct RtePlayerCustomSourceProvider RtePlayerCustomSourceProvider; +struct RtePlayerCustomSourceProvider { + void (*on_read_data)(RtePlayerCustomSourceProvider *self); + void (*on_seek)(RtePlayerCustomSourceProvider *self); +}; + +typedef struct RtePlayerObserver RtePlayerObserver; +struct RtePlayerObserver { + RteBaseObserver base_observer; + + void (*on_state_changed)(RtePlayerObserver *observer, + RtePlayerState old_state, RtePlayerState new_state, + RteError *err); + void (*on_position_changed)(RtePlayerObserver *observer, uint64_t curr_time, + uint64_t utc_time); + + void (*on_resolution_changed)(RtePlayerObserver *observer, int width, int height); + + void (*on_event)(RtePlayerObserver *observer, RtePlayerEvent event); + void (*on_metadata)(RtePlayerObserver *observer, RtePlayerMetadataType type, + const uint8_t *data, size_t length); + + void (*on_player_info_updated)(RtePlayerObserver *observer, + const RtePlayerInfo *info); + + void (*on_audio_volume_indication)(RtePlayerObserver *observer, + int32_t volume); +}; + +AGORA_RTE_API_C void RtePlayerInfoInit(RtePlayerInfo *info, RteError *err); +AGORA_RTE_API_C void RtePlayerInfoDeinit(RtePlayerInfo *info, RteError *err); + +AGORA_RTE_API_C void RtePlayerStatsInit(RtePlayerStats *stats, RteError *err); +AGORA_RTE_API_C void RtePlayerStatsDeinit(RtePlayerStats *stats, RteError *err); + +AGORA_RTE_API_C void RteMediaTrackInfoInit(RteMediaTrackInfo *info, + RteError *err); +AGORA_RTE_API_C void RteMediaTrackInfoDeinit(RteMediaTrackInfo *info, + RteError *err); + +AGORA_RTE_API_C void RtePlayerInitialConfigInit(RtePlayerInitialConfig *config, + RteError *err); + +AGORA_RTE_API_C void RtePlayerInitialConfigDeinit(RtePlayerInitialConfig *config, + RteError *err); + +AGORA_RTE_API_C void RtePlayerInitialConfigSetEnableCache( + RtePlayerInitialConfig *config, bool enable_cache, RteError *err); + +AGORA_RTE_API_C void RtePlayerInitialConfigGetEnableCache( + RtePlayerInitialConfig *config, bool *enable_cache, RteError *err); + +AGORA_RTE_API_C void RtePlayerInitialConfigSetEnableMultipleAudioTrack( + RtePlayerInitialConfig *config, bool enable_multiple_audio_track, + RteError *err); + +AGORA_RTE_API_C void RtePlayerInitialConfigGetEnableMultipleAudioTrack( + RtePlayerInitialConfig *config, bool *enable_multiple_audio_track, + RteError *err); + +AGORA_RTE_API_C void RtePlayerInitialConfigSetIsAgoraSource( + RtePlayerInitialConfig *config, bool is_agora_source, RteError *err); + +AGORA_RTE_API_C void RtePlayerInitialConfigGetIsAgoraSource( + RtePlayerInitialConfig *config, bool *is_agora_source, RteError *err); + +AGORA_RTE_API_C void RtePlayerInitialConfigSetIsLiveSource( + RtePlayerInitialConfig *config, bool is_agora_source, RteError *err); + +AGORA_RTE_API_C void RtePlayerInitialConfigGetIsLiveSource( + RtePlayerInitialConfig *config, bool *is_agora_source, RteError *err); + +AGORA_RTE_API_C void RtePlayerConfigInit(RtePlayerConfig *config, RteError *err); + +AGORA_RTE_API_C void RtePlayerConfigDeinit(RtePlayerConfig *config, + RteError *err); + +AGORA_RTE_API_C void RtePlayerConfigCopy(RtePlayerConfig *dst, + const RtePlayerConfig *src, + RteError *err); + +AGORA_RTE_API_C void RtePlayerConfigSetAutoPlay(RtePlayerConfig *config, + bool auto_play, RteError *err); + +AGORA_RTE_API_C void RtePlayerConfigGetAutoPlay(RtePlayerConfig *config, + bool *auto_play, RteError *err); + +AGORA_RTE_API_C void RtePlayerConfigSetPlaybackSpeed(RtePlayerConfig *config, + int32_t speed, + RteError *err); + +AGORA_RTE_API_C void RtePlayerConfigGetPlaybackSpeed(RtePlayerConfig *config, + int32_t *speed, + RteError *err); + +AGORA_RTE_API_C void RtePlayerConfigSetPlayoutAudioTrackIdx( + RtePlayerConfig *config, int32_t idx, RteError *err); + +AGORA_RTE_API_C void RtePlayerConfigGetPlayoutAudioTrackIdx( + RtePlayerConfig *config, int32_t *idx, RteError *err); + +AGORA_RTE_API_C void RtePlayerConfigSetPublishAudioTrackIdx( + RtePlayerConfig *config, int32_t idx, RteError *err); + +AGORA_RTE_API_C void RtePlayerConfigGetPublishAudioTrackIdx( + RtePlayerConfig *config, int32_t *idx, RteError *err); + +AGORA_RTE_API_C void RtePlayerConfigSetAudioTrackIdx(RtePlayerConfig *config, + int32_t idx, RteError *err); + +AGORA_RTE_API_C void RtePlayerConfigGetAudioTrackIdx(RtePlayerConfig *config, + int32_t *idx, + RteError *err); + +AGORA_RTE_API_C void RtePlayerConfigSetSubtitleTrackIdx(RtePlayerConfig *config, + int32_t idx, + RteError *err); + +AGORA_RTE_API_C void RtePlayerConfigGetSubtitleTrackIdx(RtePlayerConfig *config, + int32_t *idx, + RteError *err); + +AGORA_RTE_API_C void RtePlayerConfigSetExternalSubtitleTrackIdx( + RtePlayerConfig *config, int32_t idx, RteError *err); + +AGORA_RTE_API_C void RtePlayerConfigGetExternalSubtitleTrackIdx( + RtePlayerConfig *config, int32_t *idx, RteError *err); + +AGORA_RTE_API_C void RtePlayerConfigSetAudioPitch(RtePlayerConfig *config, + int32_t audio_pitch, + RteError *err); + +AGORA_RTE_API_C void RtePlayerConfigGetAudioPitch(RtePlayerConfig *config, + int32_t *audio_pitch, + RteError *err); + +AGORA_RTE_API_C void RtePlayerConfigSetPlayoutVolume(RtePlayerConfig *config, + int32_t volume, + RteError *err); + +AGORA_RTE_API_C void RtePlayerConfigGetPlayoutVolume(RtePlayerConfig *config, + int32_t *volume, + RteError *err); + +AGORA_RTE_API_C void RtePlayerConfigSetAudioPlaybackDelay( + RtePlayerConfig *config, int32_t delay, RteError *err); + +AGORA_RTE_API_C void RtePlayerConfigGetAudioPlaybackDelay( + RtePlayerConfig *config, int32_t *delay, RteError *err); + +AGORA_RTE_API_C void RtePlayerConfigSetAudioDualMonoMode( + RtePlayerConfig *config, RteAudioDualMonoMode mode, RteError *err); + +AGORA_RTE_API_C void RtePlayerConfigGetAudioDualMonoMode( + RtePlayerConfig *config, RteAudioDualMonoMode *mode, RteError *err); + +AGORA_RTE_API_C void RtePlayerConfigSetPublishVolume(RtePlayerConfig *config, + int32_t volume, + RteError *err); + +AGORA_RTE_API_C void RtePlayerConfigGetPublishVolume(RtePlayerConfig *config, + int32_t *volume, + RteError *err); + +AGORA_RTE_API_C void RtePlayerConfigSetLoopCount(RtePlayerConfig *config, + int32_t loop_count, + RteError *err); + +AGORA_RTE_API_C void RtePlayerConfigGetLoopCount(RtePlayerConfig *config, + int32_t *loop_count, + RteError *err); + +AGORA_RTE_API_C void RtePlayerConfigSetJsonParameter(RtePlayerConfig *config, + RteString *json_parameter, + RteError *err); + +AGORA_RTE_API_C void RtePlayerConfigGetJsonParameter(RtePlayerConfig *config, + RteString *json_parameter, + RteError *err); + +AGORA_RTE_API_C RtePlayer RtePlayerCreate(Rte *self, RtePlayerInitialConfig *config, + RteError *err); +AGORA_RTE_API_C void RtePlayerDestroy(RtePlayer *self, RteError *err); + +AGORA_RTE_API_C void RtePlayerPreloadWithUrl(RtePlayer *self, const char *url, + RteError *err); + +AGORA_RTE_API_C void RtePlayerOpenWithUrl( + RtePlayer *self, const char *url, uint64_t start_time, + void (*cb)(RtePlayer *self, void *cb_data, RteError *err), void *cb_data); + +AGORA_RTE_API_C void RtePlayerOpenWithCustomSourceProvider( + RtePlayer *self, RtePlayerCustomSourceProvider *provider, + uint64_t start_time, + void (*cb)(RtePlayer *self, void *cb_data, RteError *err), void *cb_data); + +AGORA_RTE_API_C void RtePlayerOpenWithStream(RtePlayer *self, RteStream *stream, + void (*cb)(RtePlayer *self, void *cb_data, + RteError *err), + void *cb_data); + + +AGORA_RTE_API_C void RtePlayerGetStats(RtePlayer *self, void (*cb)(RtePlayer *player, RtePlayerStats *stats, void *cb_data, RteError *err), void *cb_data); + +AGORA_RTE_API_C void RtePlayerSetCanvas(RtePlayer *self, RteCanvas *canvas, RteError *err); + +AGORA_RTE_API_C void RtePlayerPlay(RtePlayer *self, RteError *err); +AGORA_RTE_API_C void RtePlayerStop(RtePlayer *self, RteError *err); +AGORA_RTE_API_C void RtePlayerPause(RtePlayer *self, RteError *err); +AGORA_RTE_API_C void RtePlayerSeek(RtePlayer *self, uint64_t new_time, + RteError *err); + +AGORA_RTE_API_C void RtePlayerMuteAudio(RtePlayer *self, bool mute, RteError *err); +AGORA_RTE_API_C void RtePlayerMuteVideo(RtePlayer *self, bool mute, RteError *err); + +AGORA_RTE_API_C uint64_t RtePlayerGetPosition(RtePlayer *self, RteError *err); + +AGORA_RTE_API_C void RtePlayerGetInfo(RtePlayer *self, RtePlayerInfo *info, RteError *err); + +AGORA_RTE_API_C void RtePlayerGetConfigs(RtePlayer *self, + RtePlayerConfig *config, RteError *err); +AGORA_RTE_API_C void RtePlayerSetConfigs( + RtePlayer *self, RtePlayerConfig *config, + void (*cb)(RtePlayer *self, void *cb_data, RteError *err), void *cb_data); + + +AGORA_RTE_API_C bool RtePlayerRegisterObserver( + RtePlayer *self, RtePlayerObserver *observer, RteError *err); +AGORA_RTE_API_C bool RtePlayerUnregisterObserver(RtePlayer *self, + RtePlayerObserver *observer, + RteError *err); + +AGORA_RTE_API_C RtePlayerObserver *RtePlayerObserverCreate(RteError *err); +AGORA_RTE_API_C void RtePlayerObserverDestroy(RtePlayerObserver *observer, + RteError *err); +AGORA_RTE_API_C RtePlayer +RtePlayerObserverGetEventSrc(RtePlayerObserver *observer, RteError *err); + +AGORA_RTE_API_C RtePlayerCustomSourceProvider +RtePlayerCustomSourceProviderCreate(Rte *self, RteError *err); + +AGORA_RTE_API_C void RtePlayerCustomSourceProviderDestroy( + RtePlayerCustomSourceProvider *self, RteError *err); + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/c_rte.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/c_rte.h new file mode 100644 index 000000000..dec007f55 --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/c_rte.h @@ -0,0 +1,145 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include + +#include "common.h" +#include "handle.h" +#include "observer.h" +#include "utils/string.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef struct RteRemoteStream RteRemoteStream; +typedef struct RteLocalStream RteLocalStream; + +typedef struct RteInitialConfig { + void *placeholder; +} RteInitialConfig; + +typedef struct RteConfig { + RteString *app_id; + bool has_app_id; + + RteString *log_folder; + bool has_log_folder; + + size_t log_file_size; + bool has_log_file_size; + + int32_t area_code; + bool has_area_code; + + RteString *cloud_proxy; + bool has_cloud_proxy; + + RteString *json_parameter; + bool has_json_parameter; +} RteConfig; + +typedef struct RteObserver { + RteBaseObserver base_observer; +} RteObserver; + +AGORA_RTE_API_C void RteInitialConfigInit(RteInitialConfig *config, RteError *err); +AGORA_RTE_API_C void RteInitialConfigDeinit(RteInitialConfig *config, RteError *err); + +AGORA_RTE_API_C void RteConfigInit(RteConfig *config, RteError *err); +AGORA_RTE_API_C void RteConfigDeinit(RteConfig *config, RteError *err); +AGORA_RTE_API_C void RteConfigCopy(RteConfig *dst, const RteConfig *src, + RteError *err); + +AGORA_RTE_API_C void RteConfigSetAppId(RteConfig *config, RteString *app_id, + RteError *err); +AGORA_RTE_API_C void RteConfigGetAppId(RteConfig *config, RteString *app_id, + RteError *err); + +AGORA_RTE_API_C void RteConfigSetLogFolder(RteConfig *config, + RteString *log_folder, RteError *err); +AGORA_RTE_API_C void RteConfigGetLogFolder(RteConfig *config, + RteString *log_folder, RteError *err); + +AGORA_RTE_API_C void RteConfigSetLogFileSize(RteConfig *config, + size_t log_file_size, + RteError *err); +AGORA_RTE_API_C void RteConfigGetLogFileSize(RteConfig *config, + size_t *log_file_size, + RteError *err); + +AGORA_RTE_API_C void RteConfigSetAreaCode(RteConfig *config, int32_t area_code, + RteError *err); +AGORA_RTE_API_C void RteConfigGetAreaCode(RteConfig *config, int32_t *area_code, + RteError *err); + +AGORA_RTE_API_C void RteConfigSetCloudProxy(RteConfig *config, + RteString *cloud_proxy, + RteError *err); +AGORA_RTE_API_C void RteConfigGetCloudProxy(RteConfig *config, + RteString *cloud_proxy, + RteError *err); + +AGORA_RTE_API_C void RteConfigSetJsonParameter(RteConfig *config, + RteString *json_parameter, + RteError *err); +AGORA_RTE_API_C void RteConfigGetJsonParameter(RteConfig *config, + RteString *json_parameter, + RteError *err); + +AGORA_RTE_API_C Rte RteCreate(RteInitialConfig *config, RteError *err); +AGORA_RTE_API_C void RteDestroy(Rte *self, RteError *err); + +AGORA_RTE_API_C bool RteInitMediaEngine(Rte *self, + void (*cb)(Rte *self, void *cb_data, + RteError *err), + void *cb_data, RteError *err); + +AGORA_RTE_API_C void RteGetConfigs(Rte *self, RteConfig *config, RteError *err); +AGORA_RTE_API_C bool RteSetConfigs(Rte *self, RteConfig *config, + void (*cb)(Rte *self, void *cb_data, + RteError *err), + void *cb_data, RteError *err); + +AGORA_RTE_API_C void RteRelayStream(RteChannel *src_channel, + RteRemoteStream *src_stream, + RteChannel *dest_channel, + RteLocalStream *dest_stream, + void (*cb)(void *cb_data, RteError *err), + void *cb_data); + +AGORA_RTE_API_C void RteRegisterExtension( + Rte *self, RteString *provider_name, RteString *extension_name, + void (*cb)(Rte *self, RteString *provider_name, RteString *extension_name, + void *cb_data, RteError *err), + void *cb_data); + +AGORA_RTE_API_C void RteReportMessage(Rte *self, RteError *err, const char *fmt, + ...); + +AGORA_RTE_API_C bool RteRegisterObserver(Rte *self, RteObserver *observer, RteError *err); +AGORA_RTE_API_C bool RteUnregisterObserver(Rte *self, RteObserver *observer, + RteError *err); + +AGORA_RTE_API_C void RteStartLastMileProbeTest(Rte *self, + void (*cb)(RteError *err)); + +AGORA_RTE_API_C RteObserver *RteObserverCreate(RteError *err); + +AGORA_RTE_API_C void RteObserverDestroy(RteObserver *observer, RteError *err); + +AGORA_RTE_API_C Rte RteObserverGetEventSrc(RteObserver *observer, RteError *err); + +// @{ +// Internal use only. +AGORA_RTE_API_C bool RteGlobalListIsEmpty(); +// @} + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/channel.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/channel.h new file mode 100644 index 000000000..4e553d6a2 --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/channel.h @@ -0,0 +1,383 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include "handle.h" +#include "common.h" +#include "metadata.h" +#include "observer.h" +#include "utils/string.h" +#include "track/track.h" +#include "utils/buf.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef struct RteTrack RteTrack; +typedef struct RteUser RteUser; +typedef struct RteStream RteStream; +typedef struct Rte Rte; +typedef struct RteLocalStream RteLocalStream; +typedef struct RteLocalStreamInfo RteLocalStreamInfo; +typedef struct RteRemoteStream RteRemoteStream; +typedef struct RteRemoteStreamInfo RteRemoteStreamInfo; + +typedef enum RteChannelType { + kRteChannelTypeDefault, +} RteChannelType; + +typedef enum RteLocalUserRtcRole { + kRteLocalUserRtcRoleBroadcaster, + kRteLocalUserRtcRoleAudience, +} RteLocalUserRtcRole; + +typedef enum RteChannelConnectionState { + kRteChannelConnectionStateDisconnected, + kRteChannelConnectionStateDisconnecting, + kRteChannelConnectionStateConnecting, + kRteChannelConnectionStateConnected, + kRteChannelConnectionStateReconnecting, + kRteChannelConnectionStateFailed +} RteChannelConnectionState; + +typedef enum RteChannelConnectionStateChangedReason { + kRteChannelConnectionStateChangedReasonConnecting, + kRteChannelConnectionStateChangedReasonSuccess, + kRteChannelConnectionStateChangedReasonInterrupted, + kRteChannelConnectionStateChangedReasonBannedByServer, + kRteChannelConnectionStateChangedReasonJoinFailed, + kRteChannelConnectionStateChangedReasonLeaveChannel, + kRteChannelConnectionStateChangedReasonInvalidAppId, + kRteChannelConnectionStateChangedReasonInvalidChannelName, + kRteChannelConnectionStateChangedReasonInvalidToken, + kRteChannelConnectionStateChangedReasonTokenExpired, + kRteChannelConnectionStateChangedReasonRejectedByServer, + kRteChannelConnectionStateChangedReasonSettingProxyServer, + kRteChannelConnectionStateChangedReasonRenewToken, + kRteChannelConnectionStateChangedReasonClientIpChanged, + kRteChannelConnectionStateChangedReasonKeepAliveTimeout, + kRteChannelConnectionStateChangedReasonRejoinSuccess, + kRteChannelConnectionStateChangedReasonLost, + kRteChannelConnectionStateChangedReasonEchoTest, + kRteChannelConnectionStateChangedReasonClientIpChangedByUser, + kRteChannelConnectionStateChangedReasonSameUidLogin, + kRteChannelConnectionStateChangedReasonTooManyBroadcasters, + kRteChannelConnectionStateChangedReasonLicenseValidationFailure, + kRteChannelConnectionStateChangedReasonCertificationVerifyFailure, + kRteChannelConnectionStateChangedReasonStreamChannelNotAvailable, + kRteChannelConnectionStateChangedReasonInconsistentAppId +} RteChannelConnectionStateChangedReason; + +typedef enum RteTrackSubState { + kRteTrackSubStateSubscribing, + kRteTrackSubStateSubscribed, + kRteTrackSubStateNotSubscribed +} RteTrackSubState; + +typedef enum RteTrackSubStateChangedReason { + kRteTrackSubStateChangedReasonRemotePublished, + kRteTrackSubStateChangedReasonRemoteUnpublished, + kRteTrackSubStateChangedReasonLocalSubscribed, + kRteTrackSubStateChangedReasonLocalUnsubscribed +} RteTrackSubStateChangedReason; + +typedef enum RteTrackPubState { + kRteTrackPubStatePublishing, + kRteTrackPubStatePublished, + kRteTrackPubStateNotPublished +} RteTrackPubState; + +typedef enum RteTrackPubStateChangedReason { + kRteTrackPubStateChangedReasonLocalPublished, + kRteTrackPubStateChangedReasonLocalUnpublished +} RteTrackPubStateChangedReason; + +typedef struct RteStateItem { + RteString *key; + RteString *value; +} RteStateItem; + +typedef struct RteState { + RteString *name; + RteStateItem *items; + size_t items_cnt; +} RteState; + +typedef struct RteLock { + RteString *lock_name; + RteString *owner; + uint32_t ttl; +} RteLock; + +typedef enum RteLockChangedEvent { + kRteLockChangedEventSet, + kRteLockChangedEventRemoved, + kRteLockChangedEventAcquired, + kRteLockChangedEventReleased, + kRteLockChangedEventExpired +} RteLockChangedEvent; + +typedef struct RteChannelConfig { + RteString *channel_id; + bool has_channel_id; + + RteChannelType type; + bool has_type; + + bool is_user_id_integer_only; + bool has_is_user_id_integer_only; + + bool is_user_id_same_as_stream_id; + bool has_is_useR_id_same_as_stream_id; + + RteLocalUserRtcRole local_user_rtc_role; + bool has_local_user_rtc_role; + + bool auto_subscribe_audio; + bool has_auto_subscribe_audio; + + bool auto_subscribe_video; + bool has_auto_subscribe_video; + + RteString *json_parameter; + bool has_json_parameter; +} RteChannelConfig; + +typedef struct RteChannelObserver RteChannelObserver; +struct RteChannelObserver { + RteBaseObserver base_observer; + + void (*on_remote_stream_added)(RteChannelObserver *self, + RteRemoteStream *stream, RteRemoteUser *user); + void (*on_local_stream_info)(RteChannelObserver *self, RteLocalStream *stream, + RteLocalStreamInfo *info); + void (*on_remote_stream_info)(RteChannelObserver *self, + RteRemoteStream *stream, + RteRemoteStreamInfo *info); + void (*on_channel_message_received)(RteChannelObserver *self, + RteString publisher, RteBuf *message); +}; + +typedef struct RteSubscribeOptions { + RteTrackMediaType track_media_type; + RteString *data_track_topic; +} RteSubscribeOptions; + +AGORA_RTE_API_C void RteStateItemInit(RteStateItem *self, RteError *err); +AGORA_RTE_API_C void RteStateItemDeinit(RteStateItem *self, RteError *err); +// @} + +// @{ +// Config +AGORA_RTE_API_C void RteChannelConfigInit(RteChannelConfig *config, + RteError *err); +AGORA_RTE_API_C void RteChannelConfigDeinit(RteChannelConfig *config, + RteError *err); + +AGORA_RTE_API_C void RteChannelConfigSetChannelId(RteChannelConfig *self, + const char *channel_id, + RteError *err); +AGORA_RTE_API_C void RteChannelConfigGetChannelId(RteChannelConfig *self, + RteString *channel_id, + RteError *err); + +AGORA_RTE_API_C void RteChannelConfigSetChannelType(RteChannelConfig *self, + RteChannelType type, + RteError *err); +AGORA_RTE_API_C void RteChannelConfigGetChannelType(RteChannelConfig *self, + RteChannelType *type, + RteError *err); + +AGORA_RTE_API_C void RteChannelConfigSetIsUserIdIntegerOnly( + RteChannelConfig *self, bool is_user_id_integer_only, RteError *err); + +AGORA_RTE_API_C void RteChannelConfigGetIsUserIdIntegerOnly( + RteChannelConfig *self, bool *is_user_id_integer_only, RteError *err); + +AGORA_RTE_API_C void RteChannelConfigSetIsUserIdSameAsStreamId( + RteChannelConfig *self, bool is_user_id_same_as_stream_id, RteError *err); +AGORA_RTE_API_C void RteChannelConfigGetIsUserIdSameAsStreamId( + RteChannelConfig *self, bool *is_user_id_same_as_stream_id, RteError *err); + +AGORA_RTE_API_C void RteChannelConfigSetLocalUserRtcRole( + RteChannelConfig *self, RteLocalUserRtcRole local_user_rtc_role, + RteError *err); +AGORA_RTE_API_C void RteChannelConfigGetLocalUserRtcRole( + RteChannelConfig *self, RteLocalUserRtcRole *local_user_rtc_role, + RteError *err); + +AGORA_RTE_API_C void RteChannelConfigSetAutoSubscribeAudio( + RteChannelConfig *self, bool auto_subscribe_audio, RteError *err); +AGORA_RTE_API_C void RteChannelConfigGetAutoSubscribeAudio( + RteChannelConfig *self, bool *auto_subscribe_audio, RteError *err); + +AGORA_RTE_API_C void RteChannelConfigSetAutoSubscribeVideo( + RteChannelConfig *self, bool auto_subscribe_video, RteError *err); +AGORA_RTE_API_C void RteChannelConfigGetAutoSubscribeVideo( + RteChannelConfig *self, bool *auto_subscribe_video, RteError *err); + +AGORA_RTE_API_C void RteChannelConfigSetJsonParameter(RteChannelConfig *self, + RteString *json_parameter, + RteError *err); +AGORA_RTE_API_C void RteChannelConfigGetJsonParameter(RteChannelConfig *self, + RteString *json_parameter, + RteError *err); +// @} + +// @{ +// Observer +AGORA_RTE_API_C RteChannelObserver *RteChannelObserverCreate(RteError *err); +AGORA_RTE_API_C void RteChannelObserverDestroy(RteChannelObserver *self, + RteError *err); +AGORA_RTE_API_C RteChannel +RteChannelObserverGetEventSrc(RteChannelObserver *self, RteError *err); +// @} + +AGORA_RTE_API_C RteChannel RteChannelCreate(Rte *self, RteChannelConfig *config, + RteError *err); +AGORA_RTE_API_C void RteChannelDestroy(RteChannel *channel, RteError *err); + +AGORA_RTE_API_C void RteChannelGetConfigs(RteChannel *self, + RteChannelConfig *config, + RteError *err); +AGORA_RTE_API_C void RteChannelSetConfigs( + RteChannel *self, RteChannelConfig *config, + void (*cb)(RteChannel *self, void *cb_data, RteError *err), void *cb_data); + +AGORA_RTE_API_C void RteChannelPublishStream( + RteChannel *self, RteLocalUser *user, RteLocalStream *stream, + void (*cb)(RteChannel *self, RteLocalStream *stream, void *cb_data, + RteError *err), + void *cb_data); +AGORA_RTE_API_C void RteChannelUnpublishStream( + RteChannel *self, RteLocalStream *stream, + void (*cb)(RteChannel *self, RteLocalStream *stream, void *cb_data, + RteError *err), + void *cb_data); + +AGORA_RTE_API_C void RteChannelSubscribeTrack( + RteChannel *self, RteRemoteStream *stream, RteSubscribeOptions *options, + void (*cb)(RteChannel *self, RteTrack *track, void *cb_data, RteError *err), + void *cb_data); + +AGORA_RTE_API_C bool RteChannelRegisterObserver( + RteChannel *self, RteChannelObserver *observer, RteError *err); + +AGORA_RTE_API_C RteUser RteChannelGetLocalUser(RteChannel *self, RteError *err); +AGORA_RTE_API_C size_t RteChannelGetRemoteUsersCount(RteChannel *self, + RteError *err); +AGORA_RTE_API_C void RteChannelGetRemoteUsers(RteChannel *self, + RteRemoteUser *remote_users, + size_t start_idx, + size_t remote_users_cnt, + RteError *err); + +AGORA_RTE_API_C void RteChannelJoin(RteChannel *self, RteLocalUser *user, + RteString *channel_token, + void (*cb)(RteChannel *self, + RteLocalUser *user, void *cb_data, + RteError *err), + void *cb_data); +AGORA_RTE_API_C void RteChannelLeave(RteChannel *self, RteLocalUser *user, + void (*cb)(RteChannel *self, + RteLocalUser *user, + void *cb_data, RteError *err), + void *cb_data); + +AGORA_RTE_API_C void RteChannelRenewToken( + RteChannel *self, RteString *channel_token, + void (*cb)(RteChannel *self, void *cb_data, RteError *err), void *cb_data); + +AGORA_RTE_API_C void RteChannelPublishMessage( + RteChannel *self, RteBuf *message, + void (*cb)(RteChannel *self, void *cb_data, RteError *err), void *cb_data); +AGORA_RTE_API_C void RteChannelSubscribeMessage( + RteChannel *self, + void (*cb)(RteChannel *self, void *cb_data, RteError *err), void *cb_data); +AGORA_RTE_API_C void RteChannelUnsubscribeMessage( + RteChannel *self, + void (*cb)(RteChannel *self, void *cb_data, RteError *err), void *cb_data); + +AGORA_RTE_API_C void RteChannelGetMetadata(RteChannel *self, + void (*cb)(RteChannel *self, + RteMetadata *items, + void *cb_data, + RteError *err), + void *cb_data); +AGORA_RTE_API_C void RteChannelSubscribeMetadata( + RteChannel *self, + void (*cb)(RteChannel *self, RteMetadata *items, size_t items_cnt, + void *cb_data, RteError *err), + void *cb_data); +AGORA_RTE_API_C void RteChannelUnsubscribeMetadata( + RteChannel *self, + void (*cb)(RteChannel *self, void *cb_data, RteError *err), void *cb_data); +AGORA_RTE_API_C void RteChannelRemoveMetadata( + RteChannel *self, RteMetadata *items, + void (*cb)(RteChannel *self, void *cb_data, RteError *err), void *cb_data); + +AGORA_RTE_API_C void RteChannelGetUserState( + RteChannel *self, RteString *user_name, + void (*cb)(RteChannel *self, RteState *state, void *cb_data, RteError *err), + void *cb_data); +AGORA_RTE_API_C void RteChannelSetUserState( + RteChannel *self, RteState *state, + void (*cb)(RteChannel *self, void *cb_data, RteError *err), void *cb_data); +AGORA_RTE_API_C void RteChannelRemoveUserState( + RteChannel *self, RteString *key, + void (*cb)(RteChannel *self, void *cb_data, RteError *err), void *cb_data); +AGORA_RTE_API_C void RteChannelSubscribeUserState( + RteChannel *self, + void (*cb)(RteChannel *self, void *cb_data, RteError *err), void *cb_data); +AGORA_RTE_API_C void RteChannelUnsubscribeUserState( + RteChannel *self, + void (*cb)(RteChannel *self, void *cb_data, RteError *err), void *cb_data); + +AGORA_RTE_API_C void RteChannelSetLock( + RteChannel *self, RteString *lock_name, uint32_t ttl, + void (*cb)(RteChannel *self, void *cb_data, RteError *err), void *cb_data); +AGORA_RTE_API_C void RteChannelRemoveLock( + RteChannel *self, RteString *lock_name, + void (*cb)(RteChannel *self, void *cb_data, RteError *err), void *cb_data); +AGORA_RTE_API_C void RteChannelGetLocks(RteChannel *self, + void (*cb)(RteChannel *self, + RteLock *locks, + size_t locks_cnt, + void *cb_data, RteError *err), + void *cb_data); +AGORA_RTE_API_C void RteChannelSubscribeLocks( + RteChannel *self, + void (*cb)(RteChannel *self, void *cb_data, RteError *err), void *cb_data); +AGORA_RTE_API_C void RteChannelUnsubscribeLocks( + RteChannel *self, + void (*cb)(RteChannel *self, void *cb_data, RteError *err), void *cb_data); +AGORA_RTE_API_C void RteChannelAcquireLock( + RteChannel *self, RteString *lock_name, bool retry, + void (*cb)(RteChannel *self, RteString owner, void *cb_data, RteError *err), + void *cb_data); +AGORA_RTE_API_C void RteChannelReleaseLock( + RteChannel *self, RteString *lock_name, + void (*cb)(RteChannel *self, void *cb_data, RteError *err), void *cb_data); + +AGORA_RTE_API_C size_t RteChannelGetLocalStreamsCount(RteChannel *self, + RteError *err); +AGORA_RTE_API_C void RteChannelGetLocalStreams(RteChannel *self, + RteLocalStream *streams, + size_t start_idx, + size_t streams_cnt, + RteError *err); + +AGORA_RTE_API_C size_t RteChannelGetRemoteStreamsCount(RteChannel *self, + RteError *err); +AGORA_RTE_API_C void RteChannelGetRemoteStreams( + RteChannel *self, RteRemoteUser *user, RteRemoteStream *streams, + size_t start_idx, size_t streams_cnt, RteError *err); + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/common.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/common.h new file mode 100644 index 000000000..991c8d635 --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/common.h @@ -0,0 +1,59 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#if defined(_WIN32) + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif // !WIN32_LEAN_AND_MEAN +#if defined(__aarch64__) +#include +#endif +#include + +#ifdef AGORARTC_EXPORT +#define AGORA_RTE_API_C __declspec(dllexport) +#else +#define AGORA_RTE_API_C __declspec(dllimport) +#endif // AGORARTC_EXPORT + +#define AGORA_CALL_C __cdecl + +#elif defined(__APPLE__) + +#include + +#define AGORA_RTE_API_C __attribute__((visibility("default"))) + +#elif defined(__ANDROID__) || defined(__linux__) + +#define AGORA_RTE_API_C __attribute__((visibility("default"))) + +#else // !_WIN32 && !__APPLE__ && !(__ANDROID__ || __linux__) + +#define AGORA_RTE_API_C + +#endif // _WIN32 + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef enum RteAreaCode { + kRteAreaCodeCn = 0x00000001, + kRteAreaCodeNa = 0x00000002, + kRteAreaCodeEu = 0x00000004, + kRteAreaCodeAs = 0x00000008, + kRteAreaCodeJp = 0x00000010, + kRteAreaCodeIn = 0x00000020, + kRteAreaCodeGlob = 0xFFFFFFFF +} RteAreaCode; + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/device/audio.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/device/audio.h new file mode 100644 index 000000000..91244f55d --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/device/audio.h @@ -0,0 +1,40 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include "device/device.h" +#include "handle.h" +#include "../common.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef struct RteAudioDevice RteAudioDevice; + +typedef enum RteAudioDeviceType { + kRteAudioDeviceTypePlayout, + kRteAudioDeviceTypeRecording, +} RteAudioDeviceType; + +typedef struct RteAudioDeviceInfo { + RteDeviceInfo info; + + RteAudioDeviceType type; +} RteAudioDeviceInfo; + +AGORA_RTE_API_C void RteAudioDeviceInfoInit(RteAudioDeviceInfo *info, + RteError *err); +AGORA_RTE_API_C void RteAudioDeviceInfoDeinit(RteAudioDeviceInfo *info, + RteError *err); + +AGORA_RTE_API_C void RteAudioDeviceGetInfo(RteAudioDevice *self, + RteAudioDeviceInfo *info, + RteError *err); +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/device/audio_device_manager.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/device/audio_device_manager.h new file mode 100644 index 000000000..213138acb --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/device/audio_device_manager.h @@ -0,0 +1,97 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include + +#include "../common.h" +#include "c_error.h" +#include "device/audio.h" +#include "handle.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef struct RteAudioDeviceManagerConfig { + char placeholder; +} RteAudioDeviceManagerConfig; + +typedef struct RteAudioDeviceManager { + char placeholder; +} RteAudioDeviceManager; + +AGORA_RTE_API_C void RteAudioDeviceManagerConfigInit( + RteAudioDeviceManagerConfig *config, RteError *err); + +AGORA_RTE_API_C void RteAudioDeviceManagerConfigDeInit( + RteAudioDeviceManagerConfig *config, RteError *err); + +AGORA_RTE_API_C void RteAudioDeviceManagerConfigSetJsonParameter( + RteAudioDeviceManagerConfig *self, RteString *json_parameter, + RteError *err); + +AGORA_RTE_API_C void RteAudioDeviceManagerConfigGetJsonParameter( + RteAudioDeviceManagerConfig *config, RteString *json_parameter, + RteError *err); + +typedef void (*RteAudioDeviceManagerSetConfigsCallback)( + RteAudioDeviceManager *device_manager, RteAudioDeviceManagerConfig *config, + void *cb_data, RteError *err); + +AGORA_RTE_API_C RteAudioDeviceManager RteAudioDeviceManagerCreate( + Rte *rte, RteAudioDeviceManagerConfig *config, RteError *err); +AGORA_RTE_API_C void RteAudioDeviceManagerDestroy(RteAudioDeviceManager *self, + RteError *err); + +AGORA_RTE_API_C void RteAudioDeviceManagerGetConfigs( + RteAudioDeviceManager *self, RteAudioDeviceManagerConfig *config, + RteError *err); + +AGORA_RTE_API_C void RteAudioDeviceManagerSetConfigs( + RteAudioDeviceManager *self, RteAudioDeviceManagerConfig *config, + RteAudioDeviceManagerSetConfigsCallback cb, void *cb_data); + +typedef void (*RteAudioDeviceManagerEnumerateDevicesCallback)( + RteAudioDeviceManager *mgr, RteAudioDevice *devices, size_t devices_cnt, + void *cb_data, RteError *err); +AGORA_RTE_API_C void RteAudioDeviceManagerEnumerateDevices( + RteAudioDeviceManager *self, RteAudioDeviceType type, + RteAudioDeviceManagerEnumerateDevicesCallback cb, void *cb_data); + +AGORA_RTE_API_C void RteAudioDeviceManagerSetVolume(RteAudioDeviceManager *self, + RteAudioDeviceType type, + uint32_t volume, + RteError *err); + +AGORA_RTE_API_C void RteAudioDeviceManagerGetVolume(RteAudioDeviceManager *self, + RteAudioDeviceType type, + uint32_t *volume, + RteError *err); + +typedef void (*RteAudioDeviceManagerSetCurrentDeviceCallback)( + RteAudioDeviceManager *self, RteAudioDeviceType type, + RteAudioDevice *device, void *cb_data, RteError *err); +AGORA_RTE_API_C void RteAudioDeviceManagerSetCurrentDevice( + RteAudioDeviceManager *self, RteAudioDeviceType type, + RteAudioDevice *device, RteAudioDeviceManagerSetCurrentDeviceCallback cb, + void *cb_data); + +AGORA_RTE_API_C RteAudioDevice RteAudioDeviceManagerGetCurrentDevice( + RteAudioDeviceManager *self, RteAudioDeviceType type, RteError *err); + +#if defined(__ANDROID__) || defined(TARGET_OS_IOS) || defined(UNIT_TEST_MOCK) +AGORA_RTE_API_C void RteAudioDeviceManagerUseSpeakerphoneByDefault( + RteAudioDeviceManager *self, bool enable, RteError *err); + +AGORA_RTE_API_C void RteAudioDeviceManagerUseSpeakerphone( + RteAudioDeviceManager *self, bool enable, RteError *err); +#endif + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/device/device.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/device/device.h new file mode 100644 index 000000000..3ec850e9c --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/device/device.h @@ -0,0 +1,27 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include "../common.h" +#include "utils/string.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef struct RteDeviceInfo { + RteString *device_name; + RteString *device_id; +} RteDeviceInfo; + +AGORA_RTE_API_C void RteDeviceInfoInit(RteDeviceInfo *info, RteError *err); + +AGORA_RTE_API_C void RteDeviceInfoDeinit(RteDeviceInfo *info, RteError *err); + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/device/video.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/device/video.h new file mode 100644 index 000000000..2f9d26e5a --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/device/video.h @@ -0,0 +1,37 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include "device/device.h" +#include "../common.h" +#include "handle.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef enum RteVideoDeviceType { + kRteVideoDeviceTypeCapturing +} RteVideoDeviceType; + +typedef struct RteVideoDeviceInfo { + RteDeviceInfo base; + + RteVideoDeviceType type; +} RteVideoDeviceInfo; + +AGORA_RTE_API_C void RteVideoDeviceInfoInit(RteVideoDeviceInfo *info, + RteError *err); +AGORA_RTE_API_C void RteVideoDeviceInfoDeinit(RteVideoDeviceInfo *info, + RteError *err); + +AGORA_RTE_API_C void RteVideoDeviceGetInfo(RteVideoDevice *self, + RteVideoDeviceInfo *info, + RteError *err); +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/device/video_device_manager.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/device/video_device_manager.h new file mode 100644 index 000000000..785784bc0 --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/device/video_device_manager.h @@ -0,0 +1,67 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include + +#include "c_error.h" +#include "device/video.h" +#include "handle.h" +#include "../common.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef struct RteVideoDeviceManagerConfig { + char placeholder; +} RteVideoDeviceManagerConfig; + +typedef struct RteVideoDeviceManager { + char placeholder; +} RteVideoDeviceManager; + +AGORA_RTE_API_C void RteVideoDeviceManagerConfigInit( + RteVideoDeviceManagerConfig *config, RteError *err); +AGORA_RTE_API_C void RteVideoDeviceManagerConfigDeinit( + RteVideoDeviceManagerConfig *config, RteError *err); +AGORA_RTE_API_C void RteVideoDeviceManagerConfigSetJsonParameter( + RteVideoDeviceManagerConfig *config, RteString *json_parameter, + RteError *err); +AGORA_RTE_API_C void RteVideoDeviceManagerConfigGetJsonParameter( + RteVideoDeviceManagerConfig *config, RteString *json_parameter, + RteError *err); + +typedef void (*RteVideoDeviceManagerSetConfigsCallback)( + RteVideoDeviceManager *device_manager, RteVideoDeviceManagerConfig *config, + void *cb_data, RteError *err); + +typedef void (*RteVideoDeviceManagerEnumerateDevicesCallback)( + RteVideoDeviceManager *device_manager, RteVideoDevice *devices, + size_t devices_cnt, void *cb_data, RteError *err); + +AGORA_RTE_API_C RteVideoDeviceManager RteVideoDeviceManagerCreate( + Rte *rte, RteVideoDeviceManagerConfig *config, RteError *err); +AGORA_RTE_API_C void RteVideoDeviceManagerDestroy(RteVideoDeviceManager *self, + RteError *err); +AGORA_RTE_API_C void RteVideoDeviceManagerGetConfigs( + RteVideoDeviceManager *self, RteVideoDeviceManagerConfig *config, + RteError *err); +AGORA_RTE_API_C void RteVideoDeviceManagerSetConfigs( + RteVideoDeviceManager *self, RteVideoDeviceManagerConfig *config, + RteVideoDeviceManagerSetConfigsCallback callback, void *cb_data); +AGORA_RTE_API_C void RteVideoDeviceManagerEnumerateDevices( + RteVideoDeviceManager *self, RteVideoDeviceType type, + RteVideoDeviceManagerEnumerateDevicesCallback cb, void *cb_data); +AGORA_RTE_API_C void RteVideoDeviceManagerSetCurrentDevice( + RteVideoDeviceManager *self, RteVideoDevice *device, RteError *err); +AGORA_RTE_API_C RteVideoDevice RteVideoDeviceManagerGetCurrentDevice( + RteVideoDeviceManager *self, RteVideoDeviceType type, RteError *err); + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/handle.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/handle.h new file mode 100644 index 000000000..a7ff4ac0f --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/handle.h @@ -0,0 +1,155 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include + +#include "utils/uuid.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef struct RteHandle { + RteUuid uuid; +} RteHandle; + +typedef struct Rte { + RteHandle handle; +} Rte; + +typedef struct RtePlayer { + RteHandle handle; +} RtePlayer; + +typedef struct RteChannel { + RteHandle handle; +} RteChannel; + +typedef struct RteUser { + RteHandle handle; +} RteUser; + +typedef struct RteLocalUser { + RteHandle handle; +} RteLocalUser; + +typedef struct RteRemoteUser { + RteHandle handle; +} RteRemoteUser; + +typedef struct RteTrack { + RteHandle handle; +} RteTrack; + +typedef struct RteLocalTrack { + RteHandle handle; +} RteLocalTrack; + +typedef struct RteRemoteTrack { + RteHandle handle; +} RteRemoteTrack; + +typedef struct RteVideoTrack { + RteHandle handle; +} RteVideoTrack; + +typedef struct RteAudioTrack { + RteHandle handle; +} RteAudioTrack; + +typedef struct RteDataTrack { + RteHandle handle; +} RteDataTrack; + +typedef struct RteLocalVideoTrack { + RteHandle handle; +} RteLocalVideoTrack; + +typedef struct RteRemoteVideoTrack { + RteHandle handle; +} RteRemoteVideoTrack; + +typedef struct RteCameraVideoTrack { + RteHandle handle; +} RteCameraVideoTrack; + +typedef struct RteMixedVideoTrack { + RteHandle handle; +} RteMixedVideoTrack; + +typedef struct RteScreenVideoTrack { + RteHandle handle; +} RteScreenVideoTrack; + +typedef struct RteLocalAudioTrack { + RteHandle handle; +} RteLocalAudioTrack; + +typedef struct RteRemoteAudioTrack { + RteHandle handle; +} RteRemoteAudioTrack; + +typedef struct RteMicAudioTrack { + RteHandle handle; +} RteMicAudioTrack; + +typedef struct RteLocalDataTrack { + RteHandle handle; +} RteLocalDataTrack; + +typedef struct RteRemoteDataTrack { + RteHandle handle; +} RteRemoteDataTrack; + +typedef struct RteCanvas { + RteHandle handle; +} RteCanvas; + +typedef struct RteAudioDevice { + RteHandle handle; +} RteAudioDevice; + +typedef struct RteVideoDevice { + RteHandle handle; +} RteVideoDevice; + +typedef struct RteRealTimeStream { + RteHandle handle; +} RteRealTimeStream; + +typedef struct RteCdnStream { + RteHandle handle; +} RteCdnStream; + +typedef struct RteLocalStream { + RteHandle handle; +} RteLocalStream; + +typedef struct RteRemoteStream { + RteHandle handle; +} RteRemoteStream; + +typedef struct RteLocalCdnStream { + RteHandle handle; +} RteLocalCdnStream; + +typedef struct RteLocalRealTimeStream { + RteHandle handle; +} RteLocalRealTimeStream; + +typedef struct RteRemoteCdnStream { + RteHandle handle; +} RteRemoteCdnStream; + +typedef struct RteRemoteRealTimeStream { + RteHandle handle; +} RteRemoteRealTimeStream; + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/info.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/info.h new file mode 100644 index 000000000..eff255b9b --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/info.h @@ -0,0 +1,27 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include "c_error.h" +#include "common.h" +#include "handle.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef struct RteBaseInfo { + Rte rte; +} RteBaseInfo; + +AGORA_RTE_API_C void RteBaseInfoInit(RteBaseInfo *info, RteError *err); + +AGORA_RTE_API_C void RteBaseInfoDeinit(RteBaseInfo *info, RteError *err); + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/log.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/log.h new file mode 100644 index 000000000..8cd54c65b --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/log.h @@ -0,0 +1,48 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include + +#include "common.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef struct Rte Rte; +typedef struct RteError RteError; + +typedef enum RteLogLevel { + kRteLogInfo, + kRteLogWarn, + kRteLogError, + kRteLogFatal, +} RteLogLevel; + +AGORA_RTE_API_C void RteLog(Rte *self, RteLogLevel level, const char *func, + const char *file, size_t line, RteError *err, + const char *fmt, ...); + +#define RTE_LOG(level, ...) \ + RteLog(NULL, level, __func__, __FILE__, __LINE__, NULL, __VA_ARGS__) + +#define RTE_LOGI(...) \ + RteLog(NULL, kRteLogInfo, __func__, __FILE__, __LINE__, NULL, __VA_ARGS__) + +#define RTE_LOGW(...) \ + RteLog(NULL, kRteLogWarn, __func__, __FILE__, __LINE__, NULL, __VA_ARGS__) + +#define RTE_LOGE(...) \ + RteLog(NULL, kRteLogError, __func__, __FILE__, __LINE__, NULL, __VA_ARGS__) + +#define RTE_LOGF(...) \ + RteLog(NULL, kRteLogFatal, __func__, __FILE__, __LINE__, NULL, __VA_ARGS__) + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/metadata.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/metadata.h new file mode 100644 index 000000000..4ed3ea89b --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/metadata.h @@ -0,0 +1,92 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include +#include +#include + +#include "c_error.h" +#include "common.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef struct RteMetadataConfig { + bool record_ts; + bool has_record_ts; + + bool record_owner; + bool has_record_owner; + + RteString *lock_name; + bool has_lock_name; + + RteString *json_parameter; + bool has_json_parameter; +} RteMetadataConfig; + +typedef struct RteMetadataItem { + RteString *key; + RteString *value; + RteString *author; + int64_t revision; + int64_t update_timestamp; +} RteMetadataItem; + +typedef struct RteMetadata { + char placeholder; +} RteMetadata; + +AGORA_RTE_API_C void RteMetadataConfigInit(RteMetadataConfig *config, + RteError *err); +AGORA_RTE_API_C void RteMetadataConfigDeinit(RteMetadataConfig *config, + RteError *err); + +AGORA_RTE_API_C void RteMetadataConfigSetRecordTs(RteMetadataConfig *self, + bool record_ts, RteError *err); +AGORA_RTE_API_C void RteMetadataConfigGetRecordTs(RteMetadataConfig *self, + bool *record_ts, + RteError *err); + +AGORA_RTE_API_C void RteMetadataConfigSetRecordOwner(RteMetadataConfig *self, + bool record_owner, + RteError *err); +AGORA_RTE_API_C void RteMetadataConfigGetRecordOwner(RteMetadataConfig *self, + bool *record_owner, + RteError *err); + +AGORA_RTE_API_C void RteMetadataConfigSetJsonParameter(RteMetadataConfig *self, + RteString *json_parameter, + RteError *err); +AGORA_RTE_API_C void RteMetadataConfigGetJsonParameter(RteMetadataConfig *self, + RteString *json_parameter, + RteError *err); + +AGORA_RTE_API_C void RteMetadataItemInit(RteMetadataItem *self, RteError *err); +AGORA_RTE_API_C void RteMetadataItemDeinit(RteMetadataItem *self, RteError *err); + +AGORA_RTE_API_C void RteMetadataInit(RteMetadata *self, RteError *err); +AGORA_RTE_API_C void RteMetadataDeinit(RteMetadata *self, RteError *err); + +AGORA_RTE_API_C void RteMetadataSetRevision(RteMetadata *self, int64_t revision, + RteError *err); +AGORA_RTE_API_C void RteMetadataGetRevision(RteMetadata *self, int64_t *revision, + RteError *err); + +AGORA_RTE_API_C void RteMetadataClear(RteMetadata *self, RteError *err); + +AGORA_RTE_API_C void RteMetadataAddItem(RteMetadata *self, RteMetadataItem *item, + RteError *err); +AGORA_RTE_API_C size_t RteMetadataGetItems(RteMetadata *self, + RteMetadataItem *items, + size_t items_cnt, RteError *err); + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/observer.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/observer.h new file mode 100644 index 000000000..bae44c60c --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/observer.h @@ -0,0 +1,20 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef struct RteBaseObserver { + void *event_src; + void *me_in_target_lang; +} RteBaseObserver; + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/old.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/old.h new file mode 100644 index 000000000..b09168071 --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/old.h @@ -0,0 +1,112 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include + +#include "common.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +struct RtcStats {}; + +enum USER_OFFLINE_REASON_TYPE {}; + +struct IRtcEngineEventHandler { + // When a local user successfully joins the channel, this callback is + // triggered. + virtual void onJoinChannelSuccess(const char *channel, uid_t uid, + int elapsed); + + // When the local host successfully leaves the channel, this callback is + // triggered. + virtual void onLeaveChannel(const RtcStats &stat); + + // When a remote host successfully joins the channel, this callback is + // triggered. Upon receiving this callback, you need to immediately call + // setupRemoteVideo to set up the remote host's view. + virtual void onUserJoined(uid_t uid, int elapsed); + + // When a remote host leaves the channel or disconnects, this callback is + // triggered. + virtual void onUserOffline(uid_t uid, USER_OFFLINE_REASON_TYPE reason); +}; + +// 繼承 IRtcEngineEventHandler 類中的回呼與事件 +struct SampleEngineEventHandler : public IRtcEngineEventHandler { + SampleEngineEventHandler() = default; + virtual ~SampleEngineEventHandler() = default; + + SampleEngineEventHandler(const SampleEngineEventHandler &) = delete; + SampleEngineEventHandler &operator=(const SampleEngineEventHandler &) = + delete; + + SampleEngineEventHandler(SampleEngineEventHandler &&) = delete; + SampleEngineEventHandler &operator=(SampleEngineEventHandler &&) = delete; +}; + +struct RtcEngineContext { + IRtcEngineEventHandler *eventHandler; + std::string appId; +}; + +enum RENDER_MODE_TYPE { + RENDER_MODE_HIDDEN, + RENDER_MODE_FIT, +}; + +struct VideoCanvas { + uid_t uid; + void *view; + RENDER_MODE_TYPE renderMode; +}; + +enum VIDEO_CODEC_TYPE { VIDEO_CODEC_SOME }; + +struct VideoEncoderConfiguration { + VIDEO_CODEC_TYPE codecType; +}; + +enum CHANNEL_PROFILE { CHANNEL_PROFILE_LIVE_BROADCASTING }; + +enum CLIENT_ROLE_TYPE { CLIENT_ROLE_TYPE_BROADCASTER }; + +struct ChannelMediaOptions { + CHANNEL_PROFILE channelProfile; + CLIENT_ROLE_TYPE clientRoleType; + + bool autoSubscribeAudio; + bool autoSubscribeVideo; +}; + +struct IRtcEngine { + void initialize(RtcEngineContext &ctx); + void release(bool some_param); + + void enableVideo(); + void disableVideo(); + + void startPreview(); + void stopPreview(); + + void setupLocalVideo(VideoCanvas &canvas); + void setupRemoteVideo(VideoCanvas &canvas); + + void setVideoEncoderConfiguration(VideoEncoderConfiguration &config); + + int joinChannel(const char *app_id, const char *channel_name, int uid, + ChannelMediaOptions &options); + int leaveChannel(); +}; + +AGORA_RTE_API_C IRtcEngine *createAgoraRtcEngine(); + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/options.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/options.h new file mode 100644 index 000000000..5c1cc3aaa --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/options.h @@ -0,0 +1,22 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +/** + * @note This document lists functions that are for internal use and not part of + * the public API. + */ + +namespace rte_api_internal {} // namespace rte_api_internal + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/stream/cdn_stream.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/stream/cdn_stream.h new file mode 100644 index 000000000..89d836a89 --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/stream/cdn_stream.h @@ -0,0 +1,36 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include "c_error.h" +#include "../common.h" +#include "stream/stream.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef struct RteCdnStreamConfig { + RteStreamConfig stream_config; +} RteCdnStreamConfig; + +typedef struct RteCdnStreamStats { + RteStreamStats stream_stats; +} RteCdnStreamStats; + +typedef struct RteCdnStreamInfo { + RteStreamInfo stream_info; +} RteCdnStreamInfo; + +AGORA_RTE_API_C void RteCdnStreamStatsInit(RteCdnStreamStats *stats, + RteError *err); +AGORA_RTE_API_C void RteCdnStreamStatsDeinit(RteCdnStreamStats *stats, + RteError *err); + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/stream/local_cdn_stream.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/stream/local_cdn_stream.h new file mode 100644 index 000000000..7a60a9160 --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/stream/local_cdn_stream.h @@ -0,0 +1,59 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include "../common.h" +#include "stream/cdn_stream.h" +#include "stream/local_stream.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef struct RteUser RteUser; +typedef struct RteTrack RteTrack; +typedef struct Rte Rte; +typedef struct RteChannel RteChannel; + +typedef struct RteLocalCdnStreamConfig { + RteCdnStreamConfig cdn_stream_config; + RteLocalStreamConfig local_stream_config; +} RteLocalCdnStreamConfig; + +AGORA_RTE_API_C void RteLocalCdnStreamConfigInit(RteLocalCdnStreamConfig *config, + RteError *err); +AGORA_RTE_API_C void RteLocalCdnStreamConfigDeinit( + RteLocalCdnStreamConfig *config, RteError *err); + +AGORA_RTE_API_C void RteLocalCdnStreamConfigSetUrl(RteLocalCdnStreamConfig *self, + RteString *url, + RteError *err); +AGORA_RTE_API_C void RteLocalCdnStreamConfigGetUrl(RteLocalCdnStreamConfig *self, + RteString *url, + RteError *err); + +AGORA_RTE_API_C void RteLocalCdnStreamConfigSetJsonParameter( + RteLocalCdnStreamConfig *self, RteString *json_parameter, RteError *err); +AGORA_RTE_API_C void RteLocalCdnStreamConfigGetJsonParameter( + RteLocalCdnStreamConfig *self, RteString *json_parameter, RteError *err); + +AGORA_RTE_API_C RteLocalCdnStream RteLocalCdnStreamCreate( + Rte *rte, RteLocalCdnStreamConfig *config, RteError *err); +AGORA_RTE_API_C void RteLocalCdnStreamDestroy(RteLocalCdnStream *self, + RteError *err); + +AGORA_RTE_API_C void RteLocalCdnStreamGetConfigs(RteLocalCdnStream *self, + RteLocalCdnStreamConfig *config, + RteError *err); +AGORA_RTE_API_C void RteLocalCdnStreamSetConfigs( + RteLocalCdnStream *self, RteLocalCdnStreamConfig *config, + void (*cb)(RteLocalCdnStream *stream, void *cb_data, RteError *err), + void *cb_data); + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/stream/local_realtime_stream.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/stream/local_realtime_stream.h new file mode 100644 index 000000000..816a1a8f2 --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/stream/local_realtime_stream.h @@ -0,0 +1,33 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include "../common.h" +#include "stream/local_stream.h" +#include "stream/realtime_stream.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef struct Rte Rte; +typedef struct RteChannel RteChannel; + +typedef struct RteLocalRealTimeStreamConfig { + RteRealTimeStreamConfig realtime_stream_config; + RteLocalStreamConfig local_stream_config; +} RteLocalRealTimeStreamConfig; + +AGORA_RTE_API_C RteLocalRealTimeStream RteLocalRealTimeStreamCreate( + Rte *rte, RteLocalRealTimeStreamConfig *config, RteError *err); + +AGORA_RTE_API_C void RteLocalRealTimeStreamDestroy(RteLocalRealTimeStream *self, + RteError *err); + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/stream/local_stream.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/stream/local_stream.h new file mode 100644 index 000000000..55bac6ad8 --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/stream/local_stream.h @@ -0,0 +1,38 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include "c_error.h" +#include "../common.h" +#include "stream/stream.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef struct RteLocalStreamConfig { + RteStreamConfig stream_config; +} RteLocalStreamConfig; + +typedef struct RteLocalStreamStats { + RteStreamStats stream_stats; +} RteLocalStreamStats; + +typedef struct RteLocalStreamInfo { + RteStreamInfo stream_info; + bool has_subscribed; +} RteLocalStreamInfo; + +AGORA_RTE_API_C void RteLocalStreamStatsInit(RteLocalStreamStats *stats, + RteError *err); + +AGORA_RTE_API_C void RteLocalStreamStatsDeinit(RteLocalStreamStats *stats, + RteError *err); + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/stream/realtime_stream.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/stream/realtime_stream.h new file mode 100644 index 000000000..48afc1301 --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/stream/realtime_stream.h @@ -0,0 +1,37 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include "c_error.h" +#include "../common.h" +#include "stream/stream.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef struct RteRealTimeStreamConfig { + RteStreamConfig stream_config; +} RteRealTimeStreamConfig; + +typedef struct RteRealTimeStreamStats { + RteStreamStats stream_stats; +} RteRealTimeStreamStats; + +typedef struct RteRealTimeStreamInfo { + RteStreamInfo stream_info; +} RteRealTimeStreamInfo; + +AGORA_RTE_API_C void RteRealTimeStreamStatsInit(RteRealTimeStreamStats *stats, + RteError *err); + +AGORA_RTE_API_C void RteRealTimeStreamStatsDeinit(RteRealTimeStreamStats *stats, + RteError *err); + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/stream/remote_cdn_stream.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/stream/remote_cdn_stream.h new file mode 100644 index 000000000..e670e608b --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/stream/remote_cdn_stream.h @@ -0,0 +1,54 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include "../common.h" +#include "stream/cdn_stream.h" +#include "stream/remote_stream.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef struct RteUser RteUser; +typedef struct RteTrack RteTrack; + +typedef struct RteRemoteCdnStreamConfig { + RteRemoteStreamConfig remote_stream_config; + RteCdnStreamConfig cdn_stream_config; +} RteRemoteCdnStreamConfig; + +AGORA_RTE_API_C void RteRemoteCdnStreamConfigInit( + RteRemoteCdnStreamConfig *config, RteError *err); +AGORA_RTE_API_C void RteRemoteCdnStreamConfigDeinit( + RteRemoteCdnStreamConfig *config, RteError *err); + +AGORA_RTE_API_C void RteRemoteCdnStreamConfigSetUrl( + RteRemoteCdnStreamConfig *self, RteString *url, RteError *err); +AGORA_RTE_API_C void RteRemoteCdnStreamConfigGetUrl( + RteRemoteCdnStreamConfig *self, RteString *url, RteError *err); + +AGORA_RTE_API_C void RteRemoteCdnStreamConfigSetJsonParameter( + RteRemoteCdnStreamConfig *self, RteString *json_parameter, RteError *err); +AGORA_RTE_API_C void RteRemoteCdnStreamConfigGetJsonParameter( + RteRemoteCdnStreamConfig *self, RteString *json_parameter, RteError *err); + +AGORA_RTE_API_C RteRemoteCdnStream RteRemoteCdnStreamCreate(Rte *self, + RteError *err); +AGORA_RTE_API_C void RteRemoteCdnStreamDestroy(RteRemoteCdnStream *self, + RteError *err); + +AGORA_RTE_API_C void RteRemoteCdnStreamGetConfigs( + RteRemoteCdnStream *self, RteRemoteCdnStreamConfig *config, RteError *err); +AGORA_RTE_API_C void RteRemoteCdnStreamSetConfigs( + RteRemoteCdnStream *self, RteRemoteCdnStreamConfig *config, + void (*cb)(RteRemoteCdnStream *stream, void *cb_data, RteError *err), + void *cb_data); + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/stream/remote_realtime_stream.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/stream/remote_realtime_stream.h new file mode 100644 index 000000000..438ecc047 --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/stream/remote_realtime_stream.h @@ -0,0 +1,32 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include "../common.h" +#include "stream/realtime_stream.h" +#include "stream/remote_stream.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef struct Rte Rte; +typedef struct RteChannel RteChannel; + +typedef struct RteRemoteRealTimeStreamConfig { + RteRemoteStreamConfig remote_stream_config; + RteRealTimeStreamConfig realtime_stream_config; +} RteRemoteRealTimeStreamConfig; + +AGORA_RTE_API_C RteRemoteRealTimeStream RteRemoteRealTimeStreamCreate( + Rte *rte, RteRemoteRealTimeStreamConfig *config, RteError *err); +AGORA_RTE_API_C void RteRemoteRealTimeStreamDestroy( + RteRemoteRealTimeStream *self, RteError *err); + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/stream/remote_stream.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/stream/remote_stream.h new file mode 100644 index 000000000..954c77b77 --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/stream/remote_stream.h @@ -0,0 +1,49 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include + +#include "c_error.h" +#include "../common.h" +#include "stream/stream.h" +#include "track/track.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef struct RteRemoteStreamConfig { + RteStreamConfig stream_config; +} RteRemoteStreamConfig; + +typedef struct RteRemoteStreamStats { + RteStreamStats stream_stats; +} RteRemoteStreamStats; + +typedef struct RteRemoteStreamInfo { + RteStreamInfo stream_info; + + bool has_audio; + bool has_video; + bool has_data; + RteTrackSrc audio_track_src; + RteTrackSrc video_track_src; + RteTrackSrc audio_track_src_original; + RteTrackSrc video_track_src_original; + RteString *data_track_topics; + size_t data_track_topic_cnt; +} RteRemoteStreamInfo; + +AGORA_RTE_API_C void RteRemoteStreamStatsInit(RteRemoteStreamStats *stats, + RteError *err); +AGORA_RTE_API_C void RteRemoteStreamStatsDeinit(RteRemoteStreamStats *stats, + RteError *err); + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/stream/stream.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/stream/stream.h new file mode 100644 index 000000000..75464e1a8 --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/stream/stream.h @@ -0,0 +1,289 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include + +#include "c_error.h" +#include "handle.h" +#include "observer.h" +#include "../common.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef struct RteChannel RteChannel; +typedef struct Rte Rte; +typedef struct RteStream RteStream; + +typedef struct RteStreamObserver RteStreamObserver; +struct RteStreamObserver { + RteBaseObserver base_observer; +}; + +typedef enum RteStreamType { + kRteStreamTypeRealTime, + kRteStreamTypeCdn, +} RteStreamType; + +typedef enum RteEncryptionMode { + kRteEncryptionModeAes128Xts, + kRteEncryptionModeAes128Ecb, + kRteEncryptionModeAes128Gcm, + kRteEncryptionModeAes128Gcm2, + kRteEncryptionModeAes256Xts, + kRteEncryptionModeAes256Gcm, + kRteEncryptionModeAes256Gcm2, + kRteEncryptionModeSm4128Ecb, +} RteEncryptionMode; + +typedef enum RteAudioEncoderProfile { + kRteAudioEncoderProfileDefault, + kRteAudioEncoderProfileStdSpeech, + kRteAudioEncoderProfileStdMusic, + kRteAudioEncoderProfileStdStereoMusic, + kRteAudioEncoderProfileHighQualityMusic, + kRteAudioEncoderProfileHighQualityStereoMusic, + kRteAudioEncoderProfileIot, +} RteAudioEncoderProfile; + +typedef enum RteOrientationMode { + kRteOrientationModeAdaptive, + kRteOrientationModeFixedLandscape, + kRteOrientationModeFixedPortrait, +} RteOrientationMode; + +typedef enum RteVideoDegradationPreference { + kRteVideoDegradationPreferenceMaintainFramerate, + kRteVideoDegradationPreferenceMaintainBalanced, + kRteVideoDegradationPreferenceMaintainResolution, + kRteVideoDegradationPreferenceDisabled, +} RteVideoDegradationPreference; + +typedef enum RteVideoMirrorMode { + kRteVideoMirrorModeAuto, + kRteVideoMirrorModeEnabled, + kRteVideoMirrorModeDisabled, +} RteVideoMirrorMode; + +typedef struct RteStreamConfig { + RteStreamType type; + bool has_type; + + RteString *stream_id; + bool has_stream_id; + + RteEncryptionMode encryption_mode; + bool has_encryption_mode; + + RteString *encryption_key; + bool has_encryption_key; + + uint8_t encryption_kdf_salt[32]; + bool has_encryption_kdf_salt; + + RteAudioEncoderProfile audio_encoder_profile; + bool has_audio_encoder_profile; + + uint32_t width; + bool has_width; + + uint32_t height; + bool has_height; + + uint32_t frame_rate; + bool has_frame_rate; + + uint32_t min_bitrate; + bool has_min_bitrate; + + RteOrientationMode orientation_mode; + bool has_orientation_mode; + + RteVideoDegradationPreference degradation_preference; + bool has_degradation_preference; + + RteVideoMirrorMode mirror_mode; + bool has_mirror_mode; + + bool che_hw_decoding; + bool has_che_hw_decoding; + + RteString *json_parameter; + bool has_json_parameter; +} RteStreamConfig; + +typedef struct RteStreamStats { + int placeholder; +} RteStreamStats; + +typedef struct RteStreamInfo { + RteChannel channel; + Rte rte; +} RteStreamInfo; + +typedef struct RteStream { + RteHandle handle; +} RteStream; + +// @{ +// Observer +AGORA_RTE_API_C RteStreamObserver *RteStreamObserverCreate(RteError *err); +AGORA_RTE_API_C void RteStreamObserverDestroy(RteStreamObserver *observer, + RteError *err); +// @} + +// @{ +// Config +AGORA_RTE_API_C void RteStreamConfigInit(RteStreamConfig *config, RteError *err); +AGORA_RTE_API_C void RteStreamConfigDeinit(RteStreamConfig *config, + RteError *err); + +AGORA_RTE_API_C void RteStreamConfigSetChannel(RteStreamConfig *self, + RteChannel *channel, + RteError *err); + +AGORA_RTE_API_C void RteStreamConfigSetStreamType(RteStreamConfig *self, + RteStreamType type, + RteError *err); +AGORA_RTE_API_C void RteStreamConfigGetStreamType(RteStreamConfig *self, + RteStreamType *type, + RteError *err); + +AGORA_RTE_API_C void RteStreamConfigSetStreamId(RteStreamConfig *self, + RteString *stream_id, + RteError *err); +AGORA_RTE_API_C void RteStreamConfigGetStreamId(RteStreamConfig *self, + RteString *stream_id, + RteError *err); + +AGORA_RTE_API_C void RteStreamConfigSetEncryptionMode(RteStreamConfig *self, + RteEncryptionMode mode, + RteError *err); +AGORA_RTE_API_C void RteStreamConfigGetEncryptionMode(RteStreamConfig *self, + RteEncryptionMode *mode, + RteError *err); + +AGORA_RTE_API_C void RteStreamConfigSetEncryptionKey(RteStreamConfig *self, + RteString *encryption_key, + RteError *err); +AGORA_RTE_API_C void RteStreamConfigGetEncryptionKey(RteStreamConfig *self, + RteString *encryption_key, + RteError *err); + +AGORA_RTE_API_C void RteStreamConfigSetEncryptionKdfSalt( + RteStreamConfig *self, uint8_t *encryption_kdf_salt, RteError *err); +AGORA_RTE_API_C void RteStreamConfigGetEncryptionKdfSalt( + RteStreamConfig *self, uint8_t *encryption_kdf_salt, RteError *err); + +AGORA_RTE_API_C void RteStreamConfigSetAudioEncoderProfile( + RteStreamConfig *self, RteAudioEncoderProfile profile, RteError *err); +AGORA_RTE_API_C void RteStreamConfigGetAudioEncoderProfile( + RteStreamConfig *self, RteAudioEncoderProfile *profile, RteError *err); + +AGORA_RTE_API_C void RteStreamConfigSetWidth(RteStreamConfig *self, + uint32_t width, RteError *err); +AGORA_RTE_API_C void RteStreamConfigGetWidth(RteStreamConfig *self, + uint32_t *width, RteError *err); +AGORA_RTE_API_C void RteStreamConfigSetHeight(RteStreamConfig *self, + uint32_t height, RteError *err); +AGORA_RTE_API_C void RteStreamConfigGetHeight(RteStreamConfig *self, + uint32_t *height, RteError *err); + +AGORA_RTE_API_C void RteStreamConfigSetFrameRate(RteStreamConfig *self, + uint32_t frame_rate, + RteError *err); +AGORA_RTE_API_C void RteStreamConfigGetFrameRate(RteStreamConfig *self, + uint32_t *frame_rate, + RteError *err); + +AGORA_RTE_API_C void RteStreamConfigSetMinBitRate(RteStreamConfig *self, + uint32_t min_bitrate, + RteError *err); +AGORA_RTE_API_C void RteStreamConfigGetMinBitRate(RteStreamConfig *self, + uint32_t *min_bitrate, + RteError *err); + +AGORA_RTE_API_C void RteStreamConfigSetOrientationMode( + RteStreamConfig *self, RteOrientationMode orientation_mode, RteError *err); +AGORA_RTE_API_C void RteStreamConfigGetOrientationMode( + RteStreamConfig *self, RteOrientationMode *orientation_mode, RteError *err); + +AGORA_RTE_API_C void RteStreamConfigSetVideoDegradationPreference( + RteStreamConfig *self, RteVideoDegradationPreference degradation_preference, + RteError *err); +AGORA_RTE_API_C void RteStreamConfigGetVideoDegradationPreference( + RteStreamConfig *self, + RteVideoDegradationPreference *degradation_preference, RteError *err); + +AGORA_RTE_API_C void RteStreamConfigSetVideoMirrorMode( + RteStreamConfig *self, RteVideoMirrorMode mirror_mode, RteError *err); +AGORA_RTE_API_C void RteStreamConfigGetVideoMirrorMode( + RteStreamConfig *self, RteVideoMirrorMode *mirror_mode, RteError *err); + +AGORA_RTE_API_C void RteStreamConfigSetCheHwDecoding(RteStreamConfig *self, + bool che_hw_decoding, + RteError *err); +AGORA_RTE_API_C void RteStreamConfigGetCheHwDecoding(RteStreamConfig *self, + bool *che_hw_decoding, + RteError *err); + +AGORA_RTE_API_C void RteStreamConfigSetJsonParameter(RteStreamConfig *self, + RteString *json_parameter, + RteError *err); +AGORA_RTE_API_C void RteStreamConfigGetJsonParameter(RteStreamConfig *self, + RteString *json_parameter, + RteError *err); +// @} + +// @{ +// Info +AGORA_RTE_API_C void RteStreamInfoInit(RteStreamInfo *info, RteError *err); +AGORA_RTE_API_C void RteStreamInfoDeinit(RteStreamInfo *info, RteError *err); +// @} + +AGORA_RTE_API_C void RteStreamGetInfo(RteStream *self, RteStreamInfo *info, + RteError *err); + +AGORA_RTE_API_C void RteStreamSetConfigs( + RteStream *self, RteStreamConfig *config, + void (*cb)(RteStream *stream, void *cb_data, RteError *err), void *cb_data); + +AGORA_RTE_API_C bool RteStreamRegisterObserver( + RteStream *self, RteStreamObserver *observer, RteError *err); + +AGORA_RTE_API_C RteAudioTrack RteStreamGetAudioTrack(RteStream *self, + RteError *err); +AGORA_RTE_API_C void RteStreamAddAudioTrack(RteStream *self, + RteAudioTrack *audio_track, + RteError *err); +AGORA_RTE_API_C void RteStreamDelAudioTrack(RteStream *self, + RteAudioTrack *audio_track, + RteError *err); + +AGORA_RTE_API_C RteVideoTrack RteStreamGetVideoTrack(RteStream *self, + RteError *err); +AGORA_RTE_API_C void RteStreamAddVideoTrack(RteStream *self, + RteVideoTrack *video_track, + RteError *err); +AGORA_RTE_API_C void RteStreamDelVideoTrack(RteStream *self, + RteVideoTrack *video_track, + RteError *err); + +AGORA_RTE_API_C RteDataTrack RteStreamGetDataTrack(RteStream *self, + RteError *err); +AGORA_RTE_API_C void RteStreamAddDataTrack(RteStream *self, + RteDataTrack *data_track, + RteError *err); +AGORA_RTE_API_C void RteStreamDelDataTrack(RteStream *self, + RteDataTrack *data_track, + RteError *err); + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/camera_video_track.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/camera_video_track.h new file mode 100644 index 000000000..784c95357 --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/camera_video_track.h @@ -0,0 +1,34 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include "handle.h" +#include "../common.h" +#include "track/local_video_track.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef struct Rte Rte; + +typedef struct RteCameraVideoTrackInitialConfig { + RteLocalVideoTrackInitialConfig local_video_track_initial_config; +} RteCameraVideoTrackInitialConfig; + +typedef struct RteCameraVideoTrackConfig { + RteLocalVideoTrackConfig local_video_track_config; +} RteCameraVideoTrackConfig; + +AGORA_RTE_API_C RteCameraVideoTrack RteCameraVideoTrackCreate( + Rte *rte, RteCameraVideoTrackConfig *config, RteError *err); +AGORA_RTE_API_C void RteCameraVideoTrackDestroy(RteCameraVideoTrack *self, + RteError *err); + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/canvas.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/canvas.h new file mode 100644 index 000000000..d119d09d6 --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/canvas.h @@ -0,0 +1,91 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include "handle.h" +#include "../common.h" +#include "track/view.h" +#include "stream/stream.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef struct RteViewConfig RteViewConfig; + + +typedef enum RteVideoRenderMode { + kRteVideoRenderModeHidden, + kRteVideoRenderModeFit +} RteVideoRenderMode; + +typedef struct RteCanvasInitialConfig { + char placeholder; +} RteCanvasInitialConfig; + + +typedef struct RteCanvasConfig { + + RteVideoRenderMode render_mode; + bool has_render_mode; + + RteVideoMirrorMode mirror_mode; + bool has_mirror_mode; + + RteRect crop_area; + bool has_crop_area; +} RteCanvasConfig; + + +// @{ +// InitialConfig +AGORA_RTE_API_C void RteCanvasInitialConfigInit(RteCanvasInitialConfig *config, + RteError *err); +AGORA_RTE_API_C void RteCanvasInitialConfigDeinit(RteCanvasInitialConfig *config, + RteError *err); +// @} + +// @{ +// Config +AGORA_RTE_API_C void RteCanvasConfigInit(RteCanvasConfig *config, RteError *err); +AGORA_RTE_API_C void RteCanvasConfigDeinit(RteCanvasConfig *config, + RteError *err); + +AGORA_RTE_API_C void RteCanvasConfigSetVideoRenderMode(RteCanvasConfig *self, RteVideoRenderMode render_mode, RteError *err); + +AGORA_RTE_API_C void RteCanvasConfigGetVideoRenderMode(RteCanvasConfig *self, RteVideoRenderMode *render_mode, RteError *err); + +AGORA_RTE_API_C void RteCanvasConfigSetVideoMirrorMode(RteCanvasConfig *self, RteVideoMirrorMode mirror_mode, RteError *err); + +AGORA_RTE_API_C void RteCanvasConfigGetVideoMirrorMode(RteCanvasConfig *self, RteVideoMirrorMode *mirror_mode, RteError *err); + +AGORA_RTE_API_C void RteCanvasConfigSetCropArea(RteCanvasConfig *self, RteRect crop_area, RteError *err); + +AGORA_RTE_API_C void RteCanvasConfigGetCropArea(RteCanvasConfig *self, RteRect *crop_area, RteError *err); + +// @} + +AGORA_RTE_API_C RteCanvas RteCanvasCreate(::Rte *rte, RteCanvasInitialConfig *config, + RteError *err); +AGORA_RTE_API_C void RteCanvasDestroy(RteCanvas *self, RteError *err); + +AGORA_RTE_API_C void RteCanvasGetConfigs(RteCanvas *self, + RteCanvasConfig *config, RteError *err); +AGORA_RTE_API_C void RteCanvasSetConfigs( + RteCanvas *self, RteCanvasConfig *config, + void (*cb)(RteCanvas *canvas, void *cb_data, RteError *err), void *cb_data); + +AGORA_RTE_API_C void RteCanvasAddView( + RteCanvas *self, RteView *view, RteViewConfig *config, + void (*cb)(RteCanvas *canvas, RteView *view, void *cb_data, RteError *err), + void *cb_data); + +AGORA_RTE_API_C void RteCanvasRemoveView(RteCanvas *self, RteView *view, RteViewConfig *config, RteError *err); + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/layout.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/layout.h new file mode 100644 index 000000000..7ff31ee76 --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/layout.h @@ -0,0 +1,19 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef struct RteLayout { + char placeholder; +} RteLayout; + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/local_audio_track.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/local_audio_track.h new file mode 100644 index 000000000..cb98f95e6 --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/local_audio_track.h @@ -0,0 +1,101 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include "../common.h" +#include "track/local_track.h" +#include "utils/frame.h" +#include "utils/string.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef struct RteLocalAudioTrackConfig { + RteLocalTrackConfig local_track_config; +} RteLocalAudioTrackConfig; + +typedef struct RteLocalAudioTrackObserver RteLocalAudioTrackObserver; +struct RteLocalAudioTrackObserver { + RteLocalTrackObserver local_track_observer; + + void (*on_frame)(RteLocalAudioTrackObserver *self, + RteAudioFrame *audio_frame); +}; + +typedef struct RteLocalAudioTrackInfo { + RteLocalTrackInfo local_track_info; +} RteLocalAudioTrackInfo; + +// @{ +// Config +AGORA_RTE_API_C void RteLocalAudioTrackConfigInit( + RteLocalAudioTrackConfig *config, RteError *err); +AGORA_RTE_API_C void RteLocalAudioTrackConfigDeinit( + RteLocalAudioTrackConfig *config, RteError *err); + +AGORA_RTE_API_C void RteLocalAudioTrackConfigSetPublishVolume( + RteLocalAudioTrackConfig *self, uint32_t volume, RteError *err); +AGORA_RTE_API_C void RteLocalAudioTrackConfigGetPublishVolume( + RteLocalAudioTrackConfig *self, uint32_t *volume, RteError *err); + +AGORA_RTE_API_C void RteLocalAudioTrackConfigSetLoopbackVolume( + RteLocalAudioTrackConfig *self, uint32_t volume, RteError *err); +AGORA_RTE_API_C void RteLocalAudioTrackConfigGetLoopbackVolume( + RteLocalAudioTrackConfig *self, uint32_t *volume, RteError *err); + +AGORA_RTE_API_C void RteLocalAudioTrackConfigSetEnableLoopbackFilter( + RteLocalAudioTrackConfig *self, bool enable_loopback_filter, RteError *err); +AGORA_RTE_API_C void RteLocalAudioTrackConfigGetEnableLoopbackFilter( + RteLocalAudioTrackConfig *self, bool *enable_loopback_filter, + RteError *err); + +AGORA_RTE_API_C void RteLocalAudioTrackConfigSetEnablePublishFilter( + RteLocalAudioTrackConfig *self, bool enable_publish_filter, RteError *err); +AGORA_RTE_API_C void RteLocalAudioTrackConfigGetEnablePublishFilter( + RteLocalAudioTrackConfig *self, bool *enable_publish_filter, RteError *err); + +AGORA_RTE_API_C void RteLocalAudioTrackConfigSetJsonParameter( + RteLocalAudioTrackConfig *self, RteString *json_parameter, RteError *err); +AGORA_RTE_API_C void RteLocalAudioTrackConfigGetJsonParameter( + RteLocalAudioTrackConfig *self, RteString *json_parameter, RteError *err); +// @} + +// @{ +// Track observer +AGORA_RTE_API_C RteLocalAudioTrackObserver *RteLocalAudioTrackObserverCreate( + RteError *err); +AGORA_RTE_API_C void RteLocalAudioTrackObserverDestroy( + RteLocalAudioTrackObserver *self, RteError *err); +//} + +AGORA_RTE_API_C void RteLocalAudioTrackInit(RteLocalAudioTrack *self, + RteError *err); +AGORA_RTE_API_C void RteLocalAudioTrackDeinit(RteLocalAudioTrack *self, + RteError *err); + +AGORA_RTE_API_C void RteLocalAudioTrackGetConfigs( + RteLocalAudioTrack *self, RteLocalAudioTrackConfig *config, RteError *err); +AGORA_RTE_API_C void RteLocalAudioTrackSetConfigs( + RteLocalAudioTrack *self, RteLocalAudioTrackConfig *config, + void (*cb)(RteLocalAudioTrack *track, void *cb_data, RteError *err), + void *cb_data); + +AGORA_RTE_API_C void RteLocalAudioTrackEnableLoopback(RteLocalAudioTrack *self, + RteError *err); + +AGORA_RTE_API_C void RteLocalAudioTrackRegisterTrackObserver( + RteLocalAudioTrack *self, RteLocalAudioTrackObserver *observer, + void (*destroyer)(RteLocalAudioTrackObserver *self, RteError *err), + RteError *err); +AGORA_RTE_API_C void RteLocalAudioTrackUnregisterTrackObserver( + RteLocalAudioTrack *self, RteLocalAudioTrackObserver *observer, + RteError *err); + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/local_track.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/local_track.h new file mode 100644 index 000000000..3d5e4ecda --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/local_track.h @@ -0,0 +1,56 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include "c_error.h" +#include "../common.h" +#include "handle.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef struct RteLocalTrackInitialConfig { + char placeholder; +} RteLocalTrackInitialConfig; + +typedef struct RteLocalTrackConfig { + char placeholder; +} RteLocalTrackConfig; + +typedef struct RteLocalTrackInfo { + char placeholder; +} RteLocalTrackInfo; + +typedef struct RteLocalTrackObserver { + char placeholder; +} RteLocalTrackObserver; + +// @{ +// Info +AGORA_RTE_API_C void RteLocalTrackInfoInit(RteLocalTrackInfo *info, + RteError *err); +AGORA_RTE_API_C void RteLocalTrackInfoDeinit(RteLocalTrackInfo *info, + RteError *err); +//} + +AGORA_RTE_API_C void RteLocalTrackStart(RteLocalTrack *self, + void (*cb)(RteLocalTrack *self, + void *cb_data, RteError *err), + void *cb_data); +AGORA_RTE_API_C void RteLocalTrackStop(RteLocalTrack *self, + void (*cb)(RteLocalTrack *self, + void *cb_data, RteError *err), + void *cb_data); + +AGORA_RTE_API_C void RteLocalTrackGetInfo(RteLocalTrack *self, + RteLocalTrackInfo *info, + RteError *err); + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/local_video_track.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/local_video_track.h new file mode 100644 index 000000000..0333b66b9 --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/local_video_track.h @@ -0,0 +1,25 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include "track/local_track.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef struct RteLocalVideoTrackInitialConfig { + RteLocalTrackInitialConfig local_track_initial_config; +} RteLocalVideoTrackInitialConfig; + +typedef struct RteLocalVideoTrackConfig { + RteLocalTrackConfig local_track_config; +} RteLocalVideoTrackConfig; + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/mic_audio_track.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/mic_audio_track.h new file mode 100644 index 000000000..2d08930f7 --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/mic_audio_track.h @@ -0,0 +1,121 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include "handle.h" +#include "../common.h" +#include "track/local_audio_track.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef enum RteVoiceBeautifierPreset { + kRteVoiceBeautifierPresetOff, + kRteVoiceBeautifierPresetChatBeautifierMagnetic, + kRteVoiceBeautifierPresetChatBeautifierFresh, + kRteVoiceBeautifierPresetChatBeautifierVitality, + kRteVoiceBeautifierPresetSingingBeautifier, + kRteVoiceBeautifierPresetTimbreTransformationVigorous, + kRteVoiceBeautifierPresetTimbreTransformationDeep, + kRteVoiceBeautifierPresetTimbreTransformationMellow, + kRteVoiceBeautifierPresetTimbreTransformationFalsetto, + kRteVoiceBeautifierPresetTimbreTransformationFull, + kRteVoiceBeautifierPresetTimbreTransformationClear, + kRteVoiceBeautifierPresetTimbreTransformationResounding, + kRteVoiceBeautifierPresetTimbreTransformationRinging, + kRteVoiceBeautifierPresetUltraHighQualityVoice +} RteVoiceBeautifierPreset; + +typedef enum RteAudioEffectPreset { + kRteAudioEffectPresetOff, + kRteAudioEffectPresetKtv, + kRteAudioEffectPresetVocalConcert, + kRteAudioEffectPresetStudio, + kRteAudioEffectPresetPhonograph, + kRteAudioEffectPresetVirtualStereo, + kRteAudioEffectPresetSpecial, + kRteAudioEffectPresetEthereal, + kRteAudioEffectPresetAcoustics3DVoice, + kRteAudioEffectPresetVirtualSurroundSound, + kRteAudioEffectPresetVoiceChangerEffectUncle, + kRteAudioEffectPresetVoiceChangerEffectOldMan, + kRteAudioEffectPresetVoiceChangerEffectBoy, + kRteAudioEffectPresetVoiceChangerEffectSister, + kRteAudioEffectPresetVoiceChangerEffectGirl, + kRteAudioEffectPresetVoiceChangerEffectPigKing, + kRteAudioEffectPresetVoiceChangerEffectHulk, + kRteAudioEffectPresetStyleTransformationRnb, + kRteAudioEffectPresetStyleTransformationPopular, + kRteAudioEffectPresetPitchCorrection +} RteAudioEffectPreset; + +typedef enum RteVoiceConversionPreset { + kRteVoiceConversionPresetOff, + kRteVoiceConversionPresetNeutral, + kRteVoiceConversionPresetSweet, + kRteVoiceConversionPresetSolid, + kRteVoiceConversionPresetBass, + kRteVoiceConversionPresetCartoon, + kRteVoiceConversionPresetChildlike, + kRteVoiceConversionPresetPhoneOperator, + kRteVoiceConversionPresetMonster, + kRteVoiceConversionPresetTransformers, + kRteVoiceConversionPresetGroot, + kRteVoiceConversionPresetDarthVader, + kRteVoiceConversionPresetIronLady, + kRteVoiceConversionPresetShinChan, + kRteVoiceConversionPresetGirlishMan, + kRteVoiceConversionPresetChipMunk +} RteVoiceConversionPreset; + +typedef struct RteMicAudioTrackConfig { + RteLocalAudioTrackConfig local_audio_track_config; +} RteMicAudioTrackConfig; + +typedef struct RteMicAudioTrackInfo { + RteLocalAudioTrackInfo local_audio_track_info; +} RteMicAudioTrackInfo; + +// @{ +// Info +AGORA_RTE_API_C void RteMicAudioTrackInfoInit(RteMicAudioTrackInfo *self, + RteError *err); +AGORA_RTE_API_C void RteMicAudioTrackInfoDeinit(RteMicAudioTrackInfo *self, + RteError *err); +//} + +AGORA_RTE_API_C RteMicAudioTrack RteMicAudioTrackCreate( + Rte *self, RteMicAudioTrackConfig *config, RteError *err); +AGORA_RTE_API_C void RteMicAudioTrackDestroy(RteMicAudioTrack *self, + RteError *err); + +AGORA_RTE_API_C void RteMicAudioTrackGetConfigs(RteMicAudioTrack *self, + RteMicAudioTrackConfig *config, + RteError *err); +AGORA_RTE_API_C void RteMicAudioTrackSetConfigs( + RteMicAudioTrack *self, RteMicAudioTrackConfig *config, + void (*cb)(RteMicAudioTrack *track, void *cb_data, RteError *err), + void *cb_data); + +AGORA_RTE_API_C void RteMicAudioTrackSetVoiceBeautifierPreset( + RteMicAudioTrack *self, RteVoiceBeautifierPreset preset, + void (*cb)(RteError *err), void *cb_data); +AGORA_RTE_API_C void RteMicAudioTrackSetAudioEffectPreset( + RteMicAudioTrack *self, RteAudioEffectPreset preset, + void (*cb)(RteError *err), void *cb_data); +AGORA_RTE_API_C void RteMicAudioTrackSetVoiceConversionPreset( + RteMicAudioTrack *self, RteVoiceConversionPreset preset, + void (*cb)(RteError *err), void *cb_data); + +AGORA_RTE_API_C void RteMicAudioTrackGetInfo(RteMicAudioTrack *self, + RteMicAudioTrackInfo *info, + RteError *err); + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/mixed_video_track.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/mixed_video_track.h new file mode 100644 index 000000000..ff0dcf20c --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/mixed_video_track.h @@ -0,0 +1,35 @@ +#pragma once + +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#include + +#include "handle.h" +#include "../common.h" +#include "track/local_video_track.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef struct RteLayout RteLayout; + +typedef struct RteMixedVideoTrackConfig { + RteLocalVideoTrackConfig local_video_track_config; +} RteMixedVideoTrackConfig; + +AGORA_RTE_API_C size_t +RteMixedVideoTrackGetLayoutsCount(RteMixedVideoTrack *self, RteError *err); +AGORA_RTE_API_C void RteMixedVideoTrackGetLayouts(RteMixedVideoTrack *self, + RteLayout *layouts, + size_t start_idx, + size_t layouts_cnt, + RteError *err); + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/remote_audio_track.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/remote_audio_track.h new file mode 100644 index 000000000..936059f75 --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/remote_audio_track.h @@ -0,0 +1,87 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include + +#include "../common.h" +#include "track/remote_track.h" +#include "utils/frame.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef struct RteRemoteAudioTrackConfig { + RteRemoteTrackConfig remote_track_config; + + uint32_t playback_volume; + bool has_playback_volume; +} RteRemoteAudioTrackConfig; + +typedef struct RteRemoteAudioTrackObserver RteRemoteAudioTrackObserver; +struct RteRemoteAudioTrackObserver { + RteRemoteTrackObserver remote_track_observer; + + void (*on_frame)(RteRemoteAudioTrackObserver *self, + RteAudioFrame *audio_frame); +}; + +typedef struct RteRemoteAudioTrackInfo { + RteRemoteTrackInfo remote_track_info; +} RteRemoteAudioTrackInfo; + +// @{ +// Config +AGORA_RTE_API_C void RteRemoteAudioTrackConfigInit( + RteRemoteAudioTrackConfig *config, RteError *err); +AGORA_RTE_API_C void RteRemoteAudioTrackConfigDeinit( + RteRemoteAudioTrackConfig *config, RteError *err); + +AGORA_RTE_API_C void RteRemoteAudioTrackConfigSetPlaybackVolume( + RteRemoteAudioTrackConfig *self, uint32_t volume, RteError *err); +AGORA_RTE_API_C void RteRemoteAudioTrackConfigGetPlaybackVolume( + RteRemoteAudioTrackConfig *self, uint32_t *volume, RteError *err); + +AGORA_RTE_API_C void RteRemoteAudioTrackConfigSetJsonParameter( + RteRemoteAudioTrackConfig *self, RteString *json_parameter, RteError *err); +AGORA_RTE_API_C void RteRemoteAudioTrackConfigGetJsonParameter( + RteRemoteAudioTrackConfig *self, RteString *json_parameter, RteError *err); +// @} + +// @{ +// Track observer +AGORA_RTE_API_C RteRemoteAudioTrackObserver *RteRemoteAudioTrackObserverCreate( + RteError *err); +AGORA_RTE_API_C void RteRemoteAudioTrackObserverDestroy( + RteRemoteAudioTrackObserver *self, RteError *err); +//} + +AGORA_RTE_API_C void RteRemoteAudioTrackInit(RteRemoteAudioTrack *self, + RteError *err); +AGORA_RTE_API_C void RteRemoteAudioTrackDeinit(RteRemoteAudioTrack *self, + RteError *err); + +AGORA_RTE_API_C void RteRemoteAudioTrackGetConfigs( + RteRemoteAudioTrack *self, RteRemoteAudioTrackConfig *config, + RteError *err); +AGORA_RTE_API_C void RteRemoteAudioTrackSetConfigs( + RteRemoteAudioTrack *self, RteRemoteAudioTrackConfig *config, + void (*cb)(RteRemoteAudioTrack *track, void *cb_data, RteError *err), + void *cb_data); + +AGORA_RTE_API_C void RteRemoteAudioTrackRegisterTrackObserver( + RteRemoteAudioTrack *self, RteRemoteAudioTrackObserver *observer, + void (*destroyer)(RteRemoteAudioTrackObserver *self, RteError *err), + RteError *err); +AGORA_RTE_API_C void RteRemoteAudioTrackUnregisterTrackObserver( + RteRemoteAudioTrack *self, RteRemoteAudioTrackObserver *observer, + RteError *err); + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/remote_track.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/remote_track.h new file mode 100644 index 000000000..698134403 --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/remote_track.h @@ -0,0 +1,43 @@ +#pragma once + +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#include "handle.h" +#include "../common.h" +#include "c_error.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef struct RteRemoteTrackConfig { + char placeholder; +} RteRemoteTrackConfig; + +typedef struct RteRemoteTrackInfo { + char placeholder; +} RteRemoteTrackInfo; + +typedef struct RteRemoteTrackObserver { + char placeholder; +} RteRemoteTrackObserver; + +// @{ +// Info +AGORA_RTE_API_C void RteRemoteTrackInfoInit(RteRemoteTrackInfo *info, + RteError *err); +AGORA_RTE_API_C void RteRemoteTrackInfoDeinit(RteRemoteTrackInfo *info, + RteError *err); +//} + +AGORA_RTE_API_C void RteRemoteTrackGetInfo(RteRemoteTrack *self, + RteRemoteTrackInfo *info, + RteError *err); + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/remote_video_track.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/remote_video_track.h new file mode 100644 index 000000000..c2f79e366 --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/remote_video_track.h @@ -0,0 +1,21 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include "track/remote_track.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef struct RteRemoteVideoTrackConfig { + RteRemoteTrackConfig remote_track_config; +} RteRemoteVideoTrackConfig; + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/screen_video_track.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/screen_video_track.h new file mode 100644 index 000000000..f14505f3b --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/screen_video_track.h @@ -0,0 +1,61 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include "handle.h" +#include "../common.h" +#include "track/local_video_track.h" +#include "utils/rect.h" +#include "utils/string.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef enum RteScreenCaptureType { + kRteScreenCaptureTypeScreen, + kRteScreenCaptureTypeWindow, +} RteScreenCaptureType; + +typedef void *RteWindow; +typedef void *RteMonitor; + +typedef struct RteScreenVideoTrackConfig { + RteLocalVideoTrackConfig local_video_track_config; + + RteScreenCaptureType type; + bool has_type; + + RteWindow window; + bool has_window; + + RteMonitor monitor; + bool has_monitor; + + RteRect rect; + bool has_rect; + + RteString *json_parameter; + bool has_json_parameter; +} RteScreenVideoTrackConfig; + +AGORA_RTE_API_C RteScreenVideoTrack RteScreenVideoTrackCreate( + Rte *rte, RteScreenVideoTrackConfig *config, RteError *err); +AGORA_RTE_API_C void RteScreenVideoTrackDestroy(RteScreenVideoTrack *self, + RteError *err); + +AGORA_RTE_API_C void RteScreenVideoTrackGetConfigs( + RteScreenVideoTrack *self, RteScreenVideoTrackConfig *config, + RteError *err); +AGORA_RTE_API_C void RteScreenVideoTrackSetConfigs( + RteScreenVideoTrack *self, RteScreenVideoTrackConfig *config, + void (*cb)(RteScreenVideoTrack *track, void *cb_data, RteError *err), + void *cb_data); + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/track.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/track.h new file mode 100644 index 000000000..1dbd4efb9 --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/track.h @@ -0,0 +1,54 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include "handle.h" +#include "../common.h" +#include "c_error.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef struct RteStream RteStream; + +typedef enum RteTrackMediaType { + kRteTrackMediaTypeAudio, + kRteTrackMediaTypeVideo, + kRteTrackMediaTypeData +} RteTrackMediaType; + +typedef enum RteTrackSrc { + kRteTrackSrcUnknown, + kRteTrackSrcMix, + kRteTrackSrcNetwork, + kRteTrackSrcMicrophone, + kRteTrackSrcLoopbackRecording, + kRteTrackSrcCamera, + kRteTrackSrcScreen, + kRteTrackSrcCustom, +} RteTrackSrc; + +typedef struct RteTrackConfig { + char placeholder; +} RteTrackConfig; + +typedef struct RteTrackInfo { + RteStream *stream; +} RteTrackInfo; + +// @{ +// Info +AGORA_RTE_API_C void RteTrackInfoInit(RteTrackInfo *info, RteError *err); +AGORA_RTE_API_C void RteTrackInfoDeinit(RteTrackInfo *info, RteError *err); +//} + +AGORA_RTE_API_C void RteTrackGetInfo(RteTrack *self, RteTrackInfo *info, RteError *err); + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/video_track.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/video_track.h new file mode 100644 index 000000000..86e199501 --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/video_track.h @@ -0,0 +1,37 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include "handle.h" +#include "../common.h" +#include "track/track.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef enum RteVideoPipelinePosition { + kRteVideoPipelinePositionLocalPostCapturer, + kRteVideoPipelinePositionLocalPostFilters, + kRteVideoPipelinePositionLocalPreEncoder, + kRteVideoPipelinePositionRemotePreRenderer +} RteVideoPipelinePosition; + +typedef struct RteVideoTrackConfig { + RteTrackConfig track_config; +} RteVideoTrackConfig; + +AGORA_RTE_API_C void RteVideoTrackSetCanvas(RteVideoTrack *self, RteCanvas *canvas, + RteVideoPipelinePosition position, + void (*cb)(RteVideoTrack *self, + RteCanvas *canvas, void *cb_data, + RteError *err), + void *cb_data); + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/view.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/view.h new file mode 100644 index 000000000..3db699114 --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/track/view.h @@ -0,0 +1,55 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include "c_error.h" +#include "../common.h" +#include "utils/rect.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef struct Rte Rte; +typedef struct RteCanvas RteCanvas; +typedef void *RteView; + +typedef struct RteViewConfig { + RteRect crop_area; +} RteViewConfig; + +typedef struct RteViewInfo { + RteCanvas *attached_canvas; +} RteViewInfo; + +// @{ +// Config +AGORA_RTE_API_C void RteViewConfigInit(RteViewConfig *config, RteError *err); +AGORA_RTE_API_C void RteViewConfigDeinit(RteViewConfig *config, RteError *err); + +AGORA_RTE_API_C void RteViewConfigSetCropArea(RteViewConfig *self, + RteRect crop_area, RteError *err); +AGORA_RTE_API_C void RteViewConfigGetCropArea(RteViewConfig *self, + RteRect *crop_area, RteError *err); +// @} + +// @{ +// Info +AGORA_RTE_API_C void RteViewInfoInit(RteViewInfo *info, RteError *err); +AGORA_RTE_API_C void RteViewInfoDeinit(RteViewInfo *info, RteError *err); +// @} + +AGORA_RTE_API_C RteView RteViewCreate(Rte *self, RteViewConfig *config, + RteError *err); +AGORA_RTE_API_C void RteViewDestroy(RteView *self, RteError *err); + +AGORA_RTE_API_C void RteViewGetInfo(RteView *self, RteViewInfo *info, + RteError *err); + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/user/local_user.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/user/local_user.h new file mode 100644 index 000000000..67c0c90e7 --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/user/local_user.h @@ -0,0 +1,120 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include "handle.h" +#include "../common.h" +#include "user/user.h" +#include "utils/buf.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef struct RteLocalUserConfig { + RteUserConfig user_config; +} RteLocalUserConfig; + +typedef struct RteLocalUserInfo { + RteUserInfo user_info; +} RteLocalUserInfo; + +typedef struct RteLocalUserObserver RteLocalUserObserver; +struct RteLocalUserObserver { + RteUserObserver base_observer; + + void (*on_user_message_received)(RteLocalUserObserver *self, + RteString *publisher, RteBuf *message); +}; + +// @{ +// Config +AGORA_RTE_API_C void RteLocalUserConfigInit(RteLocalUserConfig *config, + RteError *err); +AGORA_RTE_API_C void RteLocalUserConfigDeinit(RteLocalUserConfig *config, + RteError *err); + +AGORA_RTE_API_C void RteLocalUserConfigSetUserId(RteLocalUserConfig *self, + RteString *user_id, + RteError *err); +AGORA_RTE_API_C void RteLocalUserConfigGetUserId(RteLocalUserConfig *self, + RteString *user_id, + RteError *err); + +AGORA_RTE_API_C void RteLocalUserConfigSetUserName(RteLocalUserConfig *self, + RteString *user_name, + RteError *err); +AGORA_RTE_API_C void RteLocalUserConfigGetUserName(RteLocalUserConfig *self, + RteString *user_name, + RteError *err); + +AGORA_RTE_API_C void RteLocalUserConfigSetUserToken(RteLocalUserConfig *self, + RteString *user_token, + RteError *err); +AGORA_RTE_API_C void RteLocalUserConfigGetUserToken(RteLocalUserConfig *self, + RteString *user_token, + RteError *err); + +AGORA_RTE_API_C void RteLocalUserConfigSetJsonParameter( + RteLocalUserConfig *self, RteString *json_parameter, RteError *err); +AGORA_RTE_API_C void RteLocalUserConfigGetJsonParameter( + RteLocalUserConfig *self, RteString *json_parameter, RteError *err); +// @} + +// @{ +// Info +AGORA_RTE_API_C void RteLocalUserInfoInit(RteLocalUserInfo *info, RteError *err); +AGORA_RTE_API_C void RteLocalUserInfoDeinit(RteLocalUserInfo *info, + RteError *err); +// @} + +// @{ +// Observer +AGORA_RTE_API_C RteLocalUserObserver *RteLocalUserObserverCreate(RteError *err); +AGORA_RTE_API_C void RteLocalUserObserverDestroy(RteLocalUserObserver *self, + RteError *err); + +AGORA_RTE_API_C RteLocalUser +RteLocalUserObserverGetEventSrc(RteLocalUserObserver *self, RteError *err); +// @} + +RteLocalUser RteLocalUserCreate(Rte *self, RteLocalUserConfig *config, + RteError *err); +AGORA_RTE_API_C void RteLocalUserDestroy(RteLocalUser *self, RteError *err); + +AGORA_RTE_API_C void RteLocalUserGetConfigs(RteLocalUser *self, + RteLocalUserConfig *config, + RteError *err); +AGORA_RTE_API_C void RteLocalUserSetConfigs( + RteLocalUser *self, RteLocalUserConfig *config, + void (*cb)(RteLocalUser *user, void *cb_data, RteError *err), + void *cb_data); + +AGORA_RTE_API_C void RteLocalUserGetInfo(RteLocalUser *self, + RteLocalUserInfo *info, RteError *err); + +AGORA_RTE_API_C void RteLocalUserLogin(RteLocalUser *self, + void (*cb)(void *cb_data, RteError *err), + void *cb_data); +AGORA_RTE_API_C void RteLocalUserLogout(RteLocalUser *self, + void (*cb)(void *cb_data, RteError *err), + void *cb_data); +AGORA_RTE_API_C bool RteLocalUserIsLogin(RteLocalUser *self); + +AGORA_RTE_API_C void RteLocalUserPublishMessage( + RteLocalUser *self, const char *user_name, RteBuf *message, + void (*cb)(RteLocalUser *self, void *cb_data, RteError *err), + void *cb_data); + +AGORA_RTE_API_C bool RteLocalUserRegisterObserver( + RteLocalUser *self, RteLocalUserObserver *observer, RteError *err); +AGORA_RTE_API_C bool RteLocalUserUnregisterObserver( + RteLocalUser *self, RteLocalUserObserver *observer, RteError *err); + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/user/remote_user.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/user/remote_user.h new file mode 100644 index 000000000..98c90a7a5 --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/user/remote_user.h @@ -0,0 +1,77 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include "handle.h" +#include "../common.h" +#include "user/user.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef struct RteRemoteUserConfig { + RteUserConfig user_config; +} RteRemoteUserConfig; + +typedef struct RteRemoteUserInfo { + RteUserInfo user_info; +} RteRemoteUserInfo; + +typedef struct RteRemoteUserObserver { + RteUserObserver user_observer; +} RteRemoteUserObserver; + +// @{ +// Config +AGORA_RTE_API_C void RteRemoteUserConfigInit(RteRemoteUserConfig *config, + RteError *err); +AGORA_RTE_API_C void RteRemoteUserConfigDeinit(RteRemoteUserConfig *config, + RteError *err); + +AGORA_RTE_API_C void RteRemoteUserConfigSetJsonParameter( + RteRemoteUserConfig *self, RteString *json_parameter, RteError *err); +AGORA_RTE_API_C void RteRemoteUserConfigGetJsonParameter( + RteRemoteUserConfig *self, RteString *json_parameter, RteError *err); +// @} + +// @{ +// Info +AGORA_RTE_API_C void RteRemoteUserInfoInit(RteRemoteUserInfo *info, + RteError *err); +AGORA_RTE_API_C void RteRemoteUserInfoDeinit(RteRemoteUserInfo *info, + RteError *err); +// @} + +AGORA_RTE_API_C void RteRemoteUserGetConfigs(RteRemoteUser *self, + RteRemoteUserConfig *config, + RteError *err); +AGORA_RTE_API_C void RteRemoteUserSetConfigs( + RteRemoteUser *self, RteRemoteUserConfig *config, + void (*cb)(RteRemoteUser *user, void *cb_data, RteError *err), + void *cb_data); + +AGORA_RTE_API_C void RteRemoteUserGetInfo(RteRemoteUser *self, + RteRemoteUserInfo *info, + RteError *err); + +AGORA_RTE_API_C bool RteRemoteUserRegisterObserver( + RteRemoteUser *self, RteRemoteUserObserver *observer, RteError *err); +AGORA_RTE_API_C bool RteRemoteUserUnregisterObserver( + RteRemoteUser *self, RteRemoteUserObserver *observer, RteError *err); + +AGORA_RTE_API_C RteRemoteUserObserver *RteRemoteUserObserverCreate( + RteError *err); +AGORA_RTE_API_C void RteRemoteUserObserverDestroy(RteRemoteUserObserver *self, + RteError *err); + +AGORA_RTE_API_C RteRemoteUser +RteRemoteUserObserverGetEventSrc(RteRemoteUserObserver *self, RteError *err); + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/user/user.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/user/user.h new file mode 100644 index 000000000..c1c955e49 --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/user/user.h @@ -0,0 +1,84 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include "c_error.h" +#include "handle.h" +#include "info.h" +#include "metadata.h" +#include "observer.h" +#include "../common.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef struct RteUserConfig { + char placeholder; +} RteUserConfig; + +typedef struct RteUserInfo { + RteBaseInfo base_info; + + RteString *user_name; + RteString *user_id; +} RteUserInfo; + +typedef struct RteUserObserver { + RteBaseObserver base_observer; +} RteUserObserver; + +AGORA_RTE_API_C void RteUserConfigInit(RteUserConfig *config, RteError *err); +AGORA_RTE_API_C void RteUserConfigDeinit(RteUserConfig *config, RteError *err); + +AGORA_RTE_API_C void RteUserConfigSetJsonParameter(RteUserConfig *self, + RteString *json_parameter, + RteError *err); +AGORA_RTE_API_C void RteUserConfigGetJsonParameter(RteUserConfig *self, + RteString *json_parameter, + RteError *err); + +AGORA_RTE_API_C void RteUserInit(RteUser *self, RteUserConfig *config, + RteError *err); +AGORA_RTE_API_C void RteUserDeinit(RteUser *self, RteError *err); + +AGORA_RTE_API_C void RteUserGetConfigs(RteUser *self, RteUserConfig *config, + RteError *err); +AGORA_RTE_API_C void RteUserSetConfigs(RteUser *self, RteUserConfig *config, + void (*cb)(RteUser *user, void *cb_data, + RteError *err), + void *cb_data); + +AGORA_RTE_API_C bool RteUserRegisterObserver( + RteUser *self, RteUserObserver *observer, + RteError *err); +AGORA_RTE_API_C bool RteUserUnregisterObserver(RteUser *self, + RteUserObserver *observer, + RteError *err); + +AGORA_RTE_API_C void RteUserGetMetadata(RteUser *self, const char *user_name, + void (*cb)(RteUser *self, + RteMetadata *items, + void *cb_data, RteError *err), + void *cb_data); +AGORA_RTE_API_C void RteUserSubscribeMetadata( + RteUser *self, const char *user_name, + void (*cb)(RteUser *self, void *cb_data, RteError *err), void *cb_data); +AGORA_RTE_API_C void RteUserUnsubscribeMetadata( + RteUser *self, const char *user_name, + void (*cb)(RteUser *self, void *cb_data, RteError *err), void *cb_data); +AGORA_RTE_API_C void RteUserSetMetadata( + RteUser *self, const char *user_name, RteMetadata *items, + RteMetadataConfig *config, + void (*cb)(RteUser *self, void *cb_data, RteError *err), void *cb_data); +AGORA_RTE_API_C void RteUserRemoveMetadata( + RteUser *self, const char *user_name, RteMetadata *items, + void (*cb)(RteUser *self, void *cb_data, RteError *err), void *cb_data); + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/utils/buf.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/utils/buf.h new file mode 100644 index 000000000..a93b9a40a --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/utils/buf.h @@ -0,0 +1,65 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include +#include + +#include "c_error.h" +#include "../common.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef struct RteBuf { + void *data; + size_t size; + size_t capacity; + bool own; + void *user_data; +} RteBuf; + +AGORA_RTE_API_C void RteBufReset(RteBuf *self, RteError *err); + +AGORA_RTE_API_C void RteBufInit(RteBuf *self, size_t capacity, RteError *err); +AGORA_RTE_API_C void RteBufInitFromBuffer(RteBuf *self, void *buf, size_t size, + RteError *err); +AGORA_RTE_API_C void RteBufInitFromBufferWithOffset(RteBuf *self, size_t offset, + void *buf, size_t size, + RteError *err); +AGORA_RTE_API_C void RteBufInitFromString(RteBuf *self, RteString *str, + RteError *err); +AGORA_RTE_API_C void RteBufInitWithBuffer(RteBuf *self, void *buf, size_t size, + int own, RteError *err); + +AGORA_RTE_API_C RteBuf *RteBufCreate(RteError *err); +AGORA_RTE_API_C RteBuf *RteBufCreateWithCapacity(size_t capacity, RteError *err); + +AGORA_RTE_API_C void RteBufDeinit(RteBuf *self, RteError *err); +AGORA_RTE_API_C void RteBufDestroy(RteBuf *self, RteError *err); + +AGORA_RTE_API_C void RteBufReserve(RteBuf *self, size_t len, RteError *err); + +AGORA_RTE_API_C void RteBufPush(RteBuf *self, const void *src, size_t size, + RteError *err); +AGORA_RTE_API_C void RteBufPop(RteBuf *self, void *dest, size_t size, + RteError *err); + +AGORA_RTE_API_C void RteBufPeek(RteBuf *self, void *dest, size_t size, + RteError *err); + +AGORA_RTE_API_C void RteBufDisown(RteBuf *self, RteError *err); + +AGORA_RTE_API_C void RteBufAppendNullTerminator(RteBuf *self, RteError *err); + +AGORA_RTE_API_C size_t RteBufGetSize(RteBuf *self, RteError *err); +AGORA_RTE_API_C size_t RteBufGetCapacity(RteBuf *self, RteError *err); + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/utils/frame.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/utils/frame.h new file mode 100644 index 000000000..a68efcb6c --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/utils/frame.h @@ -0,0 +1,35 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef enum RteAudioFrameType { + kRteAudioFrameTypePcm16, +} RteAudioFrameType; + +typedef struct RteAudioFrame { + RteAudioFrameType type; + int samples_per_channel; + int bytes_per_sample; + int channels; + int samples_per_sec; + void *buffer; + int64_t render_time_in_ms; + int avsync_type; + int64_t presentation_in_ms; + size_t audio_track_number; +} RteAudioFrame; + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/utils/rect.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/utils/rect.h new file mode 100644 index 000000000..2c67bdcf4 --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/utils/rect.h @@ -0,0 +1,24 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef struct RteRect { + int32_t x; + int32_t y; + int32_t width; + int32_t height; +} RteRect; + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/utils/string.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/utils/string.h new file mode 100644 index 000000000..5950b4526 --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/utils/string.h @@ -0,0 +1,52 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include +#include +#include + +#include "../common.h" + +#define RTE_STRING_PRE_BUF_SIZE 256 + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef struct RteError RteError; +typedef struct RteString RteString; + +AGORA_RTE_API_C RteString *RteStringCreate(RteError *err); +AGORA_RTE_API_C void RteStringDestroy(RteString *self, RteError *err); + +AGORA_RTE_API_C void RteStringInit(RteString *self, RteError *err); +AGORA_RTE_API_C void RteStringInitWithCStr(RteString *self, const char *c_str, + RteError *err); +AGORA_RTE_API_C void RteStringInitWithValue(RteString *self, RteError *err, + const char *fmt, ...); +AGORA_RTE_API_C void RteStringDeinit(RteString *self, RteError *err); + +AGORA_RTE_API_C void RteStringVSet(RteString *self, RteError *err, + const char *fmt, va_list ap); + +AGORA_RTE_API_C void RteStringReserve(RteString *self, size_t extra, + RteError *err); + +AGORA_RTE_API_C void RteStringCopy(RteString *self, const RteString *other, + RteError *err); + +AGORA_RTE_API_C bool RteStringIsEqual(const RteString *self, + const RteString *other, RteError *err); +AGORA_RTE_API_C bool RteStringIsEqualCStr(const RteString *self, + const char *other, RteError *err); + +AGORA_RTE_API_C const char *RteStringCStr(const RteString *self, RteError *err); + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/utils/uuid.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/utils/uuid.h new file mode 100644 index 000000000..7132acd3b --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/internal/c/utils/uuid.h @@ -0,0 +1,23 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef union RteUuid { + uint8_t bytes[16]; + uint32_t dwords[4]; + uint64_t qwords[2]; +} RteUuid; + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/rte_cpp.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/rte_cpp.h new file mode 100644 index 000000000..c23be31a6 --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/rte_cpp.h @@ -0,0 +1,14 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include "rte_cpp_error.h" // IWYU pragma: export +#include "rte_cpp_player.h" // IWYU pragma: export +#include "rte_cpp_rte.h" // IWYU pragma: export +#include "rte_cpp_canvas.h" // IWYU pragma: export +#include "rte_cpp_string.h" // IWYU pragma: export +#include "rte_cpp_callback_utils.h" // IWYU pragma: export diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/rte_cpp_callback_utils.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/rte_cpp_callback_utils.h new file mode 100644 index 000000000..b3e1ab9b5 --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/rte_cpp_callback_utils.h @@ -0,0 +1,132 @@ +#pragma once +#include +#include "rte_cpp_error.h" +#include "internal/c/handle.h" + +namespace rte { + +template +class SingleUseCallback { + public: + + using CallbackType = std::function; + + SingleUseCallback(){}; + + void Store(T* self, CallbackType cb, void* cb_data){ + self_ = self; + cb_ = cb; + cb_data_ = cb_data; + } + + void Invoke(RteError* err){ + if(cb_ != nullptr){ + cb_(self_, cb_data_, err); + + self_ = nullptr; + cb_ = nullptr; + cb_data_ = nullptr; + } + } + + bool Invalid(){ + return cb_ == nullptr; + } + + CallbackType cb_; + void* cb_data_; + T* self_; +}; // class SingleUseCallback + +template +class CallbackContext { + public: + + using CallbackType = std::function; + using CallbackTypeWithCppError = std::function; + + CallbackContext(T* self, CallbackType cb, void* cb_data) + :self_(self), cb_(cb), cb_data_(cb_data) {} + + CallbackContext(T* self, CallbackTypeWithCppError cb, void* cb_data) + :self_(self), cb_with_cpp_error_(cb), cb_data_(cb_data) {} + + CallbackType cb_; + CallbackTypeWithCppError cb_with_cpp_error_; + void* cb_data_; + T* self_; +}; + +template +void CallbackFunc(FromeType* self, void* cb_data, RteError* err){ + auto *ctx = static_cast*>(cb_data); + + if(ctx->cb_with_cpp_error_ != nullptr){ + rte::Error cpp_err(err); + ctx->cb_with_cpp_error_( self != nullptr ? ctx->self_ : nullptr, ctx->cb_data_, &cpp_err); + } + + if(ctx->cb_ != nullptr){ + ctx->cb_(self != nullptr ? ctx->self_ : nullptr, ctx->cb_data_, err); + } + + delete ctx; +} + +template +class CallbackContextWithArgs { + public: + + using CallbackType = std::function; + using CallbackTypeWithCppError = std::function; + + CallbackContextWithArgs(T* self, CallbackType cb, void* cb_data) + :self_(self), cb_(cb), cb_data_(cb_data) {} + + CallbackContextWithArgs(T* self, CallbackTypeWithCppError cb, void* cb_data) + :self_(self), cb_with_cpp_error_(cb), cb_data_(cb_data) {} + + CallbackType cb_; + CallbackTypeWithCppError cb_with_cpp_error_; + void* cb_data_; + T* self_; +}; + +template +void CallbackFuncWithArgs(FromeType* self, Args... args, void* cb_data, RteError* err){ + auto *ctx = static_cast*>(cb_data); + + if(ctx->cb_with_cpp_error_ != nullptr){ + Error cpp_err(err); + ctx->cb_with_cpp_error_(ctx->self_, args..., ctx->cb_data_, &cpp_err); + } + + if(ctx->cb_ != nullptr){ + ctx->cb_(ctx->self_, args..., ctx->cb_data_, err); + } + delete ctx; +} + +template +class ObserverDestroyContext { + public: + + using ObserverDestroyer = std::function; + + ObserverDestroyContext(ObserverDestroyer destroyer, void* cb_data) + :destroyer_(destroyer), cb_data_(cb_data) {} + + ObserverDestroyer destroyer_; + void* cb_data_; +}; + +template +void ObserverDestroyProxy(FromeType* observer, void* cb_data){ + auto *ctx = static_cast*>(cb_data); + if(ctx->destroyer_ != nullptr){ + ctx->destroyer_(static_cast(observer->base_observer.me_in_target_lang), ctx->cb_data_); + } + delete ctx; +} + +} // namespace rte diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/rte_cpp_canvas.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/rte_cpp_canvas.h new file mode 100644 index 000000000..020484fac --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/rte_cpp_canvas.h @@ -0,0 +1,109 @@ +#pragma once + +#include "internal/c/c_player.h" +#include "internal/c/handle.h" +#include "internal/c/track/canvas.h" + +#include "rte_cpp_error.h" +#include "rte_cpp_rte.h" +#include "rte_cpp_callback_utils.h" + + +namespace rte { + +using VideoRenderMode = ::RteVideoRenderMode; +using VideoMirrorMode = ::RteVideoMirrorMode; +using ViewConfig = ::RteViewConfig; +using View = ::RteView; +using Rect = ::RteRect; + +class CanvasInitialConfig { + public: + CanvasInitialConfig() {RteCanvasInitialConfigInit(&c_canvas_initial_config, nullptr);} + ~CanvasInitialConfig() {RteCanvasInitialConfigDeinit(&c_canvas_initial_config, nullptr);} + + private: + friend class Canvas; + ::RteCanvasInitialConfig c_canvas_initial_config; +}; + + +class CanvasConfig { + public: + CanvasConfig() {RteCanvasConfigInit(&c_canvas_config, nullptr);} + ~CanvasConfig() {RteCanvasConfigDeinit(&c_canvas_config, nullptr);} + + void SetRenderMode(VideoRenderMode mode, Error *err) { + RteCanvasConfigSetVideoRenderMode(&c_canvas_config, mode, err != nullptr ? err->get_underlying_impl() : nullptr); + } + + VideoRenderMode GetRenderMode(Error *err) { + VideoRenderMode mode; + RteCanvasConfigGetVideoRenderMode(&c_canvas_config, &mode, err != nullptr ? err->get_underlying_impl() : nullptr); + return mode; + } + + void SetMirrorMode(VideoMirrorMode mode, Error *err) { + RteCanvasConfigSetVideoMirrorMode(&c_canvas_config, mode, err != nullptr ? err->get_underlying_impl() : nullptr); + } + + VideoMirrorMode GetMirrorMode(Error *err) { + VideoMirrorMode mode; + RteCanvasConfigGetVideoMirrorMode(&c_canvas_config, &mode, err != nullptr ? err->get_underlying_impl() : nullptr); + return mode; + } + + void SetCropArea(RteRect &crop_area, Error *err) { + RteCanvasConfigSetCropArea(&c_canvas_config, crop_area, err != nullptr ? err->get_underlying_impl() : nullptr); + } + + RteRect GetCropArea(Error *err) { + RteRect crop_area; + RteCanvasConfigGetCropArea(&c_canvas_config, &crop_area, err != nullptr ? err->get_underlying_impl() : nullptr); + return crop_area; + } + + private: + friend class Canvas; + ::RteCanvasConfig c_canvas_config; +}; + +class Canvas { + public: + Canvas(Rte *rte, CanvasInitialConfig *initial_config) { + c_canvas = ::RteCanvasCreate(&rte->c_rte, &initial_config->c_canvas_initial_config, nullptr); + }; + ~Canvas() { RteCanvasDestroy(&c_canvas, nullptr); }; + + void Destroy(Error *err = nullptr) { + RteCanvasDestroy(&c_canvas, + err != nullptr ? err->get_underlying_impl() : nullptr); + } + + Canvas(const Canvas& other) = delete; + Canvas(Canvas&& other) = delete; + Canvas& operator=(const Canvas& other) = delete; + Canvas& operator=(Canvas&& other) = delete; + + void GetConfigs(CanvasConfig *config, Error *err) { + RteCanvasGetConfigs(&c_canvas, &config->c_canvas_config, err != nullptr ? err->get_underlying_impl() : nullptr); + } + + void SetConfigs(CanvasConfig *config, std::function cb, void *cb_data) { + CallbackContext* callbackCtx = new CallbackContext(this, cb, cb_data); + RteCanvasSetConfigs(&c_canvas, &config->c_canvas_config, &CallbackFunc<::RteCanvas, Canvas>, callbackCtx); + } + + void AddView(View *view, ViewConfig *config, std::function cb, void *cb_data) { + CallbackContextWithArgs *ctx = new CallbackContextWithArgs(this, cb, cb_data); + RteCanvasAddView(&c_canvas, view, config, &CallbackFuncWithArgs<::RteCanvas, Canvas, View*>, ctx); + } + + private: + + friend class Player; + + ::RteCanvas c_canvas; +}; + +} // namespace rte \ No newline at end of file diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/rte_cpp_error.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/rte_cpp_error.h new file mode 100644 index 000000000..1584bf789 --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/rte_cpp_error.h @@ -0,0 +1,67 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include +#include + +#include "internal/c/c_error.h" +#include "internal/c/utils/string.h" + +namespace rte { + + +class Rte; +class Player; +class Canvas; +class Config; +class PlayerConfig; +class CanvasConfig; + +class Error { + public: + + using ErrorCode = ::RteErrorCode; + + Error() : c_error(RteErrorCreate()) {} + explicit Error(::RteError *error) : c_error(error), c_error_owned(false) {} + + ~Error() { + if (c_error != nullptr && c_error_owned) { + RteErrorDestroy(c_error); + } + } + + // @{ + Error(Error &other) = delete; + Error(Error &&other) = delete; + Error &operator=(const Error &cmd) = delete; + Error &operator=(Error &&cmd) = delete; + // @} + + void Set(ErrorCode code, const char *message) { + RteErrorSet(c_error, code, "%s", message); + } + + ErrorCode Code() const { return c_error != nullptr ? c_error->code : kRteErrorDefault; } + + const char *Message() const { + if(c_error != nullptr && c_error->message != nullptr){ + return RteStringCStr(c_error->message, nullptr); + } + return ""; + } + + ::RteError *get_underlying_impl() const { return c_error; } + + private: + + ::RteError *c_error; + bool c_error_owned = true; +}; + +} // namespace rte diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/rte_cpp_player.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/rte_cpp_player.h new file mode 100644 index 000000000..c19b44265 --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/rte_cpp_player.h @@ -0,0 +1,443 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once +#include + +#include "internal/c/c_rte.h" +#include "internal/c/c_player.h" + +#include "rte_cpp_error.h" +#include "rte_cpp_callback_utils.h" +#include "rte_cpp_canvas.h" +#include "rte_cpp_string.h" +#include "rte_cpp_stream.h" + +struct RtePlayerObserver; + +namespace rte { + + +using PlayerState = ::RtePlayerState; +using PlayerEvent = ::RtePlayerEvent; +using PlayerMetadataType = ::RtePlayerMetadataType; +using PlayerInfo = ::RtePlayerInfo; +using PlayerStats = ::RtePlayerStats; +using PlayerCustomSourceProvider = ::RtePlayerCustomSourceProvider; + +class PlayerInitialConfig {}; + +static void onStateChanged(::RtePlayerObserver *observer, + RtePlayerState old_state, RtePlayerState new_state, + RteError *err); + +static void onPositionChanged(::RtePlayerObserver *observer, uint64_t curr_time, + uint64_t utc_time); + +static void onResolutionChanged(::RtePlayerObserver *observer, int width, int height); + +static void onEvent(::RtePlayerObserver *observer, RtePlayerEvent event); + +static void onMetadata(::RtePlayerObserver *observer, RtePlayerMetadataType type, + const uint8_t *data, size_t length); + +static void onPlayerInfoUpdated(::RtePlayerObserver *observer, const RtePlayerInfo *info); + +static void onAudioVolumeIndication(::RtePlayerObserver *observer, int32_t volume); + + +class PlayerObserver { + public: + PlayerObserver() : c_rte_observer(::RtePlayerObserverCreate(nullptr)) { + + c_rte_observer->base_observer.me_in_target_lang = this; + + c_rte_observer->on_state_changed = rte::onStateChanged; + c_rte_observer->on_position_changed = rte::onPositionChanged; + c_rte_observer->on_resolution_changed = rte::onResolutionChanged; + c_rte_observer->on_event = rte::onEvent; + c_rte_observer->on_metadata = rte::onMetadata; + c_rte_observer->on_player_info_updated = rte::onPlayerInfoUpdated; + c_rte_observer->on_audio_volume_indication = rte::onAudioVolumeIndication; + } + virtual ~PlayerObserver(){ RtePlayerObserverDestroy(c_rte_observer, nullptr); } + + // @{ + PlayerObserver(PlayerObserver &other) = delete; + PlayerObserver(PlayerObserver &&other) = delete; + PlayerObserver &operator=(const PlayerObserver &cmd) = delete; + PlayerObserver &operator=(PlayerObserver &&cmd) = delete; + // @} + + virtual void onStateChanged(PlayerState old_state, PlayerState new_state, + rte::Error *err) = 0; + virtual void onPositionChanged(uint64_t curr_time, + uint64_t utc_time) = 0; + virtual void onResolutionChanged(int width, int height) = 0; + virtual void onEvent(PlayerEvent event) = 0; + virtual void onMetadata(PlayerMetadataType type, + const uint8_t *data, size_t length) = 0; + + virtual void onPlayerInfoUpdated(const PlayerInfo *info) = 0; + + virtual void onAudioVolumeIndication(int32_t volume) = 0; + + private: + friend class Player; + + ::RtePlayerObserver *c_rte_observer; +}; + +void onStateChanged(::RtePlayerObserver *observer, + RtePlayerState old_state, RtePlayerState new_state, + RteError *err){ + auto *player_observer = static_cast(observer->base_observer.me_in_target_lang); + if (player_observer != nullptr){ + Error cpp_err(err); + player_observer->onStateChanged(old_state, new_state, &cpp_err); + } +} +void onPositionChanged(::RtePlayerObserver *observer, uint64_t curr_time, + uint64_t utc_time){ + auto *player_observer = static_cast(observer->base_observer.me_in_target_lang); + if (player_observer != nullptr){ + player_observer->onPositionChanged(curr_time, utc_time); + } +} + +void onResolutionChanged(::RtePlayerObserver *observer, int width, int height){ + auto *player_observer = static_cast(observer->base_observer.me_in_target_lang); + if (player_observer != nullptr){ + player_observer->onResolutionChanged(width, height); + } +} + +void onEvent(::RtePlayerObserver *observer, RtePlayerEvent event){ + auto *player_observer = static_cast(observer->base_observer.me_in_target_lang); + if (player_observer != nullptr){ + player_observer->onEvent(event); + } + +} + +void onMetadata(::RtePlayerObserver *observer, RtePlayerMetadataType type, + const uint8_t *data, size_t length){ + auto *player_observer = static_cast(observer->base_observer.me_in_target_lang); + if (player_observer != nullptr){ + player_observer->onMetadata(type, data, length); + } +} + +void onPlayerInfoUpdated(::RtePlayerObserver *observer, const RtePlayerInfo *info){ + auto *player_observer = static_cast(observer->base_observer.me_in_target_lang); + if (player_observer != nullptr){ + player_observer->onPlayerInfoUpdated(info); + } +} + +void onAudioVolumeIndication(::RtePlayerObserver *observer, int32_t volume){ + auto *player_observer = static_cast(observer->base_observer.me_in_target_lang); + if (player_observer != nullptr){ + player_observer->onAudioVolumeIndication(volume); + } +} + +class PlayerConfig { + public: + PlayerConfig() { RtePlayerConfigInit(&c_rte_player_config, nullptr); } + ~PlayerConfig() { RtePlayerConfigDeinit(&c_rte_player_config, nullptr); } + + // @{ + PlayerConfig(PlayerConfig &other) = delete; + PlayerConfig(PlayerConfig &&other) = delete; + PlayerConfig &operator=(const PlayerConfig &cmd) = delete; + PlayerConfig &operator=(PlayerConfig &&cmd) = delete; + // @} + + void SetAutoPlay(bool auto_play, Error *err) { + RtePlayerConfigSetAutoPlay(&c_rte_player_config, auto_play, + err != nullptr ? err->get_underlying_impl() : nullptr); + } + + bool GetAutoPlay(Error *err) { + bool auto_play; + RtePlayerConfigGetAutoPlay(&c_rte_player_config, &auto_play, + err != nullptr ? err->get_underlying_impl() : nullptr); + return auto_play; + } + + void SetPlaybackSpeed(int32_t speed, Error *err) { + RtePlayerConfigSetPlaybackSpeed(&c_rte_player_config, speed, + err != nullptr ? err->get_underlying_impl() : nullptr); + } + + int32_t GetPlaybackSpeed(Error *err) { + int32_t speed; + RtePlayerConfigGetPlaybackSpeed(&c_rte_player_config, &speed, + err != nullptr ? err->get_underlying_impl() : nullptr); + return speed; + } + + void SetPlayoutAudioTrackIdx(int idx, Error *err) { + RtePlayerConfigSetPlayoutAudioTrackIdx(&c_rte_player_config, idx, + err != nullptr ? err->get_underlying_impl() : nullptr); + } + + int32_t GetPlayoutAudioTrackIdx(Error *err) { + int32_t idx; + RtePlayerConfigGetPlayoutAudioTrackIdx(&c_rte_player_config, &idx, + err != nullptr ? err->get_underlying_impl() : nullptr); + return idx; + } + + void SetPublishAudioTrackIdx(int32_t idx, Error *err) { + RtePlayerConfigSetPublishAudioTrackIdx(&c_rte_player_config, idx, + err != nullptr ? err->get_underlying_impl() : nullptr); + } + + int32_t GetPublishAudioTrackIdx(Error *err) { + int32_t idx; + RtePlayerConfigGetPublishAudioTrackIdx(&c_rte_player_config, &idx, + err != nullptr ? err->get_underlying_impl() : nullptr); + return idx; + } + + void SetAudioTrackIdx(int32_t idx, Error *err) { + RtePlayerConfigSetAudioTrackIdx(&c_rte_player_config, idx, + err != nullptr ? err->get_underlying_impl() : nullptr); + } + + int32_t GetAudioTrackIdx(Error *err) { + int32_t idx; + RtePlayerConfigGetAudioTrackIdx(&c_rte_player_config, &idx, + err != nullptr ? err->get_underlying_impl() : nullptr); + return idx; + } + + void SetSubtitleTrackIdx(int32_t idx, Error *err) { + RtePlayerConfigSetSubtitleTrackIdx(&c_rte_player_config, idx, + err != nullptr ? err->get_underlying_impl() : nullptr); + } + + int32_t GetSubtitleTrackIdx(Error *err) { + int32_t idx; + RtePlayerConfigGetSubtitleTrackIdx(&c_rte_player_config, &idx, + err != nullptr ? err->get_underlying_impl() : nullptr); + return idx; + } + + void SetExternalSubtitleTrackIdx(int32_t idx, Error *err) { + RtePlayerConfigSetExternalSubtitleTrackIdx(&c_rte_player_config, idx, + err != nullptr ? err->get_underlying_impl() : nullptr); + } + + int32_t GetExternalSubtitleTrackIdx(Error *err) { + int32_t idx; + RtePlayerConfigGetExternalSubtitleTrackIdx(&c_rte_player_config, &idx, + err != nullptr ? err->get_underlying_impl() : nullptr); + return idx; + } + + void SetAudioPitch(int32_t audio_pitch, Error *err) { + RtePlayerConfigSetAudioPitch(&c_rte_player_config, audio_pitch, + err != nullptr ? err->get_underlying_impl() : nullptr); + } + + int32_t GetAudioPitch(Error *err) { + int32_t audio_pitch; + RtePlayerConfigGetAudioPitch(&c_rte_player_config, &audio_pitch, + err != nullptr ? err->get_underlying_impl() : nullptr); + return audio_pitch; + } + + void SetPlayoutVolume(int32_t volume, Error *err) { + RtePlayerConfigSetPlayoutVolume(&c_rte_player_config, volume, + err != nullptr ? err->get_underlying_impl() : nullptr); + } + + int32_t GetPlayoutVolume(Error *err) { + int32_t volume; + RtePlayerConfigGetPlayoutVolume(&c_rte_player_config, &volume, + err != nullptr ? err->get_underlying_impl() : nullptr); + return volume; + } + + void SetAudioPlaybackDelay(int32_t delay, Error *err) { + RtePlayerConfigSetAudioPlaybackDelay(&c_rte_player_config, delay, + err != nullptr ? err->get_underlying_impl() : nullptr); + } + + int32_t GetAudioPlaybackDelay(Error *err) { + int32_t delay; + RtePlayerConfigGetAudioPlaybackDelay(&c_rte_player_config, &delay, + err != nullptr ? err->get_underlying_impl() : nullptr); + return delay; + } + + void SetAudioDualMonoMode(RteAudioDualMonoMode mode, Error *err) { + RtePlayerConfigSetAudioDualMonoMode(&c_rte_player_config, mode, + err != nullptr ? err->get_underlying_impl() : nullptr); + } + + RteAudioDualMonoMode GetAudioDualMonoMode(Error *err) { + RteAudioDualMonoMode mode; + RtePlayerConfigGetAudioDualMonoMode(&c_rte_player_config, &mode, + err != nullptr ? err->get_underlying_impl() : nullptr); + return mode; + } + + void SetPublishVolume(int32_t volume, Error *err) { + RtePlayerConfigSetPublishVolume(&c_rte_player_config, volume, + err != nullptr ? err->get_underlying_impl() : nullptr); + } + + int32_t GetPublishVolume(Error *err) { + int32_t volume; + RtePlayerConfigGetPublishVolume(&c_rte_player_config, &volume, + err != nullptr ? err->get_underlying_impl() : nullptr); + return volume; + } + + void SetLoopCount(int32_t count, Error *err) { + RtePlayerConfigSetLoopCount(&c_rte_player_config, count, + err != nullptr ? err->get_underlying_impl() : nullptr); + } + + int32_t GetLoopCount(Error *err) { + int32_t count; + RtePlayerConfigGetLoopCount(&c_rte_player_config, &count, + err != nullptr ? err->get_underlying_impl() : nullptr); + return count; + } + + void SetJsonParameter(const char *json_parameter, Error *err) { + String str(json_parameter); + RtePlayerConfigSetJsonParameter(&c_rte_player_config, str.get_underlying_impl(), + err != nullptr ? err->get_underlying_impl() : nullptr); + } + + const char *GetJsonParameter(Error *err) { + String str; + RtePlayerConfigGetJsonParameter(&c_rte_player_config, str.get_underlying_impl(), + err != nullptr ? err->get_underlying_impl() : nullptr); + return str.Cstr(); + } + + private: + ::RtePlayerConfig* get_underlying_impl() { return &c_rte_player_config; } + + private: + friend class Player; + + ::RtePlayerConfig c_rte_player_config; +}; + + +class Player { + public: + explicit Player(Rte *self, PlayerInitialConfig *config = nullptr) + : c_rte(::RtePlayerCreate(&self->c_rte, nullptr, nullptr)) {}; + ~Player() { RtePlayerDestroy(&c_rte, nullptr); }; + + void Destroy(Error *err = nullptr){ + RtePlayerDestroy(&c_rte, err != nullptr ? err->get_underlying_impl() : nullptr); +}; + + Player(Player &other) = default; + Player(Player &&other) = default; + + // @{ + Player &operator=(const Player &cmd) = delete; + Player &operator=(Player &&cmd) = delete; + // @} + + void PreloadWithUrl(const char* url, Error* err) { + RtePlayerPreloadWithUrl(&c_rte, url, err != nullptr ? err->get_underlying_impl() : nullptr); + }; + + void OpenWithUrl(const char* url, uint64_t start_time, std::function cb, + void* cb_data) { + CallbackContext* callbackCtx = new CallbackContext(this, cb, cb_data); + RtePlayerOpenWithUrl(&c_rte, url, start_time, &CallbackFunc<::RtePlayer, Player>, callbackCtx); + }; + + void OpenWithCustomSourceProvider(PlayerCustomSourceProvider* provider, uint64_t start_time, + std::function cb, + void* cb_data) { + CallbackContext* callbackCtx = new CallbackContext(this, cb, cb_data); + RtePlayerOpenWithCustomSourceProvider(&c_rte, provider, start_time, &CallbackFunc<::RtePlayer, Player>, callbackCtx); + }; + + + void OpenWithStream(Stream* stream, std::function cb, + void* cb_data) { + CallbackContext* callbackCtx = new CallbackContext(this, cb, cb_data); + RtePlayerOpenWithStream(&c_rte, stream != nullptr ? &stream->c_rte_stream : nullptr, &CallbackFunc<::RtePlayer, Player>, callbackCtx); + }; + + void GetStats(std::function cb, void *cb_data){ + CallbackContextWithArgs *ctx = new CallbackContextWithArgs(this, cb, cb_data); + RtePlayerGetStats(&c_rte, &CallbackFuncWithArgs<::RtePlayer, Player, rte::PlayerStats*>, ctx); + } + + void SetCanvas(Canvas *canvas, Error *err) { + RtePlayerSetCanvas(&c_rte, canvas != nullptr ? &canvas->c_canvas : nullptr, err != nullptr ? err->get_underlying_impl() : nullptr); + }; + + void Play(Error* err) { + RtePlayerPlay(&c_rte, err != nullptr ? err->get_underlying_impl() : nullptr); + } + void Stop(Error* err) { + RtePlayerStop(&c_rte, err != nullptr ? err->get_underlying_impl() : nullptr); + } + void Pause(Error* err) { + RtePlayerPause(&c_rte, err != nullptr ? err->get_underlying_impl() : nullptr); + } + void Seek(uint64_t new_time, Error* err) { + RtePlayerSeek(&c_rte, new_time, err != nullptr ? err->get_underlying_impl() : nullptr); + } + + void MuteAudio(bool mute, Error* err) { + RtePlayerMuteAudio(&c_rte, mute, err != nullptr ? err->get_underlying_impl() : nullptr); + } + + void MuteVideo(bool mute, Error* err) { + RtePlayerMuteVideo(&c_rte, mute, err != nullptr ? err->get_underlying_impl() : nullptr); + } + + uint64_t GetPosition(Error *err){ + return RtePlayerGetPosition(&c_rte, err != nullptr ? err->get_underlying_impl() : nullptr); + } + void GetInfo(PlayerInfo *info, Error *err){ + RtePlayerGetInfo(&c_rte, info, err != nullptr ? err->get_underlying_impl() : nullptr); + } + + void GetConfigs(PlayerConfig* config, Error* err) { + RtePlayerGetConfigs(&c_rte, config->get_underlying_impl(), err != nullptr ? err->get_underlying_impl() : nullptr); + } + + void SetConfigs(PlayerConfig* config, std::function cb, + void* cb_data) { + + rte::CallbackContext* callbackCtx = new CallbackContext(this, cb, cb_data); + RtePlayerSetConfigs(&c_rte, config->get_underlying_impl(), &CallbackFunc<::RtePlayer, Player>, callbackCtx); + } + + bool RegisterObserver(PlayerObserver *observer, Error *err) { + return RtePlayerRegisterObserver( + &c_rte, observer->c_rte_observer, err != nullptr ? err->get_underlying_impl() : nullptr); + } + + void UnregisterObserver(PlayerObserver *observer, Error *err){ + RtePlayerUnregisterObserver(&c_rte, observer->c_rte_observer, + err != nullptr ? err->get_underlying_impl() : nullptr); + } + + private: + ::RtePlayer c_rte; +}; + +} // namespace rte diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/rte_cpp_rte.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/rte_cpp_rte.h new file mode 100644 index 000000000..50c49395a --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/rte_cpp_rte.h @@ -0,0 +1,218 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once + +#include "internal/c/c_rte.h" +#include "internal/c/bridge.h" + +#include "rte_cpp_error.h" +#include "rte_cpp_callback_utils.h" +#include "rte_cpp_string.h" + + +struct RteObserver; +struct RteInitialConfig; +struct RteConfig; + +namespace rte { + +class Player; + +class RteInitialConfig { + ::RteInitialConfig *c_rte_init_cfg; +}; + +class RteObserver { + public: + RteObserver(): c_rte_observer(::RteObserverCreate(nullptr)) { + c_rte_observer->base_observer.me_in_target_lang = this;} + ~RteObserver() { RteObserverDestroy(c_rte_observer, nullptr); } + + // @{ + RteObserver(RteObserver &other) = delete; + RteObserver(RteObserver &&other) = delete; + RteObserver &operator=(const RteObserver &cmd) = delete; + RteObserver &operator=(RteObserver &&cmd) = delete; + // @} + + private: + friend class Rte; + + ::RteObserver *c_rte_observer; +}; + +class Config { + public: + Config() {RteConfigInit(&c_rte_config, nullptr);} + ~Config() {RteConfigDeinit(&c_rte_config, nullptr);} + + // @{ + Config(Config &other) = delete; + Config(Config &&other) = delete; + Config &operator=(const Config &cmd) = delete; + Config &operator=(Config &&cmd) = delete; + // @} + + void SetAppId(const char *app_id, Error *err){ + String str(app_id); + RteConfigSetAppId(&c_rte_config, str.get_underlying_impl(), err != nullptr ? err->get_underlying_impl() : nullptr); + } + + const char* GetAppId(Error *err){ + String str; + RteConfigGetAppId(&c_rte_config, str.get_underlying_impl(), err != nullptr ? err->get_underlying_impl() : nullptr); + return str.Cstr(); + } + + void SetLogFolder(const char *log_folder, Error *err){ + String str(log_folder); + RteConfigSetLogFolder(&c_rte_config, str.get_underlying_impl(), err != nullptr ? err->get_underlying_impl() : nullptr); + } + + const char* GetLogFolder(Error *err){ + String str; + RteConfigGetLogFolder(&c_rte_config, str.get_underlying_impl(), err != nullptr ? err->get_underlying_impl() : nullptr); + return str.Cstr(); + } + + void SetLogFileSize(size_t log_file_size, Error *err){ + RteConfigSetLogFileSize(&c_rte_config, log_file_size, err != nullptr ? err->get_underlying_impl() : nullptr); + } + + size_t GetLogFileSize(Error *err){ + size_t log_file_size; + RteConfigGetLogFileSize(&c_rte_config, &log_file_size, err != nullptr ? err->get_underlying_impl() : nullptr); + return log_file_size; + } + + void SetAreaCode(int32_t area_code, Error *err){ + RteConfigSetAreaCode(&c_rte_config, area_code, err != nullptr ? err->get_underlying_impl() : nullptr); + } + + int32_t GetAreaCode(Error *err){ + int32_t area_code; + RteConfigGetAreaCode(&c_rte_config, &area_code, err != nullptr ? err->get_underlying_impl() : nullptr); + return area_code; + } + + void SetCloudProxy(const char *cloud_proxy, Error *err){ + String str(cloud_proxy); + RteConfigSetCloudProxy(&c_rte_config, str.get_underlying_impl(), err != nullptr ? err->get_underlying_impl() : nullptr); + } + + const char* GetCloudProxy(Error *err){ + String str; + RteConfigGetCloudProxy(&c_rte_config, str.get_underlying_impl(), err != nullptr ? err->get_underlying_impl() : nullptr); + return str.Cstr(); + } + + void SetJsonParameter(const char *json_parameter, Error *err){ + String str(json_parameter); + RteConfigSetJsonParameter(&c_rte_config, str.get_underlying_impl(), err != nullptr ? err->get_underlying_impl() : nullptr); + } + + const char* GetJsonParameter(Error *err){ + String str; + RteConfigGetJsonParameter(&c_rte_config, str.get_underlying_impl(), err != nullptr ? err->get_underlying_impl() : nullptr); + return str.Cstr(); + } + + private: + ::RteConfig* get_underlying_impl() { return &c_rte_config; } + + private: + friend class Rte; + ::RteConfig c_rte_config; +}; + +class Rte { + public: + + static Rte GetFromBridge(Error* err = nullptr){ + Rte rte( RteGetFromBridge(err != nullptr ? err->get_underlying_impl() : nullptr)); + return rte; + } + + explicit Rte(::RteInitialConfig *config = nullptr): c_rte(::RteCreate(config, nullptr)) {} + ~Rte()=default; + + void Destroy(Error *err = nullptr) { + RteDestroy(&c_rte, err != nullptr ? err->get_underlying_impl() : nullptr); + } + + bool RegisterObserver(RteObserver *observer, Error *err){ + return RteRegisterObserver(&c_rte, observer->c_rte_observer, err != nullptr ? err->get_underlying_impl() : nullptr); + } + + bool UnregisterObserver(RteObserver *observer, Error *err){ + return RteUnregisterObserver(&c_rte, observer->c_rte_observer, + err != nullptr ? err->get_underlying_impl() : nullptr); + } + + bool InitMediaEngine(std::function cb, void *cb_data, Error *err = nullptr){ + auto* ctx = new CallbackContext(this, cb, cb_data); + return RteInitMediaEngine(&c_rte, &CallbackFunc<::Rte, Rte>, ctx, err != nullptr ? err->get_underlying_impl() : nullptr); + } + + Rte(Rte &other) = default; + Rte(Rte &&other) = default; + + // @{ + Rte &operator=(const Rte &cmd) = delete; + Rte &operator=(Rte &&cmd) = delete; + // @} + + void GetConfigs(Config *config, Error *err){ + RteGetConfigs(&c_rte, config != nullptr ? config->get_underlying_impl(): nullptr, err != nullptr ? err->get_underlying_impl() : nullptr); + } + bool SetConfigs(Config *config, std::function cb, void *cb_data, Error *err = nullptr){ + CallbackContext* callbackCtx = new CallbackContext(this, cb, cb_data); + return RteSetConfigs(&c_rte, config != nullptr ? config->get_underlying_impl(): nullptr, &CallbackFunc<::Rte, Rte>, callbackCtx, err != nullptr ? err->get_underlying_impl() : nullptr); + } + + private: + + explicit Rte(::Rte other) { c_rte = other; } + + private: + friend class Player; + friend class Canvas; + + ::Rte c_rte; + +// struct RteInitMediaEngineCtx { +// RteInitMediaEngineCtx(InitMediaEngineCb cb, void *cb_data) +// : cb(cb), cb_data(cb_data) {} + +// ~RteInitMediaEngineCtx() = default; + +// // @{ +// RteInitMediaEngineCtx(RteInitMediaEngineCtx &other) = delete; +// RteInitMediaEngineCtx(RteInitMediaEngineCtx &&other) = delete; +// RteInitMediaEngineCtx &operator=(const RteInitMediaEngineCtx &cmd) = delete; +// RteInitMediaEngineCtx &operator=(RteInitMediaEngineCtx &&cmd) = delete; +// // @} + +// InitMediaEngineCb cb; +// void *cb_data; +// }; + +// static void RteInitMediaEngineCtxProxy(::Rte *self, void *cb_data, +// ::RteError *err){ +// auto *ctx = static_cast(cb_data); + +// Rte rte; +// rte.c_rte = *self; + +// Error cpp_err(err); +// ctx->cb(&rte, ctx->cb_data, &cpp_err); + +// delete ctx; +// } +}; + +} // namespace rte diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/rte_cpp_stream.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/rte_cpp_stream.h new file mode 100644 index 000000000..703feba05 --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/rte_cpp_stream.h @@ -0,0 +1,25 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once +#include "internal/c/stream/stream.h" + +namespace rte { + +class Stream { + + public: + Stream() = default; + ~Stream() = default; + + private: + friend class Rte; + friend class Player; + + ::RteStream c_rte_stream; +}; + +} // namespace rte \ No newline at end of file diff --git a/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/rte_cpp_string.h b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/rte_cpp_string.h new file mode 100644 index 000000000..76c3ef58d --- /dev/null +++ b/Android/APIExample/agora-stream-encrypt/src/main/cpp/include/agora/rte_cpp_string.h @@ -0,0 +1,61 @@ +/** + * + * Agora Real Time Engagement + * Copyright (c) 2024 Agora IO. All rights reserved. + * + */ +#pragma once +#include "internal/c/utils/string.h" + +namespace rte { + +class Config; +class PlayerConfig; + +class String { + public: + + String(){ + c_rte_string = RteStringCreate(nullptr); + RteStringInit(c_rte_string, nullptr); + } + + String(const char* str) { + c_rte_string = RteStringCreate(nullptr); + RteStringInit(c_rte_string, nullptr); + if(nullptr != str){ + RteStringInitWithCStr(c_rte_string, str, nullptr); + } + } + + ~String() { + RteStringDeinit(c_rte_string, nullptr); + RteStringDestroy(c_rte_string, nullptr); + } + + void Format(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + RteStringInitWithValue(c_rte_string, nullptr, fmt, args); + va_end(args); + } + + void Copy(const String &other) { + RteStringCopy(c_rte_string, other.c_rte_string, nullptr); + } + + const char* Cstr() const { + return RteStringCStr(c_rte_string, nullptr); + } + + friend class Config; + friend class PlayerConfig; + + private: + ::RteString* get_underlying_impl() const { return c_rte_string; } + + private: + ::RteString *c_rte_string; +}; + +} // namespace rte diff --git a/Android/APIExample/app/build.gradle b/Android/APIExample/app/build.gradle index 2b347fffd..efbde21a0 100644 --- a/Android/APIExample/app/build.gradle +++ b/Android/APIExample/app/build.gradle @@ -3,7 +3,7 @@ apply plugin: 'kotlin-android' apply from: "${rootDir.absolutePath}/git-hooks.gradle" apply from: 'vendors.gradle' -def agoraSdkVersion = "4.3.1" +def agoraSdkVersion = "4.4.1" def localSdkPath= "${rootProject.projectDir.absolutePath}/../../sdk" @@ -24,10 +24,28 @@ android { ndk.abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86-64' } + signingConfigs { + myConfig { + storeFile new File(rootProject.rootDir.absolutePath + "/keystore.key") + storePassword "965606" + keyAlias "agora" + keyPassword "965606" + v1SigningEnabled true + v2SigningEnabled true + } + } + buildTypes { - release { + debug { minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + signingConfig signingConfigs.myConfig + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + + release { + minifyEnabled true + signingConfig signingConfigs.myConfig + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } diff --git a/Android/APIExample/app/proguard-rules.pro b/Android/APIExample/app/proguard-rules.pro index f7a3f52f1..9adf339aa 100644 --- a/Android/APIExample/app/proguard-rules.pro +++ b/Android/APIExample/app/proguard-rules.pro @@ -22,4 +22,21 @@ -keep class io.agora.**{*;} -dontwarn javax.** --dontwarn com.google.devtools.build.android.** \ No newline at end of file +-dontwarn com.google.devtools.build.android.** + +# sensetime +-keep class com.softsugar.**{*;} +-keep class com.sensetime.**{*;} + +# bytedance +-keep class com.effectsar.**{*;} +-keep class com.bes.**{*;} +-keep class com.amazing.**{*;} + +# ijkplayer +-keep class tv.danmaku.ijk.media.player.** {*;} +-keep class tv.danmaku.ijk.media.player.IjkMediaPlayer{*;} +-keep class tv.danmaku.ijk.media.player.ffmpeg.FFmpegApi{*;} + +# exo +-keep class com.google.android.exoplayer2.**{*;} diff --git a/Android/APIExample/app/src/main/assets/yuvj_full_range_alpha_1280_540_left.mp4 b/Android/APIExample/app/src/main/assets/yuvj_full_range_alpha_1280_540_left.mp4 new file mode 100644 index 000000000..2e9dfbd99 Binary files /dev/null and b/Android/APIExample/app/src/main/assets/yuvj_full_range_alpha_1280_540_left.mp4 differ 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 ecffd3757..96bbd8d8b 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 @@ -271,7 +271,7 @@ public void run() { /*Display remote video stream*/ SurfaceView surfaceView = null; // Create render view by RtcEngine - surfaceView = RtcEngine.CreateRendererView(context); + surfaceView = new SurfaceView(context); surfaceView.setZOrderMediaOverlay(true); surfaceView.setZOrderOnTop(true); ViewGroup view = getAvailableView(); @@ -372,7 +372,7 @@ private void toggleVideoLayout(boolean isMultiple) { fl_remote_3.setLayoutParams(new LinearLayout.LayoutParams(0, FrameLayout.LayoutParams.MATCH_PARENT, 0.5f)); video_row2.setLayoutParams(new LinearLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, 0, 1)); // Create render view by RtcEngine - SurfaceView surfaceView = RtcEngine.CreateRendererView(getContext()); + SurfaceView surfaceView = new SurfaceView(getContext()); if (fl_local.getChildCount() > 0) { fl_local.removeAllViews(); } diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/CDNStreaming/HostFragment.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/CDNStreaming/HostFragment.java index 90d6dee98..fc7cca2cf 100644 --- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/CDNStreaming/HostFragment.java +++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/CDNStreaming/HostFragment.java @@ -170,7 +170,7 @@ public void onActivityCreated(@Nullable Bundle savedInstanceState) { private void setupEngineConfig(Context context) { // setup local video to render local camera preview - SurfaceView surfaceView = RtcEngine.CreateRendererView(context); + SurfaceView surfaceView = new SurfaceView(context); if (fl_local.getChildCount() > 0) { fl_local.removeAllViews(); } @@ -336,7 +336,7 @@ public void run() { /*Display remote video stream*/ SurfaceView surfaceView = null; // Create render view by RtcEngine - surfaceView = RtcEngine.CreateRendererView(context); + surfaceView = new SurfaceView(context); surfaceView.setZOrderMediaOverlay(true); ViewGroup view = getAvailableView(); remoteViews.put(uid, view); 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 fded9f26b..2ff5b5161 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 @@ -570,7 +570,7 @@ public void onDrawFrame(GL10 gl) { try { drawer.drawYuv(yuvUploader.getYuvTextures(), 0, RendererCommon.convertMatrixFromAndroidGraphicsMatrix(renderMatrix), lastI420Frame.getRotatedWidth(), - lastI420Frame.getRotatedHeight(), 0, 0, viewportWidth, viewportHeight); + lastI420Frame.getRotatedHeight(), 0, 0, viewportWidth, viewportHeight, 0); } catch (NullPointerException exception) { Log.e(TAG, "skip empty buffer!"); } diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/FaceCapture.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/FaceCapture.java index 842e11d39..b70faab1b 100644 --- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/FaceCapture.java +++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/FaceCapture.java @@ -30,6 +30,7 @@ import io.agora.base.VideoFrame; import io.agora.rtc2.ChannelMediaOptions; import io.agora.rtc2.Constants; +import io.agora.rtc2.ExtensionContext; import io.agora.rtc2.IMediaExtensionObserver; import io.agora.rtc2.IRtcEngineEventHandler; import io.agora.rtc2.RtcEngine; @@ -317,7 +318,9 @@ public int getObservedFramePosition() { private final IMediaExtensionObserver iMediaExtensionObserver = new IMediaExtensionObserver() { @Override - public void onEvent(String provider, String extension, String key, String value) { + public void onEventWithContext(ExtensionContext extContext, String key, String value) { + String provider = extContext.providerName; + String extension = extContext.extensionName; Log.i(TAG, String.format(Locale.US, "ExtensionObserver >> onEvent : provider=%s, extension=%s, key=%s, value=%s", provider, extension, key, value)); if ("agora_video_filters_face_capture".equals(provider) @@ -343,19 +346,25 @@ public void onEvent(String provider, String extension, String key, String value) } @Override - public void onStarted(String provider, String extension) { + public void onStartedWithContext(ExtensionContext extContext) { + String provider = extContext.providerName; + String extension = extContext.extensionName; Log.i(TAG, String.format(Locale.US, "ExtensionObserver >> onStarted : provider=%s, extension=%s", provider, extension)); } @Override - public void onStopped(String provider, String extension) { + public void onStoppedWithContext(ExtensionContext extContext) { + String provider = extContext.providerName; + String extension = extContext.extensionName; Log.i(TAG, String.format(Locale.US, "ExtensionObserver >> onStopped : provider=%s, extension=%s", provider, extension)); } @Override - public void onError(String provider, String extension, int error, String message) { + public void onErrorWithContext(ExtensionContext extContext, int error, String message) { + String provider = extContext.providerName; + String extension = extContext.extensionName; Log.i(TAG, String.format(Locale.US, "ExtensionObserver >> onError : provider=%s, extension=%s, error=%d, message=%s", provider, extension, error, message)); } diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/LiveStreaming.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/LiveStreaming.java index 99dd98d78..7ecfb5b7d 100644 --- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/LiveStreaming.java +++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/LiveStreaming.java @@ -2,6 +2,8 @@ import static io.agora.api.example.common.model.Examples.ADVANCED; import static io.agora.rtc2.Constants.CLIENT_ROLE_AUDIENCE; +import static io.agora.rtc2.video.CameraCapturerConfiguration.CAMERA_DIRECTION; +import static io.agora.rtc2.video.CameraCapturerConfiguration.CAMERA_FOCAL_LENGTH_TYPE; import static io.agora.rtc2.video.VideoEncoderConfiguration.STANDARD_BITRATE; import android.content.Context; @@ -202,9 +204,22 @@ public void onStopTrackingTouch(SeekBar seekBar) { @Override public void onItemSelected(AdapterView parent, View view, int position, long id) { AgoraFocalLengthInfo info = agoraFocalLengthInfos[position]; + CAMERA_DIRECTION direction = CAMERA_DIRECTION.CAMERA_FRONT; + if (info.cameraDirection == CAMERA_DIRECTION.CAMERA_REAR.getValue()) { + direction = CAMERA_DIRECTION.CAMERA_REAR; + } else if (info.cameraDirection == CAMERA_DIRECTION.CAMERA_EXTRAL.getValue()) { + direction = CAMERA_DIRECTION.CAMERA_EXTRAL; + } + CAMERA_FOCAL_LENGTH_TYPE focalLengthType = CAMERA_FOCAL_LENGTH_TYPE.CAMERA_FOCAL_LENGTH_DEFAULT; + if (info.focalLengthType == CAMERA_FOCAL_LENGTH_TYPE.CAMERA_FOCAL_LENGTH_WIDE_ANGLE.getValue()) { + focalLengthType = CAMERA_FOCAL_LENGTH_TYPE.CAMERA_FOCAL_LENGTH_WIDE_ANGLE; + } else if (info.focalLengthType == CAMERA_FOCAL_LENGTH_TYPE.CAMERA_FOCAL_LENGTH_ULTRA_WIDE.getValue()) { + focalLengthType = CAMERA_FOCAL_LENGTH_TYPE.CAMERA_FOCAL_LENGTH_ULTRA_WIDE; + } else if (info.focalLengthType == CAMERA_FOCAL_LENGTH_TYPE.CAMERA_FOCAL_LENGTH_TELEPHOTO.getValue()) { + focalLengthType = CAMERA_FOCAL_LENGTH_TYPE.CAMERA_FOCAL_LENGTH_TELEPHOTO; + } CameraCapturerConfiguration config = new CameraCapturerConfiguration( - getCameraDirection(info), - getFocalLengthType(info) + direction, focalLengthType ); int ret = engine.setCameraCapturerConfiguration( config @@ -302,24 +317,21 @@ public void onActivityCreated(@Nullable Bundle savedInstanceState) { agoraFocalLengthInfos = engine.queryCameraFocalLengthCapability(); ArrayList strings = new ArrayList<>(); - for (int i = 0; i < agoraFocalLengthInfos.length; i++) { - AgoraFocalLengthInfo info = agoraFocalLengthInfos[i]; - + for (AgoraFocalLengthInfo info : agoraFocalLengthInfos) { String cameraDirection = getString(R.string.camera_front); - if (getCameraDirection(info) == CameraCapturerConfiguration.CAMERA_DIRECTION.CAMERA_REAR) { + if (info.cameraDirection == CAMERA_DIRECTION.CAMERA_REAR.getValue()) { cameraDirection = getString(R.string.camera_rear); - } else if (getCameraDirection(info) == CameraCapturerConfiguration.CAMERA_DIRECTION.CAMERA_EXTRA) { + } else if (info.cameraDirection == CAMERA_DIRECTION.CAMERA_EXTRAL.getValue()) { cameraDirection = getString(R.string.camera_extral); } String focalLength = getString(R.string.camera_focal_default); - if (getFocalLengthType(info) == CameraCapturerConfiguration.CAMERA_FOCAL_LENGTH_TYPE.CAMERA_FOCAL_LENGTH_WIDE_ANGLE) { + if (info.focalLengthType == CAMERA_FOCAL_LENGTH_TYPE.CAMERA_FOCAL_LENGTH_WIDE_ANGLE.getValue()) { focalLength = getString(R.string.camera_focal_wide_angle); - } else if (getFocalLengthType(info) == CameraCapturerConfiguration.CAMERA_FOCAL_LENGTH_TYPE.CAMERA_FOCAL_LENGTH_ULTRA_WIDE) { + } else if (info.focalLengthType == CAMERA_FOCAL_LENGTH_TYPE.CAMERA_FOCAL_LENGTH_ULTRA_WIDE.getValue()) { focalLength = getString(R.string.camera_focal_urltra_wide); - } else if (getFocalLengthType(info) == CameraCapturerConfiguration.CAMERA_FOCAL_LENGTH_TYPE.CAMERA_FOCAL_LENGTH_TELEPHOTO) { + } else if (info.focalLengthType == CAMERA_FOCAL_LENGTH_TYPE.CAMERA_FOCAL_LENGTH_TELEPHOTO.getValue()) { focalLength = getString(R.string.camera_focal_telephoto); } - strings.add(String.format(Locale.US, "[%s] %s", cameraDirection, focalLength)); } mSettingBinding.spCamera.setAdapter(new ArrayAdapter(requireContext(), android.R.layout.simple_spinner_dropdown_item, strings)); @@ -329,58 +341,6 @@ public void onActivityCreated(@Nullable Bundle savedInstanceState) { } } - /** - * Get the camera direction from the AgoraFocalLengthInfo - * - * @param info AgoraFocalLengthInfo - * @return Camera direction - */ - private static CameraCapturerConfiguration.CAMERA_DIRECTION getCameraDirection(AgoraFocalLengthInfo info) { - try { - String string = info.toString(); - String[] split = string.split("cameraDirection"); - String substring = split[1].substring(1, 2); - int cameraDirection = Integer.parseInt(substring); - if (cameraDirection == CameraCapturerConfiguration.CAMERA_DIRECTION.CAMERA_FRONT.getValue()) { - return CameraCapturerConfiguration.CAMERA_DIRECTION.CAMERA_FRONT; - } else if (cameraDirection == CameraCapturerConfiguration.CAMERA_DIRECTION.CAMERA_REAR.getValue()) { - return CameraCapturerConfiguration.CAMERA_DIRECTION.CAMERA_REAR; - } else if (cameraDirection == CameraCapturerConfiguration.CAMERA_DIRECTION.CAMERA_EXTRA.getValue()) { - return CameraCapturerConfiguration.CAMERA_DIRECTION.CAMERA_EXTRA; - } - } catch (Exception e) { - Log.e(TAG, "getCameraDirection error=" + e.getMessage()); - } - return CameraCapturerConfiguration.CAMERA_DIRECTION.CAMERA_FRONT; - } - - /** - * Get the focal length type from the AgoraFocalLengthInfo - * - * @param info AgoraFocalLengthInfo - * @return Focal length type - */ - private static CameraCapturerConfiguration.CAMERA_FOCAL_LENGTH_TYPE getFocalLengthType(AgoraFocalLengthInfo info) { - try { - String string = info.toString(); - String[] split = string.split("focalLengthType"); - String substring = split[1].substring(1, 2); - int focalLength = Integer.parseInt(substring); - if (focalLength == CameraCapturerConfiguration.CAMERA_FOCAL_LENGTH_TYPE.CAMERA_FOCAL_LENGTH_DEFAULT.getValue()) { - return CameraCapturerConfiguration.CAMERA_FOCAL_LENGTH_TYPE.CAMERA_FOCAL_LENGTH_DEFAULT; - } else if (focalLength == CameraCapturerConfiguration.CAMERA_FOCAL_LENGTH_TYPE.CAMERA_FOCAL_LENGTH_WIDE_ANGLE.getValue()) { - return CameraCapturerConfiguration.CAMERA_FOCAL_LENGTH_TYPE.CAMERA_FOCAL_LENGTH_WIDE_ANGLE; - } else if (focalLength == CameraCapturerConfiguration.CAMERA_FOCAL_LENGTH_TYPE.CAMERA_FOCAL_LENGTH_ULTRA_WIDE.getValue()) { - return CameraCapturerConfiguration.CAMERA_FOCAL_LENGTH_TYPE.CAMERA_FOCAL_LENGTH_ULTRA_WIDE; - } else if (focalLength == CameraCapturerConfiguration.CAMERA_FOCAL_LENGTH_TYPE.CAMERA_FOCAL_LENGTH_TELEPHOTO.getValue()) { - return CameraCapturerConfiguration.CAMERA_FOCAL_LENGTH_TYPE.CAMERA_FOCAL_LENGTH_TELEPHOTO; - } - } catch (Exception e) { - Log.e(TAG, "getFocalLengthType error=" + e.getMessage()); - } - return CameraCapturerConfiguration.CAMERA_FOCAL_LENGTH_TYPE.CAMERA_FOCAL_LENGTH_DEFAULT; - } - @Override public void onDestroy() { @@ -622,9 +582,9 @@ private void enableWatermark(boolean enable) { } private void enableLowStream(boolean enable) { - engine.setRemoteDefaultVideoStreamType(enable ? Constants.VIDEO_STREAM_LOW : Constants.VIDEO_STREAM_HIGH); + engine.setRemoteDefaultVideoStreamType(enable ? Constants.VideoStreamType.VIDEO_STREAM_LOW : Constants.VideoStreamType.VIDEO_STREAM_HIGH); if (remoteUid != 0) { - engine.setRemoteVideoStreamType(remoteUid, enable ? Constants.VIDEO_STREAM_LOW : Constants.VIDEO_STREAM_HIGH); + engine.setRemoteVideoStreamType(remoteUid, enable ? Constants.VideoStreamType.VIDEO_STREAM_LOW : Constants.VideoStreamType.VIDEO_STREAM_HIGH); } } diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/LocalVideoTranscoding.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/LocalVideoTranscoding.java index 7852f5d6d..8e64a1256 100644 --- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/LocalVideoTranscoding.java +++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/LocalVideoTranscoding.java @@ -6,7 +6,6 @@ import android.annotation.SuppressLint; import android.content.Context; -import android.graphics.Color; import android.os.Bundle; import android.util.DisplayMetrics; import android.util.Log; @@ -472,7 +471,7 @@ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (buttonView == switchTransparentBackground) { engine.enableVirtualBackground(isChecked, new VirtualBackgroundSource(VirtualBackgroundSource.BACKGROUND_COLOR, - Color.TRANSPARENT, "", + 0x00000000, "", VirtualBackgroundSource.BLUR_DEGREE_HIGH), new SegmentationProperty()); } diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/MediaMetadata.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/MediaMetadata.java index 1102412ae..a3458e6e4 100644 --- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/MediaMetadata.java +++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/MediaMetadata.java @@ -41,6 +41,7 @@ import io.agora.rtc2.RtcEngine; import io.agora.rtc2.RtcEngineConfig; import io.agora.rtc2.proxy.LocalAccessPointConfiguration; +import io.agora.rtc2.video.AgoraMetadata; import io.agora.rtc2.video.VideoCanvas; import io.agora.rtc2.video.VideoEncoderConfiguration; @@ -327,13 +328,10 @@ public void run() { return toBeSend; } - /**Occurs when the local user receives the metadata. - * @param buffer The received metadata. - * @param uid The ID of the user who sent the metadata. - * @param timeStampMs The timestamp (ms) of the received metadata.*/ + /**Occurs when the local user receives the metadata.*/ @Override - public void onMetadataReceived(byte[] buffer, int uid, long timeStampMs) { - String data = new String(buffer, Charset.forName("UTF-8")); + public void onMetadataReceived(AgoraMetadata metadata) { + String data = new String(metadata.data, Charset.forName("UTF-8")); handler.post(new Runnable() { @Override public void run() { 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 b57a7f749..c1131224c 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 @@ -27,6 +27,7 @@ import android.widget.LinearLayout; import android.widget.SeekBar; import android.widget.Spinner; +import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -35,7 +36,9 @@ import com.yanzhenjie.permission.runtime.Permission; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import io.agora.api.example.MainApplication; import io.agora.api.example.R; @@ -85,10 +88,12 @@ public class MediaPlayer extends BaseFragment implements View.OnClickListener, A private Spinner sp_player_stream, sp_publish_stream; private List mediaStreamInfoList; private int playerStreamIndex, publishStreamIndex; + private TextView local_video_info; private boolean joined = false; private SeekBar progressBar; private long playerDuration = 0; + private final Map videoInfoMap = new LinkedHashMap<>(); private static final String SAMPLE_MOVIE_URL = "https://agora-adc-artifacts.s3.cn-north-1.amazonaws.com.cn/resources/sample.mp4"; @@ -166,6 +171,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat stop = view.findViewById(R.id.stop); pause = view.findViewById(R.id.pause); publish = view.findViewById(R.id.publish); + local_video_info = view.findViewById(R.id.local_video_info); ll_streams = view.findViewById(R.id.ll_streams); sp_publish_stream = view.findViewById(R.id.sp_publish_stream); sp_player_stream = view.findViewById(R.id.sp_player_stream); @@ -251,6 +257,7 @@ public void onClick(View v) { mediaPlayer.stop(); open.setEnabled(false); setMediaPlayerViewEnable(false); + cleanVideoInfo(); } } else if (v.getId() == R.id.open) { String url = et_url.getText().toString(); @@ -558,6 +565,30 @@ public void onDestroy() { engine = null; } + private void updateVideoInfo(String key, String value) { + runOnUIThread(() -> { + videoInfoMap.put(key, value); + StringBuilder sb = new StringBuilder(); + int size = videoInfoMap.size(); + int index = 0; + for (Map.Entry entry : videoInfoMap.entrySet()) { + sb.append(entry.getKey()).append(" : ").append(entry.getValue()); + if (index < size - 1) { + sb.append("\n"); + } + index++; + } + local_video_info.setText(sb.toString()); + }); + } + + private void cleanVideoInfo() { + runOnUIThread(() -> { + videoInfoMap.clear(); + local_video_info.setText(""); + }); + } + private void setMediaPlayerViewEnable(boolean enable) { runOnUIThread(() -> { play.setEnabled(enable); @@ -637,7 +668,7 @@ public void onMetaData(io.agora.mediaplayer.Constants.MediaPlayerMetadataType me @Override public void onPlayBufferUpdated(long l) { - + updateVideoInfo("PlayBuffer", String.valueOf(l)); } @Override @@ -667,7 +698,10 @@ public void onPlayerCacheStats(CacheStatistics stats) { @Override public void onPlayerPlaybackStats(PlayerPlaybackStats stats) { - + updateVideoInfo("Fps", String.valueOf(stats.getVideoFps())); + updateVideoInfo("videoBitrateInKbps", String.valueOf(stats.getVideoBitrate())); + updateVideoInfo("audioBitrateInKbps", String.valueOf(stats.getAudioBitrate())); + updateVideoInfo("totalBitrateInKbps", String.valueOf(stats.getTotalBitrate())); } @Override diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/SimpleExtension.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/SimpleExtension.java index 3897cae9d..bd3438dee 100644 --- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/SimpleExtension.java +++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/SimpleExtension.java @@ -35,6 +35,7 @@ import io.agora.api.example.utils.TokenUtils; import io.agora.rtc2.ChannelMediaOptions; import io.agora.rtc2.Constants; +import io.agora.rtc2.ExtensionContext; import io.agora.rtc2.IRtcEngineEventHandler; import io.agora.rtc2.RtcEngine; import io.agora.rtc2.RtcEngineConfig; @@ -476,22 +477,30 @@ public void onActiveSpeaker(int uid) { @Override - public void onEvent(String vendor, String extension, String key, String value) { + public void onEventWithContext(ExtensionContext extContext, String key, String value) { + String vendor = extContext.providerName; + String extension = extContext.extensionName; Log.i(TAG, "onEvent vendor: " + vendor + " extension: " + extension + " key: " + key + " value: " + value); } @Override - public void onStarted(String s, String s1) { - + public void onStartedWithContext(ExtensionContext extContext) { + String vendor = extContext.providerName; + String extension = extContext.extensionName; + Log.i(TAG, "onStartedWithContext vendor: " + vendor + " extension: " + extension ); } @Override - public void onStopped(String s, String s1) { - + public void onStoppedWithContext(ExtensionContext extContext) { + String vendor = extContext.providerName; + String extension = extContext.extensionName; + Log.i(TAG, "onStoppedWithContext vendor: " + vendor + " extension: " + extension); } @Override - public void onError(String s, String s1, int i, String s2) { - + public void onErrorWithContext(ExtensionContext extContext, int i, String s2) { + String vendor = extContext.providerName; + String extension = extContext.extensionName; + Log.i(TAG, "onErrorWithContext vendor: " + vendor + " extension: " + extension + " code: " + i + " msg: " + s2); } } diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/TransparentRendering.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/TransparentRendering.java new file mode 100644 index 000000000..1583744b8 --- /dev/null +++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/TransparentRendering.java @@ -0,0 +1,555 @@ +package io.agora.api.example.examples.advanced; + +import static io.agora.api.example.common.model.Examples.ADVANCED; +import static io.agora.rtc2.Constants.RENDER_MODE_FIT; +import static io.agora.rtc2.video.VideoEncoderConfiguration.STANDARD_BITRATE; + +import android.Manifest; +import android.annotation.SuppressLint; +import android.content.Context; +import android.os.Build; +import android.os.Bundle; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.SurfaceView; +import android.view.TextureView; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.EditText; +import android.widget.FrameLayout; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.yanzhenjie.permission.AndPermission; +import com.yanzhenjie.permission.runtime.Permission; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import io.agora.api.example.MainApplication; +import io.agora.api.example.R; +import io.agora.api.example.annotation.Example; +import io.agora.api.example.common.BaseFragment; +import io.agora.api.example.common.widget.VideoReportLayout; +import io.agora.api.example.utils.CommonUtil; +import io.agora.api.example.utils.TokenUtils; +import io.agora.mediaplayer.IMediaPlayer; +import io.agora.mediaplayer.data.MediaPlayerSource; +import io.agora.rtc2.ChannelMediaOptions; +import io.agora.rtc2.Constants; +import io.agora.rtc2.IRtcEngineEventHandler; +import io.agora.rtc2.RtcEngine; +import io.agora.rtc2.RtcEngineConfig; +import io.agora.rtc2.proxy.LocalAccessPointConfiguration; +import io.agora.rtc2.video.VideoCanvas; +import io.agora.rtc2.video.VideoEncoderConfiguration; + +/** + * This demo demonstrates how to make a one-to-one video call + */ +@Example( + index = 25, + group = ADVANCED, + name = R.string.item_transparentrendering, + actionId = R.id.action_mainFragment_to_transparentrendering, + tipsId = R.string.transparent_rendering +) +public class TransparentRendering extends BaseFragment implements View.OnClickListener { + private static final String TAG = TransparentRendering.class.getSimpleName(); + + private VideoReportLayout fl_player, fl_local, fl_remote; + private Button join; + private EditText et_channel; + private RtcEngine engine; + private int myUid; + private boolean joined = false; + private IMediaPlayer mediaPlayer; + + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_transparentrendering, container, false); + return view; + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + join = view.findViewById(R.id.btn_join); + et_channel = view.findViewById(R.id.et_channel); + view.findViewById(R.id.btn_join).setOnClickListener(this); + fl_player = view.findViewById(R.id.fl_player); + fl_local = view.findViewById(R.id.fl_local); + fl_remote = view.findViewById(R.id.fl_remote); + } + + @Override + public void onActivityCreated(@Nullable Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + // Check if the context is valid + Context context = getContext(); + if (context == null) { + return; + } + try { + RtcEngineConfig config = new RtcEngineConfig(); + /* + * The context of Android Activity + */ + config.mContext = context.getApplicationContext(); + /* + * The App ID issued to you by Agora. See How to get the App ID + */ + config.mAppId = getString(R.string.agora_app_id); + /* Sets the channel profile of the Agora RtcEngine. + CHANNEL_PROFILE_COMMUNICATION(0): (Default) The Communication profile. + Use this profile in one-on-one calls or group calls, where all users can talk freely. + CHANNEL_PROFILE_LIVE_BROADCASTING(1): The Live-Broadcast profile. Users in a live-broadcast + channel have a role as either broadcaster or audience. A broadcaster can both send and receive streams; + an audience can only receive streams.*/ + config.mChannelProfile = Constants.CHANNEL_PROFILE_LIVE_BROADCASTING; + /* + * IRtcEngineEventHandler is an abstract class providing default implementation. + * The SDK uses this class to report to the app on SDK runtime events. + */ + config.mEventHandler = iRtcEngineEventHandler; + config.mAudioScenario = Constants.AudioScenario.getValue(Constants.AudioScenario.DEFAULT); + config.mAreaCode = ((MainApplication) getActivity().getApplication()).getGlobalSettings().getAreaCode(); + engine = RtcEngine.create(config); + /* + * This parameter is for reporting the usages of APIExample to agora background. + * Generally, it is not necessary for you to set this parameter. + */ + engine.setParameters("{" + + "\"rtc.report_app_scenario\":" + + "{" + + "\"appScenario\":" + 100 + "," + + "\"serviceType\":" + 11 + "," + + "\"appVersion\":\"" + RtcEngine.getSdkVersion() + "\"" + + "}" + + "}"); + /* setting the local access point if the private cloud ip was set, otherwise the config will be invalid.*/ + LocalAccessPointConfiguration localAccessPointConfiguration = ((MainApplication) getActivity().getApplication()).getGlobalSettings().getPrivateCloudConfig(); + if (localAccessPointConfiguration != null) { + // This api can only be used in the private media server scenario, otherwise some problems may occur. + engine.setLocalAccessPoint(localAccessPointConfiguration); + } + } catch (Exception e) { + e.printStackTrace(); + getActivity().onBackPressed(); + } + } + + @Override + public void onDestroy() { + super.onDestroy(); + /*leaveChannel and Destroy the RtcEngine instance*/ + if (engine != null) { + engine.stopPreview(); + engine.leaveChannel(); + } + handler.post(RtcEngine::destroy); + engine = null; + } + + @SuppressLint("WrongConstant") + @Override + public void onClick(View v) { + if (v.getId() == R.id.btn_join) { + if (!joined) { + CommonUtil.hideInputBoard(getActivity(), et_channel); + // call when join button hit + String channelId = et_channel.getText().toString(); + // Check permission + List permissionList = new ArrayList<>(); + permissionList.add(Permission.READ_EXTERNAL_STORAGE); + permissionList.add(Permission.WRITE_EXTERNAL_STORAGE); + permissionList.add(Permission.RECORD_AUDIO); + permissionList.add(Permission.CAMERA); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + permissionList.add(Manifest.permission.BLUETOOTH_CONNECT); + } + + String[] permissionArray = new String[permissionList.size()]; + permissionList.toArray(permissionArray); + + if (AndPermission.hasPermissions(this, permissionArray)) { + joinChannel(channelId); + return; + } + // Request permission + AndPermission.with(this).runtime().permission( + permissionArray + ).onGranted(permissions -> { + // Permissions Granted + joinChannel(channelId); + }).start(); + } else { + joined = false; + stopPlaying(); + + engine.stopPreview(); + + /*After joining a channel, the user must call the leaveChannel method to end the + * call before joining another channel. This method returns 0 if the user leaves the + * channel and releases all resources related to the call. This method call is + * asynchronous, and the user has not exited the channel when the method call returns. + * Once the user leaves the channel, the SDK triggers the onLeaveChannel callback. + * A successful leaveChannel method call triggers the following callbacks: + * 1:The local client: onLeaveChannel. + * 2:The remote client: onUserOffline, if the user leaving the channel is in the + * Communication channel, or is a BROADCASTER in the Live Broadcast profile. + * @returns 0: Success. + * < 0: Failure. + * PS: + * 1:If you call the destroy method immediately after calling the leaveChannel + * method, the leaveChannel process interrupts, and the SDK does not trigger + * the onLeaveChannel callback. + * 2:If you call the leaveChannel method during CDN live streaming, the SDK + * triggers the removeInjectStreamUrl method.*/ + engine.leaveChannel(); + join.setText(getString(R.string.join)); + fl_local.removeAllViews(); + fl_remote.removeAllViews(); + fl_remote.setReportUid(0); + } + } + } + + private void joinChannel(String channelId) { + // Check if the context is valid + Context context = getContext(); + if (context == null) { + return; + } + + + /*In the demo, the default is to enter as the anchor.*/ + engine.setClientRole(Constants.CLIENT_ROLE_BROADCASTER); + // Enable video module + engine.enableVideo(); + // Setup video encoding configs + VideoEncoderConfiguration config = 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()) + ); + config.advanceOptions.encodeAlpha = true; + engine.setVideoEncoderConfiguration(config); + + engine.setExternalVideoSource(true, true, Constants.ExternalVideoSourceType.VIDEO_FRAME); + + + // Create render view by RtcEngine + TextureView textureView = new TextureView(context); + textureView.setOpaque(false); + // Add to the local container + fl_local.addView(textureView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + // Setup local video to render your local camera preview + VideoCanvas local = new VideoCanvas(textureView, RENDER_MODE_FIT, 0); + local.mirrorMode = Constants.VIDEO_MIRROR_MODE_DISABLED; + local.sourceType = Constants.VIDEO_SOURCE_CUSTOM; + local.enableAlphaMask = true; + engine.setupLocalVideo(local); + // Set audio route to microPhone + engine.setDefaultAudioRoutetoSpeakerphone(true); + engine.startPreview(Constants.VideoSourceType.VIDEO_SOURCE_CUSTOM); + + + ChannelMediaOptions option = new ChannelMediaOptions(); + option.autoSubscribeAudio = true; + option.autoSubscribeVideo = true; + option.publishMicrophoneTrack = true; + option.publishCameraTrack = false; + option.publishCustomVideoTrack = true; + + /*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 + * https://docs.agora.io/en/Agora%20Platform/token?platform=All%20Platforms#get-a-temporary-token + * A token generated at the server. This applies to scenarios with high-security requirements. For details, see + * https://docs.agora.io/en/cloud-recording/token_server_java?platform=Java*/ + int uid = new Random().nextInt(1000) + 100000; + TokenUtils.gen(requireContext(), channelId, uid, ret -> { + + /* Allows a user to join a channel. + if you do not specify the uid, we will generate the uid for you*/ + int res = engine.joinChannel(ret, channelId, uid, option); + if (res != 0) { + // Usually happens with invalid parameters + // Error code description can be found at: + // en: https://docs.agora.io/en/Voice/API%20Reference/java/classio_1_1agora_1_1rtc_1_1_i_rtc_engine_event_handler_1_1_error_code.html + // cn: https://docs.agora.io/cn/Voice/API%20Reference/java/classio_1_1agora_1_1rtc_1_1_i_rtc_engine_event_handler_1_1_error_code.html + showAlert(RtcEngine.getErrorDescription(Math.abs(res))); + return; + } + // Prevent repeated entry + join.setEnabled(false); + + startPlaying(); + }); + } + + private void startPlaying() { + mediaPlayer = engine.createMediaPlayer(); + + SurfaceView renderView = new SurfaceView(requireContext()); + fl_player.addView(renderView); + mediaPlayer.setView(renderView); + mediaPlayer.setRenderMode(Constants.RENDER_MODE_FIT); + + mediaPlayer.registerVideoFrameObserver(frame -> { + if (joined) { + //enum AlphaStitchMode { + // kNoStitch = 0, + // kAlphaStitchUp = 1, + // kAlphaStitchBelow = 2, + // kAlphaStitchLeft = 3, + // kAlphaStitchRight = 4 + //}; + frame.setAlphaStitchMode(3); + engine.pushExternalVideoFrame(frame); + } + }); + + MediaPlayerSource source = new MediaPlayerSource(); + source.setUrl("/assets/yuvj_full_range_alpha_1280_540_left.mp4"); + source.setAutoPlay(true); + mediaPlayer.openWithMediaSource(source); + mediaPlayer.setLoopCount(-1); + } + + private void stopPlaying() { + if (mediaPlayer != null) { + mediaPlayer.stop(); + mediaPlayer.destroy(); + mediaPlayer = null; + } + + fl_player.removeAllViews(); + } + + + /** + * IRtcEngineEventHandler is an abstract class providing default implementation. + * The SDK uses this class to report to the app on SDK runtime events. + */ + private final IRtcEngineEventHandler iRtcEngineEventHandler = new IRtcEngineEventHandler() { + /** + * Error code description can be found at: + * en: {@see https://api-ref.agora.io/en/video-sdk/android/4.x/API/class_irtcengineeventhandler.html#callback_irtcengineeventhandler_onerror} + * cn: {@see https://docs.agora.io/cn/video-call-4.x/API%20Reference/java_ng/API/class_irtcengineeventhandler.html#callback_irtcengineeventhandler_onerror} + */ + @Override + public void onError(int err) { + super.onError(err); + showLongToast("Error code:" + err + ", msg:" + RtcEngine.getErrorDescription(err)); + if (err == Constants.ERR_INVALID_TOKEN || err == Constants.ERR_TOKEN_EXPIRED) { + engine.stopPreview(); + engine.leaveChannel(); + runOnUIThread(() -> join.setEnabled(true)); + + if (Constants.ERR_INVALID_TOKEN == err) { + showAlert(getString(R.string.token_invalid)); + } else { + showAlert(getString(R.string.token_expired)); + } + } + } + + /**Occurs when a user leaves the channel. + * @param stats With this callback, the application retrieves the channel information, + * such as the call duration and statistics.*/ + @Override + public void onLeaveChannel(RtcStats stats) { + super.onLeaveChannel(stats); + Log.i(TAG, String.format("local user %d leaveChannel!", myUid)); + showLongToast(String.format("local user %d leaveChannel!", myUid)); + } + + /**Occurs when the local user joins a specified channel. + * The channel name assignment is based on channelName specified in the joinChannel method. + * If the uid is not specified when joinChannel is called, the server automatically assigns a uid. + * @param channel Channel name + * @param uid User ID + * @param elapsed Time elapsed (ms) from the user calling joinChannel until this callback is triggered*/ + @Override + public void onJoinChannelSuccess(String channel, int uid, int elapsed) { + Log.i(TAG, String.format("onJoinChannelSuccess channel %s uid %d", channel, uid)); + showLongToast(String.format("onJoinChannelSuccess channel %s uid %d", channel, uid)); + myUid = uid; + joined = true; + runOnUIThread(() -> { + join.setEnabled(true); + join.setText(getString(R.string.leave)); + fl_local.setReportUid(uid); + }); + } + + /**Since v2.9.0. + * This callback indicates the state change of the remote audio stream. + * PS: This callback does not work properly when the number of users (in the Communication profile) or + * broadcasters (in the Live-broadcast profile) in the channel exceeds 17. + * @param uid ID of the user whose audio state changes. + * @param state State of the remote audio + * REMOTE_AUDIO_STATE_STOPPED(0): The remote audio is in the default state, probably due + * to REMOTE_AUDIO_REASON_LOCAL_MUTED(3), REMOTE_AUDIO_REASON_REMOTE_MUTED(5), + * or REMOTE_AUDIO_REASON_REMOTE_OFFLINE(7). + * REMOTE_AUDIO_STATE_STARTING(1): The first remote audio packet is received. + * REMOTE_AUDIO_STATE_DECODING(2): The remote audio stream is decoded and plays normally, + * probably due to REMOTE_AUDIO_REASON_NETWORK_RECOVERY(2), + * REMOTE_AUDIO_REASON_LOCAL_UNMUTED(4) or REMOTE_AUDIO_REASON_REMOTE_UNMUTED(6). + * REMOTE_AUDIO_STATE_FROZEN(3): The remote audio is frozen, probably due to + * REMOTE_AUDIO_REASON_NETWORK_CONGESTION(1). + * REMOTE_AUDIO_STATE_FAILED(4): The remote audio fails to start, probably due to + * REMOTE_AUDIO_REASON_INTERNAL(0). + * @param reason The reason of the remote audio state change. + * REMOTE_AUDIO_REASON_INTERNAL(0): Internal reasons. + * REMOTE_AUDIO_REASON_NETWORK_CONGESTION(1): Network congestion. + * REMOTE_AUDIO_REASON_NETWORK_RECOVERY(2): Network recovery. + * REMOTE_AUDIO_REASON_LOCAL_MUTED(3): The local user stops receiving the remote audio + * stream or disables the audio module. + * REMOTE_AUDIO_REASON_LOCAL_UNMUTED(4): The local user resumes receiving the remote audio + * stream or enables the audio module. + * REMOTE_AUDIO_REASON_REMOTE_MUTED(5): The remote user stops sending the audio stream or + * disables the audio module. + * REMOTE_AUDIO_REASON_REMOTE_UNMUTED(6): The remote user resumes sending the audio stream + * or enables the audio module. + * REMOTE_AUDIO_REASON_REMOTE_OFFLINE(7): The remote user leaves the channel. + * @param elapsed Time elapsed (ms) from the local user calling the joinChannel method + * until the SDK triggers this callback.*/ + @Override + public void onRemoteAudioStateChanged(int uid, int state, int reason, int elapsed) { + super.onRemoteAudioStateChanged(uid, state, reason, elapsed); + Log.i(TAG, "onRemoteAudioStateChanged->" + uid + ", state->" + state + ", reason->" + reason); + } + + /**Since v2.9.0. + * Occurs when the remote video state changes. + * PS: This callback does not work properly when the number of users (in the Communication + * profile) or broadcasters (in the Live-broadcast profile) in the channel exceeds 17. + * @param uid ID of the remote user whose video state changes. + * @param state State of the remote video: + * REMOTE_VIDEO_STATE_STOPPED(0): The remote video is in the default state, probably due + * to REMOTE_VIDEO_STATE_REASON_LOCAL_MUTED(3), REMOTE_VIDEO_STATE_REASON_REMOTE_MUTED(5), + * or REMOTE_VIDEO_STATE_REASON_REMOTE_OFFLINE(7). + * REMOTE_VIDEO_STATE_STARTING(1): The first remote video packet is received. + * REMOTE_VIDEO_STATE_DECODING(2): The remote video stream is decoded and plays normally, + * probably due to REMOTE_VIDEO_STATE_REASON_NETWORK_RECOVERY (2), + * REMOTE_VIDEO_STATE_REASON_LOCAL_UNMUTED(4), REMOTE_VIDEO_STATE_REASON_REMOTE_UNMUTED(6), + * or REMOTE_VIDEO_STATE_REASON_AUDIO_FALLBACK_RECOVERY(9). + * REMOTE_VIDEO_STATE_FROZEN(3): The remote video is frozen, probably due to + * REMOTE_VIDEO_STATE_REASON_NETWORK_CONGESTION(1) or REMOTE_VIDEO_STATE_REASON_AUDIO_FALLBACK(8). + * REMOTE_VIDEO_STATE_FAILED(4): The remote video fails to start, probably due to + * REMOTE_VIDEO_STATE_REASON_INTERNAL(0). + * @param reason The reason of the remote video state change: + * REMOTE_VIDEO_STATE_REASON_INTERNAL(0): Internal reasons. + * REMOTE_VIDEO_STATE_REASON_NETWORK_CONGESTION(1): Network congestion. + * REMOTE_VIDEO_STATE_REASON_NETWORK_RECOVERY(2): Network recovery. + * REMOTE_VIDEO_STATE_REASON_LOCAL_MUTED(3): The local user stops receiving the remote + * video stream or disables the video module. + * REMOTE_VIDEO_STATE_REASON_LOCAL_UNMUTED(4): The local user resumes receiving the remote + * video stream or enables the video module. + * REMOTE_VIDEO_STATE_REASON_REMOTE_MUTED(5): The remote user stops sending the video + * stream or disables the video module. + * REMOTE_VIDEO_STATE_REASON_REMOTE_UNMUTED(6): The remote user resumes sending the video + * stream or enables the video module. + * REMOTE_VIDEO_STATE_REASON_REMOTE_OFFLINE(7): The remote user leaves the channel. + * REMOTE_VIDEO_STATE_REASON_AUDIO_FALLBACK(8): The remote media stream falls back to the + * audio-only stream due to poor network conditions. + * REMOTE_VIDEO_STATE_REASON_AUDIO_FALLBACK_RECOVERY(9): The remote media stream switches + * back to the video stream after the network conditions improve. + * @param elapsed Time elapsed (ms) from the local user calling the joinChannel method until + * the SDK triggers this callback.*/ + @Override + public void onRemoteVideoStateChanged(int uid, int state, int reason, int elapsed) { + super.onRemoteVideoStateChanged(uid, state, reason, elapsed); + Log.i(TAG, "onRemoteVideoStateChanged->" + uid + ", state->" + state + ", reason->" + reason); + } + + /**Occurs when a remote user (Communication)/host (Live Broadcast) joins the channel. + * @param uid ID of the user whose audio state changes. + * @param elapsed Time delay (ms) from the local user calling joinChannel/setClientRole + * until this callback is triggered.*/ + @Override + public void onUserJoined(int uid, int elapsed) { + super.onUserJoined(uid, elapsed); + Log.i(TAG, "onUserJoined->" + uid); + showLongToast(String.format("user %d joined!", uid)); + /*Check if the context is correct*/ + Context context = getContext(); + if (context == null) { + return; + } + if (fl_remote.getReportUid() <= 0) { + runOnUIThread(() -> { + /*Display remote video stream*/ + // Create render view by RtcEngine + TextureView textureView = new TextureView(context); + textureView.setOpaque(false); + fl_remote.setReportUid(uid); + // Add to the remote container + fl_remote.addView(textureView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + // Setup remote video to render + VideoCanvas remote = new VideoCanvas(textureView, VideoCanvas.RENDER_MODE_FIT, uid); + remote.enableAlphaMask = true; + engine.setupRemoteVideo(remote); + }); + } + } + + /**Occurs when a remote user (Communication)/host (Live Broadcast) leaves the channel. + * @param uid ID of the user whose audio state changes. + * @param reason Reason why the user goes offline: + * USER_OFFLINE_QUIT(0): The user left the current channel. + * USER_OFFLINE_DROPPED(1): The SDK timed out and the user dropped offline because no data + * packet was received within a certain period of time. If a user quits the + * call and the message is not passed to the SDK (due to an unreliable channel), + * the SDK assumes the user dropped offline. + * USER_OFFLINE_BECOME_AUDIENCE(2): (Live broadcast only.) The client role switched from + * the host to the audience.*/ + @Override + public void onUserOffline(int uid, int reason) { + Log.i(TAG, String.format("user %d offline! reason:%d", uid, reason)); + showLongToast(String.format("user %d offline! reason:%d", uid, reason)); + runOnUIThread(() -> { + /*Clear render view + Note: The video will stay at its last frame, to completely remove it you will need to + remove the SurfaceView from its parent*/ + if(fl_remote.getReportUid() == uid){ + fl_remote.removeAllViews(); + fl_remote.setReportUid(0); + } + }); + } + + @Override + public void onLocalAudioStats(LocalAudioStats stats) { + super.onLocalAudioStats(stats); + fl_local.setLocalAudioStats(stats); + } + + @Override + public void onRemoteAudioStats(RemoteAudioStats stats) { + super.onRemoteAudioStats(stats); + fl_remote.setRemoteAudioStats(stats); + } + + @Override + public void onLocalVideoStats(Constants.VideoSourceType source, LocalVideoStats stats) { + super.onLocalVideoStats(source, stats); + fl_local.setLocalVideoStats(stats); + } + + @Override + public void onRemoteVideoStats(RemoteVideoStats stats) { + super.onRemoteVideoStats(stats); + fl_remote.setRemoteVideoStats(stats); + } + }; + +} diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/VoiceEffects.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/VoiceEffects.java index 852a02b70..418c06a63 100644 --- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/VoiceEffects.java +++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/VoiceEffects.java @@ -109,7 +109,7 @@ public class VoiceEffects extends BaseFragment implements View.OnClickListener, private EditText et_channel; private Button join; private Spinner audioProfile, audioScenario, - chatBeautifier, timbreTransformation, voiceChanger, styleTransformation, roomAcoustics, pitchCorrection, _pitchModeOption, _pitchValueOption, voiceConversion, ainsMode, + chatBeautifier, timbreTransformation, voiceChanger, styleTransformation, roomAcoustics, pitchCorrection, _pitchModeOption, _pitchValueOption, voiceConversion, ainsMode, voiceAITuner, customBandFreq, customReverbKey; private ViewGroup _voice3DLayout, _pitchModeLayout, _pitchValueLayout; private SeekBar _voice3DCircle, customPitch, customBandGain, customReverbValue, customVoiceFormant; @@ -157,6 +157,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat _pitchValueOption = view.findViewById(R.id.audio_pitch_value_option); voiceConversion = view.findViewById(R.id.audio_voice_conversion); ainsMode = view.findViewById(R.id.audio_ains_mode); + voiceAITuner = view.findViewById(R.id.voice_ai_tuner); chatBeautifier.setOnItemSelectedListener(this); timbreTransformation.setOnItemSelectedListener(this); @@ -169,6 +170,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat _pitchModeOption.setOnItemSelectedListener(this); _pitchValueOption.setOnItemSelectedListener(this); ainsMode.setOnItemSelectedListener(this); + voiceAITuner.setOnItemSelectedListener(this); // Customize Voice Effects Layout customPitch = view.findViewById(R.id.audio_custom_pitch); // engine.setLocalVoicePitch() @@ -208,6 +210,7 @@ private void resetControlLayoutByJoined() { _pitchValueLayout.setVisibility(View.GONE); voiceConversion.setEnabled(joined); ainsMode.setEnabled(joined); + voiceAITuner.setEnabled(joined); customPitch.setEnabled(joined); customBandFreq.setEnabled(joined); @@ -646,6 +649,12 @@ public void onItemSelected(AdapterView parent, View view, int position, long */ engine.setAINSMode(enable, position - 1); } + + if (parent == voiceAITuner) { + boolean enable = position > 0; + String item = parent.getSelectedItem().toString(); + engine.enableVoiceAITuner(enable, enable ? Constants.VOICE_AI_TUNER_TYPE.valueOf(item) : Constants.VOICE_AI_TUNER_TYPE.VOICE_AI_TUNER_MATURE_MALE); + } } private int getVoiceConversionValue(String label) { diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/beauty/ByteDanceBeautySDK.kt b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/beauty/ByteDanceBeautySDK.kt index 05c9015be..8a28ab81f 100644 --- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/beauty/ByteDanceBeautySDK.kt +++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/beauty/ByteDanceBeautySDK.kt @@ -11,7 +11,7 @@ object ByteDanceBeautySDK { private const val TAG = "ByteDanceBeautySDK" - private val LICENSE_NAME = "Agora_test_20240111_20240411_io.agora.test.entfull_4.5.0_1111.licbag" + private val LICENSE_NAME = "Agora_test_20240412_20240712_io.agora.entfull_4.5.0_1443.licbag" private var storagePath = "" private var assetsPath = "" private var licensePath = "" diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/videoRender/YuvFboProgram.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/videoRender/YuvFboProgram.java index 0cc35fa72..035344a33 100644 --- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/videoRender/YuvFboProgram.java +++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/videoRender/YuvFboProgram.java @@ -96,7 +96,7 @@ public Integer drawYuv(byte[] yuv, int width, int height) { matrix.preTranslate(0.5f, 0.5f); matrix.preScale(1f, -1f); // I420-frames are upside down matrix.preTranslate(-0.5f, -0.5f); - glRectDrawer.drawYuv(yuvUploader.getYuvTextures(), 0, RendererCommon.convertMatrixFromAndroidGraphicsMatrix(matrix), width, height, 0, 0, width, height); + glRectDrawer.drawYuv(yuvUploader.getYuvTextures(), 0, RendererCommon.convertMatrixFromAndroidGraphicsMatrix(matrix), width, height, 0, 0, width, height, 0); GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); GLES20.glFlush(); diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/utils/TokenUtils.java b/Android/APIExample/app/src/main/java/io/agora/api/example/utils/TokenUtils.java index b33293ac0..e56314612 100644 --- a/Android/APIExample/app/src/main/java/io/agora/api/example/utils/TokenUtils.java +++ b/Android/APIExample/app/src/main/java/io/agora/api/example/utils/TokenUtils.java @@ -100,7 +100,7 @@ private static void gen(String appId, String certificate, String channelName, in } Request request = new Request.Builder() - .url("https://test-toolbox.bj2.agoralab.co/v1/token/generate") + .url("https://service.agora.io/toolbox-global/v1/token/generate") .addHeader("Content-Type", "application/json") .post(RequestBody.create(postBody.toString(), null)) .build(); diff --git a/Android/APIExample/app/src/main/java/io/agora/beautyapi/bytedance/utils/AgoraImageHelper.kt b/Android/APIExample/app/src/main/java/io/agora/beautyapi/bytedance/utils/AgoraImageHelper.kt index 597e84c9b..9580a6071 100644 --- a/Android/APIExample/app/src/main/java/io/agora/beautyapi/bytedance/utils/AgoraImageHelper.kt +++ b/Android/APIExample/app/src/main/java/io/agora/beautyapi/bytedance/utils/AgoraImageHelper.kt @@ -56,9 +56,9 @@ class AgoraImageHelper { GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBuffer.frameBufferId) if(texType == VideoFrame.TextureBuffer.Type.OES){ - drawer.drawOes(texId, 0, transform, width, height, 0, 0, width, height) + drawer.drawOes(texId, 0, transform, width, height, 0, 0, width, height, 0) }else{ - drawer.drawRgb(texId, 0, transform, width, height, 0, 0, width, height) + drawer.drawRgb(texId, 0, transform, width, height, 0, 0, width, height, 0) } GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0) GLES20.glFinish() diff --git a/Android/APIExample/app/src/main/java/io/agora/beautyapi/faceunity/utils/egl/GLFrameBuffer.java b/Android/APIExample/app/src/main/java/io/agora/beautyapi/faceunity/utils/egl/GLFrameBuffer.java index e30f17ac3..7b1c1dd86 100644 --- a/Android/APIExample/app/src/main/java/io/agora/beautyapi/faceunity/utils/egl/GLFrameBuffer.java +++ b/Android/APIExample/app/src/main/java/io/agora/beautyapi/faceunity/utils/egl/GLFrameBuffer.java @@ -115,9 +115,9 @@ public int process(int textureId, int textureType) { synchronized (EglBase.lock){ if(textureType == GLES11Ext.GL_TEXTURE_EXTERNAL_OES){ - drawer.drawOes(textureId, 0, matrix, mWidth, mHeight, 0, 0, mWidth, mHeight); + drawer.drawOes(textureId, 0, matrix, mWidth, mHeight, 0, 0, mWidth, mHeight, 0); }else{ - drawer.drawRgb(textureId, 0, matrix, mWidth, mHeight, 0, 0, mWidth, mHeight); + drawer.drawRgb(textureId, 0, matrix, mWidth, mHeight, 0, 0, mWidth, mHeight, 0); } } diff --git a/Android/APIExample/app/src/main/java/io/agora/beautyapi/sensetime/SenseTimeBeautyAPIImpl.kt b/Android/APIExample/app/src/main/java/io/agora/beautyapi/sensetime/SenseTimeBeautyAPIImpl.kt index 11c2461c3..ec346087a 100644 --- a/Android/APIExample/app/src/main/java/io/agora/beautyapi/sensetime/SenseTimeBeautyAPIImpl.kt +++ b/Android/APIExample/app/src/main/java/io/agora/beautyapi/sensetime/SenseTimeBeautyAPIImpl.kt @@ -40,7 +40,6 @@ import io.agora.base.VideoFrame.I420Buffer import io.agora.base.VideoFrame.SourceType import io.agora.base.VideoFrame.TextureBuffer import io.agora.base.internal.video.RendererCommon -import io.agora.base.internal.video.YuvConverter import io.agora.base.internal.video.YuvHelper import io.agora.beautyapi.sensetime.utils.LogUtils import io.agora.beautyapi.sensetime.utils.StatsHelper @@ -633,8 +632,6 @@ class SenseTimeBeautyAPIImpl : SenseTimeBeautyAPI, IVideoFrameObserver { private fun getNV21Buffer(videoFrame: VideoFrame) : ByteArray? { val buffer = videoFrame.buffer - YuvConverter.setEnablePboOpt(true) - YuvConverter.setEnableConvertPerLog(true) val i420Buffer = buffer as? I420Buffer ?: buffer.toI420() val width = i420Buffer.width val height = i420Buffer.height diff --git a/Android/APIExample/app/src/main/java/io/agora/beautyapi/sensetime/utils/egl/GLFrameBuffer.java b/Android/APIExample/app/src/main/java/io/agora/beautyapi/sensetime/utils/egl/GLFrameBuffer.java index 3db7fdd06..a73ff9287 100644 --- a/Android/APIExample/app/src/main/java/io/agora/beautyapi/sensetime/utils/egl/GLFrameBuffer.java +++ b/Android/APIExample/app/src/main/java/io/agora/beautyapi/sensetime/utils/egl/GLFrameBuffer.java @@ -113,9 +113,9 @@ public int process(int textureId, int textureType) { float[] matrix = RendererCommon.convertMatrixFromAndroidGraphicsMatrix(transform); if(textureType == GLES11Ext.GL_TEXTURE_EXTERNAL_OES){ - drawer.drawOes(textureId, 0, matrix, mWidth, mHeight, 0, 0, mWidth, mHeight); + drawer.drawOes(textureId, 0, matrix, mWidth, mHeight, 0, 0, mWidth, mHeight, 0); }else{ - drawer.drawRgb(textureId, 0, matrix, mWidth, mHeight, 0, 0, mWidth, mHeight); + drawer.drawRgb(textureId, 0, matrix, mWidth, mHeight, 0, 0, mWidth, mHeight, 0); } GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); GLES20.glFinish(); diff --git a/Android/APIExample/app/src/main/res/layout/fragment_media_player.xml b/Android/APIExample/app/src/main/res/layout/fragment_media_player.xml index 2f8ea7b18..7a4dc14f4 100644 --- a/Android/APIExample/app/src/main/res/layout/fragment_media_player.xml +++ b/Android/APIExample/app/src/main/res/layout/fragment_media_player.xml @@ -14,10 +14,23 @@ android:orientation="vertical"> + android:layout_weight="1.0"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Android/APIExample/app/src/main/res/layout/fragment_voice_effects.xml b/Android/APIExample/app/src/main/res/layout/fragment_voice_effects.xml index b044e858a..4114c7e94 100644 --- a/Android/APIExample/app/src/main/res/layout/fragment_voice_effects.xml +++ b/Android/APIExample/app/src/main/res/layout/fragment_voice_effects.xml @@ -329,6 +329,29 @@
    + + + + + + + + + + + + diff --git a/Android/APIExample/app/src/main/res/values-zh/strings.xml b/Android/APIExample/app/src/main/res/values-zh/strings.xml index ac5f8e37f..3380f6a6d 100644 --- a/Android/APIExample/app/src/main/res/values-zh/strings.xml +++ b/Android/APIExample/app/src/main/res/values-zh/strings.xml @@ -194,6 +194,7 @@ 此示例演示了如何集成第三方美颜sdk 此示例演示如何使用ktv api实现一个场景demo 此示例演示如何使用面捕功能获取面捕数据 + 此示例演示如何使用透明渲染功能播放Mp4视频 本地 SDK处理前 @@ -356,4 +357,5 @@ 超宽摄像头 长焦摄像头 进入画中画模式 + 透明渲染 \ No newline at end of file diff --git a/Android/APIExample/app/src/main/res/values/arrays.xml b/Android/APIExample/app/src/main/res/values/arrays.xml index d7588640a..e7a273f12 100644 --- a/Android/APIExample/app/src/main/res/values/arrays.xml +++ b/Android/APIExample/app/src/main/res/values/arrays.xml @@ -278,4 +278,18 @@ AINS_MODE_ULTRALOWLATENCY + + OFF + VOICE_AI_TUNER_MATURE_MALE + VOICE_AI_TUNER_FRESH_MALE + VOICE_AI_TUNER_ELEGANT_FEMALE + VOICE_AI_TUNER_SWEET_FEMALE + VOICE_AI_TUNER_WARM_MALE_SINGING + VOICE_AI_TUNER_GENTLE_FEMALE_SINGING + VOICE_AI_TUNER_HUSKY_MALE_SINGING + VOICE_AI_TUNER_WARM_ELEGANT_FEMALE_SINGING + VOICE_AI_TUNER_POWERFUL_MALE_SINGING + VOICE_AI_TUNER_DREAMY_FEMALE_SINGING + + \ No newline at end of file diff --git a/Android/APIExample/app/src/main/res/values/string_configs.xml b/Android/APIExample/app/src/main/res/values/string_configs.xml index 1592e3705..49b631ccc 100644 --- a/Android/APIExample/app/src/main/res/values/string_configs.xml +++ b/Android/APIExample/app/src/main/res/values/string_configs.xml @@ -25,14 +25,16 @@ or use the console to generate a temporary token. In order to get the APP ID, you can open the agora console (https://console.agora.io/) to create a project with the App Certificate enabled, - then the APP Certificate can be found in the project detail page. - PS: If the project does not have certificates enabled, leave this field blank. + then the APP Certificate can be found in the project detail page. If the project does not have certificates enabled, leave this field blank. + + PS: It is unsafe to place the App Certificate on the client side, it is recommended to place it on the server side to ensure that the App Certificate is not leaked. 声网APP证书 Agora 提供 App certificate 用以生成 Token。您可以在您的服务器部署并生成 Token,或者使用控制台生成临时的 Token。 - 进入声网控制台(https://console.agora.io/),创建一个带证书鉴权的项目,进入项目配置页,即可看到APP证书。 - 注意:如果项目没有开启证书鉴权,这个字段留空。 + 进入声网控制台(https://console.agora.io/),创建一个带证书鉴权的项目,进入项目配置页,即可看到APP证书。如果项目没有开启证书鉴权,这个字段留空。 + + 注意:App证书放在客户端不安全,推荐放在服务端以确保 App 证书不会泄露。 --> YOUR APP CERTIFICATE diff --git a/Android/APIExample/app/src/main/res/values/strings.xml b/Android/APIExample/app/src/main/res/values/strings.xml index 3eb08fe93..9833f83d0 100644 --- a/Android/APIExample/app/src/main/res/values/strings.xml +++ b/Android/APIExample/app/src/main/res/values/strings.xml @@ -201,6 +201,7 @@ This example demonstrates how to integrate third-party beauty sdk. This example demonstrates how to using ktv api to implement a scene demo. This example demonstrates how to use face capture function. + This example demonstrates how to use transparent rendering. PlayOut PreProcess @@ -375,4 +376,5 @@ Urltra Wide Telephoto Enter picture in picture mode + TransparentRendering diff --git a/Android/APIExample/cloud_build.sh b/Android/APIExample/cloud_build.sh new file mode 100755 index 000000000..50c656e56 --- /dev/null +++ b/Android/APIExample/cloud_build.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env sh + +# cache gradle to /tmp/.gradle +ls ~/.gradle || (mkdir -p /tmp/.gradle && ln -s /tmp/.gradle ~/.gradle && touch ~/.gradle/ln_$(date "+%y%m%d%H") && ls ~/.gradle) + +#change android maven to china repos +sed -ie "s#google()#maven { url \"https\://maven.aliyun.com/repository/public\" }\n google()#g" settings.gradle +sed -ie "s#https://services.gradle.org/distributions#https://mirrors.cloud.tencent.com/gradle#g" gradle/wrapper/gradle-wrapper.properties + +## config appId +sed -i -e "s#YOUR APP ID#${APP_ID}#g" app/src/main/res/values/string_configs.xml +sed -i -e "s#YOUR APP CERTIFICATE##g" app/src/main/res/values/string_configs.xml +sed -i -e "s#YOUR ACCESS TOKEN##g" app/src/main/res/values/string_configs.xml +rm -f app/src/main/res/values/string_configs.xml-e + +## config simple filter +sed -i -e "s#simpleFilter = false#simpleFilter = true#g" gradle.properties +rm -f gradle.properties-e +mkdir -p agora-simple-filter/src/main/agoraLibs +cp -r ../../sdk/arm64-v8a agora-simple-filter/src/main/agoraLibs/ +cp -r ../../sdk/armeabi-v7a agora-simple-filter/src/main/agoraLibs/ +curl -o opencv4.zip https://agora-adc-artifacts.s3.cn-north-1.amazonaws.com.cn/androidLibs/opencv4.zip +unzip opencv4.zip +mkdir -p agora-simple-filter/src/main/libs +mv arm64-v8a agora-simple-filter/src/main/libs +mv armeabi-v7a agora-simple-filter/src/main/libs +sed -i -e "s#jniLibs/#libs/#g" agora-simple-filter/src/main/cpp/CMakeLists.txt +rm -f agora-simple-filter/src/main/cpp/CMakeLists.txt-e + +## config agora stream encrypt +sed -i -e "s#streamEncrypt = false#streamEncrypt = true#g" gradle.properties +rm -f gradle.properties-e +mkdir -p agora-stream-encrypt/src/main/agoraLibs +cp -r ../../sdk/arm64-v8a agora-stream-encrypt/src/main/agoraLibs/ +cp -r ../../sdk/armeabi-v7a agora-stream-encrypt/src/main/agoraLibs/ + +## config beauty +sed -i -e "s#io.agora.api.example#io.agora.entfull#g" app/build.gradle +rm -f app/build.gradle-e +cd app/src/main || exit 1 +curl -L -H "X-JFrog-Art-Api:${JFROG_API_KEY}" -O "https://artifactory-api.bj2.agoralab.co/artifactory/qa_test_data/beauty/vender_faceunity_resources_apiexample.zip" +unzip -o vender_faceunity_resources_apiexample.zip +rm -f vender_faceunity_resources_apiexample.zip +curl -L -H "X-JFrog-Art-Api:${JFROG_API_KEY}" -O "https://artifactory-api.bj2.agoralab.co/artifactory/qa_test_data/beauty/vender_bytedance_resources_apiexample.zip" +unzip -o vender_bytedance_resources_apiexample.zip +rm -f vender_bytedance_resources_apiexample.zip +curl -L -H "X-JFrog-Art-Api:${JFROG_API_KEY}" -O "https://artifactory-api.bj2.agoralab.co/artifactory/qa_test_data/beauty/vender_sensetime_resources_apiexample.zip" +unzip -o vender_sensetime_resources_apiexample.zip +rm -f vender_sensetime_resources_apiexample.zip +cd - || exit 1 + +./gradlew clean || exit 1 +./gradlew :app:assembleRelease || exit 1 + +if [ "$WORKSPACE" != "" ]; then +SDK_VERSION=$(echo $sdk_url | cut -d "/" -f 5) +cp app/build/outputs/apk/release/*.apk $WORKSPACE/APIExample_${BUILD_NUMBER}_${SDK_VERSION}_$(date "+%Y%m%d%H%M%S").apk +fi \ No newline at end of file diff --git a/Android/APIExample/keystore.key b/Android/APIExample/keystore.key new file mode 100644 index 000000000..a5014a522 Binary files /dev/null and b/Android/APIExample/keystore.key differ diff --git a/README.kr.md b/README.kr.md index d508e563f..b28ff6867 100644 --- a/README.kr.md +++ b/README.kr.md @@ -6,14 +6,16 @@ 이 Repository에는 다음 플랫폼들을 포함한 Agora RTC Native SDK의 샘플 프로젝트가 포함되어 있습니다: -| 플랫폼 | 언어 | 프로젝트 위치 | SDK | -| -------- | -------- | ------------------------------------------------------ | ------------------------------------------------------------ | -| Android | Java | [/Android/APIExample](/Android/APIExample) | [RTC Java Video SDK](https://docs.agora.io/en/sdks?platform=android) | -| Android | Java | [/Android/APIExample-Audio](/Android/APIExample-Audio) | [RTC Java Audio SDK](https://docs.agora.io/en/sdks?platform=android) | -| iOS | Swift | [/iOS/APIExample](/iOS/APIExample) | [RTC Objective-C Video SDK](https://docs.agora.io/en/sdks?platform=ios) | -| iOS | Swift | [/iOS/APIExample-Audio](/iOS/APIExample-Audio) | [RTC Objective-C Audio SDK](https://docs.agora.io/en/sdks?platform=ios) | -| macOS | Swift | [/macOS](/macOS) | [RTC Objective-C Video SDK](https://docs.agora.io/en/sdks?platform=macos) | -| Windows | C++ | [/windows](/windows) | [RTC C++ Video SDK](https://docs.agora.io/en/sdks?platform=windows) | +| 플랫폼 | 언어 | 프로젝트 위치 | SDK | +|----------|-------------|------------------------------------------------------------|---------------------------------------------------------------------------| +| Android | Java | [/Android/APIExample](/Android/APIExample) | [RTC Java Video SDK](https://docs.agora.io/en/sdks?platform=android) | +| Android | Kotlin | [/Android/APIExample-Compose](/Android/APIExample-Compose) | [RTC Java Video SDK](https://docs.agora.io/en/sdks?platform=android) | +| Android | Java | [/Android/APIExample-Audio](/Android/APIExample-Audio) | [RTC Java Audio SDK](https://docs.agora.io/en/sdks?platform=android) | +| iOS | Swift | [/iOS/APIExample](/iOS/APIExample) | [RTC Objective-C Video SDK](https://docs.agora.io/en/sdks?platform=ios) | +| iOS | Objective-C | [/iOS/APIExample-OC](/iOS/APIExample-OC) | [RTC Objective-C Video SDK](https://docs.agora.io/en/sdks?platform=ios) | +| iOS | Swift | [/iOS/APIExample-Audio](/iOS/APIExample-Audio) | [RTC Objective-C Audio SDK](https://docs.agora.io/en/sdks?platform=ios) | +| macOS | Swift | [/macOS](/macOS) | [RTC Objective-C Video SDK](https://docs.agora.io/en/sdks?platform=macos) | +| Windows | C++ | [/windows](/windows) | [RTC C++ Video SDK](https://docs.agora.io/en/sdks?platform=windows) | 각 플랫폼의 내용을 참조하여 프로젝트에 대해 자세히 알아볼 수 있습니다. diff --git a/README.md b/README.md index b37dd8a3a..6d7940afd 100644 --- a/README.md +++ b/README.md @@ -6,15 +6,16 @@ _English | [中文](README.zh.md) | [한글](README.kr.md)_ This repository contains sample projects for the Agora RTC Native SDK, including the following platforms: -| Platform | Language | Project Location | SDK | -| -------- | -------- | ------------------------------------------------------ | ------------------------------------------------------------ | -| Android | Java | [/Android/APIExample](/Android/APIExample) | [RTC Java Video SDK](https://docs.agora.io/en/sdks?platform=android) | -| Android | Java | [/Android/APIExample-Audio](/Android/APIExample-Audio) | [RTC Java Audio SDK](https://docs.agora.io/en/sdks?platform=android) | -| iOS | Swift | [/iOS/APIExample](/iOS/APIExample) | [RTC Objective-C Video SDK](https://docs.agora.io/en/sdks?platform=ios)| -| iOS | Objective-C | [/iOS/APIExample-OC](/iOS/APIExample-OC) | [RTC Objective-C Video SDK](https://docs.agora.io/cn/video-call-4.x/downloads?platform=iOS) | -| iOS | Swift | [/iOS/APIExample-Audio](/iOS/APIExample-Audio) | [RTC Objective-C Audio SDK](https://docs.agora.io/en/sdks?platform=ios) | -| macOS | Swift | [/macOS](/macOS) | [RTC Objective-C Video SDK](https://docs.agora.io/en/sdks?platform=macos) | -| Windows | C++ | [/windows](/windows) | [RTC C++ Video SDK](https://docs.agora.io/en/sdks?platform=windows) | +| Platform | Language | Project Location | SDK | +|----------|-------------|------------------------------------------------------------|---------------------------------------------------------------------------| +| Android | Java | [/Android/APIExample](/Android/APIExample) | [RTC Java Video SDK](https://docs.agora.io/en/sdks?platform=android) | +| Android | Kotlin | [/Android/APIExample-Compose](/Android/APIExample-Compose) | [RTC Java Video SDK](https://docs.agora.io/en/sdks?platform=android) | +| Android | Java | [/Android/APIExample-Audio](/Android/APIExample-Audio) | [RTC Java Audio SDK](https://docs.agora.io/en/sdks?platform=android) | +| iOS | Swift | [/iOS/APIExample](/iOS/APIExample) | [RTC Objective-C Video SDK](https://docs.agora.io/en/sdks?platform=ios) | +| iOS | Objective-C | [/iOS/APIExample-OC](/iOS/APIExample-OC) | [RTC Objective-C Video SDK](https://docs.agora.io/en/sdks?platform=ios) | +| iOS | Swift | [/iOS/APIExample-Audio](/iOS/APIExample-Audio) | [RTC Objective-C Audio SDK](https://docs.agora.io/en/sdks?platform=ios) | +| macOS | Swift | [/macOS](/macOS) | [RTC Objective-C Video SDK](https://docs.agora.io/en/sdks?platform=macos) | +| Windows | C++ | [/windows](/windows) | [RTC C++ Video SDK](https://docs.agora.io/en/sdks?platform=windows) | You can refer to each individual platform to learn more about the projects. diff --git a/README.zh.md b/README.zh.md index 63b718068..631e19de5 100644 --- a/README.zh.md +++ b/README.zh.md @@ -6,15 +6,16 @@ 此仓库包含 Agora RTC Native SDK 的示例项目。 -| 平台 | 语言 | 项目位置 | SDK | -| -------- | -------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------- | -| Android | Java | [/Android/APIExample](/Android/APIExample) | [RTC Java Video SDK](https://docs.agora.io/cn/video-call-4.x/downloads?platform=Android) | -| Android | Java | [/Android/APIExample-Audio](/Android/APIExample-Audio) | [RTC Java Audio SDK](https://docs.agora.io/cn/voice-call-4.x/downloads?platform=Android) | -| iOS | Swift | [/iOS/APIExample](/iOS/APIExample) | [RTC Objective-C Video SDK](https://docs.agora.io/cn/video-call-4.x/downloads?platform=iOS) | -| iOS | Objective-C | [/iOS/APIExample-OC](/iOS/APIExample-OC) | [RTC Objective-C Video SDK](https://docs.agora.io/cn/video-call-4.x/downloads?platform=iOS) | -| iOS | Swift | [/iOS/APIExample-Audio](/iOS/APIExample-Audio) | [RTC Objective-C Audio SDK](https://docs.agora.io/cn/voice-call-4.x/downloads?platform=iOS) | -| macOS | Swift | [/macOS](/macOS) | [RTC Objective-C Video SDK](https://docs.agora.io/cn/video-call-4.x/downloads?platform=macOS) | -| Windows | C++ | [/windows](/windows) | [RTC C++ Video SDK](https://docs.agora.io/cn/video-call-4.x/downloads?platform=Windows) | +| 平台 | 语言 | 项目位置 | SDK | +|---------|-------------|------------------------------------------------------------|-------------------------------------------------------------------------------| +| Android | Java | [/Android/APIExample](/Android/APIExample) | [RTC Java Video SDK](https://doc.shengwang.cn/doc/rtc/android/resources) | +| Android | Kotlin | [/Android/APIExample-Compose](/Android/APIExample-Compose) | [RTC Java Video SDK](https://doc.shengwang.cn/doc/rtc/android/resources) | +| Android | Java | [/Android/APIExample-Audio](/Android/APIExample-Audio) | [RTC Java Audio SDK](https://doc.shengwang.cn/doc/rtc/android/resources) | +| iOS | Swift | [/iOS/APIExample](/iOS/APIExample) | [RTC Objective-C Video SDK](https://doc.shengwang.cn/doc/rtc/ios/resources) | +| iOS | Objective-C | [/iOS/APIExample-OC](/iOS/APIExample-OC) | [RTC Objective-C Video SDK](https://doc.shengwang.cn/doc/rtc/ios/resources) | +| iOS | Swift | [/iOS/APIExample-Audio](/iOS/APIExample-Audio) | [RTC Objective-C Audio SDK](https://doc.shengwang.cn/doc/rtc/ios/resources) | +| macOS | Swift | [/macOS](/macOS) | [RTC Objective-C Video SDK](https://doc.shengwang.cn/doc/rtc/macos/resources) | +| Windows | C++ | [/windows](/windows) | [RTC C++ Video SDK](https://doc.shengwang.cn/doc/rtc/windows/resources) | 你可以进入不同平台的项目进行试用或参考源代码。 diff --git a/iOS/APIExample-Audio/APIExample-Audio.xcodeproj/project.pbxproj b/iOS/APIExample-Audio/APIExample-Audio.xcodeproj/project.pbxproj index 322376567..2dcbff869 100644 --- a/iOS/APIExample-Audio/APIExample-Audio.xcodeproj/project.pbxproj +++ b/iOS/APIExample-Audio/APIExample-Audio.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 51; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -58,6 +58,7 @@ A7847F942458089E00469187 /* AgoraExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7847F932458089E00469187 /* AgoraExtension.swift */; }; A7BD7660247CC6920062A6B3 /* UITypeAlias.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7BD765F247CC6920062A6B3 /* UITypeAlias.swift */; }; A7CA48C424553CF700507435 /* Popover.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A7CA48C224553CF600507435 /* Popover.storyboard */; }; + DD8A1F812CA54F90001CEC51 /* AgoraPCMPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8A1F802CA54F90001CEC51 /* AgoraPCMPlayer.swift */; }; E70EE9E728F6A6FF00EE9E7F /* JoinChannelAudioToken.strings in Resources */ = {isa = PBXBuildFile; fileRef = E70EE9E228F6A6FF00EE9E7F /* JoinChannelAudioToken.strings */; }; E70EE9E828F6A6FF00EE9E7F /* JoinChannelAudioToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = E70EE9E428F6A6FF00EE9E7F /* JoinChannelAudioToken.swift */; }; E70EE9E928F6A6FF00EE9E7F /* JoinChannelAudioToken.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E70EE9E528F6A6FF00EE9E7F /* JoinChannelAudioToken.storyboard */; }; @@ -184,6 +185,7 @@ A7CA48C324553CF600507435 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Popover.storyboard; sourceTree = ""; }; BC25C1A6D9E6B8827D095985 /* Pods_SimpleFilter.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SimpleFilter.framework; sourceTree = BUILT_PRODUCTS_DIR; }; CC6D08A23527C200339E4FD6 /* Pods-SimpleAudioFilter.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SimpleAudioFilter.release.xcconfig"; path = "Target Support Files/Pods-SimpleAudioFilter/Pods-SimpleAudioFilter.release.xcconfig"; sourceTree = ""; }; + DD8A1F802CA54F90001CEC51 /* AgoraPCMPlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AgoraPCMPlayer.swift; sourceTree = ""; }; E70EE9E328F6A6FF00EE9E7F /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/JoinChannelAudioToken.strings"; sourceTree = ""; }; E70EE9E428F6A6FF00EE9E7F /* JoinChannelAudioToken.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JoinChannelAudioToken.swift; sourceTree = ""; }; E70EE9E628F6A6FF00EE9E7F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/JoinChannelAudioToken.storyboard; sourceTree = ""; }; @@ -374,6 +376,7 @@ 03DF1D8424CFC29700DF7151 /* ExternalAudio */ = { isa = PBXGroup; children = ( + DD8A1F802CA54F90001CEC51 /* AgoraPCMPlayer.swift */, 03DF1D8524CFC29700DF7151 /* AudioOptions.h */, 03DF1D8C24CFC29700DF7151 /* AudioWriteToFile.h */, 03DF1D8624CFC29700DF7151 /* AudioWriteToFile.m */, @@ -689,6 +692,7 @@ 03BEED08251C35E7005E78F4 /* AudioMixing.swift in Sources */, E74877B728A23B8B00CA2F58 /* NetworkManager.swift in Sources */, 03DF1D9424CFC29700DF7151 /* AudioController.m in Sources */, + DD8A1F812CA54F90001CEC51 /* AgoraPCMPlayer.swift in Sources */, 03DF1D9024CFC29700DF7151 /* AudioWriteToFile.m in Sources */, E72055EC28F94C400030E6D1 /* Util.swift in Sources */, 0339BE6D251DEAFC007D4FDD /* PrecallTest.swift in Sources */, @@ -982,12 +986,10 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = ""; CODE_SIGN_IDENTITY = "Apple Development"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 2; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = JDPG69R49Z; + DEVELOPMENT_TEAM = YS397FG5PA; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -1038,7 +1040,6 @@ PRODUCT_BUNDLE_IDENTIFIER = io.agora.api.examples.audio; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = AgoraLab2020; SWIFT_OBJC_BRIDGING_HEADER = "APIExample-Audio/APIExample-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -1056,11 +1057,9 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = ""; CODE_SIGN_IDENTITY = "Apple Development"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 2; - DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = JDPG69R49Z; + DEVELOPMENT_TEAM = YS397FG5PA; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -1111,7 +1110,6 @@ PRODUCT_BUNDLE_IDENTIFIER = io.agora.api.examples.audio; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = AgoraLab2020; SWIFT_OBJC_BRIDGING_HEADER = "APIExample-Audio/APIExample-Bridging-Header.h"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/iOS/APIExample-Audio/APIExample-Audio/Common/AgoraExtension.swift b/iOS/APIExample-Audio/APIExample-Audio/Common/AgoraExtension.swift index e0c5af050..c70221b87 100644 --- a/iOS/APIExample-Audio/APIExample-Audio/Common/AgoraExtension.swift +++ b/iOS/APIExample-Audio/APIExample-Audio/Common/AgoraExtension.swift @@ -186,6 +186,36 @@ extension AgoraAudioReverbPreset { } } + +extension AUDIO_AINS_MODE { + func description() -> String { + switch self { + case .AINS_MODE_AGGRESSIVE: return "AGGRESSIVE".localized + case .AINS_MODE_BALANCED: return "BALANCED".localized + case .AINS_MODE_ULTRALOWLATENCY: return "ULTRALOWLATENCY".localized + @unknown default: return "\(self.rawValue)" + } + } +} + +extension AgoraVoiceAITunerType { + func description() -> String { + switch self { + case .matureMale: return "AI_Tunner_Mature_Male".localized + case .freshMale: return "AI_Tunner_Fresh_Male".localized + case .elegantFemale: return "AI_Tunner_Elegant_Female".localized + case .sweetFemale: return "AI_Tunner_Sweet_Female".localized + case .warmMaleSinging: return "AI_Tunner_Warm_Male_Singing".localized + case .gentleFemaleSinging: return "AI_Tunner_Gentle_Female_Singing".localized + case .huskyMaleSinging: return "AI_Tunner_Husky_Male_Singing".localized + case .warmElegantFemaleSinging: return "AI_Tunner_Warm_Elegant_Female_Singing".localized + case .powerfulMaleSinging: return "AI_Tunner_Powerful_Male_Singing".localized + case .dreamyFemaleSinging: return "AI_Tunner_Dreamy_Female_Singing".localized + @unknown default: return "\(self.rawValue)" + } + } +} + extension AgoraAudioEffectPreset { func description() -> String { switch self { diff --git a/iOS/APIExample-Audio/APIExample-Audio/Common/ExternalAudio/AgoraPCMPlayer.swift b/iOS/APIExample-Audio/APIExample-Audio/Common/ExternalAudio/AgoraPCMPlayer.swift new file mode 100644 index 000000000..94fdff3fe --- /dev/null +++ b/iOS/APIExample-Audio/APIExample-Audio/Common/ExternalAudio/AgoraPCMPlayer.swift @@ -0,0 +1,56 @@ +// +// AgoraPCMPlayer.swift +// APIExample +// +// Created by wushengtao on 2024/9/26. +// Copyright © 2024 Agora Corp. All rights reserved. +// + +import AVFoundation + +class AgoraPCMPlayer { + private var audioEngine: AVAudioEngine + private var playerNode: AVAudioPlayerNode + private var sampleRate: Double + private var channels: AVAudioChannelCount + + init(sampleRate: Double, channels: AVAudioChannelCount) { + self.sampleRate = sampleRate + self.channels = channels + + audioEngine = AVAudioEngine() + playerNode = AVAudioPlayerNode() + + audioEngine.attach(playerNode) + + let format = AVAudioFormat(standardFormatWithSampleRate: sampleRate, channels: channels) + audioEngine.connect(playerNode, to: audioEngine.mainMixerNode, format: format) + + do { + try audioEngine.start() + } catch { + print("Audio Engine failed to start: \(error)") + } + } + + func playPCMData(pcmData: UnsafeMutablePointer, count: UInt) { + guard let format = AVAudioFormat(standardFormatWithSampleRate: sampleRate, channels: channels), + let audioBuffer = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: AVAudioFrameCount(count / 4)), // 16位立体声每帧4字节 + let channelData = audioBuffer.floatChannelData else { + return + } + + audioBuffer.frameLength = AVAudioFrameCount(count / 4) + + for frame in 0.. + static let Certificate: String? = nil } diff --git a/iOS/APIExample-Audio/APIExample-Audio/Common/NetworkManager/NetworkManager.swift b/iOS/APIExample-Audio/APIExample-Audio/Common/NetworkManager/NetworkManager.swift index 2ca0a6df5..74a3403e1 100644 --- a/iOS/APIExample-Audio/APIExample-Audio/Common/NetworkManager/NetworkManager.swift +++ b/iOS/APIExample-Audio/APIExample-Audio/Common/NetworkManager/NetworkManager.swift @@ -30,7 +30,7 @@ class NetworkManager { static let shared = NetworkManager() private init() { } - private let baseUrl = "https://test-toolbox.bj2.agoralab.co/v1/token/generate" + private let baseUrl = "https://service.agora.io/toolbox-global/v1/token/generate" func generateToken(channelName: String, uid: UInt = 0, success: @escaping (String?) -> Void) { if KeyCenter.Certificate == nil || KeyCenter.Certificate?.isEmpty == true { diff --git a/iOS/APIExample-Audio/APIExample-Audio/Examples/Advanced/CustomAudioRender/CustomAudioRender.swift b/iOS/APIExample-Audio/APIExample-Audio/Examples/Advanced/CustomAudioRender/CustomAudioRender.swift index d4bbcb631..a8ad51532 100644 --- a/iOS/APIExample-Audio/APIExample-Audio/Examples/Advanced/CustomAudioRender/CustomAudioRender.swift +++ b/iOS/APIExample-Audio/APIExample-Audio/Examples/Advanced/CustomAudioRender/CustomAudioRender.swift @@ -6,10 +6,15 @@ // Copyright © 2020 Agora Corp. All rights reserved. // -import Foundation +import AVFoundation import AgoraRtcKit import AGEVideoLayout +private let bitsPerSample: UInt = 16 +private let samples: UInt = 441 * 10 +private let sampleRate: UInt = 44100 +private let channels: UInt = 2 + class CustomAudioRenderEntry : UIViewController { @IBOutlet weak var joinButton: AGButton! @@ -36,7 +41,6 @@ class CustomAudioRenderEntry : UIViewController class CustomAudioRenderMain: BaseViewController { var agoraKit: AgoraRtcEngineKit! - var exAudio: ExternalAudio = ExternalAudio.shared() @IBOutlet weak var container: AGEVideoContainer! var audioViews: [UInt:VideoView] = [:] @@ -44,11 +48,11 @@ class CustomAudioRenderMain: BaseViewController { // indicate if current instance has joined channel var isJoined: Bool = false + lazy var player = AgoraPCMPlayer(sampleRate: Double(sampleRate), channels: AVAudioChannelCount(channels)) + override func viewDidLoad(){ super.viewDidLoad() - let sampleRate:UInt = 44100, channel:UInt = 1 - // set up agora instance when view loaded let config = AgoraRtcEngineConfig() config.appId = KeyCenter.AppId @@ -61,9 +65,6 @@ class CustomAudioRenderMain: BaseViewController { guard let channelName = configs["channelName"] as? String else {return} - // make myself a broadcaster - agoraKit.setClientRole(GlobalSettings.shared.getUserRole()) - // disable video module agoraKit.disableVideo() agoraKit.enableAudio() @@ -76,7 +77,6 @@ class CustomAudioRenderMain: BaseViewController { // agoraKit.setAudioFrameDelegate(self) // agoraKit.setPlaybackAudioFrameParametersWithSampleRate(Int(sampleRate), channel: Int(channel), mode: .readOnly, samplesPerCall: Int(sampleRate*channel)/100) - exAudio.setupExternalAudio(withAgoraKit: agoraKit, sampleRate: UInt32(sampleRate), channels: UInt32(channel), audioCRMode: .sdkCaptureExterRender, ioType: .remoteIO) agoraKit.setParameters("{\"che.audio.external_render\": true}") agoraKit.setParameters("{\"che.audio.keep.audiosession\": true}") @@ -88,8 +88,13 @@ class CustomAudioRenderMain: BaseViewController { // the token has to match the ones used for channel join let option = AgoraRtcChannelMediaOptions() option.publishCameraTrack = false - option.publishMicrophoneTrack = true - option.clientRoleType = GlobalSettings.shared.getUserRole() + option.publishMicrophoneTrack = false + option.autoSubscribeAudio = true + option.autoSubscribeVideo = false + option.channelProfile = .liveBroadcasting + option.clientRoleType = .broadcaster + + agoraKit.enableExternalAudioSink(true, sampleRate: sampleRate, channels: channels) NetworkManager.shared.generateToken(channelName: channelName, success: { token in let result = self.agoraKit.joinChannel(byToken: token, channelId: channelName, uid: 0, mediaOptions: option) if result != 0 { @@ -107,12 +112,35 @@ class CustomAudioRenderMain: BaseViewController { // leave channel when exiting the view if isJoined { agoraKit.disableAudio() + agoraKit.enableExternalAudioSink(false, sampleRate: sampleRate, channels: channels) + isJoined = false agoraKit.leaveChannel { (stats) -> Void in LogUtils.log(message: "left channel, duration: \(stats.duration)", level: .info) } } } } + + private func startPullAudio() { + DispatchQueue.global().async { + let pullMs: TimeInterval = 100 + let lengthInByte = sampleRate / 1000 * 2 * channels * UInt(pullMs) + let pointer = UnsafeMutablePointer.allocate(capacity: Int(lengthInByte)) + var deltaMs: TimeInterval = 0 + while self.isJoined { + let date = Date() + memset(pointer, 0, Int(lengthInByte)) + let ret = self.agoraKit.pullPlaybackAudioFrameRawData(pointer, lengthInByte: lengthInByte) + self.player.playPCMData(pcmData: pointer, count: lengthInByte) + let cost = -date.timeIntervalSinceNow * 1000 + usleep(UInt32(max((pullMs - cost - deltaMs) * 1000, 0))) + // add deltaMs to ensure that the thread processing time is less than pullMs, as thread sleep may not be accurate + deltaMs = max((-date.timeIntervalSinceNow * 1000) - pullMs, 0) * 2 +// print("pullPlaybackAudioFrameRawData: \(ret) lengthInByte: \(lengthInByte) cost: \(-date.timeIntervalSinceNow * 1000) ms, deltaMs: \(deltaMs) ms") + } + pointer.deallocate() + } + } } /// agora rtc engine delegate events @@ -147,6 +175,7 @@ extension CustomAudioRenderMain: AgoraRtcEngineDelegate { self.audioViews[uid] = view view.setPlaceholder(text: self.getAudioLabel(uid: uid, isLocal: true)) self.container.layoutStream3x3(views: Array(self.audioViews.values)) + startPullAudio() } /// callback when a remote user is joinning the channel, note audience in live broadcast mode will NOT trigger this event diff --git a/iOS/APIExample-Audio/APIExample-Audio/Examples/Advanced/CustomPcmAudioSource/CustomPcmAudioSource.swift b/iOS/APIExample-Audio/APIExample-Audio/Examples/Advanced/CustomPcmAudioSource/CustomPcmAudioSource.swift index ce7da158b..46c2b0a96 100644 --- a/iOS/APIExample-Audio/APIExample-Audio/Examples/Advanced/CustomPcmAudioSource/CustomPcmAudioSource.swift +++ b/iOS/APIExample-Audio/APIExample-Audio/Examples/Advanced/CustomPcmAudioSource/CustomPcmAudioSource.swift @@ -51,7 +51,7 @@ class CustomPcmAudioSourceMain: BaseViewController { } } - let sampleRate:UInt = 44100, channel:UInt = 1, bitPerSample = 16, samples = 441 * 10 + let sampleRate:UInt = 44100, channel:UInt = 2, bitPerSample = 16, samples = 441 * 10 override func viewDidLoad(){ super.viewDidLoad() @@ -96,7 +96,7 @@ class CustomPcmAudioSourceMain: BaseViewController { // the token has to match the ones used for channel join let option = AgoraRtcChannelMediaOptions() option.publishCameraTrack = false - option.publishMicrophoneTrack = GlobalSettings.shared.getUserRole() == .broadcaster + option.publishMicrophoneTrack = false option.publishCustomAudioTrack = GlobalSettings.shared.getUserRole() == .broadcaster option.publishCustomAudioTrackId = Int(trackId) option.clientRoleType = GlobalSettings.shared.getUserRole() @@ -143,7 +143,7 @@ class CustomPcmAudioSourceMain: BaseViewController { extension CustomPcmAudioSourceMain: AgoraPcmSourcePushDelegate { func onAudioFrame(data: UnsafeMutablePointer) { agoraKit.pushExternalAudioFrameRawData(data, - samples: samples, + samples: samples*Int(channel), sampleRate: Int(sampleRate), channels: Int(channel), trackId: Int(trackId), diff --git a/iOS/APIExample-Audio/APIExample-Audio/Examples/Advanced/VoiceChanger/Base.lproj/VoiceChanger.storyboard b/iOS/APIExample-Audio/APIExample-Audio/Examples/Advanced/VoiceChanger/Base.lproj/VoiceChanger.storyboard index 7f574fd57..ac93c8386 100644 --- a/iOS/APIExample-Audio/APIExample-Audio/Examples/Advanced/VoiceChanger/Base.lproj/VoiceChanger.storyboard +++ b/iOS/APIExample-Audio/APIExample-Audio/Examples/Advanced/VoiceChanger/Base.lproj/VoiceChanger.storyboard @@ -1,9 +1,9 @@ - + - + @@ -12,7 +12,7 @@ - + @@ -64,7 +64,7 @@ - + @@ -80,17 +80,43 @@ - + + + + + + + + + + + + + + + - + - + - + - + - + - + @@ -235,7 +261,7 @@ - + - + - + @@ -296,7 +322,7 @@ - + - + - + @@ -376,8 +402,42 @@ + + + + + + + + + + + + + + + + + - + @@ -404,13 +464,13 @@ - + - + - - - - + + - - - - - - - + + + + + + + + @@ -494,7 +554,10 @@ + + + @@ -502,8 +565,12 @@ - + + + + + @@ -527,7 +594,7 @@ - + @@ -578,6 +645,7 @@ + @@ -591,6 +659,7 @@ + diff --git a/iOS/APIExample-Audio/APIExample-Audio/Examples/Advanced/VoiceChanger/VoiceChanger.swift b/iOS/APIExample-Audio/APIExample-Audio/Examples/Advanced/VoiceChanger/VoiceChanger.swift index cdd93c6fc..a3b263e70 100644 --- a/iOS/APIExample-Audio/APIExample-Audio/Examples/Advanced/VoiceChanger/VoiceChanger.swift +++ b/iOS/APIExample-Audio/APIExample-Audio/Examples/Advanced/VoiceChanger/VoiceChanger.swift @@ -11,9 +11,7 @@ import UIKit import AgoraRtcKit import AGEVideoLayout - -class VoiceChangerEntry : UIViewController -{ +class VoiceChangerEntry: UIViewController { @IBOutlet weak var joinButton: AGButton! @IBOutlet weak var channelTextField: AGTextField! let identifier = "VoiceChanger" @@ -24,14 +22,16 @@ class VoiceChangerEntry : UIViewController @IBAction func doJoinPressed(sender: AGButton) { guard let channelName = channelTextField.text else {return} - //resign channel text field + // 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} + guard let newViewController = storyBoard.instantiateViewController(withIdentifier: identifier) as? BaseViewController else { + return + } newViewController.title = channelName - newViewController.configs = ["channelName":channelName] + newViewController.configs = ["channelName": channelName] navigationController?.pushViewController(newViewController, animated: true) } } @@ -47,24 +47,28 @@ class VoiceChangerMain: BaseViewController { @IBOutlet weak var voiceConversionBtn: UIButton! @IBOutlet weak var equalizationFreqBtn: UIButton! @IBOutlet weak var reverbKeyBtn: UIButton! + @IBOutlet weak var voiceAiTunerBtn: UIButton! @IBOutlet weak var reverbValueSlider: UISlider! @IBOutlet weak var audioEffectParam1Slider: UISlider! @IBOutlet weak var audioEffectParam2Slider: UISlider! @IBOutlet weak var audioEffectParam1Label: UILabel! @IBOutlet weak var audioEffectParam2Label: UILabel! @IBOutlet weak var container: AGEVideoContainer! - var audioViews: [UInt:VideoView] = [:] + @IBOutlet weak var ainsModeButton: UIButton! + + var audioViews: [UInt: VideoView] = [:] var equalizationFreq: AgoraAudioEqualizationBandFrequency = .band31 + var aitunerType: AgoraVoiceAITunerType? var equalizationGain: Int = 0 var reverbType: AgoraAudioReverbType = .dryLevel - var reverbMap:[AgoraAudioReverbType:Int] = [ - .dryLevel:0, - .wetLevel:0, - .roomSize:0, - .wetDelay:0, - .strength:0 + var reverbMap: [AgoraAudioReverbType: Int] = [ + .dryLevel: 0, + .wetLevel: 0, + .roomSize: 0, + .wetDelay: 0, + .strength: 0 ] - var currentAudioEffects:AgoraAudioEffectPreset = .off + var currentAudioEffects: AgoraAudioEffectPreset = .off // indicate if current instance has joined channel var isJoined: Bool = false @@ -77,11 +81,12 @@ class VoiceChangerMain: BaseViewController { roomAcousticsBtn.setTitle("Off", for: .normal) pitchCorrectionBtn.setTitle("Off", for: .normal) voiceConversionBtn.setTitle("Off", for: .normal) + ainsModeButton.setTitle(AUDIO_AINS_MODE.AINS_MODE_BALANCED.description(), for: .normal) } - func updateAudioEffectsControls(_ effect:AgoraAudioEffectPreset) { + func updateAudioEffectsControls(_ effect: AgoraAudioEffectPreset) { currentAudioEffects = effect - if(effect == .roomAcous3DVoice) { + if effect == .roomAcous3DVoice { audioEffectParam1Slider.isEnabled = true audioEffectParam2Slider.isEnabled = false audioEffectParam1Label.text = "Cycle" @@ -89,7 +94,7 @@ class VoiceChangerMain: BaseViewController { audioEffectParam1Slider.minimumValue = 0 audioEffectParam1Slider.maximumValue = 60 audioEffectParam1Slider.value = 10 - } else if(effect == .pitchCorrection) { + } else if effect == .pitchCorrection { audioEffectParam1Slider.isEnabled = true audioEffectParam2Slider.isEnabled = true audioEffectParam1Label.text = "Tonic Mode" @@ -109,74 +114,88 @@ class VoiceChangerMain: BaseViewController { } } - func getChatBeautifierAction(_ chatBeautifier:AgoraVoiceBeautifierPreset) -> UIAlertAction{ - return UIAlertAction(title: "\(chatBeautifier.description())", style: .default, handler: {[unowned self] action in + func getChatBeautifierAction(_ chatBeautifier: AgoraVoiceBeautifierPreset) -> UIAlertAction { + return UIAlertAction(title: "\(chatBeautifier.description())", + style: .default, + handler: { [unowned self] _ in self.resetVoiceChanger() self.updateAudioEffectsControls(.off) - //when using this method with setLocalVoiceReverbPreset, - //the method called later overrides the one called earlier + // when using this method with setLocalVoiceReverbPreset, + // the method called later overrides the one called earlier self.agoraKit.setVoiceBeautifierPreset(chatBeautifier) self.chatBeautifierBtn.setTitle("\(chatBeautifier.description())", for: .normal) }) } - func getTimbreTransformationAction(_ timbreTransformation:AgoraVoiceBeautifierPreset) -> UIAlertAction{ - return UIAlertAction(title: "\(timbreTransformation.description())", style: .default, handler: {[unowned self] action in + func getTimbreTransformationAction(_ timbreTransformation: AgoraVoiceBeautifierPreset) -> UIAlertAction { + return UIAlertAction(title: "\(timbreTransformation.description())", + style: .default, + handler: { [unowned self] _ in self.resetVoiceChanger() self.updateAudioEffectsControls(.off) - //when using this method with setLocalVoiceReverbPreset, - //the method called later overrides the one called earlier + // when using this method with setLocalVoiceReverbPreset, + // the method called later overrides the one called earlier self.agoraKit.setVoiceBeautifierPreset(timbreTransformation) self.timbreTransformationBtn.setTitle("\(timbreTransformation.description())", for: .normal) }) } - func getVoiceChangerAction(_ voiceChanger:AgoraAudioEffectPreset) -> UIAlertAction{ - return UIAlertAction(title: "\(voiceChanger.description())", style: .default, handler: {[unowned self] action in + func getVoiceChangerAction(_ voiceChanger: AgoraAudioEffectPreset) -> UIAlertAction { + return UIAlertAction(title: "\(voiceChanger.description())", + style: .default, + handler: { [unowned self] _ in self.resetVoiceChanger() self.updateAudioEffectsControls(voiceChanger) - //when using this method with setLocalVoiceReverbPreset, - //the method called later overrides the one called earlier + // when using this method with setLocalVoiceReverbPreset, + // the method called later overrides the one called earlier self.agoraKit.setAudioEffectPreset(voiceChanger) self.voiceChangerBtn.setTitle("\(voiceChanger.description())", for: .normal) }) } - func getStyleTransformationAction(_ styleTransformation:AgoraAudioEffectPreset) -> UIAlertAction{ - return UIAlertAction(title: "\(styleTransformation.description())", style: .default, handler: {[unowned self] action in + func getStyleTransformationAction(_ styleTransformation: AgoraAudioEffectPreset) -> UIAlertAction { + return UIAlertAction(title: "\(styleTransformation.description())", + style: .default, + handler: { [unowned self] _ in self.resetVoiceChanger() self.updateAudioEffectsControls(styleTransformation) - //when using this method with setLocalVoiceChanger, - //the method called later overrides the one called earlier + // when using this method with setLocalVoiceChanger, + // the method called later overrides the one called earlier self.agoraKit.setAudioEffectPreset(styleTransformation) self.styleTransformationBtn.setTitle("\(styleTransformation.description())", for: .normal) }) } - func getRoomAcousticsAction(_ roomAcoustics:AgoraAudioEffectPreset) -> UIAlertAction{ - return UIAlertAction(title: "\(roomAcoustics.description())", style: .default, handler: {[unowned self] action in + func getRoomAcousticsAction(_ roomAcoustics: AgoraAudioEffectPreset) -> UIAlertAction { + return UIAlertAction(title: "\(roomAcoustics.description())", + style: .default, + handler: { [unowned self] _ in self.resetVoiceChanger() self.updateAudioEffectsControls(roomAcoustics) - //when using this method with setLocalVoiceReverbPreset, - //the method called later overrides the one called earlier + // when using this method with setLocalVoiceReverbPreset, + // the method called later overrides the one called earlier self.agoraKit.setAudioEffectPreset(roomAcoustics) self.roomAcousticsBtn.setTitle("\(roomAcoustics.description())", for: .normal) }) } - func getPitchCorrectionAction(_ pitchCorrection:AgoraAudioEffectPreset) -> UIAlertAction{ - return UIAlertAction(title: "\(pitchCorrection.description())", style: .default, handler: {[unowned self] action in + func getPitchCorrectionAction(_ pitchCorrection: AgoraAudioEffectPreset) -> UIAlertAction { + return UIAlertAction(title: "\(pitchCorrection.description())", + style: .default, + handler: { [unowned self] _ in self.resetVoiceChanger() self.updateAudioEffectsControls(pitchCorrection) - //when using this method with setLocalVoiceReverbPreset, - //the method called later overrides the one called earlier + // when using this method with setLocalVoiceReverbPreset, + // the method called later overrides the one called earlier self.agoraKit.setAudioEffectPreset(pitchCorrection) self.pitchCorrectionBtn.setTitle("\(pitchCorrection.description())", for: .normal) }) } - func getVoiceConversionAction(_ voiceConversion:AgoraVoiceConversionPreset) -> UIAlertAction{ - return UIAlertAction(title: "\(voiceConversion.description())", style: .default, handler: {[unowned self] action in + func getVoiceConversionAction(_ voiceConversion: AgoraVoiceConversionPreset) -> UIAlertAction { + return UIAlertAction(title: "\(voiceConversion.description())", + style: .default, + handler: { [unowned self] _ in self.resetVoiceChanger() self.updateAudioEffectsControls(.off) self.agoraKit.setVoiceConversionPreset(voiceConversion) @@ -184,8 +203,10 @@ class VoiceChangerMain: BaseViewController { }) } - func getEqualizationFreqAction(_ freq:AgoraAudioEqualizationBandFrequency) -> UIAlertAction { - return UIAlertAction(title: "\(freq.description())", style: .default, handler: {[unowned self] action in + func getEqualizationFreqAction(_ freq: AgoraAudioEqualizationBandFrequency) -> UIAlertAction { + return UIAlertAction(title: "\(freq.description())", + style: .default, + handler: { [unowned self] _ in self.equalizationFreq = freq self.equalizationFreqBtn.setTitle("\(freq.description())", for: .normal) LogUtils.log(message: "onLocalVoiceEqualizationGain \(self.equalizationFreq.description()) \(self.equalizationGain)", level: .info) @@ -193,16 +214,52 @@ class VoiceChangerMain: BaseViewController { }) } - func getReverbKeyAction(_ reverbType:AgoraAudioReverbType) -> UIAlertAction { - return UIAlertAction(title: "\(reverbType.description())", style: .default, handler: {[unowned self] action in + func getReverbKeyAction(_ reverbType: AgoraAudioReverbType) -> UIAlertAction { + return UIAlertAction(title: "\(reverbType.description())", + style: .default, + handler: { [unowned self] _ in self.updateReverbValueRange(reverbKey: reverbType) self.reverbKeyBtn.setTitle("\(reverbType.description())", for: .normal) }) } + func getVoiceAITunerAction(_ type: AgoraVoiceAITunerType?) -> UIAlertAction { + let title = "\(type?.description() ?? "Off")" + return UIAlertAction(title: title, + style: .default, + handler: { [unowned self] _ in + self.updateVoiceAiTuner(type: type) + self.voiceAiTunerBtn.setTitle(title, for: .normal) + }) + } + + func getAINSModeAction(_ ainsMode: AUDIO_AINS_MODE) -> UIAlertAction { + return UIAlertAction(title: "\(ainsMode.description())", + style: .default, + handler: { [unowned self] _ in + self.agoraKit.setAINSMode(true, mode: ainsMode) + self.ainsModeButton.setTitle("\(ainsMode.description())", for: .normal) + }) + } + + @IBAction func onTapAINSButton(_ sender: UIButton) { + let style: UIAlertController.Style = UIDevice.current.userInterfaceIdiom == .pad ? .alert : .actionSheet + let alert = UIAlertController(title: "Set AINS Mode".localized, + message: nil, + preferredStyle: style) + alert.addAction(getAINSModeAction(.AINS_MODE_BALANCED)) + alert.addAction(getAINSModeAction(.AINS_MODE_AGGRESSIVE)) + alert.addAction(getAINSModeAction(.AINS_MODE_ULTRALOWLATENCY)) + alert.addCancelAction() + present(alert, animated: true, completion: nil) + } + /// callback when voice changer button hit @IBAction func onChatBeautifier() { - let alert = UIAlertController(title: "Set Chat Beautifier".localized, message: nil, preferredStyle: UIDevice.current.userInterfaceIdiom == .pad ? UIAlertController.Style.alert : UIAlertController.Style.actionSheet) + let style: UIAlertController.Style = UIDevice.current.userInterfaceIdiom == .pad ? .alert : .actionSheet + let alert = UIAlertController(title: "Set Chat Beautifier".localized, + message: nil, + preferredStyle: style) alert.addAction(getChatBeautifierAction(.presetOff)) alert.addAction(getChatBeautifierAction(.presetChatBeautifierFresh)) alert.addAction(getChatBeautifierAction(.presetChatBeautifierVitality)) @@ -213,7 +270,10 @@ class VoiceChangerMain: BaseViewController { /// callback when voice changer button hit @IBAction func onTimbreTransformation() { - let alert = UIAlertController(title: "Set Timbre Transformation".localized, message: nil, preferredStyle: UIDevice.current.userInterfaceIdiom == .pad ? UIAlertController.Style.alert : UIAlertController.Style.actionSheet) + let style: UIAlertController.Style = UIDevice.current.userInterfaceIdiom == .pad ? .alert : .actionSheet + let alert = UIAlertController(title: "Set Timbre Transformation".localized, + message: nil, + preferredStyle: style) alert.addAction(getTimbreTransformationAction(.presetOff)) alert.addAction(getTimbreTransformationAction(.timbreTransformationVigorous)) alert.addAction(getTimbreTransformationAction(.timbreTransformationDeep)) @@ -229,7 +289,8 @@ class VoiceChangerMain: BaseViewController { /// callback when voice changer button hit @IBAction func onVoiceChanger() { - let alert = UIAlertController(title: "Set Voice Changer".localized, message: nil, preferredStyle: UIDevice.current.userInterfaceIdiom == .pad ? UIAlertController.Style.alert : UIAlertController.Style.actionSheet) + let style: UIAlertController.Style = UIDevice.current.userInterfaceIdiom == .pad ? .alert : .actionSheet + let alert = UIAlertController(title: "Set Voice Changer".localized, message: nil, preferredStyle: style) alert.addAction(getVoiceChangerAction(.off)) alert.addAction(getVoiceChangerAction(.voiceChangerEffectUncle)) alert.addAction(getVoiceChangerAction(.voiceChangerEffectOldMan)) @@ -244,7 +305,8 @@ class VoiceChangerMain: BaseViewController { /// callback when voice changer button hit @IBAction func onStyleTransformation() { - let alert = UIAlertController(title: "Set Style Transformation".localized, message: nil, preferredStyle: UIDevice.current.userInterfaceIdiom == .pad ? UIAlertController.Style.alert : UIAlertController.Style.actionSheet) + let style: UIAlertController.Style = UIDevice.current.userInterfaceIdiom == .pad ? .alert : .actionSheet + let alert = UIAlertController(title: "Set Style Transformation".localized, message: nil, preferredStyle: style) alert.addAction(getStyleTransformationAction(.off)) alert.addAction(getStyleTransformationAction(.styleTransformationPopular)) alert.addAction(getStyleTransformationAction(.styleTransformationRnb)) @@ -254,7 +316,8 @@ class VoiceChangerMain: BaseViewController { /// callback when voice changer button hit @IBAction func onRoomAcoustics() { - let alert = UIAlertController(title: "Set Room Acoustics".localized, message: nil, preferredStyle: UIDevice.current.userInterfaceIdiom == .pad ? UIAlertController.Style.alert : UIAlertController.Style.actionSheet) + let style: UIAlertController.Style = UIDevice.current.userInterfaceIdiom == .pad ? .alert : .actionSheet + let alert = UIAlertController(title: "Set Room Acoustics".localized, message: nil, preferredStyle: style) alert.addAction(getRoomAcousticsAction(.roomAcousticsKTV)) alert.addAction(getRoomAcousticsAction(.roomAcousVocalConcer)) alert.addAction(getRoomAcousticsAction(.roomAcousStudio)) @@ -269,14 +332,16 @@ class VoiceChangerMain: BaseViewController { /// callback when voice changer button hit @IBAction func onPitchCorrection() { - let alert = UIAlertController(title: "Set Pitch Correction".localized, message: nil, preferredStyle: UIDevice.current.userInterfaceIdiom == .pad ? UIAlertController.Style.alert : UIAlertController.Style.actionSheet) + let style: UIAlertController.Style = UIDevice.current.userInterfaceIdiom == .pad ? .alert : .actionSheet + let alert = UIAlertController(title: "Set Pitch Correction".localized, message: nil, preferredStyle: style) alert.addAction(getPitchCorrectionAction(.pitchCorrection)) alert.addCancelAction() present(alert, animated: true, completion: nil) } @IBAction func onVoiceConversion(_ sender: Any) { - let alert = UIAlertController(title: "Set Voice Conversion".localized, message: nil, preferredStyle: UIDevice.current.userInterfaceIdiom == .pad ? UIAlertController.Style.alert : UIAlertController.Style.actionSheet) + let style: UIAlertController.Style = UIDevice.current.userInterfaceIdiom == .pad ? .alert : .actionSheet + let alert = UIAlertController(title: "Set Voice Conversion".localized, message: nil, preferredStyle: style) alert.addAction(getVoiceConversionAction(.off)) alert.addAction(getVoiceConversionAction(.neutral)) alert.addAction(getVoiceConversionAction(.sweet)) @@ -293,16 +358,14 @@ class VoiceChangerMain: BaseViewController { agoraKit.setAudioEffectParameters(currentAudioEffects, param1: param1, param2: param2) } - @IBAction func onLocalVoicePitch(_ sender:UISlider) { + @IBAction func onLocalVoicePitch(_ sender: UISlider) { LogUtils.log(message: "onLocalVoicePitch \(Double(sender.value))", level: .info) agoraKit.setLocalVoicePitch(Double(sender.value)) } - @IBAction func onVoiceFormantChange(_ sender: UISlider) { - agoraKit.setLocalVoiceFormant(Double(sender.value)) - } - @IBAction func onLocalVoiceEqualizaitonFreq(_ sender:UIButton) { - let alert = UIAlertController(title: "Set Band Frequency".localized, message: nil, preferredStyle: UIDevice.current.userInterfaceIdiom == .pad ? UIAlertController.Style.alert : UIAlertController.Style.actionSheet) + @IBAction func onLocalVoiceEqualizaitonFreq(_ sender: UIButton) { + let style: UIAlertController.Style = UIDevice.current.userInterfaceIdiom == .pad ? .alert : .actionSheet + let alert = UIAlertController(title: "Set Band Frequency".localized, message: nil, preferredStyle: style) alert.addAction(getEqualizationFreqAction(.band31)) alert.addAction(getEqualizationFreqAction(.band62)) alert.addAction(getEqualizationFreqAction(.band125)) @@ -317,35 +380,35 @@ class VoiceChangerMain: BaseViewController { present(alert, animated: true, completion: nil) } - @IBAction func onLocalVoiceEqualizationGain(_ sender:UISlider) { + @IBAction func onLocalVoiceEqualizationGain(_ sender: UISlider) { equalizationGain = Int(sender.value) LogUtils.log(message: "onLocalVoiceEqualizationGain \(equalizationFreq.description()) \(equalizationGain)", level: .info) agoraKit.setLocalVoiceEqualizationOf(equalizationFreq, withGain: equalizationGain) } - func updateReverbValueRange(reverbKey:AgoraAudioReverbType) { - var min:Float = 0, max:Float = 0 + func updateReverbValueRange(reverbKey: AgoraAudioReverbType) { + var min: Float = 0, max: Float = 0 switch reverbKey { case .dryLevel: min = -20 max = 10 - break + case .wetLevel: min = -20 max = 10 - break + case .roomSize: min = 0 max = 100 - break + case .wetDelay: min = 0 max = 200 - break + case .strength: min = 0 max = 100 - break + default: break } reverbValueSlider.minimumValue = min @@ -353,8 +416,18 @@ class VoiceChangerMain: BaseViewController { reverbValueSlider.value = Float(reverbMap[reverbType] ?? 0) } - @IBAction func onLocalVoiceReverbKey(_ sender:UIButton) { - let alert = UIAlertController(title: "Set Reverb Key".localized, message: nil, preferredStyle: UIDevice.current.userInterfaceIdiom == .pad ? UIAlertController.Style.alert : UIAlertController.Style.actionSheet) + func updateVoiceAiTuner(type: AgoraVoiceAITunerType?) { + LogUtils.log(message: "onVoiceAITunerAction: \(type?.rawValue ?? -1)", level: .info) + if let type = type { + agoraKit.enableVoiceAITuner(true, type: type) + } else { + agoraKit.enableVoiceAITuner(false, type: .matureMale) + } + } + + @IBAction func onLocalVoiceReverbKey(_ sender: UIButton) { + let style: UIAlertController.Style = UIDevice.current.userInterfaceIdiom == .pad ? .alert : .actionSheet + let alert = UIAlertController(title: "Set Reverb Key".localized, message: nil, preferredStyle: style) alert.addAction(getReverbKeyAction(.dryLevel)) alert.addAction(getReverbKeyAction(.wetLevel)) alert.addAction(getReverbKeyAction(.roomSize)) @@ -364,20 +437,42 @@ class VoiceChangerMain: BaseViewController { present(alert, animated: true, completion: nil) } - @IBAction func onLocalVoiceReverbValue(_ sender:UISlider) { + @IBAction func onLocalVoiceReverbValue(_ sender: UISlider) { let value = Int(sender.value) reverbMap[reverbType] = value LogUtils.log(message: "onLocalVoiceReverbValue \(reverbType.description()) \(value)", level: .info) agoraKit.setLocalVoiceReverbOf(reverbType, withValue: value) } - override func viewDidLoad(){ + @IBAction func onVoiceAITunerAction(_ sender: UIButton) { + let style: UIAlertController.Style = UIDevice.current.userInterfaceIdiom == .pad ? .alert : .actionSheet + let alert = UIAlertController(title: "Set_Voice_AI_Tuner".localized, message: nil, preferredStyle: style) + alert.addAction(getVoiceAITunerAction(nil)) + alert.addAction(getVoiceAITunerAction(.matureMale)) + alert.addAction(getVoiceAITunerAction(.freshMale)) + alert.addAction(getVoiceAITunerAction(.elegantFemale)) + alert.addAction(getVoiceAITunerAction(.sweetFemale)) + alert.addAction(getVoiceAITunerAction(.warmMaleSinging)) + alert.addAction(getVoiceAITunerAction(.gentleFemaleSinging)) + alert.addAction(getVoiceAITunerAction(.huskyMaleSinging)) + alert.addAction(getVoiceAITunerAction(.warmElegantFemaleSinging)) + alert.addAction(getVoiceAITunerAction(.powerfulMaleSinging)) + alert.addAction(getVoiceAITunerAction(.dreamyFemaleSinging)) + alert.addCancelAction() + present(alert, animated: true, completion: nil) + } + + @IBAction func onVoiceFormantChange(_ sender: UISlider) { + agoraKit.setLocalVoiceFormant(Double(sender.value)) + } + + override func viewDidLoad() { super.viewDidLoad() // set up agora instance when view loadedlet config = AgoraRtcEngineConfig() let config = AgoraRtcEngineConfig() config.appId = KeyCenter.AppId -// config.areaCode = GlobalSettings.shared.area.rawValue + // config.areaCode = GlobalSettings.shared.area.rawValue // setup log file path let logConfig = AgoraLogConfig() logConfig.filePath = LogUtils.sdkLogPath() @@ -386,6 +481,7 @@ class VoiceChangerMain: BaseViewController { agoraKit = AgoraRtcEngineKit.sharedEngine(with: config, delegate: self) // Configuring Privatization Parameters Util.configPrivatization(agoraKit: agoraKit) + guard let channelName = configs["channelName"] as? String else {return} self.title = channelName @@ -394,6 +490,8 @@ class VoiceChangerMain: BaseViewController { equalizationFreqBtn.setTitle("\(equalizationFreq.description())", for: .normal) reverbKeyBtn.setTitle("\(reverbType.description())", for: .normal) + voiceAiTunerBtn.setTitle(aitunerType?.description() ?? "Off", for: .normal) + // Before calling the method, you need to set the profile // parameter of setAudioProfile to AUDIO_PROFILE_MUSIC_HIGH_QUALITY(4) // or AUDIO_PROFILE_MUSIC_HIGH_QUALITY_STEREO(5), and to set @@ -419,11 +517,14 @@ class VoiceChangerMain: BaseViewController { // when joining channel. The channel name and uid used to calculate // the token has to match the ones used for channel join NetworkManager.shared.generateToken(channelName: channelName, success: { token in - let result = self.agoraKit.joinChannel(byToken: token, channelId: channelName, info: nil, uid: 0) {[unowned self] (channel, uid, elapsed) -> Void in + let result = self.agoraKit.joinChannel(byToken: token, + channelId: channelName, + info: nil, + uid: 0) {[unowned self] (channel, uid, elapsed) -> Void in self.isJoined = true LogUtils.log(message: "Join \(channel) with uid \(uid) elapsed \(elapsed)ms", level: .info) - - //set up local audio view, this view will not show video but just a placeholder + + // set up local audio view, this view will not show video but just a placeholder let view = Bundle.loadView(fromNib: "VideoView", withType: VideoView.self) self.audioViews[uid] = view view.setPlaceholder(text: self.getAudioLabel(uid: uid, isLocal: true)) @@ -484,7 +585,7 @@ extension VoiceChangerMain: AgoraRtcEngineDelegate { isJoined = true LogUtils.log(message: "Join \(channel) with uid \(uid) elapsed \(elapsed)ms", level: .info) - //set up local audio view, this view will not show video but just a placeholder + // set up local audio view, this view will not show video but just a placeholder let view = Bundle.loadView(fromNib: "VideoView", withType: VideoView.self) audioViews[uid] = view view.setPlaceholder(text: self.getAudioLabel(uid: uid, isLocal: true)) @@ -497,7 +598,7 @@ extension VoiceChangerMain: AgoraRtcEngineDelegate { func rtcEngine(_ engine: AgoraRtcEngineKit, didJoinedOfUid uid: UInt, elapsed: Int) { LogUtils.log(message: "remote user join: \(uid) \(elapsed)ms", level: .info) - //set up remote audio view, this view will not show video but just a placeholder + // set up remote audio view, this view will not show video but just a placeholder let view = Bundle.loadView(fromNib: "VideoView", withType: VideoView.self) self.audioViews[uid] = view view.setPlaceholder(text: self.getAudioLabel(uid: uid, isLocal: false)) @@ -512,7 +613,7 @@ extension VoiceChangerMain: AgoraRtcEngineDelegate { func rtcEngine(_ engine: AgoraRtcEngineKit, didOfflineOfUid uid: UInt, reason: AgoraUserOfflineReason) { LogUtils.log(message: "remote user left: \(uid) reason \(reason)", level: .info) - //remove remote audio view + // remove remote audio view self.audioViews.removeValue(forKey: uid) self.container.layoutStream2x1(views: Array(self.audioViews.values)) self.container.reload(level: 0, animated: true) diff --git a/iOS/APIExample-Audio/APIExample-Audio/zh-Hans.lproj/Localizable.strings b/iOS/APIExample-Audio/APIExample-Audio/zh-Hans.lproj/Localizable.strings index e0eb6d90c..d67b8fdc0 100644 --- a/iOS/APIExample-Audio/APIExample-Audio/zh-Hans.lproj/Localizable.strings +++ b/iOS/APIExample-Audio/APIExample-Audio/zh-Hans.lproj/Localizable.strings @@ -132,3 +132,20 @@ "please input Token!" = "请输入Token"; "Quick input APPID and Token methods" = "快捷输入APPID和Token方法"; "I: the mobile phone and Mac log in to the same Apple account. After copying the Mac, it will automatically synchronize other terminals with the same account. The mobile phone can directly click the input box to paste.\n\n II: use https://cl1p.net/ online clipboard:\n\n1.Enter in a URL that starts with cl1p.net. Example cl1p.net/uqztgjnqcalmd\n\n2.Paste in anything you want.\n\n3.On another computer enter the same URL and get your stuff." = "I: 如果手机和Mac登录的同一个Apple账号, Mac复制后会自动同步到其它相同账号终端, 手机可直接点击输入框粘贴即可.\n\n II: 使用https://cl1p.net/在线剪切板:\n\n1.输入以cl1p.net开头的URL。示例cl1p.net/uqztgjnqcalmd。\n\n2.粘贴任何你想要的东西。\n\n3.在另一台计算机上,输入相同的URL并获取您的东西。"; + + +"AGGRESSIVE" = "强降噪模式"; +"BALANCED" = "(默认)均衡降噪模式"; +"ULTRALOWLATENCY" = "低延时强降噪模式"; + +"Set_Voice_AI_Tuner"="设置AI调音器"; +"AI_Tunner_Mature_Male"="大叔声"; +"AI_Tunner_Fresh_Male"="清新男音"; +"AI_Tunner_Elegant_Female"="御姐音"; +"AI_Tunner_Sweet_Female"="萝莉音"; +"AI_Tunner_Warm_Male_Singing"="暖男歌声"; +"AI_Tunner_Gentle_Female_Singing"="温柔女歌声"; +"AI_Tunner_Husky_Male_Singing"="烟嗓叔音歌声"; +"AI_Tunner_Warm_Elegant_Female_Singing"="温暖御姐歌声"; +"AI_Tunner_Powerful_Male_Singing"="力量男歌声"; +"AI_Tunner_Dreamy_Female_Singing"="梦幻女歌声"; diff --git a/iOS/APIExample-Audio/Podfile b/iOS/APIExample-Audio/Podfile index 05b9e8b2f..a011aa68c 100644 --- a/iOS/APIExample-Audio/Podfile +++ b/iOS/APIExample-Audio/Podfile @@ -7,10 +7,10 @@ target 'APIExample-Audio' do pod 'Floaty', '~> 4.2.0' pod 'AGEVideoLayout', '~> 1.0.2' - pod 'AgoraAudio_iOS', '4.3.1' - # pod 'sdk', :path => 'sdk.podspec' + pod 'AgoraAudio_iOS', '4.4.0' +# pod 'sdk', :path => 'sdk.podspec' end pre_install do |installer| - # system("sh .download_script.sh 4.3.1 true") + # system("sh .download_script.sh 4.3.2 true") end diff --git a/iOS/APIExample-Audio/README.md b/iOS/APIExample-Audio/README.md index 22b9d207f..acefe6e3c 100644 --- a/iOS/APIExample-Audio/README.md +++ b/iOS/APIExample-Audio/README.md @@ -62,8 +62,8 @@ To build and run the sample application, get an App Id: Agora provides App certificate to generate Token. You can deploy and generate a token on your server, or use the console to generate a temporary token. In order to get the APP ID, you can open the agora console (https://console.agora.io/) to create a project with the App Certificate enabled, - then the APP Certificate can be found in the project detail page. - PS: If the project does not have certificates enabled, leave this field blank. + then the APP Certificate can be found in the project detail page.If the project does not have certificates enabled, leave this field blank. + PS: It is unsafe to place the App Certificate on the client side, it is recommended to place it on the server side to ensure that the App Certificate is not leaked. */ static var Certificate: String? = <#YOUR Certificate#> diff --git a/iOS/APIExample-Audio/README.zh.md b/iOS/APIExample-Audio/README.zh.md index c941622df..8552a1b05 100644 --- a/iOS/APIExample-Audio/README.zh.md +++ b/iOS/APIExample-Audio/README.zh.md @@ -56,8 +56,8 @@ pod install /** Agora 提供 App certificate 用以生成 Token。您可以在您的服务器部署并生成 Token,或者使用控制台生成临时的 Token。 - 进入声网控制台(https://console.agora.io/),创建一个带证书鉴权的项目,进入项目配置页,即可看到APP证书。 - 注意:如果项目没有开启证书鉴权,这个字段留空。 + 进入声网控制台(https://console.agora.io/),创建一个带证书鉴权的项目,进入项目配置页,即可看到APP证书。如果项目没有开启证书鉴权,这个字段留空。 + 注意:App证书放在客户端不安全,推荐放在服务端以确保 App 证书不会泄露。 */ static var Certificate: String? = <#YOUR Certificate#> ``` diff --git a/iOS/APIExample-Audio/cloud_build.sh b/iOS/APIExample-Audio/cloud_build.sh new file mode 100755 index 000000000..891825139 --- /dev/null +++ b/iOS/APIExample-Audio/cloud_build.sh @@ -0,0 +1,95 @@ +#!/usr/bin/env sh + +PROJECT_PATH=$PWD + +if [ "$WORKSPACE" = "" ]; then + WORKSPACE=$PWD +fi +if [ "$BUILD_NUMBER" = "" ]; then + BUILD_NUMBER=888 +fi + + +cd ${PROJECT_PATH} && pod install || exit 1 + +# 打包环境 +CONFIGURATION="Debug" + +#工程文件路径 +APP_PATH="$(ls | grep xcworkspace)" + +# 项目target名 +TARGET_NAME=${APP_PATH%%.*} + +KEYCENTER_PATH=$TARGET_NAME/Common/KeyCenter.swift + +#工程配置路径 +PBXPROJ_PATH=${TARGET_NAME}.xcodeproj/project.pbxproj + +# Debug +/usr/libexec/PlistBuddy -c "Set :objects:03D13BF72448758C00B599B3:buildSettings:CODE_SIGN_STYLE 'Manual'" $PBXPROJ_PATH +/usr/libexec/PlistBuddy -c "Set :objects:03D13BF72448758C00B599B3:buildSettings:DEVELOPMENT_TEAM 'GM72UGLGZW'" $PBXPROJ_PATH +/usr/libexec/PlistBuddy -c "Set :objects:03D13BF72448758C00B599B3:buildSettings:PROVISIONING_PROFILE_SPECIFIER 'App'" $PBXPROJ_PATH +# Release +/usr/libexec/PlistBuddy -c "Set :objects:03D13BF82448758C00B599B3:buildSettings:CODE_SIGN_STYLE 'Manual'" $PBXPROJ_PATH +/usr/libexec/PlistBuddy -c "Set :objects:03D13BF82448758C00B599B3:buildSettings:DEVELOPMENT_TEAM 'GM72UGLGZW'" $PBXPROJ_PATH +/usr/libexec/PlistBuddy -c "Set :objects:03D13BF82448758C00B599B3:buildSettings:PROVISIONING_PROFILE_SPECIFIER 'App'" $PBXPROJ_PATH + +#修改build number +# Debug +/usr/libexec/PlistBuddy -c "Set :objects:03D13BF72448758C00B599B3:buildSettings:CURRENT_PROJECT_VERSION ${BUILD_NUMBER}" $PBXPROJ_PATH +# Release +/usr/libexec/PlistBuddy -c "Set :objects:03D13BF82448758C00B599B3:buildSettings:CURRENT_PROJECT_VERSION ${BUILD_NUMBER}" $PBXPROJ_PATH + +# 读取APPID环境变量 +echo AGORA_APP_ID: $APP_ID + +echo PROJECT_PATH: $PROJECT_PATH +echo TARGET_NAME: $TARGET_NAME +echo KEYCENTER_PATH: $KEYCENTER_PATH +echo APP_PATH: $APP_PATH +å +#修改Keycenter文件 +sed -i -e "s#<\#YOUR AppId\#>#\"$APP_ID\"#g" $KEYCENTER_PATH +sed -i -e "s#<\#YOUR Certificate\#>#nil#g" $KEYCENTER_PATH +rm -f ${KEYCENTER_PATH}-e + +# Xcode clean +xcodebuild clean -workspace "${APP_PATH}" -configuration "${CONFIGURATION}" -scheme "${TARGET_NAME}" + +# 时间戳 +CURRENT_TIME=$(date "+%Y-%m-%d %H-%M-%S") + +# 归档路径 +ARCHIVE_PATH="${WORKSPACE}/${TARGET_NAME}_${BUILD_NUMBER}.xcarchive" + +# 编译环境 + +# plist路径 +PLIST_PATH="${PROJECT_PATH}/ExportOptions.plist" + +echo PLIST_PATH: $PLIST_PATH + +# archive 这边使用的工作区间 也可以使用project +xcodebuild CODE_SIGN_STYLE="Manual" archive -workspace "${APP_PATH}" -scheme "${TARGET_NAME}" clean CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO -configuration "${CONFIGURATION}" -archivePath "${ARCHIVE_PATH}" -destination 'generic/platform=iOS' -quiet || exit 1 + + +cd ${WORKSPACE} + +# 压缩archive +7za a -tzip "${TARGET_NAME}_${BUILD_NUMBER}.xcarchive.zip" "${ARCHIVE_PATH}" + + +# 签名 +# sh sign "${TARGET_NAME}_${BUILD_NUMBER}.xcarchive.zip" --type xcarchive --plist "${PLIST_PATH}" +sh export "${TARGET_NAME}_${BUILD_NUMBER}.xcarchive.zip" --plist "${PLIST_PATH}" + +SDK_VERSION=$(echo $sdk_url | cut -d "/" -f 5) +OUTPUT_FILE=${WORKSPACE}/${TARGET_NAME}_${BUILD_NUMBER}_${SDK_VERSION}_$(date "+%Y%m%d%H%M%S").ipa +mv ${TARGET_NAME}_${BUILD_NUMBER}.ipa $OUTPUT_FILE + +rm -rf *.xcarchive +rm -rf *.xcarchive.zip +echo OUTPUT_FILE: $OUTPUT_FILE + + diff --git a/iOS/APIExample-OC/APIExample-OC.xcodeproj/project.pbxproj b/iOS/APIExample-OC/APIExample-OC.xcodeproj/project.pbxproj index 464f75615..bf5e1d2d0 100644 --- a/iOS/APIExample-OC/APIExample-OC.xcodeproj/project.pbxproj +++ b/iOS/APIExample-OC/APIExample-OC.xcodeproj/project.pbxproj @@ -3,13 +3,14 @@ archiveVersion = 1; classes = { }; - objectVersion = 53; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ 749177BD2429C778B3C6D520 /* Pods_APIExample_OC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D931C433C0CD269F3C3323D /* Pods_APIExample_OC.framework */; }; 961E8DCA7B4A6B12564DDC18 /* Pods_SimpleFilter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6D0773F7A8BA9611CF846635 /* Pods_SimpleFilter.framework */; }; C2C764A5CC78FAC392BE5584 /* Pods_Agora_ScreenShare_Extension_OC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27A6720DD8507EB2C642C0FD /* Pods_Agora_ScreenShare_Extension_OC.framework */; }; + DD8A1F7F2CA50749001CEC51 /* AgoraPCMPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = DD8A1F7E2CA50749001CEC51 /* AgoraPCMPlayer.m */; }; E70ADDF12A5D004F009947CF /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = E70ADDF02A5D004F009947CF /* AppDelegate.m */; }; E70ADDF72A5D004F009947CF /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E70ADDF62A5D004F009947CF /* ViewController.m */; }; E70ADDFA2A5D004F009947CF /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E70ADDF82A5D004F009947CF /* Main.storyboard */; }; @@ -179,6 +180,8 @@ BBBCB16091148F518866D952 /* Pods-SimpleFilter.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SimpleFilter.release.xcconfig"; path = "Target Support Files/Pods-SimpleFilter/Pods-SimpleFilter.release.xcconfig"; sourceTree = ""; }; C4F0E8C20AFE260912A60198 /* Pods-Agora-ScreenShare-Extension-OC.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Agora-ScreenShare-Extension-OC.release.xcconfig"; path = "Target Support Files/Pods-Agora-ScreenShare-Extension-OC/Pods-Agora-ScreenShare-Extension-OC.release.xcconfig"; sourceTree = ""; }; D5882D286343E06CF635A5E6 /* 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 = ""; }; + DD8A1F7D2CA50749001CEC51 /* AgoraPCMPlayer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AgoraPCMPlayer.h; sourceTree = ""; }; + DD8A1F7E2CA50749001CEC51 /* AgoraPCMPlayer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AgoraPCMPlayer.m; sourceTree = ""; }; E22416FC36DE1212EE1E1760 /* Pods-Agora-ScrrenShare-Extension-OC.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Agora-ScrrenShare-Extension-OC.release.xcconfig"; path = "Target Support Files/Pods-Agora-ScrrenShare-Extension-OC/Pods-Agora-ScrrenShare-Extension-OC.release.xcconfig"; sourceTree = ""; }; E70ADDEC2A5D004F009947CF /* APIExample-OC.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "APIExample-OC.app"; sourceTree = BUILT_PRODUCTS_DIR; }; E70ADDEF2A5D004F009947CF /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; @@ -677,6 +680,8 @@ E70ADEE42A6A71FE009947CF /* ExternalAudio.mm */, E70ADEDB2A6A2D7D009947CF /* AgoraPCMSourcePush.h */, E70ADEDC2A6A2D7D009947CF /* AgoraPCMSourcePush.m */, + DD8A1F7D2CA50749001CEC51 /* AgoraPCMPlayer.h */, + DD8A1F7E2CA50749001CEC51 /* AgoraPCMPlayer.m */, ); path = ExternalAudio; sourceTree = ""; @@ -1283,6 +1288,7 @@ E70ADEF42A6A72C6009947CF /* AudioWriteToFile.m in Sources */, E72F62002A7796A200C963D2 /* SpatialAudioActionSheet.swift in Sources */, E72F61A52A710AB200C963D2 /* MediaPlayer.m in Sources */, + DD8A1F7F2CA50749001CEC51 /* AgoraPCMPlayer.m in Sources */, E7361F6B2A6E6B9F00925BD6 /* SimpleFilter.m in Sources */, E72F618D2A70F8AF00C963D2 /* AgoraCustomEncryption.mm in Sources */, E72F62042A77A63D00C963D2 /* ContentInspect.m in Sources */, @@ -1589,7 +1595,7 @@ PROVISIONING_PROFILE_SPECIFIER = ""; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OBJC_BRIDGING_HEADER = "APIExample-OC/APIExample-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -1630,7 +1636,7 @@ PROVISIONING_PROFILE_SPECIFIER = ""; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OBJC_BRIDGING_HEADER = "APIExample-OC/APIExample-Bridging-Header.h"; SWIFT_VERSION = 5.0; diff --git a/iOS/APIExample-OC/APIExample-OC/Common/BaseViewController.h b/iOS/APIExample-OC/APIExample-OC/Common/BaseViewController.h index cccb4461c..9d7aa7f6c 100644 --- a/iOS/APIExample-OC/APIExample-OC/Common/BaseViewController.h +++ b/iOS/APIExample-OC/APIExample-OC/Common/BaseViewController.h @@ -22,6 +22,7 @@ NS_ASSUME_NONNULL_BEGIN message: (NSString *)message textAlignment: (NSTextAlignment)alignment; +- (void)presentAlertViewController:(UIAlertController*)alertVC; @end // 布局模块 diff --git a/iOS/APIExample-OC/APIExample-OC/Common/BaseViewController.m b/iOS/APIExample-OC/APIExample-OC/Common/BaseViewController.m index a09e39cdd..459e40483 100644 --- a/iOS/APIExample-OC/APIExample-OC/Common/BaseViewController.m +++ b/iOS/APIExample-OC/APIExample-OC/Common/BaseViewController.m @@ -41,6 +41,17 @@ - (void)showAlertWithTitle: (NSString *)title [self presentViewController:alertVC animated:YES completion:nil]; } +- (void)presentAlertViewController:(UIAlertController*)alertVC { + + // 判断设备类型 + if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { + // iPad 上需要提供位置信息 + alertVC.popoverPresentationController.sourceView = self.view; // 设置源视图 + alertVC.popoverPresentationController.sourceRect = CGRectMake(self.view.bounds.size.width / 2.0, self.view.bounds.size.height / 2.0, 1.0, 1.0); // 设置源矩形 + } + [self presentViewController:alertVC animated:YES completion:nil]; +} + @end diff --git a/iOS/APIExample-OC/APIExample-OC/Common/ExternalAudio/AgoraPCMPlayer.h b/iOS/APIExample-OC/APIExample-OC/Common/ExternalAudio/AgoraPCMPlayer.h new file mode 100644 index 000000000..b11f3f1aa --- /dev/null +++ b/iOS/APIExample-OC/APIExample-OC/Common/ExternalAudio/AgoraPCMPlayer.h @@ -0,0 +1,24 @@ +// +// AgoraPCMPlayer.h +// APIExample-OC +// +// Created by wushengtao on 2024/9/26. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface AgoraPCMPlayer : NSObject + +@property (nonatomic, strong) AVAudioEngine *audioEngine; +@property (nonatomic, strong) AVAudioPlayerNode *playerNode; +@property (nonatomic) double sampleRate; +@property (nonatomic) AVAudioChannelCount channels; + +- (instancetype)initWithSampleRate:(double)sampleRate channels:(AVAudioChannelCount)channels; +- (void)playPCMData:(UInt8 *)pcmData count:(NSUInteger)count; + +@end + +NS_ASSUME_NONNULL_END diff --git a/iOS/APIExample-OC/APIExample-OC/Common/ExternalAudio/AgoraPCMPlayer.m b/iOS/APIExample-OC/APIExample-OC/Common/ExternalAudio/AgoraPCMPlayer.m new file mode 100644 index 000000000..67b3e22e4 --- /dev/null +++ b/iOS/APIExample-OC/APIExample-OC/Common/ExternalAudio/AgoraPCMPlayer.m @@ -0,0 +1,58 @@ +// +// AgoraPCMPlayer.m +// APIExample-OC +// +// Created by wushengtao on 2024/9/26. +// + +#import "AgoraPCMPlayer.h" +@implementation AgoraPCMPlayer + +- (instancetype)initWithSampleRate:(double)sampleRate channels:(AVAudioChannelCount)channels { + self = [super init]; + if (self) { + _sampleRate = sampleRate; + _channels = channels; + + _audioEngine = [[AVAudioEngine alloc] init]; + _playerNode = [[AVAudioPlayerNode alloc] init]; + + [_audioEngine attachNode:_playerNode]; + + // 使用 AVAudioFormat 的 init 方法 + AVAudioFormat *format = [[AVAudioFormat alloc] initStandardFormatWithSampleRate:sampleRate channels:channels]; + [_audioEngine connect:_playerNode to:_audioEngine.mainMixerNode format:format]; + + NSError *error = nil; + // 使用 start 方法的无参数版本 + [_audioEngine startAndReturnError:&error]; + if (error) { + NSLog(@"Audio Engine failed to start: %@", error); + } + } + return self; +} + +- (void)playPCMData:(UInt8 *)pcmData count:(NSUInteger)count { + AVAudioFormat *format = [[AVAudioFormat alloc] initStandardFormatWithSampleRate:self.sampleRate channels:self.channels]; + AVAudioPCMBuffer *audioBuffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:format frameCapacity:(AVAudioFrameCount)(count / 4)]; // 16-bit stereo, 4 bytes per frame + + if (!audioBuffer.floatChannelData) { + return; + } + + audioBuffer.frameLength = (AVAudioFrameCount)(count / 4); + + for (NSUInteger frame = 0; frame < audioBuffer.frameLength; frame++) { + int16_t leftSample = pcmData[frame * 4] | (pcmData[frame * 4 + 1] << 8); + int16_t rightSample = pcmData[frame * 4 + 2] | (pcmData[frame * 4 + 3] << 8); + + audioBuffer.floatChannelData[0][frame] = (float)leftSample / INT16_MAX; + audioBuffer.floatChannelData[1][frame] = (float)rightSample / INT16_MAX; + } + + [_playerNode scheduleBuffer:audioBuffer completionHandler:nil]; + [_playerNode play]; +} + +@end diff --git a/iOS/APIExample-OC/APIExample-OC/Common/KeyCenter.h b/iOS/APIExample-OC/APIExample-OC/Common/KeyCenter.h index 5706e82ee..d7cd02c87 100644 --- a/iOS/APIExample-OC/APIExample-OC/Common/KeyCenter.h +++ b/iOS/APIExample-OC/APIExample-OC/Common/KeyCenter.h @@ -31,12 +31,12 @@ NS_ASSUME_NONNULL_BEGIN Agora provides App certificate to generate Token. You can deploy and generate a token on your server, or use the console to generate a temporary token. In order to get the APP ID, you can open the agora console (https://console.agora.io/) to create a project with the App Certificate enabled, - then the APP Certificate can be found in the project detail page. - PS: If the project does not have certificates enabled, leave this field blank. + then the APP Certificate can be found in the project detail page.If the project does not have certificates enabled, leave this field blank. + PS: It is unsafe to place the App Certificate on the client side, it is recommended to place it on the server side to ensure that the App Certificate is not leaked. 声网APP证书 Agora 提供 App certificate 用以生成 Token。您可以在您的服务器部署并生成 Token,或者使用控制台生成临时的 Token。 - 进入声网控制台(https://console.agora.io/),创建一个带证书鉴权的项目,进入项目配置页,即可看到APP证书。 - 注意:如果项目没有开启证书鉴权,这个字段留空。 + 进入声网控制台(https://console.agora.io/),创建一个带证书鉴权的项目,进入项目配置页,即可看到APP证书。如果项目没有开启证书鉴权,这个字段留空。 + 注意:App证书放在客户端不安全,推荐放在服务端以确保 App 证书不会泄露。 */ + (nullable NSString *)Certificate; diff --git a/iOS/APIExample-OC/APIExample-OC/Common/KeyCenter.m b/iOS/APIExample-OC/APIExample-OC/Common/KeyCenter.m index 09d0f77bc..0fbfa9003 100644 --- a/iOS/APIExample-OC/APIExample-OC/Common/KeyCenter.m +++ b/iOS/APIExample-OC/APIExample-OC/Common/KeyCenter.m @@ -7,7 +7,7 @@ #import "KeyCenter.h" -static NSString * const APPID = <#YOUR APPID#> +static NSString * const APPID = <#YOUR APPID#>; static NSString * const Certificate = nil; @implementation KeyCenter diff --git a/iOS/APIExample-OC/APIExample-OC/Common/NetworkManager/NetworkManager.swift b/iOS/APIExample-OC/APIExample-OC/Common/NetworkManager/NetworkManager.swift index 481591d90..53c0a837d 100644 --- a/iOS/APIExample-OC/APIExample-OC/Common/NetworkManager/NetworkManager.swift +++ b/iOS/APIExample-OC/APIExample-OC/Common/NetworkManager/NetworkManager.swift @@ -32,7 +32,7 @@ class NetworkManager: NSObject { @objc static let shared = NetworkManager() private override init() { } - private let baseUrl = "https://test-toolbox.bj2.agoralab.co/v1/token/generate" + private let baseUrl = "https://service.agora.io/toolbox-global/v1/token/generate" @objc func generateToken(channelName: String, uid: UInt = 0, success: @escaping (String?) -> Void) { diff --git a/iOS/APIExample-OC/APIExample-OC/Examples/Advanced/AudioMixing/AudioMixing.m b/iOS/APIExample-OC/APIExample-OC/Examples/Advanced/AudioMixing/AudioMixing.m index b0f220ab2..0e3f0b754 100644 --- a/iOS/APIExample-OC/APIExample-OC/Examples/Advanced/AudioMixing/AudioMixing.m +++ b/iOS/APIExample-OC/APIExample-OC/Examples/Advanced/AudioMixing/AudioMixing.m @@ -49,7 +49,8 @@ - (IBAction)setAudioScenario:(UIButton *)sender { } UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"Cancel".localized style:(UIAlertActionStyleCancel) handler:nil]; [alertVC addAction:cancel]; - [self presentViewController:alertVC animated:YES completion:nil]; +// [self presentViewController:alertVC animated:YES completion:nil]; + [self presentAlertViewController:alertVC]; } - (IBAction)setAudioProfile:(UIButton *)sender { UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:@"Set Audio Profile".localized message:nil preferredStyle:(UIAlertControllerStyleActionSheet)]; @@ -73,7 +74,8 @@ - (IBAction)setAudioProfile:(UIButton *)sender { } UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"Cancel".localized style:(UIAlertActionStyleCancel) handler:nil]; [alertVC addAction:cancel]; - [self presentViewController:alertVC animated:YES completion:nil]; +// [self presentViewController:alertVC animated:YES completion:nil]; + [self presentAlertViewController:alertVC]; } - (IBAction)onClickJoinButton:(id)sender { diff --git a/iOS/APIExample-OC/APIExample-OC/Examples/Advanced/CustomAudioRender/CustomAudioRender.m b/iOS/APIExample-OC/APIExample-OC/Examples/Advanced/CustomAudioRender/CustomAudioRender.m index e25523865..85989534d 100644 --- a/iOS/APIExample-OC/APIExample-OC/Examples/Advanced/CustomAudioRender/CustomAudioRender.m +++ b/iOS/APIExample-OC/APIExample-OC/Examples/Advanced/CustomAudioRender/CustomAudioRender.m @@ -10,7 +10,13 @@ #import #import "VideoView.h" #import "APIExample_OC-swift.h" -#import "ExternalAudio.h" +#import "AgoraPCMPlayer.h" + + +const NSUInteger bitsPerSample = 16; +const NSUInteger samples = 441 * 10; +const NSUInteger sampleRate = 44100; +const NSUInteger channels = 2; @interface CustomAudioRenderEntry () @property (weak, nonatomic) IBOutlet UITextField *textField; @@ -40,17 +46,19 @@ @interface CustomAudioRender () @property (nonatomic, strong)VideoView *localView; @property (nonatomic, strong)VideoView *remoteView; @property (nonatomic, strong)AgoraRtcEngineKit *agoraKit; -@property (nonatomic, strong)ExternalAudio *exAudio; +@property (nonatomic, strong)AgoraPCMPlayer *player; +@property (assign, atomic) BOOL isJoined; @end @implementation CustomAudioRender -- (ExternalAudio *)exAudio { - if (_exAudio == nil) { - _exAudio = [ExternalAudio sharedExternalAudio]; +- (AgoraPCMPlayer*)player { + if (_player == nil) { + _player = [[AgoraPCMPlayer alloc] initWithSampleRate:sampleRate channels:channels]; } - return _exAudio; + + return _player; } - (VideoView *)localView { @@ -84,22 +92,9 @@ - (void)viewDidLoad { self.agoraKit = [AgoraRtcEngineKit sharedEngineWithConfig:config delegate:self]; NSString *channelName = [self.configs objectForKey:@"channelName"]; - // make myself a broadcaster - [self.agoraKit setClientRole:(AgoraClientRoleBroadcaster)]; // enable video module and set up video encoding configs [self.agoraKit enableAudio]; - AgoraVideoEncoderConfiguration *encoderConfig = [[AgoraVideoEncoderConfiguration alloc] initWithSize:CGSizeMake(960, 540) - frameRate:(AgoraVideoFrameRateFps15) - bitrate:15 - orientationMode:(AgoraVideoOutputOrientationModeFixedPortrait) - mirrorMode:(AgoraVideoMirrorModeAuto)]; - [self.agoraKit setVideoEncoderConfiguration:encoderConfig]; - - [self.exAudio setupExternalAudioWithAgoraKit:self.agoraKit sampleRate:44100 channels:1 audioCRMode:(AudioCRModeExterCaptureExterRender) IOType:(IOUnitTypeRemoteIO)]; - [self.agoraKit setParameters:@"{\"che.audio.external_render\": true}"]; - [self.agoraKit setParameters:@"{\"che.audio.keep.audiosession\": true}"]; - // set up local video to render your local camera preview AgoraRtcVideoCanvas *videoCanvas = [[AgoraRtcVideoCanvas alloc] init]; videoCanvas.uid = 0; @@ -118,12 +113,14 @@ - (void)viewDidLoad { // when joining channel. The channel name and uid used to calculate // the token has to match the ones used for channel join AgoraRtcChannelMediaOptions *options = [[AgoraRtcChannelMediaOptions alloc] init]; + options.publishMicrophoneTrack = NO; + options.publishCameraTrack = NO; options.autoSubscribeAudio = YES; options.autoSubscribeVideo = NO; - options.publishMicrophoneTrack = YES; - options.publishCameraTrack = NO; options.clientRoleType = AgoraClientRoleBroadcaster; + [self.agoraKit enableExternalAudioSink:YES sampleRate:sampleRate channels:channels]; + [[NetworkManager shared] generateTokenWithChannelName:channelName uid:0 success:^(NSString * _Nullable token) { int result = [self.agoraKit joinChannelByToken:token channelId:channelName uid:0 mediaOptions:options joinSuccess:nil]; if (result != 0) { @@ -140,7 +137,36 @@ - (void)viewDidDisappear:(BOOL)animated { [super viewDidDisappear:animated]; [self.agoraKit disableAudio]; [self.agoraKit leaveChannel:nil]; + [self.agoraKit enableExternalAudioSink:NO sampleRate:sampleRate channels:channels]; [AgoraRtcEngineKit destroy]; + self.isJoined = NO; +} + +- (void)startPullAudio { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + NSTimeInterval pullMs = 100; + NSUInteger lengthInByte = (sampleRate / 1000) * 2 * channels * (NSUInteger)pullMs; + + UInt8 *pointer = (UInt8 *)malloc(lengthInByte); + NSTimeInterval deltaMs = 0; + + while (self.isJoined) { + NSDate *date = [NSDate date]; + memset(pointer, 0, lengthInByte); + + NSInteger ret = [self.agoraKit pullPlaybackAudioFrameRawData:pointer lengthInByte:lengthInByte]; + [self.player playPCMData:pointer count:lengthInByte]; + + NSTimeInterval cost = -[date timeIntervalSinceNow] * 1000; + usleep((useconds_t)MAX((pullMs - cost - deltaMs) * 1000, 0)); + + // Add deltaMs to ensure that the thread processing time is less than pullMs + deltaMs = MAX((- [date timeIntervalSinceNow] * 1000) - pullMs, 0) * 2; + // NSLog(@"pullPlaybackAudioFrameRawData: %ld lengthInByte: %lu cost: %f ms, deltaMs: %f ms", ret, (unsigned long)lengthInByte, -[date timeIntervalSinceNow] * 1000, deltaMs); + } + + free(pointer); + }); } /// callback when error occured for agora sdk, you are recommended to display the error descriptions on demand @@ -156,6 +182,8 @@ - (void)rtcEngine:(AgoraRtcEngineKit *)engine didOccurError:(AgoraErrorCode)erro - (void)rtcEngine:(AgoraRtcEngineKit *)engine didJoinChannel:(NSString *)channel withUid:(NSUInteger)uid elapsed:(NSInteger)elapsed { [LogUtil log:[NSString stringWithFormat:@"Join %@ with uid %lu elapsed %ldms", channel, uid, elapsed] level:(LogLevelDebug)]; self.localView.uid = uid; + [self startPullAudio]; + self.isJoined = YES; } /// callback when a remote user is joinning the channel, note audience in live broadcast mode will NOT trigger this event diff --git a/iOS/APIExample-OC/APIExample-OC/Examples/Advanced/CustomPcmAudioSource/CustomPcmAudioSource.m b/iOS/APIExample-OC/APIExample-OC/Examples/Advanced/CustomPcmAudioSource/CustomPcmAudioSource.m index fc5f76fe2..fdd8d4141 100644 --- a/iOS/APIExample-OC/APIExample-OC/Examples/Advanced/CustomPcmAudioSource/CustomPcmAudioSource.m +++ b/iOS/APIExample-OC/APIExample-OC/Examples/Advanced/CustomPcmAudioSource/CustomPcmAudioSource.m @@ -38,6 +38,7 @@ - (IBAction)onClickJoinButton:(id)sender { @interface CustomPcmAudioSource () @property (weak, nonatomic) IBOutlet UIView *containerView; @property (weak, nonatomic) IBOutlet UISwitch *pushPcmSwitch; +@property (weak, nonatomic) IBOutlet UISwitch *pushMicrophoneSwitch; @property (nonatomic, strong)VideoView *localView; @property (nonatomic, strong)VideoView *remoteView; @property (nonatomic, strong)AgoraRtcEngineKit *agoraKit; @@ -72,7 +73,7 @@ - (void)viewDidLoad { [self.containerView layoutStream:@[self.localView, self.remoteView]]; NSString *filePath = [[NSBundle mainBundle] pathForResource:@"output.raw" ofType:nil]; - self.pcmSourcePush = [[AgoraPCMSourcePush alloc] initWithDelegate:self filePath:filePath sampleRate:44100 channelsPerFrame:1 bitPerSamples:16 samples:441 * 10]; + self.pcmSourcePush = [[AgoraPCMSourcePush alloc] initWithDelegate:self filePath:filePath sampleRate:44100 channelsPerFrame:2 bitPerSamples:16 samples:441 * 10]; // set up agora instance when view loaded AgoraRtcEngineConfig *config = [[AgoraRtcEngineConfig alloc] init]; @@ -82,18 +83,9 @@ - (void)viewDidLoad { self.agoraKit = [AgoraRtcEngineKit sharedEngineWithConfig:config delegate:self]; NSString *channelName = [self.configs objectForKey:@"channelName"]; - // make myself a broadcaster - [self.agoraKit setClientRole:(AgoraClientRoleBroadcaster)]; // enable video module and set up video encoding configs [self.agoraKit enableAudio]; - AgoraVideoEncoderConfiguration *encoderConfig = [[AgoraVideoEncoderConfiguration alloc] initWithSize:CGSizeMake(960, 540) - frameRate:(AgoraVideoFrameRateFps15) - bitrate:15 - orientationMode:(AgoraVideoOutputOrientationModeFixedPortrait) - mirrorMode:(AgoraVideoMirrorModeAuto)]; - [self.agoraKit setVideoEncoderConfiguration:encoderConfig]; - AgoraAudioTrackConfig *trackConfig = [[AgoraAudioTrackConfig alloc] init]; trackConfig.enableLocalPlayback = YES; self.trackId = [self.agoraKit createCustomAudioTrack:(AgoraAudioTrackTypeMixable) config:trackConfig]; @@ -117,12 +109,9 @@ - (void)viewDidLoad { // when joining channel. The channel name and uid used to calculate // the token has to match the ones used for channel join AgoraRtcChannelMediaOptions *options = [[AgoraRtcChannelMediaOptions alloc] init]; - options.autoSubscribeAudio = YES; options.autoSubscribeVideo = NO; - options.publishMicrophoneTrack = YES; + options.publishMicrophoneTrack = NO; options.publishCameraTrack = NO; - options.publishCustomAudioTrack = YES; - options.publishCustomAudioTrackId = self.trackId; options.clientRoleType = AgoraClientRoleBroadcaster; [[NetworkManager shared] generateTokenWithChannelName:channelName uid:0 success:^(NSString * _Nullable token) { @@ -144,6 +133,13 @@ - (IBAction)pushPCM:(UISwitch *)sender { } AgoraRtcChannelMediaOptions *mediaOption = [[AgoraRtcChannelMediaOptions alloc] init]; mediaOption.publishCustomAudioTrack = sender.isOn; + mediaOption.publishCustomAudioTrackId = self.trackId; + [self.agoraKit updateChannelWithMediaOptions:mediaOption]; +} + +- (IBAction)pushMicrophone:(UISwitch*)sender { + AgoraRtcChannelMediaOptions *mediaOption = [[AgoraRtcChannelMediaOptions alloc] init]; + mediaOption.publishMicrophoneTrack = sender.isOn; [self.agoraKit updateChannelWithMediaOptions:mediaOption]; } @@ -159,9 +155,9 @@ - (void)viewDidDisappear:(BOOL)animated { // AgoraPcmSourcePushDelegate - (void)onAudioFrame:(void *)data { [self.agoraKit pushExternalAudioFrameRawData:data - samples:441 * 10 + samples:441 * 10 * 2 sampleRate:44100 - channels:1 + channels:2 trackId:self.trackId timestamp:0]; } diff --git a/iOS/APIExample-OC/APIExample-OC/Examples/Advanced/CustomPcmAudioSource/CustomPcmAudioSource.storyboard b/iOS/APIExample-OC/APIExample-OC/Examples/Advanced/CustomPcmAudioSource/CustomPcmAudioSource.storyboard index 6a293c9ee..8af06565e 100644 --- a/iOS/APIExample-OC/APIExample-OC/Examples/Advanced/CustomPcmAudioSource/CustomPcmAudioSource.storyboard +++ b/iOS/APIExample-OC/APIExample-OC/Examples/Advanced/CustomPcmAudioSource/CustomPcmAudioSource.storyboard @@ -1,9 +1,9 @@ - + - + @@ -76,32 +76,59 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/iOS/APIExample-OC/APIExample-OC/Examples/Advanced/FusionCDN/FusionCDN.m b/iOS/APIExample-OC/APIExample-OC/Examples/Advanced/FusionCDN/FusionCDN.m index 070a141f9..7c7c1727e 100644 --- a/iOS/APIExample-OC/APIExample-OC/Examples/Advanced/FusionCDN/FusionCDN.m +++ b/iOS/APIExample-OC/APIExample-OC/Examples/Advanced/FusionCDN/FusionCDN.m @@ -51,7 +51,8 @@ - (IBAction)setStreamingMode:(UIButton *)sender { [alertVC addAction:cdn]; [alertVC addAction:cancel]; - [self presentViewController:alertVC animated:YES completion:nil]; +// [self presentViewController:alertVC animated:YES completion:nil]; + [self presentAlertViewController:alertVC]; } - (IBAction)joinAsHost:(id)sender { [self.textField resignFirstResponder]; @@ -477,7 +478,8 @@ - (IBAction)setCDNChannel:(UIButton *)sender { } UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"Cancel".localized style:(UIAlertActionStyleCancel) handler:nil]; [alertVC addAction:cancel]; - [self presentViewController:alertVC animated:YES completion:nil]; +// [self presentViewController:alertVC animated:YES completion:nil]; + [self presentAlertViewController:alertVC]; } - (IBAction)setRtcStreaming:(UISwitch *)sender { self.isRtcStreaming = sender.isOn; diff --git a/iOS/APIExample-OC/APIExample-OC/Examples/Advanced/LiveStreaming/LiveStreaming.m b/iOS/APIExample-OC/APIExample-OC/Examples/Advanced/LiveStreaming/LiveStreaming.m index de9f417fc..f9f23ef9e 100644 --- a/iOS/APIExample-OC/APIExample-OC/Examples/Advanced/LiveStreaming/LiveStreaming.m +++ b/iOS/APIExample-OC/APIExample-OC/Examples/Advanced/LiveStreaming/LiveStreaming.m @@ -29,6 +29,7 @@ - (void)viewDidLoad { } - (IBAction)onClickChoseBackgroundColor:(UIButton *)sender { + [self.view endEditing:YES]; PickerView *pickerView = [[PickerView alloc] init]; NSDictionary *colors = @{ @"Red".localized: @(0xff0d00ff), @@ -56,7 +57,8 @@ - (IBAction)onClickFirstFrameSwitch:(UISwitch *)sender { }]; [alertVC addAction:ok]; [alertVC addAction:cancel]; - [self presentViewController:alertVC animated:YES completion:nil]; +// [self presentViewController:alertVC animated:YES completion:nil]; + [self presentAlertViewController:alertVC]; } else { self.isFirstFrame = NO; } @@ -89,7 +91,8 @@ - (IBAction)onClickJoinButton:(id)sender { [alertVC addAction:broadcaster]; [alertVC addAction:audience]; [alertVC addAction:cancel]; - [self presentViewController:alertVC animated:YES completion:nil]; +// [self presentViewController:alertVC animated:YES completion:nil]; + [self presentAlertViewController:alertVC]; } @@ -105,6 +108,12 @@ - (void)doJoin { [self.navigationController pushViewController:newViewController animated:YES]; } +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { + [super touchesEnded:touches withEvent:event]; + + [self.view endEditing:YES]; +} + @end diff --git a/iOS/APIExample-OC/APIExample-OC/Examples/Advanced/StreamEncryption/StreamEncryption.m b/iOS/APIExample-OC/APIExample-OC/Examples/Advanced/StreamEncryption/StreamEncryption.m index 528077093..4d880ca67 100644 --- a/iOS/APIExample-OC/APIExample-OC/Examples/Advanced/StreamEncryption/StreamEncryption.m +++ b/iOS/APIExample-OC/APIExample-OC/Examples/Advanced/StreamEncryption/StreamEncryption.m @@ -54,7 +54,8 @@ - (IBAction)setEncryptionMode:(UIButton *)sender { UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"Cancel".localized style:(UIAlertActionStyleCancel) handler:nil]; [alertVC addAction:custom]; [alertVC addAction:cancel]; - [self presentViewController:alertVC animated:YES completion:nil]; +// [self presentViewController:alertVC animated:YES completion:nil]; + [self presentAlertViewController:alertVC]; } - (IBAction)onClickJoinButton:(id)sender { diff --git a/iOS/APIExample-OC/APIExample-OC/Examples/Advanced/VideoMetadata/VideoMetadata.m b/iOS/APIExample-OC/APIExample-OC/Examples/Advanced/VideoMetadata/VideoMetadata.m index fda2c9f86..d210ce78c 100644 --- a/iOS/APIExample-OC/APIExample-OC/Examples/Advanced/VideoMetadata/VideoMetadata.m +++ b/iOS/APIExample-OC/APIExample-OC/Examples/Advanced/VideoMetadata/VideoMetadata.m @@ -215,16 +215,12 @@ - (NSData *)readyToSendMetadataAtTimestamp:(NSTimeInterval)timestamp sourceType: self.metadata = nil; return metadata; } -/// Callback when the local user receives the metadata. -/// @param data The received metadata. -/// @param uid The ID of the user who sends the metadata. -/// @param timestamp The timestamp (ms) of the received metadata. -- (void)receiveMetadata:(NSData *)data fromUser:(NSInteger)uid atTimestamp:(NSTimeInterval)timestamp { + +- (void)didMetadataReceived:(AgoraMetadata * _Nonnull)metadata { dispatch_async(dispatch_get_main_queue(), ^{ [LogUtil log: @"metadata received" level:LogLevelInfo]; - NSString *message = [[NSString alloc] initWithData:data encoding:kCFStringEncodingUTF8]; + NSString *message = [[NSString alloc] initWithData:metadata.data encoding:kCFStringEncodingUTF8]; [self showAlertWithTitle:@"Metadata received" message:message]; }); } - @end diff --git a/iOS/APIExample-OC/APIExample-OC/Examples/Advanced/VoiceChanger/VoiceChanger.m b/iOS/APIExample-OC/APIExample-OC/Examples/Advanced/VoiceChanger/VoiceChanger.m index a528e5cfa..b9d700124 100644 --- a/iOS/APIExample-OC/APIExample-OC/Examples/Advanced/VoiceChanger/VoiceChanger.m +++ b/iOS/APIExample-OC/APIExample-OC/Examples/Advanced/VoiceChanger/VoiceChanger.m @@ -114,7 +114,8 @@ - (IBAction)onChatBeautifier:(UIButton *)sender { } UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"Cancel".localized style:(UIAlertActionStyleCancel) handler:nil]; [alertVC addAction:cancel]; - [self presentViewController:alertVC animated:YES completion:nil]; +// [self presentViewController:alertVC animated:YES completion:nil]; + [self presentAlertViewController:alertVC]; } - (IBAction)onVoiceChanger:(UIButton *)sender { UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:@"Set Voice Changer".localized message:nil preferredStyle:(UIAlertControllerStyleActionSheet)]; @@ -144,7 +145,8 @@ - (IBAction)onVoiceChanger:(UIButton *)sender { } UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"Cancel".localized style:(UIAlertActionStyleCancel) handler:nil]; [alertVC addAction:cancel]; - [self presentViewController:alertVC animated:YES completion:nil]; +// [self presentViewController:alertVC animated:YES completion:nil]; + [self presentAlertViewController:alertVC]; } - (IBAction)onStyleTransformation:(UIButton *)sender { UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:@"Set Style Transformation".localized message:nil preferredStyle:(UIAlertControllerStyleActionSheet)]; @@ -169,7 +171,8 @@ - (IBAction)onStyleTransformation:(UIButton *)sender { } UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"Cancel".localized style:(UIAlertActionStyleCancel) handler:nil]; [alertVC addAction:cancel]; - [self presentViewController:alertVC animated:YES completion:nil]; +// [self presentViewController:alertVC animated:YES completion:nil]; + [self presentAlertViewController:alertVC]; } - (IBAction)onRoomAcoustics:(UIButton *)sender { UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:@"Set Room Acoustics".localized message:nil preferredStyle:(UIAlertControllerStyleActionSheet)]; @@ -200,7 +203,8 @@ - (IBAction)onRoomAcoustics:(UIButton *)sender { } UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"Cancel".localized style:(UIAlertActionStyleCancel) handler:nil]; [alertVC addAction:cancel]; - [self presentViewController:alertVC animated:YES completion:nil]; +// [self presentViewController:alertVC animated:YES completion:nil]; + [self presentAlertViewController:alertVC]; } - (IBAction)onPitchCorrection:(UIButton *)sender { UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:@"Set Pitch Correction".localized message:nil preferredStyle:(UIAlertControllerStyleActionSheet)]; @@ -223,7 +227,8 @@ - (IBAction)onPitchCorrection:(UIButton *)sender { } UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"Cancel".localized style:(UIAlertActionStyleCancel) handler:nil]; [alertVC addAction:cancel]; - [self presentViewController:alertVC animated:YES completion:nil]; +// [self presentViewController:alertVC animated:YES completion:nil]; + [self presentAlertViewController:alertVC]; } - (IBAction)onVoiceConversion:(UIButton *)sender { UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:@"Set Voice Conversion".localized message:nil preferredStyle:(UIAlertControllerStyleActionSheet)]; @@ -250,7 +255,8 @@ - (IBAction)onVoiceConversion:(UIButton *)sender { } UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"Cancel".localized style:(UIAlertActionStyleCancel) handler:nil]; [alertVC addAction:cancel]; - [self presentViewController:alertVC animated:YES completion:nil]; +// [self presentViewController:alertVC animated:YES completion:nil]; + [self presentAlertViewController:alertVC]; } - (IBAction)onTimbreTransformation:(UIButton *)sender { @@ -282,7 +288,8 @@ - (IBAction)onTimbreTransformation:(UIButton *)sender { } UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"Cancel".localized style:(UIAlertActionStyleCancel) handler:nil]; [alertVC addAction:cancel]; - [self presentViewController:alertVC animated:YES completion:nil]; +// [self presentViewController:alertVC animated:YES completion:nil]; + [self presentAlertViewController:alertVC]; } - (IBAction)onAudioEffectsParamUpdated:(UISlider *)sender { int param1 = self.audioEffectParam1Slider.isEnabled ? self.audioEffectParam1Slider.value : 0; @@ -319,7 +326,8 @@ - (IBAction)onLocalVoiceEqualizaitonFreq:(UIButton *)sender { } UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"Cancel".localized style:(UIAlertActionStyleCancel) handler:nil]; [alertVC addAction:cancel]; - [self presentViewController:alertVC animated:YES completion:nil]; +// [self presentViewController:alertVC animated:YES completion:nil]; + [self presentAlertViewController:alertVC]; } - (IBAction)onLocalVoiceEqualizationGain:(UISlider *)sender { self.equalizationGain = (int)sender.value; @@ -374,7 +382,8 @@ - (IBAction)onLocalVoiceReverbKey:(UIButton *)sender { } UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"Cancel".localized style:(UIAlertActionStyleCancel) handler:nil]; [alertVC addAction:cancel]; - [self presentViewController:alertVC animated:YES completion:nil]; +// [self presentViewController:alertVC animated:YES completion:nil]; + [self presentAlertViewController:alertVC]; } - (IBAction)onLocalVoiceReverbValue:(UISlider *)sender { int value = (int)sender.value; diff --git a/iOS/APIExample-OC/APIExample-OC/Info.plist b/iOS/APIExample-OC/APIExample-OC/Info.plist index 7ff6874fd..8a4ef01cb 100644 --- a/iOS/APIExample-OC/APIExample-OC/Info.plist +++ b/iOS/APIExample-OC/APIExample-OC/Info.plist @@ -2,12 +2,12 @@ - UIFileSharingEnabled - UIBackgroundModes audio processing + UIFileSharingEnabled + diff --git a/iOS/APIExample-OC/Podfile b/iOS/APIExample-OC/Podfile index 52714f180..5fbcd5c1d 100644 --- a/iOS/APIExample-OC/Podfile +++ b/iOS/APIExample-OC/Podfile @@ -5,24 +5,23 @@ target 'APIExample-OC' do # Comment the next line if you don't want to use dynamic frameworks use_frameworks! - pod 'AgoraRtcEngine_iOS', '4.3.1' - # pod 'sdk', :path => 'sdk.podspec' - + pod 'AgoraRtcEngine_iOS', '4.4.0' +# pod 'sdk', :path => 'sdk.podspec' end target 'Agora-ScreenShare-Extension-OC' do use_frameworks! - # pod 'sdk', :path => 'sdk.podspec' - pod 'AgoraRtcEngine_iOS', '4.3.1' +# pod 'sdk', :path => 'sdk.podspec' + pod 'AgoraRtcEngine_iOS', '4.4.0' end target 'SimpleFilter' do use_frameworks! - # pod 'sdk', :path => 'sdk.podspec' - pod 'AgoraRtcEngine_iOS', '4.3.1' +# pod 'sdk', :path => 'sdk.podspec' + pod 'AgoraRtcEngine_iOS', '4.4.0' end pre_install do |installer| - # system("sh .download_script.sh 4.3.1 true") + # system("sh .download_script.sh 4.3.2 true") end diff --git a/iOS/APIExample-OC/README.md b/iOS/APIExample-OC/README.md index 194f014f4..e64199d83 100644 --- a/iOS/APIExample-OC/README.md +++ b/iOS/APIExample-OC/README.md @@ -62,8 +62,8 @@ To build and run the sample application, get an App Id: Agora provides App certificate to generate Token. You can deploy and generate a token on your server, or use the console to generate a temporary token. In order to get the APP ID, you can open the agora console (https://console.agora.io/) to create a project with the App Certificate enabled, - then the APP Certificate can be found in the project detail page. - PS: If the project does not have certificates enabled, leave this field blank. + then the APP Certificate can be found in the project detail page.If the project does not have certificates enabled, leave this field blank. + PS: It is unsafe to place the App Certificate on the client side, it is recommended to place it on the server side to ensure that the App Certificate is not leaked. */ static NSString * const Certificate = <#YOUR Certificate#> diff --git a/iOS/APIExample-OC/README.zh.md b/iOS/APIExample-OC/README.zh.md index 0e5eee347..5b5196015 100644 --- a/iOS/APIExample-OC/README.zh.md +++ b/iOS/APIExample-OC/README.zh.md @@ -56,8 +56,8 @@ pod install /** Agora 提供 App certificate 用以生成 Token。您可以在您的服务器部署并生成 Token,或者使用控制台生成临时的 Token。 - 进入声网控制台(https://console.agora.io/),创建一个带证书鉴权的项目,进入项目配置页,即可看到APP证书。 - 注意:如果项目没有开启证书鉴权,这个字段留空。 + 进入声网控制台(https://console.agora.io/),创建一个带证书鉴权的项目,进入项目配置页,即可看到APP证书。如果项目没有开启证书鉴权,这个字段留空。 + 注意:App证书放在客户端不安全,推荐放在服务端以确保 App 证书不会泄露。 */ static NSString * const Certificate = <#YOUR Certificate#> ``` diff --git a/iOS/APIExample-OC/cloud_build.sh b/iOS/APIExample-OC/cloud_build.sh new file mode 100755 index 000000000..6f034dcbd --- /dev/null +++ b/iOS/APIExample-OC/cloud_build.sh @@ -0,0 +1,108 @@ +#!/usr/bin/env sh + +PROJECT_PATH=$PWD + +if [ "$WORKSPACE" = "" ]; then + WORKSPACE=$PWD +fi +if [ "$BUILD_NUMBER" = "" ]; then + BUILD_NUMBER=888 +fi + +cd ${PROJECT_PATH} && pod install || exit 1 + +# 打包环境 +CONFIGURATION="Debug" + +#工程文件路径 +APP_PATH="$(ls | grep xcworkspace)" + +# 项目target名 +TARGET_NAME=${APP_PATH%%.*} + +KEYCENTER_PATH=$TARGET_NAME/Common/KeyCenter.m + +#工程配置路径 +PBXPROJ_PATH=${TARGET_NAME}.xcodeproj/project.pbxproj + +# Debug +/usr/libexec/PlistBuddy -c "Set :objects:E70ADE062A5D0050009947CF:buildSettings:CODE_SIGN_STYLE 'Manual'" $PBXPROJ_PATH +/usr/libexec/PlistBuddy -c "Set :objects:E70ADE062A5D0050009947CF:buildSettings:DEVELOPMENT_TEAM 'GM72UGLGZW'" $PBXPROJ_PATH +/usr/libexec/PlistBuddy -c "Set :objects:E70ADE062A5D0050009947CF:buildSettings:PROVISIONING_PROFILE_SPECIFIER 'App'" $PBXPROJ_PATH +# Release +/usr/libexec/PlistBuddy -c "Set :objects:E70ADE072A5D0050009947CF:buildSettings:CODE_SIGN_STYLE 'Manual'" $PBXPROJ_PATH +/usr/libexec/PlistBuddy -c "Set :objects:E70ADE072A5D0050009947CF:buildSettings:DEVELOPMENT_TEAM 'GM72UGLGZW'" $PBXPROJ_PATH +/usr/libexec/PlistBuddy -c "Set :objects:E70ADE072A5D0050009947CF:buildSettings:PROVISIONING_PROFILE_SPECIFIER 'App'" $PBXPROJ_PATH +# 屏幕共享Extension +# Debug +/usr/libexec/PlistBuddy -c "Set :objects:E72F61D42A7256D500C963D2:buildSettings:CODE_SIGN_STYLE 'Manual'" $PBXPROJ_PATH +/usr/libexec/PlistBuddy -c "Set :objects:E72F61D42A7256D500C963D2:buildSettings:DEVELOPMENT_TEAM 'GM72UGLGZW'" $PBXPROJ_PATH +/usr/libexec/PlistBuddy -c "Set :objects:E72F61D42A7256D500C963D2:buildSettings:PROVISIONING_PROFILE_SPECIFIER 'App'" $PBXPROJ_PATH +# Release +/usr/libexec/PlistBuddy -c "Set :objects:E72F61D52A7256D500C963D2:buildSettings:CODE_SIGN_STYLE 'Manual'" $PBXPROJ_PATH +/usr/libexec/PlistBuddy -c "Set :objects:E72F61D52A7256D500C963D2:buildSettings:DEVELOPMENT_TEAM 'GM72UGLGZW'" $PBXPROJ_PATH +/usr/libexec/PlistBuddy -c "Set :objects:E72F61D52A7256D500C963D2:buildSettings:PROVISIONING_PROFILE_SPECIFIER 'App'" $PBXPROJ_PATH +# SimpleFilter +# Debug +/usr/libexec/PlistBuddy -c "Set :objects:E7361F932A6E6E7100925BD6:buildSettings:CODE_SIGN_STYLE 'Manual'" $PBXPROJ_PATH +/usr/libexec/PlistBuddy -c "Set :objects:E7361F932A6E6E7100925BD6:buildSettings:DEVELOPMENT_TEAM ''" $PBXPROJ_PATH +/usr/libexec/PlistBuddy -c "Set :objects:E7361F932A6E6E7100925BD6:buildSettings:PROVISIONING_PROFILE_SPECIFIER ''" $PBXPROJ_PATH +# Release +/usr/libexec/PlistBuddy -c "Set :objects:E7361F942A6E6E7100925BD6:buildSettings:CODE_SIGN_STYLE 'Manual'" $PBXPROJ_PATH +/usr/libexec/PlistBuddy -c "Set :objects:E7361F942A6E6E7100925BD6:buildSettings:DEVELOPMENT_TEAM ''" $PBXPROJ_PATH +/usr/libexec/PlistBuddy -c "Set :objects:E7361F942A6E6E7100925BD6:buildSettings:PROVISIONING_PROFILE_SPECIFIER ''" $PBXPROJ_PATH +#修改build number +# Debug +/usr/libexec/PlistBuddy -c "Set :objects:E70ADE062A5D0050009947CF:buildSettings:CURRENT_PROJECT_VERSION ${BUILD_NUMBER}" $PBXPROJ_PATH +# Release +/usr/libexec/PlistBuddy -c "Set :objects:E70ADE072A5D0050009947CF:buildSettings:CURRENT_PROJECT_VERSION ${BUILD_NUMBER}" $PBXPROJ_PATH + +# 读取APPID环境变量 +echo AGORA_APP_ID: $APP_ID + +echo PROJECT_PATH: $PROJECT_PATH +echo TARGET_NAME: $TARGET_NAME +echo KEYCENTER_PATH: $KEYCENTER_PATH +echo APP_PATH: $APP_PATH + +#修改Keycenter文件 +sed -i -e "s#<\#YOUR APPID\#>#@\"$APP_ID\";#g" $KEYCENTER_PATH +rm -f ${KEYCENTER_PATH}-e + +# Xcode clean +xcodebuild clean -workspace "${APP_PATH}" -configuration "${CONFIGURATION}" -scheme "${TARGET_NAME}" + +# 时间戳 +CURRENT_TIME=$(date "+%Y-%m-%d %H-%M-%S") + +# 归档路径 +ARCHIVE_PATH="${WORKSPACE}/${TARGET_NAME}_${BUILD_NUMBER}.xcarchive" + +# 编译环境 + +# plist路径 +PLIST_PATH="${PROJECT_PATH}/ExportOptions.plist" + +echo PLIST_PATH: $PLIST_PATH + +# archive 这边使用的工作区间 也可以使用project +xcodebuild CODE_SIGN_STYLE="Manual" archive -workspace "${APP_PATH}" -scheme "${TARGET_NAME}" clean CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO -configuration "${CONFIGURATION}" -archivePath "${ARCHIVE_PATH}" -destination 'generic/platform=iOS' -quiet || exit 1 + +cd ${WORKSPACE} + +# 压缩archive +7za a -tzip "${TARGET_NAME}_${BUILD_NUMBER}.xcarchive.zip" "${ARCHIVE_PATH}" + +# 签名 +# sh sign "${TARGET_NAME}_${BUILD_NUMBER}.xcarchive.zip" --type xcarchive --plist "${PLIST_PATH}" +sh export "${TARGET_NAME}_${BUILD_NUMBER}.xcarchive.zip" --plist "${PLIST_PATH}" + +SDK_VERSION=$(echo $sdk_url | cut -d "/" -f 5) +OUTPUT_FILE=${WORKSPACE}/${TARGET_NAME}_${BUILD_NUMBER}_${SDK_VERSION}_$(date "+%Y%m%d%H%M%S").ipa +mv ${TARGET_NAME}_${BUILD_NUMBER}.ipa $OUTPUT_FILE + +rm -rf *.xcarchive +rm -rf *.xcarchive.zip +echo OUTPUT_FILE: $OUTPUT_FILE + + diff --git a/iOS/APIExample/APIExample.xcodeproj/project.pbxproj b/iOS/APIExample/APIExample.xcodeproj/project.pbxproj index 908ec7643..0d257cf92 100644 --- a/iOS/APIExample/APIExample.xcodeproj/project.pbxproj +++ b/iOS/APIExample/APIExample.xcodeproj/project.pbxproj @@ -134,6 +134,13 @@ A7CA48C424553CF700507435 /* Popover.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A7CA48C224553CF600507435 /* Popover.storyboard */; }; B9C914453E92C7F49C93E1F5 /* Pods_APIExample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8551A4BC255CE49B383BC575 /* Pods_APIExample.framework */; }; CBCDE23FB64E60D6A79F3723 /* Pods_Agora_ScreenShare_Extension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09E72C5D1AABD812866E41A6 /* Pods_Agora_ScreenShare_Extension.framework */; }; + DD6ED7F12CB69FFB007B3B5D /* LocalCompositeGraph.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DD6ED7EC2CB69FFB007B3B5D /* LocalCompositeGraph.storyboard */; }; + DD6ED7F22CB69FFB007B3B5D /* LocalCompositeGraph.strings in Resources */ = {isa = PBXBuildFile; fileRef = DD6ED7EE2CB69FFB007B3B5D /* LocalCompositeGraph.strings */; }; + DD6ED7F32CB69FFB007B3B5D /* LocalCompositeGraph.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6ED7EF2CB69FFB007B3B5D /* LocalCompositeGraph.swift */; }; + DD8A1F7C2CA5048D001CEC51 /* AgoraPCMPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8A1F7B2CA5048D001CEC51 /* AgoraPCMPlayer.swift */; }; + DDFD50262C205BF20050B344 /* TransparentRender.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDFD50252C205BF20050B344 /* TransparentRender.swift */; }; + DDFD50282C205CF40050B344 /* TransparentRender.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DDFD50272C205CF40050B344 /* TransparentRender.storyboard */; }; + DDFD502A2C2065D00050B344 /* yuv_limit_range_alpha_1280_540_right.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = DDFD50292C2065D00050B344 /* yuv_limit_range_alpha_1280_540_right.mp4 */; }; E7163F812964149800EBBD55 /* ARKit.strings in Resources */ = {isa = PBXBuildFile; fileRef = E7163F7C2964149800EBBD55 /* ARKit.strings */; }; E7163F822964149800EBBD55 /* ARKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7163F7E2964149800EBBD55 /* ARKit.swift */; }; E7163F832964149800EBBD55 /* ARKit.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E7163F7F2964149800EBBD55 /* ARKit.storyboard */; }; @@ -142,13 +149,14 @@ E72055EA28F943520030E6D1 /* Util.swift in Sources */ = {isa = PBXBuildFile; fileRef = E72055E928F943520030E6D1 /* Util.swift */; }; E72055F32900F8780030E6D1 /* KtvCopyrightMusic.swift in Sources */ = {isa = PBXBuildFile; fileRef = E72055F12900F8780030E6D1 /* KtvCopyrightMusic.swift */; }; E721600F28D3314B006431BD /* AlertManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E721600E28D3314B006431BD /* AlertManager.swift */; }; - E726BFFA2A949F70006870E2 /* AuidoRouterPlayer.strings in Resources */ = {isa = PBXBuildFile; fileRef = E726BFF52A949F70006870E2 /* AuidoRouterPlayer.strings */; }; - E726BFFB2A949F70006870E2 /* AuidoRouterPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E726BFF72A949F70006870E2 /* AuidoRouterPlayer.swift */; }; - E726BFFC2A949F70006870E2 /* AuidoRouterPlayer.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E726BFF82A949F70006870E2 /* AuidoRouterPlayer.storyboard */; }; + E726BFFA2A949F70006870E2 /* AudioRouterPlayer.strings in Resources */ = {isa = PBXBuildFile; fileRef = E726BFF52A949F70006870E2 /* AudioRouterPlayer.strings */; }; + E726BFFB2A949F70006870E2 /* AudioRouterPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E726BFF72A949F70006870E2 /* AudioRouterPlayer.swift */; }; + E726BFFC2A949F70006870E2 /* AudioRouterPlayer.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E726BFF82A949F70006870E2 /* AudioRouterPlayer.storyboard */; }; E726C0032A96FD3A006870E2 /* AudioWaveform.strings in Resources */ = {isa = PBXBuildFile; fileRef = E726BFFE2A96FD3A006870E2 /* AudioWaveform.strings */; }; E726C0042A96FD3A006870E2 /* AudioWaveform.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E726C0002A96FD3A006870E2 /* AudioWaveform.storyboard */; }; E726C0052A96FD3A006870E2 /* AudioWaveform.swift in Sources */ = {isa = PBXBuildFile; fileRef = E726C0022A96FD3A006870E2 /* AudioWaveform.swift */; }; E726C0082A96FF15006870E2 /* ZSNBoxingView.m in Sources */ = {isa = PBXBuildFile; fileRef = E726C0062A96FF15006870E2 /* ZSNBoxingView.m */; }; + E728B84828B5FFCB00674A4A /* PictureInPicture.swift in Sources */ = {isa = PBXBuildFile; fileRef = E728B84428B5FFCB00674A4A /* PictureInPicture.swift */; }; E728B84C28B6015800674A4A /* AgoraPictureInPictureController.m in Sources */ = {isa = PBXBuildFile; fileRef = E728B84B28B6015800674A4A /* AgoraPictureInPictureController.m */; }; E728B84F28B601A300674A4A /* AgoraSampleBufferRender.m in Sources */ = {isa = PBXBuildFile; fileRef = E728B84E28B601A300674A4A /* AgoraSampleBufferRender.m */; }; E728B85128B60D5B00674A4A /* VideoViewSampleBufferDisplayView.xib in Resources */ = {isa = PBXBuildFile; fileRef = E728B85028B60D5B00674A4A /* VideoViewSampleBufferDisplayView.xib */; }; @@ -220,7 +228,15 @@ E7A49D6B2909115200F06DD4 /* BEEffectResourceHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = E7A49D6A2909115100F06DD4 /* BEEffectResourceHelper.m */; }; E7AD0DE129C85FFB00C9A4B0 /* sample.mov in Resources */ = {isa = PBXBuildFile; fileRef = E7AD0DE029C85FFB00C9A4B0 /* sample.mov */; }; E7AD0DE329C95EB500C9A4B0 /* PickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7AD0DE229C95EB500C9A4B0 /* PickerView.swift */; }; - F70DE4D72C6627C100FFC922 /* PictureInPicture.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70DE4D52C6627C100FFC922 /* PictureInPicture.swift */; }; + F728B9D72CA295D7007813BB /* PictureInPicture.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F728B9CD2CA295D7007813BB /* PictureInPicture.storyboard */; }; + F728B9D82CA295D7007813BB /* ChannelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F728B9CE2CA295D7007813BB /* ChannelViewController.swift */; }; + F728B9D92CA295D7007813BB /* CustomViewPIPService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F728B9CF2CA295D7007813BB /* CustomViewPIPService.swift */; }; + F728B9DA2CA295D7007813BB /* CustomViewPIPViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F728B9D02CA295D7007813BB /* CustomViewPIPViewController.swift */; }; + F728B9DB2CA295D7007813BB /* PIPBaseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F728B9D22CA295D7007813BB /* PIPBaseViewController.swift */; }; + F728B9DC2CA295D7007813BB /* PixelBufferPIPService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F728B9D32CA295D7007813BB /* PixelBufferPIPService.swift */; }; + F728B9DD2CA295D7007813BB /* PixelBufferPIPViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F728B9D42CA295D7007813BB /* PixelBufferPIPViewController.swift */; }; + F728B9DE2CA295D7007813BB /* PixelBufferRenderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F728B9D52CA295D7007813BB /* PixelBufferRenderView.swift */; }; + F728B9E02CA29625007813BB /* PictureInPicture.strings in Resources */ = {isa = PBXBuildFile; fileRef = F728B9DF2CA29625007813BB /* PictureInPicture.strings */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -444,6 +460,13 @@ A7CA48C324553CF600507435 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Popover.storyboard; sourceTree = ""; }; BC25C1A6D9E6B8827D095985 /* Pods_SimpleFilter.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SimpleFilter.framework; sourceTree = BUILT_PRODUCTS_DIR; }; CC6D08A23527C200339E4FD6 /* Pods-SimpleAudioFilter.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SimpleAudioFilter.release.xcconfig"; path = "Target Support Files/Pods-SimpleAudioFilter/Pods-SimpleAudioFilter.release.xcconfig"; sourceTree = ""; }; + DD6ED7EB2CB69FFB007B3B5D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LocalCompositeGraph.storyboard; sourceTree = ""; }; + DD6ED7ED2CB69FFB007B3B5D /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/LocalCompositeGraph.strings"; sourceTree = ""; }; + DD6ED7EF2CB69FFB007B3B5D /* LocalCompositeGraph.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalCompositeGraph.swift; sourceTree = ""; }; + DD8A1F7B2CA5048D001CEC51 /* AgoraPCMPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgoraPCMPlayer.swift; sourceTree = ""; }; + DDFD50252C205BF20050B344 /* TransparentRender.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransparentRender.swift; sourceTree = ""; }; + DDFD50272C205CF40050B344 /* TransparentRender.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = TransparentRender.storyboard; sourceTree = ""; }; + DDFD50292C2065D00050B344 /* yuv_limit_range_alpha_1280_540_right.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = yuv_limit_range_alpha_1280_540_right.mp4; sourceTree = ""; }; E7163F7D2964149800EBBD55 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/ARKit.strings"; sourceTree = ""; }; E7163F7E2964149800EBBD55 /* ARKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ARKit.swift; sourceTree = ""; }; E7163F802964149800EBBD55 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/ARKit.storyboard; sourceTree = ""; }; @@ -452,14 +475,15 @@ E72055E928F943520030E6D1 /* Util.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Util.swift; sourceTree = ""; }; E72055F12900F8780030E6D1 /* KtvCopyrightMusic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KtvCopyrightMusic.swift; sourceTree = ""; }; E721600E28D3314B006431BD /* AlertManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlertManager.swift; sourceTree = ""; }; - E726BFF62A949F70006870E2 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/AuidoRouterPlayer.strings"; sourceTree = ""; }; - E726BFF72A949F70006870E2 /* AuidoRouterPlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuidoRouterPlayer.swift; sourceTree = ""; }; - E726BFF92A949F70006870E2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/AuidoRouterPlayer.storyboard; sourceTree = ""; }; + E726BFF62A949F70006870E2 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/AudioRouterPlayer.strings"; sourceTree = ""; }; + E726BFF72A949F70006870E2 /* AudioRouterPlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioRouterPlayer.swift; sourceTree = ""; }; + E726BFF92A949F70006870E2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/AudioRouterPlayer.storyboard; sourceTree = ""; }; E726BFFF2A96FD3A006870E2 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/AudioWaveform.strings"; sourceTree = ""; }; E726C0012A96FD3A006870E2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/AudioWaveform.storyboard; sourceTree = ""; }; E726C0022A96FD3A006870E2 /* AudioWaveform.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioWaveform.swift; sourceTree = ""; }; E726C0062A96FF15006870E2 /* ZSNBoxingView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ZSNBoxingView.m; sourceTree = ""; }; E726C0072A96FF15006870E2 /* ZSNBoxingView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZSNBoxingView.h; sourceTree = ""; }; + E728B84428B5FFCB00674A4A /* PictureInPicture.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PictureInPicture.swift; sourceTree = ""; }; E728B84A28B6015800674A4A /* AgoraPictureInPictureController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AgoraPictureInPictureController.h; sourceTree = ""; }; E728B84B28B6015800674A4A /* AgoraPictureInPictureController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AgoraPictureInPictureController.m; sourceTree = ""; }; E728B84D28B601A300674A4A /* AgoraSampleBufferRender.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AgoraSampleBufferRender.h; sourceTree = ""; }; @@ -571,7 +595,15 @@ E7AD0DE229C95EB500C9A4B0 /* PickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickerView.swift; 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; }; - F70DE4D52C6627C100FFC922 /* PictureInPicture.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PictureInPicture.swift; sourceTree = ""; }; + F728B9CD2CA295D7007813BB /* PictureInPicture.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = PictureInPicture.storyboard; sourceTree = ""; }; + F728B9CE2CA295D7007813BB /* ChannelViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChannelViewController.swift; sourceTree = ""; }; + F728B9CF2CA295D7007813BB /* CustomViewPIPService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomViewPIPService.swift; sourceTree = ""; }; + F728B9D02CA295D7007813BB /* CustomViewPIPViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomViewPIPViewController.swift; sourceTree = ""; }; + F728B9D22CA295D7007813BB /* PIPBaseViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PIPBaseViewController.swift; sourceTree = ""; }; + F728B9D32CA295D7007813BB /* PixelBufferPIPService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PixelBufferPIPService.swift; sourceTree = ""; }; + F728B9D42CA295D7007813BB /* PixelBufferPIPViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PixelBufferPIPViewController.swift; sourceTree = ""; }; + F728B9D52CA295D7007813BB /* PixelBufferRenderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PixelBufferRenderView.swift; sourceTree = ""; }; + F728B9DF2CA29625007813BB /* PictureInPicture.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = PictureInPicture.strings; sourceTree = ""; }; 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 = ""; }; /* End PBXFileReference section */ @@ -836,6 +868,7 @@ 03BEED09251C4446005E78F4 /* Resources */ = { isa = PBXGroup; children = ( + DDFD50292C2065D00050B344 /* yuv_limit_range_alpha_1280_540_right.mp4 */, 8B349FE22681E2CE007247F2 /* agora-logo.png */, 576CA80925A9CC3A0091520B /* output.raw */, E7AD0DE029C85FFB00C9A4B0 /* sample.mov */, @@ -930,6 +963,7 @@ 03DF1D8E24CFC29700DF7151 /* UIView+CSshortFrame.h */, 03DF1D8A24CFC29700DF7151 /* UIView+CSshortFrame.m */, 576CA80B25AA0FA90091520B /* AgoraPcmSourcePush.swift */, + DD8A1F7B2CA5048D001CEC51 /* AgoraPCMPlayer.swift */, ); path = ExternalAudio; sourceTree = ""; @@ -1098,9 +1132,10 @@ A75A56D724A0603000D0089E /* Advanced */ = { isa = PBXGroup; children = ( + DDFD50242C205BDF0050B344 /* TransparentRender */, E7883AE32B074746003CCF44 /* FaceCapture */, E726BFFD2A96FD3A006870E2 /* AudioWaveform */, - E726BFF42A949F70006870E2 /* AuidoRouterPlayer */, + E726BFF42A949F70006870E2 /* AudioRouterPlayer */, E7163F7B2964149800EBBD55 /* ARKit */, E7A49CE829029E0000F06DD4 /* ThirdBeautify */, E7A49CB229011E7500F06DD4 /* MutliCamera */, @@ -1127,6 +1162,7 @@ 034C625C2524A06800296ECF /* VoiceChanger */, 038576772521E568003C369A /* MediaChannelRelay */, 0339BE9225203293007D4FDD /* ScreenShare */, + DD6ED7F02CB69FFB007B3B5D /* LocalCompositeGraph */, 0339BE6E251EF074007D4FDD /* MediaPlayer */, 03BEED00251C35A4005E78F4 /* AudioMixing */, 0339D6CA24E9170D008739CD /* QuickSwitchChannel */, @@ -1151,6 +1187,25 @@ name = "Supporting Files"; sourceTree = ""; }; + DD6ED7F02CB69FFB007B3B5D /* LocalCompositeGraph */ = { + isa = PBXGroup; + children = ( + DD6ED7EC2CB69FFB007B3B5D /* LocalCompositeGraph.storyboard */, + DD6ED7EE2CB69FFB007B3B5D /* LocalCompositeGraph.strings */, + DD6ED7EF2CB69FFB007B3B5D /* LocalCompositeGraph.swift */, + ); + path = LocalCompositeGraph; + sourceTree = ""; + }; + DDFD50242C205BDF0050B344 /* TransparentRender */ = { + isa = PBXGroup; + children = ( + DDFD50252C205BF20050B344 /* TransparentRender.swift */, + DDFD50272C205CF40050B344 /* TransparentRender.storyboard */, + ); + path = TransparentRender; + sourceTree = ""; + }; E7163F7B2964149800EBBD55 /* ARKit */ = { isa = PBXGroup; children = ( @@ -1178,14 +1233,14 @@ path = KtvCopyrightMusic; sourceTree = ""; }; - E726BFF42A949F70006870E2 /* AuidoRouterPlayer */ = { + E726BFF42A949F70006870E2 /* AudioRouterPlayer */ = { isa = PBXGroup; children = ( - E726BFF52A949F70006870E2 /* AuidoRouterPlayer.strings */, - E726BFF72A949F70006870E2 /* AuidoRouterPlayer.swift */, - E726BFF82A949F70006870E2 /* AuidoRouterPlayer.storyboard */, + E726BFF52A949F70006870E2 /* AudioRouterPlayer.strings */, + E726BFF72A949F70006870E2 /* AudioRouterPlayer.swift */, + E726BFF82A949F70006870E2 /* AudioRouterPlayer.storyboard */, ); - path = AuidoRouterPlayer; + path = AudioRouterPlayer; sourceTree = ""; }; E726BFFD2A96FD3A006870E2 /* AudioWaveform */ = { @@ -1201,7 +1256,13 @@ E728B84128B5FFCB00674A4A /* PictureInPicture */ = { isa = PBXGroup; children = ( - F70DE4D52C6627C100FFC922 /* PictureInPicture.swift */, + F728B9CE2CA295D7007813BB /* ChannelViewController.swift */, + F728B9D12CA295D7007813BB /* CustomViewPIPViewController */, + F728B9CD2CA295D7007813BB /* PictureInPicture.storyboard */, + F728B9D22CA295D7007813BB /* PIPBaseViewController.swift */, + F728B9D62CA295D7007813BB /* PixelBufferPIPViewController */, + E728B84428B5FFCB00674A4A /* PictureInPicture.swift */, + F728B9DF2CA29625007813BB /* PictureInPicture.strings */, ); path = PictureInPicture; sourceTree = ""; @@ -1444,6 +1505,25 @@ path = Manager; sourceTree = ""; }; + F728B9D12CA295D7007813BB /* CustomViewPIPViewController */ = { + isa = PBXGroup; + children = ( + F728B9CF2CA295D7007813BB /* CustomViewPIPService.swift */, + F728B9D02CA295D7007813BB /* CustomViewPIPViewController.swift */, + ); + path = CustomViewPIPViewController; + sourceTree = ""; + }; + F728B9D62CA295D7007813BB /* PixelBufferPIPViewController */ = { + isa = PBXGroup; + children = ( + F728B9D32CA295D7007813BB /* PixelBufferPIPService.swift */, + F728B9D42CA295D7007813BB /* PixelBufferPIPViewController.swift */, + F728B9D52CA295D7007813BB /* PixelBufferRenderView.swift */, + ); + path = PixelBufferPIPViewController; + sourceTree = ""; + }; FD17F473C6A05604A44BDDDE /* Pods */ = { isa = PBXGroup; children = ( @@ -1615,8 +1695,9 @@ 033A9F3F252D89BC00BC26E1 /* RTMPStreaming.storyboard in Resources */, 033A9F2A252D737900BC26E1 /* Localizable.strings in Resources */, E726C0032A96FD3A006870E2 /* AudioWaveform.strings in Resources */, + F728B9E02CA29625007813BB /* PictureInPicture.strings in Resources */, 8BE7ABC2279E065000DFBCEF /* FusionCDN.strings in Resources */, - E726BFFC2A949F70006870E2 /* AuidoRouterPlayer.storyboard in Resources */, + E726BFFC2A949F70006870E2 /* AudioRouterPlayer.storyboard in Resources */, 576EA54825AC3523000B3D79 /* CustomPcmAudioSource.storyboard in Resources */, E728B85828B86B0700674A4A /* CustomVideoSourcePushMulti.strings in Resources */, 6709B23C2806BB4A000BCC58 /* RawAudioData.storyboard in Resources */, @@ -1655,6 +1736,7 @@ E7163F832964149800EBBD55 /* ARKit.storyboard in Resources */, 8BA5459526AFEC8D00ED4295 /* SimpleFilter.storyboard in Resources */, E7163F812964149800EBBD55 /* ARKit.strings in Resources */, + DD6ED7F12CB69FFB007B3B5D /* LocalCompositeGraph.storyboard in Resources */, 67CB2F0D27EB318200CB19D2 /* SpatialAudio.storyboard in Resources */, 8BA5459426AFEC8D00ED4295 /* SimpleFilter.strings in Resources */, 033A9F4D252D89DB00BC26E1 /* CustomAudioSource.storyboard in Resources */, @@ -1665,17 +1747,21 @@ 033A9F84252D8B6400BC26E1 /* StreamEncryption.storyboard in Resources */, 67B8C7B628057D1500195106 /* RawVideoData.storyboard in Resources */, E726C0042A96FD3A006870E2 /* AudioWaveform.storyboard in Resources */, + DD6ED7F22CB69FFB007B3B5D /* LocalCompositeGraph.strings in Resources */, 033A9F30252D860100BC26E1 /* JoinChannelAudio.storyboard in Resources */, 033A9F75252D8B4800BC26E1 /* ScreenShare.storyboard in Resources */, 671BD6B827E1DB2D0076D5E1 /* CustomAudioRender.strings in Resources */, + DDFD50282C205CF40050B344 /* TransparentRender.storyboard in Resources */, 033A9F5C252D89FD00BC26E1 /* CustomVideoSourcePush.storyboard in Resources */, 03D13BD92448758B00B599B3 /* Assets.xcassets in Resources */, - E726BFFA2A949F70006870E2 /* AuidoRouterPlayer.strings in Resources */, + DDFD502A2C2065D00050B344 /* yuv_limit_range_alpha_1280_540_right.mp4 in Resources */, + E726BFFA2A949F70006870E2 /* AudioRouterPlayer.strings in Resources */, 033A9F6B252D8B3500BC26E1 /* MediaChannelRelay.storyboard in Resources */, 671BD6B927E1DB2D0076D5E1 /* CustomAudioRender.storyboard in Resources */, E728B85128B60D5B00674A4A /* VideoViewSampleBufferDisplayView.xib in Resources */, E7A49D362907EB6000F06DD4 /* SENSEME.lic in Resources */, 03D13BD72448758900B599B3 /* Main.storyboard in Resources */, + F728B9D72CA295D7007813BB /* PictureInPicture.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1807,13 +1893,15 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + DD6ED7F32CB69FFB007B3B5D /* LocalCompositeGraph.swift in Sources */, 8BA5459626AFEC8D00ED4295 /* SimpleFilter.swift in Sources */, 036CBA47251990B400D74FAD /* AgoraCustomEncryption.h in Sources */, - E726BFFB2A949F70006870E2 /* AuidoRouterPlayer.swift in Sources */, + E726BFFB2A949F70006870E2 /* AudioRouterPlayer.swift in Sources */, E7A49D4E29090FB800F06DD4 /* BEResourceHelper.m in Sources */, E7A49D232907DD9A00F06DD4 /* EffectsCommonObject.m in Sources */, E7A49D652909111400F06DD4 /* BEHttpRequestProvider.mm in Sources */, E7A49CFB29029E0000F06DD4 /* ThirdBeautify.swift in Sources */, + F728B9D92CA295D7007813BB /* CustomViewPIPService.swift in Sources */, 036CBA3F2519186300D74FAD /* StreamEncryption.swift in Sources */, E7A49D5129090FCC00F06DD4 /* BEEffectManager.mm in Sources */, E76347D32AAEF4AF005D130F /* BeautyAPI.m in Sources */, @@ -1847,11 +1935,12 @@ 0339BE64251DCA3B007D4FDD /* GlobalSettings.swift in Sources */, E728B85A28B86B0700674A4A /* CustomVideoSourcePushMulti.swift in Sources */, E728B84C28B6015800674A4A /* AgoraPictureInPictureController.m in Sources */, + F728B9D82CA295D7007813BB /* ChannelViewController.swift in Sources */, 8407E0942472320800AC5DE8 /* (null) in Sources */, 8B5E5B50274CB68E0040E97D /* RhythmPlayer.swift in Sources */, E7A49D342907E74A00F06DD4 /* BundleUtil.m in Sources */, 036C42B524D2A3C600A59000 /* AgoraMetalRender.swift in Sources */, - F70DE4D72C6627C100FFC922 /* PictureInPicture.swift in Sources */, + F728B9DC2CA295D7007813BB /* PixelBufferPIPService.swift in Sources */, E72055EA28F943520030E6D1 /* Util.swift in Sources */, E74788AE29C7FB6900CD7415 /* JoinChannelVideoRecorder.swift in Sources */, E7A49D41290907E200F06DD4 /* BytedEffectVC.m in Sources */, @@ -1863,12 +1952,16 @@ E7A49D282907DDFF00F06DD4 /* EffectsDetector.m in Sources */, 576EA54225AC3310000B3D79 /* CustomPcmAudioSource.swift in Sources */, E7A49D0B29067F8300F06DD4 /* SenseBeautifyVC.m in Sources */, + F728B9DB2CA295D7007813BB /* PIPBaseViewController.swift in Sources */, E7A49D202907DD8F00F06DD4 /* EffectsAttribute.m in Sources */, + DDFD50262C205BF20050B344 /* TransparentRender.swift in Sources */, E7A49D6B2909115200F06DD4 /* BEEffectResourceHelper.m in Sources */, E7A49D572909101D00F06DD4 /* BEImageUtils.m in Sources */, 8BC751D7273E502700552265 /* LiveStreaming.swift in Sources */, + F728B9DD2CA295D7007813BB /* PixelBufferPIPViewController.swift in Sources */, E721600F28D3314B006431BD /* AlertManager.swift in Sources */, E7A49CFF29029E0000F06DD4 /* FUManager.m in Sources */, + DD8A1F7C2CA5048D001CEC51 /* AgoraPCMPlayer.swift in Sources */, 0339D6D424E91BAA008739CD /* QuickSwitchChannelVCItem.swift in Sources */, E7163F822964149800EBBD55 /* ARKit.swift in Sources */, 036C42B624D2A3C600A59000 /* AgoraMetalShader.metal in Sources */, @@ -1897,6 +1990,7 @@ E76347D12AAEF4AF005D130F /* SenseBeautyRender.m in Sources */, 8BC751DA273E57C900552265 /* VideoProcess.swift in Sources */, E7AD0DE329C95EB500C9A4B0 /* PickerView.swift in Sources */, + E728B84828B5FFCB00674A4A /* PictureInPicture.swift in Sources */, 5744CE0925BA99FF0099AB66 /* VideoChat.swift in Sources */, 034C625E2524A06800296ECF /* VoiceChanger.swift in Sources */, E7A49D2B2907DEE600F06DD4 /* EFMotionManager.m in Sources */, @@ -1910,6 +2004,7 @@ E7A49CFE29029E0000F06DD4 /* FUBeautifyVC.m in Sources */, E77902672A484A8A008791AD /* KFMP4Demuxer.m in Sources */, 0385767E2521E5A0003C369A /* MediaChannelRelay.swift in Sources */, + F728B9DA2CA295D7007813BB /* CustomViewPIPViewController.swift in Sources */, 03BCEC762449EB5000ED7177 /* LogViewController.swift in Sources */, E7A49CBA29011E7500F06DD4 /* MutliCamera.swift in Sources */, 0339D6D624E91CEB008739CD /* QuickSwitchChannel.swift in Sources */, @@ -1917,6 +2012,7 @@ A7847F942458089E00469187 /* AgoraExtension.swift in Sources */, E74877BA28A23C1400CA2F58 /* JSONObject.swift in Sources */, 03414B5325546C7200AB114D /* AgoraYUVImageSourcePush.m in Sources */, + F728B9DE2CA295D7007813BB /* PixelBufferRenderView.swift in Sources */, 03BCEC50244938C500ED7177 /* BaseViewController.swift in Sources */, 67CB2F0C27EB318200CB19D2 /* SpatialAudio.swift in Sources */, ); @@ -2258,6 +2354,22 @@ name = Popover.storyboard; sourceTree = ""; }; + DD6ED7EC2CB69FFB007B3B5D /* LocalCompositeGraph.storyboard */ = { + isa = PBXVariantGroup; + children = ( + DD6ED7EB2CB69FFB007B3B5D /* Base */, + ); + name = LocalCompositeGraph.storyboard; + sourceTree = ""; + }; + DD6ED7EE2CB69FFB007B3B5D /* LocalCompositeGraph.strings */ = { + isa = PBXVariantGroup; + children = ( + DD6ED7ED2CB69FFB007B3B5D /* zh-Hans */, + ); + name = LocalCompositeGraph.strings; + sourceTree = ""; + }; E7163F7C2964149800EBBD55 /* ARKit.strings */ = { isa = PBXVariantGroup; children = ( @@ -2274,20 +2386,20 @@ name = ARKit.storyboard; sourceTree = ""; }; - E726BFF52A949F70006870E2 /* AuidoRouterPlayer.strings */ = { + E726BFF52A949F70006870E2 /* AudioRouterPlayer.strings */ = { isa = PBXVariantGroup; children = ( E726BFF62A949F70006870E2 /* zh-Hans */, ); - name = AuidoRouterPlayer.strings; + name = AudioRouterPlayer.strings; sourceTree = ""; }; - E726BFF82A949F70006870E2 /* AuidoRouterPlayer.storyboard */ = { + E726BFF82A949F70006870E2 /* AudioRouterPlayer.storyboard */ = { isa = PBXVariantGroup; children = ( E726BFF92A949F70006870E2 /* Base */, ); - name = AuidoRouterPlayer.storyboard; + name = AudioRouterPlayer.storyboard; sourceTree = ""; }; E726BFFE2A96FD3A006870E2 /* AudioWaveform.strings */ = { @@ -2471,12 +2583,9 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = G726234S43; + DEVELOPMENT_TEAM = YS397FG5PA; ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/APIExample", - ); + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = "Agora-ScreenShare-Extension/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( @@ -2508,12 +2617,9 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = G726234S43; + DEVELOPMENT_TEAM = YS397FG5PA; ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/APIExample", - ); + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = "Agora-ScreenShare-Extension/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( @@ -2667,12 +2773,9 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 2; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DEVELOPMENT_TEAM = G726234S43; + DEVELOPMENT_TEAM = YS397FG5PA; ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/APIExample", - ); + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = APIExample/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( @@ -2741,12 +2844,9 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 2; - DEVELOPMENT_TEAM = G726234S43; + DEVELOPMENT_TEAM = YS397FG5PA; ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/APIExample", - ); + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = APIExample/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/iOS/APIExample/APIExample/Common/AgoraExtension.swift b/iOS/APIExample/APIExample/Common/AgoraExtension.swift index 034c96006..a8be4e36d 100644 --- a/iOS/APIExample/APIExample/Common/AgoraExtension.swift +++ b/iOS/APIExample/APIExample/Common/AgoraExtension.swift @@ -225,6 +225,24 @@ extension AUDIO_AINS_MODE { } } +extension AgoraVoiceAITunerType { + func description() -> String { + switch self { + case .matureMale: return "AI_Tunner_Mature_Male".localized + case .freshMale: return "AI_Tunner_Fresh_Male".localized + case .elegantFemale: return "AI_Tunner_Elegant_Female".localized + case .sweetFemale: return "AI_Tunner_Sweet_Female".localized + case .warmMaleSinging: return "AI_Tunner_Warm_Male_Singing".localized + case .gentleFemaleSinging: return "AI_Tunner_Gentle_Female_Singing".localized + case .huskyMaleSinging: return "AI_Tunner_Husky_Male_Singing".localized + case .warmElegantFemaleSinging: return "AI_Tunner_Warm_Elegant_Female_Singing".localized + case .powerfulMaleSinging: return "AI_Tunner_Powerful_Male_Singing".localized + case .dreamyFemaleSinging: return "AI_Tunner_Dreamy_Female_Singing".localized + @unknown default: return "\(self.rawValue)" + } + } +} + extension AgoraVoiceConversionPreset { func description() -> String { switch self { @@ -299,7 +317,7 @@ extension Date { extension AgoraFocalLengthInfo { var value: [String: AgoraFocalLength] { - let title = cameraDirection == 0 ? "Front camera".localized + " - " : "Rear camera".localized + " - " + let title = cameraDirection == 1 ? "Front camera".localized + " - " : "Rear camera".localized + " - " switch focalLengthType { case .default: return [title + "Default".localized: focalLengthType] case .wide: return [title + "Wide".localized: focalLengthType] @@ -309,3 +327,27 @@ extension AgoraFocalLengthInfo { } } } + +extension AgoraApplicationScenarioType { + func description() -> String { + switch self { + case .applicationGeneralScenario: return "General".localized + case .applicationMeetingScenario: return "Meeting".localized + case .application1V1Scenario: return "1v1".localized + case .applicationLiveShowScenario: return "Live Show".localized + @unknown default: return "" + } + } +} + +extension AgoraVideoModulePosition { + func description() -> String { + switch self { + case .postCapture: return "Post Capture".localized + case .preRenderer: return "PreRenderer".localized + case .preEncoder: return "PreEncoder".localized + case .postCaptureOrigin: return "Post Capture Origin".localized + @unknown default: return "" + } + } +} diff --git a/iOS/APIExample/APIExample/Common/AlertManager.swift b/iOS/APIExample/APIExample/Common/AlertManager.swift index 6a2cd0499..be3c8930e 100644 --- a/iOS/APIExample/APIExample/Common/AlertManager.swift +++ b/iOS/APIExample/APIExample/Common/AlertManager.swift @@ -8,8 +8,26 @@ import UIKit import AVFoundation -let cl_screenWidht = UIScreen.main.bounds.width -let cl_screenHeight = UIScreen.main.bounds.height +extension UIScreen { + static var currentScreenSize: CGSize { + // Get the main window's available size + guard let window = UIApplication.shared.windows.first(where: { $0.isKeyWindow }) else { + return CGSize.zero + } + + // Calculate available width and height + let bounds = window.bounds + let insets = window.safeAreaInsets + + let width = bounds.width - insets.left - insets.right + let height = bounds.height - insets.top - insets.bottom + + return CGSize(width: width, height: height) + } +} + +let cl_screenWidht = UIScreen.currentScreenSize.width +let cl_screenHeight = UIScreen.currentScreenSize.height class AlertManager: NSObject { private struct AlertViewCache { var view: UIView? diff --git a/iOS/APIExample/APIExample/Common/BaseViewController.swift b/iOS/APIExample/APIExample/Common/BaseViewController.swift index 9167e6ad7..071cf66cf 100644 --- a/iOS/APIExample/APIExample/Common/BaseViewController.swift +++ b/iOS/APIExample/APIExample/Common/BaseViewController.swift @@ -36,6 +36,16 @@ class BaseViewController: AGViewController { self.present(alertController, animated: true, completion: nil) } + func presentAlertViewController(_ alertVC: UIAlertController) { + // 判断设备类型 + if UIDevice.current.userInterfaceIdiom == .pad { + // iPad 上需要提供位置信息 + alertVC.popoverPresentationController?.sourceView = self.view // 设置源视图 + alertVC.popoverPresentationController?.sourceRect = CGRect(x: self.view.bounds.size.width / 2.0, y: self.view.bounds.size.height / 2.0, width: 1.0, height: 1.0) // 设置源矩形 + } + present(alertVC, animated: true, completion: nil) + } + func getAudioLabel(uid: UInt, isLocal: Bool) -> String { return "AUDIO ONLY\n\(isLocal ? "Local" : "Remote")\n\(uid)" } diff --git a/iOS/APIExample/APIExample/Common/ExternalAudio/AgoraPCMPlayer.swift b/iOS/APIExample/APIExample/Common/ExternalAudio/AgoraPCMPlayer.swift new file mode 100644 index 000000000..8f469bf54 --- /dev/null +++ b/iOS/APIExample/APIExample/Common/ExternalAudio/AgoraPCMPlayer.swift @@ -0,0 +1,56 @@ +// +// AgoraPCMPlayer.swift +// APIExample +// +// Created by wushengtao on 2024/9/26. +// Copyright © 2024 Agora Corp. All rights reserved. +// + +import Foundation + +class AgoraPCMPlayer { + private var audioEngine: AVAudioEngine + private var playerNode: AVAudioPlayerNode + private var sampleRate: Double + private var channels: AVAudioChannelCount + + init(sampleRate: Double, channels: AVAudioChannelCount) { + self.sampleRate = sampleRate + self.channels = channels + + audioEngine = AVAudioEngine() + playerNode = AVAudioPlayerNode() + + audioEngine.attach(playerNode) + + let format = AVAudioFormat(standardFormatWithSampleRate: sampleRate, channels: channels) + audioEngine.connect(playerNode, to: audioEngine.mainMixerNode, format: format) + + do { + try audioEngine.start() + } catch { + print("Audio Engine failed to start: \(error)") + } + } + + func playPCMData(pcmData: UnsafeMutablePointer, count: UInt) { + guard let format = AVAudioFormat(standardFormatWithSampleRate: sampleRate, channels: channels), + let audioBuffer = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: AVAudioFrameCount(count / 4)), // 16位立体声每帧4字节 + let channelData = audioBuffer.floatChannelData else { + return + } + + audioBuffer.frameLength = AVAudioFrameCount(count / 4) + + for frame in 0.. Void) { diff --git a/iOS/APIExample/APIExample/Examples/Advanced/AuidoRouterPlayer/AuidoRouterPlayer.swift b/iOS/APIExample/APIExample/Examples/Advanced/AudioRouterPlayer/AudioRouterPlayer.swift similarity index 89% rename from iOS/APIExample/APIExample/Examples/Advanced/AuidoRouterPlayer/AuidoRouterPlayer.swift rename to iOS/APIExample/APIExample/Examples/Advanced/AudioRouterPlayer/AudioRouterPlayer.swift index 8c4032344..d4d9b7e02 100644 --- a/iOS/APIExample/APIExample/Examples/Advanced/AuidoRouterPlayer/AuidoRouterPlayer.swift +++ b/iOS/APIExample/APIExample/Examples/Advanced/AudioRouterPlayer/AudioRouterPlayer.swift @@ -8,23 +8,29 @@ import UIKit import AGEVideoLayout import AgoraRtcKit +import AVKit + +#if canImport(IJKMediaFramework) import IJKMediaFramework +#elseif canImport(MobileVLCKit) +import MobileVLCKit +#endif enum ThirdPlayerType: String { - case ijk = "ijkplayer" + case vendor = "thirdPartyPlayer" case origin = "avplayer" } -class AuidoRouterPlayerEntry: UIViewController { +class AudioRouterPlayerEntry: UIViewController { @IBOutlet weak var joinButton: UIButton! @IBOutlet weak var channelTextField: UITextField! - let identifier = "AuidoRouterPlayer" + let identifier = "AudioRouterPlayer" @IBOutlet var resolutionBtn: UIButton! @IBOutlet var fpsBtn: UIButton! @IBOutlet var orientationBtn: UIButton! @IBOutlet weak var chosePlayerButton: UIButton! var width: Int = 960, height: Int = 540, orientation: AgoraVideoOutputOrientationMode = .adaptative, fps = 15 - private var playerType: ThirdPlayerType = .ijk + private var playerType: ThirdPlayerType = .origin override func viewDidLoad() { super.viewDidLoad() @@ -32,10 +38,10 @@ class AuidoRouterPlayerEntry: UIViewController { @IBAction func onChosePlayerType(_ sender: UIButton) { let style: UIAlertController.Style = UIDevice.current.userInterfaceIdiom == .pad ? .alert : .actionSheet - let alert = UIAlertController(title: "Player Type(ijkplayer/avplayer)".localized, + let alert = UIAlertController(title: "Player Type(thirdPartyPlayer/avplayer)".localized, message: nil, preferredStyle: style) - alert.addAction(getPlayerAction(ThirdPlayerType.ijk.rawValue)) + alert.addAction(getPlayerAction(ThirdPlayerType.vendor.rawValue)) alert.addAction(getPlayerAction(ThirdPlayerType.origin.rawValue)) alert.addCancelAction() present(alert, animated: true, completion: nil) @@ -43,7 +49,7 @@ class AuidoRouterPlayerEntry: UIViewController { func getPlayerAction(_ title: String) -> UIAlertAction { return UIAlertAction(title: title, style: .default, handler: { [unowned self] _ in self.chosePlayerButton.setTitle(title, for: .normal) - self.playerType = ThirdPlayerType(rawValue: title) ?? .ijk + self.playerType = ThirdPlayerType(rawValue: title) ?? .vendor }) } func getResolutionAction(width: Int, height: Int) -> UIAlertAction { @@ -121,14 +127,15 @@ class AuidoRouterPlayerEntry: UIViewController { } } -class AuidoRouterPlayerMain: BaseViewController { +class AudioRouterPlayerMain: BaseViewController { var localVideo = Bundle.loadVideoView(type: .local, audioOnly: false) @IBOutlet weak var playerView: UIView! @IBOutlet weak var speakerSwitch: UISwitch! @IBOutlet weak var container: AGEVideoContainer! var agoraKit: AgoraRtcEngineKit! private let videoString = "https://agora-adc-artifacts.s3.cn-north-1.amazonaws.com.cn/resources/sample.mp4" - private lazy var ijkPlayer: IJKAVMoviePlayerController? = { + private lazy var vendorPlayer: Any? = { +#if canImport(IJKMediaFramework) let player = IJKAVMoviePlayerController(contentURL: URL(string: videoString)) player?.view.autoresizingMask = [.flexibleWidth, .flexibleHeight] player?.view.frame = playerView.bounds @@ -139,7 +146,20 @@ class AuidoRouterPlayerMain: BaseViewController { player?.allowsMediaAirPlay = true player?.isDanmakuMediaAirPlay = true return player +#elseif canImport(MobileVLCKit) + let player = VLCMediaPlayer() + if let url = URL(string: videoString) { + let media = VLCMedia(url: url) + player.media = media + } + let videoView = UIView(frame: playerView.bounds) + player.drawable = videoView + player.play() + return player +#endif + return nil }() + private lazy var avPlayer: AVPlayerViewController? = { guard let url = URL(string: videoString) else { return nil } let player = AVPlayer(url: url) @@ -232,16 +252,21 @@ class AuidoRouterPlayerMain: BaseViewController { override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) let playerType = ThirdPlayerType(rawValue: configs["playerType"] as? String ?? "") - if playerType == .ijk { - setupIJKPlayer() - } else { + if playerType == .origin { setupAVPlayer() + } else { + setupVendorPlayer() } } - private func setupIJKPlayer() { - guard let ijkPlayerView = ijkPlayer?.view else { return } + private func setupVendorPlayer() { +#if canImport(IJKMediaFramework) + guard let ijkPlayerView = (vendorPlayer as? IJKAVMoviePlayerController)?.view else { return } playerView.addSubview(ijkPlayerView) +#elseif canImport(MobileVLCKit) + guard let vlcPlayerView = (vendorPlayer as? VLCMediaPlayer)?.drawable as? UIView else { return } + playerView.addSubview(vlcPlayerView) +#endif } private func setupAVPlayer() { @@ -268,12 +293,16 @@ class AuidoRouterPlayerMain: BaseViewController { if playerType == .origin { avPlayer?.player?.pause() } else { - ijkPlayer?.shutdown() +#if canImport(IJKMediaFramework) + (vendorPlayer as? IJKAVMoviePlayerController)?.shutdown() +#elseif canImport(MobileVLCKit) + (vendorPlayer as? VLCMediaPlayer)?.stop() +#endif } } } -extension AuidoRouterPlayerMain: AVPlayerViewControllerDelegate { +extension AudioRouterPlayerMain: AVPlayerViewControllerDelegate { func playerViewController(_ playerViewController: AVPlayerViewController, willEndFullScreenPresentationWithAnimationCoordinator coordinator: UIViewControllerTransitionCoordinator) { // The system pauses when returning from full screen, we need to 'resume' manually. @@ -284,7 +313,7 @@ extension AuidoRouterPlayerMain: AVPlayerViewControllerDelegate { } /// agora rtc engine delegate events -extension AuidoRouterPlayerMain: AgoraRtcEngineDelegate { +extension AudioRouterPlayerMain: 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: diff --git a/iOS/APIExample/APIExample/Examples/Advanced/AuidoRouterPlayer/Base.lproj/AuidoRouterPlayer.storyboard b/iOS/APIExample/APIExample/Examples/Advanced/AudioRouterPlayer/Base.lproj/AudioRouterPlayer.storyboard similarity index 95% rename from iOS/APIExample/APIExample/Examples/Advanced/AuidoRouterPlayer/Base.lproj/AuidoRouterPlayer.storyboard rename to iOS/APIExample/APIExample/Examples/Advanced/AudioRouterPlayer/Base.lproj/AudioRouterPlayer.storyboard index 826971e21..765cd6cb5 100644 --- a/iOS/APIExample/APIExample/Examples/Advanced/AuidoRouterPlayer/Base.lproj/AuidoRouterPlayer.storyboard +++ b/iOS/APIExample/APIExample/Examples/Advanced/AudioRouterPlayer/Base.lproj/AudioRouterPlayer.storyboard @@ -1,18 +1,18 @@ - + - + - + - + @@ -33,9 +33,9 @@ + + + @@ -74,11 +95,15 @@ + + + + @@ -93,9 +118,11 @@ + + @@ -129,7 +156,7 @@ - + - + - + + + + - + + @@ -396,6 +456,24 @@ + + + + + + + + + + + + + + + + + + @@ -403,13 +481,18 @@ + + + + + @@ -421,15 +504,20 @@ + - + + + + + @@ -437,6 +525,7 @@ + @@ -444,6 +533,8 @@ + + diff --git a/iOS/APIExample/APIExample/Examples/Advanced/LiveStreaming/LiveStreaming.swift b/iOS/APIExample/APIExample/Examples/Advanced/LiveStreaming/LiveStreaming.swift index 76b58a29e..04b3c45ef 100644 --- a/iOS/APIExample/APIExample/Examples/Advanced/LiveStreaming/LiveStreaming.swift +++ b/iOS/APIExample/APIExample/Examples/Advanced/LiveStreaming/LiveStreaming.swift @@ -13,10 +13,23 @@ class LiveStreamingEntry: UIViewController { @IBOutlet weak var joinButton: UIButton! @IBOutlet weak var preloadButton: UIButton! @IBOutlet weak var channelTextField: UITextField! + @IBOutlet weak var cameraButton: UIButton? + @IBOutlet weak var videoScenarioButton: UIButton? let identifier = "LiveStreaming" var role: AgoraClientRole = .broadcaster private var isFirstFrame: Bool = false private var backgroundColor: UInt32 = 0x000000 + private var cameraKey: String? + + private lazy var agoraKit: AgoraRtcEngineKit = { + let config = AgoraRtcEngineConfig() + config.appId = KeyCenter.AppId + config.channelProfile = .liveBroadcasting + let kit = AgoraRtcEngineKit.sharedEngine(with: config, delegate: nil) + Util.configPrivatization(agoraKit: kit) + kit.setLogFile(LogUtils.sdkLogPath()) + return kit + }() override func viewDidLoad() { super.viewDidLoad() @@ -24,6 +37,12 @@ class LiveStreamingEntry: UIViewController { preloadButton.setTitle("cancel preload".localized, for: .selected) } + override func willMove(toParent parent: UIViewController?) { + if parent == nil { + AgoraRtcEngineKit.destroy() + } + } + func getRoleAction(_ role: AgoraClientRole) -> UIAlertAction { return UIAlertAction(title: "\(role.description())", style: .default, handler: { [unowned self] _ in self.role = role @@ -64,6 +83,7 @@ class LiveStreamingEntry: UIViewController { } @IBAction func doChoseBackgroundColor(_ sender: UIButton) { + self.view.endEditing(true) let pickerView = PickerView() let colors = ["Red".localized: 0xff0d00ff, "Blue".localized: 0x0400ffff, @@ -93,6 +113,43 @@ class LiveStreamingEntry: UIViewController { present(alert, animated: true, completion: nil) } + @IBAction func onTapCameraFocalButton(_ sender: UIButton) { + let infos = agoraKit.queryCameraFocalLengthCapability() + let pickerView = PickerView() + let params = infos?.flatMap({ $0.value }) + pickerView.dataArray = params?.map({ $0.key }) + AlertManager.show(view: pickerView, alertPostion: .bottom) + pickerView.pickerViewSelectedValueClosure = { [weak self] key in + guard let self = self else { return } + let type = params?.first(where: { $0.key == key })?.value ?? .default + let config = AgoraCameraCapturerConfiguration() + config.cameraFocalLengthType = type + config.cameraDirection = key.contains("Front camera".localized) ? .front : .rear + sender.setTitle(key, for: .normal) + self.agoraKit.setCameraCapturerConfiguration(config) + self.cameraKey = key + } + } + + @IBAction func onTapVideoScenarioButton(_ sender: UIButton) { + let pickerView = PickerView() + pickerView.dataArray = [ + AgoraApplicationScenarioType.applicationGeneralScenario.description(), + AgoraApplicationScenarioType.applicationMeetingScenario.description(), + AgoraApplicationScenarioType.application1V1Scenario.description(), + AgoraApplicationScenarioType.applicationLiveShowScenario.description() + ] + AlertManager.show(view: pickerView, alertPostion: .bottom) + pickerView.pickerViewSelectedValueClosure = { [weak self, weak pickerView] key in + guard let self = self else { return } + let idx = pickerView?.dataArray?.firstIndex(where: { $0 == key}) ?? 0 + let type = AgoraApplicationScenarioType(rawValue: idx) ?? .applicationGeneralScenario + let ret = self.agoraKit.setVideoScenario(type) + print("setVideoScenario[\(type.rawValue)] ret = \(ret)") + self.videoScenarioButton?.setTitle(key, for: .normal) + } + } + func doJoin() { guard let channelName = channelTextField.text else { return } let storyBoard: UIStoryboard = UIStoryboard(name: identifier, bundle: nil) @@ -104,11 +161,23 @@ class LiveStreamingEntry: UIViewController { "role": self.role, "isFirstFrame": isFirstFrame, "isPreloadChannel": preloadButton.isSelected, - "backgroundColor": backgroundColor] + "backgroundColor": backgroundColor, + "cameraKey": cameraKey ?? "", + "engine": agoraKit] navigationController?.pushViewController(newViewController, animated: true) } + + override func touchesEnded(_ touches: Set, with event: UIEvent?) { + super.touchesEnded(touches, with: event) + self.view.endEditing(true) + } } +private let stabilizationModeParams: [[String: AgoraCameraStabilizationMode]] = [["off": .off], + ["auto": .auto], + ["level1": .level1], + ["level2": .level2], + ["level3": .level3]] class LiveStreamingMain: BaseViewController { var foregroundVideo = Bundle.loadVideoView(type: .local, audioOnly: false) var backgroundVideo = Bundle.loadVideoView(type: .remote, audioOnly: false) @@ -127,6 +196,9 @@ class LiveStreamingMain: BaseViewController { @IBOutlet weak var videoImageContainer: UIView! @IBOutlet weak var centerStageContainerView: UIView! @IBOutlet weak var CameraFocalButton: UIButton! + @IBOutlet weak var cameraStabilizationButton: UIButton? + @IBOutlet weak var localRenderTextField: UITextField? + @IBOutlet weak var remoteRenderTextField: UITextField? var remoteUid: UInt? { didSet { foregroundVideoContainer.isHidden = !(role == .broadcaster && remoteUid != nil) @@ -169,14 +241,21 @@ class LiveStreamingMain: BaseViewController { foregroundVideo.bindFrameToSuperviewBounds() backgroundVideo.bindFrameToSuperviewBounds() + localRenderTextField?.addTarget(self, action: #selector(textFieldDidChange), for: .editingChanged) + remoteRenderTextField?.addTarget(self, action: #selector(textFieldDidChange), for: .editingChanged) + + let modeKey = stabilizationModeParams.first?.keys.first ?? "" + cameraStabilizationButton?.setTitle("\("CameraStabilizationMode".localized) \(modeKey)", for: .normal) + // set up agora instance when view loadedlet config = AgoraRtcEngineConfig() - let config = AgoraRtcEngineConfig() - config.appId = KeyCenter.AppId - config.channelProfile = .liveBroadcasting - agoraKit = AgoraRtcEngineKit.sharedEngine(with: config, delegate: self) - // Configuring Privatization Parameters - Util.configPrivatization(agoraKit: agoraKit) - agoraKit.setLogFile(LogUtils.sdkLogPath()) + if let engine = configs["engine"] as? AgoraRtcEngineKit { + agoraKit = engine + agoraKit.delegate = self + } + + if let key = configs["cameraKey"] as? String, key.isEmpty == false { + CameraFocalButton.setTitle(key, for: .normal) + } if let isFirstFrame = configs["isFirstFrame"] as? Bool, isFirstFrame == true { agoraKit.enableInstantMediaRendering() @@ -291,35 +370,31 @@ class LiveStreamingMain: BaseViewController { self.agoraKit.switchCamera() } sender.setTitle(key, for: .normal) - DispatchQueue.main.asyncAfter(deadline: .now() + 1.0, execute: DispatchWorkItem(block: { - self.agoraKit.setCameraCapturerConfiguration(config) - })) + self.agoraKit.setCameraCapturerConfiguration(config) self.cameraDirection = config.cameraDirection } } @IBAction func onTapCenterStage(_ sender: UISwitch) { if agoraKit.isCameraCenterStageSupported() { - let params: [String: AgoraCameraStabilizationMode] = ["auto": .auto, - "level1": .level1, - "level2": .level2, - "level3": .level3] - if sender.isOn { - let pickerView = PickerView() - pickerView.dataArray = params.map({ $0.key }).sorted() - AlertManager.show(view: pickerView, alertPostion: .bottom) - pickerView.pickerViewSelectedValueClosure = { [weak self] key in - DispatchQueue.global().async { - self?.agoraKit.setCameraStabilizationMode(params[key] ?? .auto) - } - } - } agoraKit.enableCameraCenterStage(sender.isOn) } else { showAlert(message: "This device does not support Center Stage".localized) sender.setOn(false, animated: false) } } + + @IBAction func onTapCameraStabilization(_ sender: UIButton) { + let pickerView = PickerView() + pickerView.dataArray = stabilizationModeParams.map({ $0.keys.first ?? "" }) + AlertManager.show(view: pickerView, alertPostion: .bottom) + pickerView.pickerViewSelectedValueClosure = { [weak self] key in + guard let map = stabilizationModeParams.filter({$0.keys.contains(key)}).first else {return} + sender.setTitle("\("CameraStabilizationMode".localized) \(key)", for: .normal) + self?.agoraKit.setCameraStabilizationMode(map[key] ?? .auto) + } + } + @IBAction func onTapVideoImageSwitch(_ sender: UISwitch) { let options = AgoraImageTrackOptions() let imgPath = Bundle.main.path(forResource: "agora-logo", ofType: "png") @@ -384,6 +459,29 @@ class LiveStreamingMain: BaseViewController { agoraKit.takeSnapshot(Int(remoteUid), filePath: path) showAlert(title: "Screenshot successful".localized, message: path) } + + @IBAction func onTakeLocalSnapshot(_ sender: Any) { + let pickerView = PickerView() + let values: [AgoraVideoModulePosition] = [ +// .postCapture, + .preRenderer, + .preEncoder, + .postCaptureOrigin + ] + pickerView.dataArray = values.map({ $0.description()}) + AlertManager.show(view: pickerView, alertPostion: .bottom) + pickerView.pickerViewSelectedValueClosure = { [weak self, weak pickerView] key in + guard let self = self else { return } + let idx = pickerView?.dataArray?.firstIndex(where: { $0 == key}) ?? 0 + let position = values[idx] + let config = AgoraSnapshotConfig() + config.position = position + config.filePath = NSTemporaryDirectory().appending("local_\(position.rawValue).png") + let ret = agoraKit.takeSnapshotWithConfig(0, config: config) + print("takeSnapshot ret: \(ret) path: \(config.filePath ?? "")") + showAlert(title: "Screenshot successful".localized, message: config.filePath ?? "") + } + } @IBAction func onTapForegroundVideo(_ sender: UIGestureRecognizer) { isLocalVideoForeground = !isLocalVideoForeground let localVideoCanvas = AgoraRtcVideoCanvas() @@ -437,13 +535,31 @@ class LiveStreamingMain: BaseViewController { if isJoined { agoraKit.disableVideo() agoraKit.disableAudio() + agoraKit.delegate = nil agoraKit.leaveChannel { (stats) -> Void in LogUtils.log(message: "left channel, duration: \(stats.duration)", level: .info) } - AgoraRtcEngineKit.destroy() } } } + + @objc func textFieldDidChange(_ textField: UITextField) { + if let text = textField.text, let number = Int(text) { + if number > 60 { + textField.text = "60" + } else if number == 0 { + textField.text = "" + } + } else { + textField.text = "" + } + + if textField == localRenderTextField { + agoraKit.setLocalRenderTargetFps(.camera, targetFps: Int32(textField.text ?? "") ?? 15) + } else { + agoraKit.setRemoteRenderTargetFps(Int32(textField.text ?? "") ?? 15) + } + } } /// agora rtc engine delegate events @@ -459,7 +575,7 @@ extension LiveStreamingMain: AgoraRtcEngineDelegate { } func rtcEngine(_ engine: AgoraRtcEngineKit, cameraFocusDidChangedTo rect: CGRect) { - ToastView.show(text: "The camera has changed".localized + " \(rect)") + LogUtils.log(message: "The camera has changed".localized + " \(rect)", level: .warning) } /// callback when error occured for agora sdk, you are recommended to display the error descriptions on demand @@ -477,6 +593,7 @@ extension LiveStreamingMain: AgoraRtcEngineDelegate { /// @param channel /// @param uid uid of local user /// @param elapsed time elapse since current sdk instance join the channel in ms + /// func rtcEngine(_ engine: AgoraRtcEngineKit, didJoinChannel channel: String, withUid uid: UInt, elapsed: Int) { isJoined = true foregroundVideo.statsInfo?.updateUid(uid: uid) diff --git a/iOS/APIExample/APIExample/Examples/Advanced/LiveStreaming/zh-Hans.lproj/LiveStreaming.strings b/iOS/APIExample/APIExample/Examples/Advanced/LiveStreaming/zh-Hans.lproj/LiveStreaming.strings index 724cc29ea..07532f2d2 100644 --- a/iOS/APIExample/APIExample/Examples/Advanced/LiveStreaming/zh-Hans.lproj/LiveStreaming.strings +++ b/iOS/APIExample/APIExample/Examples/Advanced/LiveStreaming/zh-Hans.lproj/LiveStreaming.strings @@ -9,13 +9,19 @@ /* Class = "UIButton"; normalTitle = "Join"; ObjectID = "kbN-ZR-nNn"; */ "kbN-ZR-nNn.normalTitle" = "加入频道"; -"w4q-aT-JBc.normalTitle" = "截图"; +"w4q-aT-JBc.normalTitle" = "本地截图"; +"lpn-6Z-VV3.normalTitle" = "远端截图"; "ohV-am-Acd.text" = "首帧出图"; "Q0N-nV-bez.normalTitle" = "默认背景色"; "S19-UR-C2c.normalTitle" = "预加载"; -"S19-UR-C2c.selectTitle" = "取消预加载"; "8kn-Rl-VMd.text" = "垫片推流"; "dZm-Rf-imt.normalTitle" = "相机对焦"; +"ug1-fz-GYz.normalTitle" = "相机对焦"; + +"ZB2-jf-zOV.normalTitle" = "视频业务场景"; + +"tOf-AP-HSe.placeholder" = "本地渲染帧率(1-60),默认15"; +"UFF-wU-Wze.placeholder" = "远端渲染帧率(1-60),默认15"; diff --git a/iOS/APIExample/APIExample/Examples/Advanced/PictureInPicture/Base.lproj/PictureInPicture.storyboard b/iOS/APIExample/APIExample/Examples/Advanced/LocalCompositeGraph/Base.lproj/LocalCompositeGraph.storyboard similarity index 74% rename from iOS/APIExample/APIExample/Examples/Advanced/PictureInPicture/Base.lproj/PictureInPicture.storyboard rename to iOS/APIExample/APIExample/Examples/Advanced/LocalCompositeGraph/Base.lproj/LocalCompositeGraph.storyboard index 84b72944c..1da29a8f6 100644 --- a/iOS/APIExample/APIExample/Examples/Advanced/PictureInPicture/Base.lproj/PictureInPicture.storyboard +++ b/iOS/APIExample/APIExample/Examples/Advanced/LocalCompositeGraph/Base.lproj/LocalCompositeGraph.storyboard @@ -1,18 +1,18 @@ - + - + - + - + @@ -59,30 +59,35 @@ - + - - + + - - + + - + + + + - + + + + + + + + + + - + diff --git a/iOS/APIExample/APIExample/Examples/Advanced/LocalCompositeGraph/LocalCompositeGraph.swift b/iOS/APIExample/APIExample/Examples/Advanced/LocalCompositeGraph/LocalCompositeGraph.swift new file mode 100644 index 000000000..8f11f643d --- /dev/null +++ b/iOS/APIExample/APIExample/Examples/Advanced/LocalCompositeGraph/LocalCompositeGraph.swift @@ -0,0 +1,290 @@ +// +// LocalCompositeGraph.swift +// APIExample +// +// Created by 张乾泽 on 2020/4/17. +// Copyright © 2020 Agora Corp. All rights reserved. +// +import UIKit +import AGEVideoLayout +import AgoraRtcKit + +class LocalCompositeGraphEntry: UIViewController { + @IBOutlet weak var joinButton: UIButton! + @IBOutlet weak var channelTextField: UITextField! + let identifier = "LocalCompositeGraph" + + override func viewDidLoad() { + super.viewDidLoad() + } + + @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] + navigationController?.pushViewController(newViewController, animated: true) + } +} + +class LocalCompositeGraphMain: BaseViewController { + var localVideo = Bundle.loadVideoView(type: .local, audioOnly: false) + + @IBOutlet weak var container: AGEVideoContainer! + var agoraKit: AgoraRtcEngineKit! + + // indicate if current instance has joined channel + var isJoined: Bool = false + + private lazy var screenParams: AgoraScreenCaptureParameters2 = { + let params = AgoraScreenCaptureParameters2() + params.captureVideo = true + params.captureAudio = true + let audioParams = AgoraScreenAudioParameters() + audioParams.captureSignalVolume = 50 + params.audioParams = audioParams + let videoParams = AgoraScreenVideoParameters() + videoParams.dimensions = screenShareVideoDimension() + videoParams.frameRate = .fps15 + videoParams.bitrate = AgoraVideoBitrateStandard + params.videoParams = videoParams + return params + }() + + private lazy var option: AgoraRtcChannelMediaOptions = { + let option = AgoraRtcChannelMediaOptions() + option.clientRoleType = GlobalSettings.shared.getUserRole() + option.publishCameraTrack = true + option.publishMicrophoneTrack = true + return option + }() + + private var systemBroadcastPicker: RPSystemBroadcastPickerView? + + override func viewDidLoad() { + super.viewDidLoad() + // layout render view + localVideo.setPlaceholder(text: "Local Host".localized) + container.layoutStream(views: [localVideo]) + + // 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) + + agoraKit.setLogFile(LogUtils.sdkLogPath()) + + // get channel name from configs + guard let channelName = configs["channelName"] as? String else {return} + + // make myself a broadcaster + agoraKit.setClientRole(GlobalSettings.shared.getUserRole()) + // enable video module and set up video encoding configs + agoraKit.enableVideo() + agoraKit.enableAudio() + + // 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.mirrorMode = .disabled + videoCanvas.renderMode = .fit + videoCanvas.sourceType = .transCoded + agoraKit.setupLocalVideo(videoCanvas) + // you have to call startPreview to see local video + agoraKit.startPreview() + + // Set audio route to speaker + agoraKit.setDefaultAudioRouteToSpeakerphone(true) + + //start screen capture + self.startScreenCapture() + + // start camera + let captureConfig = AgoraCameraCapturerConfiguration() + captureConfig.dimensions = CGSize(width: 100, height: 100) + agoraKit.startCameraCapture(.camera, config: captureConfig) + + // 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.publishTranscodedVideoTrack = true + option.clientRoleType = .broadcaster + NetworkManager.shared.generateToken(channelName: channelName, success: { token in + let result = self.agoraKit.joinChannel(byToken: token, channelId: channelName, uid: 0, mediaOptions: option) + if result != 0 { + // Usually happens with invalid parameters + // Error code description can be found at: + // en: https://api-ref.agora.io/en/video-sdk/ios/4.x/documentation/agorartckit/agoraerrorcode + // cn: https://doc.shengwang.cn/api-ref/rtc/ios/error-code + self.showAlert(title: "Error", message: "joinChannel call failed: \(result), please check your params") + } + self.startVideoTranscoder() + }) + } + + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + agoraKit.disableAudio() + agoraKit.disableVideo() + if isJoined { + stopScreenCapture() + agoraKit.stopPreview() + agoraKit.leaveChannel { (stats) -> Void in + LogUtils.log(message: "left channel, duration: \(stats.duration)", level: .info) + } + } + } + + private func screenShareVideoDimension() -> CGSize { + let screenSize = UIScreen.main.bounds + var boundingSize = CGSize(width: 540, height: 960) + let mW: CGFloat = boundingSize.width / screenSize.width + let mH: CGFloat = boundingSize.height / screenSize.height + if mH < mW { + boundingSize.width = boundingSize.height / screenSize.height * screenSize.width + } else if mW < mH { + boundingSize.height = boundingSize.width / screenSize.width * screenSize.height + } + return boundingSize + } + + private func prepareSystemBroadcaster() { + if #available(iOS 12.0, *) { + let frame = CGRect(x: 0, y: 0, width: 60, height: 60) + systemBroadcastPicker = RPSystemBroadcastPickerView(frame: frame) + systemBroadcastPicker?.showsMicrophoneButton = false + systemBroadcastPicker?.autoresizingMask = [.flexibleTopMargin, .flexibleRightMargin] + let bundleId = Bundle.main.bundleIdentifier ?? "" + systemBroadcastPicker?.preferredExtension = "\(bundleId).Agora-ScreenShare-Extension" + + } else { + self.showAlert(message: "Minimum support iOS version is 12.0") + } + } + + private func stopScreenCapture() { + agoraKit.stopScreenCapture() + option.publishScreenCaptureVideo = false + option.publishScreenCaptureAudio = false + agoraKit.updateChannel(with: option) + } + + private func startScreenCapture() { + guard !UIScreen.main.isCaptured else { return } + agoraKit.startScreenCapture(screenParams) + prepareSystemBroadcaster() + guard let picker = systemBroadcastPicker else { return } + for view in picker.subviews where view is UIButton { + (view as? UIButton)?.sendActions(for: .allEvents) + break + } + + option.publishScreenCaptureVideo = true + option.publishScreenCaptureAudio = true + agoraKit.updateChannel(with: option) + } + + private func startVideoTranscoder() { + + // camera capture + let cameraStream = AgoraTranscodingVideoStream() + cameraStream.rect = CGRect(origin: .zero, size: CGSize(width: 100, height: 100)) + cameraStream.sourceType = .camera + + // screen capture + let screenStream = AgoraTranscodingVideoStream() + screenStream.sourceType = .screen + screenStream.rect = CGRect(origin: .zero, size: screenShareVideoDimension()) + + let config = AgoraLocalTranscoderConfiguration() + config.videoOutputConfiguration.dimensions = screenShareVideoDimension() + // set transcoder config + config.videoInputStreams = [screenStream, cameraStream] + agoraKit.startLocalVideoTranscoder(config) + } +} + +/// agora rtc engine delegate events +extension LocalCompositeGraphMain: 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/video-sdk/ios/4.x/documentation/agorartckit/agoraerrorcode + /// cn: https://doc.shengwang.cn/api-ref/rtc/ios/error-code + /// @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) + } + + /// 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) + } + + /// 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) + } + + func rtcEngine(_ engine: AgoraRtcEngineKit, didLocalVideoTranscoderErrorWithStream stream: AgoraTranscodingVideoStream, errorCode: AgoraVideoTranscoderError) { + print("didLocalVideoTranscoderError: \(errorCode.rawValue), source type: \(stream.sourceType.rawValue)") + } +} diff --git a/iOS/APIExample/APIExample/Examples/Advanced/LocalCompositeGraph/zh-Hans.lproj/LocalCompositeGraph.strings b/iOS/APIExample/APIExample/Examples/Advanced/LocalCompositeGraph/zh-Hans.lproj/LocalCompositeGraph.strings new file mode 100644 index 000000000..25a97ee8c --- /dev/null +++ b/iOS/APIExample/APIExample/Examples/Advanced/LocalCompositeGraph/zh-Hans.lproj/LocalCompositeGraph.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/Examples/Advanced/MediaChannelRelay/MediaChannelRelay.swift b/iOS/APIExample/APIExample/Examples/Advanced/MediaChannelRelay/MediaChannelRelay.swift index 539a2cf57..cddf2a31b 100644 --- a/iOS/APIExample/APIExample/Examples/Advanced/MediaChannelRelay/MediaChannelRelay.swift +++ b/iOS/APIExample/APIExample/Examples/Advanced/MediaChannelRelay/MediaChannelRelay.swift @@ -45,6 +45,8 @@ class MediaChannelRelayMain: BaseViewController { @IBOutlet weak var resumeButton: UIButton! @IBOutlet weak var relayChannelField: UITextField! var agoraKit: AgoraRtcEngineKit! + // configure source info, channel name defaults to current, and uid defaults to local + let mediaRelayconfig = AgoraChannelMediaRelayConfiguration() // indicate if current instance has joined channel var isJoined: Bool = false @@ -128,6 +130,7 @@ class MediaChannelRelayMain: BaseViewController { // cn: https://doc.shengwang.cn/api-ref/rtc/ios/error-code self.showAlert(title: "Error", message: "joinChannel call failed: \(result), please check your params") } + self.mediaRelayconfig.sourceInfo = AgoraChannelMediaRelayInfo(token: token) }) } @@ -140,15 +143,12 @@ class MediaChannelRelayMain: BaseViewController { self.showAlert(message: "Destination channel name is empty") return } + NetworkManager.shared.generateToken(channelName: destinationChannelName) { token in - // configure source info, channel name defaults to current, and uid defaults to local - let config = AgoraChannelMediaRelayConfiguration() - config.sourceInfo = AgoraChannelMediaRelayInfo(token: token) - // configure target channel info let destinationInfo = AgoraChannelMediaRelayInfo(token: token) - config.setDestinationInfo(destinationInfo, forChannelName: destinationChannelName) - self.agoraKit.startOrUpdateChannelMediaRelay(config) + self.mediaRelayconfig.setDestinationInfo(destinationInfo, forChannelName: destinationChannelName) + self.agoraKit.startOrUpdateChannelMediaRelay(self.mediaRelayconfig) } } diff --git a/iOS/APIExample/APIExample/Examples/Advanced/PictureInPicture/ChannelViewController.swift b/iOS/APIExample/APIExample/Examples/Advanced/PictureInPicture/ChannelViewController.swift new file mode 100644 index 000000000..49e931b0c --- /dev/null +++ b/iOS/APIExample/APIExample/Examples/Advanced/PictureInPicture/ChannelViewController.swift @@ -0,0 +1,59 @@ +// +// ViewController.swift +// PIPDemo +// +// Created by qinhui on 2024/8/7. +// + +import UIKit + +class ChannelViewController: UIViewController { + lazy var textField: UITextField = { + let t = UITextField() + t.placeholder = "输入房间号" + t.borderStyle = .line + t.backgroundColor = .orange + return t + }() + + var pipCls: T.Type? + + lazy var button: UIButton = { + let b = UIButton(type: .custom) + b.setTitle("加入房间", for: .normal) + b.setTitleColor(.blue, for: .normal) + b.addTarget(self, action: #selector(joinAction), for: .touchUpInside) + return b + }() + + override func viewDidLoad() { + super.viewDidLoad() + view.backgroundColor = .white + + view.addSubview(textField) + view.addSubview(button) + + button.snp.makeConstraints { make in + make.center.equalTo(view) + } + + textField.snp.makeConstraints { make in + make.bottom.equalTo(button.snp.top).offset(-50) + make.centerX.equalTo(button) + make.width.equalTo(150) + make.height.equalTo(30) + } + } + + @objc func joinAction() { + guard let channelId = textField.text, let cls = pipCls else { return } + + let vc = cls.init() + vc.channelId = channelId + self.navigationController?.pushViewController(vc, animated: true) + } + + override func touchesBegan(_ touches: Set, with event: UIEvent?) { + view.endEditing(true) + } +} diff --git a/iOS/APIExample/APIExample/Examples/Advanced/PictureInPicture/CustomViewPIPViewController/CustomViewPIPService.swift b/iOS/APIExample/APIExample/Examples/Advanced/PictureInPicture/CustomViewPIPViewController/CustomViewPIPService.swift new file mode 100644 index 000000000..4e99795c7 --- /dev/null +++ b/iOS/APIExample/APIExample/Examples/Advanced/PictureInPicture/CustomViewPIPViewController/CustomViewPIPService.swift @@ -0,0 +1,169 @@ +// +// RtcManager.swift +// PIPDemo +// +// Created by qinhui on 2024/8/7. +// + +import Foundation +import AgoraRtcKit + +class CustomViewPIPService: NSObject { + var rtcEngineDelegate: AgoraRtcEngineDelegate? + var videoFrameDelegte: AgoraVideoFrameDelegate? + + weak var localView: UIView? + weak var remoteView: UIView? + var channelId: String + + private lazy var rtcConfig: AgoraRtcEngineConfig = { + let config = AgoraRtcEngineConfig() + config.appId = KeyCenter.AppId + config.areaCode = .global + config.channelProfile = .liveBroadcasting + return config + }() + + private lazy var rtcEngine: AgoraRtcEngineKit = { + let engine = AgoraRtcEngineKit.sharedEngine(with: rtcConfig, delegate: self) + engine.setClientRole(.broadcaster) + engine.enableAudio() + engine.enableVideo() + engine.setVideoEncoderConfiguration(AgoraVideoEncoderConfiguration(size: CGSize(width: 960, height: 540), + frameRate: .fps15, + bitrate: AgoraVideoBitrateStandard, + orientationMode: .adaptative, + mirrorMode: .auto)) + engine.setVideoFrameDelegate(self) + return engine + }() + + init(localView: UIView, remoteView: UIView, channelId: String) { + self.localView = localView + self.remoteView = remoteView + self.channelId = channelId + + super.init() + + setupRtcEngin() + } + + private func setupRtcEngin() { + let videoCanvas = AgoraRtcVideoCanvas() + videoCanvas.uid = 0 + videoCanvas.view = localView + videoCanvas.renderMode = .hidden + + rtcEngine.setupLocalVideo(videoCanvas) + rtcEngine.startPreview() + rtcEngine.setDefaultAudioRouteToSpeakerphone(true) + rtcEngine.setVideoFrameDelegate(self) + + let option = AgoraRtcChannelMediaOptions() + option.publishCameraTrack = true + option.publishMicrophoneTrack = true + option.clientRoleType = .broadcaster + + NetworkManager.shared.generateToken(channelName: channelId, success: { [weak self] token in + guard let self = self else { return } + + let result = self.rtcEngine.joinChannel(byToken: token, channelId: self.channelId, uid: 0, mediaOptions: option) + if result != 0 { + ToastView.showWait(text: "joinChannel call failed: \(result), please check your params", view: nil) + } + }) + } + + func disable() { + rtcEngine.disableAudio() + rtcEngine.disableVideo() + } + + func leave() { + rtcEngine.stopPreview() + rtcEngine.leaveChannel(nil) + } + +} + +extension CustomViewPIPService: AgoraRtcEngineDelegate { + func rtcEngine(_ engine: AgoraRtcEngineKit, didOccur errorType: AgoraEncryptionErrorType) { + rtcEngineDelegate?.rtcEngine?(engine, didOccur: errorType) + } + + func rtcEngine(_ engine: AgoraRtcEngineKit, didJoinChannel channel: String, withUid uid: UInt, elapsed: Int) { + rtcEngineDelegate?.rtcEngine?(engine, didJoinChannel: channel, withUid: uid, elapsed: elapsed) + } + + /// 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) { + // 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 = remoteView + videoCanvas.renderMode = .hidden + rtcEngine.setupRemoteVideo(videoCanvas) + + rtcEngineDelegate?.rtcEngine?(engine, didJoinedOfUid: uid, elapsed: elapsed) + } + + /// 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) { + let videoCanvas = AgoraRtcVideoCanvas() + videoCanvas.uid = uid + // the view to be binded + videoCanvas.view = nil + videoCanvas.renderMode = .hidden + rtcEngine.setupRemoteVideo(videoCanvas) + + rtcEngineDelegate?.rtcEngine?(engine, didOfflineOfUid: uid, reason: reason) + } + + func rtcEngine(_ engine: AgoraRtcEngineKit, connectionChangedTo state: AgoraConnectionState, reason: AgoraConnectionChangedReason) { + rtcEngineDelegate?.rtcEngine?(engine, connectionChangedTo: state, reason: reason) + } + + /// 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) { + rtcEngineDelegate?.rtcEngine?(engine, reportRtcStats: 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) { + rtcEngineDelegate?.rtcEngine?(engine, localAudioStats: stats) + } + + /// Reports the statistics of the video stream from each remote user/host. + /// @param stats stats struct + func rtcEngine(_ engine: AgoraRtcEngineKit, remoteVideoStats stats: AgoraRtcRemoteVideoStats) { + rtcEngineDelegate?.rtcEngine?(engine, remoteVideoStats: 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) { + rtcEngineDelegate?.rtcEngine?(engine, remoteAudioStats: stats) + } +} + +extension CustomViewPIPService: AgoraVideoFrameDelegate { + func onCapture(_ videoFrame: AgoraOutputVideoFrame, sourceType: AgoraVideoSourceType) -> Bool { + print("") + return true + } + + func onRenderVideoFrame(_ videoFrame: AgoraOutputVideoFrame, uid: UInt, channelId: String) -> Bool { + print("") + return true + } +} diff --git a/iOS/APIExample/APIExample/Examples/Advanced/PictureInPicture/CustomViewPIPViewController/CustomViewPIPViewController.swift b/iOS/APIExample/APIExample/Examples/Advanced/PictureInPicture/CustomViewPIPViewController/CustomViewPIPViewController.swift new file mode 100644 index 000000000..ebe0ac770 --- /dev/null +++ b/iOS/APIExample/APIExample/Examples/Advanced/PictureInPicture/CustomViewPIPViewController/CustomViewPIPViewController.swift @@ -0,0 +1,234 @@ +// +// VideoViewController.swift +// PIPDemo +// +// Created by qinhui on 2024/8/7. +// + +import UIKit +import SnapKit +import AgoraRtcKit +import AVKit + +@available(iOS 15.0, *) +class CustomViewPIPViewController: PIPBaseViewController { + private let containerH = 250.0 + private var isJoined: Bool = false + private var pipController: AVPictureInPictureController? + private var videoCallbackController: AVPictureInPictureVideoCallViewController? + private var backgroundTask: UIBackgroundTaskIdentifier = .invalid + + private var pipSizes = [ + CGSize(width: 150, height: 300), + CGSize(width: 300, height: 150) + ] + + private lazy var pipButton: UIButton = { + let button = UIButton(type: .custom) + button.setTitle("画中画", for: .normal) + button.addTarget(self, action: #selector(pipAction), for: .touchUpInside) + button.backgroundColor = .purple + return button + }() + + private lazy var sizeButton: UIButton = { + let button = UIButton(type: .custom) + button.setTitle("切换尺寸", for: .normal) + button.addTarget(self, action: #selector(sizeAction), for: .touchUpInside) + button.backgroundColor = .red + + return button + }() + + private lazy var localVideoView: UIView = { + let view = UIView() + view.backgroundColor = .green + return view + }() + + private lazy var remoteVideoView: UIView = { + let view = UIView() + view.backgroundColor = .orange + return view + }() + + private lazy var videoContainerView: UIView = { + let view = UIView() + view.backgroundColor = .purple + return view + }() + + private var rtcService: CustomViewPIPService! + + override func viewDidLoad() { + super.viewDidLoad() + view.backgroundColor = .white + initRtc() + configViews() + if AVPictureInPictureController.isPictureInPictureSupported() { + configPIPViewController() + } + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + guard let pipController = pipController else { return } + pipController.stopPictureInPicture() + } + + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + rtcService.disable() + + if isJoined { + rtcService.leave() + } + } +} + +@available(iOS 15.0, *) +extension CustomViewPIPViewController { + @objc func pipAction() { + guard let pipController = pipController else { return } + + if pipController.isPictureInPictureActive { + pipController.stopPictureInPicture() + } else { + pipController.startPictureInPicture() + } + } + + @objc func sizeAction() { + guard let videoCallbackController = videoCallbackController else { return } + + let i = Int.random(in: 0.. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iOS/APIExample/APIExample/Examples/Advanced/PictureInPicture/zh-Hans.lproj/PictureInPicture.strings b/iOS/APIExample/APIExample/Examples/Advanced/PictureInPicture/PictureInPicture.strings similarity index 100% rename from iOS/APIExample/APIExample/Examples/Advanced/PictureInPicture/zh-Hans.lproj/PictureInPicture.strings rename to iOS/APIExample/APIExample/Examples/Advanced/PictureInPicture/PictureInPicture.strings diff --git a/iOS/APIExample/APIExample/Examples/Advanced/PictureInPicture/PictureInPicture.swift b/iOS/APIExample/APIExample/Examples/Advanced/PictureInPicture/PictureInPicture.swift index 6787012ac..f686853bb 100644 --- a/iOS/APIExample/APIExample/Examples/Advanced/PictureInPicture/PictureInPicture.swift +++ b/iOS/APIExample/APIExample/Examples/Advanced/PictureInPicture/PictureInPicture.swift @@ -1,332 +1,59 @@ // -// PictureInPicture.swift -// APIExample +// HomeViewController.swift +// PIPDemo // -// Created by 胡润辰 on 2022/4/6. -// Copyright © 2022 Agora Corp. All rights reserved. +// Created by qinhui on 2024/8/8. // import UIKit -import AGEVideoLayout -import AgoraRtcKit -import MediaPlayer -class PictureInPictureEntry: UIViewController { - @IBOutlet weak var joinButton: AGButton! - @IBOutlet weak var channelTextField: AGTextField! - let identifier = "PictureInPicture" - - override func viewDidLoad() { - super.viewDidLoad() - } - - @IBAction func doJoinPressed(sender: AGButton) { - 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] - navigationController?.pushViewController(newViewController, animated: true) +class Model { + var title: String + var cls: T.Type + init(title: String, cls: T.Type) { + self.title = title + self.cls = cls } } -@available(iOS 15.0, *) -class PictureInPictureMain: BaseViewController { - var localVideo = Bundle.loadVideoView(type: .local, audioOnly: false) - var remoteVideo = Bundle.loadView(fromNib: "VideoViewSampleBufferDisplayView", withType: SampleBufferDisplayView.self) - var agoraKit: AgoraRtcEngineKit! - private lazy var callViewController: AVPictureInPictureVideoCallViewController = { - let callViewController = AVPictureInPictureVideoCallViewController() - callViewController.preferredContentSize = view.bounds.size - callViewController.view.backgroundColor = .clear - callViewController.modalPresentationStyle = .overFullScreen - return callViewController - }() - var pipController: AVPictureInPictureController? - var remoteUid: UInt? - // indicate if current instance has joined channel - var isJoined: Bool = false - private lazy var containerView: UIView = { - let view = UIView() - view.backgroundColor = .red - return view +class PictureInPicture: UITableViewController { + lazy var dataArray: [Model] = { + if #available(iOS 15.0, *) { + return [ + Model(title: "SDK 渲染", cls: CustomViewPIPViewController.self), + Model(title: "多人视频自渲染", cls: PixelBufferPIPViewController.self) + ] + } else { + // Fallback on earlier versions + return [] + } }() - // swiftlint: disable function_body_length override func viewDidLoad() { super.viewDidLoad() - // layout render view - localVideo.setPlaceholder(text: "Local Host".localized) - remoteVideo.setPlaceholder(text: "Remote Host".localized) - view.addSubview(containerView) - containerView.frame = CGRect(x: 0, y: 0, width: SCREENSIZE.width, height: 280) - containerView.addSubview(localVideo) - containerView.addSubview(remoteVideo) - localVideo.translatesAutoresizingMaskIntoConstraints = false - remoteVideo.translatesAutoresizingMaskIntoConstraints = false - localVideo.leadingAnchor.constraint(equalTo: containerView.leadingAnchor).isActive = true - localVideo.topAnchor.constraint(equalTo: containerView.topAnchor).isActive = true - localVideo.bottomAnchor.constraint(equalTo: containerView.bottomAnchor).isActive = true - localVideo.widthAnchor.constraint(equalTo: containerView.widthAnchor, multiplier: 0.5).isActive = true - remoteVideo.trailingAnchor.constraint(equalTo: containerView.trailingAnchor).isActive = true - remoteVideo.topAnchor.constraint(equalTo: containerView.topAnchor).isActive = true - remoteVideo.bottomAnchor.constraint(equalTo: containerView.bottomAnchor).isActive = true - remoteVideo.widthAnchor.constraint(equalTo: containerView.widthAnchor, multiplier: 0.5).isActive = true - - pipController = AVPictureInPictureController(contentSource: .init(activeVideoCallSourceView: containerView, - contentViewController: callViewController)) - pipController?.canStartPictureInPictureAutomaticallyFromInline = true - pipController?.delegate = self - - // set up agora instance when view loadedlet config = AgoraRtcEngineConfig() - let config = AgoraRtcEngineConfig() - config.appId = KeyCenter.AppId - config.channelProfile = .liveBroadcasting - config.areaCode = GlobalSettings.shared.area - // setup log file path - let logConfig = AgoraLogConfig() - logConfig.level = .info - config.logConfig = logConfig - agoraKit = AgoraRtcEngineKit.sharedEngine(with: config, delegate: self) - // Configuring Privatization Parameters - Util.configPrivatization(agoraKit: agoraKit) - // get channel name from configs - guard let channelName = configs["channelName"] as? String, - let resolution = GlobalSettings.shared.getSetting(key: "resolution")?.selectedOption().value as? CGSize, - let fps = GlobalSettings.shared.getSetting(key: "fps")?.selectedOption().value as? AgoraVideoFrameRate, - let orientation = GlobalSettings.shared.getSetting(key: "orientation")? - .selectedOption().value as? AgoraVideoOutputOrientationMode else { - return - } - // To enable MPNowPlayingInfoCenter, you need to add the following two private parameters - agoraKit.setAudioOptionParams("{\"adm_mix_with_others\":false}") - agoraKit.setParameters("{\"che.audio.nonmixable.option\":true}") - - // make myself a broadcaster - agoraKit.setChannelProfile(.liveBroadcasting) - agoraKit.setClientRole(GlobalSettings.shared.getUserRole()) - - // enable video module and set up video encoding configs - agoraKit.enableVideo() - agoraKit.setVideoEncoderConfiguration(AgoraVideoEncoderConfiguration(size: resolution, - frameRate: fps, - bitrate: AgoraVideoBitrateStandard, - orientationMode: orientation, - mirrorMode: AgoraVideoMirrorMode.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) - - // Set audio route to speaker - agoraKit.setDefaultAudioRouteToSpeakerphone(true) - - // Setup raw video data frame observer - agoraKit.setVideoFrameDelegate(self) - - // 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() - NetworkManager.shared.generateToken(channelName: channelName, success: { token in - let result = self.agoraKit.joinChannel(byToken: token, channelId: channelName, uid: 0, mediaOptions: option, joinSuccess: nil) - if result != 0 { - // Usually happens with invalid parameters - // Error code description can be found at: - // en: https://api-ref.agora.io/en/video-sdk/ios/4.x/documentation/agorartckit/agoraerrorcode - // cn: https://doc.shengwang.cn/api-ref/rtc/ios/error-code - self.showAlert(title: "Error", message: "joinChannel call failed: \(result), please check your params") - } - }) - - NotificationCenter.default.addObserver(self, - selector: #selector(didEnterBackgroundNotification), - name: UIApplication.willResignActiveNotification, - object: nil) + title = "Picture In Picture" + tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell") } - // swiftlint: enable function_body_length - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - setupPlayintInfoCenter() - } - - private func setupPlayintInfoCenter() { - UIApplication.shared.beginReceivingRemoteControlEvents() - var nowPlayingInfo = [String: Any]() - let path = Bundle.main.path(forResource: "agora-logo", ofType: "png") ?? "" - guard let image = UIImage(contentsOfFile: path) else { return } - let artWork = MPMediaItemArtwork(boundsSize: image.size) { _ in - return image - } - nowPlayingInfo[MPMediaItemPropertyArtwork] = artWork - nowPlayingInfo[MPMediaItemPropertyTitle] = "Song Title" - nowPlayingInfo[MPMediaItemPropertyArtist] = "Artist Name" - nowPlayingInfo[MPMediaItemPropertyAlbumTitle] = "Album Name" - nowPlayingInfo[MPNowPlayingInfoPropertyIsLiveStream] = true - MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo - MPNowPlayingInfoCenter.default().playbackState = .playing + override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + return 50 } - override var canBecomeFirstResponder: Bool { - true + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + dataArray.count } - deinit { - NotificationCenter.default.removeObserver(self) + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let model = dataArray[indexPath.row] + let vc = ChannelViewController() + vc.pipCls = model.cls + self.navigationController?.pushViewController(vc, animated: true) } - @objc - private func didEnterBackgroundNotification() { - onPIP(_btn: UIButton()) - } - - @IBAction func onPIP(_btn: UIButton) { - if let currentPipController = pipController { - currentPipController.startPictureInPicture() - } else { - showAlert(message: "PIP Support iOS 15+".localized) - } - } - - override func willMove(toParent parent: UIViewController?) { - if parent == nil { - // leave channel when exiting the view - if isJoined { - if let pipController = pipController, pipController.isPictureInPictureActive { - pipController.stopPictureInPicture() - } - agoraKit.leaveChannel { (stats) -> Void in - LogUtils.log(message: "left channel, duration: \(stats.duration)", level: .info) - } - } - } - } -} - -/// agora rtc engine delegate events -@available(iOS 15.0, *) -extension PictureInPictureMain: 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/video-sdk/ios/4.x/documentation/agorartckit/agoraerrorcode - /// cn: https://doc.shengwang.cn/api-ref/rtc/ios/error-code - /// @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") - } - - /// callback when the local user joins a specified channel. - /// @param channel - /// @param uid uid of local user - /// @param elapsed time elapse since current sdk instance join the channel in ms - func rtcEngine(_ engine: AgoraRtcEngineKit, didJoinChannel channel: String, withUid uid: UInt, elapsed: Int) { - 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) - - remoteVideo.videoView.reset() - } - - /// 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 -// remoteVideo.videoView.reset() - } - func rtcEngine(_ engine: AgoraRtcEngineKit, - remoteVideoStateChangedOfUid uid: UInt, - state: AgoraVideoRemoteState, reason: AgoraVideoRemoteReason, - elapsed: Int) { - if reason == .remoteMuted { - let pixelBuffer = MediaUtils.cvPixelBufferRef(from: UIImage(named: "agora-logo") ?? UIImage()).takeRetainedValue() - let videoFrame = AgoraOutputVideoFrame() - videoFrame.pixelBuffer = pixelBuffer - videoFrame.width = Int32(remoteVideo.videoView.frame.width) - videoFrame.height = Int32(remoteVideo.videoView.frame.height) - remoteVideo.videoView.renderVideoPixelBuffer(videoFrame) - } - } -} - -// MARK: - AgoraVideoDataFrameProtocol -@available(iOS 15.0, *) -extension PictureInPictureMain: AgoraVideoFrameDelegate { - func onCapture(_ videoFrame: AgoraOutputVideoFrame, sourceType: AgoraVideoSourceType) -> Bool { - true - } - - func onRenderVideoFrame(_ videoFrame: AgoraOutputVideoFrame, uid: UInt, channelId: String) -> Bool { - remoteVideo.videoView.renderVideoPixelBuffer(videoFrame) - return true - } - - func getVideoFormatPreference() -> AgoraVideoFormat { - .cvPixelBGRA - } - func getRotationApplied() -> Bool { - true - } -} - -@available(iOS 15.0, *) -extension PictureInPictureMain: AVPictureInPictureControllerDelegate { - func pictureInPictureControllerWillStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { - } - - func pictureInPictureControllerDidStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { - containerView.removeFromSuperview() - let vc = pictureInPictureController.contentSource?.activeVideoCallContentViewController - containerView.frame.size = vc?.view.bounds.size ?? .zero - vc?.view.addSubview(containerView) - } - - func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, - failedToStartPictureInPictureWithError error: Error) { - } - - func pictureInPictureControllerWillStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { - containerView.removeFromSuperview() - containerView.frame.size = CGSize(width: SCREENSIZE.width, height: 280) - view.addSubview(containerView) - } - - func pictureInPictureControllerDidStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) + let model = dataArray[indexPath.row] + cell.textLabel?.text = model.title + return cell } } diff --git a/iOS/APIExample/APIExample/Examples/Advanced/PictureInPicture/PixelBufferPIPViewController/PixelBufferPIPService.swift b/iOS/APIExample/APIExample/Examples/Advanced/PictureInPicture/PixelBufferPIPViewController/PixelBufferPIPService.swift new file mode 100644 index 000000000..d34047993 --- /dev/null +++ b/iOS/APIExample/APIExample/Examples/Advanced/PictureInPicture/PixelBufferPIPViewController/PixelBufferPIPService.swift @@ -0,0 +1,114 @@ +// +// PixelBufferPIPService.swift +// PIPDemo +// +// Created by qinhui on 2024/8/8. +// + +import Foundation +import AgoraRtcKit + +class PixelBufferPIPService: NSObject { + var videoFrameDelegte: AgoraVideoFrameDelegate? + var rtcEngineDelegate: AgoraRtcEngineDelegate? + weak var localView: PixelBufferRenderView? + + var uid: UInt = 0 + var channelId: String + + private lazy var rtcConfig: AgoraRtcEngineConfig = { + let config = AgoraRtcEngineConfig() + config.appId = KeyCenter.AppId + config.areaCode = .global + config.channelProfile = .liveBroadcasting + return config + }() + + private lazy var rtcEngine: AgoraRtcEngineKit = { + let engine = AgoraRtcEngineKit.sharedEngine(with: rtcConfig, delegate: self) + engine.setClientRole(.broadcaster) + engine.enableAudio() + engine.enableVideo() + engine.setVideoEncoderConfiguration(AgoraVideoEncoderConfiguration(size: CGSize(width: 960, height: 540), + frameRate: .fps15, + bitrate: AgoraVideoBitrateStandard, + orientationMode: .fixedPortrait, + mirrorMode: .auto)) + engine.setVideoFrameDelegate(self) + + return engine + }() + + init(channelId: String, uid: UInt, localView: PixelBufferRenderView) { + self.channelId = channelId + self.uid = uid + self.localView = localView + super.init() + + setupRtcEngin() + } + + private func setupRtcEngin() { + let videoCanvas = AgoraRtcVideoCanvas() + videoCanvas.uid = 0 + videoCanvas.view = localView + videoCanvas.renderMode = .hidden + + rtcEngine.setupLocalVideo(videoCanvas) + rtcEngine.startPreview() + + rtcEngine.setDefaultAudioRouteToSpeakerphone(true) + rtcEngine.setVideoFrameDelegate(self) + + let option = AgoraRtcChannelMediaOptions() + option.publishCameraTrack = true + option.publishMicrophoneTrack = true + option.clientRoleType = .broadcaster + + NetworkManager.shared.generateToken(channelName: channelId, success: { [weak self] token in + guard let self = self else { return } + let result = self.rtcEngine.joinChannel(byToken: token, channelId: self.channelId, uid: self.uid, mediaOptions: option) + if result != 0 { + ToastView.showWait(text: "joinChannel call failed: \(result), please check your params", view: nil) + } else { + self.localView?.uid = self.uid + } + }) + } + + func disable() { + rtcEngine.disableAudio() + rtcEngine.disableVideo() + } + + func leave() { + rtcEngine.stopPreview() + rtcEngine.leaveChannel(nil) + } + +} + +extension PixelBufferPIPService: AgoraVideoFrameDelegate { + func onCapture(_ videoFrame: AgoraOutputVideoFrame, sourceType: AgoraVideoSourceType) -> Bool { + return ((self.videoFrameDelegte?.onCapture?(videoFrame, sourceType: sourceType)) != nil) + } + + func onRenderVideoFrame(_ videoFrame: AgoraOutputVideoFrame, uid: UInt, channelId: String) -> Bool { + return ((self.videoFrameDelegte?.onRenderVideoFrame?(videoFrame, uid: uid, channelId: channelId)) != nil) + } +} + +extension PixelBufferPIPService: AgoraRtcEngineDelegate { + func rtcEngine(_ engine: AgoraRtcEngineKit, didJoinChannel channel: String, withUid uid: UInt, elapsed: Int) { + rtcEngineDelegate?.rtcEngine?(engine, didJoinChannel: channel, withUid: uid, elapsed: elapsed) + } + + func rtcEngine(_ engine: AgoraRtcEngineKit, didJoinedOfUid uid: UInt, elapsed: Int) { + rtcEngineDelegate?.rtcEngine?(engine, didJoinedOfUid: uid, elapsed: elapsed) + } + + func rtcEngine(_ engine: AgoraRtcEngineKit, didOfflineOfUid uid: UInt, reason: AgoraUserOfflineReason) { + rtcEngineDelegate?.rtcEngine?(engine, didOfflineOfUid: uid, reason: reason) + } + +} diff --git a/iOS/APIExample/APIExample/Examples/Advanced/PictureInPicture/PixelBufferPIPViewController/PixelBufferPIPViewController.swift b/iOS/APIExample/APIExample/Examples/Advanced/PictureInPicture/PixelBufferPIPViewController/PixelBufferPIPViewController.swift new file mode 100644 index 000000000..9378fb7bc --- /dev/null +++ b/iOS/APIExample/APIExample/Examples/Advanced/PictureInPicture/PixelBufferPIPViewController/PixelBufferPIPViewController.swift @@ -0,0 +1,264 @@ +// +// PixelBufferPIPViewController.swift +// PIPDemo +// +// Created by qinhui on 2024/8/8. +// +import UIKit +import AVKit +import AgoraRtcKit + +@available(iOS 15.0, *) +class PixelBufferPIPViewController: PIPBaseViewController { + private let mockUid: UInt = UInt.random(in: 0...100000) + private var pipController: AVPictureInPictureController? + private var videoCallbackController: AVPictureInPictureVideoCallViewController? + var isJoined = false + private var pipSizes = [ + CGSize(width: 150, height: 300), + CGSize(width: 300, height: 150) + ] + + private lazy var pipButton: UIButton = { + let button = UIButton(type: .custom) + button.setTitle("画中画", for: .normal) + button.setTitleColor(.black, for: .normal) + button.addTarget(self, action: #selector(pipAction), for: .touchUpInside) + + return button + }() + + private lazy var sizeButton: UIButton = { + let button = UIButton(type: .custom) + button.setTitle("切换尺寸", for: .normal) + button.setTitleColor(.black, for: .normal) + button.addTarget(self, action: #selector(sizeAction), for: .touchUpInside) + + return button + }() + + private lazy var topLeftView: PixelBufferRenderView = { + let view = PixelBufferRenderView() + view.backgroundColor = .blue + return view + }() + + private lazy var topRightView: PixelBufferRenderView = { + let view = PixelBufferRenderView() + view.backgroundColor = .red + return view + }() + + private lazy var bottomLeftView: PixelBufferRenderView = { + let view = PixelBufferRenderView() + view.backgroundColor = .green + return view + }() + + private lazy var bottomRightView: PixelBufferRenderView = { + let view = PixelBufferRenderView() + view.backgroundColor = .purple + return view + }() + + private lazy var videoContainerView: UIView = { + let view = UIView() + view.backgroundColor = .purple + return view + }() + + private lazy var displayViews: NSHashTable = { + let table = NSHashTable(options: .weakMemory, capacity: 4) + table.add(self.topLeftView) + table.add(self.topRightView) + table.add(bottomLeftView) + table.add(bottomRightView) + return table + }() + + private var rtcService: PixelBufferPIPService! + + override func viewDidLoad() { + super.viewDidLoad() + view.backgroundColor = .white + initRtc() + configViews() + if AVPictureInPictureController.isPictureInPictureSupported() { + configPIPViewController() + } + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + guard let pipController = pipController else { return } + pipController.stopPictureInPicture() + } + + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + rtcService.disable() + + if isJoined { + rtcService.leave() + } + } +} + +@available(iOS 15.0, *) +extension PixelBufferPIPViewController { + private func configPIPViewController() { + let videoCallViewController = AVPictureInPictureVideoCallViewController() + videoCallViewController.preferredContentSize = view.bounds.size + videoCallViewController.view.backgroundColor = .clear + videoCallViewController.modalPresentationStyle = .overFullScreen + + self.videoCallbackController = videoCallViewController + pipController = AVPictureInPictureController(contentSource: .init(activeVideoCallSourceView: videoContainerView, + contentViewController: videoCallViewController)) + pipController?.canStartPictureInPictureAutomaticallyFromInline = true + pipController?.delegate = self + pipController?.setValue(1, forKey: "controlsStyle") + } + + private func configViews() { + self.view.addSubview(videoContainerView) + videoContainerView.addSubview(topLeftView) + videoContainerView.addSubview(topRightView) + videoContainerView.addSubview(bottomLeftView) + videoContainerView.addSubview(bottomRightView) + + self.view.addSubview(pipButton) + self.view.addSubview(sizeButton) + + videoContainerView.snp.makeConstraints { make in + make.left.top.right.bottom.equalTo(0) + } + + topLeftView.snp.makeConstraints { make in + make.top.left.equalToSuperview() + make.width.equalToSuperview().dividedBy(2) + make.height.equalToSuperview().dividedBy(2) + } + + topRightView.snp.makeConstraints { make in + make.top.equalToSuperview() + make.right.equalToSuperview() + make.width.equalToSuperview().dividedBy(2) + make.height.equalToSuperview().dividedBy(2) + } + + bottomLeftView.snp.makeConstraints { make in + make.bottom.equalToSuperview() + make.left.equalToSuperview() + make.width.equalToSuperview().dividedBy(2) + make.height.equalToSuperview().dividedBy(2) + } + + bottomRightView.snp.makeConstraints { make in + make.bottom.equalToSuperview() + make.right.equalToSuperview() + make.width.equalToSuperview().dividedBy(2) + make.height.equalToSuperview().dividedBy(2) + } + + pipButton.snp.makeConstraints { make in + make.center.equalTo(view) + } + + sizeButton.snp.makeConstraints { make in + make.top.equalTo(self.pipButton.snp.bottom).offset(10) + make.centerX.equalTo(self.pipButton.snp.centerX) + } + } + + private func initRtc() { + guard let channelId = channelId else { return } + rtcService = PixelBufferPIPService(channelId: channelId, uid: mockUid, localView: topLeftView) + rtcService.videoFrameDelegte = self + rtcService.rtcEngineDelegate = self + } + + @objc func pipAction() { + guard let pipController = pipController else { return } + + if pipController.isPictureInPictureActive { + pipController.stopPictureInPicture() + } else { + pipController.startPictureInPicture() + } + } + + @objc func sizeAction() { + guard let videoCallbackController = videoCallbackController else { return } + + let i = Int.random(in: 0.. Bool { + if let view = displayViews.allObjects.first(where: { $0.uid == mockUid }), let pixelBuffer = videoFrame.pixelBuffer { + view.renderVideoPixelBuffer(pixelBuffer: pixelBuffer, width: videoFrame.width, height: videoFrame.height) + } + + return true + } + + func onRenderVideoFrame(_ videoFrame: AgoraOutputVideoFrame, uid: UInt, channelId: String) -> Bool { + if let view = displayViews.allObjects.first(where: { $0.uid == uid }) { + view.renderFromVideoFrameData(videoData: videoFrame, uid: Int(uid)) + } + + return true + } +} diff --git a/iOS/APIExample/APIExample/Examples/Advanced/PictureInPicture/PixelBufferPIPViewController/PixelBufferRenderView.swift b/iOS/APIExample/APIExample/Examples/Advanced/PictureInPicture/PixelBufferPIPViewController/PixelBufferRenderView.swift new file mode 100644 index 000000000..330b3f9cb --- /dev/null +++ b/iOS/APIExample/APIExample/Examples/Advanced/PictureInPicture/PixelBufferPIPViewController/PixelBufferRenderView.swift @@ -0,0 +1,179 @@ +// +// PixelBufferRenderView.swift +// PIPDemo +// +// Created by qinhui on 2024/8/8. +// + +import UIKit +import AVFoundation +import AgoraRtcKit + +enum VideoPosition { + case topLeft + case topRight + case bottomLeft + case bottomRight +} + +class PixelBufferRenderView: UIView { + var uid: UInt = 0 + private var videoWidth: Int32 = 0 + private var videoHeight: Int32 = 0 + + lazy var displayLayer: AVSampleBufferDisplayLayer = { + let layer = AVSampleBufferDisplayLayer() + return layer + }() + + override init(frame: CGRect) { + super.init(frame: frame) + configLayers() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func configLayers() { + self.layer.addSublayer(displayLayer) + displayLayer.frame = self.bounds + } + + func createLayer() -> AVSampleBufferDisplayLayer { + let layer = AVSampleBufferDisplayLayer() + return layer + } + + func clean() { + uid = 0 + self.displayLayer.removeFromSuperlayer() + self.displayLayer = createLayer() + self.layer.addSublayer(displayLayer) + } + + func renderFromVideoFrameData(videoData: AgoraOutputVideoFrame, uid: Int) { + let width = videoData.width + let height = videoData.height + let yStride = videoData.yStride + let uStride = videoData.uStride + let vStride = videoData.vStride + + let yBuffer = videoData.yBuffer + let uBuffer = videoData.uBuffer + let vBuffer = videoData.vBuffer + + autoreleasepool { + var pixelBuffer: CVPixelBuffer? + let pixelAttributes: [String: Any] = [kCVPixelBufferIOSurfacePropertiesKey as String: [:]] + + let result = CVPixelBufferCreate(kCFAllocatorDefault, + Int(width), + Int(height), + kCVPixelFormatType_420YpCbCr8Planar, + pixelAttributes as CFDictionary, + &pixelBuffer) + + guard result == kCVReturnSuccess, let pixelBuffer = pixelBuffer else { + print("Unable to create CVPixelBuffer: \(result)") + return + } + + CVPixelBufferLockBaseAddress(pixelBuffer, .init(rawValue: 0)) + let yPlane = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0) + let pixelBufferYBytes = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0) + + for i in 0.. 0, videoHeight > 0, !CGSizeEqualToSize(self.frame.size, CGSize.zero) else { + return + } + + let viewWidth = self.frame.size.width + let viewHeight = self.frame.size.height + + let videoRatio = CGFloat(videoWidth) / CGFloat(videoHeight) + let viewRatio = viewWidth / viewHeight + + var videoSize = CGSize.zero + if videoRatio >= viewRatio { + videoSize.height = viewHeight + videoSize.width = videoSize.height * videoRatio + } else { + videoSize.width = viewWidth + videoSize.height = videoSize.width / videoRatio + } + + let xOffset = max(0, (viewWidth - videoSize.width) / 2) + let yOffset = max(0, (viewHeight - videoSize.height) / 2) + let renderRect = CGRect(x: xOffset, y: yOffset, width: videoSize.width, height: videoSize.height) + + if !renderRect.equalTo(displayLayer.frame) { + displayLayer.frame = renderRect + } + } + +} diff --git a/iOS/APIExample/APIExample/Examples/Advanced/RawAudioData/RawAudioData.swift b/iOS/APIExample/APIExample/Examples/Advanced/RawAudioData/RawAudioData.swift index 5dd055894..a29f6f3d2 100644 --- a/iOS/APIExample/APIExample/Examples/Advanced/RawAudioData/RawAudioData.swift +++ b/iOS/APIExample/APIExample/Examples/Advanced/RawAudioData/RawAudioData.swift @@ -40,7 +40,7 @@ class RawAudioDataViewController: BaseViewController { // Audio4 is required to send Audio Meta Data. agoraKit.setParameters("{\"rtc.use_audio4\":true}") - // Setup raw auido data frame observer + // Setup raw audio data frame observer agoraKit.setAudioFrameDelegate(self) agoraKit.enableAudio() NetworkManager.shared.generateToken(channelName: channelId, success: { token in diff --git a/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/ThirdBeautify.swift b/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/ThirdBeautify.swift index 74f908f10..9de2fcbd8 100644 --- a/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/ThirdBeautify.swift +++ b/iOS/APIExample/APIExample/Examples/Advanced/ThirdBeautify/ThirdBeautify.swift @@ -14,7 +14,7 @@ enum ThirdBeautifyType: String { case bytedEffect = "BytedEffect" } -class ThirdBeautifyEntry: UIViewController { +class ThirdBeautifyEntry: BaseViewController { @IBOutlet weak var channelTextField: UITextField! @@ -47,7 +47,8 @@ class ThirdBeautifyEntry: UIViewController { actionSheetVC.addAction(fu) actionSheetVC.addAction(bytedEffect) actionSheetVC.addAction(cancel) - present(actionSheetVC, animated: true, completion: nil) +// present(actionSheetVC, animated: true, completion: nil) + presentAlertViewController(actionSheetVC) } private func jumpHandler(type: ThirdBeautifyType) { diff --git a/iOS/APIExample/APIExample/Examples/Advanced/TransparentRender/TransparentRender.storyboard b/iOS/APIExample/APIExample/Examples/Advanced/TransparentRender/TransparentRender.storyboard new file mode 100644 index 000000000..3165e7ea4 --- /dev/null +++ b/iOS/APIExample/APIExample/Examples/Advanced/TransparentRender/TransparentRender.storyboard @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iOS/APIExample/APIExample/Examples/Advanced/TransparentRender/TransparentRender.swift b/iOS/APIExample/APIExample/Examples/Advanced/TransparentRender/TransparentRender.swift new file mode 100644 index 000000000..145dfb4cc --- /dev/null +++ b/iOS/APIExample/APIExample/Examples/Advanced/TransparentRender/TransparentRender.swift @@ -0,0 +1,180 @@ +// +// TransparentRender.swift +// APIExample +// +// Created by wushengtao on 2024/6/17. +// Copyright © 2024 Agora Corp. All rights reserved. +// + +import Foundation + +class TransparentRenderEntry: UIViewController { + @IBOutlet weak var joinButton: AGButton! + @IBOutlet weak var channelTextField: AGTextField! + let identifier = "TransparentRender" + + override func viewDidLoad() { + super.viewDidLoad() + } + + @IBAction func doJoinPressed(sender: AGButton) { + 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] + navigationController?.pushViewController(newViewController, animated: true) + } +} + +class TransparentRenderViewController: BaseViewController { + private lazy var agoraKit: AgoraRtcEngineKit = { + + // set up agora instance when view loaded + let config = AgoraRtcEngineConfig() + config.appId = KeyCenter.AppId + config.areaCode = GlobalSettings.shared.area + config.channelProfile = .liveBroadcasting + let agoraKit = AgoraRtcEngineKit.sharedEngine(with: config, delegate: self) + // Configuring Privatization Parameters + Util.configPrivatization(agoraKit: agoraKit) + + agoraKit.setLogFile(LogUtils.sdkLogPath()) + + return agoraKit + }() + + private lazy var mediaPlayer: AgoraRtcMediaPlayerProtocol? = { + let mediaPlayer = agoraKit.createMediaPlayer(with: self) + mediaPlayer?.setVideoFrameDelegate(self) + return mediaPlayer + }() + + @IBOutlet private weak var topCanvasView: UIView? + @IBOutlet private weak var leftCanvasView: UIView? + @IBOutlet private weak var rightCanvasView: UIView? + + override func viewDidLoad() { + super.viewDidLoad() + + let channelName = self.configs["channelName"] as? String ?? "" + + openMedia() + + agoraKit.setExternalVideoSource(true, useTexture: false, sourceType: .videoFrame) + agoraKit.enableVideo() + agoraKit.startPreview() + let option = AgoraRtcChannelMediaOptions() + option.publishCameraTrack = false + option.publishMicrophoneTrack = false + option.clientRoleType = .broadcaster + option.publishCustomVideoTrack = true + NetworkManager.shared.generateToken(channelName: channelName, success: { token in + let result = self.agoraKit.joinChannel(byToken: token, channelId: channelName, uid: 0, mediaOptions: option) { channel, uid, elapsed in + LogUtils.log(message: "join channel[\(channelName)] success", level: .info) + } + if result != 0 { + // Usually happens with invalid parameters + // Error code description can be found at: + // en: https://api-ref.agora.io/en/video-sdk/ios/4.x/documentation/agorartckit/agoraerrorcode + // cn: https://doc.shengwang.cn/api-ref/rtc/ios/error-code + self.showAlert(title: "Error", message: "joinChannel call failed: \(result), please check your params") + } + }) + setupLocalCavans() + setupLocalAlphaCavans() + } + + override func viewDidDisappear(_ animated: Bool) { + agoraKit.leaveChannel() + agoraKit.disableVideo() + agoraKit.stopPreview() + AgoraRtcEngineKit.destroy() + } + + private func openMedia() { + let url = Bundle.main.path(forResource: "yuv_limit_range_alpha_1280_540_right", ofType: "mp4") ?? "" + let ret = try? mediaPlayer?.open(url, startPos: 0) + LogUtils.log(message: "openMedia: open ret:\(ret) url: \(url)", level: .info) + } + + private func setupLocalCavans() { + mediaPlayer?.setView(topCanvasView) + } + + private func setupLocalAlphaCavans() { + let videoCanvas = AgoraRtcVideoCanvas() + videoCanvas.view = leftCanvasView + videoCanvas.renderMode = .fit + videoCanvas.sourceType = .custom + videoCanvas.enableAlphaMask = true + agoraKit.setupLocalVideo(videoCanvas) + } + + private func setupRemoteAlphaCavans(uid: UInt) { + let videoCanvas = AgoraRtcVideoCanvas() + videoCanvas.view = rightCanvasView + videoCanvas.uid = uid + videoCanvas.renderMode = .fit + videoCanvas.enableAlphaMask = true + agoraKit.setupRemoteVideo(videoCanvas) + } +} + +extension TransparentRenderViewController: AgoraRtcEngineDelegate { + func rtcEngine(_ engine: AgoraRtcEngineKit, didJoinedOfUid uid: UInt, elapsed: Int) { + setupRemoteAlphaCavans(uid: uid) + } + + func rtcEngine(_ engine: AgoraRtcEngineKit, didOfflineOfUid uid: UInt, reason: AgoraUserOfflineReason) { + let videoCanvas = AgoraRtcVideoCanvas() + videoCanvas.view = nil + videoCanvas.uid = uid + agoraKit.setupRemoteVideo(videoCanvas) + } + + func rtcEngine(_ engine: AgoraRtcEngineKit, didOccurError errorCode: AgoraErrorCode) { + LogUtils.log(message: "didOccurError: \(errorCode)", level: .error) + } + + func rtcEngine(_ engine: AgoraRtcEngineKit, didOccurWarning warningCode: AgoraWarningCode) { + LogUtils.log(message: "didOccurWarning: \(warningCode)", level: .warning) + } +} + +extension TransparentRenderViewController: AgoraRtcMediaPlayerDelegate { + func AgoraRtcMediaPlayer(_ playerKit: AgoraRtcMediaPlayerProtocol, + didChangedTo state: AgoraMediaPlayerState, + reason: AgoraMediaPlayerReason) { + LogUtils.log(message: "didChangedTo state: \(state.rawValue), reason: \(reason.rawValue)", level: .info) + switch state { + case .openCompleted, .playBackCompleted, .playBackAllLoopsCompleted: + let ret = mediaPlayer?.play() + LogUtils.log(message: "play media ret:\(ret ?? -1)", level: .info) + default: + break + } + } + + func AgoraRtcMediaPlayer(_ playerKit: AgoraRtcMediaPlayerProtocol, didChangedTo positionMs: Int, atTimestamp timestampMs: TimeInterval) { + LogUtils.log(message: "didChangedTo positionMs: \(positionMs), timestampMs: \(timestampMs)", level: .info) + } +} + +extension TransparentRenderViewController: AgoraRtcMediaPlayerVideoFrameDelegate { + func AgoraRtcMediaPlayer(_ playerKit: AgoraRtcMediaPlayerProtocol, didReceiveVideoFrame videoFrame: AgoraOutputVideoFrame) { + let newVideoFrame = AgoraVideoFrame() + newVideoFrame.format = 12 + newVideoFrame.textureBuf = videoFrame.pixelBuffer + newVideoFrame.rotation = videoFrame.rotation + newVideoFrame.alphaStitchMode = .alphaStitchRight + let _ = agoraKit.pushExternalVideoFrame(newVideoFrame) +// LogUtils.log(message: "pushExternalVideoFrame: \(ret)", level: .info) + } +} diff --git a/iOS/APIExample/APIExample/Examples/Advanced/VideoMetadata/VideoMetadata.swift b/iOS/APIExample/APIExample/Examples/Advanced/VideoMetadata/VideoMetadata.swift index 0c4a1c245..2ddaedb94 100644 --- a/iOS/APIExample/APIExample/Examples/Advanced/VideoMetadata/VideoMetadata.swift +++ b/iOS/APIExample/APIExample/Examples/Advanced/VideoMetadata/VideoMetadata.swift @@ -272,11 +272,14 @@ extension VideoMetadataMain: AgoraMediaMetadataDelegate, AgoraMediaMetadataDataS return metadata } - /// Callback when the local user receives the metadata. - /// @param data The received metadata. - /// @param uid The ID of the user who sends the metadata. - /// @param timestamp The timestamp (ms) of the received metadata. - func receiveMetadata(_ data: Data, fromUser uid: Int, atTimestamp timestamp: TimeInterval) { + /** Occurs when the local user receives the metadata. + * + * @param metadata The received metadata. See \ref AgoraMetadata. + */ + func didMetadataReceived(_ metadata: AgoraMetadata) { + let data = metadata.data + let uid = metadata.uid + let timestamp = metadata.timestamp DispatchQueue.main.async { LogUtils.log(message: "metadata received", level: .info) let alert = UIAlertController(title: "Metadata received", message: String(data: data, encoding: .utf8), preferredStyle: .alert) diff --git a/iOS/APIExample/APIExample/Examples/Advanced/VideoProcess/Base.lproj/VideoProcess.storyboard b/iOS/APIExample/APIExample/Examples/Advanced/VideoProcess/Base.lproj/VideoProcess.storyboard index 3c367780c..58b662400 100644 --- a/iOS/APIExample/APIExample/Examples/Advanced/VideoProcess/Base.lproj/VideoProcess.storyboard +++ b/iOS/APIExample/APIExample/Examples/Advanced/VideoProcess/Base.lproj/VideoProcess.storyboard @@ -1,9 +1,9 @@ - + - + @@ -18,7 +18,7 @@ - + @@ -44,13 +44,13 @@ - + @@ -87,89 +87,156 @@ - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - + + + - + - + - + @@ -217,19 +281,16 @@ - + - + @@ -244,7 +305,7 @@ - + - + - + - + + + + - - - - + @@ -343,61 +415,60 @@ - + - - - - - + + + + - - + - - - + - - - + + + - + - - - - - + + - - + @@ -409,6 +480,7 @@ + @@ -421,7 +493,7 @@ - + diff --git a/iOS/APIExample/APIExample/Examples/Advanced/VideoProcess/VideoProcess.swift b/iOS/APIExample/APIExample/Examples/Advanced/VideoProcess/VideoProcess.swift index f1b6c7ca3..0cabf8487 100644 --- a/iOS/APIExample/APIExample/Examples/Advanced/VideoProcess/VideoProcess.swift +++ b/iOS/APIExample/APIExample/Examples/Advanced/VideoProcess/VideoProcess.swift @@ -44,6 +44,8 @@ class VideoProcessMain: BaseViewController { @IBOutlet weak var smoothSlider: UISlider! @IBOutlet weak var strengthSlider: UISlider! @IBOutlet weak var skinProtectSlider: UISlider! + @IBOutlet weak var whiteningSlider: UISlider? + @IBOutlet weak var beautyScrollView: UIScrollView? var agoraKit: AgoraRtcEngineKit! var localVideo = Bundle.loadVideoView(type: .local, audioOnly: false) @@ -54,11 +56,21 @@ class VideoProcessMain: BaseViewController { var beautifyOption = AgoraBeautyOptions() var skinProtect = 0.5 var strength = 0.5 + var whintening = 0.5 + + private var makeupParams = [String: Any]() + private var enableFaceShape: Bool = false + private lazy var faceshapeOption = AgoraFaceShapeBeautyOptions() + private var beautyShapeParames = [String: Float]() override func viewDidLoad() { super.viewDidLoad() setupUI() + if let beautyScrollView = beautyScrollView { + beautyScrollView.contentSize = CGSize(width: 800, height: beautyScrollView.frame.size.height) + } + // get channel name from configs guard let channelName = configs["channelName"] as? String, let resolution = GlobalSettings.shared.getSetting(key: "resolution")?.selectedOption().value as? CGSize, @@ -167,17 +179,18 @@ class VideoProcessMain: BaseViewController { smoothSlider.value = beautifyOption.smoothnessLevel strengthSlider.value = Float(strength) skinProtectSlider.value = Float(skinProtect) + whiteningSlider?.value = Float(whintening) } @IBAction func onChangeBeauty(_ sender: UISwitch) { - if sender.isOn { - if agoraKit.isFeatureAvailable(onDevice: .videoPreprocessBeauty) { - agoraKit.setBeautyEffectOptions(true, options: beautifyOption) - } else { - ToastView.show(text: "The feature is unavailable in the device!") - } - } else { - agoraKit.setBeautyEffectOptions(false, options: beautifyOption) + guard agoraKit.isFeatureAvailable(onDevice: .videoPreprocessBeauty) else { + ToastView.show(text: "The feature is unavailable in the device!") + return + } + + agoraKit.setBeautyEffectOptions(sender.isOn, options: beautifyOption) + if let slider = whiteningSlider { + onWhinteningSlider(slider) } } @@ -238,6 +251,15 @@ class VideoProcessMain: BaseViewController { agoraKit.setColorEnhanceOptions(colorEnhanceSwitch.isOn, options: options) } + @IBAction func onWhinteningSlider(_ sender: UISlider) { + let options = AgoraFilterEffectOptions() + options.path = "built_in_whiten_filter" + options.strength = sender.value + whintening = Double(sender.value) + let ret = agoraKit.setFilterEffectOptions(beautySwitch.isOn, options: options) + print("onWhinteningSlider: \(ret), \(options.strength)") + } + @IBAction func onChangeVirtualBgSwtich(_ sender: UISwitch) { if sender.isOn { if agoraKit.isFeatureAvailable(onDevice: .videoPreprocessVirtualBackground) { @@ -368,3 +390,494 @@ extension VideoProcessMain: AgoraRtcEngineDelegate { remoteVideo.statsInfo?.updateAudioStats(stats) } } + +private let makeupList = [ + [ + "name": "Makeup_enable_mu".localized, + "key": "enable_mu", + "type": "switch" + ], [ + "name": "Makeup_browStyle".localized, + "key": "browStyle", + "type": "segment", + "value": ["Makeup_browStyle_value1".localized, "Makeup_browStyle_value2".localized, "Makeup_browStyle_value3".localized] + ], [ + "name": "Makeup_browColor".localized, + "key": "browColor", + "type": "segment", + "value": ["Makeup_browColor_value1".localized, "Makeup_browColor_value2".localized, "Makeup_browColor_value3".localized] + ], [ + "name": "Makeup_browStrength".localized, + "key": "browStrength", + "type": "slider", + "value": [0, 1] + ], [ + "name": "Makeup_lashStyle".localized, + "key": "lashStyle", + "type": "segment", + "value": ["Makeup_browStyle_value1".localized, "Makeup_browStyle_value2".localized, "Makeup_browStyle_value3".localized] + ], [ + "name": "Makeup_lashColor".localized, + "key": "lashColor", + "type": "segment", + "value": ["Makeup_browColor_value1".localized, "Makeup_browColor_value2".localized, "Makeup_browColor_value3".localized] + ], [ + "name": "Makeup_lashStrength".localized, + "key": "lashStrength", + "type": "slider", + "value": [0, 1] + ], [ + "name": "Makeup_shadowStyle".localized, + "key": "shadowStyle", + "type": "segment", + "value": ["Makeup_browStyle_value1".localized, "Makeup_browStyle_value2".localized, "Makeup_browStyle_value3".localized] + ], + // ["name": "Makeup_shadowColor".localized, "key": "shadowColor"], + [ + "name": "Makeup_shadowStrength".localized, + "key": "shadowStrength", + "type": "slider", + "value": [0, 1] + ], [ + "name": "Makeup_pupilStyle".localized, + "key": "pupilStyle", + "type": "segment", + "value": ["Makeup_browStyle_value1".localized, "Makeup_browStyle_value2".localized, "Makeup_browStyle_value3".localized] + ], + // ["name": "Makeup_pupilColor".localized, "key": "pupilColor"], + [ + "name": "Makeup_pupilStrength".localized, + "key": "pupilStrength", + "type": "slider", + "value": [0, 1] + ], [ + "name": "Makeup_blushStyle".localized, + "key": "blushStyle", + "type": "segment", + "value": ["Makeup_browStyle_value1".localized, "Makeup_browStyle_value2".localized, "Makeup_browStyle_value3".localized] + ], [ + "name": "Makeup_blushColor".localized, + "key": "blushColor", + "type": "segment", + "value": [ + "Makeup_blushColor_value1".localized, + "Makeup_blushColor_value2".localized, + "Makeup_blushColor_value3".localized, + "Makeup_blushColor_value4".localized, + "Makeup_blushColor_value5".localized + ] + ], [ + "name": "Makeup_blushStrength".localized, + "key": "blushStrength", + "type": "slider", + "value": [0, 1] + ], [ + "name": "Makeup_lipStyle".localized, + "key": "lipStyle", + "type": "segment", + "value": ["Makeup_browColor_value1".localized, "Makeup_browColor_value2".localized, "Makeup_browColor_value3".localized] + ], [ + "name": "Makeup_lipColor".localized, + "key": "lipColor", + "type": "segment", + "value": [ + "Makeup_blushColor_value1".localized, + "Makeup_blushColor_value2".localized, + "Makeup_blushColor_value3".localized, + "Makeup_blushColor_value4".localized, + "Makeup_blushColor_value5".localized + ] + ], [ + "name": "Makeup_lipStrength".localized, + "key": "lipStrength", + "type": "slider", + "value": [0, 1] + ] +] + +// MARK: make up setting +extension VideoProcessMain { + @IBAction func onShowMakeUpAction() { + // 创建自定义视图控制器 + let customAlertVC = UIViewController() + customAlertVC.modalPresentationStyle = .overFullScreen + customAlertVC.view.backgroundColor = .clear + + // 自定义内容视图 + let alertView = UIView() + alertView.translatesAutoresizingMaskIntoConstraints = false + alertView.backgroundColor = UIColor.white + alertView.layer.shadowColor = UIColor.black.cgColor + alertView.layer.shadowOpacity = 0.2 + alertView.layer.shadowOffset = CGSize(width: 0, height: 2) + alertView.layer.shadowRadius = 4 + + customAlertVC.view.addSubview(alertView) + + // 设置 alertView 的约束 + NSLayoutConstraint.activate([ + alertView.centerXAnchor.constraint(equalTo: customAlertVC.view.centerXAnchor), + alertView.centerYAnchor.constraint(equalTo: customAlertVC.view.centerYAnchor), + alertView.widthAnchor.constraint(equalTo: customAlertVC.view.widthAnchor, constant: -20), + alertView.heightAnchor.constraint(equalToConstant: 300) + ]) + + // 创建 scrollView + let scrollView = UIScrollView() + scrollView.translatesAutoresizingMaskIntoConstraints = false + alertView.addSubview(scrollView) + + NSLayoutConstraint.activate([ + scrollView.topAnchor.constraint(equalTo: alertView.topAnchor), + scrollView.leadingAnchor.constraint(equalTo: alertView.leadingAnchor), + scrollView.trailingAnchor.constraint(equalTo: alertView.trailingAnchor), + scrollView.bottomAnchor.constraint(equalTo: alertView.bottomAnchor, constant: -50) // 留出按钮位置 + ]) + + let contentView = UIView() + contentView.translatesAutoresizingMaskIntoConstraints = false + scrollView.addSubview(contentView) + + NSLayoutConstraint.activate([ + contentView.topAnchor.constraint(equalTo: scrollView.topAnchor), + contentView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor), + contentView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor), + contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor), + contentView.widthAnchor.constraint(equalTo: scrollView.widthAnchor) + ]) + + + // 添加 UILabels 和 UISliders 到 contentView + var lastLabel: UILabel? + for i in 0.. - + - + @@ -80,7 +80,7 @@ - + @@ -402,6 +402,40 @@ + + + + + + + + + + + + + + + + + @@ -531,8 +565,12 @@ - + + + + + @@ -621,6 +659,7 @@ + diff --git a/iOS/APIExample/APIExample/Examples/Advanced/VoiceChanger/VoiceChanger.swift b/iOS/APIExample/APIExample/Examples/Advanced/VoiceChanger/VoiceChanger.swift index 1945f79a1..a3b263e70 100644 --- a/iOS/APIExample/APIExample/Examples/Advanced/VoiceChanger/VoiceChanger.swift +++ b/iOS/APIExample/APIExample/Examples/Advanced/VoiceChanger/VoiceChanger.swift @@ -47,6 +47,7 @@ class VoiceChangerMain: BaseViewController { @IBOutlet weak var voiceConversionBtn: UIButton! @IBOutlet weak var equalizationFreqBtn: UIButton! @IBOutlet weak var reverbKeyBtn: UIButton! + @IBOutlet weak var voiceAiTunerBtn: UIButton! @IBOutlet weak var reverbValueSlider: UISlider! @IBOutlet weak var audioEffectParam1Slider: UISlider! @IBOutlet weak var audioEffectParam2Slider: UISlider! @@ -57,6 +58,7 @@ class VoiceChangerMain: BaseViewController { var audioViews: [UInt: VideoView] = [:] var equalizationFreq: AgoraAudioEqualizationBandFrequency = .band31 + var aitunerType: AgoraVoiceAITunerType? var equalizationGain: Int = 0 var reverbType: AgoraAudioReverbType = .dryLevel var reverbMap: [AgoraAudioReverbType: Int] = [ @@ -221,6 +223,16 @@ class VoiceChangerMain: BaseViewController { }) } + func getVoiceAITunerAction(_ type: AgoraVoiceAITunerType?) -> UIAlertAction { + let title = "\(type?.description() ?? "Off")" + return UIAlertAction(title: title, + style: .default, + handler: { [unowned self] _ in + self.updateVoiceAiTuner(type: type) + self.voiceAiTunerBtn.setTitle(title, for: .normal) + }) + } + func getAINSModeAction(_ ainsMode: AUDIO_AINS_MODE) -> UIAlertAction { return UIAlertAction(title: "\(ainsMode.description())", style: .default, @@ -404,6 +416,15 @@ class VoiceChangerMain: BaseViewController { reverbValueSlider.value = Float(reverbMap[reverbType] ?? 0) } + func updateVoiceAiTuner(type: AgoraVoiceAITunerType?) { + LogUtils.log(message: "onVoiceAITunerAction: \(type?.rawValue ?? -1)", level: .info) + if let type = type { + agoraKit.enableVoiceAITuner(true, type: type) + } else { + agoraKit.enableVoiceAITuner(false, type: .matureMale) + } + } + @IBAction func onLocalVoiceReverbKey(_ sender: UIButton) { let style: UIAlertController.Style = UIDevice.current.userInterfaceIdiom == .pad ? .alert : .actionSheet let alert = UIAlertController(title: "Set Reverb Key".localized, message: nil, preferredStyle: style) @@ -423,6 +444,24 @@ class VoiceChangerMain: BaseViewController { agoraKit.setLocalVoiceReverbOf(reverbType, withValue: value) } + @IBAction func onVoiceAITunerAction(_ sender: UIButton) { + let style: UIAlertController.Style = UIDevice.current.userInterfaceIdiom == .pad ? .alert : .actionSheet + let alert = UIAlertController(title: "Set_Voice_AI_Tuner".localized, message: nil, preferredStyle: style) + alert.addAction(getVoiceAITunerAction(nil)) + alert.addAction(getVoiceAITunerAction(.matureMale)) + alert.addAction(getVoiceAITunerAction(.freshMale)) + alert.addAction(getVoiceAITunerAction(.elegantFemale)) + alert.addAction(getVoiceAITunerAction(.sweetFemale)) + alert.addAction(getVoiceAITunerAction(.warmMaleSinging)) + alert.addAction(getVoiceAITunerAction(.gentleFemaleSinging)) + alert.addAction(getVoiceAITunerAction(.huskyMaleSinging)) + alert.addAction(getVoiceAITunerAction(.warmElegantFemaleSinging)) + alert.addAction(getVoiceAITunerAction(.powerfulMaleSinging)) + alert.addAction(getVoiceAITunerAction(.dreamyFemaleSinging)) + alert.addCancelAction() + present(alert, animated: true, completion: nil) + } + @IBAction func onVoiceFormantChange(_ sender: UISlider) { agoraKit.setLocalVoiceFormant(Double(sender.value)) } @@ -451,6 +490,8 @@ class VoiceChangerMain: BaseViewController { equalizationFreqBtn.setTitle("\(equalizationFreq.description())", for: .normal) reverbKeyBtn.setTitle("\(reverbType.description())", for: .normal) + voiceAiTunerBtn.setTitle(aitunerType?.description() ?? "Off", for: .normal) + // Before calling the method, you need to set the profile // parameter of setAudioProfile to AUDIO_PROFILE_MUSIC_HIGH_QUALITY(4) // or AUDIO_PROFILE_MUSIC_HIGH_QUALITY_STEREO(5), and to set 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 index ad4c20098..5fa45d957 100644 --- a/iOS/APIExample/APIExample/Examples/Basic/JoinChannelVideo(Recorder)/Base.lproj/JoinChannelVideoRecorder.storyboard +++ b/iOS/APIExample/APIExample/Examples/Basic/JoinChannelVideo(Recorder)/Base.lproj/JoinChannelVideoRecorder.storyboard @@ -1,9 +1,9 @@ - + - + @@ -76,17 +76,32 @@ + + + + diff --git a/iOS/APIExample/APIExample/Examples/Basic/JoinChannelVideo(Recorder)/JoinChannelVideoRecorder.swift b/iOS/APIExample/APIExample/Examples/Basic/JoinChannelVideo(Recorder)/JoinChannelVideoRecorder.swift index cb92323ef..ec2d1d5ad 100644 --- a/iOS/APIExample/APIExample/Examples/Basic/JoinChannelVideo(Recorder)/JoinChannelVideoRecorder.swift +++ b/iOS/APIExample/APIExample/Examples/Basic/JoinChannelVideo(Recorder)/JoinChannelVideoRecorder.swift @@ -105,6 +105,8 @@ class JoinChannelVideoRecorderEntry: UIViewController { } class JoinChannelVideoRecorder: BaseViewController { + private var channelName: String? + @IBOutlet var joinOrLeaveButton: UIButton? private lazy var localRecordButton: UIButton = { let button = UIButton() button.translatesAutoresizingMaskIntoConstraints = false @@ -144,6 +146,16 @@ class JoinChannelVideoRecorder: BaseViewController { remoteRecordButton.isHidden = remoteUid == 0 } } + private lazy var localPreviewRecord: AgoraMediaRecorder? = { + let streamInfo = AgoraRecorderStreamInfo() + streamInfo.channelId = title ?? "" + streamInfo.uid = localUid + streamInfo.type = .preview + let record = agoraKit.createMediaRecorder(withInfo: streamInfo) + record?.setMediaRecorderDelegate(self) + return record + }() + private lazy var localRecord: AgoraMediaRecorder? = { let streamInfo = AgoraRecorderStreamInfo() streamInfo.channelId = title ?? "" @@ -188,6 +200,9 @@ class JoinChannelVideoRecorder: BaseViewController { remoteRecordButton.widthAnchor.constraint(equalToConstant: 70).isActive = true remoteRecordButton.heightAnchor.constraint(equalToConstant: 30).isActive = true + joinOrLeaveButton?.setTitle("Leave Channel".localized, for: .selected) + joinOrLeaveButton?.setTitle("Join Channel".localized, for: .normal) + setupRTC() } @@ -208,6 +223,7 @@ class JoinChannelVideoRecorder: BaseViewController { let resolution = configs["resolution"] as? CGSize, let fps = configs["fps"] as? Int, let orientation = configs["orientation"] as? AgoraVideoOutputOrientationMode else {return} + self.channelName = channelName // make myself a broadcaster agoraKit.setClientRole(GlobalSettings.shared.getUserRole()) @@ -232,6 +248,10 @@ class JoinChannelVideoRecorder: BaseViewController { // Set audio route to speaker agoraKit.setDefaultAudioRouteToSpeakerphone(true) +// joinChannel(channelName: channelName) + } + + private func joinChannel(channelName: String) { // start joining channel // 1. Users can only see each other after they join the // same channel successfully using the same app id. @@ -276,16 +296,28 @@ class JoinChannelVideoRecorder: BaseViewController { @objc private func onTapLocalRecordButton(sender: UIButton) { sender.isSelected = !sender.isSelected - let path = storagePath + "/\(localUid).mp4" + var path: String? = nil + var recorder: AgoraMediaRecorder? = nil + if joinOrLeaveButton?.isSelected ?? false == true { + path = storagePath + "/\(localUid).mp4" + recorder = localRecord + } else { + path = storagePath + "/preview_\(localUid).mp4" + recorder = localPreviewRecord + agoraKit.startRecordingDeviceTest(500) + } + guard let path = path, let recorder = recorder else {return} if sender.isSelected { let config = AgoraMediaRecorderConfiguration() config.storagePath = path config.containerFormat = .MP4 config.maxDurationMs = 10 * 1000 - localRecord?.startRecording(config) + let ret = recorder.startRecording(config) + print("startRecording: \(ret)") } else { - localRecord?.stopRecording() + let ret = recorder.stopRecording() ToastView.show(text: path) + print("stopRecording: \(ret) path = \(path)") } } @objc @@ -307,6 +339,18 @@ class JoinChannelVideoRecorder: BaseViewController { deinit { AgoraRtcEngineKit.destroy() } + + @IBAction func onJoinOrLeave(sender: UIButton) { + sender.isSelected.toggle() + + if sender.isSelected { + print("join channel") + joinChannel(channelName: channelName ?? "") + } else { + print("leave channel") + agoraKit.leaveChannel() + } + } } extension JoinChannelVideoRecorder: AgoraMediaRecorderDelegate { diff --git a/iOS/APIExample/APIExample/Info.plist b/iOS/APIExample/APIExample/Info.plist index 989504b53..08cfa7552 100644 --- a/iOS/APIExample/APIExample/Info.plist +++ b/iOS/APIExample/APIExample/Info.plist @@ -47,6 +47,8 @@ audio + UIFileSharingEnabled + UILaunchStoryboardName LaunchScreen UIMainStoryboardFile diff --git a/iOS/APIExample/APIExample/Resources/yuv_limit_range_alpha_1280_540_right.mp4 b/iOS/APIExample/APIExample/Resources/yuv_limit_range_alpha_1280_540_right.mp4 new file mode 100644 index 000000000..8d3c6154d Binary files /dev/null and b/iOS/APIExample/APIExample/Resources/yuv_limit_range_alpha_1280_540_right.mp4 differ diff --git a/iOS/APIExample/APIExample/ViewController.swift b/iOS/APIExample/APIExample/ViewController.swift index 99b71f539..39d06995d 100644 --- a/iOS/APIExample/APIExample/ViewController.swift +++ b/iOS/APIExample/APIExample/ViewController.swift @@ -57,6 +57,7 @@ class ViewController: AGViewController { MenuItem(name: "Precall Test".localized, storyboard: "PrecallTest", controller: ""), MenuItem(name: "Media Player".localized, storyboard: "MediaPlayer", controller: ""), MenuItem(name: "Screen Share".localized, storyboard: "ScreenShare", controller: ""), + MenuItem(name: "Local Composite Graph".localized, storyboard: "LocalCompositeGraph", controller: ""), MenuItem(name: "Video Process".localized, storyboard: "VideoProcess", controller: "VideoProcess"), MenuItem(name: "Rhythm Player".localized, storyboard: "RhythmPlayer", controller: "RhythmPlayer"), MenuItem(name: "Create Data Stream".localized, storyboard: "CreateDataStream", controller: ""), @@ -70,9 +71,10 @@ class ViewController: AGViewController { controller: "KtvCopyrightMusic"), MenuItem(name: "Third Beautify".localized, storyboard: "ThirdBeautify", controller: ""), MenuItem(name: "ARKit".localized, storyboard: "ARKit", controller: ""), - MenuItem(name: "Audio Router(Third Party Player)".localized, storyboard: "AuidoRouterPlayer", controller: ""), + MenuItem(name: "Audio Router(Third Party Player)".localized, storyboard: "AudioRouterPlayer", controller: ""), MenuItem(name: "Audio Waveform".localized, storyboard: "AudioWaveform", controller: ""), - MenuItem(name: "Face Capture".localized, storyboard: "FaceCapture", controller: "") + MenuItem(name: "Face Capture".localized, storyboard: "FaceCapture", controller: ""), + MenuItem(name: "Transparent_Render".localized, storyboard: "TransparentRender", controller: "") ]) ] override func viewDidLoad() { diff --git a/iOS/APIExample/APIExample/zh-Hans.lproj/Localizable.strings b/iOS/APIExample/APIExample/zh-Hans.lproj/Localizable.strings index d994de594..ebb3d638b 100644 --- a/iOS/APIExample/APIExample/zh-Hans.lproj/Localizable.strings +++ b/iOS/APIExample/APIExample/zh-Hans.lproj/Localizable.strings @@ -32,6 +32,7 @@ "Precall Test" = "通话前网络/设备测试"; "Media Player" = "流媒体播放器"; "Screen Share" = "屏幕共享"; +"Local Composite Graph" = "本地合图"; "Super Resolution" = "超级分辨率"; "Media Channel Relay" = "跨频道流转发"; "Set Resolution" = "设置视频分辨率"; @@ -156,6 +157,8 @@ "Tap to place remote video canvas" = "点击屏幕以放置视频画布"; "Recording" = "开始录制"; "Stop Recording" = "停止录制"; +"Join Channel" = "加入频道"; +"Leave Channel" = "离开频道"; "PIP Support iOS 15+" = "画中画只支持iOS15及以上版本"; "Red" = "红色"; "Blue" = "蓝色"; @@ -183,3 +186,80 @@ "Front camera" = "前置"; "Rear camera" = "后置"; "The camera has changed:" = "摄像头已改变:"; +"CameraStabilizationMode" = "防抖:"; + +"General"="通用场景"; +"Meeting"="会议场景"; +"1v1"="1v1 视频通话"; +"Live Show"="秀场直播"; + +"Set_Voice_AI_Tuner"="设置AI调音器"; +"AI_Tunner_Mature_Male"="大叔声"; +"AI_Tunner_Fresh_Male"="清新男音"; +"AI_Tunner_Elegant_Female"="御姐音"; +"AI_Tunner_Sweet_Female"="萝莉音"; +"AI_Tunner_Warm_Male_Singing"="暖男歌声"; +"AI_Tunner_Gentle_Female_Singing"="温柔女歌声"; +"AI_Tunner_Husky_Male_Singing"="烟嗓叔音歌声"; +"AI_Tunner_Warm_Elegant_Female_Singing"="温暖御姐歌声"; +"AI_Tunner_Powerful_Male_Singing"="力量男歌声"; +"AI_Tunner_Dreamy_Female_Singing"="梦幻女歌声"; + +"Transparent_Render"="透明渲染"; + +"Makeup_enable_mu" = "美妆开关"; +"Makeup_browStyle" = "眉毛类型"; +"Makeup_browColor" = "眉毛颜色"; +"Makeup_browStrength" = "眉毛强度"; +"Makeup_lashStyle" = "眼睫毛类型"; +"Makeup_lashColor" = "眼睫毛颜色"; +"Makeup_lashStrength" = "眼睫毛强度"; +"Makeup_shadowStyle" = "眼影类型"; +"Makeup_shadowColor" = "眼影颜色"; +"Makeup_shadowStrength" = "眼影强度"; +"Makeup_pupilStyle" = "瞳孔类型"; +"Makeup_pupilColor" = "瞳孔颜色"; +"Makeup_pupilStrength" = "瞳孔强度"; +"Makeup_blushStyle" = "腮红类型"; +"Makeup_blushColor" = "腮红颜色"; +"Makeup_blushStrength" = "腮红强度"; +"Makeup_lipStyle" = "唇彩类型"; +"Makeup_lipColor" = "唇彩颜色"; +"Makeup_lipStrength" = "唇彩强度"; + +"Makeup_browStyle_value1"="关闭"; +"Makeup_browStyle_value2"="类型1"; +"Makeup_browStyle_value3"="类型2"; + +"Makeup_browColor_value1"="无"; +"Makeup_browColor_value2"="黑色"; +"Makeup_browColor_value3"="棕色"; + +"Makeup_blushColor_value1"="无"; +"Makeup_blushColor_value2"="1号色"; +"Makeup_blushColor_value3"="2号色"; +"Makeup_blushColor_value4"="3号色"; +"Makeup_blushColor_value5"="4号色"; +"Makeup_blushColor_value6"="5号色"; + + + + +"face_shape_enable"="美型开关"; +"face_shape_gender"="性别"; +"face_shape_gender_male"="男性"; +"face_shape_gender_female"="女性"; +"face_shape_intensity"="强度"; + +"face_shape_area_head_scale"="头部比例"; +"face_shape_area_forehead"="额头"; +"face_shape_area_face_contour"="脸型轮廓"; +"face_shape_area_face_length"="脸长"; +"face_shape_area_face_width"="脸宽"; +"face_shape_area_cheek_bone"="颧骨"; +"face_shape_area_cheek"="脸颊"; +"face_shape_area_chin"="下巴"; +"face_shape_area_eye_scale"="眼睛比例"; +"face_shape_area_nose_length"="鼻子长度"; +"face_shape_area_nose_width"="鼻子宽度"; +"face_shape_area_mouth_scale"="嘴巴比例"; diff --git a/iOS/APIExample/Podfile b/iOS/APIExample/Podfile index 6dccfd758..4e2cea8d0 100644 --- a/iOS/APIExample/Podfile +++ b/iOS/APIExample/Podfile @@ -8,9 +8,13 @@ target 'APIExample' do pod 'Floaty', '~> 4.2.0' pod 'AGEVideoLayout', '~> 1.0.2' pod 'CocoaAsyncSocket', '7.6.5' - pod 'ijkplayer', '~> 1.1.3' +# 如需测试SDK与三方播放器的”AudioRouter“兼容,可以使用ijkplayer或MobileVLCKit +# pod 'ijkplayer', '~> 1.1.3' +# pod 'MobileVLCKit', '3.5.1' pod 'SwiftLint', '~> 0.53.0' - pod 'AgoraRtcEngine_iOS', '4.3.1' + pod 'AgoraRtcEngine_iOS', '4.4.0' + pod 'SnapKit', '~> 5.7.0' + # pod 'sdk', :path => 'sdk.podspec' # pod 'senseLib', :path => 'sense.podspec' # pod 'bytedEffect', :path => 'bytedEffect.podspec' @@ -20,5 +24,15 @@ end target 'Agora-ScreenShare-Extension' do use_frameworks! # pod 'sdk', :path => 'sdk.podspec' - pod 'AgoraRtcEngine_iOS', '4.3.1' - + pod 'AgoraRtcEngine_iOS', '4.4.0' +end + +target 'SimpleFilter' do + use_frameworks! +# pod 'sdk', :path => 'sdk.podspec' + pod 'AgoraRtcEngine_iOS', '4.4.0' +end + +pre_install do |installer| +# system("sh .download_script.sh 4.3.2 true") +end diff --git a/iOS/APIExample/README.md b/iOS/APIExample/README.md index 22b9d207f..acefe6e3c 100644 --- a/iOS/APIExample/README.md +++ b/iOS/APIExample/README.md @@ -62,8 +62,8 @@ To build and run the sample application, get an App Id: Agora provides App certificate to generate Token. You can deploy and generate a token on your server, or use the console to generate a temporary token. In order to get the APP ID, you can open the agora console (https://console.agora.io/) to create a project with the App Certificate enabled, - then the APP Certificate can be found in the project detail page. - PS: If the project does not have certificates enabled, leave this field blank. + then the APP Certificate can be found in the project detail page.If the project does not have certificates enabled, leave this field blank. + PS: It is unsafe to place the App Certificate on the client side, it is recommended to place it on the server side to ensure that the App Certificate is not leaked. */ static var Certificate: String? = <#YOUR Certificate#> diff --git a/iOS/APIExample/README.zh.md b/iOS/APIExample/README.zh.md index c941622df..8552a1b05 100644 --- a/iOS/APIExample/README.zh.md +++ b/iOS/APIExample/README.zh.md @@ -56,8 +56,8 @@ pod install /** Agora 提供 App certificate 用以生成 Token。您可以在您的服务器部署并生成 Token,或者使用控制台生成临时的 Token。 - 进入声网控制台(https://console.agora.io/),创建一个带证书鉴权的项目,进入项目配置页,即可看到APP证书。 - 注意:如果项目没有开启证书鉴权,这个字段留空。 + 进入声网控制台(https://console.agora.io/),创建一个带证书鉴权的项目,进入项目配置页,即可看到APP证书。如果项目没有开启证书鉴权,这个字段留空。 + 注意:App证书放在客户端不安全,推荐放在服务端以确保 App 证书不会泄露。 */ static var Certificate: String? = <#YOUR Certificate#> ``` diff --git a/iOS/APIExample/cloud_build.sh b/iOS/APIExample/cloud_build.sh new file mode 100755 index 000000000..de0bac2cb --- /dev/null +++ b/iOS/APIExample/cloud_build.sh @@ -0,0 +1,116 @@ +#!/usr/bin/env sh + +PROJECT_PATH=$PWD + +if [ "$WORKSPACE" = "" ]; then + WORKSPACE=$PWD +fi +if [ "$BUILD_NUMBER" = "" ]; then + BUILD_NUMBER=888 +fi + + +cd ${PROJECT_PATH} + +#打开第三方播放器配置 +sed -i -e "s#\# pod 'ijkplayer'# pod 'ijkplayer'#g" Podfile + +pod install || exit 1 + +# 打包环境 +CONFIGURATION="Debug" + +#工程文件路径 +APP_PATH="$(ls | grep xcworkspace)" + +# 项目target名 +TARGET_NAME=${APP_PATH%%.*} + +KEYCENTER_PATH=$TARGET_NAME/Common/KeyCenter.swift + +#工程配置路径 +PBXPROJ_PATH=${TARGET_NAME}.xcodeproj/project.pbxproj + +# Debug +/usr/libexec/PlistBuddy -c "Set :objects:03D13BF72448758C00B599B3:buildSettings:CODE_SIGN_STYLE 'Manual'" $PBXPROJ_PATH +/usr/libexec/PlistBuddy -c "Set :objects:03D13BF72448758C00B599B3:buildSettings:DEVELOPMENT_TEAM 'GM72UGLGZW'" $PBXPROJ_PATH +/usr/libexec/PlistBuddy -c "Set :objects:03D13BF72448758C00B599B3:buildSettings:PROVISIONING_PROFILE_SPECIFIER 'App'" $PBXPROJ_PATH +# Release +/usr/libexec/PlistBuddy -c "Set :objects:03D13BF82448758C00B599B3:buildSettings:CODE_SIGN_STYLE 'Manual'" $PBXPROJ_PATH +/usr/libexec/PlistBuddy -c "Set :objects:03D13BF82448758C00B599B3:buildSettings:DEVELOPMENT_TEAM 'GM72UGLGZW'" $PBXPROJ_PATH +/usr/libexec/PlistBuddy -c "Set :objects:03D13BF82448758C00B599B3:buildSettings:PROVISIONING_PROFILE_SPECIFIER 'App'" $PBXPROJ_PATH + +# 屏幕共享Extension +# Debug +/usr/libexec/PlistBuddy -c "Set :objects:0339BEB825205B80007D4FDD:buildSettings:CODE_SIGN_STYLE 'Manual'" $PBXPROJ_PATH +/usr/libexec/PlistBuddy -c "Set :objects:0339BEB825205B80007D4FDD:buildSettings:DEVELOPMENT_TEAM 'GM72UGLGZW'" $PBXPROJ_PATH +/usr/libexec/PlistBuddy -c "Set :objects:0339BEB825205B80007D4FDD:buildSettings:PROVISIONING_PROFILE_SPECIFIER 'App'" $PBXPROJ_PATH +# Release +/usr/libexec/PlistBuddy -c "Set :objects:0339BEB925205B80007D4FDD:buildSettings:CODE_SIGN_STYLE 'Manual'" $PBXPROJ_PATH +/usr/libexec/PlistBuddy -c "Set :objects:0339BEB925205B80007D4FDD:buildSettings:DEVELOPMENT_TEAM 'GM72UGLGZW'" $PBXPROJ_PATH +/usr/libexec/PlistBuddy -c "Set :objects:0339BEB925205B80007D4FDD:buildSettings:PROVISIONING_PROFILE_SPECIFIER 'App'" $PBXPROJ_PATH + +# SimpleFilter +# Debug +/usr/libexec/PlistBuddy -c "Set :objects:8B10BE1726AFFFA6002E1373:buildSettings:CODE_SIGN_STYLE 'Manual'" $PBXPROJ_PATH +/usr/libexec/PlistBuddy -c "Set :objects:8B10BE1726AFFFA6002E1373:buildSettings:DEVELOPMENT_TEAM ''" $PBXPROJ_PATH +/usr/libexec/PlistBuddy -c "Set :objects:8B10BE1726AFFFA6002E1373:buildSettings:PROVISIONING_PROFILE_SPECIFIER ''" $PBXPROJ_PATH +# Release +/usr/libexec/PlistBuddy -c "Set :objects:8B10BE1826AFFFA6002E1373:buildSettings:CODE_SIGN_STYLE 'Manual'" $PBXPROJ_PATH +/usr/libexec/PlistBuddy -c "Set :objects:8B10BE1826AFFFA6002E1373:buildSettings:DEVELOPMENT_TEAM ''" $PBXPROJ_PATH +/usr/libexec/PlistBuddy -c "Set :objects:8B10BE1826AFFFA6002E1373:buildSettings:PROVISIONING_PROFILE_SPECIFIER ''" $PBXPROJ_PATH + +#修改build number +# Debug +/usr/libexec/PlistBuddy -c "Set :objects:03D13BF72448758C00B599B3:buildSettings:CURRENT_PROJECT_VERSION ${BUILD_NUMBER}" $PBXPROJ_PATH +# Release +/usr/libexec/PlistBuddy -c "Set :objects:03D13BF82448758C00B599B3:buildSettings:CURRENT_PROJECT_VERSION ${BUILD_NUMBER}" $PBXPROJ_PATH + +# 读取APPID环境变量 +echo AGORA_APP_ID: $APP_ID + +echo PROJECT_PATH: $PROJECT_PATH +echo TARGET_NAME: $TARGET_NAME +echo KEYCENTER_PATH: $KEYCENTER_PATH +echo APP_PATH: $APP_PATH + +#修改Keycenter文件 +sed -i -e "s#<\#YOUR AppId\#>#\"$APP_ID\"#g" $KEYCENTER_PATH +rm -f ${KEYCENTER_PATH}-e + +# Xcode clean +xcodebuild clean -workspace "${APP_PATH}" -configuration "${CONFIGURATION}" -scheme "${TARGET_NAME}" + +# 时间戳 +CURRENT_TIME=$(date "+%Y-%m-%d %H-%M-%S") + +# 归档路径 +ARCHIVE_PATH="${WORKSPACE}/${TARGET_NAME}_${BUILD_NUMBER}.xcarchive" + +# 编译环境 + +# plist路径 +PLIST_PATH="${PROJECT_PATH}/ExportOptions.plist" + + +# archive 这边使用的工作区间 也可以使用project +xcodebuild CODE_SIGN_STYLE="Manual" archive -workspace "${APP_PATH}" -scheme "${TARGET_NAME}" clean CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO -configuration "${CONFIGURATION}" -archivePath "${ARCHIVE_PATH}" -destination 'generic/platform=iOS' -quiet || exit 1 + +cd ${WORKSPACE} + +# 压缩archive +7za a -tzip "${TARGET_NAME}_${BUILD_NUMBER}.xcarchive.zip" "${ARCHIVE_PATH}" + +# 签名 +# sh sign "${TARGET_NAME}_${BUILD_NUMBER}.xcarchive.zip" --type xcarchive --plist "${PLIST_PATH}" +sh export "${TARGET_NAME}_${BUILD_NUMBER}.xcarchive.zip" --plist "${PLIST_PATH}" + +SDK_VERSION=$(echo $sdk_url | cut -d "/" -f 5) +OUTPUT_FILE=${WORKSPACE}/${TARGET_NAME}_${BUILD_NUMBER}_${SDK_VERSION}_$(date "+%Y%m%d%H%M%S").ipa +mv ${TARGET_NAME}_${BUILD_NUMBER}.ipa $OUTPUT_FILE + +rm -rf *.xcarchive +rm -rf *.xcarchive.zip +echo OUTPUT_FILE: $OUTPUT_FILE + + diff --git a/macOS/APIExample.xcodeproj/project.pbxproj b/macOS/APIExample.xcodeproj/project.pbxproj index 8bc7e7700..e1a687d0c 100644 --- a/macOS/APIExample.xcodeproj/project.pbxproj +++ b/macOS/APIExample.xcodeproj/project.pbxproj @@ -91,6 +91,7 @@ 8BD4AE73272513FF00E95B87 /* SimpleFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BD4AE72272513FF00E95B87 /* SimpleFilter.swift */; }; 8BE63B4227253CB000597DB1 /* SimpleFilter.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8BE63B4427253CB000597DB1 /* SimpleFilter.storyboard */; }; 8BF2243B275F82CF00B65EF8 /* SimpleFilter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 8BD4AE79272518D600E95B87 /* SimpleFilter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + DD8A1F922CA55AA4001CEC51 /* AgoraPCMPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8A1F912CA55AA4001CEC51 /* AgoraPCMPlayer.swift */; }; E702C1E728B4DB4800D7C7ED /* LiveStreaming.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E702C1E328B4DB4800D7C7ED /* LiveStreaming.storyboard */; }; E702C1E828B4DB4800D7C7ED /* LiveStreaming.swift in Sources */ = {isa = PBXBuildFile; fileRef = E702C1E528B4DB4800D7C7ED /* LiveStreaming.swift */; }; E71E7B0B289B7D7900B846C7 /* SimpleFilter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BD4AE79272518D600E95B87 /* SimpleFilter.framework */; }; @@ -271,6 +272,7 @@ 8BD4AE72272513FF00E95B87 /* SimpleFilter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleFilter.swift; sourceTree = ""; }; 8BD4AE79272518D600E95B87 /* SimpleFilter.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SimpleFilter.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 8BE63B4527253CD900597DB1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/SimpleFilter.storyboard; sourceTree = ""; }; + DD8A1F912CA55AA4001CEC51 /* AgoraPCMPlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AgoraPCMPlayer.swift; sourceTree = ""; }; E702C1E528B4DB4800D7C7ED /* LiveStreaming.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LiveStreaming.swift; sourceTree = ""; }; E71E7B0D289B96FA00B846C7 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/VideoProcess.storyboard; sourceTree = ""; }; E71E7B12289B971900B846C7 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/VideoProcess.strings; sourceTree = ""; }; @@ -381,6 +383,7 @@ 0336A1BB25034F4600D61B7F /* ExternalAudio */ = { isa = PBXGroup; children = ( + DD8A1F912CA55AA4001CEC51 /* AgoraPCMPlayer.swift */, 0336A1BC25034F4600D61B7F /* AudioOptions.h */, 0336A1C325034F4700D61B7F /* AudioWriteToFile.h */, 0336A1BD25034F4600D61B7F /* AudioWriteToFile.m */, @@ -1192,6 +1195,7 @@ E7883AE22B046CB8003CCF44 /* FaceCapture.swift in Sources */, 0336A1CA25034F4700D61B7F /* ExternalAudio.mm in Sources */, E779025B2A482CFC008791AD /* KFMP4Demuxer.m in Sources */, + DD8A1F922CA55AA4001CEC51 /* AgoraPCMPlayer.swift in Sources */, 033A9FA1252EA86A00BC26E1 /* CustomAudioRender.swift in Sources */, 034C627C2526C43900296ECF /* ScreenShare.swift in Sources */, 034C62712525A35800296ECF /* StreamEncryption.swift in Sources */, diff --git a/macOS/APIExample/Common/AgoraExtension.swift b/macOS/APIExample/Common/AgoraExtension.swift index 6d89b0bb5..8bb8b9fcf 100644 --- a/macOS/APIExample/Common/AgoraExtension.swift +++ b/macOS/APIExample/Common/AgoraExtension.swift @@ -99,6 +99,23 @@ extension AUDIO_AINS_MODE { } } +extension AgoraVoiceAITunerType { + func description() -> String? { + switch self { + case .matureMale: return "AI_Tunner_Mature_Male".localized + case .freshMale: return "AI_Tunner_Fresh_Male".localized + case .elegantFemale: return "AI_Tunner_Elegant_Female".localized + case .sweetFemale: return "AI_Tunner_Sweet_Female".localized + case .warmMaleSinging: return "AI_Tunner_Warm_Male_Singing".localized + case .gentleFemaleSinging: return "AI_Tunner_Gentle_Female_Singing".localized + case .huskyMaleSinging: return "AI_Tunner_Husky_Male_Singing".localized + case .warmElegantFemaleSinging: return "AI_Tunner_Warm_Elegant_Female_Singing".localized + case .powerfulMaleSinging: return "AI_Tunner_Powerful_Male_Singing".localized + case .dreamyFemaleSinging: return "AI_Tunner_Dreamy_Female_Singing".localized + @unknown default: return nil + } + } +} extension AgoraVirtualBackgroundSourceType { func description() -> String { diff --git a/macOS/APIExample/Common/ExternalAudio/AgoraPCMPlayer.swift b/macOS/APIExample/Common/ExternalAudio/AgoraPCMPlayer.swift new file mode 100644 index 000000000..8f469bf54 --- /dev/null +++ b/macOS/APIExample/Common/ExternalAudio/AgoraPCMPlayer.swift @@ -0,0 +1,56 @@ +// +// AgoraPCMPlayer.swift +// APIExample +// +// Created by wushengtao on 2024/9/26. +// Copyright © 2024 Agora Corp. All rights reserved. +// + +import Foundation + +class AgoraPCMPlayer { + private var audioEngine: AVAudioEngine + private var playerNode: AVAudioPlayerNode + private var sampleRate: Double + private var channels: AVAudioChannelCount + + init(sampleRate: Double, channels: AVAudioChannelCount) { + self.sampleRate = sampleRate + self.channels = channels + + audioEngine = AVAudioEngine() + playerNode = AVAudioPlayerNode() + + audioEngine.attach(playerNode) + + let format = AVAudioFormat(standardFormatWithSampleRate: sampleRate, channels: channels) + audioEngine.connect(playerNode, to: audioEngine.mainMixerNode, format: format) + + do { + try audioEngine.start() + } catch { + print("Audio Engine failed to start: \(error)") + } + } + + func playPCMData(pcmData: UnsafeMutablePointer, count: UInt) { + guard let format = AVAudioFormat(standardFormatWithSampleRate: sampleRate, channels: channels), + let audioBuffer = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: AVAudioFrameCount(count / 4)), // 16位立体声每帧4字节 + let channelData = audioBuffer.floatChannelData else { + return + } + + audioBuffer.frameLength = AVAudioFrameCount(count / 4) + + for frame in 0..= viewRatio) { videoSize.height = viewHeight; - videoSize.width = videoSize.height * videoRatio; + videoSize.width = viewHeight * videoRatio; }else { videoSize.width = viewWidth; - videoSize.height = videoSize.width / videoRatio; + videoSize.height = viewWidth / videoRatio; } - CGRect renderRect = CGRectMake(0.5 * (viewWidth - videoSize.width), 0.5 * (viewHeight - videoSize.height), videoSize.width, videoSize.height); - + CGRect renderRect = CGRectMake(0, 0, viewWidth, viewHeight); if (!CGRectEqualToRect(renderRect, self.displayLayer.frame)) { self.displayLayer.frame = renderRect; } } - (void)reset { + self.displayLayer.frame = CGRectMake(0, 0, 0, 0); [self.displayLayer flushAndRemoveImage]; } @@ -206,10 +206,10 @@ - (void)renderVideoPixelBuffer:(AgoraOutputVideoFrame *_Nonnull)videoData { CMSampleBufferCreateReadyWithImageBuffer(kCFAllocatorDefault, pixelBuffer, videoInfo, &timingInfo, &sampleBuffer); if (sampleBuffer) { [self.displayLayer enqueueSampleBuffer:sampleBuffer]; - [self.displayLayer setNeedsDisplay]; - [_displayLayer display]; - [self.layer display]; - CMSampleBufferInvalidate(sampleBuffer); +// [self.displayLayer setNeedsDisplay]; +// [_displayLayer display]; +// [self.layer display]; +// CMSampleBufferInvalidate(sampleBuffer); CFRelease(sampleBuffer); } } diff --git a/macOS/APIExample/Common/KeyCenter.swift b/macOS/APIExample/Common/KeyCenter.swift index fd440e323..7de1cee79 100644 --- a/macOS/APIExample/Common/KeyCenter.swift +++ b/macOS/APIExample/Common/KeyCenter.swift @@ -30,12 +30,12 @@ struct KeyCenter { Agora provides App certificate to generate Token. You can deploy and generate a token on your server, or use the console to generate a temporary token. In order to get the APP ID, you can open the agora console (https://console.agora.io/) to create a project with the App Certificate enabled, - then the APP Certificate can be found in the project detail page. - PS: If the project does not have certificates enabled, leave this field blank. + then the APP Certificate can be found in the project detail page.If the project does not have certificates enabled, leave this field blank. + PS: It is unsafe to place the App Certificate on the client side, it is recommended to place it on the server side to ensure that the App Certificate is not leaked. 声网APP证书 Agora 提供 App certificate 用以生成 Token。您可以在您的服务器部署并生成 Token,或者使用控制台生成临时的 Token。 - 进入声网控制台(https://console.agora.io/),创建一个带证书鉴权的项目,进入项目配置页,即可看到APP证书。 - 注意:如果项目没有开启证书鉴权,这个字段留空。 + 进入声网控制台(https://console.agora.io/),创建一个带证书鉴权的项目,进入项目配置页,即可看到APP证书。如果项目没有开启证书鉴权,这个字段留空。 + 注意:App证书放在客户端不安全,推荐放在服务端以确保 App 证书不会泄露。 */ static let Certificate: String? = nil diff --git a/macOS/APIExample/Common/NetworkManager/NetworkManager.swift b/macOS/APIExample/Common/NetworkManager/NetworkManager.swift index c6125bf39..0eac9764c 100644 --- a/macOS/APIExample/Common/NetworkManager/NetworkManager.swift +++ b/macOS/APIExample/Common/NetworkManager/NetworkManager.swift @@ -30,7 +30,7 @@ class NetworkManager { static let shared = NetworkManager() private init() { } - private let baseUrl = "https://test-toolbox.bj2.agoralab.co/v1/token/generate" + private let baseUrl = "https://service.agora.io/toolbox-global/v1/token/generate" func generateToken(channelName: String, uid: UInt = 0, success: @escaping (String?) -> Void) { if KeyCenter.Certificate == nil || KeyCenter.Certificate?.isEmpty == true { @@ -78,10 +78,16 @@ class NetworkManager { } DispatchQueue.global().async { let downloadTask = session.downloadTask(with: request) { location, response, error in - let locationPath = location!.path + guard error == nil, let locationPath = location?.path else { + failure?(error?.localizedDescription ?? "download fail") + return + } + let fileManager = FileManager.default try? fileManager.moveItem(atPath: locationPath, toPath: documnets) - success?(["fileName": fileName, "path": documnets]) + DispatchQueue.main.async { + success?(["fileName": fileName, "path": documnets]) + } } downloadTask.resume() } diff --git a/macOS/APIExample/Common/Utils/KFMP4Demuxer.m b/macOS/APIExample/Common/Utils/KFMP4Demuxer.m index 1bfae527a..6db4f35ed 100644 --- a/macOS/APIExample/Common/Utils/KFMP4Demuxer.m +++ b/macOS/APIExample/Common/Utils/KFMP4Demuxer.m @@ -57,6 +57,7 @@ @interface KFMP4Demuxer () { @implementation KFMP4Demuxer #pragma mark - LifeCycle - (instancetype)initWithConfig:(KFDemuxerConfig *)config { + NSLog(@"KFMP4Demuxer init"); self = [super init]; if (self) { _config = config; @@ -72,6 +73,7 @@ - (instancetype)initWithConfig:(KFDemuxerConfig *)config { } - (void)dealloc { + NSLog(@"KFMP4Demuxer dealloc"); // 清理状态机。 if (self.demuxerStatus == KFMP4DemuxerStatusRunning) { self.demuxerStatus = KFMP4DemuxerStatusCancelled; @@ -105,26 +107,27 @@ - (void)dealloc { - (void)startReading { __weak typeof(self) weakSelf = self; dispatch_async(_demuxerQueue, ^{ - dispatch_semaphore_wait(weakSelf.demuxerSemaphore, DISPATCH_TIME_FOREVER); + __strong typeof(self) strongSelf = weakSelf; + dispatch_semaphore_wait(strongSelf.demuxerSemaphore, DISPATCH_TIME_FOREVER); // 在第一次开始读数据时,创建解封装器实例。 - if (!weakSelf.demuxReader) { + if (!strongSelf.demuxReader) { NSError *error; - [weakSelf _setupDemuxReader:&error]; - weakSelf.audioEOF = !weakSelf.hasAudioTrack; - weakSelf.videoEOF = !weakSelf.hasVideoTrack; - weakSelf.demuxerStatus = error ? KFMP4DemuxerStatusFailed : KFMP4DemuxerStatusRunning; - dispatch_semaphore_signal(weakSelf.demuxerSemaphore); + [strongSelf _setupDemuxReader:&error]; + strongSelf.audioEOF = !strongSelf.hasAudioTrack; + strongSelf.videoEOF = !strongSelf.hasVideoTrack; + strongSelf.demuxerStatus = error ? KFMP4DemuxerStatusFailed : KFMP4DemuxerStatusRunning; + dispatch_semaphore_signal(strongSelf.demuxerSemaphore); if (error == nil) { // Demuxer 启动成功后,就可以从它里面获取解封装后的数据了。 - [weakSelf fetchAndSaveDemuxedData]; + [strongSelf fetchAndSaveDemuxedData]; } else { NSLog(@"KFMP4Demuxer error: %zi %@", error.code, error.localizedDescription); } return; } - dispatch_semaphore_signal(weakSelf.demuxerSemaphore); + dispatch_semaphore_signal(strongSelf.demuxerSemaphore); }); } @@ -132,16 +135,17 @@ - (void)cancelReading { __weak typeof(self) weakSelf = self; if (self.demuxerQueue == nil) { return; } dispatch_async(_demuxerQueue, ^{ - if (weakSelf.demuxerSemaphore == nil) { return; } - dispatch_semaphore_wait(weakSelf.demuxerSemaphore, DISPATCH_TIME_FOREVER); + __strong typeof(self) strongSelf = weakSelf; + if (strongSelf.demuxerSemaphore == nil) { return; } + dispatch_semaphore_wait(strongSelf.demuxerSemaphore, DISPATCH_TIME_FOREVER); // 取消读数据。 - if (weakSelf.demuxReader && weakSelf.demuxReader.status == AVAssetReaderStatusReading) { - [weakSelf.demuxReader cancelReading]; + if (strongSelf.demuxReader && strongSelf.demuxReader.status == AVAssetReaderStatusReading) { + [strongSelf.demuxReader cancelReading]; } - weakSelf.demuxerStatus = KFMP4DemuxerStatusCancelled; + strongSelf.demuxerStatus = KFMP4DemuxerStatusCancelled; - dispatch_semaphore_signal(weakSelf.demuxerSemaphore); + dispatch_semaphore_signal(strongSelf.demuxerSemaphore); }); } @@ -455,9 +459,10 @@ - (void)_asyncLoadNextSampleBuffer { // 异步加载下一份采样数据。 __weak typeof(self) weakSelf = self; dispatch_async(_demuxerQueue, ^{ - dispatch_semaphore_wait(weakSelf.demuxerSemaphore, DISPATCH_TIME_FOREVER); - [weakSelf _loadNextSampleBuffer]; - dispatch_semaphore_signal(weakSelf.demuxerSemaphore); + __strong typeof(self) strongSelf = weakSelf; + dispatch_semaphore_wait(strongSelf.demuxerSemaphore, DISPATCH_TIME_FOREVER); + [strongSelf _loadNextSampleBuffer]; + dispatch_semaphore_signal(strongSelf.demuxerSemaphore); }); } diff --git a/macOS/APIExample/Examples/Advanced/ChannelMediaRelay/ChannelMediaRelay.swift b/macOS/APIExample/Examples/Advanced/ChannelMediaRelay/ChannelMediaRelay.swift index fa2ff91b1..87dbc9b96 100644 --- a/macOS/APIExample/Examples/Advanced/ChannelMediaRelay/ChannelMediaRelay.swift +++ b/macOS/APIExample/Examples/Advanced/ChannelMediaRelay/ChannelMediaRelay.swift @@ -15,6 +15,8 @@ class ChannelMediaRelay: BaseViewController { @IBOutlet weak var Container: AGEVideoContainer! var agoraKit: AgoraRtcEngineKit! + // configure source info, channel name defaults to current, and uid defaults to local + let mediaRelayconfig = AgoraChannelMediaRelayConfiguration() /** --- Channel TextField --- @@ -58,15 +60,15 @@ class ChannelMediaRelay: BaseViewController { self.showAlert(message: "Destination channel name is empty") return } - // configure source info, channel name defaults to current, and uid defaults to local - let config = AgoraChannelMediaRelayConfiguration() - config.sourceInfo = AgoraChannelMediaRelayInfo(token: nil) + isProcessing = true - // configure target channel info - let destinationInfo = AgoraChannelMediaRelayInfo(token: nil) - config.setDestinationInfo(destinationInfo, forChannelName: destinationChannelName) - agoraKit.startOrUpdateChannelMediaRelay(config) - pauseRelayButton.isEnabled = true + NetworkManager.shared.generateToken(channelName: destinationChannelName) { token in + // configure target channel info + let destinationInfo = AgoraChannelMediaRelayInfo(token: token) + self.mediaRelayconfig.setDestinationInfo(destinationInfo, forChannelName: destinationChannelName) + self.agoraKit.startOrUpdateChannelMediaRelay(self.mediaRelayconfig) + self.pauseRelayButton.isEnabled = true + } } else { isProcessing = true isPauseRelaying = false @@ -211,6 +213,7 @@ class ChannelMediaRelay: BaseViewController { // cn: https://doc.shengwang.cn/api-ref/rtc/ios/error-code self.showAlert(title: "Error", message: "joinChannel call failed: \(result), please check your params") } + self.mediaRelayconfig.sourceInfo = AgoraChannelMediaRelayInfo(token: token) }) } else { isProcessing = true diff --git a/macOS/APIExample/Examples/Advanced/CustomAudioRender/CustomAudioRender.swift b/macOS/APIExample/Examples/Advanced/CustomAudioRender/CustomAudioRender.swift index fdfbaba1f..6c60993f7 100644 --- a/macOS/APIExample/Examples/Advanced/CustomAudioRender/CustomAudioRender.swift +++ b/macOS/APIExample/Examples/Advanced/CustomAudioRender/CustomAudioRender.swift @@ -9,11 +9,17 @@ import Cocoa import AgoraRtcKit import AGEVideoLayout +private let bitsPerSample: UInt = 16 +private let samples: UInt = 441 * 10 +private let sampleRate: UInt = 44100 +private let channels: UInt = 2 + class CustomAudioRender: BaseViewController { var agoraKit: AgoraRtcEngineKit! - var exAudio: ExternalAudio = ExternalAudio.shared() - + + lazy var player = AgoraPCMPlayer(sampleRate: Double(sampleRate), channels: AVAudioChannelCount(channels)) + var videos: [VideoView] = [] @IBOutlet weak var Container: AGEVideoContainer! @@ -134,11 +140,12 @@ class CustomAudioRender: BaseViewController { override func viewWillBeRemovedFromSplitView() { if isJoined { - self.exAudio.stopWork() agoraKit.leaveChannel { (stats:AgoraChannelStats) in LogUtils.log(message: "Left channel", level: .info) } } + agoraKit.enableExternalAudioSink(false, sampleRate: sampleRate, channels: channels) + isJoined = false AgoraRtcEngineKit.destroy() } @@ -164,15 +171,8 @@ class CustomAudioRender: BaseViewController { // set live broadcaster mode agoraKit.setChannelProfile(.liveBroadcasting) - // set myself as broadcaster to stream audio - agoraKit.setClientRole(.broadcaster) - // setup external audio source - exAudio.setupExternalAudio(withAgoraKit: agoraKit, sampleRate: UInt32(sampleRate), channels: UInt32(audioChannel), audioCRMode: .sdkCaptureExterRender, ioType: .remoteIO) - // important!! this example is using onPlaybackAudioFrame to do custom rendering - // by default the audio output will still be processed by SDK hence below api call is mandatory to disable that behavior - agoraKit.setParameters("{\"che.audio.external_render\": true}") - agoraKit.setParameters("{\"che.audio.keep.audiosession\": true}") + // start joining channel // 1. Users can only see each other after they join the @@ -182,8 +182,14 @@ class CustomAudioRender: BaseViewController { // the token has to match the ones used for channel join isProcessing = true let option = AgoraRtcChannelMediaOptions() - option.publishCameraTrack = true + option.publishCameraTrack = false + option.publishMicrophoneTrack = false + option.autoSubscribeAudio = true + option.autoSubscribeVideo = false + option.channelProfile = .liveBroadcasting option.clientRoleType = .broadcaster + + agoraKit.enableExternalAudioSink(true, sampleRate: sampleRate, channels: channels) NetworkManager.shared.generateToken(channelName: channel, success: { token in let result = self.agoraKit.joinChannel(byToken: token, channelId: channel, uid: 0, mediaOptions: option) if result != 0 { @@ -224,6 +230,27 @@ class CustomAudioRender: BaseViewController { // layout render view Container.layoutStream(views: videos) } + + private func startPullAudio() { + DispatchQueue.global().async { + let pullMs: TimeInterval = 100 + let lengthInByte = sampleRate / 1000 * 2 * channels * UInt(pullMs) + let pointer = UnsafeMutablePointer.allocate(capacity: Int(lengthInByte)) + var deltaMs: TimeInterval = 0 + while self.isJoined { + let date = Date() + memset(pointer, 0, Int(lengthInByte)) + let ret = self.agoraKit.pullPlaybackAudioFrameRawData(pointer, lengthInByte: lengthInByte) + self.player.playPCMData(pcmData: pointer, count: lengthInByte) + let cost = -date.timeIntervalSinceNow * 1000 + usleep(UInt32(max((pullMs - cost - deltaMs) * 1000, 0))) + // add deltaMs to ensure that the thread processing time is less than pullMs, as thread sleep may not be accurate + deltaMs = max((-date.timeIntervalSinceNow * 1000) - pullMs, 0) * 2 +// print("pullPlaybackAudioFrameRawData: \(ret) lengthInByte: \(lengthInByte) cost: \(-date.timeIntervalSinceNow * 1000) ms, deltaMs: \(deltaMs) ms") + } + pointer.deallocate() + } + } } /// agora rtc engine delegate events @@ -259,7 +286,7 @@ extension CustomAudioRender: AgoraRtcEngineDelegate { let localVideo = videos[0] localVideo.uid = uid LogUtils.log(message: "Join \(channel) with uid \(uid) elapsed \(elapsed)ms", level: .info) - exAudio.startWork() + startPullAudio() } /// callback when a remote user is joinning the channel, note audience in live broadcast mode will NOT trigger this event diff --git a/macOS/APIExample/Examples/Advanced/CustomAudioSource/CustomAudioSource.swift b/macOS/APIExample/Examples/Advanced/CustomAudioSource/CustomAudioSource.swift index 884ad7cf5..5a4764be8 100644 --- a/macOS/APIExample/Examples/Advanced/CustomAudioSource/CustomAudioSource.swift +++ b/macOS/APIExample/Examples/Advanced/CustomAudioSource/CustomAudioSource.swift @@ -164,7 +164,7 @@ class CustomAudioSource: BaseViewController { agoraKit.updateChannel(with: mediaOption) } - let sampleRate:UInt = 44100, audioChannel:UInt = 1, bitPerSample = 16, samples = 441 * 10 + let sampleRate:UInt = 44100, audioChannel:UInt = 2, bitPerSample = 16, samples = 441 * 10 @IBAction func onJoinPressed(_ sender:Any) { if !isJoined { // check configuration @@ -214,7 +214,7 @@ class CustomAudioSource: BaseViewController { isProcessing = true let option = AgoraRtcChannelMediaOptions() - option.publishMicrophoneTrack = true + option.publishMicrophoneTrack = false option.publishCustomAudioTrack = true option.publishCameraTrack = false option.publishCustomAudioTrackId = Int(trackId) @@ -336,7 +336,7 @@ extension CustomAudioSource: AgoraRtcEngineDelegate { extension CustomAudioSource: AgoraPcmSourcePushDelegate { func onAudioFrame(data: UnsafeMutablePointer) { agoraKit.pushExternalAudioFrameRawData(data, - samples: samples, + samples: samples*Int(audioChannel), sampleRate: Int(sampleRate), channels: Int(audioChannel), trackId: Int(trackId), diff --git a/macOS/APIExample/Examples/Advanced/CustomVideoRender/CustomVideoRender.swift b/macOS/APIExample/Examples/Advanced/CustomVideoRender/CustomVideoRender.swift index 377ab6f22..cf7b96278 100644 --- a/macOS/APIExample/Examples/Advanced/CustomVideoRender/CustomVideoRender.swift +++ b/macOS/APIExample/Examples/Advanced/CustomVideoRender/CustomVideoRender.swift @@ -178,11 +178,13 @@ class CustomVideoRender: BaseViewController { AgoraRtcEngineKit.destroy() } - @IBAction func onJoinPressed(_ sender:Any) { + @IBAction func onJoinPressed(_ sender:NSButton) { + sender.isEnabled = false if !isJoined { // check configuration let channel = channelField.stringValue if channel.isEmpty { + sender.isEnabled = true return } @@ -209,6 +211,7 @@ class CustomVideoRender: BaseViewController { option.publishCameraTrack = true option.publishMicrophoneTrack = true NetworkManager.shared.generateToken(channelName: channel, success: { token in + sender.isEnabled = true let result = self.agoraKit.joinChannel(byToken: token, channelId: channel, uid: 0, mediaOptions: option) if result != 0 { self.isJoined = false @@ -221,6 +224,7 @@ class CustomVideoRender: BaseViewController { }) } else { agoraKit.leaveChannel { (stats:AgoraChannelStats) in + sender.isEnabled = true LogUtils.log(message: "Left channel", level: .info) self.videos[0].uid = nil self.isJoined = false @@ -313,7 +317,7 @@ extension CustomVideoRender: AgoraRtcEngineDelegate { // } if let customRender = videos[1].videocanvas { - customRender.stopRender(uid: uid) + customRender.stopRender() } } } diff --git a/macOS/APIExample/Examples/Advanced/CustomVideoSourcePushMulti/CustomVideoSourcePushMulti.swift b/macOS/APIExample/Examples/Advanced/CustomVideoSourcePushMulti/CustomVideoSourcePushMulti.swift index 5fd69dd42..dc1734f28 100644 --- a/macOS/APIExample/Examples/Advanced/CustomVideoSourcePushMulti/CustomVideoSourcePushMulti.swift +++ b/macOS/APIExample/Examples/Advanced/CustomVideoSourcePushMulti/CustomVideoSourcePushMulti.swift @@ -19,6 +19,8 @@ class UserModel { var customEncodeSource: KFMP4Demuxer? } +var kLocalUid: UInt = 999 + class CustomVideoSourcePushMulti: BaseViewController { @IBOutlet weak var Container: AGEVideoContainer! lazy var localVideo: SampleBufferDisplayView = { @@ -187,7 +189,7 @@ class CustomVideoSourcePushMulti: BaseViewController { } }) let connection = AgoraRtcConnection() - connection.localUid = 999 + connection.localUid = kLocalUid connection.channelId = channelField.stringValue agoraKit.leaveChannelEx(connection) { state in LogUtils.log(message: "warning: \(state.description)", level: .info) @@ -221,14 +223,14 @@ class CustomVideoSourcePushMulti: BaseViewController { // agoraKit.setCloudProxy(AgoraCloudProxyType.init(rawValue: UInt(proxySetting)) ?? .noneProxy) // setup my own camera as custom video source - customCamera = AgoraYUVImageSourcePush(size: CGSize(width: 320, height: 180), - fileName: "sample" , - frameRate: 15) - customCamera?.trackId = agoraKit.createCustomVideoTrack() - customCamera?.delegate = self - customCamera?.startSource() - agoraKit.setExternalVideoSource(true, useTexture: true, sourceType: .videoFrame) -// agoraKit.setExternalVideoSource(true, useTexture: true, encodedFrame: true) + if(customCamera == nil){ + customCamera = AgoraYUVImageSourcePush(size: CGSize(width: 320, height: 180), + fileName: "sample" , + frameRate: 15) + customCamera?.trackId = agoraKit.createCustomVideoTrack() + customCamera?.delegate = self + customCamera?.startSource() + } // enable video module and set up video encoding configs agoraKit.setVideoEncoderConfiguration( AgoraVideoEncoderConfiguration( @@ -246,9 +248,11 @@ class CustomVideoSourcePushMulti: BaseViewController { // when joining channel. The channel name and uid used to calculate // the token has to match the ones used for channel join isProcessing = true - joinChannel(uid: 999, trackId: customCamera?.trackId ?? 0, publishEncodedVideoTrack: false) + joinChannel(uid: kLocalUid, trackId: customCamera?.trackId ?? 0, publishEncodedVideoTrack: false) } else { self.customCamera?.stopSource() + agoraKit.destroyCustomVideoTrack(UInt(self.customCamera?.trackId ?? 0)) + self.customCamera = nil agoraKit.leaveChannel { (stats:AgoraChannelStats) in print(stats) } @@ -256,17 +260,21 @@ class CustomVideoSourcePushMulti: BaseViewController { isProcessing = false isJoined = false remoteVideos.forEach({ + let trackId = $0.trackId + $0.trackId = 0 $0.isEncode = false - $0.isJoin = false - $0.uid = UInt(Int.random(in: 10000...99999)) - agoraKit.destroyCustomVideoTrack(UInt($0.trackId)) - agoraKit.destroyCustomEncodedVideoTrack(UInt($0.trackId)) $0.customEncodeSource?.cancelReading() - $0.customEncodeSource = nil + agoraKit.destroyCustomEncodedVideoTrack(UInt(trackId)) $0.customSource?.stopSource() + agoraKit.destroyCustomVideoTrack(UInt(trackId)) $0.customSource = nil - $0.trackId = 0 + $0.isJoin = false + $0.uid = UInt(Int.random(in: 10000...99999)) + }) + remoteVideos.forEach({ + $0.canvasView?.videoView?.reset() }) + localVideo.videoView.reset() } } @@ -276,18 +284,23 @@ class CustomVideoSourcePushMulti: BaseViewController { option.publishCustomVideoTrack = !publishEncodedVideoTrack option.publishMicrophoneTrack = false option.publishCameraTrack = false - option.autoSubscribeAudio = true - option.autoSubscribeVideo = true + option.autoSubscribeAudio = uid == kLocalUid + option.autoSubscribeVideo = uid == kLocalUid option.publishEncodedVideoTrack = publishEncodedVideoTrack option.customVideoTrackId = Int(trackId) option.clientRoleType = .broadcaster let connection = AgoraRtcConnection() connection.localUid = uid connection.channelId = channelName + + var delegate : AgoraRtcEngineDelegate? = self + if(uid != kLocalUid){ + delegate = nil + } NetworkManager.shared.generateToken(channelName: channelName, uid: uid) { token in let result = self.agoraKit.joinChannelEx(byToken: token, connection: connection, - delegate: self, + delegate: delegate, mediaOptions: option, joinSuccess: nil) if result != 0 { @@ -301,6 +314,9 @@ class CustomVideoSourcePushMulti: BaseViewController { } @IBAction func onClickCustomEncodeVideoTrack(_ sender: Any) { + if(!isJoined) { + return + } guard let userModel = remoteVideos.first(where: { $0.isJoin == false }) else { return } if userModel.trackId == 0 { let encodeVideoOptions = AgoraEncodedVideoTrackOptions() @@ -309,22 +325,28 @@ class CustomVideoSourcePushMulti: BaseViewController { userModel.trackId = agoraKit.createCustomEncodedVideoTrack(encodeVideoOptions) userModel.isEncode = true userModel.isJoin = true + } else { + return } NetworkManager.shared.download(urlString: "https://agora-adc-artifacts.s3.cn-north-1.amazonaws.com.cn/resources/sample.mp4") { response in + guard userModel.isJoin else {return} let path = response["path"] as? String let config = KFDemuxerConfig() config.demuxerType = .video let asset = AVAsset(url: URL(fileURLWithPath: path ?? "")) config.asset = asset - let demuxer = KFMP4Demuxer(config: config) + guard let demuxer = KFMP4Demuxer(config: config) else {return} userModel.customEncodeSource = demuxer - demuxer?.startReading() - demuxer?.dataCallBack = { [weak self] data, sampleBuffer in + demuxer.startReading() + demuxer.dataCallBack = { [weak self, weak demuxer] data, sampleBuffer in guard let self = self, let data = data, let sampleBuffer = sampleBuffer else { return } let info = AgoraEncodedVideoFrameInfo() info.frameType = .keyFrame info.framesPerSecond = 30 info.codecType = .H264 + if(userModel.trackId == 0){ + return + } self.agoraKit.pushExternalEncodedVideoFrame(data, info: info, videoTrackId: UInt(userModel.trackId)) @@ -337,10 +359,15 @@ class CustomVideoSourcePushMulti: BaseViewController { } @IBAction func onClickCreateTrack(_ sender: Any) { + if(!isJoined) { + return + } guard let userModel = remoteVideos.first(where: { $0.isJoin == false }) else { return } if userModel.trackId == 0 { userModel.trackId = agoraKit.createCustomVideoTrack() userModel.isEncode = false + } else { + return } createVideoTrack(userModel: userModel) } @@ -358,25 +385,34 @@ class CustomVideoSourcePushMulti: BaseViewController { } @IBAction func onClickDestoryTrack(_ sender: Any) { + if(!isJoined) { + return + } let channelName = channelField.stringValue - let userModel = remoteVideos.filter({ $0.isJoin == true }).last + let userModel = remoteVideos.filter({ $0.isJoin == true && $0.trackId != 0}).last + if(userModel == nil){ + return + } + let trackId = userModel?.trackId userModel?.isJoin = false + userModel?.trackId = 0 userModel?.customSource?.stopSource() userModel?.customEncodeSource?.cancelReading() userModel?.customEncodeSource = nil - userModel?.canvasView?.videoView.reset() userModel?.customSource = nil let connection = AgoraRtcConnection() connection.localUid = userModel?.uid ?? 0 connection.channelId = channelName - agoraKit.destroyCustomVideoTrack(UInt(userModel?.trackId ?? 0)) - agoraKit.destroyCustomEncodedVideoTrack(UInt(userModel?.trackId ?? 0)) - userModel?.trackId = 0 + agoraKit.destroyCustomVideoTrack(UInt(trackId ?? 0)) + agoraKit.destroyCustomEncodedVideoTrack(UInt(trackId ?? 0)) + userModel?.isEncode = false userModel?.uid = UInt(Int.random(in: 10000...99999)) agoraKit.leaveChannelEx(connection) { state in LogUtils.log(message: "warning: \(state.description)", level: .info) } + + userModel?.canvasView?.videoView.reset() } func layoutVideos(_ count: Int) { @@ -425,21 +461,26 @@ extension CustomVideoSourcePushMulti: AgoraRtcEngineDelegate { /// @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) - if uid == 999 { return } + if uid == kLocalUid { return } for model in remoteVideos { - if model.uid == uid { + if model.trackId != 0 && model.uid == uid { return } } + let videoCanvas = AgoraRtcVideoCanvas() videoCanvas.uid = uid // the view to be binded - guard let userModel = remoteVideos.first(where: { $0.isJoin == false }) else { return } + guard let userModel = remoteVideos.first(where: { $0.isJoin == false && $0.trackId == 0 }) else { return } videoCanvas.view = userModel.canvasView?.videoView videoCanvas.renderMode = .hidden userModel.uid = uid userModel.isJoin = true - agoraKit.setupRemoteVideo(videoCanvas) + + let connect = AgoraRtcConnection() + connect.localUid = kLocalUid + connect.channelId = channelField.stringValue + agoraKit.setupRemoteVideoEx(videoCanvas, connection: connect) } /// callback when a remote user is leaving the channel, note audience in live broadcast mode will NOT trigger this event @@ -448,11 +489,23 @@ extension CustomVideoSourcePushMulti: AgoraRtcEngineDelegate { /// 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) + for model in remoteVideos { + if model.trackId != 0 && model.uid == uid { + return + } + } + + let videoCanvas = AgoraRtcVideoCanvas() + videoCanvas.uid = uid + let connect = AgoraRtcConnection() + connect.localUid = kLocalUid + connect.channelId = channelField.stringValue + agoraKit.setupRemoteVideoEx(videoCanvas, connection: connect) // 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 userModel = remoteVideos.first(where: { $0.uid == uid }) + let userModel = remoteVideos.first(where: { $0.uid == uid && $0.trackId == 0 }) userModel?.isJoin = false userModel?.uid = UInt(Int.random(in: 10000...99999)) userModel?.canvasView?.videoView.reset() diff --git a/macOS/APIExample/Examples/Advanced/JoinMultiChannel/JoinMultiChannel.swift b/macOS/APIExample/Examples/Advanced/JoinMultiChannel/JoinMultiChannel.swift index fe43705be..a5300d92f 100644 --- a/macOS/APIExample/Examples/Advanced/JoinMultiChannel/JoinMultiChannel.swift +++ b/macOS/APIExample/Examples/Advanced/JoinMultiChannel/JoinMultiChannel.swift @@ -170,7 +170,7 @@ class JoinMultipleChannel: BaseViewController { guard isJoined2 else { return } let filePath = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask).first?.absoluteString let programPath = filePath?.components(separatedBy: "/")[4] ?? "" - let path = "/Users/\(programPath)/Downloads/1.png" + let path = "/Users/\(programPath)/Downloads/\(Date()).png" let channel2 = AgoraRtcConnection() channel2.channelId = channelName2 channel2.localUid = channel2Uid diff --git a/macOS/APIExample/Examples/Advanced/LiveStreaming/Base.lproj/LiveStreaming.storyboard b/macOS/APIExample/Examples/Advanced/LiveStreaming/Base.lproj/LiveStreaming.storyboard index 3d12b4a0c..eea24b9ec 100644 --- a/macOS/APIExample/Examples/Advanced/LiveStreaming/Base.lproj/LiveStreaming.storyboard +++ b/macOS/APIExample/Examples/Advanced/LiveStreaming/Base.lproj/LiveStreaming.storyboard @@ -1,8 +1,8 @@ - + - + @@ -30,43 +30,43 @@ - + - + - + - + - + - + - + - + - + - + @@ -76,8 +76,8 @@ - - + + @@ -89,7 +89,7 @@ - + @@ -149,6 +149,15 @@ + + + + + + + + + @@ -159,11 +168,11 @@ - + - + @@ -171,7 +180,7 @@ - + @@ -179,7 +188,7 @@ - + @@ -191,6 +200,7 @@ + @@ -200,7 +210,7 @@ - + @@ -208,7 +218,7 @@ - + @@ -227,7 +237,7 @@ - + @@ -235,7 +245,7 @@ - + @@ -255,9 +265,9 @@ - + - + @@ -283,9 +293,9 @@ - + - + @@ -311,7 +321,7 @@ - + @@ -325,24 +335,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -350,6 +390,8 @@ + + @@ -360,6 +402,8 @@ + + diff --git a/macOS/APIExample/Examples/Advanced/LiveStreaming/LiveStreaming.swift b/macOS/APIExample/Examples/Advanced/LiveStreaming/LiveStreaming.swift index 87d3e9c1f..e78e460ab 100644 --- a/macOS/APIExample/Examples/Advanced/LiveStreaming/LiveStreaming.swift +++ b/macOS/APIExample/Examples/Advanced/LiveStreaming/LiveStreaming.swift @@ -262,20 +262,16 @@ class LiveStreamingMain: BaseViewController { @IBOutlet weak var centerStage: Picker! func initSelectCentetStagePicker() { centerStage.isEnabled = agoraKit.isCameraCenterStageSupported() - let params: [String: AgoraCameraStabilizationMode] = ["auto": .auto, - "level1": .level1, - "level2": .level2, - "level3": .level3, - "off": .off] + let params = ["off": false, "on": true] let datas = params.map { $0.key }.sorted() centerStage.label.stringValue = "Center Stage".localized centerStage.picker.addItems(withTitles: datas) centerStage.onSelectChanged { [weak self] in guard let self = self else { return } - let index = self.selectRolePicker.indexOfSelectedItem + let index = self.centerStage.picker.indexOfSelectedItem let key = datas[index] - let mode = params[key] - self.agoraKit.enableCameraCenterStage(mode != .off) + let mode = params[key] ?? false + self.agoraKit.enableCameraCenterStage(mode) } } @@ -326,7 +322,7 @@ class LiveStreamingMain: BaseViewController { Util.configPrivatization(agoraKit: agoraKit) agoraKit.enableVideo() - scrollView.documentView?.setFrameSize(CGSizeMake(314, 645)) + scrollView.documentView?.setFrameSize(CGSizeMake(314, 720)) initSelectCameraPicker() initSelectResolutionPicker() @@ -492,6 +488,14 @@ class LiveStreamingMain: BaseViewController { functionVC?.clickEncoderSegmentSwitch = { [weak self] s in self?.onTapEncoderSegment(s) } + + functionVC?.onChangeLocalRenderFps = { [weak self] fps in + self?.agoraKit.setLocalRenderTargetFps(.camera, targetFps: fps) + } + + functionVC?.onChangeRemoteRenderFps = { [weak self] fps in + self?.agoraKit.setRemoteRenderTargetFps(fps) + } } private func onTakeSnapshot() { @@ -572,6 +576,8 @@ class LiveStreamingMain: BaseViewController { encoderConfig.advancedVideoOptions = advancedOptions agoraKit.setVideoEncoderConfiguration(encoderConfig) } + + } class LiveStreamingRTCFunctionVC: BaseViewController { @@ -582,6 +588,8 @@ class LiveStreamingRTCFunctionVC: BaseViewController { var clickVideoImageSwitch: ((NSSwitch) -> Void)? var clickBFrameSwitch: ((NSSwitch) -> Void)? var clickEncoderSegmentSwitch: ((NSSegmentedControl) -> Void)? + var onChangeRemoteRenderFps: ((Int32)->())? + var onChangeLocalRenderFps: ((Int32)->())? @IBOutlet weak var snapShot: NSButton! @IBAction func onTakeSnapshot(_ sender: Any) { clickTakeSnapshotClosure?() @@ -617,6 +625,33 @@ class LiveStreamingRTCFunctionVC: BaseViewController { @IBAction func onTapEncoderSegment(_ sender: NSSegmentedControl) { clickEncoderSegmentSwitch?(sender) } + + // lcoal render fps editor + @IBOutlet weak var localRenderTextField: NSTextField? + + // remote render fps editor + @IBOutlet weak var remoteRenderTextField: NSTextField? +} + +extension LiveStreamingRTCFunctionVC: NSTextFieldDelegate { + func controlTextDidChange(_ obj: Notification) { + guard let textField = obj.object as? NSTextField else {return} + if let number = Int(textField.stringValue) { + if number > 60 { + textField.stringValue = "60" + } else if number == 0 { + textField.stringValue = "" + } + } else { + textField.stringValue = "" + } + + if textField == localRenderTextField { + self.onChangeLocalRenderFps?(Int32(textField.stringValue) ?? 15) + } else { + self.onChangeRemoteRenderFps?(Int32(textField.stringValue) ?? 15) + } + } } /// agora rtc engine delegate events @@ -736,6 +771,9 @@ extension LiveStreamingMain: AgoraRtcEngineDelegate { func rtcEngine(_ engine: AgoraRtcEngineKit, localVideoStateChangedOf state: AgoraVideoLocalState, reason: AgoraLocalVideoStreamReason, sourceType: AgoraVideoSourceType) { LogUtils.log(message: "AgoraRtcEngineKit state: \(state), error \(reason.rawValue)", level: .info) + if state == .encoding { + centerStage.isEnabled = agoraKit.isCameraCenterStageSupported() + } } func rtcEngine(_ engine: AgoraRtcEngineKit, videoRenderingTracingResultOfUid uid: UInt, currentEvent: AgoraMediaTraceEvent, tracingInfo: AgoraVideoRenderingTracingInfo) { diff --git a/macOS/APIExample/Examples/Advanced/LiveStreaming/zh-Hans.lproj/LiveStreaming.strings b/macOS/APIExample/Examples/Advanced/LiveStreaming/zh-Hans.lproj/LiveStreaming.strings index 764753351..71691c316 100644 --- a/macOS/APIExample/Examples/Advanced/LiveStreaming/zh-Hans.lproj/LiveStreaming.strings +++ b/macOS/APIExample/Examples/Advanced/LiveStreaming/zh-Hans.lproj/LiveStreaming.strings @@ -38,3 +38,6 @@ "gCs-hv-sr4.title" = "预加载"; "gBn-zJ-ZES.title" = "垫片推流"; + +"JyF-xa-die.placeholderString"="远端渲染帧率(1-60),默认15"; +"Ddt-E2-7gz.placeholderString"="本地渲染帧率(1-60),默认15"; diff --git a/macOS/APIExample/Examples/Advanced/ScreenShare/ScreenShare.swift b/macOS/APIExample/Examples/Advanced/ScreenShare/ScreenShare.swift index add14339d..9e064cd2d 100644 --- a/macOS/APIExample/Examples/Advanced/ScreenShare/ScreenShare.swift +++ b/macOS/APIExample/Examples/Advanced/ScreenShare/ScreenShare.swift @@ -262,7 +262,6 @@ class ScreenShare: BaseViewController { mediaOptions.publishCameraTrack = false mediaOptions.publishScreenTrack = true agoraKit.updateChannel(with: mediaOptions) - agoraKit.startPreview() setupLocalPreview(isScreenSharing: true) } } else { diff --git a/macOS/APIExample/Examples/Advanced/VideoProcess/Base.lproj/VideoProcess.storyboard b/macOS/APIExample/Examples/Advanced/VideoProcess/Base.lproj/VideoProcess.storyboard index 18a36327d..88dd0a683 100644 --- a/macOS/APIExample/Examples/Advanced/VideoProcess/Base.lproj/VideoProcess.storyboard +++ b/macOS/APIExample/Examples/Advanced/VideoProcess/Base.lproj/VideoProcess.storyboard @@ -1,8 +1,8 @@ - + - + @@ -29,34 +29,34 @@ - + - + - + - + - + - + - - + + @@ -65,14 +65,14 @@ - + - - + + @@ -81,14 +81,14 @@ - + - - + + @@ -97,22 +97,22 @@ - + - + - - + + @@ -121,15 +121,15 @@ - + - - + + @@ -137,8 +137,8 @@ - - + + @@ -147,14 +147,14 @@ - + - - + + @@ -163,15 +163,15 @@ - + - + - - + + @@ -180,15 +180,15 @@ - + - - + + @@ -196,8 +196,8 @@ - - + + @@ -206,7 +206,7 @@ - + @@ -214,13 +214,30 @@ - + + + + + + + + + + + + + + + + + + @@ -230,7 +247,7 @@ - diff --git a/macOS/APIExample/Examples/Advanced/VideoProcess/VideoProcess.swift b/macOS/APIExample/Examples/Advanced/VideoProcess/VideoProcess.swift index a9f2fd7ad..6e51a3839 100644 --- a/macOS/APIExample/Examples/Advanced/VideoProcess/VideoProcess.swift +++ b/macOS/APIExample/Examples/Advanced/VideoProcess/VideoProcess.swift @@ -30,10 +30,11 @@ class VideoProcess: BaseViewController { @IBOutlet weak var skinProtectLabel: NSTextField! @IBOutlet weak var beautySwitch: NSSwitch! - @IBOutlet weak var whiteningSlider: NSSlider! + @IBOutlet weak var lightenSlider: NSSlider! @IBOutlet weak var ruddySlider: NSSlider! @IBOutlet weak var sharpSlider: NSSlider! @IBOutlet weak var smoothingSlider: NSSlider! + @IBOutlet weak var whiteningSlider: NSSlider? var videos: [VideoView] = [] let layouts = [Layout("1v1", 2), Layout("1v3", 4), Layout("1v8", 9), Layout("1v15", 16)] @@ -42,6 +43,7 @@ class VideoProcess: BaseViewController { var beautifyOption = AgoraBeautyOptions() var skinProtect = 0.5 var strength = 0.5 + var whintening = 0.5 // indicate if current instance has joined channel var isJoined: Bool = false { @@ -95,7 +97,7 @@ class VideoProcess: BaseViewController { strenghtLabel.stringValue = "Strength".localized skinProtectLabel.stringValue = "Skin Protect".localized - whiteningSlider.floatValue = beautifyOption.lighteningLevel + lightenSlider.floatValue = beautifyOption.lighteningLevel ruddySlider.floatValue = beautifyOption.rednessLevel sharpSlider.floatValue = beautifyOption.sharpnessLevel smoothingSlider.floatValue = beautifyOption.smoothnessLevel @@ -220,7 +222,7 @@ class VideoProcess: BaseViewController { } } - @IBAction func onWhiteningSliderChange(_ sender: NSSlider) { + @IBAction func onLightenSliderChange(_ sender: NSSlider) { beautifyOption.lighteningLevel = sender.floatValue agoraKit.setBeautyEffectOptions(beautySwitch.state == .on, options: beautifyOption) } @@ -240,6 +242,15 @@ class VideoProcess: BaseViewController { agoraKit.setBeautyEffectOptions(beautySwitch.state == .on, options: beautifyOption) } + @IBAction func onWhinteningSliderChange(_ sender: NSSlider) { + let options = AgoraFilterEffectOptions() + options.path = "built_in_whiten_filter" + options.strength = sender.floatValue + whintening = sender.doubleValue + let ret = agoraKit.setFilterEffectOptions(beautySwitch.state == .on, options: options) + print("onWhinteningSlider: \(ret), \(options.strength)") + } + @IBAction func onVirtualBackgroundSwitchChange(_ sender: NSSwitch) { if sender.state == .on { if agoraKit.isFeatureAvailable(onDevice: .videoPreprocessVirtualBackground) { diff --git a/macOS/APIExample/Examples/Advanced/VideoProcess/en.lproj/VideoProcess.strings b/macOS/APIExample/Examples/Advanced/VideoProcess/en.lproj/VideoProcess.strings index f1c2bbcb2..0a7a6644c 100644 --- a/macOS/APIExample/Examples/Advanced/VideoProcess/en.lproj/VideoProcess.strings +++ b/macOS/APIExample/Examples/Advanced/VideoProcess/en.lproj/VideoProcess.strings @@ -33,7 +33,10 @@ "plA-5C-vDg.title" = "Video Denoise"; /* Class = "NSTextFieldCell"; title = "美白"; ObjectID = "q1I-U0-llK"; */ -"q1I-U0-llK.title" = "Whitening"; +"q1I-U0-llK.title" = "Lightening"; /* Class = "NSTextFieldCell"; title = "平滑"; ObjectID = "sFK-pV-vaj"; */ "sFK-pV-vaj.title" = "Smoothing"; + + +"AAl-Un-v63.title" = "Whitening"; diff --git a/macOS/APIExample/Examples/Advanced/VideoProcess/zh-Hans.lproj/VideoProcess.strings b/macOS/APIExample/Examples/Advanced/VideoProcess/zh-Hans.lproj/VideoProcess.strings index dd38bf91c..0328b3828 100644 --- a/macOS/APIExample/Examples/Advanced/VideoProcess/zh-Hans.lproj/VideoProcess.strings +++ b/macOS/APIExample/Examples/Advanced/VideoProcess/zh-Hans.lproj/VideoProcess.strings @@ -37,3 +37,5 @@ /* Class = "NSTextFieldCell"; title = "平滑"; ObjectID = "sFK-pV-vaj"; */ "sFK-pV-vaj.title" = "平滑"; + +"AAl-Un-v63.title" = "提亮"; diff --git a/macOS/APIExample/Examples/Advanced/VoiceChanger/Base.lproj/VoiceChanger.storyboard b/macOS/APIExample/Examples/Advanced/VoiceChanger/Base.lproj/VoiceChanger.storyboard index fdf1f6303..d51d9a375 100644 --- a/macOS/APIExample/Examples/Advanced/VoiceChanger/Base.lproj/VoiceChanger.storyboard +++ b/macOS/APIExample/Examples/Advanced/VoiceChanger/Base.lproj/VoiceChanger.storyboard @@ -1,8 +1,8 @@ - + - + @@ -30,47 +30,47 @@ - + - + - + - + - + - + - + - + - + - + - + @@ -101,15 +101,15 @@ - + - + - + @@ -139,7 +139,7 @@ - + @@ -171,7 +171,7 @@ - + @@ -203,7 +203,7 @@ - + @@ -235,7 +235,7 @@ - + @@ -267,7 +267,7 @@ - + @@ -299,7 +299,7 @@ - + @@ -331,7 +331,7 @@ - + @@ -363,7 +363,7 @@ - + @@ -395,7 +395,7 @@ - + @@ -427,7 +427,7 @@ - + @@ -481,7 +481,11 @@ - + + + + + @@ -493,7 +497,7 @@ - + @@ -576,6 +580,7 @@ + diff --git a/macOS/APIExample/Examples/Advanced/VoiceChanger/VoiceChanger.swift b/macOS/APIExample/Examples/Advanced/VoiceChanger/VoiceChanger.swift index 3b7722bbd..b852b076f 100644 --- a/macOS/APIExample/Examples/Advanced/VoiceChanger/VoiceChanger.swift +++ b/macOS/APIExample/Examples/Advanced/VoiceChanger/VoiceChanger.swift @@ -47,6 +47,8 @@ class VoiceChanger: BaseViewController { var videos: [VideoView] = [] + var aitunerType: AgoraVoiceAITunerType? + @IBOutlet weak var container: AGEVideoContainer! var agoraKit: AgoraRtcEngineKit! @@ -78,7 +80,8 @@ class VoiceChanger: BaseViewController { self.mics = self.agoraKit.enumerateDevices(.audioRecording) ?? [] } - selectMicsPicker.onSelectChanged { + selectMicsPicker.onSelectChanged {[weak self] in + guard let self = self else {return} if !self.isJoined { return } @@ -107,7 +110,8 @@ class VoiceChanger: BaseViewController { layoutVideos(2) selectLayoutPicker.label.stringValue = "Layout".localized selectLayoutPicker.picker.addItems(withTitles: layouts.map { $0.label }) - selectLayoutPicker.onSelectChanged { + selectLayoutPicker.onSelectChanged {[weak self] in + guard let self = self else {return} if self.isJoined { return } @@ -122,7 +126,8 @@ class VoiceChanger: BaseViewController { selectAINSModePicker.isEnabled = isJoined selectAINSModePicker.label.stringValue = "AINSMode" selectAINSModePicker.picker.addItems(withTitles: ainsMode.map({ $0.description() })) - selectAINSModePicker.onSelectChanged { + selectAINSModePicker.onSelectChanged {[weak self] in + guard let self = self else {return} let index = self.selectAINSModePicker.indexOfSelectedItem if index < 0 || index >= self.ainsMode.count { return } self.agoraKit.setAINSMode(true, mode: self.ainsMode[index]) @@ -173,7 +178,8 @@ class VoiceChanger: BaseViewController { selectChatBeautifierPicker.isEnabled = false selectChatBeautifierPicker.label.stringValue = "Chat Beautifier".localized selectChatBeautifierPicker.picker.addItems(withTitles: chatBeautifiers.map { $0.description() }) - selectChatBeautifierPicker.onSelectChanged { + selectChatBeautifierPicker.onSelectChanged {[weak self] in + guard let self = self else {return} if !self.isJoined { return } @@ -199,7 +205,8 @@ class VoiceChanger: BaseViewController { selectTimbreTransformationPicker.isEnabled = false selectTimbreTransformationPicker.label.stringValue = "Timbre Transformation".localized selectTimbreTransformationPicker.picker.addItems(withTitles: timbreTransformations.map { $0.description() }) - selectTimbreTransformationPicker.onSelectChanged { + selectTimbreTransformationPicker.onSelectChanged {[weak self] in + guard let self = self else {return} if !self.isJoined { return } @@ -225,7 +232,8 @@ class VoiceChanger: BaseViewController { selectVoiceChangerPicker.isEnabled = false selectVoiceChangerPicker.label.stringValue = "Voice Changer".localized selectVoiceChangerPicker.picker.addItems(withTitles: voiceChangers.map { $0.description() }) - selectVoiceChangerPicker.onSelectChanged { + selectVoiceChangerPicker.onSelectChanged {[weak self] in + guard let self = self else {return} if !self.isJoined { return } @@ -251,7 +259,8 @@ class VoiceChanger: BaseViewController { selectStyleTransformationPicker.isEnabled = false selectStyleTransformationPicker.label.stringValue = "Style Transformation".localized selectStyleTransformationPicker.picker.addItems(withTitles: styleTransformations.map { $0.description() }) - selectStyleTransformationPicker.onSelectChanged { + selectStyleTransformationPicker.onSelectChanged {[weak self] in + guard let self = self else {return} if !self.isJoined { return } @@ -277,7 +286,8 @@ class VoiceChanger: BaseViewController { selectRoomAcousticsPicker.isEnabled = false selectRoomAcousticsPicker.label.stringValue = "Room Acoustics".localized selectRoomAcousticsPicker.picker.addItems(withTitles: roomAcoustics.map { $0.description() }) - selectRoomAcousticsPicker.onSelectChanged { + selectRoomAcousticsPicker.onSelectChanged {[weak self] in + guard let self = self else {return} if !self.isJoined { return } @@ -303,7 +313,8 @@ class VoiceChanger: BaseViewController { selectPitchCorrectionPicker.isEnabled = false selectPitchCorrectionPicker.label.stringValue = "Pitch Correction".localized selectPitchCorrectionPicker.picker.addItems(withTitles: pitchCorrections.map { $0.description() }) - selectPitchCorrectionPicker.onSelectChanged { + selectPitchCorrectionPicker.onSelectChanged {[weak self] in + guard let self = self else {return} if !self.isJoined { return } @@ -312,6 +323,36 @@ class VoiceChanger: BaseViewController { } } + /* + --- voice AI Tuner Picker --- + */ + @IBOutlet weak var voiceAITunerPicker: Picker! + + func initSelectVoiceAITunerPicker() { + voiceAITunerPicker.isEnabled = false + voiceAITunerPicker.label.stringValue = "Voice_AI_Tuner".localized + let items: [AgoraVoiceAITunerType?] = [nil] + (0...9).map { AgoraVoiceAITunerType(rawValue: $0) } + let titles = items.map { $0?.description() ?? "Off" } + voiceAITunerPicker.picker.addItems(withTitles: titles) + voiceAITunerPicker.onSelectChanged {[weak self] in + guard let self = self else {return} + if !self.isJoined { + return + } + let type = items[self.voiceAITunerPicker.picker.indexOfSelectedItem] + self.updateVoiceAiTuner(type: type) + } + } + + func updateVoiceAiTuner(type: AgoraVoiceAITunerType?) { + LogUtils.log(message: "onVoiceAITunerAction: \(type?.rawValue ?? -1)", level: .info) + if let type = type { + agoraKit.enableVoiceAITuner(true, type: type) + } else { + agoraKit.enableVoiceAITuner(false, type: .matureMale) + } + } + /** --- set audio effect button --- */ @@ -447,6 +488,7 @@ class VoiceChanger: BaseViewController { selectStyleTransformationPicker.isEnabled = isJoined selectRoomAcousticsPicker.isEnabled = isJoined selectPitchCorrectionPicker.isEnabled = isJoined + voiceAITunerPicker.isEnabled = isJoined voicePitchSlider.isEnabled = isJoined equalization31hzPicker.isEnabled = isJoined equalization62hzPicker.isEnabled = isJoined @@ -492,6 +534,7 @@ class VoiceChanger: BaseViewController { initSelectStyleTransformationPicker() initSelectRoomAcousticsPicker() initSelectPitchCorrectionPicker() + initSelectVoiceAITunerPicker() initAudioEffectParam1Field() initAudioEffectParam2Field() initAudioEffectButton() diff --git a/macOS/APIExample/zh-Hans.lproj/Localizable.strings b/macOS/APIExample/zh-Hans.lproj/Localizable.strings index ce6bcae55..231eeb7a7 100644 --- a/macOS/APIExample/zh-Hans.lproj/Localizable.strings +++ b/macOS/APIExample/zh-Hans.lproj/Localizable.strings @@ -223,3 +223,17 @@ "Please contact Agora customer service to obtain a face capture certificate" = "请联系声网客服获取面捕证书"; "Start Video Echo Test" = "开始视频回路测试"; "Stop Video Echo Test" = "停止视频回路测试"; + + +"Voice_AI_Tuner"="AI调音器"; +"Set_Voice_AI_Tuner"="设置AI调音器"; +"AI_Tunner_Mature_Male"="大叔声"; +"AI_Tunner_Fresh_Male"="清新男音"; +"AI_Tunner_Elegant_Female"="御姐音"; +"AI_Tunner_Sweet_Female"="萝莉音"; +"AI_Tunner_Warm_Male_Singing"="暖男歌声"; +"AI_Tunner_Gentle_Female_Singing"="温柔女歌声"; +"AI_Tunner_Husky_Male_Singing"="烟嗓叔音歌声"; +"AI_Tunner_Warm_Elegant_Female_Singing"="温暖御姐歌声"; +"AI_Tunner_Powerful_Male_Singing"="力量男歌声"; +"AI_Tunner_Dreamy_Female_Singing"="梦幻女歌声"; diff --git a/macOS/Podfile b/macOS/Podfile index fa1058f2f..ae9f8f091 100644 --- a/macOS/Podfile +++ b/macOS/Podfile @@ -4,19 +4,19 @@ target 'APIExample' do use_frameworks! pod 'AGEVideoLayout', '1.0.2' - pod 'AgoraRtcEngine_macOS', '4.3.1' - # pod 'sdk', :path => 'sdk.podspec' + pod 'AgoraRtcEngine_macOS', '4.4.0' +# pod 'sdk', :path => 'sdk.podspec' end target 'SimpleFilter' do use_frameworks! - # pod 'sdk', :path => 'sdk.podspec' - pod 'AgoraRtcEngine_macOS', '4.3.1' +# pod 'sdk', :path => 'sdk.podspec' + pod 'AgoraRtcEngine_macOS', '4.4.0' end post_install do |installer| - # system("sh .download_script.sh 4.3.1 true") + # system("sh .download_script.sh 4.3.2 true") installer.pods_project.targets.each do |target| target.build_configurations.each do |config| config.build_settings['MACOSX_DEPLOYMENT_TARGET'] = '10.13' diff --git a/macOS/README.md b/macOS/README.md index 71db4f17f..18db0efa9 100644 --- a/macOS/README.md +++ b/macOS/README.md @@ -54,8 +54,8 @@ To build and run the sample application, get an App Id: Agora provides App certificate to generate Token. You can deploy and generate a token on your server, or use the console to generate a temporary token. In order to get the APP ID, you can open the agora console (https://console.agora.io/) to create a project with the App Certificate enabled, - then the APP Certificate can be found in the project detail page. - PS: If the project does not have certificates enabled, leave this field blank. + then the APP Certificate can be found in the project detail page.If the project does not have certificates enabled, leave this field blank. + PS: It is unsafe to place the App Certificate on the client side, it is recommended to place it on the server side to ensure that the App Certificate is not leaked. */ static var Certificate: String? = <#YOUR Certificate#> diff --git a/macOS/README.zh.md b/macOS/README.zh.md index a17ed1b42..d49de7c80 100644 --- a/macOS/README.zh.md +++ b/macOS/README.zh.md @@ -48,8 +48,8 @@ pod install /** Agora 提供 App certificate 用以生成 Token。您可以在您的服务器部署并生成 Token,或者使用控制台生成临时的 Token。 - 进入声网控制台(https://console.agora.io/),创建一个带证书鉴权的项目,进入项目配置页,即可看到APP证书。 - 注意:如果项目没有开启证书鉴权,这个字段留空。 + 进入声网控制台(https://console.agora.io/),创建一个带证书鉴权的项目,进入项目配置页,即可看到APP证书。如果项目没有开启证书鉴权,这个字段留空。 + 注意:App证书放在客户端不安全,推荐放在服务端以确保 App 证书不会泄露。 */ static var Certificate: String? = <#YOUR Certificate#> ``` diff --git a/macOS/cloud_build.sh b/macOS/cloud_build.sh new file mode 100755 index 000000000..c2dc367b3 --- /dev/null +++ b/macOS/cloud_build.sh @@ -0,0 +1,95 @@ +#!/usr/bin/env sh + +PROJECT_PATH=$PWD + +if [ "$WORKSPACE" = "" ]; then + WORKSPACE=$PWD +fi +if [ "$BUILD_NUMBER" = "" ]; then + BUILD_NUMBER=888 +fi + + +cd ${PROJECT_PATH} && pod install || exit 1 + +# 打包环境 +CONFIGURATION="Debug" + +#工程文件路径 +APP_PATH="$(ls | grep xcworkspace)" + +# 项目target名 +TARGET_NAME=${APP_PATH%%.*} + +KEYCENTER_PATH=$TARGET_NAME/Common/KeyCenter.swift + +#工程配置路径 +PBXPROJ_PATH=${TARGET_NAME}.xcodeproj/project.pbxproj + +# 主项目工程配置 +# Debug +/usr/libexec/PlistBuddy -c "Set :objects:03896D5324F8A011008593CD:buildSettings:CODE_SIGN_STYLE 'Manual'" $PBXPROJ_PATH +/usr/libexec/PlistBuddy -c "Set :objects:03896D5324F8A011008593CD:buildSettings:CODE_SIGN_IDENTITY 'Developer ID Application'" $PBXPROJ_PATH +/usr/libexec/PlistBuddy -c "Set :objects:03896D5324F8A011008593CD:buildSettings:DEVELOPMENT_TEAM 'YS397FG5PA'" $PBXPROJ_PATH +/usr/libexec/PlistBuddy -c "Set :objects:03896D5324F8A011008593CD:buildSettings:PROVISIONING_PROFILE_SPECIFIER 'apiexamplemac'" $PBXPROJ_PATH +# Release +/usr/libexec/PlistBuddy -c "Set :objects:03896D5424F8A011008593CD:buildSettings:CODE_SIGN_STYLE 'Manual'" $PBXPROJ_PATH +/usr/libexec/PlistBuddy -c "Set :objects:03896D5424F8A011008593CD:buildSettings:CODE_SIGN_IDENTITY 'Developer ID Application'" $PBXPROJ_PATH +/usr/libexec/PlistBuddy -c "Set :objects:03896D5424F8A011008593CD:buildSettings:DEVELOPMENT_TEAM 'YS397FG5PA'" $PBXPROJ_PATH +/usr/libexec/PlistBuddy -c "Set :objects:03896D5424F8A011008593CD:buildSettings:PROVISIONING_PROFILE_SPECIFIER 'apiexamplemac'" $PBXPROJ_PATH + +#修改build number +# Debug +/usr/libexec/PlistBuddy -c "Set :objects:03896D5324F8A011008593CD:buildSettings:CURRENT_PROJECT_VERSION ${BUILD_NUMBER}" $PBXPROJ_PATH +# Release +/usr/libexec/PlistBuddy -c "Set :objects:03896D5424F8A011008593CD:buildSettings:CURRENT_PROJECT_VERSION ${BUILD_NUMBER}" $PBXPROJ_PATH + + +# 读取APPID环境变量 +echo AGORA_APP_ID: $APP_ID + +echo PROJECT_PATH: $PROJECT_PATH +echo TARGET_NAME: $TARGET_NAME +echo KEYCENTER_PATH: $KEYCENTER_PATH +echo APP_PATH: $APP_PATH + +#修改Keycenter文件 +sed -i -e "s#<\#YOUR AppId\#>#\"$APP_ID\"#g" $KEYCENTER_PATH +rm -f ${KEYCENTER_PATH}-e + +# Xcode clean +xcodebuild clean -workspace "${APP_PATH}" -configuration "${CONFIGURATION}" -scheme "${TARGET_NAME}" + +# 时间戳 +CURRENT_TIME=$(date "+%Y-%m-%d %H-%M-%S") + +# 归档路径 +ARCHIVE_PATH="${WORKSPACE}/${TARGET_NAME}_${BUILD_NUMBER}.xcarchive" + +# 编译环境 + +# plist路径 +PLIST_PATH="${PROJECT_PATH}/ExportOptions.plist" + + +# archive 这边使用的工作区间 也可以使用project +xcodebuild archive -workspace "${APP_PATH}" -scheme "${TARGET_NAME}" -configuration "${CONFIGURATION}" -archivePath "${ARCHIVE_PATH}" + +cd ${WORKSPACE} + +# 压缩archive +7za a -tzip "${TARGET_NAME}_${BUILD_NUMBER}.xcarchive.zip" "${ARCHIVE_PATH}" + +# 签名 +sh sign "${WORKSPACE}/${TARGET_NAME}_${BUILD_NUMBER}.xcarchive.zip" --type xcarchive --plist "${PLIST_PATH}" --application macApp + + +SDK_VERSION=$(echo $sdk_url | cut -d "/" -f 5) +OUTPUT_FILE=${WORKSPACE}/${TARGET_NAME}_${BUILD_NUMBER}_${SDK_VERSION}_$(date "+%Y%m%d%H%M%S").app.zip +mv ${TARGET_NAME}_${BUILD_NUMBER}.app.zip $OUTPUT_FILE + +rm -rf *.xcarchive +rm -rf *.xcarchive.zip +echo OUTPUT_FILE: $OUTPUT_FILE + + diff --git a/macOS/libs/AgoraAiEchoCancellationExtension.xcframework/Info.plist b/macOS/libs/AgoraAiEchoCancellationExtension.xcframework/Info.plist deleted file mode 100644 index 7914fc4c0..000000000 --- a/macOS/libs/AgoraAiEchoCancellationExtension.xcframework/Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - AvailableLibraries - - - LibraryIdentifier - macos-arm64_x86_64 - LibraryPath - AgoraAiEchoCancellationExtension.framework - SupportedArchitectures - - arm64 - x86_64 - - SupportedPlatform - macos - - - CFBundlePackageType - XFWK - XCFrameworkFormatVersion - 1.0 - - diff --git a/macOS/libs/AgoraAiNoiseSuppressionExtension.xcframework/Info.plist b/macOS/libs/AgoraAiNoiseSuppressionExtension.xcframework/Info.plist deleted file mode 100644 index 53f49ed9c..000000000 --- a/macOS/libs/AgoraAiNoiseSuppressionExtension.xcframework/Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - AvailableLibraries - - - LibraryIdentifier - macos-arm64_x86_64 - LibraryPath - AgoraAiNoiseSuppressionExtension.framework - SupportedArchitectures - - arm64 - x86_64 - - SupportedPlatform - macos - - - CFBundlePackageType - XFWK - XCFrameworkFormatVersion - 1.0 - - diff --git a/macOS/libs/AgoraAudioBeautyExtension.xcframework/Info.plist b/macOS/libs/AgoraAudioBeautyExtension.xcframework/Info.plist deleted file mode 100644 index ee614813e..000000000 --- a/macOS/libs/AgoraAudioBeautyExtension.xcframework/Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - AvailableLibraries - - - LibraryIdentifier - macos-arm64_x86_64 - LibraryPath - AgoraAudioBeautyExtension.framework - SupportedArchitectures - - arm64 - x86_64 - - SupportedPlatform - macos - - - CFBundlePackageType - XFWK - XCFrameworkFormatVersion - 1.0 - - diff --git a/macOS/libs/AgoraClearVisionExtension.xcframework/Info.plist b/macOS/libs/AgoraClearVisionExtension.xcframework/Info.plist deleted file mode 100644 index fc5e96331..000000000 --- a/macOS/libs/AgoraClearVisionExtension.xcframework/Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - AvailableLibraries - - - LibraryIdentifier - macos-arm64_x86_64 - LibraryPath - AgoraClearVisionExtension.framework - SupportedArchitectures - - arm64 - x86_64 - - SupportedPlatform - macos - - - CFBundlePackageType - XFWK - XCFrameworkFormatVersion - 1.0 - - diff --git a/macOS/libs/AgoraContentInspectExtension.xcframework/Info.plist b/macOS/libs/AgoraContentInspectExtension.xcframework/Info.plist deleted file mode 100644 index ffff731d2..000000000 --- a/macOS/libs/AgoraContentInspectExtension.xcframework/Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - AvailableLibraries - - - LibraryIdentifier - macos-arm64_x86_64 - LibraryPath - AgoraContentInspectExtension.framework - SupportedArchitectures - - arm64 - x86_64 - - SupportedPlatform - macos - - - CFBundlePackageType - XFWK - XCFrameworkFormatVersion - 1.0 - - diff --git a/macOS/libs/AgoraCore.xcframework/Info.plist b/macOS/libs/AgoraCore.xcframework/Info.plist deleted file mode 100644 index 31930b239..000000000 --- a/macOS/libs/AgoraCore.xcframework/Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - AvailableLibraries - - - LibraryIdentifier - macos-arm64_x86_64 - LibraryPath - AgoraCore.framework - SupportedArchitectures - - arm64 - x86_64 - - SupportedPlatform - macos - - - CFBundlePackageType - XFWK - XCFrameworkFormatVersion - 1.0 - - diff --git a/macOS/libs/AgoraDrmLoaderExtension.xcframework/Info.plist b/macOS/libs/AgoraDrmLoaderExtension.xcframework/Info.plist deleted file mode 100644 index 31b317a6b..000000000 --- a/macOS/libs/AgoraDrmLoaderExtension.xcframework/Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - AvailableLibraries - - - LibraryIdentifier - macos-arm64_x86_64 - LibraryPath - AgoraDrmLoaderExtension.framework - SupportedArchitectures - - arm64 - x86_64 - - SupportedPlatform - macos - - - CFBundlePackageType - XFWK - XCFrameworkFormatVersion - 1.0 - - diff --git a/macOS/libs/AgoraFaceDetectionExtension.xcframework/Info.plist b/macOS/libs/AgoraFaceDetectionExtension.xcframework/Info.plist deleted file mode 100644 index fc174cffa..000000000 --- a/macOS/libs/AgoraFaceDetectionExtension.xcframework/Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - AvailableLibraries - - - LibraryIdentifier - macos-arm64_x86_64 - LibraryPath - AgoraFaceDetectionExtension.framework - SupportedArchitectures - - arm64 - x86_64 - - SupportedPlatform - macos - - - CFBundlePackageType - XFWK - XCFrameworkFormatVersion - 1.0 - - diff --git a/macOS/libs/AgoraRtcKit.xcframework/Info.plist b/macOS/libs/AgoraRtcKit.xcframework/Info.plist deleted file mode 100644 index ad02a0417..000000000 --- a/macOS/libs/AgoraRtcKit.xcframework/Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - AvailableLibraries - - - LibraryIdentifier - macos-arm64_x86_64 - LibraryPath - AgoraRtcKit.framework - SupportedArchitectures - - arm64 - x86_64 - - SupportedPlatform - macos - - - CFBundlePackageType - XFWK - XCFrameworkFormatVersion - 1.0 - - diff --git a/macOS/libs/AgoraScreenCaptureExtension.xcframework/Info.plist b/macOS/libs/AgoraScreenCaptureExtension.xcframework/Info.plist deleted file mode 100644 index 6a5e12272..000000000 --- a/macOS/libs/AgoraScreenCaptureExtension.xcframework/Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - AvailableLibraries - - - LibraryIdentifier - macos-arm64_x86_64 - LibraryPath - AgoraScreenCaptureExtension.framework - SupportedArchitectures - - arm64 - x86_64 - - SupportedPlatform - macos - - - CFBundlePackageType - XFWK - XCFrameworkFormatVersion - 1.0 - - diff --git a/macOS/libs/AgoraSoundTouch.xcframework/Info.plist b/macOS/libs/AgoraSoundTouch.xcframework/Info.plist deleted file mode 100644 index 6e19d5454..000000000 --- a/macOS/libs/AgoraSoundTouch.xcframework/Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - AvailableLibraries - - - LibraryIdentifier - macos-arm64_x86_64 - LibraryPath - AgoraSoundTouch.framework - SupportedArchitectures - - arm64 - x86_64 - - SupportedPlatform - macos - - - CFBundlePackageType - XFWK - XCFrameworkFormatVersion - 1.0 - - diff --git a/macOS/libs/AgoraSpatialAudioExtension.xcframework/Info.plist b/macOS/libs/AgoraSpatialAudioExtension.xcframework/Info.plist deleted file mode 100644 index 9d99a1db9..000000000 --- a/macOS/libs/AgoraSpatialAudioExtension.xcframework/Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - AvailableLibraries - - - LibraryIdentifier - macos-arm64_x86_64 - LibraryPath - AgoraSpatialAudioExtension.framework - SupportedArchitectures - - arm64 - x86_64 - - SupportedPlatform - macos - - - CFBundlePackageType - XFWK - XCFrameworkFormatVersion - 1.0 - - diff --git a/macOS/libs/AgoraVideoDecoderExtension.xcframework/Info.plist b/macOS/libs/AgoraVideoDecoderExtension.xcframework/Info.plist deleted file mode 100644 index 55eb22f8a..000000000 --- a/macOS/libs/AgoraVideoDecoderExtension.xcframework/Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - AvailableLibraries - - - LibraryIdentifier - macos-arm64_x86_64 - LibraryPath - AgoraVideoDecoderExtension.framework - SupportedArchitectures - - arm64 - x86_64 - - SupportedPlatform - macos - - - CFBundlePackageType - XFWK - XCFrameworkFormatVersion - 1.0 - - diff --git a/macOS/libs/AgoraVideoEncoderExtension.xcframework/Info.plist b/macOS/libs/AgoraVideoEncoderExtension.xcframework/Info.plist deleted file mode 100644 index d81bc4b5e..000000000 --- a/macOS/libs/AgoraVideoEncoderExtension.xcframework/Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - AvailableLibraries - - - LibraryIdentifier - macos-arm64_x86_64 - LibraryPath - AgoraVideoEncoderExtension.framework - SupportedArchitectures - - arm64 - x86_64 - - SupportedPlatform - macos - - - CFBundlePackageType - XFWK - XCFrameworkFormatVersion - 1.0 - - diff --git a/macOS/libs/AgoraVideoQualityAnalyzerExtension.xcframework/Info.plist b/macOS/libs/AgoraVideoQualityAnalyzerExtension.xcframework/Info.plist deleted file mode 100644 index fcf68d819..000000000 --- a/macOS/libs/AgoraVideoQualityAnalyzerExtension.xcframework/Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - AvailableLibraries - - - LibraryIdentifier - macos-arm64_x86_64 - LibraryPath - AgoraVideoQualityAnalyzerExtension.framework - SupportedArchitectures - - arm64 - x86_64 - - SupportedPlatform - macos - - - CFBundlePackageType - XFWK - XCFrameworkFormatVersion - 1.0 - - diff --git a/macOS/libs/AgoraVideoSegmentationExtension.xcframework/Info.plist b/macOS/libs/AgoraVideoSegmentationExtension.xcframework/Info.plist deleted file mode 100644 index a7a93a954..000000000 --- a/macOS/libs/AgoraVideoSegmentationExtension.xcframework/Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - AvailableLibraries - - - LibraryIdentifier - macos-arm64_x86_64 - LibraryPath - AgoraVideoSegmentationExtension.framework - SupportedArchitectures - - arm64 - x86_64 - - SupportedPlatform - macos - - - CFBundlePackageType - XFWK - XCFrameworkFormatVersion - 1.0 - - diff --git a/macOS/libs/Agorafdkaac.xcframework/Info.plist b/macOS/libs/Agorafdkaac.xcframework/Info.plist deleted file mode 100644 index 4cef2adfa..000000000 --- a/macOS/libs/Agorafdkaac.xcframework/Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - AvailableLibraries - - - LibraryIdentifier - macos-arm64_x86_64 - LibraryPath - Agorafdkaac.framework - SupportedArchitectures - - arm64 - x86_64 - - SupportedPlatform - macos - - - CFBundlePackageType - XFWK - XCFrameworkFormatVersion - 1.0 - - diff --git a/macOS/libs/Agoraffmpeg.xcframework/Info.plist b/macOS/libs/Agoraffmpeg.xcframework/Info.plist deleted file mode 100644 index d46c72f57..000000000 --- a/macOS/libs/Agoraffmpeg.xcframework/Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - AvailableLibraries - - - LibraryIdentifier - macos-arm64_x86_64 - LibraryPath - Agoraffmpeg.framework - SupportedArchitectures - - arm64 - x86_64 - - SupportedPlatform - macos - - - CFBundlePackageType - XFWK - XCFrameworkFormatVersion - 1.0 - - diff --git a/macOS/libs/video_dec.xcframework/Info.plist b/macOS/libs/video_dec.xcframework/Info.plist deleted file mode 100644 index a7aae8dcb..000000000 --- a/macOS/libs/video_dec.xcframework/Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - AvailableLibraries - - - LibraryIdentifier - macos-arm64_x86_64 - LibraryPath - video_dec.framework - SupportedArchitectures - - arm64 - x86_64 - - SupportedPlatform - macos - - - CFBundlePackageType - XFWK - XCFrameworkFormatVersion - 1.0 - - diff --git a/macOS/libs/video_enc.xcframework/Info.plist b/macOS/libs/video_enc.xcframework/Info.plist deleted file mode 100644 index 9c3f80979..000000000 --- a/macOS/libs/video_enc.xcframework/Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - AvailableLibraries - - - LibraryIdentifier - macos-arm64_x86_64 - LibraryPath - video_enc.framework - SupportedArchitectures - - arm64 - x86_64 - - SupportedPlatform - macos - - - CFBundlePackageType - XFWK - XCFrameworkFormatVersion - 1.0 - - diff --git a/windows/APIExample/APIExample/APIExample.rc b/windows/APIExample/APIExample/APIExample.rc index bad29a8a9..7621d33ff 100755 --- a/windows/APIExample/APIExample/APIExample.rc +++ b/windows/APIExample/APIExample/APIExample.rc @@ -109,7 +109,6 @@ BEGIN GROUPBOX "Canvas",IDC_STATIC,169,346,150,43 CONTROL "Hidden",IDC_RADIO_CANVAS_HIDDEN,"Button",BS_AUTORADIOBUTTON | WS_GROUP,177,359,38,10 CONTROL "Fit",IDC_RADIO_CANVAS_FIT,"Button",BS_AUTORADIOBUTTON,221,359,34,10 - CONTROL "Adaptive",IDC_RADIO_CANVAS_ADAPTIVE,"Button",BS_AUTORADIOBUTTON,262,359,45,10 CONTROL "",IDC_SLIDER_CANVAS_COLOR,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,176,371,99,15 LTEXT "",IDC_STATIC_CANVAS_COLOR,275,373,39,10 CONTROL "VideoImage",IDC_CHECK_VIDEO_IMAGE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,392,53,10 @@ -600,26 +599,28 @@ IDD_DIALOG_BEAUTY_AUDIO DIALOGEX 0, 0, 632, 400 STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN - LTEXT "",IDC_STATIC_VIDEO,1,0,483,310,NOT WS_VISIBLE - LISTBOX IDC_LIST_INFO_BROADCASTING,491,0,139,312,LBS_NOINTEGRALHEIGHT | LBS_DISABLENOSCROLL | WS_VSCROLL | WS_HSCROLL | WS_TABSTOP - LTEXT "Channel Name",IDC_STATIC_CHANNELNAME,11,328,48,8 - EDITTEXT IDC_EDIT_CHANNELNAME,70,326,218,13,ES_AUTOHSCROLL - PUSHBUTTON "JoinChannel",IDC_BUTTON_JOINCHANNEL,307,326,60,14 - LTEXT "Audio Change",IDC_STATIC_AUDIO_CHANGER,12,345,48,8 - COMBOBOX IDC_COMBO_AUDIO_CHANGER,70,343,172,30,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP - LTEXT "",IDC_STATIC_DETAIL,463,331,167,58 - LTEXT "Reverb Preset",IDC_STATIC_BEAUTY_AUDIO_TYPE,11,362,48,8 - COMBOBOX IDC_COMBO_AUDIO_PERVERB_PRESET,70,360,171,30,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "Button2",IDC_BUTTON_SET_BEAUTY_AUDIO,463,342,58,14 - EDITTEXT IDC_EDIT_PARAM1,289,342,56,14,ES_AUTOHSCROLL - LTEXT "param1",IDC_STATIC_PARAM1,251,345,36,8 - LTEXT "param2",IDC_STATIC_PARAM2,355,345,38,8 - EDITTEXT IDC_EDIT_PARAM2,393,342,56,14,ES_AUTOHSCROLL - CONTROL "",IDC_SLIDER_VOICE_FORMANT,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,296,359,123,15 - LTEXT "VoiceFomant",IDC_STATIC_BEAUTY_AUDIO_TYPE2,251,362,48,8 - LTEXT "0",IDC_STATIC_VOICE_FORMAT_VALUE,425,362,46,8 - LTEXT "AINS Mode",IDC_STATIC_BEAUTY_AUDIO_AINS_MODE,11,378,48,8 - COMBOBOX IDC_COMBO_AUDIO_AINS_MODE,70,376,171,30,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + LTEXT "",IDC_STATIC_VIDEO,1,0,486,302,NOT WS_VISIBLE + LISTBOX IDC_LIST_INFO_BROADCASTING,491,0,139,305,LBS_NOINTEGRALHEIGHT | LBS_DISABLENOSCROLL | WS_VSCROLL | WS_HSCROLL | WS_TABSTOP + LTEXT "Channel Name",IDC_STATIC_CHANNELNAME,11,314,48,8 + EDITTEXT IDC_EDIT_CHANNELNAME,70,312,218,13,ES_AUTOHSCROLL + PUSHBUTTON "JoinChannel",IDC_BUTTON_JOINCHANNEL,307,311,60,14 + LTEXT "Audio Change",IDC_STATIC_AUDIO_CHANGER,12,332,48,8 + COMBOBOX IDC_COMBO_AUDIO_CHANGER,70,330,172,30,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + LTEXT "",IDC_STATIC_DETAIL,463,350,159,43 + LTEXT "Reverb Preset",IDC_STATIC_BEAUTY_AUDIO_TYPE,11,350,48,8 + COMBOBOX IDC_COMBO_AUDIO_PERVERB_PRESET,70,348,171,30,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Button2",IDC_BUTTON_SET_BEAUTY_AUDIO,463,329,58,14 + EDITTEXT IDC_EDIT_PARAM1,289,329,56,14,ES_AUTOHSCROLL + LTEXT "param1",IDC_STATIC_PARAM1,251,332,36,8 + LTEXT "param2",IDC_STATIC_PARAM2,355,332,38,8 + EDITTEXT IDC_EDIT_PARAM2,393,329,56,14,ES_AUTOHSCROLL + CONTROL "",IDC_SLIDER_VOICE_FORMANT,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,296,347,123,15 + LTEXT "VoiceFomant",IDC_STATIC_BEAUTY_AUDIO_TYPE2,251,350,48,8 + LTEXT "0",IDC_STATIC_VOICE_FORMAT_VALUE,425,350,46,8 + LTEXT "AINS Mode",IDC_STATIC_BEAUTY_AUDIO_AINS_MODE,11,367,48,8 + COMBOBOX IDC_COMBO_AUDIO_AINS_MODE,70,365,171,30,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + LTEXT "AINS Mode",IDC_STATIC_BEAUTY_AUDIO_AI_TUNER,11,384,48,8 + COMBOBOX IDC_COMBO_AUDIO_AI_TUNER,70,382,171,30,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP END IDD_DIALOG_SPATIAL_AUDIO DIALOGEX 0, 0, 633, 399 @@ -751,6 +752,20 @@ BEGIN EDITTEXT IDC_EDIT_TOKEN,69,363,128,29,ES_AUTOHSCROLL END +IDD_DIALOG_TRANSPARENT_BG DIALOGEX 0, 0, 632, 400 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Channel Name",IDC_STATIC_CHANNELNAME,136,360,48,8 + EDITTEXT IDC_EDIT_CHANNELNAME,216,358,172,13,ES_AUTOHSCROLL + PUSHBUTTON "JoinChannel",IDC_BUTTON_JOINCHANNEL,420,358,75,14 + LTEXT "",IDC_STATIC_DETAIL,23,370,412,27 + LTEXT "",IDC_STATIC_VIDEO,0,0,419,147 + LTEXT "",IDC_STATIC_VIDEO_RIGHT,233,175,176,136 + LTEXT "",IDC_STATIC_VIDEO_LEFT,2,173,171,136 + LISTBOX IDC_LIST_INFO_BROADCASTING,491,0,139,312,LBS_NOINTEGRALHEIGHT | LBS_DISABLENOSCROLL | WS_VSCROLL | WS_HSCROLL | WS_TABSTOP +END + ///////////////////////////////////////////////////////////////////////////// // @@ -906,6 +921,14 @@ BEGIN HORZGUIDE, 163 END + IDD_DIALOG_VOLUME, DIALOG + BEGIN + END + + IDD_DIALOG_PEPORT_IN_CALL, DIALOG + BEGIN + END + IDD_DIALOG_REGIONAL_CONNECTION, DIALOG BEGIN END @@ -956,9 +979,11 @@ BEGIN VERTGUIDE, 11 VERTGUIDE, 70 BOTTOMMARGIN, 397 - HORZGUIDE, 349 - HORZGUIDE, 366 - HORZGUIDE, 382 + HORZGUIDE, 318 + HORZGUIDE, 336 + HORZGUIDE, 354 + HORZGUIDE, 371 + HORZGUIDE, 388 END IDD_DIALOG_SPATIAL_AUDIO, DIALOG @@ -1045,6 +1070,12 @@ BEGIN HORZGUIDE, 348 HORZGUIDE, 363 END + + IDD_DIALOG_TRANSPARENT_BG, DIALOG + BEGIN + RIGHTMARGIN, 630 + BOTTOMMARGIN, 397 + END END #endif // APSTUDIO_INVOKED @@ -1184,6 +1215,11 @@ BEGIN 0 END +IDD_DIALOG_TRANSPARENT_BG AFX_DIALOG_LAYOUT +BEGIN + 0 +END + ///////////////////////////////////////////////////////////////////////////// // diff --git a/windows/APIExample/APIExample/APIExample.vcxproj b/windows/APIExample/APIExample/APIExample.vcxproj index ae9d15a06..ca7174446 100644 --- a/windows/APIExample/APIExample/APIExample.vcxproj +++ b/windows/APIExample/APIExample/APIExample.vcxproj @@ -94,7 +94,7 @@ Level3 Disabled false - WIN32;_WINDOWS;_DEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions); + WIN32;_WINDOWS;_DEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) $(solutionDir)ThirdParty\libFFmpeg\include;$(solutionDir)ThirdParty\Boost;$(SolutionDir)sdk\high_level_api\include;$(solutionDir)ThirdParty\libYUV;$(ProjectDir) MultiThreadedDLL @@ -121,7 +121,8 @@ if exist zh-cn.ini (copy zh-cn.ini $(SolutionDir)$(Configuration)) if exist en.ini (copy en.ini $(SolutionDir)$(Configuration)) copy $(SolutionDir)APIExample\Advanced\LocalVideoTranscoding\agora.png $(SolutionDir)$(Configuration) copy $(SolutionDir)APIExample\Advanced\LocalVideoTranscoding\agora.jpg $(SolutionDir)$(Configuration) -if exist sample.yuv (copy sample.yuv $(SolutionDir)$(Configuration)) +if exist sample.yuv (copy sample.yuv $(SolutionDir)$(Configuration)) +if exist $(SolutionDir)APIExample\res\yuvj_full_range_alpha_1280_540_left.mp4 (copy $(SolutionDir)APIExample\res\yuvj_full_range_alpha_1280_540_left.mp4 $(SolutionDir)$(Configuration)) @@ -167,6 +168,7 @@ if exist en.ini (copy en.ini $(SolutionDir)$(Platform)\$(Configuration)) copy $(SolutionDir)APIExample\Advanced\LocalVideoTranscoding\agora.png $(SolutionDir)$(Platform)\$(Configuration) copy $(SolutionDir)APIExample\Advanced\LocalVideoTranscoding\agora.jpg $(SolutionDir)$(Platform)\$(Configuration) if exist sample.yuv (copy sample.yuv $(SolutionDir)$(Platform)$(Configuration)) +if exist $(SolutionDir)APIExample\res\yuvj_full_range_alpha_1280_540_left.mp4 (copy $(SolutionDir)APIExample\res\yuvj_full_range_alpha_1280_540_left.mp4 $(SolutionDir)$(Configuration)) @@ -267,6 +269,7 @@ if exist sample.yuv (copy sample.yuv $(SolutionDir)$(Platform)\$(Configuration)) + @@ -295,6 +298,7 @@ if exist sample.yuv (copy sample.yuv $(SolutionDir)$(Platform)\$(Configuration)) + @@ -319,6 +323,7 @@ if exist sample.yuv (copy sample.yuv $(SolutionDir)$(Platform)\$(Configuration)) + @@ -347,6 +352,7 @@ if exist sample.yuv (copy sample.yuv $(SolutionDir)$(Platform)\$(Configuration)) + @@ -395,6 +401,7 @@ if exist sample.yuv (copy sample.yuv $(SolutionDir)$(Platform)\$(Configuration)) Document + @@ -418,6 +425,7 @@ if exist sample.yuv (copy sample.yuv $(SolutionDir)$(Platform)\$(Configuration)) Document + diff --git a/windows/APIExample/APIExample/APIExample.vcxproj.filters b/windows/APIExample/APIExample/APIExample.vcxproj.filters index de1e3f9ea..a16906e88 100644 --- a/windows/APIExample/APIExample/APIExample.vcxproj.filters +++ b/windows/APIExample/APIExample/APIExample.vcxproj.filters @@ -112,6 +112,9 @@ {ed782797-3b06-44a9-8894-6b9d93d0dfea} + + {2a74c488-9982-4577-8594-8320c71beb6d} + @@ -264,6 +267,12 @@ Advanced\Metadata + + Advanced\TransparentBg + + + Header Files + @@ -404,6 +413,12 @@ Advanced\Metadata + + Advanced\TransparentBg + + + Source Files + @@ -420,6 +435,9 @@ Resource Files + + Resource Files + @@ -442,6 +460,9 @@ Resource Files + + Resource Files + diff --git a/windows/APIExample/APIExample/APIExampleDlg.cpp b/windows/APIExample/APIExample/APIExampleDlg.cpp index 7281c0795..959268f1f 100755 --- a/windows/APIExample/APIExample/APIExampleDlg.cpp +++ b/windows/APIExample/APIExample/APIExampleDlg.cpp @@ -255,6 +255,9 @@ void CAPIExampleDlg::InitSceneDialog() m_pMultiChannelDlg->Create(CAgoraMultiChannelDlg::IDD); m_pMultiChannelDlg->MoveWindow(&rcWnd); + //transparent bg + m_vecAdvanced.push_back(TransparentBackground); + //inject m_pRtmpInjectDlg = new CAgoraRtmpInjectionDlg(&m_staMainArea); m_pRtmpInjectDlg->Create(CAgoraRtmpInjectionDlg::IDD); @@ -382,6 +385,12 @@ void CAPIExampleDlg::InitSceneDialog() m_pMultiVideoSourceTracks = new MultiVideoSourceTracks(&m_staMainArea); m_pMultiVideoSourceTracks->Create(MultiVideoSourceTracks::IDD); m_pMultiVideoSourceTracks->MoveWindow(&rcWnd); + + // transparent bg + m_TransparentDlg = new CTransparentBgDlg(&m_staMainArea); + m_TransparentDlg->Create(CTransparentBgDlg::IDD); + m_TransparentDlg->MoveWindow(&rcWnd); + } void CAPIExampleDlg::InitSceneList() @@ -604,6 +613,10 @@ void CAPIExampleDlg::CreateScene(CTreeCtrl& treeScene, CString selectedText) m_pmediaRecorderDlg->InitAgora(); m_pmediaRecorderDlg->ShowWindow(SW_SHOW); } + else if (selectedText.Compare(TransparentBackground) == 0) { + m_TransparentDlg->InitAgora(); + m_TransparentDlg->ShowWindow(SW_SHOW); + } //Sleep(500); } @@ -709,6 +722,10 @@ void CAPIExampleDlg::ReleaseScene(CTreeCtrl& treeScene, HTREEITEM& hSelectItem) m_pmediaRecorderDlg->UnInitAgora(); m_pmediaRecorderDlg->ShowWindow(SW_HIDE); } + else if (str.Compare(TransparentBackground) == 0) { + m_TransparentDlg->UnInitAgora(); + m_TransparentDlg->ShowWindow(SW_HIDE); + } //Sleep(500); } diff --git a/windows/APIExample/APIExample/APIExampleDlg.h b/windows/APIExample/APIExample/APIExampleDlg.h index 12c9bd6bb..295ed85c9 100755 --- a/windows/APIExample/APIExample/APIExampleDlg.h +++ b/windows/APIExample/APIExample/APIExampleDlg.h @@ -33,6 +33,7 @@ #include "Advanced/PushExternalVideoYUV/PushExternalVideoYUV.h" #include "Advanced/MultiVideoSourceTracks/MultiVideoSourceTracks.h" #include "Advanced/FaceCapture/CAgoraFaceCaptureDlg.h" +#include "Advanced/TransparentBg/TransparentBgDialog.h" #include #include #include @@ -108,6 +109,7 @@ class CAPIExampleDlg : public CDialogEx MultiVideoSourceTracks *m_pMultiVideoSourceTracks = nullptr; CAgoraFaceCaptureDlg *m_pAgoraFaceCaptureDlg = nullptr; CDlgBeauty * m_pDlgBeauty = nullptr; + CTransparentBgDlg* m_TransparentDlg = nullptr; CString m_preSelectedItemText = _T(""); std::vector m_vecBasic, m_vecAdvanced; std::mutex m_mutex; diff --git a/windows/APIExample/APIExample/AbstractMediaPlayerSourceObserver.cpp b/windows/APIExample/APIExample/AbstractMediaPlayerSourceObserver.cpp new file mode 100644 index 000000000..2cfadad76 --- /dev/null +++ b/windows/APIExample/APIExample/AbstractMediaPlayerSourceObserver.cpp @@ -0,0 +1,45 @@ +#include "AbstractMediaPlayerSourceObserver.h" + +void AbstractMediaPlayerSourceObserver::onPlayerSourceStateChanged(media::base::MEDIA_PLAYER_STATE state, media::base::MEDIA_PLAYER_REASON reason) +{ +} + +void AbstractMediaPlayerSourceObserver::onPositionChanged(int64_t positionMs, int64_t timestampMs) +{ +} + +void AbstractMediaPlayerSourceObserver::onPlayerEvent(media::base::MEDIA_PLAYER_EVENT eventCode, int64_t elapsedTime, const char *message) +{ +} + +void AbstractMediaPlayerSourceObserver::onMetaData(const void *data, int length) +{ +} + +void AbstractMediaPlayerSourceObserver::onPlayBufferUpdated(int64_t playCachedBuffer) +{ +} + +void AbstractMediaPlayerSourceObserver::onPreloadEvent(const char *src, media::base::PLAYER_PRELOAD_EVENT event) +{ +} + +void AbstractMediaPlayerSourceObserver::onCompleted() +{ +} + +void AbstractMediaPlayerSourceObserver::onAgoraCDNTokenWillExpire() +{ +} + +void AbstractMediaPlayerSourceObserver::onPlayerSrcInfoChanged(const media::base::SrcInfo &from, const media::base::SrcInfo &to) +{ +} + +void AbstractMediaPlayerSourceObserver::onPlayerInfoUpdated(const media::base::PlayerUpdatedInfo &info) +{ +} + +void AbstractMediaPlayerSourceObserver::onAudioVolumeIndication(int volume) +{ +} diff --git a/windows/APIExample/APIExample/AbstractMediaPlayerSourceObserver.h b/windows/APIExample/APIExample/AbstractMediaPlayerSourceObserver.h new file mode 100644 index 000000000..362b10e5e --- /dev/null +++ b/windows/APIExample/APIExample/AbstractMediaPlayerSourceObserver.h @@ -0,0 +1,27 @@ +#pragma once +#include "stdafx.h" +#include +#include +#include + +using namespace agora; +using namespace agora::rtc; +class AbstractMediaPlayerSourceObserver : + public IMediaPlayerSourceObserver +{ +public: + + virtual void onPlayerSourceStateChanged(media::base::MEDIA_PLAYER_STATE state, media::base::MEDIA_PLAYER_REASON reason) override; + void onPositionChanged(int64_t positionMs, int64_t timestampMs) override; + void onPlayerEvent(media::base::MEDIA_PLAYER_EVENT eventCode, int64_t elapsedTime, const char* message) override; + void onMetaData(const void* data, int length) override; + void onPlayBufferUpdated(int64_t playCachedBuffer) override; + void onPreloadEvent(const char* src, media::base::PLAYER_PRELOAD_EVENT event) override; + void onCompleted() override; + void onAgoraCDNTokenWillExpire() override; + void onPlayerSrcInfoChanged(const media::base::SrcInfo& from, const media::base::SrcInfo& to) override; + void onPlayerInfoUpdated(const media::base::PlayerUpdatedInfo& info) override; + void onAudioVolumeIndication(int volume) override; + +}; + diff --git a/windows/APIExample/APIExample/Advanced/BeautyAudio/CAgoraBeautyAudio.cpp b/windows/APIExample/APIExample/Advanced/BeautyAudio/CAgoraBeautyAudio.cpp index b94737eb7..08159d516 100644 --- a/windows/APIExample/APIExample/Advanced/BeautyAudio/CAgoraBeautyAudio.cpp +++ b/windows/APIExample/APIExample/Advanced/BeautyAudio/CAgoraBeautyAudio.cpp @@ -28,6 +28,7 @@ void CAgoraBeautyAudio::InitCtrlText() m_staParam1.SetWindowText(beautyAudioCtrlParam1); m_staParam2.SetWindowText(beautyAudioCtrlParam2); m_staAINSMode.SetWindowText(beautyAudioCtrlAINSMode); + m_staAITuner.SetWindowText(beautyAudioCtrlAITuner); } @@ -143,6 +144,14 @@ void CAgoraBeautyAudio::ResumeStatus() } m_cmbAINSMode.SetCurSel(0); + m_cmbAITuner.ResetContent(); + nIndex = 0; + for (auto& str : m_setAITuner) + { + m_cmbAITuner.InsertString(nIndex++, str.first); + } + m_cmbAITuner.SetCurSel(0); + } void CAgoraBeautyAudio::DoDataExchange(CDataExchange* pDX) @@ -160,7 +169,9 @@ void CAgoraBeautyAudio::DoDataExchange(CDataExchange* pDX) DDX_Control(pDX, IDC_STATIC_BEAUTY_AUDIO_TYPE, m_staAudioType); DDX_Control(pDX, IDC_COMBO_AUDIO_PERVERB_PRESET, m_cmbPerverbPreset); DDX_Control(pDX, IDC_COMBO_AUDIO_AINS_MODE, m_cmbAINSMode); + DDX_Control(pDX, IDC_COMBO_AUDIO_AI_TUNER, m_cmbAITuner); DDX_Control(pDX, IDC_STATIC_BEAUTY_AUDIO_AINS_MODE, m_staAINSMode); + DDX_Control(pDX, IDC_STATIC_BEAUTY_AUDIO_AI_TUNER, m_staAITuner); DDX_Control(pDX, IDC_STATIC_PARAM1, m_staParam1); DDX_Control(pDX, IDC_STATIC_PARAM2, m_staParam2); DDX_Control(pDX, IDC_EDIT_PARAM1, m_edtParam1); @@ -184,6 +195,7 @@ BEGIN_MESSAGE_MAP(CAgoraBeautyAudio, CDialogEx) ON_CBN_SELCHANGE(IDC_COMBO_AUDIO_PERVERB_PRESET, &CAgoraBeautyAudio::OnSelchangeComboAudioPerverbPreset) ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_SLIDER_VOICE_FORMANT, &CAgoraBeautyAudio::OnNMCustomdrawSliderVoiceFormant) ON_CBN_SELCHANGE(IDC_COMBO_AUDIO_AINS_MODE, &CAgoraBeautyAudio::OnCbnSelchangeComboAudioAinsMode) + ON_CBN_SELCHANGE(IDC_COMBO_AUDIO_AI_TUNER, &CAgoraBeautyAudio::OnCbnSelchangeComboAudioAITuner) END_MESSAGE_MAP() @@ -267,6 +279,18 @@ BOOL CAgoraBeautyAudio::OnInitDialog() m_setAINSMode.push_back(_T("AINS_MODE_BALANCED")); m_setAINSMode.push_back(_T("AINS_MODE_AGGRESSIVE")); m_setAINSMode.push_back(_T("AINS_MODE_ULTRALOWLATENCY")); + + m_setAITuner.insert(std::make_pair(_T("AI_TUNER_OFF"), VOICE_AI_TUNER_TYPE::VOICE_AI_TUNER_MATURE_MALE)); + m_setAITuner.insert(std::make_pair(_T("AI_TUNER_MATURE_MALE"), VOICE_AI_TUNER_TYPE::VOICE_AI_TUNER_MATURE_MALE)); + m_setAITuner.insert(std::make_pair(_T("AI_TUNER_FRESH_MALE"), VOICE_AI_TUNER_TYPE::VOICE_AI_TUNER_FRESH_MALE)); + m_setAITuner.insert(std::make_pair(_T("AI_TUNER_ELEGANT_FEMALE"), VOICE_AI_TUNER_TYPE::VOICE_AI_TUNER_ELEGANT_FEMALE)); + m_setAITuner.insert(std::make_pair(_T("AI_TUNER_SWEET_FEMALE"), VOICE_AI_TUNER_TYPE::VOICE_AI_TUNER_SWEET_FEMALE)); + m_setAITuner.insert(std::make_pair(_T("AI_TUNER_WARM_MALE_SINGING"), VOICE_AI_TUNER_TYPE::VOICE_AI_TUNER_WARM_MALE_SINGING)); + m_setAITuner.insert(std::make_pair(_T("AI_TUNER_GENTLE_FEMALE_SINGING"), VOICE_AI_TUNER_TYPE::VOICE_AI_TUNER_GENTLE_FEMALE_SINGING)); + m_setAITuner.insert(std::make_pair(_T("AI_TUNER_HUSKY_MALE_SINGING"), VOICE_AI_TUNER_TYPE::VOICE_AI_TUNER_HUSKY_MALE_SINGING)); + m_setAITuner.insert(std::make_pair(_T("AI_TUNER_WARM_ELEGANT_FEMALE_SINGING"), VOICE_AI_TUNER_TYPE::VOICE_AI_TUNER_WARM_ELEGANT_FEMALE_SINGING)); + m_setAITuner.insert(std::make_pair(_T("AI_TUNER_POWERFUL_MALE_SINGING"), VOICE_AI_TUNER_TYPE::VOICE_AI_TUNER_POWERFUL_MALE_SINGING)); + m_setAITuner.insert(std::make_pair(_T("AI_TUNER_DREAMY_FEMALE_SINGING"), VOICE_AI_TUNER_TYPE::VOICE_AI_TUNER_DREAMY_FEMALE_SINGING)); m_setChanger.insert(std::make_pair(_T("AUDIO_EFFECT_OFF"), AUDIO_EFFECT_OFF)); @@ -700,5 +724,32 @@ void CAgoraBeautyAudio::OnCbnSelchangeComboAudioAinsMode() }else if (position == 2) { mode = AUDIO_AINS_MODE::AINS_MODE_ULTRALOWLATENCY; } - m_rtcEngine->setAINSMode(enable, mode); + + CString str; + m_cmbAINSMode.GetWindowText(str); + + int ret = m_rtcEngine->setAINSMode(enable, mode); + + str.Format(_T("setAINSMode enable=%d,ret=%d,type=%s"), enable, ret, str); + m_lstInfo.InsertString(m_lstInfo.GetCount(), str); +} + + +void CAgoraBeautyAudio::OnCbnSelchangeComboAudioAITuner() +{ + if (!m_rtcEngine) { + return; + } + int position = m_cmbAITuner.GetCurSel(); + boolean enable = position > 0; + + CString str; + m_cmbAITuner.GetWindowText(str); + + VOICE_AI_TUNER_TYPE type = m_setAITuner[str]; + + int ret = m_rtcEngine->enableVoiceAITuner(enable, type); + + str.Format(_T("enableVoiceAITuner enable=%d,ret=%d,type=%s"), enable, ret, str); + m_lstInfo.InsertString(m_lstInfo.GetCount(), str); } diff --git a/windows/APIExample/APIExample/Advanced/BeautyAudio/CAgoraBeautyAudio.h b/windows/APIExample/APIExample/Advanced/BeautyAudio/CAgoraBeautyAudio.h index 516ca3414..6393af4b9 100644 --- a/windows/APIExample/APIExample/Advanced/BeautyAudio/CAgoraBeautyAudio.h +++ b/windows/APIExample/APIExample/Advanced/BeautyAudio/CAgoraBeautyAudio.h @@ -114,6 +114,7 @@ class CAgoraBeautyAudio : public CDialogEx CAudioChangeEventHandler m_eventHandler; std::map> m_mapBeauty; std::vector m_setAINSMode; + std::map m_setAITuner; std::mapm_setChanger; std::mapm_setReverbPreSet; std::mapm_setVoiceConversion; @@ -148,7 +149,9 @@ class CAgoraBeautyAudio : public CDialogEx CComboBox m_cmbPerverbPreset; CComboBox m_cmbAINSMode; + CComboBox m_cmbAITuner; CComboBox m_staAINSMode; + CComboBox m_staAITuner; CButton m_btnSetBeautyAudio; CStatic m_staAudioType; afx_msg void OnSelchangeComboAudioChanger(); @@ -159,4 +162,5 @@ class CAgoraBeautyAudio : public CDialogEx afx_msg void OnSelchangeComboAudioPerverbPreset(); afx_msg void OnNMCustomdrawSliderVoiceFormant(NMHDR* pNMHDR, LRESULT* pResult); afx_msg void OnCbnSelchangeComboAudioAinsMode(); + afx_msg void OnCbnSelchangeComboAudioAITuner(); }; diff --git a/windows/APIExample/APIExample/Advanced/FaceCapture/CAgoraFaceCaptureDlg.cpp b/windows/APIExample/APIExample/Advanced/FaceCapture/CAgoraFaceCaptureDlg.cpp index fb2e54a97..468630fa4 100644 --- a/windows/APIExample/APIExample/Advanced/FaceCapture/CAgoraFaceCaptureDlg.cpp +++ b/windows/APIExample/APIExample/Advanced/FaceCapture/CAgoraFaceCaptureDlg.cpp @@ -555,9 +555,13 @@ void FaceCaptureEventHandler::onRemoteVideoStateChanged(uid_t uid, REMOTE_VIDEO_ } } -void FaceCaptureEventHandler::onExtensionEvent(const char* provider, const char* extension, const char* key, const char* value) +void FaceCaptureEventHandler::onExtensionEventWithContext(const ExtensionContext& context, const char* key, const char* value) { if (m_hMsgHanlder) { + + const char* provider = context.providerName; + const char* extension = context.extensionName; + PExtensionEvent evnet = new ExtensionEvent(); int len = strlen(provider); evnet->provider = new char[len + 1]; diff --git a/windows/APIExample/APIExample/Advanced/FaceCapture/CAgoraFaceCaptureDlg.h b/windows/APIExample/APIExample/Advanced/FaceCapture/CAgoraFaceCaptureDlg.h index a76d13666..241739aa4 100644 --- a/windows/APIExample/APIExample/Advanced/FaceCapture/CAgoraFaceCaptureDlg.h +++ b/windows/APIExample/APIExample/Advanced/FaceCapture/CAgoraFaceCaptureDlg.h @@ -158,7 +158,7 @@ class FaceCaptureEventHandler : public IRtcEngineEventHandler virtual void onRemoteVideoStateChanged(uid_t uid, REMOTE_VIDEO_STATE state, REMOTE_VIDEO_STATE_REASON reason, int elapsed) override; - virtual void onExtensionEvent(const char* provider, const char* extension, const char* key, const char* value) override; + virtual void onExtensionEventWithContext(const ExtensionContext& context, const char* key, const char* value) override; private: HWND m_hMsgHanlder; }; diff --git a/windows/APIExample/APIExample/Advanced/OriginalVideo/CAgoraOriginalVideoDlg.cpp b/windows/APIExample/APIExample/Advanced/OriginalVideo/CAgoraOriginalVideoDlg.cpp index 52b108f72..aae24ec9a 100755 --- a/windows/APIExample/APIExample/Advanced/OriginalVideo/CAgoraOriginalVideoDlg.cpp +++ b/windows/APIExample/APIExample/Advanced/OriginalVideo/CAgoraOriginalVideoDlg.cpp @@ -345,7 +345,7 @@ int countSize(int row, int col, int width, int height, int r) { y = r * 2 + 1; return x * y; } -int dataBuffers[1920][1080][2]; +int dataBuffers[1080][1920][2]; //average filtering algorithm void CAverageFilterVideoProcFrameObserver::AverageFiltering(unsigned char * data, int width, int height, int step) { diff --git a/windows/APIExample/APIExample/Advanced/TransparentBg/TransparentBgDialog.cpp b/windows/APIExample/APIExample/Advanced/TransparentBg/TransparentBgDialog.cpp new file mode 100644 index 000000000..f3f7cb8d2 --- /dev/null +++ b/windows/APIExample/APIExample/Advanced/TransparentBg/TransparentBgDialog.cpp @@ -0,0 +1,687 @@ +#include "stdafx.h" +#include "APIExample.h" +#include "TransparentBgDialog.h" + +void CTransparentBgEventHandler::onJoinChannelSuccess(const char *channel, uid_t uid, int elapsed) +{ + if (m_hMsgHanlder) + { + ::PostMessage(m_hMsgHanlder, WM_MSGID(EID_JOINCHANNEL_SUCCESS), (WPARAM)uid, (LPARAM)elapsed); + } +} + +void CTransparentBgEventHandler::onUserJoined(uid_t uid, int elapsed) +{ + if (m_hMsgHanlder) + { + ::PostMessage(m_hMsgHanlder, WM_MSGID(EID_USER_JOINED), (WPARAM)uid, (LPARAM)elapsed); + } +} + +void CTransparentBgEventHandler::onUserOffline(uid_t uid, USER_OFFLINE_REASON_TYPE reason) +{ + if (m_hMsgHanlder) + { + ::PostMessage(m_hMsgHanlder, WM_MSGID(EID_USER_OFFLINE), (WPARAM)uid, (LPARAM)reason); + } +} + +void CTransparentBgEventHandler::onLeaveChannel(const RtcStats &stats) +{ + if (m_hMsgHanlder) + { + ::PostMessage(m_hMsgHanlder, WM_MSGID(EID_LEAVE_CHANNEL), 0, 0); + } +} + +void CTransparentBgEventHandler::onLocalVideoStats(VIDEO_SOURCE_TYPE source, const LocalVideoStats &stats) +{ + if (m_hMsgHanlder && report) + { + LocalVideoStats *s = new LocalVideoStats; + *s = stats; + ::PostMessage(m_hMsgHanlder, WM_MSGID(EID_LOCAL_VIDEO_STATS), (WPARAM)s, 0); + } +} + +IMPLEMENT_DYNAMIC(CTransparentBgDlg, CDialogEx) + +CTransparentBgDlg::CTransparentBgDlg(CWnd *pParent /*=nullptr*/) + : CDialogEx(IDD_DIALOG_TRANSPARENT_BG, pParent) +{ +} + +CTransparentBgDlg::~CTransparentBgDlg() +{ +} + +void CTransparentBgDlg::DoDataExchange(CDataExchange *pDX) +{ + CDialogEx::DoDataExchange(pDX); + DDX_Control(pDX, IDC_STATIC_CHANNELNAME, m_staticChannelName); + DDX_Control(pDX, IDC_BUTTON_JOINCHANNEL, m_bnJoinChannel); + DDX_Control(pDX, IDC_EDIT_CHANNELNAME, m_editChannel); + DDX_Control(pDX, IDC_STATIC_VIDEO, m_staticVideo); + DDX_Control(pDX, IDC_STATIC_VIDEO_LEFT, m_staticVideoLeft); + DDX_Control(pDX, IDC_STATIC_VIDEO_RIGHT, m_staticVideoRight); + DDX_Control(pDX, IDC_LIST_INFO_BROADCASTING, m_listInfo); +} + +BEGIN_MESSAGE_MAP(CTransparentBgDlg, CDialogEx) + +ON_MESSAGE(WM_MSGID(EID_JOINCHANNEL_SUCCESS), &CTransparentBgDlg::OnEIDJoinChannelSuccess) +ON_MESSAGE(WM_MSGID(EID_ERROR), &CTransparentBgDlg::OnEIDError) +ON_MESSAGE(WM_MSGID(EID_LEAVE_CHANNEL), &CTransparentBgDlg::OnEIDLeaveChannel) +ON_MESSAGE(WM_MSGID(EID_USER_JOINED), &CTransparentBgDlg::OnEIDUserJoined) +ON_MESSAGE(WM_MSGID(EID_USER_OFFLINE), &CTransparentBgDlg::OnEIDUserOffline) + +ON_MESSAGE(WM_MSGID(EID_LOCAL_AUDIO_STATS), &CTransparentBgDlg::onEIDLocalAudioStats) +ON_MESSAGE(WM_MSGID(EID_REMOTE_AUDIO_STATS), &CTransparentBgDlg::onEIDRemoteAudioStats) +ON_MESSAGE(WM_MSGID(EID_LOCAL_VIDEO_STATS), &CTransparentBgDlg::onEIDLocalVideoStats) +ON_MESSAGE(WM_MSGID(EID_REMOTE_VIDEO_STATS), &CTransparentBgDlg::onEIDRemoteVideoStats) + +ON_WM_SHOWWINDOW() + +ON_BN_CLICKED(IDC_BUTTON_JOINCHANNEL, &CTransparentBgDlg::OnBnClickedButtonJoinchannel) +ON_WM_SHOWWINDOW() +END_MESSAGE_MAP() + +BOOL CTransparentBgDlg::_IsWindowsVersionOrGreater(WORD major, WORD minor, WORD) +{ + typedef LONG(WINAPI * PFN_RtlVerifyVersionInfo)(OSVERSIONINFOEXW *, ULONG, ULONGLONG); + static PFN_RtlVerifyVersionInfo RtlVerifyVersionInfoFn = NULL; + if (RtlVerifyVersionInfoFn == NULL) + if (HMODULE ntdllModule = ::GetModuleHandleA("ntdll.dll")) + RtlVerifyVersionInfoFn = (PFN_RtlVerifyVersionInfo)GetProcAddress( + ntdllModule, "RtlVerifyVersionInfo"); + if (RtlVerifyVersionInfoFn == NULL) + return FALSE; + RTL_OSVERSIONINFOEXW versionInfo = {}; + ULONGLONG conditionMask = 0; + versionInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW); + versionInfo.dwMajorVersion = major; + versionInfo.dwMinorVersion = minor; + VER_SET_CONDITION(conditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL); + VER_SET_CONDITION(conditionMask, VER_MINORVERSION, VER_GREATER_EQUAL); + return (RtlVerifyVersionInfoFn(&versionInfo, + VER_MAJORVERSION | VER_MINORVERSION, + conditionMask) == 0) + ? TRUE + : FALSE; +} + +void CTransparentBgDlg::enable_alpha_composition(void *hwnd) +{ + if (!_IsWindowsVistaOrGreater()) + { + return; + } + BOOL composition; + if (FAILED(::DwmIsCompositionEnabled(&composition)) || !composition) + return; + BOOL opaque; + DWORD color; + if (_IsWindows8OrGreater() || + (SUCCEEDED(::DwmGetColorizationColor(&color, &opaque)) && !opaque)) + { + HRGN region = ::CreateRectRgn(0, 0, -1, -1); + DWM_BLURBEHIND bb = {}; + bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION; + bb.hRgnBlur = region; + bb.fEnable = TRUE; + ::DwmEnableBlurBehindWindow((HWND)hwnd, &bb); + ::DeleteObject(region); + } + else + { + DWM_BLURBEHIND bb = {}; + bb.dwFlags = DWM_BB_ENABLE; + ::DwmEnableBlurBehindWindow((HWND)hwnd, &bb); + } +} + +// CTransparentBgDlg message handlers +BOOL CTransparentBgDlg::OnInitDialog() +{ + CDialogEx::OnInitDialog(); + CString className("TransparentWindowClass"); + WNDCLASS wndcls = {}; + HINSTANCE hInst = AfxGetInstanceHandle(); + if (!(::GetClassInfo(hInst, className, &wndcls))) + { + wndcls.style = CS_HREDRAW | CS_VREDRAW; + wndcls.lpfnWndProc = ::DefWindowProc; + wndcls.cbClsExtra = wndcls.cbWndExtra = 0; + wndcls.hInstance = hInst; + wndcls.hIcon = NULL; + wndcls.hCursor = AfxGetApp()->LoadStandardCursor(IDC_ARROW); + wndcls.hbrBackground = NULL; + wndcls.lpszMenuName = NULL; + wndcls.lpszClassName = className; + if (AfxRegisterClass(&wndcls)) + { + hWnd = CreateWindowEx( + 0, // 扩展窗口样式 + className, // 窗口类名 + _T("透明窗口"), // 窗口标题 + WS_OVERLAPPEDWINDOW & ~WS_MINIMIZEBOX & ~WS_MAXIMIZEBOX & ~WS_SYSMENU, // 窗口样式 + CW_USEDEFAULT, CW_USEDEFAULT, // 窗口位置 + 800, 600, // 窗口大小 + this->GetSafeHwnd(), // 父窗口句柄 + NULL, // 菜单句柄 + AfxGetInstanceHandle(), // 应用程序实例句柄 + NULL // 窗口创建数据 + ); + if (hWnd) + { + enable_alpha_composition(hWnd); + } + } + } + return TRUE; +} + +void CTransparentBgDlg::ShowTransparentWindow() +{ + if (hWnd) + { + ::ShowWindow(hWnd, SW_SHOW); // 显示窗口 + UpdateWindow(); // 更新窗口 + } +} +void CTransparentBgDlg::HideTransparentWindow() +{ + if (hWnd) + { + ::ShowWindow(hWnd, SW_HIDE); // 隐藏窗口 + UpdateWindow(); // 更新窗口 + } +} + +// set control text from config. +void CTransparentBgDlg::InitCtrlText() +{ + m_staticChannelName.SetWindowText(commonCtrlChannel); + m_bnJoinChannel.SetWindowText(commonCtrlJoinChannel); + m_editChannel.SetWindowText(_T("")); +} + +void CTransparentBgDlg::InvalidateVideo() +{ + m_staticVideo.Invalidate(); + m_staticVideoLeft.Invalidate(); + m_staticVideoRight.Invalidate(); +} + +int CTransparentBgDlg::JoinChannel(const char *channel) +{ + VideoEncoderConfiguration config; + config.advanceOptions.encodeAlpha = true; + m_rtcEngine->setVideoEncoderConfiguration(config); + + ChannelMediaOptions options; + options.channelProfile = CHANNEL_PROFILE_LIVE_BROADCASTING; + options.clientRoleType = CLIENT_ROLE_BROADCASTER; + options.autoSubscribeAudio = false; + options.autoSubscribeVideo = true; + options.publishMicrophoneTrack = false; + options.publishCameraTrack = false; + options.publishCustomVideoTrack = true; + // join channel in the engine. + int ret = m_rtcEngine->joinChannel(APP_TOKEN, channel, m_uid, options); + + return ret; +} + +int CTransparentBgDlg::LeaveChannel() +{ + if (!m_joinChannel) + { + return 0; + }; + m_rtcEngine->stopPreview(); + int ret = m_rtcEngine->leaveChannel(); + m_joinChannel = false; + m_remoteId = 0; + return ret; +} + +void CTransparentBgDlg::StartPlay() +{ + m_mediaPlayer = m_rtcEngine->createMediaPlayer(); + if (m_mediaPlayer) + { + m_mediaPlayer->setView((agora::media::base::view_t)m_staticVideo.GetSafeHwnd()); + m_mediaPlayer->setRenderMode(RENDER_MODE_FIT); + m_mediaPlayer->registerVideoFrameObserver(this); + m_mediaPlayer->registerPlayerSourceObserver(this); + MediaSource mediaSource; + CString videoUrl = GetExePath() + _T("\\yuvj_full_range_alpha_1280_540_left.mp4"); + std::string tmp = cs2utf8(videoUrl); + mediaSource.url = tmp.c_str(); + CString pathInfo; + int ret = m_mediaPlayer->openWithMediaSource(mediaSource); + CString strInfo; + strInfo.Format(_T("openWithMediaSource ret: %d"), ret); + m_listInfo.InsertString(m_listInfo.GetCount(), strInfo); + m_mediaPlayer->setLoopCount(-1); + } +} + +void CTransparentBgDlg::StopPlay() +{ + if (m_mediaPlayer) + { + m_mediaPlayer->stop(); + } +} +void CTransparentBgDlg::onFrame(const VideoFrame *frame) +{ + if (m_rtcEngine) + { + + // 创建ExternalVideoFrame对象 + agora::media::base::ExternalVideoFrame externalFrame; + externalFrame.alphaStitchMode = ALPHA_STITCH_LEFT; + externalFrame.type = agora::media::base::ExternalVideoFrame::VIDEO_BUFFER_TYPE::VIDEO_BUFFER_RAW_DATA; + externalFrame.format = agora::media::base::VIDEO_PIXEL_I420; + externalFrame.stride = frame->width; + externalFrame.height = frame->height; + externalFrame.timestamp = m_rtcEngine->getCurrentMonotonicTimeInMs(); + + int bufferSize = frame->yStride * frame->height + frame->uStride * (frame->height >> 1) + frame->vStride * (frame->height >> 1); + externalFrame.buffer = new uint8_t[bufferSize]; + memcpy(externalFrame.buffer, frame->yBuffer, frame->yStride * frame->height); + memcpy((uint8_t *)externalFrame.buffer + frame->yStride * frame->height, frame->uBuffer, frame->uStride * (frame->height >> 1)); + memcpy((uint8_t *)externalFrame.buffer + frame->yStride * frame->height + frame->uStride * (frame->height >> 1), frame->vBuffer, frame->vStride * (frame->height / 2)); + mediaEngine->pushVideoFrame(&externalFrame); + // 释放缓冲区 + delete[] externalFrame.buffer; + + // CString strInfo; + // strInfo.Format(_T("height ret: %d"), frame->height); + // m_listInfo.InsertString(m_listInfo.GetCount(), strInfo); + } +} + +void CTransparentBgDlg::onPlayerSourceStateChanged(media::base::MEDIA_PLAYER_STATE state, media::base::MEDIA_PLAYER_REASON reason) +{ + CString strState; + CString strError; + switch (state) + { + case agora::media::base::PLAYER_STATE_OPEN_COMPLETED: + strState = _T("PLAYER_STATE_OPEN_COMPLETED"); + m_mediaPlayer->play(); + break; + case agora::media::base::PLAYER_STATE_OPENING: + strState = _T("PLAYER_STATE_OPENING"); + break; + case agora::media::base::PLAYER_STATE_IDLE: + strState = _T("PLAYER_STATE_IDLE"); + break; + case agora::media::base::PLAYER_STATE_PLAYING: + strState = _T("PLAYER_STATE_PLAYING"); + break; + case agora::media::base::PLAYER_STATE_PLAYBACK_COMPLETED: + strState = _T("PLAYER_STATE_PLAYBACK_COMPLETED"); + break; + case agora::media::base::PLAYER_STATE_PLAYBACK_ALL_LOOPS_COMPLETED: + strState = _T("PLAYER_STATE_PLAYBACK_ALL_LOOPS_COMPLETED"); + break; + case agora::media::base::PLAYER_STATE_PAUSED: + strState = _T("PLAYER_STATE_PAUSED"); + break; + case agora::media::base::PLAYER_STATE_STOPPED: + strState = _T("PLAYER_STATE_STOPPED"); + break; + case agora::media::base::PLAYER_STATE_FAILED: + strState = _T("PLAYER_STATE_FAILED"); + m_mediaPlayer->stop(); + break; + default: + strState = _T("PLAYER_STATE_UNKNOWN"); + break; + } + switch (reason) + { + case agora::media::base::PLAYER_REASON_URL_NOT_FOUND: + strError = _T("PLAYER_ERROR_URL_NOT_FOUND"); + break; + case agora::media::base::PLAYER_REASON_NONE: + strError = _T("PLAYER_ERROR_NONE"); + break; + case agora::media::base::PLAYER_REASON_CODEC_NOT_SUPPORTED: + strError = _T("PLAYER_ERROR_NONE"); + break; + case agora::media::base::PLAYER_REASON_INVALID_ARGUMENTS: + strError = _T("PLAYER_ERROR_INVALID_ARGUMENTS"); + break; + case agora::media::base::PLAYER_REASON_SRC_BUFFER_UNDERFLOW: + strError = _T("PLAY_ERROR_SRC_BUFFER_UNDERFLOW"); + break; + case agora::media::base::PLAYER_REASON_INTERNAL: + strError = _T("PLAYER_ERROR_INTERNAL"); + break; + case agora::media::base::PLAYER_REASON_INVALID_STATE: + strError = _T("PLAYER_ERROR_INVALID_STATE"); + break; + case agora::media::base::PLAYER_REASON_NO_RESOURCE: + strError = _T("PLAYER_ERROR_NO_RESOURCE"); + break; + case agora::media::base::PLAYER_REASON_OBJ_NOT_INITIALIZED: + strError = _T("PLAYER_ERROR_OBJ_NOT_INITIALIZED"); + break; + case agora::media::base::PLAYER_REASON_INVALID_CONNECTION_STATE: + strError = _T("PLAYER_ERROR_INVALID_CONNECTION_STATE"); + break; + case agora::media::base::PLAYER_REASON_UNKNOWN_STREAM_TYPE: + strError = _T("PLAYER_ERROR_UNKNOWN_STREAM_TYPE"); + break; + case agora::media::base::PLAYER_REASON_VIDEO_RENDER_FAILED: + strError = _T("PLAYER_ERROR_VIDEO_RENDER_FAILED"); + break; + } + CString strInfo; + strInfo.Format(_T("sta:%s,\nerr:%s"), strState, strError); + m_listInfo.InsertString(m_listInfo.GetCount(), strInfo); +} + +// Initialize the Agora SDK +bool CTransparentBgDlg::InitAgora() +{ + // create Agora RTC engine + m_rtcEngine = createAgoraRtcEngine(); + if (!m_rtcEngine) + { + m_listInfo.InsertString(m_listInfo.GetCount(), _T("createAgoraRtcEngine failed")); + return false; + } + // set message notify receiver window + m_eventHandler.SetMsgReceiver(m_hWnd); + + RtcEngineContext context; + std::string strAppID = GET_APP_ID; + context.appId = strAppID.c_str(); + context.eventHandler = &m_eventHandler; + // set channel profile in the engine to the CHANNEL_PROFILE_LIVE_BROADCASTING. + context.channelProfile = CHANNEL_PROFILE_LIVE_BROADCASTING; + // initialize the Agora RTC engine context. + int ret = m_rtcEngine->initialize(context); + if (ret != 0) + { + m_initialize = false; + CString strInfo; + strInfo.Format(_T("initialize failed: %d"), ret); + m_listInfo.InsertString(m_listInfo.GetCount(), strInfo); + return false; + } + else + { + m_initialize = true; + m_listInfo.InsertString(m_listInfo.GetCount(), _T("createAgoraRtcEngine success")); + + mediaEngine.queryInterface(m_rtcEngine, agora::rtc::AGORA_IID_MEDIA_ENGINE); + agora::base::AParameter apm(m_rtcEngine); + mediaEngine->setExternalVideoSource(true, true); + } + m_rtcEngine->enableVideo(); + m_rtcEngine->enableAudio(); + return true; +} + +// UnInitialize the Agora SDK +void CTransparentBgDlg::UnInitAgora() +{ + if (m_rtcEngine) + { + if (m_joinChannel) + LeaveChannel(); + // m_rtcEngine->stopPreview(); + m_rtcEngine->disableVideo(); + if (m_mediaPlayer) + { + // m_mediaPlayer->registerPlayerSourceObserver(nullptr); + m_mediaPlayer->Release(); + m_mediaPlayer = nullptr; + } + + if (m_initialize) + { + m_rtcEngine->release(true); + } + m_rtcEngine = NULL; + + // if (hWnd) + // { + + // ::DestroyWindow(hWnd); + // hWnd = NULL; + // } + HideTransparentWindow(); + } +} + +LRESULT CTransparentBgDlg::OnEIDJoinChannelSuccess(WPARAM wParam, LPARAM lParam) +{ + CString strInfo; + strInfo.Format(TEXT("self join success, wParam=%u"), wParam); + m_listInfo.InsertString(m_listInfo.GetCount(), strInfo); + // notify parent window + ::PostMessage(GetParent()->GetSafeHwnd(), WM_MSGID(EID_JOINCHANNEL_SUCCESS), TRUE, 0); + return 0; +} + +LRESULT CTransparentBgDlg::OnEIDError(WPARAM wParam, LPARAM lParam) +{ + CString strInfo; + char *message = (char *)wParam; + int code = lParam; + strInfo.Format(TEXT("Error >> code=%d, message=%s"), code, message); + m_listInfo.InsertString(m_listInfo.GetCount(), strInfo); + delete message; + return 0; +} + +LRESULT CTransparentBgDlg::OnEIDLeaveChannel(WPARAM wParam, LPARAM lParam) +{ + CString strInfo; + strInfo.Format(TEXT("leave channel success")); + m_listInfo.InsertString(m_listInfo.GetCount(), strInfo); + // notify parent window + ::PostMessage(GetParent()->GetSafeHwnd(), WM_MSGID(EID_JOINCHANNEL_SUCCESS), FALSE, 0); + InvalidateVideo(); + return 0; +} + +LRESULT CTransparentBgDlg::OnEIDUserJoined(WPARAM wParam, LPARAM lParam) +{ + if (m_remoteId != 0) + { + m_listInfo.InsertString(m_listInfo.GetCount(), _T("user joined already")); + return 0; + } + CString strInfo; + strInfo.Format(TEXT("user %u joined"), wParam); + m_listInfo.InsertString(m_listInfo.GetCount(), strInfo); + uid_t remoteUid = (uid_t)wParam; + m_remoteId = remoteUid; + VideoCanvas canvas; + canvas.enableAlphaMask = true; + canvas.uid = m_remoteId; + // canvas.view = m_staticVideoRight.GetSafeHwnd(); + canvas.view = hWnd; + canvas.renderMode = media::base::RENDER_MODE_FIT; + m_rtcEngine->setupRemoteVideo(canvas); + ShowTransparentWindow(); + return 0; +} + +LRESULT CTransparentBgDlg::OnEIDUserOffline(WPARAM wParam, LPARAM lParam) +{ + uid_t remoteUid = (uid_t)wParam; + if (m_remoteId == remoteUid) + { + m_remoteId = 0; + VideoCanvas canvas; + canvas.uid = remoteUid; + canvas.view = NULL; + m_rtcEngine->setupRemoteVideo(canvas); + HideTransparentWindow(); + CString strInfo; + strInfo.Format(TEXT("%u offline, reason:%d"), remoteUid, lParam); + m_listInfo.InsertString(m_listInfo.GetCount(), strInfo); + } + InvalidateVideo(); + return 0; +} + +BOOL CTransparentBgDlg::PreTranslateMessage(MSG *pMsg) +{ + if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN) + { + return TRUE; + } + return CDialogEx::PreTranslateMessage(pMsg); +} + +LRESULT CTransparentBgDlg::onEIDLocalAudioStats(WPARAM wParam, LPARAM lParam) +{ + LocalAudioStats *stats = (LocalAudioStats *)wParam; + + CString strInfo = _T("===onLocalAudioStats==="); + m_listInfo.InsertString(m_listInfo.GetCount(), strInfo); + + strInfo.Format(_T("numChannels:%u"), stats->numChannels); + m_listInfo.InsertString(m_listInfo.GetCount(), strInfo); + strInfo.Format(_T("sentSampleRate:%u"), stats->sentSampleRate); + m_listInfo.InsertString(m_listInfo.GetCount(), strInfo); + return 0; +} + +LRESULT CTransparentBgDlg::onEIDRemoteAudioStats(WPARAM wParam, LPARAM lParam) +{ + RemoteAudioStats *stats = (RemoteAudioStats *)wParam; + + CString strInfo = _T("===onRemoteAudioStats==="); + m_listInfo.InsertString(m_listInfo.GetCount(), strInfo); + + strInfo.Format(_T("uid:%u"), stats->uid); + m_listInfo.InsertString(m_listInfo.GetCount(), strInfo); + strInfo.Format(_T("quality:%d"), stats->quality); + m_listInfo.InsertString(m_listInfo.GetCount(), strInfo); + return 0; +} + +LRESULT CTransparentBgDlg::onEIDLocalVideoStats(WPARAM wParam, LPARAM lParam) +{ + LocalVideoStats *stats = (LocalVideoStats *)wParam; + + CString strInfo = _T("===onLocalVideoStats==="); + m_listInfo.InsertString(m_listInfo.GetCount(), strInfo); + + strInfo.Format(_T("uid:%u"), stats->uid); + m_listInfo.InsertString(m_listInfo.GetCount(), strInfo); + strInfo.Format(_T("sentBitrate:%d"), stats->sentBitrate); + m_listInfo.InsertString(m_listInfo.GetCount(), strInfo); + + strInfo.Format(_T("sentFrameRate:%d"), stats->sentFrameRate); + m_listInfo.InsertString(m_listInfo.GetCount(), strInfo); + strInfo.Format(_T("encoderOutputFrameRate:%d"), stats->encoderOutputFrameRate); + m_listInfo.InsertString(m_listInfo.GetCount(), strInfo); + + return 0; +} + +LRESULT CTransparentBgDlg::onEIDRemoteVideoStats(WPARAM wParam, LPARAM lParam) +{ + RemoteVideoStats *stats = (RemoteVideoStats *)wParam; + CString strInfo = _T("===onRemoteVideoStats==="); + m_listInfo.InsertString(m_listInfo.GetCount(), strInfo); + strInfo.Format(_T("uid:%u"), stats->uid); + m_listInfo.InsertString(m_listInfo.GetCount(), strInfo); + strInfo.Format(_T("delay:%d"), stats->delay); + m_listInfo.InsertString(m_listInfo.GetCount(), strInfo); + + strInfo.Format(_T("width:%d"), stats->width); + m_listInfo.InsertString(m_listInfo.GetCount(), strInfo); + strInfo.Format(_T("height:%d"), stats->height); + m_listInfo.InsertString(m_listInfo.GetCount(), strInfo); + + strInfo.Format(_T("receivedBitrate:%d"), stats->receivedBitrate); + m_listInfo.InsertString(m_listInfo.GetCount(), strInfo); + return 0; +} + +void CTransparentBgDlg::OnBnClickedButtonJoinchannel() +{ + CString info; + if (!m_joinChannel) + { + CString strChannelName; + m_editChannel.GetWindowText(strChannelName); + if (strChannelName.IsEmpty()) + { + MessageBox(_T("频道号不能为空")); + return; + } + + VideoCanvas canvas; + canvas.mirrorMode = VIDEO_MIRROR_MODE_DISABLED; + canvas.sourceType = VIDEO_SOURCE_CUSTOM; + canvas.enableAlphaMask = true; + canvas.renderMode = media::base::RENDER_MODE_FIT; + canvas.uid = m_uid; + canvas.view = m_staticVideoLeft.GetSafeHwnd(); + m_rtcEngine->setupLocalVideo(canvas); + m_rtcEngine->startPreview(VIDEO_SOURCE_CUSTOM); + + std::string szChannelName = cs2utf8(strChannelName); + int ret = JoinChannel(szChannelName.c_str()); + if (ret == 0) + { + m_joinChannel = true; + m_bnJoinChannel.SetWindowText(commonCtrlLeaveChannel); + + StartPlay(); + } + else + { + const char *des = getAgoraSdkErrorDescription(ret); + info.Format(TEXT("join channel failed >> code=%d, des=%s"), ret, utf82cs(std::string(des))); + m_listInfo.InsertString(m_listInfo.GetCount(), info); + } + } + else + { + int ret = LeaveChannel(); + HideTransparentWindow(); + if (0 == ret) + { + InitCtrlText(); + InvalidateVideo(); + StopPlay(); + } + else + { + const char *des = getAgoraSdkErrorDescription(ret); + info.Format(TEXT("leave channel failed >> code=%d, des=%s"), ret, utf82cs(std::string(des))); + m_listInfo.InsertString(m_listInfo.GetCount(), info); + } + } +} + +void CTransparentBgDlg::OnShowWindow(BOOL bShow, UINT nStatus) +{ + CDialogEx::OnShowWindow(bShow, nStatus); + + if (bShow) + { + m_listInfo.ResetContent(); + } + else + { + InitCtrlText(); + InvalidateVideo(); + } +} diff --git a/windows/APIExample/APIExample/Advanced/TransparentBg/TransparentBgDialog.h b/windows/APIExample/APIExample/Advanced/TransparentBg/TransparentBgDialog.h new file mode 100644 index 000000000..f470e59a1 --- /dev/null +++ b/windows/APIExample/APIExample/Advanced/TransparentBg/TransparentBgDialog.h @@ -0,0 +1,140 @@ +#pragma once +#include "AGVideoWnd.h" +#include +#include +#include "stdafx.h" +#include +#include +#include "AbstractMediaPlayerSourceObserver.h" +using namespace agora::media::base; + +#define _IsWindowsVistaOrGreater() \ + _IsWindowsVersionOrGreater(HIBYTE(0x0600), LOBYTE(0x0600), \ + 0) // _WIN32_WINNT_VISTA +#define _IsWindows8OrGreater() \ + _IsWindowsVersionOrGreater(HIBYTE(0x0602), LOBYTE(0x0602), \ + 0) // _WIN32_WINNT_WIN8 +#define _IsWindows8Point1OrGreater() \ + _IsWindowsVersionOrGreater(HIBYTE(0x0603), LOBYTE(0x0603), \ + 0) // _WIN32_WINNT_WINBLUE +#define _IsWindows10OrGreater() \ + _IsWindowsVersionOrGreater( \ + HIBYTE(0x0A00), LOBYTE(0x0A00), \ + 0) // _WIN32_WINNT_WINTHRESHOLD / _WIN32_WINNT_WIN10 + +class CTransparentBgEventHandler : public IRtcEngineEventHandler +{ +public: + void SetMsgReceiver(HWND hWnd) { m_hMsgHanlder = hWnd; } + virtual void onJoinChannelSuccess(const char *channel, uid_t uid, int elapsed) override; + virtual void onUserJoined(uid_t uid, int elapsed) override; + virtual void onUserOffline(uid_t uid, USER_OFFLINE_REASON_TYPE reason) override; + virtual void onLeaveChannel(const RtcStats &stats) override; + virtual void onLocalAudioStats(const LocalAudioStats &stats) override + { + if (m_hMsgHanlder && report) + { + LocalAudioStats *s = new LocalAudioStats; + *s = stats; + ::PostMessage(m_hMsgHanlder, WM_MSGID(EID_LOCAL_AUDIO_STATS), (WPARAM)s, 0); + } + } + virtual void onRemoteAudioStats(const RemoteAudioStats &stats) + { + if (m_hMsgHanlder && report) + { + RemoteAudioStats *s = new RemoteAudioStats; + *s = stats; + ::PostMessage(m_hMsgHanlder, WM_MSGID(EID_REMOTE_AUDIO_STATS), (WPARAM)s, 0); + } + } + virtual void onRemoteVideoStats(const RemoteVideoStats &stats) + { + if (m_hMsgHanlder && report) + { + RemoteVideoStats *s = new RemoteVideoStats; + *s = stats; + ::PostMessage(m_hMsgHanlder, WM_MSGID(EID_REMOTE_VIDEO_STATS), (WPARAM)s, 0); + } + } + virtual void onLocalVideoStats(VIDEO_SOURCE_TYPE source, const LocalVideoStats &stats) override; + virtual void onError(int err, const char *msg) + { + if (m_hMsgHanlder) + { + char *message = new char[1024]{0}; + memcpy(message, msg, strlen(msg)); + ::PostMessage(m_hMsgHanlder, WM_MSGID(EID_ERROR), (WPARAM)message, err); + } + } + +private: + HWND m_hMsgHanlder = nullptr; + bool report = true; +}; + +class CTransparentBgDlg : public CDialogEx, media::base::IVideoFrameObserver, AbstractMediaPlayerSourceObserver +{ + DECLARE_DYNAMIC(CTransparentBgDlg) + +public: + CTransparentBgDlg(CWnd *pParent = nullptr); // standard constructor + virtual ~CTransparentBgDlg(); + bool InitAgora(); + void UnInitAgora(); + void StartPlay(); + void StopPlay(); + virtual void onFrame(const VideoFrame *frame); + virtual void onPlayerSourceStateChanged(media::base::MEDIA_PLAYER_STATE state, media::base::MEDIA_PLAYER_REASON reason); + enum + { + IDD = IDD_DIALOG_TRANSPARENT_BG + }; + +protected: + virtual void DoDataExchange(CDataExchange *pDX); // DDX/DDV support + virtual BOOL OnInitDialog(); + virtual BOOL PreTranslateMessage(MSG *pMsg); + DECLARE_MESSAGE_MAP() +public: + afx_msg LRESULT OnEIDJoinChannelSuccess(WPARAM wParam, LPARAM lParam); + afx_msg LRESULT OnEIDError(WPARAM wParam, LPARAM lParam); + afx_msg LRESULT OnEIDLeaveChannel(WPARAM wParam, LPARAM lParam); + afx_msg LRESULT OnEIDUserJoined(WPARAM wParam, LPARAM lParam); + afx_msg LRESULT OnEIDUserOffline(WPARAM wParam, LPARAM lParam); + afx_msg LRESULT onEIDLocalAudioStats(WPARAM wParam, LPARAM lParam); + afx_msg LRESULT onEIDRemoteAudioStats(WPARAM wParam, LPARAM lParam); + afx_msg LRESULT onEIDLocalVideoStats(WPARAM wParam, LPARAM lParam); + afx_msg LRESULT onEIDRemoteVideoStats(WPARAM wParam, LPARAM lParam); + afx_msg void OnShowWindow(BOOL bShow, UINT nStatus); + afx_msg void OnBnClickedButtonJoinchannel(); + +private: + void InitCtrlText(); + int JoinChannel(const char *channel); + int LeaveChannel(); + void InvalidateVideo(); + static BOOL _IsWindowsVersionOrGreater(WORD major, WORD minor, WORD); + void enable_alpha_composition(void *hwnd); + void ShowTransparentWindow(); + void HideTransparentWindow(); + + CStatic m_staticChannelName; + CButton m_bnJoinChannel; + CEdit m_editChannel; + CStatic m_staticVideo; + CStatic m_staticVideoLeft; + CStatic m_staticVideoRight; + CListBox m_listInfo; + + IRtcEngine *m_rtcEngine = nullptr; + agora_refptr m_mediaPlayer = nullptr; + agora::util::AutoPtr mediaEngine; + CTransparentBgEventHandler m_eventHandler; + // agora::media::base::ExternalVideoFrame m_videoFrame; + bool m_joinChannel = false; + bool m_initialize = false; + int m_uid = 1001; + int m_remoteId = 0; + HWND hWnd = NULL; +}; diff --git a/windows/APIExample/APIExample/Basic/LiveBroadcasting/CLiveBroadcastingDlg.cpp b/windows/APIExample/APIExample/Basic/LiveBroadcasting/CLiveBroadcastingDlg.cpp index a719912d1..428d9b7f1 100755 --- a/windows/APIExample/APIExample/Basic/LiveBroadcasting/CLiveBroadcastingDlg.cpp +++ b/windows/APIExample/APIExample/Basic/LiveBroadcasting/CLiveBroadcastingDlg.cpp @@ -181,7 +181,6 @@ BEGIN_MESSAGE_MAP(CLiveBroadcastingDlg, CDialogEx) ON_BN_CLICKED(IDC_BUTTON_PRELOAD, &CLiveBroadcastingDlg::OnBnClickedButtonPreload) ON_BN_CLICKED(IDC_RADIO_CANVAS_HIDDEN, &CLiveBroadcastingDlg::OnBnClickedRadioCanvasRenderMode) ON_BN_CLICKED(IDC_RADIO_CANVAS_FIT, &CLiveBroadcastingDlg::OnBnClickedRadioCanvasRenderMode) - ON_BN_CLICKED(IDC_RADIO_CANVAS_ADAPTIVE, &CLiveBroadcastingDlg::OnBnClickedRadioCanvasRenderMode) ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_SLIDER_CANVAS_COLOR, &CLiveBroadcastingDlg::OnNMCustomdrawSliderCanvasColor) ON_BN_CLICKED(IDC_CHECK_VIDEO_IMAGE, &CLiveBroadcastingDlg::OnBnClickedCheckVideoImage) END_MESSAGE_MAP() @@ -1171,7 +1170,7 @@ void CLiveBroadcastingDlg::OnBnClickedRadioCanvasRenderMode() m_canvasRenderMode = media::base::RENDER_MODE_FIT; break; default: - m_canvasRenderMode = media::base::RENDER_MODE_ADAPTIVE; + m_canvasRenderMode = media::base::RENDER_MODE_HIDDEN; break; } ResetVideoView(); diff --git a/windows/APIExample/APIExample/Language.h b/windows/APIExample/APIExample/Language.h index be0021ae4..9a18d37b6 100755 --- a/windows/APIExample/APIExample/Language.h +++ b/windows/APIExample/APIExample/Language.h @@ -151,6 +151,7 @@ extern wchar_t beautyAudioCtrlSetAudioChange[INFO_LEN]; extern wchar_t beautyAudioCtrlUnSetAudioChange[INFO_LEN]; extern wchar_t beautyAudioCtrlChange[INFO_LEN]; extern wchar_t beautyAudioCtrlAINSMode[INFO_LEN]; +extern wchar_t beautyAudioCtrlAITuner[INFO_LEN]; extern wchar_t beautyAudioCtrlPreSet[INFO_LEN]; extern wchar_t beautyAudioCtrlParam1[INFO_LEN]; extern wchar_t beautyAudioCtrlParam2[INFO_LEN]; @@ -347,3 +348,6 @@ extern wchar_t localVideoTranscodingVirtualBg[INFO_LEN]; extern wchar_t advancedMetadataVideo[INFO_LEN]; extern wchar_t advancedMetadataAudio[INFO_LEN]; extern wchar_t advancedMetadataSend[INFO_LEN]; + +//transparent background +extern wchar_t TransparentBackground[INFO_LEN]; diff --git a/windows/APIExample/APIExample/en.ini b/windows/APIExample/APIExample/en.ini index 02ccca071..0fad41a7c 100755 --- a/windows/APIExample/APIExample/en.ini +++ b/windows/APIExample/APIExample/en.ini @@ -290,6 +290,7 @@ BeautyAudio.Ctrl.Change=Change BeautyAudio.Ctrl.Param1=EffectParam1 BeautyAudio.Ctrl.Param2=EffectParam2 BeautyAudio.Ctrl.AINSMode=AINSMode +BeautyAudio.Ctrl.AITuner=AITuner SpatialAudio=Spatial Audio SpatialAudio.Init.Info=Insert earphone feel 3D audio effect. @@ -303,4 +304,6 @@ LocalVideoTranscoding.VirtualBg=TranslusionBackground Metadata.Video=Video Metadata Metadata.Audio=Audio Metadata -Metadata.Send=Send \ No newline at end of file +Metadata.Send=Send + +TransparentBackground=TransparentBackground \ No newline at end of file diff --git a/windows/APIExample/APIExample/res/yuvj_full_range_alpha_1280_540_left.mp4 b/windows/APIExample/APIExample/res/yuvj_full_range_alpha_1280_540_left.mp4 new file mode 100644 index 000000000..2e9dfbd99 Binary files /dev/null and b/windows/APIExample/APIExample/res/yuvj_full_range_alpha_1280_540_left.mp4 differ diff --git a/windows/APIExample/APIExample/resource.h b/windows/APIExample/APIExample/resource.h index 3302b60af..5cfaac205 100755 --- a/windows/APIExample/APIExample/resource.h +++ b/windows/APIExample/APIExample/resource.h @@ -45,6 +45,7 @@ #define IDD_DIALOG_MULTI_VIDEO_SOURCE_TRACKS 162 #define IDD_DIALOG_FACE_CAPTURE 163 #define IDD_DIALOG_JOINCHANNELVIDEOBYTOKEN 164 +#define IDD_DIALOG_TRANSPARENT_BG 165 #define IDB_BITMAP_LOCAL 168 #define IDB_BITMAP_REMOTE 170 #define IDB_BITMAP_SPEAKER 171 @@ -201,8 +202,11 @@ #define IDC_COMBO_AUDIO_AINS_MODE 1067 #define IDC_STATIC_REGION_RECT 1068 #define IDC_STATIC_MIXING_PUBLISH_VOLUME 1068 +#define IDC_STATIC_BEAUTY_AUDIO_AI_TUNER 1068 #define IDC_STATIC_SCREEN_INFO 1069 #define IDC_STATIC_EFFECT_VOLUME 1069 +#define IDC_COMBO_AUDIO_AINS_MODE2 1069 +#define IDC_COMBO_AUDIO_AI_TUNER 1069 #define IDC_STATIC_SCREEN_INFO2 1070 #define IDC_STATIC_ORIGINAL_AUDIO 1070 #define IDC_STATIC_ORIGINAL_AUDIO_PROC 1071 @@ -398,7 +402,9 @@ #define IDC_EDIT_TOKEN 1150 #define IDC_EDIT_METADATA_INFO 1150 #define IDC_STATIC_TOKEN 1151 +#define IDC_STATIC_VIDEO_LEFT 1151 #define IDC_USER_ID 1152 +#define IDC_STATIC_VIDEO_RIGHT 1152 #define IDC_EDIT_USER_ID 1153 #define IDC_BUTTON_ADD_CROSS_CHANNEL 1154 #define IDC_CROSS_CHANNEL_LIST 1155 @@ -420,9 +426,9 @@ // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 173 +#define _APS_NEXT_RESOURCE_VALUE 177 #define _APS_NEXT_COMMAND_VALUE 32771 -#define _APS_NEXT_CONTROL_VALUE 1151 +#define _APS_NEXT_CONTROL_VALUE 1153 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/windows/APIExample/APIExample/stdafx.cpp b/windows/APIExample/APIExample/stdafx.cpp index 57cfaf651..8797e7223 100755 --- a/windows/APIExample/APIExample/stdafx.cpp +++ b/windows/APIExample/APIExample/stdafx.cpp @@ -5,6 +5,7 @@ #include "stdafx.h" #include "CConfig.h" + wchar_t commonGroupDoc[INFO_LEN] = { 0 }; wchar_t commonDocumentWebsite[INFO_LEN] = { 0 }; wchar_t commonFAQWebsite[INFO_LEN] = { 0 }; @@ -115,6 +116,7 @@ wchar_t beautyAudioCtrlPreSet[INFO_LEN] = { 0 }; wchar_t beautyAudioCtrlParam1[INFO_LEN] = { 0 }; wchar_t beautyAudioCtrlParam2[INFO_LEN] = { 0 }; wchar_t beautyAudioCtrlAINSMode[INFO_LEN] = { 0 }; +wchar_t beautyAudioCtrlAITuner[INFO_LEN] = { 0 }; //set audio profile @@ -317,6 +319,11 @@ wchar_t advancedMetadataVideo[INFO_LEN] = { 0 }; wchar_t advancedMetadataAudio[INFO_LEN] = { 0 }; wchar_t advancedMetadataSend[INFO_LEN] = { 0 }; + +//transparent backgroud +wchar_t TransparentBackground[INFO_LEN] = { 0 }; + + std::string cs2utf8(CString str) { char szBuf[2 * MAX_PATH] = { 0 }; @@ -726,6 +733,7 @@ void InitKeyInfomation() _tcscpy_s(beautyAudioCtrlParam1, INFO_LEN, Str(_T("BeautyAudio.Ctrl.Param1"))); _tcscpy_s(beautyAudioCtrlParam2, INFO_LEN, Str(_T("BeautyAudio.Ctrl.Param2"))); _tcscpy_s(beautyAudioCtrlAINSMode, INFO_LEN, Str(_T("BeautyAudio.Ctrl.AINSMode"))); + _tcscpy_s(beautyAudioCtrlAITuner, INFO_LEN, Str(_T("BeautyAudio.Ctrl.AITuner"))); @@ -765,6 +773,10 @@ void InitKeyInfomation() _tcscpy_s(advancedMetadataAudio, INFO_LEN, Str(_T("Metadata.Audio"))); _tcscpy_s(advancedMetadataSend, INFO_LEN, Str(_T("Metadata.Send"))); + //transparent bg + _tcscpy_s(TransparentBackground, INFO_LEN, Str(_T("TransparentBackground"))); + + /* _tcscpy_s(, INFO_LEN, Str(_T(""))); _tcscpy_s(, INFO_LEN, Str(_T(""))); _tcscpy_s(, INFO_LEN, Str(_T(""))); diff --git a/windows/APIExample/APIExample/zh-cn.ini b/windows/APIExample/APIExample/zh-cn.ini index 21c02b671..7d44135cb 100755 --- a/windows/APIExample/APIExample/zh-cn.ini +++ b/windows/APIExample/APIExample/zh-cn.ini @@ -138,6 +138,7 @@ BeautyAudio.Ctrl.Change= BeautyAudio.Ctrl.Param1=Ч1 BeautyAudio.Ctrl.Param2=Ч2 BeautyAudio.Ctrl.AINSMode=AIģʽ +BeautyAudio.Ctrl.AITuner=AI AudioMixing.Ctrl.Mixing= AudioMixing.Ctrl.Effect=Ч @@ -293,4 +294,6 @@ LocalVideoTranscoding.VirtualBg=͸ Metadata.Video=ƵԪ Metadata.Audio=ƵԪ -Metadata.Send= \ No newline at end of file +Metadata.Send= + +TransparentBackground=͸ \ No newline at end of file diff --git a/windows/APIExample/cloud_build.bat b/windows/APIExample/cloud_build.bat new file mode 100644 index 000000000..6df7200fb --- /dev/null +++ b/windows/APIExample/cloud_build.bat @@ -0,0 +1,10 @@ +echo "compile start..." +call installThirdParty.bat +"C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\MSBuild.exe" "APIExample.sln" /p:platform="Win32" /p:configuration="Release" +7z a -tzip result.zip -r Release + +set h=%time:~0,2% +set h=%h: =0% +copy result.zip %WORKSPACE%\\APIExample_windows_%BUILD_NUMBER%_%date:~4,2%%date:~7,2%%h%%time:~3,2%_Release_exe.zip +del /F result.zip +echo "compile done." \ No newline at end of file diff --git a/windows/APIExample/install.ps1 b/windows/APIExample/install.ps1 index 097b988ce..64d207336 100644 --- a/windows/APIExample/install.ps1 +++ b/windows/APIExample/install.ps1 @@ -1,6 +1,6 @@ $ThirdPartysrc = 'https://fullapp.oss-cn-beijing.aliyuncs.com/API-Examples/ThirdParty.zip' $ThirdPartydes = 'ThirdParty.zip' -$agora_sdk = 'https://download.agora.io/sdk/release/Agora_Native_SDK_for_Windows_v4.3.1_FULL.zip' +$agora_sdk = 'https://download.agora.io/sdk/release/Agora_Native_SDK_for_Windows_v4.4.0_FULL.zip' $agora_des = 'AgoraSdk.zip' $agora_local_sdk = '../../sdk'