实现音视频互动
本文介绍如何集成声网实时互动 SDK,通过少量代码从 0 开始实现一个简单的实时互动 App,适用于互动直播和视频通话场景。
首先,你需要了解以下有关音视频实时互动的基础概念:
- 声网实时互动 SDK:由声网开发的、帮助开发者在 App 中实现实时音视频互动的 SDK。
- 频道:用于传输数据的通道,在同一个频道内的用户可以进行实时互动。
- 主播:可以在频道内发布音视频,同时也可以订阅其他主播发布的音视频。
- 观众:可以在频道内订阅音视频,不具备发布音视频权限。
更多概念详见关键概念。
下图以直播场景为例,展示在 App 中实现音视频互动的基本工作流程:

- 所有用户调用
joinChannel
方法加入频道,并根据需要设置用户角色:- 互动直播:如果用户需要在频道中发流,则设为主播;如果用户只需要收流,则设为观众。
- 视频通话:将所有的用户角色都为主播。
- 加入频道后,不同角色的用户具备不同的行为:
- 所有用户默认都可以接收频道中的音视频流。
- 主播可以在频道内发布音视频流。
- 观众如果需要发流,可在频道内调用
setClientRole
方法修改用户角色,使其具备发流权限。
前提条件
-
已下载 Unity Hub。
-
Unity 2017.4.35 或以上版本。
-
一个 Unity 项目,你可以参考 Unity 官方文档来创建一个项目。
-
操作系统与编译器要求:
开发平台 操作系统版本 编译器版本 Android Android 4.1 或以上 Android Studio 3.0 或以上 iOS iOS 9.0 或以上 Xcode 9.0 或以上 macOS macOS 10.10 或以上 Xcode 9.0 或以上 Windows Windows 7 或以上 Microsoft Visual Studio 2017 或以上 -
计算机可以访问互联网。请确保你的网络环境未部署防火墙,否则可能无法正常使用声网服务。
-
一个有效的声网账号以及声网项目。请参考开通服务从声网控制台获得以下信息:
- App ID:声网随机生成的字符串,用于识别你的项目。
- 临时 Token:Token 也称为动态密钥,在客户端加入频道时对用户鉴权。临时 Token 的有效期为 24 小时。
- 频道名:用于标识频道的字符串。
创建 Unity 项目
参考以下步骤或 Unity 官方操作指南创建一个 Unity 项目。若已有 Unity 项目,可以直接查看集成 SDK。
创建 Unity 项目
- 开始前确保你已安装 Unity。若未安装,点击此处下载。
- 打开 Unity,点击 New。
- 依次填入以下内容后,点击 Create project。
- Project name:项目名称。
- Location:项目存储路径。
- Template:项目类型。选择 3D。
集成 SDK
- 前往资源获取,下载最新版的 Unity SDK。
- 下载完成后在你的 Unity 编辑器中打开 SDK 包并取消勾选不需要的插件(默认会勾选 SDK 包含的所有插件),然后点击 Import。
实现流程
下面列出了一段实现实时音视频互动基本流程的完整代码以供参考。复制以下代码到 JoinChannelVideo.cs
文件中替换原有内容即可。
在 _appId
、_token
和 _channelName
字段中传入你在控制台获取到的 App ID、临时 Token,以及生成临时 Token 时填入的频道名。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Agora.Rtc;
#if (UNITY_2018_3_OR_NEWER && UNITY_ANDROID)
using UnityEngine.Android;
#endif
public class JoinChannelVideo : MonoBehaviour
{
// 填入你的 app ID。
private string _appID= "";
// 填入你的频道名。
private string _channelName = "";
// 填入 Token。
private string _token = "";
internal VideoSurface LocalView;
internal VideoSurface RemoteView;
internal IRtcEngine RtcEngine;
#if (UNITY_2018_3_OR_NEWER && UNITY_ANDROID)
private ArrayList permissionList = new ArrayList() { Permission.Camera, Permission.Microphone };
#endif
// Start is called before the first frame update
void Start()
{
SetupVideoSDKEngine();
InitEventHandler();
SetupUI();
}
// Update is called once per frame
void Update()
{
CheckPermissions();
}
void OnApplicationQuit()
{
if (RtcEngine != null)
{
Leave();
// 销毁 IRtcEngine。
RtcEngine.Dispose();
RtcEngine = null;
}
}
private void CheckPermissions() {
#if (UNITY_2018_3_OR_NEWER && UNITY_ANDROID)
foreach (string permission in permissionList)
{
if (!Permission.HasUserAuthorizedPermission(permission))
{
Permission.RequestUserPermission(permission);
}
}
#endif
}
private void SetupUI()
{
GameObject go = GameObject.Find("LocalView");
LocalView = go.AddComponent<VideoSurface>();
go.transform.Rotate(0.0f, 0.0f, -180.0f);
go = GameObject.Find("RemoteView");
RemoteView = go.AddComponent<VideoSurface>();
go.transform.Rotate(0.0f, 0.0f, -180.0f);
go = GameObject.Find("Leave");
go.GetComponent<Button>().onClick.AddListener(Leave);
go = GameObject.Find("Join");
go.GetComponent<Button>().onClick.AddListener(Join);
}
private void SetupVideoSDKEngine()
{
// 创建 IRtcEngine 实例。
RtcEngine = Agora.Rtc.RtcEngine.CreateAgoraRtcEngine();
RtcEngineContext context = new RtcEngineContext(_appID, 0,CHANNEL_PROFILE_TYPE.CHANNEL_PROFILE_LIVE_BROADCASTING, AUDIO_SCENARIO_TYPE.AUDIO_SCENARIO_DEFAULT);
// 初始化 IRtcEngine。
RtcEngine.Initialize(context);
}
// 创建用户回调类实例,并设置回调。
private void InitEventHandler()
{
UserEventHandler handler = new UserEventHandler(this);
RtcEngine.InitEventHandler(handler);
}
public void Join()
{
// 启用视频模块。
RtcEngine.EnableVideo();
// 设置频道媒体选项。
ChannelMediaOptions options = new ChannelMediaOptions();
// 开始视频渲染。
LocalView.SetEnable(true);
// 自动订阅所有音频流。
options.autoSubscribeAudio.SetValue(true);
// 自动订阅所有视频流。
options.autoSubscribeVideo.SetValue(true);
// 将频道场景设为直播。
options.channelProfile.SetValue(CHANNEL_PROFILE_TYPE.CHANNEL_PROFILE_LIVE_BROADCASTING);
// 将用户角色设为主播。
options.clientRoleType.SetValue(CLIENT_ROLE_TYPE.CLIENT_ROLE_BROADCASTER);
// 加入频道。
RtcEngine.JoinChannel(_token, _channelName, 0, options);
}
public void Leave()
{
Debug.Log("Leaving _channelName");
// 离开频道。
RtcEngine.LeaveChannel();
// 关闭视频模块。
RtcEngine.DisableVideo();
// 停止远端视频渲染。
RemoteView.SetEnable(false);
// 停止远端视频渲染。
LocalView.SetEnable(false);
}
// 实现你自己的回调类,可以继承 IRtcEngineEventHandler 接口类实现。
internal class UserEventHandler : IRtcEngineEventHandler
{
private readonly JoinChannelVideo _videoSample;
internal UserEventHandler(JoinChannelVideo videoSample)
{
_videoSample = videoSample;
}
// 发生错误回调。
public override void OnError(int err, string msg)
{
}
// 本地用户成功加入频道时,会触发该回调。
public override void OnJoinChannelSuccess(RtcConnection connection, int elapsed)
{
_videoSample.LocalView.SetForUser(0,"");
}
// SDK 接收到第一帧远端视频并成功解码时,会触发 OnUserJoined 回调。
public override void OnUserJoined(RtcConnection connection, uint uid, int elapsed)
{
// 设置远端视频显示。
_videoSample.RemoteView.SetForUser(uid, connection.channelId, VIDEO_SOURCE_TYPE.VIDEO_SOURCE_REMOTE);
// 开始视频渲染。
_videoSample.RemoteView.SetEnable(true);
Debug.Log("Remote user joined");
}
// 远端用户离开当前频道时会触发该回调。
public override void OnUserOffline(RtcConnection connection, uint uid, USER_OFFLINE_REASON_TYPE reason)
{
_videoSample.RemoteView.SetEnable(false);
}
}
}
本节介绍如何使用声网实时音视频互动 SDK 在你的项目中实现基本的音视频互动。
创建用户界面
根据应用场景为你你的项目创建一个用户界面 (UI)。
例如,你可以在 UI 上添加以下控件:
- 本地视图窗口
- 远端视图窗口
- 加入/离开频道按钮
参考以下步骤创建 UI。若你的项目中已有用户界面可略过此步骤。
-
创建加入/离开频道按钮
a. 在你的 Unity 项目中,右击 Sample Scene,选择 Game Object > UI > Button。此时你可以在场景画布中看到你刚刚创建的按钮。
b. 在 Inspector 面板中,将 Button 重命名为 Leave,并调整按钮的位置:
- Pos X:
- 350
- Pos Y:
- 172
你可以根据自己的需求调整按钮位置。
c. 选中 Leave 按钮的 Text 控件,在 Inspector 面板中将 Text 的文本内容修改为 Leave。
d. 重复上述步骤来创建一个 Join 按钮。
- Pos X:
-
创建本地及远端视图窗口
a. 右击 Canvas,选择 UI > Raw Image。
b. 创建本地视图窗口。在 Inspector 面板中,将 Raw Image 重命名为LocalView,并调整在画布中位置:
- Pos X:
-250
- Pos Y:
0
- Width:
250
- Height:
250
你可以根据自己的需求调整位置。
c. 重复上述步骤创建远端视图窗口,将其命名为RemoteView,并调整在画布中的位置:
- Pos X:
250
- Pos Y:
0
- Width:
250
- Height:
250
- Pos X:
-
保存上述步骤的更改。 此时你的 UI 界面如下图所示:
处理系统逻辑
本节介绍导入实现音视频互动所需的库以及获取摄像头及麦克风设备权限。
在 UNITY_2018_3_OR_NEWER 或以上版本中,Unity 不会主动向用户获取麦克风和相机权限,需要用户调用 CheckPermission
方法获取权限。
仅 Android 平台需要设置此步骤。
-
新建脚本并导入 Unity 库
a. 在 Project 中,打开 Assets > Agora-Unity-RTC-SDK > Code >Rtc,右击然后选择 Create > C# Script,此时你的 Assets 中会有一个
NewBehaviourScript.cs
文件,将这个文件重新命名为JoinChannelVideo.cs
然后打开文件。b. 将以下代码添加至
UnityEngine;
之后:C#using UnityEngine.UI;
-
获取安卓权限
a. 在
Assets/Agora-Unity-RTC-SDK/Code/Rtc/JoinChannelVideo.cs
中,将下列代码添加至UnityEngine.UI;
之后:C##if (UNITY_2018_3_OR_NEWER && UNITY_ANDROID)
using UnityEngine.Android;
#endifb. 创建需要获取的权限列表。将下列代码添加至
Start
之前:C##if (UNITY_2018_3_OR_NEWER && UNITY_ANDROID)
private ArrayList permissionList = new ArrayList() { Permission.Camera, Permission.Microphone };
#endifc. 检查是否已获取权限。将下列代码添加至
Update
之后:C#private void CheckPermissions() {
#if (UNITY_2018_3_OR_NEWER && UNITY_ANDROID)
foreach (string permission in permissionList)
{
if (!Permission.HasUserAuthorizedPermission(permission))
{
Permission.RequestUserPermission(permission);
}
}
#endif
} -
将脚本和 Canvas 绑定 在
Assets/Agora-Unity-RTC-SDK/Code/Rtc
中,选择JoinChannelVideo.cs
文件,将其拖至 Canvas。在 Inspector 面板中你可以看到该文件已经和 Canvas 绑定。
实现音视频互动逻辑
参考以下步骤来实现音视频互动:
-
导入声网库 在
Assets/Agora-Unity-RTC-SDK/Code/Rtc/JoinChannelVideo.cs
中,将下列代码添加至using UnityEngine.UI;
之后:C#using Agora.Rtc;
-
创建加入频道相关变量 在
Assets/Agora-Unity-RTC-SDK/Code/Rtc/JoinChannelVideo.cs
中,将下列代码添加至JoinChannelVideo: MonoBehaviour {
之后:C#// 填入你的 App ID
private string _appID= "";
// 填入你的频道名
private string _channelName = "";
// 填入 Token
private string _token = "";
internal VideoSurface LocalView;
internal VideoSurface RemoteView;
internal IRtcEngine RtcEngine; -
创建并初始化
IRtcEngine
在调用其他声网 API 前,需要创建并初始化IRtcEngine
实例。调用CreateAgoraRtcEngine
来创建IRtcEngine
实例。在Assets/AgoraEngine/Scripts/JoinChannelVideo.cs
中,将下列代码添加至CheckPermissions
之后:C#private void SetupVideoSDKEngine()
{
// 创建 IRtcEngine 实例
RtcEngine = Agora.Rtc.RtcEngine.CreateAgoraRtcEngine();
RtcEngineContext context = new RtcEngineContext(_appID, 0,CHANNEL_PROFILE_TYPE.CHANNEL_PROFILE_LIVE_BROADCASTING, AUDIO_SCENARIO_TYPE.AUDIO_SCENARIO_DEFAULT);
// 初始化 IRtcEngine
RtcEngine.Initialize(context);
} -
创建用户回调类实例, 并设置回调。在
Assets/Agora-Unity-RTC-SDK/Code/Rtc/JoinChannelVideo.cs
中,将下列代码添加至SetupVideoSDKEngine()
之后:C#// 创建用户回调类实例,并设置回调
private void InitEventHandler()
{
UserEventHandler handler = new UserEventHandler(this);
RtcEngine.InitEventHandler(handler);
}
// 实现你自己的回调类,可以继承 IRtcEngineEventHandler 接口类实现
internal class UserEventHandler : IRtcEngineEventHandler
{
private readonly JoinChannelVideo _videoSample;
internal UserEventHandler(JoinChannelVideo videoSample)
{
_videoSample = videoSample;
}
// 发生错误回调
public override void OnError(int err, string msg)
{
}
// 本地用户成功加入频道时,会触发该回调
public override void OnJoinChannelSuccess(RtcConnection connection, int elapsed)
{
_videoSample.LocalView.SetForUser(0,"");
}
} -
UI 元素引用 在
Assets/Agora-Unity-RTC-SDK/Code/Rtc/JoinChannelVideo.cs
中,将下列代码添加至SetupVideoSDKEngine()
之前:C#private void SetupUI()
{
GameObject go = GameObject.Find("LocalView");
LocalView = go.AddComponent<VideoSurface>();
go.transform.Rotate(0.0f, 0.0f, -180.0f);
go = GameObject.Find("RemoteView");
RemoteView = go.AddComponent<VideoSurface>();
go.transform.Rotate(0.0f, 0.0f, -180.0f);
go = GameObject.Find("Leave");
go.GetComponent<Button>().onClick.AddListener(Leave);
go = GameObject.Find("Join");
go.GetComponent<Button>().onClick.AddListener(Join);
} -
设置本地视频显示并加入频道 在
Assets/Agora-Unity-RTC-SDK/Code/Rtc/JoinChannelVideo.cs
中,将下列代码InitEventHandler
之后:C#public void Join()
{
// 启用视频模块
RtcEngine.EnableVideo();
// 设置频道媒体选项
ChannelMediaOptions options = new ChannelMediaOptions();
// 开始视频渲染
LocalView.SetEnable(true);
// 自动订阅所有音频流
options.autoSubscribeAudio.SetValue(true);
// 自动订阅所有视频流
options.autoSubscribeVideo.SetValue(true);
// 将频道场景设为直播
options.channelProfile.SetValue(CHANNEL_PROFILE_TYPE.CHANNEL_PROFILE_LIVE_BROADCASTING);
// 将用户角色设为主播
options.clientRoleType.SetValue(CLIENT_ROLE_TYPE.CLIENT_ROLE_BROADCASTER);
// 加入频道
RtcEngine.JoinChannel(_token, _channelName, 0, options);
} -
设置远端视频显示 在
Assets/AgoraEngine/Scripts/JoinChannelVideo.cs
中,将下列代码添加至OnJoinChannelSuccess
之后:C#// SDK 接收到第一帧远端视频并成功解码时,会触发 OnUserJoined 回调
public override void OnUserJoined(RtcConnection connection, uint uid, int elapsed)
{
// 设置远端视频显示
_videoSample.RemoteView.SetForUser(uid, connection.channelId, VIDEO_SOURCE_TYPE.VIDEO_SOURCE_REMOTE);
// 开始视频渲染
_videoSample.RemoteView.SetEnable(true);
Debug.Log("Remote user joined");
} -
设置远端用户离开频道或掉线后停止远端视频显示 在
Assets/Agora-Unity-RTC-SDK/Code/Rtc/JoinChannelVideo.cs
中,将下列代码添加至OnUserJoined
之后:C#// 远端用户离开当前频道时会触发该回调
public override void OnUserOffline(RtcConnection connection, uint uid, USER_OFFLINE_REASON_TYPE reason)
{
_videoSample.RemoteView.SetEnable(false);
} -
离开频道 根据场景需要,如结束通话时,调用
Leave
离开当前通话频道。在Assets/Agora-Unity-RTC-SDK/Code/Rtc/JoinChannelVideo.cs
中,将下列代码添加至Join
之后:C#public void Leave()
{
Debug.Log("Leaving _channelName");
// 离开频道
RtcEngine.LeaveChannel();
// 关闭视频模块
RtcEngine.DisableVideo();
// 停止远端视频渲染
RemoteView.SetEnable(false);
// 停止远端视频渲染
LocalView.SetEnable(false);
}
启动/停止你的项目
本节介绍如何实现启动及停止你的项目。用户可以通过 Join
及 Leave
按钮来加入、离开频道。
-
检查是否已获取设备权限 调用
CheckPermissions
方法检查是否已获取实现音视频互动所需的权限。在Assets/Agora-Unity-RTC-SDK/Code/Rtc/JoinChannelVideo.cs
中,将文件中的Update
替换为以下代码:C#void Update()
{
CheckPermissions();
} -
开始音视频互动 在
Assets/Agora-Unity-RTC-SDK/Code/Rtc/JoinChannelVideo.cs
中,将Start
替换为以下代码:C#void Start()
{
SetupVideoSDKEngine();
InitEventHandler();
SetupUI();
} -
清理所有会话相关的资源。 离开频道后,如果你想退出应用或者释放
IRtcEngine
内存,需调用Dispose
方法销毁IRtcEngine
。将下列代码添加至Update
之后:C#void OnApplicationQuit()
{
if (RtcEngine != null)
{
Leave();
// 销毁 IRtcEngine
RtcEngine.Dispose();
RtcEngine = null;
}
}
测试你的项目
按照以下步骤来测试你的音视频互动项目:
- 在声网控制台获取一个临时 Token。请参考开通服务获取。
- 在
Assets/AgoraEngine/Scripts/JoinChannelVideo.cs
中,将你的项目的 App ID、频道名以及临时 Token 分别填入到_appID
、_channelName
、_token
之后。 - 在 Unity 编辑器中,点击播放按钮来运行你的项目,然后点击 Join 按钮加入频道。
- 邀请一位朋友通过另一台设备来使用相同的 App ID、Token、频道名加入频道。你的朋友加入频道后,你们可以听见、看见对方。
后续步骤
- 本文的示例使用了临时 Token 加入频道。在测试或生产环境中,为确保通信安全,声网推荐使用 Token 服务器来生成 Token,详见使用 Token 鉴权。
- 如果你想要实现极速直播场景,可以在互动直播的基础上,通过修改观众端的延时级别为低延时 (
AUDIENCE_LATENCY_LEVEL_LOW_LATENCY
)实现。详见实现极速直播。
相关信息
本节提供了额外的信息供参考。
示例项目
声网在 GitHub 上提供了一个开源的示例项目 JoinChannelVideo 供你参考。