# 实现字节火山美颜 (/zh-CN/solutions/showroom/build/beauty/bytedance-integrate)

> For AI agents: see the complete documentation index at [llms.txt](/llms.txt).

<_PlatformTabsGroup>
<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="android" platform="android" />
## 示例项目 [#示例项目]

本文介绍如何通过声网美颜场景化 API (Beauty API) 集成字节火山美颜功能到实时音视频互动中。声网在 GitHub 上提供开源 `BeautyAPI` 示例项目供你参考。

<Cards>
  <Card title="BeautyAPI" href="https://github.com/AgoraIO-Community/BeautyAPI" />
</Cards>

## 准备开发环境 [#准备开发环境]

### 前提条件 [#前提条件]

开始前，请确保满足以下前提条件：

- [Git](https://git-scm.com/downloads)
- [Java Development Kit](https://www.oracle.com/java/technologies/javase-downloads.html) 11
- [Android Studio](https://developer.android.com/studio/) 3.5 及以上
- Android 手机，版本 Android 5.0（API Level 21）及以上
- [开通秀场直播服务](/zh-CN/solutions/showroom/build/enable-service)
- 已联系字节技术获取最新的美颜 SDK、美颜资源、美颜证书
- 已在项目中添加 Kotlin 插件

### 创建项目并集成 SDK [#创建项目并集成-sdk]

按照如下步骤准备开发环境：

1. 如需创建新项目，在 **Android Studio** 里，依次选择 **Phone and Tablet > Empty Activity**，创建 [Android 项目](https://developer.android.com/studio/projects/create-project)。

   :::warning[注意]
   创建项目后，Android Studio 会自动开始同步 Gradle，稍等片刻，待同步成功后再进行下一步操作。
   :::

2. 使用 Maven Central 将声网 RTC SDK 集成到你的项目中。

   1. 在 `/Gradle Scripts/build.gradle(Project: <projectname>)` 文件中添加如下代码，添加 Maven Central 依赖：

        ```kotlin
        buildscript {
            repositories {
                ...
                mavenCentral()
            }
            ...
        }
        allprojects {
            repositories {
                ...
                mavenCentral()
            }
        }
        ```

        :::warning[注意]
        如果你的 Android 项目设置了 [dependencyResolutionManagement](https://docs.gradle.org/current/userguide/centralizing_repositories.html)，添加 Maven Central 依赖的方式可能存在差异。
        :::

   2. 在 `/Gradle Scripts/build.gradle(Module: <projectname>.app)` 文件中添加如下代码，将声网 RTC SDK 集成到你的 Android 项目中：

      ```kotlin
      ...
      dependencies {
          ...
          // x.y.z，请填写具体的 SDK 版本号，如：4.0.0 或 4.1.0-1
          // 通过实时互动产品发版说明获取最新版本号
          implementation 'io.agora.rtc:full-sdk:x.y.z'
      }
      ```

3. 将字节火山美颜 SDK 复制到你的项目中：请联系字节技术支持获取美颜 SDK、美颜资源、证书等文件。下载并解压文件，然后添加到美颜项目对应的文件路径下：

    <DocsTable>
    <thead>
    <tr>
    <th scope="col">文件</th>
    <th scope="col">项目路径</th>
    </tr>
    </thead>
    <tbody>
    <tr>
    <td>`resource/LicenseBag.bundle`</td>
    <td rowSpan={4}>`app/src/main/assets/beauty_bytedance`</td>
    </tr>
    <tr>
    <td>`resource/ModelResource.bundle`</td>
    </tr>
    <tr>
    <td>`resource/ComposeMakeup.bundle`</td>
    </tr>
    <tr>
    <td>`resource/StickerResource.bundle`</td>
    </tr>
    </tbody>
    </DocsTable>

4. 将声网美颜场景化 API 集成到你的项目中。添加 [Android/lib_bytedance/src/main/java/io/agora/beautyapi/bytedance](https://github.com/AgoraIO-Community/BeautyAPI/tree/main/Android/lib_bytedance/src/main/java/io/agora/beautyapi/bytedance) 目录下的文件到项目中，具体文件如下：
    - `utils` 文件夹
    - `ByteDanceBeautyAPI.kt` 文件
    - `ByteDanceBeautyAPIImpl.kt` 文件

    :::warning[注意]
    为方便后续代码升级，请不要修改你添加的这些文件的名称和路径。
    :::

5. 添加网络及设备权限。

   在 `/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"/>
   ```

6. 在 `/Gradle Scripts/proguard-rules.pro` 文件中添加如下行，以防止声网 RTC SDK 的代码被混淆：

   ```kotlin
   -keep class io.agora.**{*;}
   -dontwarn javax.**
   -dontwarn com.google.devtools.build.android.**
   ```

## 实现美颜 [#实现美颜]

本节展示如何在直播间内实现美颜功能，参考 [API 时序图](#api-时序图)可查看总览。声网 RTC SDK 承担实时音视频的业务，字节火山美颜 SDK 提供美颜功能，声网 Beauty API 封装了两个 SDK 中的 API 调用逻辑以简化你需要实现的代码逻辑。通过 Beauty API，你可以实现基础美颜功能，但是如果你还需要更丰富的美颜效果，例如贴纸、美妆风格，请直接调用美颜 SDK 中的 API。

### 1. 初始化 RtcEngine [#1-初始化-rtcengine]

调用声网 RTC SDK 中的 `create` 创建并初始化 `RtcEngine` 对象。

```kotlin
// 初始化声网 RtcEngine
private val mRtcEngine by lazy {
    RtcEngine.create(RtcEngineConfig().apply {
        mContext = applicationContext
        // 传入你从控制台获取的声网项目的 APP ID
        mAppId = BuildConfig.AGORA_APP_ID
        mEventHandler = object : IRtcEngineEventHandler() {}
    })
}
```

### 2. 初始化 RenderManager [#2-初始化-rendermanager]

复制美颜证书、美颜资源到 `sdcard` 存储中。

```kotlin
// 初始化美颜 SDK
fun initBeautySDK(context: Context){
    val storagePath = context.getExternalFilesDir("")?.absolutePath ?: return
    val assetsPath = "beauty_bytedance"

    workerThread.execute {
        // copy license
        licensePath = "$storagePath/beauty_bytedance/LicenseBag.bundle/$LICENSE_NAME"
        FileUtils.copyAssets(context, "$assetsPath/LicenseBag.bundle/$LICENSE_NAME", licensePath)

        // copy models
        modelsPath = "$storagePath/beauty_bytedance/ModelResource.bundle"
        FileUtils.copyAssets(context, "$assetsPath/ModelResource.bundle", modelsPath)

        // copy beauty node
        beautyNodePath = "$storagePath/beauty_bytedance/ComposeMakeup.bundle/ComposeMakeup/beauty_Android_lite"
        FileUtils.copyAssets(context, "$assetsPath/ComposeMakeup.bundle/ComposeMakeup/beauty_Android_lite", beautyNodePath)

        // copy beauty 4items node
        beauty4ItemsNodePath = "$storagePath/beauty_bytedance/ComposeMakeup.bundle/ComposeMakeup/beauty_4Items"
        FileUtils.copyAssets(context, "$assetsPath/ComposeMakeup.bundle/ComposeMakeup/beauty_4Items", beauty4ItemsNodePath)

        // copy resharp node
        reSharpNodePath = "$storagePath/beauty_bytedance/ComposeMakeup.bundle/ComposeMakeup/reshape_lite"
        FileUtils.copyAssets(context, "$assetsPath/ComposeMakeup.bundle/ComposeMakeup/reshape_lite", reSharpNodePath)

        // copy makeup node
        makeupTianmeiNodePath = "$storagePath/beauty_bytedance/ComposeMakeup.bundle/ComposeMakeup/style_makeup/tianmei"
        FileUtils.copyAssets(context, "$assetsPath/ComposeMakeup.bundle/ComposeMakeup/style_makeup/tianmei", makeupTianmeiNodePath)

        // copy stickers
        stickerPath = "$storagePath/beauty_bytedance/StickerResource.bundle/stickers"
        FileUtils.copyAssets(context, "$assetsPath/StickerResource.bundle/stickers", stickerPath)
    }
}
```

在 GL 线程 (Graphics Library Thread) 中通过美颜 SDK 的 `initEffect` 方法初始化 `RenderManager` 实例。

```kotlin
// GL 线程中
// 初始化美颜 SDK 中的 RenderManager 实例
fun initEffect(context: Context){
    val ret = renderManager.init(
        context,
        modelsPath, licensePath, false, false, 0
    )
    if(!checkResult("RenderManager init ", ret)){
        return
    }
    renderManager.useBuiltinSensor(true)
    renderManager.set3Buffer(false)
    renderManager.appendComposerNodes(arrayOf(beautyNodePath, beauty4ItemsNodePath, reSharpNodePath))
    renderManager.loadResourceWithTimeout(-1)
}
```

### 3. 初始化 Beauty API [#3-初始化-beauty-api]

调用 `createByteDanceBeautyAPI` 创建 Beauty API 对象。Beauty API 对象基于 `RenderManager` 对象封装。

```kotlin
// 创建 Beauty API 对象
private val mByteDanceApi by lazy {
    createByteDanceBeautyAPI()
}
```

调用 `initialize` 初始化 Beauty API 对象。你需要在 `config` 参数中传入如下字段：

- `applicationContext`：传入 Android Context（上下文）。
- `mRtcEngine`：传入之前初始化的 `RtcEngine` 对象。
- `renderManager`：传入之前初始化的 `RenderManager` 对象。
- `captureMode`：视频的采集模式：
    - 如果你使用声网模块采集视频，请传入 `CaptureMode.Agora`。
    - 如果自定义采集视频，请传入 `CaptureMode.Custom`。
- `statsEnable`：是否开启美颜统计数据回调。`true` 代表开启，`false` 代表不开启。开启后，会有周期性的 `onBeautyStats` 回调事件。
- `statsDuration`：美颜统计数据回调的周期。单位为毫秒。
- `cameraConfig`：设置视频镜像模式。如果在初始化 Beauty API 后你想修改镜像模式，可以调用 Beauty API 的 `updateCameraConfig`。
- `eventCallback`：你希望监听的回调事件。

```kotlin
// 初始化 Beauty API 对象
mByteDanceApi.initialize(
    Config(
        // Android Context（上下文）
        applicationContext,
        // RtcEngine
        mRtcEngine,
        // 美颜特效管理器
        renderManager,
        // 设置视频采集模式
        // CaptureMode.Agora 意味着使用声网模块采集视频
        // CaptureMode.Custom 意味着使用开发者自定义采集视频
        captureMode = if (isCustomCaptureMode) CaptureMode.Custom else
        CaptureMode.Agora,
        // 是否开启美颜统计数据
        // 开启后，会有周期性的 onBeautyStats 回调事件
        statsEnable = true,
        // 设置美颜统计数据的统计区间为 1000 毫秒（默认）
        statsDuration = 1000,
        // 配置视频镜像模式
        cameraConfig = cameraConfig,
        // 用于监听 Beauty API 的回调事件
        eventCallback = EventCallback(
            // 美颜统计数据回调
            onBeautyStats = {stats ->
                Log.d(TAG, "BeautyStats stats = $stats")
            },
            // GL 线程中
            // 美颜特效初始化完成回调
            onEffectInitialized = {
                ByteDanceBeautySDK.initEffect(applicationContext)
                Log.d(TAG, "onEffectInitialized")
            },
            // GL 线程中
            // 美颜特效销毁回调
            onEffectDestroyed = {
                ByteDanceBeautySDK.unInitEffect()
                Log.d(TAG, "onEffectInitialized")
            }
        )
    ))
```

### 4. 开启美颜 [#4-开启美颜]

调用 Beauty API 的 `enable` 方法并将参数设为 `true` 开启美颜。

```kotlin
mByteDanceApi.enable(true)
```

### 5. 开启视频采集 [#5-开启视频采集]

开发者可以使用声网模块采集视频，也可以自定义采集视频。本节介绍在这两种场景下如何开启视频采集。

#### 使用声网模块采集视频 [#使用声网模块采集视频]

使用声网模块采集视频时，你需要先调用 `RtcEngine` 类的 `enableVideo` 开启声网 RTC SDK 的视频模块，然后调用 Beauty API 的 `setupLocalVideo` 开启本地视图。

```kotlin
// 开启视频模块
mRtcEngine.enableVideo()
// 设置本地视图
mByteDanceApi.setupLocalVideo(mBinding.localVideoView, Constants.RENDER_MODE_FIT)
```

#### 自定义视频采集 [#自定义视频采集]

使用自定义视频采集时，你需要进行如下操作：

1. 调用 `RtcEngine` 类的 `enableVideo` 开启声网 RTC SDK 的视频模块。
2. 开启自定义视频采集。可以使用声网 RTC SDK 的 API 开启视频自采集，也可以使用第三方视频自采集。如下示例代码展示通过声网 RTC SDK 的 API 进行的自定义视频采集，具体逻辑为通过 `RtcEngine` 类的 `registerVideoFrameObserver` 注册原始视频数据观测器并在其中实现 `onCaptureVideoFrame` 函数。
3. 完成视频自采集后，通过 Beauty API 的 `onFrame` 函数将外部自采集的视频数据包装成 `VideoFrame` 传入并进行处理。当处理结果不为 `SKIPPED`（忽略）时，`VideoFrame` 里的视频 Buffer 数据会被替换成美颜处理过后的数据，你可以取出处理后的 Buffer 数据传入声网 RTC SDK。

```kotlin
// 开启视频模块
mRtcEngine.enableVideo()
// 注册原始视频数据观测器
// 自定义视频采集时，即 CaptureMode 为 Custom 时，你需要注册原始视频观测器
mRtcEngine.registerVideoFrameObserver(object : IVideoFrameObserver {

    override fun onCaptureVideoFrame(
        sourceType: Int,
        videoFrame: VideoFrame?
        ) = when (mByteDanceApi.onFrame(videoFrame!!)) {
            // 当处理结果为 SKIPPED（忽略）时，代表你丢帧，即外部自采集的视频数据不传入声网 RTC SDK
            // 当处理结果为其他时，外部自采集的视频数据传入声网 RTC SDK
            ErrorCode.ERROR_FRAME_SKIPPED.value -> false
            else -> true
        }

    // 设置是否对原始视频数据作镜像处理
    override fun getMirrorApplied() = mByteDanceApi.getMirrorApplied()

    // 设置观测点为本地采集时的视频数据
    override fun getObservedFramePosition() = IVideoFrameObserver.POSITION_POST_CAPTURER

    // override 视频观测器中的其他回调函数
    ...
})
```

### 6. 加入频道 [#6-加入频道]

调用 `RtcEngine` 类的 `joinChannel` 加入频道，同时传入如下参数：

- `token`：用于鉴权的动态密钥。如果在[创建声网项目](/zh-CN/solutions/showroom/build/enable-service#创建声网项目)时启用**调试模式**，那么将 `token` 参数传空。如果启用**安全模式**，那么你先参考[使用 Token 鉴权](/zh-CN/realtime-media/rtc/build/setup-and-access/token-authentication)在你的业务服务端生成 Token，然后将生成的 Token 传入该参数。
- `channelId`：频道名。
- `options`：频道媒体设置选项。

```kotlin
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.（可选）更新摄像头配置 [#7可选更新摄像头配置]

Beauty API 默认的镜像行为如下：

- 使用设备前置摄像头时，本地和远端视图都使用镜像。
- 使用设备后置摄像头时，本地和远端视图都不使用镜像。

如果默认的摄像头配置不满足你的业务需求，你可以参考本节更新摄像头配置。具体步骤如下：

调用 Beauty API 的 `updateCameraConfig` 并通过 `cameraConfig` 配置前置和后置摄像头的镜像效果。所有镜像类型包含如下：

- `MIRROR_LOCAL_REMOTE`：本地和远端视图都使用镜像。前置摄像头默认使用该配置。
- `MIRROR_LOCAL_ONLY`：本地视图使用镜像，远端视图不使用镜像。建议在直播画面中会出现告示牌或者文字的情况下使用该配置。
- `MIRROR_REMOTE_ONLY`：本地视图不使用镜像，远端视图使用镜像。
- `MIRROR_NONE`：本地和远端视图都不使用镜像。后置摄像头默认使用该配置。

```kotlin
val isFront = mByteDanceApi.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
        }
    )
}
// 更新摄像头配置
mByteDanceApi.updateCameraConfig(cameraConfig)
```

### 8. 离开频道 [#8-离开频道]

调用 `RtcEngine` 类的 `leaveChannel` 离开频道。

```kotlin
mRtcEngine.leaveChannel()
```

### 9. 销毁资源 [#9-销毁资源]

调用 Beauty API 的 `release` 销毁 Beauty API。

```kotlin
mByteDanceApi.release()
```

在 GL 线程 (Graphics Library Thread) 中调用美颜 SDK 的 `unInitEffect` 销毁 `RenderManager`。

```kotlin
fun unInitEffect(){
    renderManager.release()
}
```

调用 `RtcEngine` 的 `destroy` 销毁 `RtcEngine`。

```kotlin
RtcEngine.destroy()
```

### API 时序图 [#api-时序图]

![美颜集成的 API 时序图](https://web-cdn.agora.io/docs-portal/img/showroom/bd-and.svg)
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="android" platform="ios" />
本文介绍如何通过声网美颜场景化 API (Beauty API) 集成字节火山美颜功能到实时音视频互动中。
## 示例项目 [#示例项目-1]

声网在 GitHub 上提供开源 `BeautyAPI` 示例项目供你参考。

<Cards>
  <Card title="BeautyAPI" href="https://github.com/AgoraIO-Community/BeautyAPI" />
</Cards>

## 准备开发环境 [#准备开发环境-1]

### 前提条件 [#前提条件-1]

开始前，请确保满足以下前提条件：

- [Git](https://git-scm.com/downloads)
- [CocoaPods](https://guides.cocoapods.org/using/getting-started.html#getting-started)
- [Xcode](https://apps.apple.com/cn/app/xcode/id497799835?mt=12) 12.0 及以上
- iOS 设备，版本 13.0 及以上
- 有效的苹果开发者账号
- [开通秀场直播服务](/zh-CN/solutions/showroom/build/enable-service)
- 已联系字节火山技术获取最新的美颜 SDK、美颜资源、美颜证书

### 创建项目并集成 SDK [#创建项目并集成-sdk-1]

在 Xcode 中进行以下操作，在你的 App 中实现场景化美颜功能：

1. [创建一个新的项目](https://help.apple.com/xcode/mac/current/#/dev07db0e578)，**Application** 选择 **App**，**Interface** 选择 **Storyboard**，**Language** 选择 **Swift**。

    :::warning[注意]
    如果你没有添加过开发团队信息，会看到 **Add account…** 按钮。点击该按钮并按照屏幕提示登入 Apple ID，点击 **Next**，完成后即可选择你的 Apple 账户作为开发团队。
    :::

2. 为你的项目设置[自动签名](https://help.apple.com/xcode/mac/current/#/dev23aab79b4)。

3. 设置部署你的 App 的[目标设备](https://help.apple.com/xcode/mac/current/#/deve69552ee5)。

4. 添加项目的设备权限。在项目导航栏中打开 `info.plist` 文件，编辑[属性列表](https://help.apple.com/xcode/mac/current/#/dev3f399a2a6)，添加以下属性：

    | key                                    | type   | value                                                        |
    | -------------------------------------- | ------ | ------------------------------------------------------------ |
    | Privacy - Microphone Usage Description | String | 使用麦克风的目的，例如 for a live interactive streaming |
    | Privacy - Camera Usage Description       | String | 使用摄像头的目的，例如 for a live interactive streaming |

    :::warning[注意]
    - 如果你的项目中需要添加第三方插件或库（例如第三方摄像头），且该插件或库的签名与项目的签名不一致，你还需勾选 **Hardened Runtime > Runtime Exceptions** 中的 **Disable Library Validation**。
    - 更多注意事项，可以参考 [Preparing Your App for Distribution](https://developer.apple.com/documentation/xcode/preparing_your_app_for_distribution)。
    :::

5. 将字节火山美颜 SDK 复制到你的项目中：请联系字节火山技术支持获取美颜 SDK、美颜资源、证书等文件。下载并解压文件，然后添加到美颜项目对应的文件路径下：

    | 文件 | 项目路径 |
    | --- | --- |
    | `byted_effect_ios_static/iossample_static/app/Resource/*.bundle` | `iOS/ByteEffectLib/Resource/*.bundle` |
    | `byted_effect_ios_static/iossample_static/effect-sdk.framework` | `iOS/ByteEffectLib/effect-sdk.framework` |
    | `byted_effect_ios_static/libeffect-sdk.a` | `iOS/ByteEffectLib/ibeffect-sdk.a` |
    | 证书：`LicenseBag.bundle/<prefix>_<your_bundle_id>_<version_id>.licbag` | `iOS/ByteEffectLib/Resource/LicenseBag.bundle/<prefix>_<your_bundle_id>_<version_id>.licbag` |

6. 将声网美颜场景化 API 集成到你的项目中。添加 [iOS/BeautyAPI/BeautyAPI](https://github.com/AgoraIO-Community/BeautyAPI/tree/main/iOS/BeautyAPI/BeautyAPI) 目录下的文件到项目中，具体文件如下：

    - `Render/BytesRender` 文件夹
    - `BeautyAPI.h` 文件
    - `BeautyAPI.m` 文件

    :::warning[注意]
    为方便后续代码升级，请不要修改你添加的这些文件的名称和路径。
    :::

7. 将声网 RTC SDK 和字节火山美颜依赖库集成到你的项目。

    1. 在终端里进入项目根目录，并运行 `pod init` 命令。项目文件夹下会生成一个 `Podfile` 文本文件。
    2. 打开 `Podfile` 文件，修改文件为如下内容。注意将 `Your App` 替换为你的 Target 名称。

    ```ruby
    platform :ios, '9.0'
    # 替换成你的 target 名称
    target 'Your App' do
        # x.y.z 请填写具体的 RTC SDK 版本号，如 4.0.1 或 4.0.0.4
        # 可通过实时互动发版说明获取最新版本号
        pod 'AgoraRtcEngine_iOS', 'x.y.z'
        # 配置字节火山美颜的依赖库
        pod 'bytedEffect', :path => 'bytedEffect.podspec'
    end
    ```

8. 在终端内运行 `pod install` 命令安装声网 RTC SDK 和字节火山美颜依赖。

9. 成功安装后，Terminal 中会显示 `Pod installation complete!`。项目文件夹下会生成一个后缀为 `.xcworkspace` 的文件，通过 Xcode 打开该文件进行后续操作。

## 实现美颜 [#实现美颜-1]

本节展示如何在直播间内实现美颜功能，参考 [API 时序图](#api-时序图-1)可查看总览。声网 RTC SDK 承担实时音视频的业务，字节火山美颜 SDK 提供美颜功能，声网 Beauty API 封装了两个 SDK 中的 API 调用逻辑以简化你需要实现的代码逻辑。通过 Beauty API，你可以实现基础美颜功能，但是如果你还需要更丰富的美颜效果，例如贴纸、美妆风格，请直接调用美颜 SDK 中的 API。

### 1. 初始化 AgoraRtcEngineKit [#1-初始化-agorartcenginekit]

调用声网 RTC SDK 中的 `sharedEngineWithConfig` 创建并初始化 `AgoraRtcEngineKit` 对象。调用 `enableVideo` 开启声网 RTC SDK 的视频模块。

```swift
// 初始化 AgoraRtcEngineKit
private lazy var rtcEngine: AgoraRtcEngineKit = {
    let config = AgoraRtcEngineConfig()
    // 传入你从控制台获取的声网项目的 APP ID
    config.appId = KeyCenter.AppId
    config.channelProfile = .liveBroadcasting
    let rtc = AgoraRtcEngineKit.sharedEngine(with: config, delegate: self)
    // 设置用户角色为主播，主播可以发送音视频流也可以接收音视频流
    rtc.setClientRole(.broadcaster)
    // 开启 SDK 音频模块
    rtc.enableAudio()
    // 开启 SDK 视频模块
    rtc.enableVideo()
    // 设置默认音频路由为扬声器
    rtc.setDefaultAudioRouteToSpeakerphone(true)
    return rtc
}()
```

### 2. 初始化美颜和 Beauty API [#2-初始化美颜和-beauty-api]

创建 `BytesBeautyRender` 和 Beauty API 对象。Beauty API 对象基于 `BytesBeautyRender` 对象封装。

```swift
private lazy var bytesRender = BytesBeautyRender()
private lazy var beautyAPI = BeautyAPI()
```

调用 `initialize` 初始化 Beauty API 对象。你需要在 `config` 参数中传入如下字段：

- `rtcEngine`：传入之前初始化的 `AgoraRtcEngineKit` 对象。
- `beautyRender`：传入之前初始化的 `BytesBeautyRender` 对象。
- `captureMode`：视频的采集模式：
    - 如果你使用声网模块采集视频，请传入 `CaptureMode.Agora`。
    - 如果自定义采集视频，请传入 `CaptureMode.Custom`。
- `cameraConfig`：设置视频镜像模式。如果在初始化 Beauty API 后你想修改镜像模式，可以调用 Beauty API 的 `updateCameraConfig`。
- `statsEnable`：是否开启美颜统计数据回调。`true` 代表开启，`false` 代表不开启。开启后，会有周期性回调事件。
- `statsDuration`：美颜统计数据回调的周期。单位为秒。
- `eventCallback`：监听的美颜统计数据回调事件。

```swift
let config = BeautyConfig()
// AgoraRtcEngineKit
config.rtcEngine = rtcEngine
// 设置视频采集模式
// .agora 意味着使用声网模块采集视频
// .custom 意味着使用开发者自定义采集视频
config.captureMode = capture == "Custom" ? .custom : .agora
// 配置视频镜像模式
config.cameraConfig = cameraConfig
// BytesBeautyRender
config.beautyRender = bytesRender
// 是否开启美颜统计数据
// 开启后，会有周期性回调事件
config.statsEnable = false
// 设置美颜统计数据的统计区间为 1 秒（默认）
config.statsDuration = 1
// Beauty API 的回调事件
config.eventCallback = { stats in
    print("min == \(stats.minCostMs)")
    print("max == \(stats.maxCostMs)")
    print("averageCostMs == \(stats.averageCostMs)")
}

// 初始化 Beauty API 对象
let result = beautyAPI.initialize(config)
if result != 0 {
    print("initialize error == \(result)")
}
```

### 3. 开启美颜 [#3-开启美颜]

调用 Beauty API 的 `enable` 方法并将参数设为 `true` 开启美颜。

```swift
beautyAPI.enable(true)
```

### 4. 开启视频采集 [#4-开启视频采集]

开发者可以使用声网模块采集视频，也可以自定义采集视频。本节介绍在这两种场景下如何开启视频采集。

#### 使用声网模块采集视频 [#使用声网模块采集视频-1]

使用声网模块采集视频时，调用 Beauty API 的 `setupLocalVideo` 开启本地视图。

```swift
beautyAPI.setupLocalVideo(localView, renderMode: .hidden)
```

#### 自定义视频采集 [#自定义视频采集-1]

使用自定义视频采集时，你需要在完成视频自采集后，通过 Beauty API 的 `onFrame` 函数将外部自采集的视频数据传入并进行处理。

如下示例代码展示通过声网 RTC SDK 的 API 进行的自定义视频采集，具体逻辑为通过 `AgoraRtcEngineKit` 类的 `setVideoFrameDelegate` 注册原始视频数据观测器并在其中实现 `onCaptureVideoFrame` 函数。你可以根据实际情况参考这种自采集方式，或使用第三方视频自采集。

```swift
if capture == "Custom" {
    // 注册原始视频数据观测器
    // 自定义视频采集时，即 CaptureMode 为 Custom 时，你需要注册原始视频观测器
    rtcEngine.setVideoFrameDelegate(self)
}

extension BeautyViewController: AgoraVideoFrameDelegate {
    func onCapture(_ videoFrame: AgoraOutputVideoFrame, sourceType: AgoraVideoSourceType) -> Bool {
        // 将外部自采集的视频数据传入声网 RTC SDK
        guard let pixelBuffer = videoFrame.pixelBuffer else { return true }
        beautyAPI.onFrame(pixelBuffer) { pixelBuffer in
            videoFrame.pixelBuffer = pixelBuffer
        }

        return true
    }

    // 设置是否对原始视频数据作镜像处理
    func getMirrorApplied() -> Bool {
        beautyAPI.getMirrorApplied()
    }

    // 设置观测点为本地采集时的视频数据
    func getObservedFramePosition() -> AgoraVideoFramePosition {
        .postCapture
    }

    // 实现视频观测器中的其他回调函数
    ...
}
```

### 5. 加入频道 [#5-加入频道]

调用 `AgoraRtcEngineKit` 类的 `joinChannelByToken` 加入频道，同时传入如下参数：

- `token`：用于鉴权的动态密钥。如果在[创建声网项目](/zh-CN/solutions/showroom/build/enable-service#创建声网项目)时启用**调试模式**，那么将 `token` 参数传空。如果启用**安全模式**，那么你先参考[使用 Token 鉴权](/zh-CN/realtime-media/rtc/build/setup-and-access/token-authentication)在你的业务服务端生成 Token，然后将生成的 Token 传入该参数。
- `channelId`：频道名。
- `mediaOptions`：频道媒体设置选项。

```swift
let mediaOption = AgoraRtcChannelMediaOptions()
mediaOption.clientRoleType = isBroadcast ? .broadcaster : .audience
// 设置进入频道时是否自动订阅频道内其他用户的音频流
mediaOption.autoSubscribeAudio = true
// 设置进入频道时是否自动订阅频道内其他用户的视频流
mediaOption.autoSubscribeVideo = true
// 设置是否发布摄像头采集的视频流（适用于使用声网模块采集视频的情况）
// 用户角色为主播时，设置发布
// 用户角色为观众时，设置不发布
mediaOption.publishCameraTrack = mediaOption.clientRoleType == .broadcaster
// 设置是否发布自定义采集的视频流（适用于自定义采集视频的情况）
mediaOption.publishCustomVideoTrack = false
// 设置是否发布麦克风采集的音频流
// 用户角色为主播时，设置发布
// 用户角色为观众时，设置不发布
mediaOption.publishMicrophoneTrack = mediaOption.clientRoleType == .broadcaster

// 加入频道
let result = rtcEngine.joinChannel(byToken: nil, channelId: channelName ?? "", uid: 0, mediaOptions: mediaOption)
if result != 0 {
    print("join failed")
}
```

### 6.（可选）更新摄像头配置 [#6可选更新摄像头配置]

Beauty API 默认用户使用设备前置摄像头参与直播。默认的镜像行为如下：

- 使用设备前置摄像头时，本地和远端视图都使用镜像。
- 使用设备后置摄像头时，本地和远端视图都不使用镜像。

如果默认的摄像头配置不满足你的业务需求，你可以参考本节更新摄像头配置。具体步骤如下：

1. 如果当前使用的摄像头不是前置，那么调用 Beauty API 的 `switchCamera` 切换摄像头。调用后，Beauty API 内部会通过 `isFrontCamera` 知晓当前使用的是前置还是后置摄像头，从而在后续步骤中让你能针对前后置摄像头设置正确的镜像效果。

    ```swift
    beautyAPI.switchCamera()
    ```

    多次调用该方法可以多次切换前后置摄像头。

2. 如果默认的镜像行为不满足你的业务需求，那么调用 Beauty API 的 `updateCameraConfig` 并通过 `cameraConfig` 配置前置和后置摄像头的镜像效果。所有镜像类型包含如下：

    - `LOCAL_REMOTE`：本地和远端视图都使用镜像。前置摄像头默认使用该配置。
    - `LOCAL_ONLY`：本地视图使用镜像，远端视图不使用镜像。建议在直播画面中会出现告示牌或者文字的情况下使用该配置。
    - `REMOTE_ONLY`：本地视图不使用镜像，远端视图使用镜像。
    - `NONE`：本地和远端视图都不使用镜像。后置摄像头默认使用该配置。

    ```swift
    let cameraConfig = BeautyManager.shareManager.cameraConfig
    // 判断 isFrontCamera
    if self.beautyAPI?.isFrontCamera ?? false {
        // 通过 frontMirror 配置前置摄像头
        cameraConfig.frontMirror = Configs.mirrorTypes[value] ?? .LOCAL_REMOTE
    } else {
        // 通过 backMirror 配置后置摄像头
        cameraConfig.backMirror = Configs.mirrorTypes[value] ?? .NONE
    }
    // 更新摄像头配置
    self.beautyAPI?.update(cameraConfig)
    ```

### 7. 离开频道 [#7-离开频道]

调用 `AgoraRtcEngineKit` 类的 `leaveChannel` 离开频道。

```swift
rtcEngine.leaveChannel()
```

### 8. 销毁资源 [#8-销毁资源]

调用 Beauty API 的 `destroy` 销毁 Beauty API。

```swift
beautyAPI.destroy()
```

:::warning[注意]
Beauty API 自 1.0.2 版起，已将存在笔误的 `destory` 方法名修正为 `destroy`。
:::

调用 `AgoraRtcEngineKit` 的 `destroy` 销毁 `AgoraRtcEngineKit`。

```swift
AgoraRtcEngineKit.destroy()
```

### API 时序图 [#api-时序图-1]

![美颜集成的 API 时序图](https://web-cdn.agora.io/docs-portal/img/showroom/bd-ios.svg)
<_PlatformProcessedMarker close="true" />
</_PlatformTabsGroup>
