实时语音开发指南
SDK 获取
可以在 下载页 获得 TapSDK,引入 TapRTC 模块:
- Unity
- Android
- iOS
请先确保系统已安装 git-lfs,然后通过 UPM 方式添加依赖:
"dependencies":{
...
"com.taptap.tds.rtc":"https://github.com/taptap/tapsdk-v3-unity.git?path=/RTC#3.30.3",
}
repositories{
flatDir {
dirs 'libs'
}
}
dependencies {
...
implementation (name:'TapRTC_1.1.0', ext:'aar')
}
- 在 Xcode 选择工程,到 Build Setting > Other Linker Flags 添加
-ObjC和-Wl -ld_classic。 - 拖拽
TapRTC_SDK目录到项目目录。 TapRTC.framework和GMESDK.framework文件拖进项目,选择Do Not Embed。- 将资源文件 TapRTC.bundle 拖进项目。
- 添加 SDK 依赖的系统库:libz、libresolv、libiconv、libc++、CoreMedia.framework、CoreAudio.framework、AVFoundation.framework、SystemConfiguration.framework、UIKit.framework、AudioToolbox.framework、OpenAL.framework、Security.framework。
TapRTC.framework
注意事项
- RTC 使用前需要在开发者中心进行相关配置。
- 开发者需要在自己的服务器上实现相应的签名鉴权服务。
- C# SDK 需要周期性的调用
TapRTC.Poll接口,才能触发相关的事件回调。
Android 平台需要申请网络、音频相关权限:
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
iOS 平台需要申请麦克风权限(Privacy - Microphone Usage Description)。
游戏通常没有后台通话的需求,玩家一般边玩游戏边语音,游戏保持在前台。 如果游戏类型比较特殊,需要支持后台通话,那么还需申请后台播放权限: 在 target 中配置 Capability > Background Modes > Audio, AirPlay, and Picture in Picture。
核心接口
RTC 通过 TapRTCConfig 来进行初始化配置。初始化的过程是异步的,需要等待初始化结果返回之后,才能进行下一步的操作。
ClientId、ClientToken、ServerUrl,参见关于应用凭证的说明。UserId: 开发者自定义的用户 Id,用于标记玩家DeviceId: 开发者自定义的设备 Id,用于标记设备AudioPerfProfile: 音频质量配置(LOW、MID、HIGH,默认为MID)
- Unity
- Android
- iOS
using TapTap.RTC;
var config = new TapRTCConfig.Builder()
.ClientID("ClientId")
.ClientToken("ClientToken")
.ServerUrl("ServerUrl")
.UserId("UserId")
.DeviceId("DeviceId")
.AudioProfile(AudioPerfProfile.MID)
.ConfigBuilder();
ResultCode code = await TapRTC.Init(config);
if (code == ResultCode.OK) {
// 初始化成功
} else {
// 失败
}
// SDK 的 RTC 模块中,返回 ResultCode 的接口均以 ResultCode.OK 表示操作成功。
import android.app.Application;
import com.taptap.taprtc.Config;
import com.taptap.taprtc.Config.AudioPerfProfile;
import com.taptap.taprtc.DeviceID;
import com.taptap.taprtc.UserID;
import com.taptap.taprtc.TapRTCEngine;
public class MyApp extends Application {
@Override
public void onCreate() {
super.onCreate();
Config config = new Config();
config.appId = "AppId";
config.appKey = "AppKey";
config.serverUrl = "ServerUrl"; // 请去掉 http/https 前缀,直接传入自定义域名或者形如「xxx.cloud.tds1.tapapis.cn」之类的域名即可。
config.userId = new UserID("UserId");
config.deviceId = new DeviceID("DeviceId");
// 如需使用范围语音功能,此项必须设为 LOW
config.profile = AudioPerfProfile.MID;
try {
TapRTCEngine.get().init(this, config, resultCode -> {
if (resultCode == ResultCode.OK) {
// 初始化成功
} else {
// 初始化失败
}
});
} catch (TapRTCException e) {
throw new RuntimeException(e);
}
}
}
// SDK 的 RTC 模块中,返回 ResultCode 的接口均以 ResultCode.OK 表示操作成功。
TapRTCConfig *config = [[TapRTCConfig alloc] initWithAppId:@"AppId"
appKey:@"AppKey" serverUrl:@"ServerUrl"
userId:@"UserId" deviceId:@"DeviceId"
profile:AudioPerfProfileMID];
TapRTCEngine *engine = [TapRTCEngine defaultEngine];
[engine initializeWithConfig:config resultBlock:^(NSError * _Nullable error) {
if (error) {
// handle error
}
})];
触发回调事件
C# SDK 需要在 Update 方法中调用 Poll 方法触发事件回调。如果不调用该方法,会导致 SDK 运行异常。
public void Update()
{
ResultCode code = TapRTC.Poll();
if (code == ResultCode.OK) {
// 成功触发回调
} else {
// 失败
}
}
Java SDK、Objective-C SDK 无需定期调用 Poll 方法。
恢复系统
- Unity
- Android
- iOS
ResultCode code = TapRTC.Resume();
if (code == ResultCode.OK) {
// 成功恢复
} else {
// 失败
}
import com.taptap.taprtc.TapRTCEngine;
ResultCode code = TapRTCEngine.get().resume();
TapRTCResultCode resultCode = [engine resume];
if (resultCode == TapRTCResultCode_Success) {
// resumed
} else {
// failed to resume
}
暂停系统
- Unity
- Android
- iOS
ResultCode code = TapRTC.Pause();
if (code == ResultCode.OK) {
// 成功暂停
} else {
// 失败
}
import com.taptap.taprtc.TapRTCEngine;
ResultCode code = TapRTCEngine.get().pause();
TapRTCResultCode resultCode = [engine pause];
if (resultCode == TapRTCResultCode_Success) {
// paused
} else {
// failed to pause
}
房间相关接口
创建房间
初始化成功之后,SDK 在创建房间之后,才可以进行实时语音通话。
创建房间时需指定房间号(roomId)。
是否启用范 围语音也需在创建房间时设置,C# SDK 默认不启用,Java SDK 创建时必须指定是否启用范围语音,Objective-C SDK 使用单独的接口创建范围语音房间。
- Unity
- Android
- iOS
bool enableRangeAudio = false;
var room = await TapRTC.AcquireRoom("roomId", enableRangeAudio);
import com.taptap.taprtc.TapRTCEngine;
import com.taptap.taprtc.RoomID;
RoomId roomId = new RoomID("roomId");
boolean enableRangeAudio = false;
TapRTCRoom room = TapRTCEngine.get().acquireRoom(roomId, enableRangeAudio);
TapRTCRoom *room = [engine acquireRoomWithRoomId:@"roomID"];
注册房间相关的回调事件
- Unity
- Android
- iOS
room.RegisterEventAction(new TapRTCEvent()
{
OnDisconnect = (code, message) => { label.text += "\n" + $"断开连接 code:{code} msg:{e}"; },
OnEnterFailure = s => { label.text += "\n" + $"进入房间失败:{s}"; },
OnEnterSuccess = () => { label.text += "\n" + $"进入房间成功"; },
OnExit = () => { label.text += "\n" + $"退出房间成功"; },
OnUserEnter = userId => { label.text += "\n" + $"玩家{userId}进入房间"; },
OnUserExit = userId => { label.text += "\n" + $"玩家{userId}退出房间"; },
OnUserSpeaker = (userId, volume) => { label.text += "\n" + $"玩家{userId}在房间说话,音量{volume}"; },
OnUserSpeakEnd = userId => { label.text += "\n" + $"玩家{userId}在房间说话结束"; },
// 返回切换后的音质,参见下文「切换音频质量」一节
OnRoomTypeChanged = (i) => { label.text += "\n" + $"房间音质改为{i}"; },
OnRoomQualityChanged = (weight, loss, delay) =>
{
Debug.Log($"音频质量:{weight} 丢包率:{loss}% 延迟:{delay}ms");
},
});
import com.taptap.taprtc.UserID;
import com.taptap.taprtc.TapRTCRoom;
room.registerCallback(new TapRTCRoom.Callback() {
// 进入房间成功
@Override public void onEnterSuccess() {}
// 进入房间失败
@Override public void onEnterFailure(String msg) {}
// 连接断开
@Override public void onDisconnect() {}
// 重连成功
@Override public void onReConnected() {}
// 当前玩家退出房间
@Override public void onExit() {}
// 其他玩家进入房间
@Override public void onUserEnter(UserID userId) {}
// 其他玩家退出房间
@Override public void onUserExit(UserID userId) {}
// 玩家说话开始
@Override public void onUserSpeakStart(UserID userId, int volume) {}
// 玩家说话结束
@Override public void onUserSpeakEnd(UserID userId) {}
// 音频质量变化
@Override public void onRoomQualityChanged(int weight, double loss, int delay) {}
});
// 需实现 TapRTCRoomDelegate
// 进入房间成功
- (void)onEnterSuccess;
// 进入房间失败
- (void)onEnterFailure:(NSError *)error;
// 其他玩家进入房间
- (void)onUsersEnter:(NSString *)userId;
// 其他玩家退出房间
- (void)onUsersExit:(NSString *)userId;
// 当前玩家退出房间
- (void)onExit;
// 连接断开
- (void)onDisconnect;
// 重连成功
- (void)onReconnected;
// 玩家说话开始
- (void)onUsersSpeakStart:(NSString *)userId volume:(NSInteger)volume;
// 玩家说话结束
- (void)onUsersSpeakEnd:(NSString *)userId;
// 音频质量变化(返回音频质量、丢包率、延迟)
- (void)onQualityCallBackWithWeight:(int)weight loss:(float)loss delay:(int)delay;
加入房间
使用服务端生成的鉴权信息进入房间。
进入房间成功之后,会通过 TapRTCEvent 中的 OnEnterSuccess 进行回调。
- Unity
- Android
- iOS
ResultCode code = await room.Join("authBuffer");
if (code == ResultCode.OK) {
// 成功加入
}
if (code == ResultCode.ERROR_ALREADY_IN_ROOM) {
// 玩家已经在此房间内
}
import com.taptap.taprtc.Authority;
Authority authBuffer = new Authority("authBuffer");
ResultCode code = room.join(authBuffer);
if (code == ResultCode.OK) {
// 成功加入
}
if (code == ResultCode.ERROR_ALREADY_IN_ROOM) {
// 玩家已经在此房间内
}
[room joinWithAuth:@"authBuffer"];
authBuffer 是在服务端生成的鉴权信息,详见下文服务端鉴权一节的说明。
退出房间
退出房间之后,会通过 TapRTCEvent 中的 OnExit 进行回调。
- Unity
- Android
- iOS
ResultCode code = room.Exit();
ResultCode code = room.exit();
TapRTCResultCode resultCode = [room exit];
if (resultCode == TapRTCResultCode_Success) {
// exited
} else {
// failed exit
}
收听某人语音(默认收听)
- Unity
- Android
- iOS
ResultCode code = room.EnableUserAudio("userId");
if (code == ResultCode.OK) {
// 成功
}
if (code == ResultCode.ERROR_USER_NOT_EXIST) {
// 玩家不存在
}
import com.taptap.taprtc.UserID;
UserId userId = new UserID("userId");
ResultCode code = room.enableUserAudio(userId);
if (code == ResultCode.OK) {
// 成功
}
if (code == ResultCode.ERROR_USER_NOT_EXIST) {
// 玩家不存在
}
TapRTCResultCode resultCode = [room enableUserAudioWithUserId:@"userId"];
拒收某人语音
- Unity
- Android
- iOS
ResultCode code = room.DisableUserAudio("userId");
if (code == ResultCode.OK) {
// 成功
}
if (code == ResultCode.ERROR_USER_NOT_EXIST) {
// 玩家不存在
}
import com.taptap.taprtc.UserID;
UserId userId = new UserID("userId");
ResultCode code = room.disableUserAudio(userId);
if (code == ResultCode.OK) {
// 成功
}
if (code == ResultCode.ERROR_USER_NOT_EXIST) {
// 玩家不存在
}
TapRTCResultCode resultCode = [room disableUserAudioWithUserId:@"userId"];
打开/关闭语音
这个接口设置音频下行的开关。 一般来说,推荐游戏使用打开/关闭扬声器的接口。
- Unity
- Android
- iOS
// 开启
ResultCode code = room.EnableAudioReceiver(true);
// 关闭
ResultCode code = room.EnableAudioReceiver(false);
// 开启
ResultCode code = room.enableAudioReceiver(true);
// 关闭
ResultCode code = room.enableAudioReceiver(false);
TapRTCResultCode resultCode = [room enableAudioReceiver:YES];
切换音频质量
音频质量分为 LOW、MID、HIGH 三档。
进入房间后,可以切换音频质量。
- Unity
- Android
- iOS
room.ChangeRoomType(AudioPerfProfile.LOW);
room.ChangeRoomType(AudioPerfProfile.MID);
room.ChangeRoomType(AudioPerfProfile.HIGH);
// 暂未支持
// 暂未支持
切换音频质量会触发 OnRoomTypeChanged 回调。
获取当前房间的用户
- Unity
- Android
- iOS
HashSet<string> userIdList = room.Users;
List<UserID> userIdList = room.getUsers();
[room getUsers:^(NSArray<NSString *>*userIDs, NSError * _Nullable error) {
if (error) {
// 处理错误
} else {
// userIDs 是房间内的用户 ID 列表
}
})];
音频控制相关接口
打开/关闭麦克风
- Unity
- Android
- iOS
// 打开
ResultCode code = TapRTC.GetAudioDevice().EnableMic(true);
// 关闭
ResultCode code = TapRTC.GetAudioDevice().EnableMic(false);
// 打开
boolean ok = TapRTCEngine.get().getAudioDevice().enableMic(true);
// 关闭
boolean ok = TapRTCEngine.get().getAudioDevice().enableMic(false);
// 打开
TapRTCResultCode code = [engine.audioDevice enableMic:YES];
// 关闭
TapRTCResultCode code = [engine.audioDevice enableMic:NO];