Unity 语音和视频通话快速解决方案——声网 SDK接入指南(Android)

Unity 语音和视频通话快速解决方案——声网 SDK接入指南(Android)

1、前言

当前游戏为了增长社交互动和代入感,好比狼人杀、团队竞技游戏等,常常会产生须要实时语音和视频通话的需求。可是对于我的开发者和小团队,这种须要先后端配合,重网络的开发需求会带来很大的挑战。android

为此咱们须要寻找一个成熟、可靠的解决方案。每月提供 10000 分钟无偿使用时长声网 Agora成为了个人最佳选择。而且声网的 SDK(Software Development Kit) 包体积很小,运行时CPU和内存占用率低,对于移动端的游戏开发很友好。2019年7月声网正式成为了 Unity 官方认证合做伙伴,语音和视频的 SDK 也已经发布在了 Unity 资源商店中,可以很是方便的接入。c#

注意:语音和视频的包有冲突,不兼容(一些库和平台配置不一样,能够本身手动修改),请根据需求,第四步和第五步二选一。引擎支持开关视频和声音,因此能够接入视频 SDK 包,关闭视频,仅仅使用语音通话。后端

2、后台建立应用

为了方便后续接入操做,这里先注册和登陆到 官方后台 建立应用,获取咱们以后须要的 App ID。能够根据官网的新手引导来建立应用,也能够参考以下步骤。api

输入项目名称,目前暂时使用 APP ID 的鉴权模式,后续根据须要也能够在项目编辑界面切换到 Token 鉴权模式,该模式更加安全,生成 Token 程序须要搭建在服务器上,能够参考官方文档安全

建立成功后,咱们点击 APP ID 下的显示按钮,APPID 就会复制到粘贴板。服务器

3、获取 SDK

这里我使用的 Unity 版本为 2019.3.14,进入商店咱们搜索 Agora,能够发现视频和语音两个 SDK 包。网络

若是打不开 Unity Store 的同窗还能够从官网开发者中心下载。app

4、接入 Agora Voice 语音 SDK

1. 导入工程

从 Asset Store 的个人资源(My Asset)中找到咱们下载的 Agora Voice SDK For Unity ,点击 Import 导入到工程中。编辑器


导入的文件结构以下。ide

  • Demo:官方提供的测试语音 Demo
  • Edior:iOS 构建后处理脚本
  • Plugins:不一样平台所依赖的库
  • Scripts:SDK 源码

2. 搭建测试场景

为了验证 Agora Voice 的效果,咱们打开官方的 Demo,或者是本身搭建一个相似的简单场景。主要有三个按钮,分别用来测试加入频道、离开频道和静音。

3. 申请麦克风权限

在 Unity 2018.3 版本之后的新版本中,须要咱们主动申请麦克风权限。

#if(UNITY_2018_3_OR_NEWER)
using UnityEngine.Android;
#endif
// ...
    private void PermissionRequest()
    {
#if (UNITY_2018_3_OR_NEWER)
			if (!Permission.HasUserAuthorizedPermission(Permission.Microphone))	// 判断是否有麦克风权限
			{
				Permission.RequestUserPermission(Permission.Microphone);	// 申请麦克风权限
			}
#endif
    }
// ...

4. 初始化 IRtcEngine

咱们在调用 Agora 的接口前,须要先初始化 IRtcEngine。此时咱们第二步获取的 APP ID,就在这里派上用处了。在经过 APP ID 建立 IRtcEngine 后,能够根据需求增长回调事件,这里我接入了一些比较经常使用的回调。

