0. PhotoVoice 光子语音PhotonVoice | 光子引擎photonengine中文站
1. Vivox 来自 Vivox 的游戏内语音和文本聊天 SDK | Unity Multiplayer 服务
2. Agora 声网 声网 - 全球实时互动API平台开创者
3. Zego HarmonyOS Java 实时音视频概述 - 开发者中心 - ZEGO即构科技
Vivox是什么
-
Vivox是第三方语音和文本通信系统,常用于构建游戏项目的语音、实时聊天系统, 在 PC、移动或游戏主机平台上为玩家提供通信服务。
-
Vivox被Unity收购,因此做为独立的第三方通信服务商的同时,Vivox成为了Unity产品矩阵中所提供的一项服务,可以为游戏项目提供语音通信服务。
https://unity.com/products
https://unity.com/products/vivox
二、如何接入
使用Unity的Vivox服务来构建游戏项目中的聊天系统:
-
配置Unity客户端:Unity商店中购买并安装插件
-
购买配置vivox服务端:https://support.unity.com/hc/en-us/articles/6380084154772-Vivox-How-do-I-get-started-with-Vivox-in-my-Unity-Project-
-
调用SDK API接入项目。
https://docs.vivox.com/v5/general/unity/15_1_180000/en-us/Default.htm#Unity/Unity.htm
-
Service Relay
https://docs.unity.com/relay/introduction.html
Unity Relay 中继服务通过充当代理提供通用中继服务器连接,而无需维护第三方或专用游戏服务器 (DGS) 或担心架构点对点网络服务的复杂度。
中继服务有两个关键组件:the Relay servers 中继服务器and the Relay Allocations service中继分配服务。
-
Service Lobby
https://docs.unity.com/lobby/unity-lobby-service-overview.html
Lobby 聊天大厅服务提供了两个主要功能:公共大厅和私人大厅,以供玩家创建和查找游戏会话。
三、Sample Chat Channel
演示 Vivox SDK 如何集成到 Unity 游戏中。
https://docs.vivox.com/v5/general/unity/15_1_180000/en-us/Default.htm#Unity/chat-channel-sample/chat-channel-sample-overview.htm
四、Sample Chat Lobby
演示大厅聊天频道的游戏集成
https://github.com/Unity-Technologies/com.unity.services.samples.game-lobby
Agora声网
文档:实现语音通话
https://docs.agora.io/cn/live-streaming-premium-legacy/start_call_audio_unity
文档:使用自定义的音视频源或渲染器,实现相关场景:
https://docs.agora.io/cn/voice-legacy/custom_audio_unity?platform=Unity
文档:采集音频原始数据:
https://docs.agora.io/cn/live-streaming-premium-legacy/raw_data_audio_unity?platform=Unity
采集实现方法
音频传输过程中,我们可以获取采集到的音频原始数据。
Agora Unity SDK 通过提供 AudioRawDataManager
类,实现采集原始音频数据功能。
在使用原始音频数据功能前,请确保你已在项目中完成基本的实时音频功能,详见实现音频通话或实现音频直播。
参考如下步骤,在你的项目中实现原始音频数据功能:
-
加入频道前调用
RegisterAudioRawDataObserver
方法注册音频观测器。
-
成功注册后,根据需求调用以下方法:
-
调用
SetOnRecordAudioFrameCallback
监听OnRecordAudioFrameHandler
回调。SDK 会通过OnRecordAudioFrameHandler
回调向用户发送采集到的原始音频数据。 -
调用
SetOnPlaybackAudioFrameCallback
监听OnPlaybackAudioFrameHandler
回调。SDK 会通过OnPlaybackAudioFrameHandler
回调向用户发送播放的原始音频数据,即所有远端用户混音后的音频数据。 -
调用
SetOnMixedAudioFrameCallback
监听OnMixedAudioFrameHandler
回调。SDK 会通过OnMixedAudioFrameHandler
回调向用户发送混音后的采集和播放的原始音频数据,即所有本地和远端用户混音后的音频数据。 -
调用
SetOnPlaybackAudioFrameBeforeMixingCallback
监听OnPlaybackAudioFrameBeforeMixingHandler
回调。SDK 会通过OnPlaybackAudioFrameBeforeMixingHandler
回调向用户发送指定远端用户混音前的原始音频数据。
-
-
用户拿到音频数据后,根据场景需要自行进行处理。以通过 Unity
AudioSource
组件播放音频原始数据为例,大致流程如下:-
创建一个有限队列。
-
在步骤 2 中调用任意回调后,会返回
buffer
数据。在该队列后端插入回调返回的buffer
数据。 -
通过
AudioClip
组件的setData
方法,从该队列前端按顺序取出buffer
数据,并存入AudioClip
组件。 -
通过
AudioSource
组件播放AudioClip
组件中的数据。
-
-
离开频道后调用
UnRegisterAudioRawDataObserver
注销音频观测器。
API 调用时序
下图展示使用原始音频数据的 API 调用时序:
示例代码
你可以对照 API 时序图,参考下面的示例代码片段,在项目中实现原始音频数据功能:
void Start()
{// 初始化 IRtcEngine 对象。mRtcEngine = IRtcEngine.GetEngine(mVendorKey);// 获取 AudioRawDataManager 对象。AudioRawDataManager = AudioRawDataManager.GetInstance(mRtcEngine);// 注册音频观测器。mRtcEngine.RegisterAudioRawDataObserver();// 监听 OnRecordAudioFrameHandler delegate。AudioRawDataManager.SetOnRecordAudioFrameCallback(OnRecordAudioFrameHandler);// 监听 OnPlaybackAudioFrameHandler delegate。AudioRawDataManager.SetOnPlaybackAudioFrameCallback(OnPlaybackAudioFrameHandler);// 监听 OnMixedAudioFrameHandler delegate。AudioRawDataManager.SetOnMixedAudioFrameCallback(OnMixedAudioFrameHandler);// 监听 OnPlaybackAudioFrameBeforeMixingHandler delegate。AudioRawDataManager.SetOnPlaybackAudioFrameBeforeMixingCallback(OnPlaybackAudioFrameBeforeMixingHandler);
}// 获取本地采集到的原始音频数据。
void OnRecordAudioFrameHandler(AudioFrame audioFrame)
{Debug.Log("OnRecordAudioFrameHandler");
}// 获取从远端接收到的原始音频数据。
void OnPlaybackAudioFrameHandler(AudioFrame audioFrame)
{Debug.Log("OnPlaybackAudioFrameHandler");
}// 获取本地和远端混音后的原始音频数据。
void OnMixedAudioFrameHandler(AudioFrame audioFrame)
{Debug.Log("OnMixedAudioFrameHandler");
}// 获取指定本地或远端用户混音前的原始音频数据。
void OnPlaybackAudioFrameBeforeMixingHandler(uint uid, AudioFrame audioFrame)
{Debug.Log("OnPlaybackAudioFrameBeforeMixingHandler");
}public enum AUDIO_FRAME_TYPE
{// 0: PCM16FRAME_TYPE_PCM16 = 0,
};public struct AudioFrame
{// 音频帧类型。详见 #AUDIO_FRAME_TYPE 。public AUDIO_FRAME_TYPE type;// 每个声道的采样点数。public int samples; // 每个采样点的字节数。通常为十六位,即两个字节。public int bytesPerSample; // 声道数量(如果是立体声,数据是交叉的)// - 1: 单声道。// - 2: 双声道。public int channels; // 采样率。public int samplesPerSec; // 声音数据缓存区(如果是立体声,数据是交叉存储的)。缓存区数据大小:buffer = samples × channels × bytesPerSample。public byte[] buffer; // 外部音频帧的渲染时间戳。你可以使用该时间戳还原音频帧顺序;在有视频的场景中(包含使用外部视频源的场景),该参数可以用于实现音视频同步。public long renderTimeMs;// 预留参数。public int avsync_type;
};
public byte[] ConvertClipToBytes(AudioClip audioClip){float[] samples = new float[audioClip.samples];audioClip.GetData(samples, 0);short[] intData = new short[samples.Length];byte[] bytesData = new byte[samples.Length * 2];int rescaleFactor = 32767;for (int i = 0; i < samples.Length; i++){intData[i] = (short)(samples[i] * rescaleFactor);byte[] byteArr = new byte[2]; byteArr = BitConverter.GetBytes(intData[i]);byteArr.CopyTo(bytesData, i * 2);}return bytesData;}