实现相芯美颜
本文介绍如何通过声网美颜场景化 API (Beauty API) 集成相芯美颜到实时音视频互动中。
示例项目
声网在 GitHub 上提供开源 BeautyAPI 示例项目供你参考。
准备开发环境
前提条件
开始前,请确保满足以下前提条件:
- Git
 - Java Development Kit 11
 - Android Studio 3.5 及以上
 - Android 手机,版本 Android 5.0(API Level 21)及以上
 - 开通服务
 - 已联系相芯技术获取最新的美颜 SDK、美颜资源、美颜证书
 - 已在项目中添加 Kotlin 插件
 
创建项目并集成 SDK
按照如下步骤准备开发环境:
- 
如需创建新项目,在 Android Studio 里,依次选择 Phone and Tablet > Empty Activity,创建 Android 项目。
注意创建项目后,Android Studio 会自动开始同步 gradle, 稍等片刻至同步成功后再进行下一步操作。
 - 
使用 Maven Central 将声网 RTC SDK 集成到你的项目中。
- 
在
/Gradle Scripts/build.gradle(Project: <projectname>)文件中添加如下代码,添加 Maven Central 依赖:Kotlinbuildscript {
repositories {
...
mavenCentral()
}
...
}
allprojects {
repositories {
...
mavenCentral()
}
}注意如果你的 Android 项目设置了 dependencyResolutionManagement,添加 Maven Central 依赖的方式可能存在差异。
 - 
在
/Gradle Scripts/build.gradle(Module: <projectname>.app)文件中添加如下代码,将声网 RTC SDK 集成到你的 Android 项目中:Kotlin// 配置相芯依赖库
allprojects {
repositories {
maven {
url = "http://maven.faceunity.com/repository/maven-public/"
allowInsecureProtocol = true
}
}
}
dependencies {
...
// x.y.z,请填写具体的 RTC SDK 版本号,如:4.0.0 或 4.1.0-1
// 通过实时互动产品发版说明获取最新版本号
implementation 'io.agora.rtc:full-sdk:x.y.z'
// 集成相芯美颜资源
implementation 'com.faceunity:core:8.3.0'
implementation 'com.faceunity:model:8.3.0'
} 
 - 
 - 
将相芯美颜 SDK 复制到你的项目中:请联系相芯技术支持获取美颜 SDK、美颜资源、证书等文件。下载并解压文件,然后添加到美颜项目对应的文件路径下:
文件 项目路径 美妆资源:例如 naicha.bundleapp/src/main/assets/beauty_faceunity/makeup贴纸资源:例如 fashi.bundleapp/src/main/assets/beauty_faceunity/sticker证书: authpack.javaapp/src/main/java/io/agora/beauty/demo/authpack.java - 
将声网美颜场景化 API 集成到你的项目中。添加 Android/lib_faceunity/src/main/java/io/agora/beautyapi/faceunity 目录下的文件到项目中,具体文件如下:
utils文件夹FaceUnityBeautyAPI.kt文件FaceUnityBeautyAPIImpl文件
注意为方便后续代码升级,请不要修改你添加的这些文件的名称和路径。
 - 