using agora_gaming_rtc;
// ...
    private IRtcEngine mRtcEngine = null;
    public const string APP_ID = "你本身的应用 APP ID";
	private void InitEngine()
    {
    	// 经过 APP ID 建立
        mRtcEngine = IRtcEngine.GetEngine(APP_ID);
        
        // 加入频道成功后的回调
        // channelName:频道名称
        // uid:用户ID(发起请求时候若是没有指定,服务器会自动分配一个)
        // elapsed:从本地用户调用 JoinChannelByKey 到该回调触发的延迟(毫秒)。
        mRtcEngine.OnJoinChannelSuccess += (string channelName, uint uid, int elapsed) =>
        {
            // ...
        };
		
        
        // 离开频道的回调
        // stats:通话统计的数据
        //		duration:通话时长
        //		txBytes:发送字节数(bytes)
        //		rxBytes:接收字节数(bytes)
        //		txKBitRate:发送码率(kbps)
        //		rxKBitRate:接收码率(kbps)
        mRtcEngine.OnLeaveChannel += (RtcStats stats) =>
        {
            string leaveChannelMessage = string.Format("onLeaveChannel callback duration {0}, tx: {1}, rx: {2}, tx kbps: {3}, rx kbps: {4}", stats.duration, stats.txBytes, stats.rxBytes, stats.txKBitRate, stats.rxKBitRate);
			// ...
        };
		
        // 用户加入回调
        // uid:新加入频道的远端用户/主播 ID
        // elapsed:从本地用户调用 JoinChannelByKey 到该回调触发的延迟(毫秒)。
        mRtcEngine.OnUserJoined += (uint uid, int elapsed) =>
        {
			// ...
        };
		
        // 用户离开回调
        // uid:离线用户或主播的用户 ID
        // reason:离线缘由(主动离开、超时、直播模式身份切换)
        mRtcEngine.OnUserOffline += (uint uid, USER_OFFLINE_REASON reason) =>
        {
            string userOfflineMessage = string.Format("onUserOffline callback uid {0} {1}", uid, reason);
            Debug.Log(userOfflineMessage);
        };
		
        // 提示频道内谁在说话
        // speakers:说话人信息
        // speakerNumber:说话人数[0,3]
        // totalVolume:总音量
        mRtcEngine.OnVolumeIndication += (AudioVolumeInfo[] speakers, int speakerNumber, int totalVolume) =>
        {
            // ...
        };
	
        // 用户静音提示回调
        // uid:用户 ID
        // muted:是否静音
        mRtcEngine.OnUserMutedAudio += (uint uid, bool muted) =>
        {
            // ...
        };
		
        // 发生警告回调
        mRtcEngine.OnWarning += (int warn, string msg) =>
        {
            // ...
        };
		
        // 发生错误回调
        mRtcEngine.OnError += (int error, string msg) =>
        {
            // ...
        };
		
        // 当前通话统计回调,每两秒触发一次。
        mRtcEngine.OnRtcStats += (RtcStats stats) =>
        {
            // ...
        };
		
        // 语音路由已发生变化回调。(只在移动平台生效)
        mRtcEngine.OnAudioRouteChanged += (AUDIO_ROUTE route) =>
        {
            // ...
        };
		
        // Token 过时回调
        mRtcEngine.OnRequestToken += () =>
        {
            // ...
        };
		
        // 网络中断回调(创建成功后才会触发)
        mRtcEngine.OnConnectionInterrupted += () =>
        {
            // ...
        };
		
        // 网络链接丢失回调
        mRtcEngine.OnConnectionLost += () =>
        {
            // ...
        };
		
	    // 设置 Log 级别
        mRtcEngine.SetLogFilter(LOG_FILTER.INFO);
		// 设置为自由说话模式,经常使用于一对一或者群聊
        mRtcEngine.SetChannelProfile(CHANNEL_PROFILE.CHANNEL_PROFILE_COMMUNICATION);
    }
...

5. 经常使用 API

5.1 加入频道

public void JoinChannel()
    {
        // 从界面的输入框获取频道名称
        string channelName = mChannelNameInputField.text.Trim();

        Debug.Log(string.Format("tap joinChannel with channel name {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);
    }

5.2 静音

void MuteButtonTapped()
    {
        string labeltext = isMuted ? "静音" : "取消静音";
        Text label = muteButton.GetComponentInChildren<Text>();
        if (label != null)
        {
            label.text = labeltext;
        }
        isMuted = !isMuted;
        // 设置静音(中止推送本地音频)
        mRtcEngine.MuteLocalAudioStream(!isMuted);
    }

5.3 离开频道 & 销毁 IRtcEngine

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();
            mRtcEngine = null;
        }
    }

6. 最终效果

咱们能够先在编辑器上验证,可以正常运行后,将平台切换到 Android 后,直接出包就行,Agora SDK 中已经提供了 Android 中所须要的库。

最后运行在 Android 上的效果以下:

  1. 输入1234 成功加入频道,分配给咱们用户 id:1186284123
  2. 有其余用户加入,用户id:2996662973
  3. 用户id:2996662973 开启静音
  4. 用户id:2996662973 离开频道
  5. 咱们离开频道,显示通话统计数据

