# 实现音视频互动 (/zh-CN/realtime-media/rtc/get-started/quick-start/blueprint)

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

本文介绍如何集成声网实时互动 SDK，通过少量代码从 0 开始实现一个简单的实时互动 App，适用于互动直播和视频通话场景。
首先，你需要了解以下有关音视频实时互动的基础概念：

- 声网实时互动 SDK：由声网开发的、帮助开发者在 App 中实现实时音视频互动的 SDK。
- 频道：用于传输数据的通道，在同一个频道内的用户可以进行实时互动。
- 主播：可以在频道内**发布**音视频，同时也可以**订阅**其他主播发布的音视频。
- 观众：可以在频道内**订阅**音视频，不具备**发布**音视频权限。

更多概念详见[关键概念](/zh-CN/realtime-media/rtc/reference/key-concept)。

下图展示在 App 中实现音视频互动的基本工作流程：

<DocsImage alt="实现音视频互动" src="https://web-cdn.agora.io/docs-portal/img/rtc/quick-start-tech.png" displayWidth="60%" />

1. 所有用户调用 `joinChannel` 方法加入频道，并根据需要设置用户角色：

   - 互动直播：如果用户需要在频道中发流，则设为主播；如果用户只需要收流，则设为观众。
   - 视频通话：将所有用户的角色都设为主播。
2. 加入频道后，不同角色的用户具备不同的行为：

   - 所有用户默认都可以接收频道中的音视频流。
   - 主播可以在频道内发布音视频流。
   - 观众如果需要发流，可在频道内调用 `setClientRole` 方法修改用户角色，使其具备发流权限。

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

