Unity实战篇 | 接入 声网SDK 实现 音频通话 —— 自己动手做一个 语音聊天房
Posted 呆呆敲代码的小Y
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity实战篇 | 接入 声网SDK 实现 音频通话 —— 自己动手做一个 语音聊天房相关的知识,希望对你有一定的参考价值。
目录
🐱🏍前言
- 最近正好在网上看到
声网
,然后就顺道了解到了声网这个平台,发现声网的功能还真挺多呢 - 也是一个兼容几十种平台的大公司啊,到现在才了解到,也算是相见恨晚~
- 所以就赶紧来用
Unity
结合声网
做一个语音聊天房! - 我也是第一次接入声网的SDK,可能有些地方不是很熟练,正好写一篇文章来记录学习一下~
🎂Unity 接入 声网SDK 实现 音频通话
先简单的介绍一下声网,不了解的小伙伴可以简单认识一下~
声网 官网:https://www.agora.io/cn/community/
成立于 2014 年 4 月的声网Agora 是实时互动 API 平台行业开创者,是专业服务商。开发者只需简单调用 API,即可在应用内构建多种实时音视频互动场景。声网 SDK 已经赋能社交直播、在线教育、游戏电竞、IoT、AR/VR、金融、保险、医疗、企业协作等 10 余行业,共计 100 多种场景。
声网的 SDK(Software Development Kit) 包体积很小,运行时CPU和内存占用率低,对于移动端的游戏开发很友好。
2019年7月声网正式成为了 Unity 官方认证合作伙伴,语音和视频的 SDK 也已经发布在了 Unity 资源商店中,能够非常方便的接入。
下面就来一步一步搞一下这个音视频通话试试吧!
第1️⃣步,创建声网应用
首先我们需要去声网,注册登录一系列的就不说了
登录上之后来我们的控制台创建一个应用,拿到我们创建的这个应用的APPID
由于我们是用来测试使用的,直接选择调试模式就好啦,可以参考官网文档查看二者的区别:https://docs.agora.io/cn/Agora%20Platform/token?platform=All%20Platforms
创建成功后,点击APP ID
按钮,将其复制出来,后面的代码中会用到!
第2️⃣步,获取相应的SDK
接下来我们要下载一下音视频的SDK包,下载方式有两种
一种是在声网的官网下载对应的UnitySDK包,如下所示
另一种则是在Unity商店里下载,因为前面说了, 声网和Unity已经进行过合作了
如果是第一种方式在声网下载的,则是一个压缩包
里面分别有一个多平台的工具包 和 一个Unity示例工程
我们直接使用Unity示例工程即可
如果是从Unity商店下载的包,那就直接导入Unity工程中就好了!
两种方式都可以,我这里是直接在声网下载的SDK,以因为Unity商店的网速实在是太卡了,不友好~
第3️⃣步,将SDK接入Unity中
我们这里直接使用UnityHub把在声网下载的那个Unity示例工程给打开
当然也可以在一个Unity中导入在Unity商店下载的包,都是一样的效果
打开之后工程视图如下所示
- Demo:官方提供的测试语音 Demo
- Edior:ios 构建后处理脚本
- Plugins:不同平台所依赖的库
- Scripts:SDK 源码
第4️⃣步:搭建一个测试场景,编写测试代码
示例工程中有个场景,我们可以使用它的,也可以自己新建一个场景测试!
简单修改一下场景UI,一起来看下代码
场景中新建一个脚本HelloUnity3D
,挂载到场景中即可
using UnityEngine;
using UnityEngine.UI;
#if(UNITY_2018_3_OR_NEWER)
using UnityEngine.android;
#endif
using agora_gaming_rtc;
public class HelloUnity3D : MonoBehaviour
{
public InputField mChannelNameInputField;//频道号
public Text mShownMessage;//提示
public Text versionText;//版本号
public Button joinChannel;//加入房间
public Button leaveChannel;//离开房间
public Button muteButton;//静音
private IRtcEngine mRtcEngine = null;
// 输入App ID后,在App ID外删除##
[SerializeField]
private string AppID = "app_id";
void Awake()
{
QualitySettings.vSyncCount = 0;
Application.targetFrameRate = 30;
muteButton.enabled = false;
CheckAppId();
}
// 进行初始化
void Start()
{
#if (UNITY_2018_3_OR_NEWER)
// 判断是否有麦克风权限,没有权限的话主动申请权限
if (!Permission.HasUserAuthorizedPermission(Permission.Microphone))
{
Permission.RequestUserPermission(Permission.Microphone);
}
#endif
joinChannel.onClick.AddListener(JoinChannel);
leaveChannel.onClick.AddListener(LeaveChannel);
muteButton.onClick.AddListener(MuteButtonTapped);
mRtcEngine = IRtcEngine.GetEngine(AppID);
versionText.GetComponent<Text>().text = "Version : " + getSdkVersion();
//加入频道成功后的回调
mRtcEngine.OnJoinChannelSuccess += (string channelName, uint uid, int elapsed) =>
{
string joinSuccessMessage = string.Format("加入频道 回调 uid: {0}, channel: {1}, version: {2}", uid, channelName, getSdkVersion());
Debug.Log(joinSuccessMessage);
mShownMessage.GetComponent<Text>().text = (joinSuccessMessage);
muteButton.enabled = true;
};
//离开频道回调。
mRtcEngine.OnLeaveChannel += (RtcStats stats) =>
{
string leaveChannelMessage = string.Format("离开频道回调时间 {0}, tx: {1}, rx: {2}, tx kbps: {3}, rx kbps: {4}", stats.duration, stats.txBytes, stats.rxBytes, stats.txKBitRate, stats.rxKBitRate);
Debug.Log(leaveChannelMessage);
mShownMessage.GetComponent<Text>().text = (leaveChannelMessage);
muteButton.enabled = false;
// 重置静音键状态
if (isMuted)
{
MuteButtonTapped();
}
};
//远端用户加入当前频道回调。
mRtcEngine.OnUserJoined += (uint uid, int elapsed) =>
{
string userJoinedMessage = string.Format("远端用户加入当前频道回调 uid {0} {1}", uid, elapsed);
Debug.Log(userJoinedMessage);
mShownMessage.GetComponent<Text>().text = (userJoinedMessage);
};
//远端用户离开当前频道回调。
mRtcEngine.OnUserOffline += (uint uid, USER_OFFLINE_REASON reason) =>
{
string userOfflineMessage = string.Format("远端用户离开当前频道回调 uid {0} {1}", uid, reason);
Debug.Log(userOfflineMessage);
mShownMessage.GetComponent<Text>().text = (userOfflineMessage);
};
// 用户音量提示回调。
mRtcEngine.OnVolumeIndication += (AudioVolumeInfo[] speakers, int speakerNumber, int totalVolume) =>
{
if (speakerNumber == 0 || speakers == null)
{
Debug.Log(string.Format("本地用户音量提示回调 {0}", totalVolume));
}
for (int idx = 0; idx < speakerNumber; idx++)
{
string volumeIndicationMessage = string.Format("{0} onVolumeIndication {1} {2}", speakerNumber, speakers[idx].uid, speakers[idx].volume);
Debug.Log(volumeIndicationMessage);
}
};
//用户静音提示回调
mRtcEngine.OnUserMutedAudio += (uint uid, bool muted) =>
{
string userMutedMessage = string.Format("用户静音提示回调 uid {0} {1}", uid, muted);
Debug.Log(userMutedMessage);
mShownMessage.GetComponent<Text>().text = (userMutedMessage);
};
//发生警告回调
mRtcEngine.OnWarning += (int warn, string msg) =>
{
string description = IRtcEngine.GetErrorDescription(warn);
string warningMessage = string.Format("发生警告回调 {0} {1} {2}", warn, msg, description);
Debug.Log(warningMessage);
};
//发生错误回调
mRtcEngine.OnError += (int error, string msg) =>
{
string description = IRtcEngine.GetErrorDescription(error);
string errorMessage = string.Format("发生错误回调 {0} {1} {2}", error, msg, description);
Debug.Log(errorMessage);
};
// 当前通话统计回调,每两秒触发一次。
mRtcEngine.OnRtcStats += (RtcStats stats) =>
{
string rtcStatsMessage = string.Format("onRtcStats callback duration {0}, tx: {1}, rx: {2}, tx kbps: {3}, rx kbps: {4}, tx(a) kbps: {5}, rx(a) kbps: {6} users {7}",
stats.duration, stats.txBytes, stats.rxBytes, stats.txKBitRate, stats.rxKBitRate, stats.txAudioKBitRate, stats.rxAudioKBitRate, stats.userCount);
//Debug.Log(rtcStatsMessage);
int lengthOfMixingFile = mRtcEngine.GetAudioMixingDuration();
int currentTs = mRtcEngine.GetAudioMixingCurrentPosition();
string mixingMessage = string.Format("Mixing File Meta {0}, {1}", lengthOfMixingFile, currentTs);
//Debug.Log(mixingMessage);
};
//语音路由已发生变化回调。(只在移动平台生效)
mRtcEngine.OnAudioRouteChanged += (AUDIO_ROUTE route) =>
{
string routeMessage = string.Format("onAudioRouteChanged {0}", route);
Debug.Log(routeMessage);
};
//Token 过期回调
mRtcEngine.OnRequestToken += () =>
{
string requestKeyMessage = string.Format("OnRequestToken");
Debug.Log(requestKeyMessage);
};
// 网络中断回调(建立成功后才会触发)
mRtcEngine.OnConnectionInterrupted += () =>
{
string interruptedMessage = string.Format("OnConnectionInterrupted");
Debug.Log(interruptedMessage);
};
// 网络连接丢失回调
mRtcEngine.OnConnectionLost += () =>
{
string lostMessage = string.Format("OnConnectionLost");
Debug.Log(lostMessage);
};
// 设置 Log 级别
mRtcEngine.SetLogFilter(LOG_FILTER.INFO);
// 1.设置为自由说话模式,常用于一对一或者群聊
//mRtcEngine.SetChannelProfile(CHANNEL_PROFILE.CHANNEL_PROFILE_COMMUNICATION);
//2.设置为直播模式,适用于聊天室或交互式视频流等场景。
//mRtcEngine.SetChannelProfile (CHANNEL_PROFILE.CHANNEL_PROFILE_LIVE_BROADCASTING);
//3.设置为游戏模式。这个配置文件使用较低比特率的编解码器,消耗更少的电力。适用于所有游戏玩家都可以自由交谈的游戏场景。
//mRtcEngine.SetChannelProfile(CHANNEL_PROFILE.CHANNEL_PROFILE_GAME);
//设置直播场景下的用户角色。
//mRtcEngine.SetClientRole (CLIENT_ROLE_TYPE.CLIENT_ROLE_BROADCASTER);
}
private void CheckAppId()
{
Debug.Assert(AppID.Length > 10, "请先在Game Controller对象上填写你的AppId。.");
GameObject go = GameObject.Find("AppIDText");
if (go != null)
{
Text appIDText = go.GetComponent<Text>();
if (appIDText != null)
{
if (string.IsNullOrEmpty(AppID))
{
appIDText.text = "AppID: " + "UNDEFINED!";
appIDText.color = Color.red;
}
else
{
appIDText.text = "AppID: " + AppID.Substring(0, 4) + "********" + AppID.Substring(AppID.Length - 4, 4);
}
}
}
}
/// <summary>
/// 加入频道
/// </summary>
public void JoinChannel()
{
// 从界面的输入框获取频道名称
string channelName = mChannelNameInputField.text.Trim();
Debug.Log(string.Format("从界面的输入框获取频道名称 {0}", channelName));
if (string.IsNullOrEmpty(channelName))
{
return;
}
// 加入频道
// channelKey: 动态秘钥,我们最开始没有选择 Token 模式,这里就可以传入 null;否则需要传入服务器生成的 Token
// channelName: 频道名称
// info: 开发者附带信息(非必要),不会传递给频道内其他用户
// uid: 用户ID,0 为自动分配
//mRtcEngine.JoinChannelByKey(channelKey: null, channelName: channelName, info: "extra", uid: 0);
//加入频道并设置发布和订阅状态。
mRtcEngine.JoinChannel(channelName, "extra", 0);
}
/// <summary>
/// 离开频道
/// </summary>
public void LeaveChannel()
{
// 离开频道
mRtcEngine.LeaveChannel();
string channelName = mChannelNameInputField.text.Trim();
Debug.Log(string.Format("left channel name {0}", channelName));
}
void OnApplicationQuit()
{
if (mRtcEngine != null)
{
// 销毁 IRtcEngine
IRtcEngine.Destroy();
}
}
/// <summary>
/// 查询 SDK 版本号。
/// </summary>
/// <returns></returns>
public string getSdkVersion()
{
string ver = IRtcEngine.GetSdkVersion();
return ver;
}
bool isMuted = false;
void MuteButtonTapped()
{
//设置静音或者取消静音
string labeltext = isMuted ? "Mute" : "Unmute";
Text label = muteButton.GetComponentInChildren<Text>();
if (label != null)
{
label.text = labeltext;
}
isMuted = !isMuted;
// 设置静音(停止推送本地音频)
mRtcEngine.EnableLocalAudio(!isMuted);
Debug.Log("静音方法执行完成");
}
}
上述代码中都有注释,一个完整的语音通话案例就写好了!
只需要把自己在声网后台获取到的APP ID 赋值一下就好了~
第5️⃣步:音频通话API
在声网有关于视频通话的一堆API,我们可以来参考一下
音频通话API:https://docs.agora.io/cn/Voice/API%20Reference/unity/index.html
这里我们只介绍几种核心的API,也是在本次实例中用到的做重点介绍,其他的可以有时间的时候自己研究一下 ~
第6️⃣步:效果测试
可以先在编辑器下看看运行效果
我这里是 打包成PC的一个exe文件运行 和 在编辑器下运行
让这两个进行语音通话,效果如下:
语音通话的 API 调用时序见下图:
🎨总结
- 本文简单做了一个使用Unity实现语音通话的案例
- 其实非常简单,根本就没怎么动手做,因为官网的这个工程中已经帮我们把实例场景和脚本都写好了
- 核心就是上面那张时序图一样,先进行初始化,然后加入频道聊天就可以了!
- 下期我们再来整一个视频通话的小案例继续学习一下,其实原理都差不多,主要是代码有所不同,那我们下次再见啦!
以上是关于Unity实战篇 | 接入 声网SDK 实现 音频通话 —— 自己动手做一个 语音聊天房的主要内容,如果未能解决你的问题,请参考以下文章
Unity实战篇 | 接入 声网SDK 实现 视频通话——自己动手做一个 视频通话
Unity实战篇 | 接入 声网SDK 实现 视频通话——自己动手做一个 视频通话
Unity实战篇 | 接入 声网SDK 实现 视频通话——自己动手做一个 视频通话
Unity 结合 声网音频SDK做一个打地猫小游戏,还可以跟好友们边聊天边撸猫~