语音通话的 API 时序图以下:

5、接入 Agora Video 视频 SDK

1. 导入工程

若是你按照第五步导入过音频了,这里相似直接从商店导入 Agora Video SDK。要注意的是两个包的内容不一样,固然两个 SDK 包的本质仍是相同的,只是不一样平台中的配置相关有些不一样,若是同时使用,会出现问题。有能力的同窗也能够尝试修改兼容,这里仍是比较推荐直接删除音频 Voice 的包,再导入新的 Video 的包,这样就不用咱们费尽的设置不一样平台配置了。

2. 搭建测试场景

一样的咱们能够直接使用Demo 中提供的场景,SceneHome 是启动场景,在最后测试的时候,注意须要修改 Build Setting 中场景列表。或者能够搭建一个简易以下的场景。

3. 申请麦克风+相机权限

与音频不一样,视频须要咱们添加相机权限的申请。

private void PermissionRequest () {
#if (UNITY_2018_3_OR_NEWER)
        if (!Permission.HasUserAuthorizedPermission (Permission.Microphone)) {
            Permission.RequestUserPermission (Permission.Microphone);
        }
        // 增长相机权限
        if (!Permission.HasUserAuthorizedPermission (Permission.Camera)) {
            Permission.RequestUserPermission (Permission.Camera);
        }
#endif
    }

4. 初始化 IRtcEngine

与音频惟一不一样的是须要打开视频功能。

private void InitEngine () {
        mRtcEngine = IRtcEngine.GetEngine (APP_ID);
        // 启用视频
        mRtcEngine.EnableVideo ();
        // 容许相机回调
        mRtcEngine.EnableVideoObserver ();

     // 后续与音频相同添加回调...
 }

5. 经常使用 API

5.1 设置本身画面

官方已经提供好了一个用于显示的 VideoSurface 类,咱们只要把它添加到须要显示的对象上便可,默认显示的即为本身相机拍摄画面。

private void CreateMyCamera()
    {
        GameObject myCamera = GameObject.Find("MyCamera");
        if (ReferenceEquals(myCamera, null))
        {
            Debug.LogError("没有找到 MyCamera 对象!");
            return;
        }
        else
        {
            // 添加显示画面类
            myCamera.AddComponent<VideoSurface>();
            // 画面须要垂直翻转
            myCamera.transform.Rotate(0f, 0.0f, 180.0f);
        }
    }

5.2 设置其余用户画面

private void CreateUserCamera(uint uid)
    {
        VideoSurface videoSurface;
        GameObject userCamera = GameObject.Find("UserCamera");
        if (ReferenceEquals(userCamera, null))
        {
            Debug.LogError("没有找到 UserCamera 对象!");
            return;
        }
        else
        {
            videoSurface = userCamera.AddComponent<VideoSurface>();
            userCamera.transform.Rotate(0f, 0.0f, 180.0f);

        }
        // 设置显示用户
        videoSurface.SetForUser(uid);
        videoSurface.SetEnable(true);
        // 设置平面类型
        videoSurface.SetVideoSurfaceType(AgoraVideoSurfaceType.RawImage);
        // 设置画面帧率
        videoSurface.SetGameFps(30);
    }

5.3 开关视频

咱们能够在应用暂停的时候,中止视频,画面将不会再更新。

public void EnableVideo(bool pauseVideo)
    {
        if (mRtcEngine != null)
        {
            if (!pauseVideo)
            {
                // 启动视频
                mRtcEngine.EnableVideo();
            }
            else
            {
                // 关闭视频
                mRtcEngine.DisableVideo();
            }
        }
    }

6. 最终效果

最后在 Android 上运行的效果以下:

  1. 咱们加入到 123 频道
  2. 给咱们分配id 722438456,并显示出咱们本身的画面
  3. 显示频道内另外一个用户 1173951071 的画面
  4. 离开频道,视频画面中止

视频通话的 API 时序图以下:

6、总结

总的来讲,声网的接入仍是较为简单的。咱们能够从商店导入对应的包,就可以直接使用,最麻烦的不一样平台的配置,官方已经帮忙解决了。并且通话和视频质量不错,再加上有无偿使用时长,对于开发者来讲是至关友好的。具体细节官方文档说的也比较清楚。

几行就搞定了麻烦的语音和视频,光速下班,真香。

相关文章
相关标签/搜索