资讯专栏INFORMATION COLUMN

Unity实战篇 | 接入 声网SDK 实现 音频通话 —— 自己动手做一个 语音聊天房

rollback / 1221人阅读

摘要:年月声网正式成为了官方认证合作伙伴,语音和视频的也已经发布在了资源商店中,能够非常方便的接入。离开频道离开频道销毁查询版本号。

?‍?前言

  • 最近正好在网上看到声网,然后就顺道了解到了声网这个平台,发现声网的功能还真挺多呢
  • 也是一个兼容几十种平台的大公司啊,到现在才了解到,也算是相见恨晚~
  • 所以就赶紧来用 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;#endifusing 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);                }            }        }    }    ///     /// 加入频道    ///     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);    }    ///     ///  离开频道    ///     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();        }    }    ///     /// 查询 SDK 版本号。     ///     ///     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实现语音通话的案例
  • 其实非常简单,根本就没怎么动手做,因为官网的这个工程中已经帮我们把实例场景和脚本都写好了
  • 核心就是上面那张时序图一样,先进行初始化,然后加入频道聊天就可以了!
  • 下期我们再来整一个视频通话的小案例继续学习一下,其实原理都差不多,主要是代码有所不同,那我们下次再见啦!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/123157.html

相关文章

  • 如何快速实现FaceTime多人视频效果

    摘要:我们今天来分享几个往年的热门话题,并讲解基于声网如何来实现。如果基于声网来实现,我们只需要利用控制好对音频视频流的发送逻辑即可。如果你基于声网尝试实现了其它效果,并写在自己的博客中,欢迎在评论中分享。 一年一度的 WWDC 开幕了。我们看了来自各个勤劳媒体的回顾与解读。在这些新功能中,最吸引我们的还是最新更新的 FaceTime。 这次 FaceTime 不仅开始支持群组视频通话,还可...

    ashe 评论0 收藏0
  • Android端实现多人音视频聊天应用(一)

    作者:声网Agora用户,资深Android开发者吴东洋。本系列文章分享了基于Agora SDK 2.1实现多人视频通话的实践经验。 自从2016年,鼓吹互联网寒冬的论调甚嚣尘上,2017年亦有愈演愈烈之势。但连麦直播、在线抓娃娃、直播问答、远程狼人杀等类型的项目却异军突起,成了投资人的风口,创业者的蓝海和用户的必装App,而这些方向的项目都有一个共同的特点——都依赖视频通话和全互动直播技术。 声...

    raoyi 评论0 收藏0
  • 从0到1搭建视频通话系统,我1天就搞定了

    摘要:上述就是本文根据腾讯云官方文档指引,以为例总结的从零搭建视频通话服务的主要步骤,如有遇到其他问题,欢迎留言。 欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由腾讯云视频发表于云+社区专栏 showImg(https://segmentfault.com/img/remote/1460000016553455?w=1081&h=755); 本文作者,ericxwli,...

    tianlai 评论0 收藏0
  • 小程序直播连麦的技术实现与解析

    摘要:微信在去年年底开放了小程序直播接口。基于声网的小程序直播互动连麦方案开发的小程序,在的低延时实时网络环境下可实现与其它平台的无缝互通。 微信在去年年底开放了小程序直播接口。小程序从仅适用于阅读、生活服务、工具等应用的流量入口,成为了许多音视频应用的又一个新平台。新功能的开放让更多应用可以利用微信的熟人社交链为应用快速拉新,提供便捷的增值服务,或加速应用变现。我们的客户,荔枝 FM 就在...

    xiaoxiaozi 评论0 收藏0
  • Android端实现多人音视频聊天应用(二):实现多人通话

    摘要:我们先实现一个瀑布流瀑布流的实现方式很多,本文采用结合的来实现。有了一个可用的瀑布流之后,下面我们就可以实现动态聊天窗了动态聊天窗的要点在于的大小由视频的宽高比决定,因此及其对应的就该注意不要写死尺寸。 作者:声网用户,资深Android工程师吴东洋本系列文章分享了基于Agora SDK 2.1实现多人视频通话的实践经验。 在上一篇《Android 多人视频聊天应用的开发(一)一对一聊...

    The question 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<