添加网络及设备权限。
在
/app/Manifests/AndroidManifest.xml文件中,在</application>后面添加如下权限:XML<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>
<!-- 对于 Android 12.0 及以上且集成 v4.1.0 以下声网 RTC SDK 的设备,还需要添加以下权限 -->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
<!-- 对于 Android 12.0 及以上设备,还需要添加以下权限 -->
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"/> - 
在
/Gradle Scripts/proguard-rules.pro文件中添加如下行,以防止声网 RTC SDK 的代码被混淆:Kotlin-keep class io.agora.**{*;}
-dontwarn javax.**
-dontwarn com.google.devtools.build.android.** 
实现美颜
本节展示如何在直播间内实现美颜功能,参考 API 时序图可查看总览。声网 RTC SDK 承担实时音视频的业务,相芯美颜 SDK 提供美颜功能,声网 Beauty API 封装了两个 SDK 中的 API 调用逻辑以简化你需要实现的代码逻辑。通过 Beauty API,你可以实现基础美颜功能,但是如果你还需要更丰富的美颜效果,例如贴纸、美妆风格,请直接调用美颜 SDK 中的 API。
1. 初始化 RtcEngine
调用声网 RTC SDK 中的 create 创建并初始化 RtcEngine 对象。
// 初始化声网 RtcEngine
private val mRtcEngine by lazy {
    RtcEngine.create(RtcEngineConfig().apply {
        mContext = applicationContext
        // 传入你从控制台获取的声网项目的 APP ID
        mAppId = BuildConfig.AGORA_APP_ID
        mEventHandler = object : IRtcEngineEventHandler() {}
    })
}
2. 初始化美颜 SDK
调用相芯美颜 SDK 的 registerFURender 初始化 FURenderKit 对象,同时传入如下参数:
context:Android Context(上下文)。getAuth():getAuth方法返回的鉴权字段,格式为 ByteArray。鉴权字段与本地authpack.java证书文件相关,只有验证通过后,你才能使用相芯美颜 SDK。object:事件回调。
初始化美颜 SDK 成功后,在新线程中通过 loadAIProcessor 加载 AI 道具。
object FaceUnityBeautySDK {
    private val TAG = "FaceUnityBeautySDK"
    private val fuAIKit = FUAIKit.getInstance()
    val fuRenderKit = FURenderKit.getInstance()
    // AI 道具
    private val BUNDLE_AI_FACE = "model" + File.separator + "ai_face_processor.bundle"
    private val BUNDLE_AI_HUMAN = "model" + File.separator + "ai_human_processor.bundle"
    private val workerThread = Executors.newSingleThreadExecutor()
    fun initBeauty(context: Context){
        // 设置美颜 SDK 日志
        FURenderManager.setKitDebug(FULogger.LogLevel.TRACE)
        FURenderManager.setCoreDebug(FULogger.LogLevel.ERROR)
        // 初始化美颜 SDK
        // 需传入美颜 SDK 鉴权字段并设置美颜 SDK 事件监听
        FURenderManager.registerFURender(context, getAuth(), object : OperateCallback {
            override fun onSuccess(code: Int, msg: String) {
                Log.i(TAG, "FURenderManager onSuccess -- code=$code, msg=$msg")
                if (code == OPERATE_SUCCESS_AUTH) {
                    faceunity.fuSetUseTexAsync(1)
                    // 如果初始化美颜 SDK 成功,在新线程中加载 AI 道具
                    workerThread.submit {
                        fuAIKit.loadAIProcessor(BUNDLE_AI_FACE, FUAITypeEnum.FUAITYPE_FACEPROCESSOR)
                        fuAIKit.loadAIProcessor(BUNDLE_AI_HUMAN, FUAITypeEnum.FUAITYPE_HUMAN_PROCESSOR)
                    }
                }
            }
            override fun onFail(errCode: Int, errMsg: String) {
                Log.e(TAG, "FURenderManager onFail -- code=$errCode, msg=$errMsg")
            }
        })
    }
    // 获取美颜 SDK 鉴权字段
    private fun getAuth(): ByteArray{
        val authpack = Class.forName("io.agora.beautyapi.demo.authpack")
        val aMethod = authpack.getDeclaredMethod("A")
        aMethod.isAccessible = true
        val authValue = aMethod.invoke(null) as? ByteArray
        return authValue ?: ByteArray(0)
    }
}
3. 初始化 Beauty API
调用 createFaceUnityBeautyAPI 创建 Beauty API 对象。Beauty API 对象基于 FuRenderKit 对象封装。
// 创建 Beauty API 对象
private val mFaceUnityApi by lazy {
    createFaceUnityBeautyAPI()
}
调用 initialize 初始化 Beauty API 对象。你需要在 config 参数中传入如下字段:
applicationContext:传入 Android Context(上下文)。mRtcEngine:传入之前初始化的RtcEngine对象。fuRenderKit:传入之前初始化的FuRenderKit对象。captureMode:视频的采集模式:- 如果你使用声网模块采集视频,请传入 
CaptureMode.Agora。 - 如果自定义采集视频,请传入 
CaptureMode.Custom。 
- 如果你使用声网模块采集视频,请传入 
 cameraConfig:设置视频镜像模式。如果在初始化 Beauty API 后你想修改镜像模式,可以调用 Beauty API 的updateCameraConfig。statsEnable:是否开启美颜统计数据回调。true代表开启,false代表不开启。开启后,会有周期性的onBeautyStats回调事件。statsDuration:美颜统计数据回调的周期。单位为毫秒。eventCallback:你希望监听的回调事件。