- [Unreal Engine](https://www.unrealengine.com/zh-CN/download) 4.27 及以上版本
- 两台设备，不可使用虚拟机。参考下方列出的 Unreal Engine 官方文档，根据你的目标平台和引擎版本准备开发环境：
  | 开发平台 | 参考文档 | 备注 |
  | :--- | :------ | :------------------------------ |
  | Android | [Android 开发环境要求](https://dev.epicgames.com/documentation/zh-cn/unreal-engine/android-development-requirements?application_version=4.27) | 无 |
  | iOS | [iOS 开发环境要求](https://dev.epicgames.com/documentation/zh-cn/unreal-engine/ios-and-tvos-development-requirements?application_version=4.27) | 有效的 Apple 开发者签名。 |
  | macOS | [macOS 开发环境要求](https://dev.epicgames.com/documentation/zh-cn/unreal-engine/hardware-and-software-specifications?application_version=4.27) | 有效的 Apple 开发者签名。 |
  | Windows | [Windows 开发环境要求](https://dev.epicgames.com/documentation/zh-cn/unreal-engine/hardware-and-software-specifications?application_version=4.27) | 32 位 Windows 仅支持 Unreal Engine 4 及以下版本，你需要在 `AgoraPluginLibrary.Build.cs` 文件中将 Windows 32 相关的代码取消注释。 |
- 可以访问互联网的计算机。如果你的网络环境部署了防火墙，参考[应对防火墙限制](/zh-CN/realtime-media/rtc/build/setup-and-access/firewall)以正常使用声网服务。
- 一个有效的声网账号以及声网项目。请参考[开通 RTC 服务](/zh-CN/realtime-media/rtc/build/setup-and-access/enable-service)从声网控制台获得以下信息：
    - App ID：声网随机生成的字符串，用于识别你的项目。
    - 临时 Token：Token 也称为动态密钥，在客户端加入频道时对用户鉴权。临时 Token 的有效期为 24 小时。
    - 频道名：用于标识频道的字符串。

<a id="project"></a>
## 创建项目 [#创建项目-11]

参考以下步骤或 [Unreal 官方操作指南](https://dev.epicgames.com/documentation/zh-cn/unreal-engine/create-a-new-project?application_version=4.27)创建一个 **Unreal** 项目。如果已有 **Unreal** 项目，可进行下一步[集成 SDK](#集成-sdk-11)。

1. 打开 **Unreal Engine**，在 **New Project Categories** 下选择 **Games**，点击 **Next**。
2. 在 **SelectTemplate** 页面，选择 **Blank**，点击 **Next**。
3. 在 **Project Settings** 页面，进行如下设置：
    1. 选择 **Blueprint** 或 **C++**。
    2. 根据你开发的目标平台选择 **Desktop/Console** 或 **Mobile/Tablet**。
    3. 选择项目存储路径，并为项目命名。
    4. 点击 **Create Project** 完成创建。

![创建 Unreal 项目的界面示意图](https://web-cdn.agora.io/docs-files/1689850102339)

## 集成 SDK [#集成-sdk-11]

1. 前往[下载](/zh-CN/realtime-media/rtc/reference/downloads)页面，下载最新版本的 Unreal 视频 SDK，并解压缩。
2. 在你的项目根目录文件夹下，创建名为 `Plugins` 的文件夹。
3. 将 Unreal SDK 文件夹中的 `AgoraPlugin` 拷贝到 `Plugins` 中。

![](https://web-cdn.agora.io/docs-files/1689830211462)

## 创建 UMG [#创建-umg]

为直观地体验实时音视频互动，需根据应用场景创建以下控件：

   - 本地和远端的视频窗口
   - 加入和离开频道的按钮

![](https://web-cdn.agora.io/docs-files/1689830239247)

参考以下步骤创建上图所示 UI 界面。如果你的项目中已有用户界面，可进行下一步[创建关卡](#创建关卡)。

1. 创建 **Widget Blueprint**。
    1. 在 **Content Browser** 的 **Content** 文件夹中，右击选择 **User Interface** > **Widget Blueprint** 创建 **Widget Blueprint**，并将其命名为 **BP_VideoWidget**。
    2. 双击打开 **BP_VideoWidget**。此时可以在 **Hierarchy** 面板中看到 **BP_VideoWidget** > **Canvas Panel**。
2. 在 **Widget Blueprint** 中创建本地和远端的视频窗口。
    1. 创建视图背景。在 **Palette** 面板中，选择 **Common** > **Image**，将其拖至 **Canvas Panel** 中，命名为 **background**。通过拖拽调整至画布大小，在 **Details** 面板中调整背景颜色。
    2. 创建本地视图窗口。在 **Palette** 面板中，选择 **Common** > **Image**，将其拖至 **Canvas Panel** 中。将其命名为 **Img_LocalVideoView**，并在 **Details** 面板中调整其在画布中的位置和大小：
        - **Position X**：-500
        - **Position Y**：-100
        - **Size X**：640
        - **Size Y**：360
    3. 重复上述步骤创建远端视图窗口，将其命名为 **Img_RemoteVideoView**，并调整其在画布中的位置和大小：
        - **Position X**：500
        - **Position Y**：-100
        - **Size X**：640
        - **Size Y**：360
3. 在 **Widget Blueprint** 中创建加入和离开频道的按钮。
    1. 创建加入频道按钮。选择 **Common** > **Button**，将其拖至 **Canvas Panel** 中，重命名为 **Btn_JoinChannel**，并调整其在画布中的位置和大小：
        - **Position X**：-500
        - **Position Y**：200
        - **Size X**：100
        - **Size Y**：130
    2. 选择 **Common** > **Text**，将其拖至 **Btn_JoinChannel** 中，并在 **Details** 面板中将 **Text** 文本内容修改为 **JoinChannel**。
    3. 重复上述步骤创建离开频道按钮，将其命名为 **Btn_LeaveChannel**，并将 **Text** 文本内容修改为 **LeaveChannel**。调整按钮在画布中的位置和大小：
        - **Position X**：500
        - **Position Y**：200
        - **Size X**：130
        - **Size Y**：130
4. 保存上述更改。

## 实现步骤 [#实现步骤-12]

本节介绍如何使用声网 RTC SDK 在你的项目中实现实时音视频互动功能。

### 创建关卡 [#创建关卡]

1. 在 **Content Browser** 的 **Content** 文件夹中，右击选择 **Level** 创建 **Level Blueprint**，并将其命名为 **BasicVideoCallScene**。
2. 双击 **BasicVideoCallScene**，点击编辑器上方的 **Blueprints** > **Open Level Blueprint** 打开关卡蓝图。

![](https://web-cdn.agora.io/docs-files/1689830289542)

### 实现基础流程 [#实现基础流程]

在 **My Blueprint** 面板中，双击 **Graphs** > **EventGraph** 打开事件图表，其中已有 **Event BeginPlay** (游戏开始) 和 **Event End Play** (游戏结束) 两个事件节点。接下来，创建实时音视频互动相关的事件节点以及对应的函数和变量，并将它们连接起来以实现实时音视频互动的逻辑和交互，如下图所示：

![](https://web-cdn.agora.io/docs-files/1689836764960)

其中主要节点如下：

| 序号 | 节点 | 类型 | 描述 |
| --- | --- | --- | --- |
| 1 | **Set Show Mouse Cursor** | 原生* | [quick-start-8119-2-3 details](#table-slot-quick-start-8119-2-3) |
| 2 | **Load Agora Config** | 自定义** | 加载声网配置。用于后续在创建和加入频道时验证用户身份。 |
| 3 | **Create BP Video Widget Widget** | 原生 | [quick-start-8119-4-3 details](#table-slot-quick-start-8119-4-3) |
| 4 | **Set Basic Video Call Widget** | 自定义 | [quick-start-8119-5-3 details](#table-slot-quick-start-8119-5-3) |
| 5 | **Bind UIEvent** | 自定义 | 绑定 UI 事件，用于处理点击 **JoinChannel** 和 **LeaveChannel** 按钮后的事件逻辑。 |
| 6 | **Add to Viewport** | 原生 | 将用户界面添加到视口。 |
| 7 | **Check Permission** | 自定义 | [quick-start-8119-8-3 details](#table-slot-quick-start-8119-8-3) |
| 8 | **Init Rtc Engine** | 自定义 | 创建并初始化 RTC 引擎。 |
| 9 | **Un Init Rtc Engine** | 自定义 | 离开频道并释放资源。 |

<a id="table-slot-quick-start-8119-2-3"></a>

**quick-start-8119-2-3 details**

- (可选) 设置是否显示鼠标光标，勾选代表显示。
- 该节点仅适用于 Windows 和 macOS。
- 如果在创建时检索不到该节点，可以取消勾选 **Context Sensitive**。

<DocsImage alt="Unreal Blueprint 基础流程节点示例" src="https://web-cdn.agora.io/docs-files/1689863102745" displayWidth="30%" />

<a id="table-slot-quick-start-8119-4-3"></a>

**quick-start-8119-4-3 details**

创建用户界面。步骤如下：

1. 创建 **Create Widget** 节点。
2. 选择该节点的 **Class** 为 **BP_VideoWidget**，将该节点关联至已经创建好的用户界面。

<a id="table-slot-quick-start-8119-5-3"></a>

**quick-start-8119-5-3 details**

设置用户界面。步骤如下：

1. 创建 **BasicVideoCallWidget** 变量，选择变量的 **Variable Type** 为 **BP_VideoWidget**，即已经创建好的用户界面，用于在蓝图中存储对用户界面的引用。
2. 拖拽创建好的变量到 **EventGraph** 后，会出现 **Set BasicVideoCallWidget** 和 **Get BasicVideoCallWidget** 两个选项，选择 **Set BasicVideoCallWidget** 创建节点，用于访问并设置用户界面。

<a id="table-slot-quick-start-8119-8-3"></a>

**quick-start-8119-8-3 details**

- (可选) 检查是否已获取实时音视频互动所需的系统权限，如访问摄像头和麦克风等。
- 如果你的目标平台是 Android，需要创建该节点用于检查系统权限。


*原生节点为蓝图自带的节点，可以直接添加调用。

**自定义节点非蓝图自带，需要在创建自定义函数后才能添加对应节点。

### 加入频道相关变量 [#加入频道相关变量]

1. 创建 **Token**、**ChannelId** 和 **AppId** 三个变量，选择 **Variable Type** 为 **String**。
2. 在 **Load Agora Config** 函数中，添加 **Sequence** 节点，然后分别连接 **Set Token**、**Set Channel Id** 和 **Set App Id**，在其中填入从声网控制台获取的 Token、频道名和 App ID，用于后续创建和加入频道时验证用户身份。

![](https://web-cdn.agora.io/docs-files/1689836892597)

### 初始化 RTC 引擎 [#初始化-rtc-引擎]

:::warning[注意]
在初始化 SDK 前，需确保终端用户已经充分了解并同意相关的隐私政策。
:::

1. (可选) 如果你的目标平台是 Android，在初始化 RTC 引擎前，需要检查是否已获取 Android 系统权限。在 **CheckPermission** 函数中，参照下图创建节点，用于添加 Android 系统中访问麦克风、访问摄像头等权限。

![](https://web-cdn.agora.io/docs-files/1689838387857)

   :::info[信息]
   如果你的目标平台是 macOS 或 iOS，请参考[如何为 Unreal Engine 项目添加实时互动所需的权限？](/zh-CN/reference/faq/integration/unreal_permissions)
   :::

2. 在 **InitRtcEngine** 函数中，参照下图创建并连接节点以初始化 RTC 引擎。

![](https://web-cdn.agora.io/docs-files/1689838445025)

   主要步骤如下：

      1. 创建 `IRtcEngine` 和 `IRtcEngineEventHandler`。
         1. 创建 **RtcEngine** 和 **EventHandler** 变量，将 **Variable Type** 分别设置为 **Agora Rtc Engine** 和 **IRtc Engine Event Handler**，即声网 SDK 的基础功能类和事件回调类，用于在蓝图中存储对这两个接口类的引用。
         2. 添加两个 **Construct Object From Class** 节点，将 **Class** 分别设置为 **Agora Rtc Engine** 和 **IRtc Engine Event Handler**，并分别连接 **Set Rtc Engine** 和 **Set Event Handler**。

      2. 绑定 `IRtcEngineEventHandler` 类相关的回调函数。

创建 **onJoinChannelSuccess**、**onLeaveChannel**、**onUserJoined** 和 **onUserOffline** 回调函数。参考下表配置回调的输入参数：

| 回调函数 | 描述 | 输入参数 |
| --- | --- | --- |
| `FOnJoinChannelSuccess` | 成功加入频道回调。 | [quick-start-8170-2-2 details](#table-slot-quick-start-8170-2-2) |
| `FOnLeaveChannel` | 离开频道回调。 | `stats`: 通话的统计数据。详见 [`FRtcStats`](/zh-CN/api-reference/rtc/unreal-blueprint/class-rtcstats)。 |
| `FOnUserJoined` | 远端观众或主播加入当前频道回调。 | [quick-start-8170-4-2 details](#table-slot-quick-start-8170-4-2) |
| `FOnUserOffline` | 远端观众或主播离开当前频道回调。 | [quick-start-8170-5-2 details](#table-slot-quick-start-8170-5-2) |

<a id="table-slot-quick-start-8170-2-2"></a>

**quick-start-8170-2-2 details**

- `channel`: (String) 频道名。
- `uid`: (Integer64) 加入频道的用户 ID。
- `elapsed`: (Integer) 从本地调用 `JoinChannel` 开始到发生此事件过去的时间（毫秒）。

<a id="table-slot-quick-start-8170-4-2"></a>

**quick-start-8170-4-2 details**

- `uid`: (Integer64) 新加入频道的远端观众或主播的用户 ID。
- `elapsed`: (Integer) 从本地调用 `JoinChannel` 开始到发生此事件过去的时间（毫秒）。

<a id="table-slot-quick-start-8170-5-2"></a>

**quick-start-8170-5-2 details**

- `uid`: (Integer64) 离线观众或主播的用户 ID。
- `reason`: 离线原因。详见 [`EUSER_OFFLINE_REASON_TYPE`](/zh-CN/api-reference/rtc/unreal-blueprint/enum-userofflinereasontype)。

创建 **Bind Event** 函数，在该函数中，添加 **Sequence** 节点，然后分别绑定 **onJoinChannelSuccess**、**onLeaveChannel**、**onUserJoined** 和 **onUserOffline** 回调事件。

![](https://web-cdn.agora.io/docs-files/1689863944284)

      3. 初始化 `IRtcEngine`。
         1. 调用 **Initialize** 初始化 RTC 引擎。
         2. 连接 **RtcEngineContext** 配置 `IRtcEngine` 实例，注意将 **Channel Profile** 选为 **CHANNEL_PROFILE_LIVE_BROADCASTING** (直播场景)。

### 绑定 UI 事件 [#绑定-ui-事件]

:::info[信息]
你也可以在 UMG 中绑定 UI 事件，本文仅展示使用 **Bind UIEvent** 函数进行绑定。
:::

1. 创建并实现 **OnJoinChannelClicked** 事件回调。

![](https://web-cdn.agora.io/docs-files/1689838863648)

   主要步骤如下：

      1. 调用 **Enable Video** 和 **Enable Audio** 启用视频和音频模块。
      2. 调用 **Join Channel** 加入频道。
      3. 在 **Make ChannelMediaOptions** 中设置以下参数：
         - 设置 **Publish Camera Track** 为 **AGORA TRUE VALUE**，发布摄像头采集的视频流。
         - 设置 **Publish Microphone Track** 为 **AGORA TRUE VALUE**，发布麦克风采集的音频流。
         - 设置 **Auto Subscribe Video** 为 **AGORA TRUE VALUE**，自动订阅所有视频流。
         - 设置 **Auto Subscribe Audio** 为 **AGORA TRUE VALUE**，自动订阅所有音频流。
         - 勾选 **Client Role Type Set Value** 并设置 **Client Role Type** 为 **CLIENT_ROLE_BROADCASTER** 或 **CLIENT_ROLE_AUDIENCE**，设置用户角色为主播或观众。
         - 勾选 **Channel Profile Set Value** 并设置 **Channel Profile** 为 **CHANNEL_PROFILE_LIVE_BROADCASTING**，设置频道场景为直播场景。

2. 创建并实现 **OnLeaveChannelClicked** 事件回调。当事件触发时，调用 **Leave Channel** 离开频道。

![](https://web-cdn.agora.io/docs-files/1689838885320)

3. 在 **Bind UIEvent** 函数中，参考下图将 **OnJoinChannelClicked** 和 **OnLeaveChannelClicked** 回调函数分别绑定 **JoinChannel** 和 **LeaveChannel** 按钮。点击按钮时，会触发对应的事件回调。

![](https://web-cdn.agora.io/docs-files/1689838906424)

### 设置本地和远端视图 [#设置本地和远端视图]

1. 创建并实现 **MakeVideoView** 函数，在本地或远端的观众或主播加入频道时加载视图。

![](https://web-cdn.agora.io/docs-files/1689839685482)

   主要步骤如下：

      1. 在该函数中，创建 **SavedUID**、**SavedSourceType** 和 **SavedChannelID** 三个本地变量，分别设置 **Variable Type** 为 **Integer64**、**VIDEO_SOURCE_TYPE** 和 **String**，保存变量在后续加载视图时使用。
      2. 创建本地视图。本地视图中，`uid` 为 0，由 SDK 随机分配，视频源类型为 **VIDEO_SOURCE_CAMERA_PRIMARY**（第一个摄像头）。
      3. 创建远端视图。远端视图中，uid 为远端发送来的 uid，视频源类型为 **VIDEO_SOURCE_REMOTE** (网络获取的远端视频)。

2. 创建并实现 **ReleaseVideoView** 函数，在本地或远端的观众或主播离开频道时释放视图。

![](https://web-cdn.agora.io/docs-files/1689839722484)

   主要步骤如下：

      1. 在该函数中，创建 **SavedUID**、**SavedSourceType** 和 **SavedChannelID** 三个本地变量，分别设置 **Variable Type** 为 **Integer64**、**VIDEO_SOURCE_TYPE** 和 **String**，保存变量在后续释放视图时使用。
      2. 释放本地视图。
      3. 释放远端视图。

### 实现回调函数 [#实现回调函数]

将之前创建的 **onJoinChannelSuccess**、**onLeaveChannel**、**onUserJoined** 和 **onUserOffline** 回调函数按照以下实现步骤进行配置：

1. 本地用户成功加入频道后，触发 **onJoinChannelSuccess** 回调，创建本地视图，uid 为 0，由 SDK 随机分配，视频源类型为 **VIDEO_SOURCE_CAMERA_PRIMARY** (第一个摄像头)。

![](https://web-cdn.agora.io/docs-files/1689839345488)

2. 本地用户离开频道后，触发 **onLeaveChannel** 回调，释放本地视图。

![](https://web-cdn.agora.io/docs-files/1689839368946)

3. 远端用户加入频道时，触发 **onUserJoined** 回调，创建远端视图，uid 为远端发送来的 uid，视频源类型为 **VIDEO_SOURCE_REMOTE** (网络获取的远端视频)。

![](https://web-cdn.agora.io/docs-files/1689839388183)

4. 远端用户离开频道时，触发 **onUserOffline** 回调，释放远端视图。

![](https://web-cdn.agora.io/docs-files/1689839447883)

### 离开频道并释放资源 [#离开频道并释放资源]

参考下图实现 **UnInitRtcEngine** 函数。

![](https://web-cdn.agora.io/docs-files/1689839168810)

主要步骤如下：

   1. 离开频道。
   2. 取消注册 RTC 事件回调。
   3. 销毁 `IRtcEngine` 对象，释放声网 SDK 使用的所有资源。

## 后续步骤 [#后续步骤-10]

- 如果你的目标平台是 macOS 或 iOS，则需要在打包时添加实时互动所需的摄像头和麦克风等权限，详见[如何为 Unreal Engine 项目添加实时互动所需的权限？](/zh-CN/reference/faq/integration/unreal_permissions)
- 在本文示例中，使用了临时 Token 加入频道。在测试或生产环境中，为确保通信安全，声网推荐使用 Token 服务器来生成 Token，详见[使用 Token 鉴权](/zh-CN/realtime-media/rtc/build/setup-and-access/token-authentication)。
- 如果你想要实现极速直播场景，可以在实时互动直播的基础上，通过修改观众端的延时级别为低延时 (`AUDIENCE_LATENCY_LEVEL_LOW_LATENCY`)实现。详见[实现极速直播](/zh-CN/realtime-media/rtc/build/advanced-channel/ls-quick-start)。

## 测试你的项目 [#测试你的项目-3]

按照以下步骤来测试你的实时音视频互动项目：

   1. 在 **Load Agora Config** 函数中，分别填入将你的项目的 App ID、频道名以及临时 Token。
   2. 在 **Unreal Editor** 中，点击 <InlineImage alt="编译按钮" src="https://web-cdn.agora.io/docs-files/1690867946685" width={25} /> 来运行你的项目，然后点击 **JoinChannel** 加入频道。
   3. 邀请一位朋友通过另一台设备来使用相同的 App ID、频道名、Token 加入频道。如果你的朋友以**主播**身份加入，你们可以听见、看见对方；如果作为**观众**加入，你只能看到自己，你的朋友可以看到你并听到你的声音。

## 参考信息 [#参考信息-12]

### 示例项目 [#示例项目-12]

声网提供了开源的示例项目供你参考，你可以前往下载或查看其中的源代码。

<Cards>
  <Card title="Unreal Blueprint 示例项目" href="https://github.com/AgoraIO-Extensions/Agora-Unreal-RTC-SDK/tree/main/Agora-Unreal-SDK-Blueprint-Example" />
</Cards>

### 常见问题 [#常见问题-11]

- [直播场景下，如何监听远端观众角色用户加入/离开频道的事件？](/zh-CN/reference/faq/integration/audience_event)
- [如何处理视频黑屏问题？](/zh-CN/reference/faq/quality/video_blank)
- [为什么我无法打开摄像头？](/zh-CN/reference/faq/quality/video_camera)
- [如何处理频道相关常见问题？](/zh-CN/reference/faq/integration/channel)
- [如何设置日志文件？](/zh-CN/reference/faq/integration/set_log_file)
- [为什么部分 Android 版本应用锁屏或切后台后采集音视频无效？](/zh-CN/reference/faq/quality/android_background)
- [编译 Xcode 项目时遇到无法打开 framework 的弹窗警告怎么办？](/zh-CN/reference/faq/integration/framework_cannot_be_opened)
- [为什么 SDK 中使用的是动态库而不是静态库？](/zh-CN/reference/faq/integration/dynamic_or_static_library)

### 相关文档 [#相关文档-11]

- [SDK 错误码](/zh-CN/realtime-media/rtc/reference/error-code)
- [频道连接状态管理](/zh-CN/realtime-media/rtc/build/initialize-and-channel/channel-connection)