// 初始化 Beauty API 对象
mFaceUnityApi.initialize(
    Config(
        // Android Context(上下文)
        applicationContext,
        // RtcEngine
        mRtcEngine,
        // 美颜特效管理器
        fuRenderKit,
        // 设置视频采集模式
        // CaptureMode.Agora 意味着使用声网模块采集视频
        // CaptureMode.Custom 意味着使用开发者自定义采集视频
        captureMode = if(isCustomCaptureMode) CaptureMode.Custom else CaptureMode.Agora,
        // 配置视频镜像模式
        cameraConfig = this.cameraConfig,
        // 是否开启美颜统计数据
        // 开启后,会有周期性的 onBeautyStats 回调事件
        statsEnable = true,
        // 设置美颜统计数据的统计区间为 1000 毫秒(默认)
        statsDuration = 1000,
        // 用于监听 Beauty API 的回调事件
        eventCallback = object: IEventCallback{
            override fun onBeautyStats(stats: BeautyStats) {
                Log.d(TAG, "BeautyStats stats = $stats")
            }
        }
    )
)
4. 开启美颜
调用 Beauty API 的 enable 方法并将参数设为 true 开启美颜。
mFaceUnityApi.enable(true)
5. 开启视频采集
开发者可以使用声网模块采集视频,也可以自定义采集视频。本节介绍在这两种场景下如何开启视频采集。
使用声网模块采集视频
使用声网模块采集视频时,你需要先调用 RtcEngine 类的 enableVideo 开启声网 RTC SDK 的视频模块,然后调用 Beauty API 的 setupLocalVideo 开启本地视图。
// 开启视频模块
mRtcEngine.enableVideo()
// 设置本地视图
mFaceUnityApi.setupLocalVideo(mBinding.localVideoView, Constants.RENDER_MODE_FIT)
自定义视频采集
使用自定义视频采集时,你需要进行如下操作:
- 调用 
RtcEngine类的enableVideo开启声网 RTC SDK 的视频模块。 - 开启自定义视频采集。可以使用声网 RTC SDK 的 API 开启视频自采集,也可以使用第三方视频自采集。如下示例代码展示通过声网 RTC SDK 的 API 进行的自定义视频采集,具体逻辑为通过 
RtcEngine类的registerVideoFrameObserver注册原始视频数据观测器并在其中实现onCaptureVideoFrame函数。 - 完成视频自采集后,通过 Beauty API 的 
onFrame函数将外部自采集的视频数据包装成VideoFrame传入并进行处理。当处理结果不为SKIPPED(忽略)时,VideoFrame里的视频 Buffer 数据会被替换成美颜处理过后的数据,你可以取出处理后的 Buffer 数据传入声网 RTC SDK。 
// 开启视频模块
mRtcEngine.enableVideo()
// 注册原始视频数据观测器
// 自定义视频采集时,即 CaptureMode 为 Custom 时,你需要注册原始视频观测器
mRtcEngine.registerVideoFrameObserver(object : IVideoFrameObserver {
    override fun onCaptureVideoFrame(
        sourceType: Int,
        videoFrame: VideoFrame?
    ) = when (mFaceUnityApi.onFrame(videoFrame!!)) {
        // 当处理结果为 SKIPPED(忽略)时,代表你丢帧,即外部自采集的视频数据不传入声网 RTC SDK
        // 当处理结果为其他时,外部自采集的视频数据传入声网 RTC SDK
        ErrorCode.ERROR_FRAME_SKIPPED.value -> false
        else -> true
    }
    // 设置是否对原始视频数据作镜像处理
    override fun getMirrorApplied() = mFaceUnityApi.getMirrorApplied()
    // 设置观测点为本地采集时的视频数据
    override fun getObservedFramePosition() = IVideoFrameObserver.POSITION_POST_CAPTURER
    // override 视频观测器中的其他回调函数
    ...
})
6. 加入频道
调用 RtcEngine 类的 joinChannel 加入频道,同时传入如下参数:
token:用于鉴权的动态密钥。如果在创建声网项目时启用调试模式,那么将token参数传空。如果启用安全模式,那么你先参考使用 Token 鉴权在你的业务服务端生成 Token,然后将生成的 Token 传入该参数。channelId:频道名。options:频道媒体设置选项。
mRtcEngine.joinChannel(null, mChannelName, 0, ChannelMediaOptions().apply {
    // 设置频道场景为直播
    channelProfile = Constants.CHANNEL_PROFILE_LIVE_BROADCASTING
    // 设置用户角色为主播,主播可以在频道里发布和订阅音视频流
    clientRoleType = Constants.CLIENT_ROLE_BROADCASTER
    // 设置是否发布摄像头采集的视频流(适用于使用声网模块采集视频的情况)
    publishCameraTrack = true
    // 设置是否发布自定义采集的视频流(适用于自定义采集视频的情况)
    publishCustomVideoTrack = false
    // 设置是否发布麦克风采集的音频流
    publishMicrophoneTrack = false
    // 设置进入频道时是否自动订阅频道内其他用户的音频流
    autoSubscribeAudio = false
    // 设置进入频道时是否自动订阅频道内其他用户的视频流
    autoSubscribeVideo = true
})
7.(可选)更新摄像头配置
Beauty API 默认的镜像行为如下:
- 使用设备前置摄像头时,本地和远端视图都使用镜像。
 - 使用设别后置摄像头时,本地和远端视图都不使用镜像。
 
如果默认的摄像头配置不满足你的业务需求,你可以参考本节更新摄像头配置。具体步骤如下:
调用 Beauty API 的 updateCameraConfig 并通过 cameraConfig 配置前置和后置摄像头的镜像效果。所有镜像类型包含如下:
MIRROR_LOCAL_REMOTE:本地和远端视图都使用镜像。前置摄像头默认使用该配置。MIRROR_LOCAL_ONLY:本地视图使用镜像,远端视图不使用镜像。建议在直播画面中会出现告示牌或者文字的情况下使用该配置。MIRROR_REMOTE_ONLY:本地视图不使用镜像,远端视图使用镜像。MIRROR_NONE:本地和远端视图都不使用镜像。后置摄像头默认使用该配置。
val isFront = mFaceUnityApi.isFrontCamera()
// 判断 isFrontCamera
if (isFront) {
    cameraConfig = CameraConfig(
        // 通过 frontMirror 配置前置摄像头
        frontMirror = when (cameraConfig.frontMirror) {
            MirrorMode.MIRROR_LOCAL_REMOTE -> MirrorMode.MIRROR_LOCAL_ONLY
            MirrorMode.MIRROR_LOCAL_ONLY -> MirrorMode.MIRROR_REMOTE_ONLY
            MirrorMode.MIRROR_REMOTE_ONLY -> MirrorMode.MIRROR_NONE
            MirrorMode.MIRROR_NONE -> MirrorMode.MIRROR_LOCAL_REMOTE
        },
        backMirror = cameraConfig.backMirror
    )
} else {
    cameraConfig = CameraConfig(
        frontMirror = cameraConfig.frontMirror,
        // 通过 backMirror 配置后置摄像头
        backMirror = when (cameraConfig.backMirror) {
            MirrorMode.MIRROR_NONE -> MirrorMode.MIRROR_LOCAL_REMOTE
            MirrorMode.MIRROR_LOCAL_REMOTE -> MirrorMode.MIRROR_LOCAL_ONLY
            MirrorMode.MIRROR_LOCAL_ONLY -> MirrorMode.MIRROR_REMOTE_ONLY
            MirrorMode.MIRROR_REMOTE_ONLY -> MirrorMode.MIRROR_NONE
        }
    )
}
// 更新摄像头配置
mFaceUnityApi.updateCameraConfig(cameraConfig)
8. 离开频道
调用 RtcEngine 类的 leaveChannel 离开频道。
mRtcEngine.leaveChannel()
9. 销毁资源
调用 Beauty API 的 release 销毁 Beauty API。
mFaceUnityApi.release()
调用美颜 SDK 的 destroy 销毁 FURenderKit。
FURenderKit.destroy()
调用 RtcEngine 的 destroy 销毁 RtcEngine。
RtcEngine.destroy()