本文介绍如何在项目中接入GME 实时语音功能,为了方便演示,只提供了基础功能的接入,其他功能的使用请参考API 文档。
前置条件:
已完成 GME 应用的创建,并获取SDK AppID 和 Key。请参见 服务开通指引。
已开通 GME 实时语音服务,请参见 服务开通指引。
已将 GME SDK导入到项目中,请参见 导入 SDK 到 Android 项目。
接口调用流程图

调用指引
步骤1:引入头文件
#include "tmg_sdk.h"
import com.gme.TMG.ITMGContext;
#import "GMESDK/TMGEngine.h"
步骤2:获取SDK实例并设置事件监听器
gmeContext = ITMGContextGetInstance();gmeContext->SetTMGDelegate(this);
gmeContext = ITMGContext.GetInstance(this);gmeContext.SetTMGDelegate(this);
gmeContext = [ITMGContext GetInstance];[[ITMGContext GetInstance] setTMGDelegate:self];
步骤3:监听 SDK 的事件
通过设置事件回调接口,您可以监听 SDK 在运行期间所发生的各种音视事件。
// 我们可以让自己的类继承 ITMGDelegate,并重载OnEvent函数,然后在该函数中处理需要响应的事件// 重载OnEvent函数void OnEvent(ITMG_MAIN_EVENT_TYPE eventType, const char* data) {switch (eventType) {case ITMG_MAIN_EVENT_TYPE_ENTER_ROOM:// 进房完成事件break;case ITMG_MAIN_EVENT_TYPE_EXIT_ROOM:// 退房完成事件break;}}
// 我们可以让自己的类继承 ITMGContext.ITMGDelegate,并重载OnEvent函数,然后在该函数中处理需要响应的事件// 重载OnEvent函数public void OnEvent(ITMGType.ITMG_MAIN_EVENT_TYPE eventType, Intent data) {switch (eventType) {case ITMGType.ITMG_MAIN_EVENT_TYPE.ITMG_MAIN_EVENT_TYPE_ENTER_ROOM:// 进房完成事件break;case ITMGType.ITMG_MAIN_EVENT_TYPE.ITMG_MAIN_EVENT_TYPE_EXIT_ROOM:// 退房完成事件break;}}
// 我们可以让自己的类继承 ITMGDelegate,并重载OnEvent函数,然后在该函数中处理需要响应的事件// 重载OnEvent函数- (void)OnEvent:(ITMG_MAIN_EVENT_TYPE)eventType data:(NSDictionary *)data {switch (eventType) {case ITMG_MAIN_EVENT_TYPE_ENTER_ROOM:// 进房完成事件break;case ITMG_MAIN_EVENT_TYPE_EXIT_ROOM:// 退房完成事件break;}}
步骤4:初始化SDK
在使用 GME SDK 相关功能之前必须先对 SDK 进行初始化。
gmeContext = ITMGContextGetInstance();int ret = gmeContext->Init(sdkAppId, userID);if (ret== AV_OK) {// 初始化成功} else {// 初始化失败}
gmeContext = ITMGContext.GetInstance(this);int ret = gmeContext.Init(sdkAppId, userID);if (ret == AV_OK) {// 初始化成功} else {// 初始化失败}
gmeContext = [ITMGContext GetInstance];int result = [gmeContext InitEngine:sdkAppId openID:userID];if (ret == QAV_OK) {// 初始化成功} else {// 初始化失败}
步骤5:触发事件回调
周期性(例如每30ms)调用 Poll 接口可以触发 GME 的事件回调。Poll 是 GME 的消息泵,GME 需要周期性的调用 Poll 接口触发事件回调,如果没有调用 Poll ,将会导致整个 SDK 服务运行异常,可参考 GME Sample Code 中 EnginePollHelper 的实现。
gmeContext = ITMGContextGetInstance();gmeContext ->Poll();
gmeContext = ITMGContext.GetInstance(this);gmeContext.Poll();
gmeContext = [ITMGContext GetInstance];[gmeContext Poll];
步骤6:计算鉴权信息
在调用进房接口时需要传入进房鉴权票据(UserSig),该鉴权票据是通过 SDKAppID 和 userId 等信息计算得到的;开发调试时您可使用 SDK 提供的接口生成userSig(为了方便 2.X 版本的升级,SDK 中计算 UserSig 的接口依然命名为 AuthBuffer),应用上线发布时建议使用服务器生成鉴权信息,计算方法请参见 UserSig 相关 。
// 接口原型// int GMESDK_CALL QAVSDK_AuthBuffer_GenAuthBuffer(unsigned int appId, const char* roomId, const char* userId, const char* key, unsigned char* authBuffer, unsigned int authBufferLen);int userSigLen = 0;unsigned char userSig[1024] = {0};userSigLen = QAVSDK_AuthBuffer_GenAuthBuffer(1400******, "", "1000", "key*****", userSig, userSigLen);
import com.gme.av.sig.AuthBuffer;// 接口原型// public byte[] genAuthBuffer(int appId, String roomId, String userId, String appKey)byte[] userSig;userSig = AuthBuffer.getInstance().genAuthBuffer(1400******, "", "1000", "key*****");
#import "GMESDK/QAVAuthBuffer.h"// 接口原型// + (NSData*)GenAuthBuffer:(unsigned int)appId roomID:(NSString*)roomID openID:(NSString*)openID key:(NSString*)key;NSData* userSig = [QAVAuthBuffer GenAuthBuffer:1400****** roomID:@"" openID:@"1000" key:@"key*****"];
步骤7:加入实时语音房间
调用EnterRoom接口加入到实时语音房间,返回值为AV_OK只表示该次函数调用成功,并不代表进房成功。
// 接口原型// int EnterRoom(const char* room_id, ITMG_ROOM_TYPE room_type, const char* userSig, int userSigLen)gmeContext = ITMGContextGetInstance();gmeContext->EnterRoom("roomid_001", ITMG_ROOM_TYPE_FLUENCY, userSig, userSigLen);
// 接口原型// public abstract int EnterRoom(String roomId, ITMG_ROOM_TYPE roomType, byte[] userSig);gmeContext = ITMGContext.GetInstance(this);gmeContext.EnterRoom("roomid_001", ITMGType.ITMG_ROOM_TYPE.ITMG_ROOM_TYPE_FLUENCY, userSig);
// 接口原型// - (int)EnterRoom:(NSString *)roomID roomType:(ITMG_ROOM_TYPE)roomType authBuffer:(NSData *)userSig;gmeContext = [ITMGContext GetInstance];[gmeContext EnterRoom:@"roomid_001" roomType:ITMG_ROOM_TYPE_FLUENCY authBuffer:userSig];
参数名称 | 类型 | 含义 |
room_id | 字符串 | 房间号,最大支持127字符 |
room_type | 枚举值 | 房间类型,代表了房间的音频质量,每个用户都可设置自己房间类型,不会影响其他用户,房间类型有下面3种可选
ITMG_ROOM_TYPE_FLUENCY:流畅音质,端到端延迟小,适合游戏开黑场景
ITMG_ROOM_TYPE_STANDARD:标准音质,端到端延迟中等,适合狼人杀场景
ITMG_ROOM_TYPE_HIGHQUALITY:高清音质,端到端延时相对大一些,适合娱乐连麦场景 |
userSig | 字符串 | |
userSigLen | 数字 | 进房鉴权票据长度(仅C++版本需要该参数) |
步骤8:处理进房完成事件
加入实时语音房间是一个异步过程,GME SDK 会将进房结果通过事件回调的方式通知给上层应用,上层应用需要处理ITMG_MAIN_EVENT_TYPE_ENTER_ROOM 事件。
// 重载OnEvent函数void OnEvent(ITMG_MAIN_EVENT_TYPE eventType, const char* data) {switch (eventType) {case ITMG_MAIN_EVENT_TYPE_ENTER_ROOM: // 进房完成事件// data字段是一个Json串,形式如:{"result":0, "error_info":""}// 解析中data中的字段int result = GetItemFromJson(data, "result");if (result == AV_OK) {// 进房成功} else {// 进房失败}break;}}
// 重载OnEvent函数public void OnEvent(ITMGType.ITMG_MAIN_EVENT_TYPE eventType, Intent data) {switch (eventType) {case ITMGType.ITMG_MAIN_EVENT_TYPE.ITMG_MAIN_EVENT_TYPE_ENTER_ROOM: // 进房完成事件int result = data.getIntExtra("result" , -1);String error_info = data.getStringExtra("error_info");if (result == AVError.AV_OK) {// 进房成功} else {// 进房失败}break;}}
// 重载OnEvent函数- (void)OnEvent:(ITMG_MAIN_EVENT_TYPE)eventType data:(NSDictionary *)data {switch (eventType) {case ITMG_MAIN_EVENT_TYPE_ENTER_ROOM: // 进房完成事件int result = ((NSNumber *)[data objectForKey:@"result"]).intValue;NSString *error_info = [data objectForKey:@"error_info"];if (result == QAV_OK) {// 进房成功} else {// 进房失败}break;}}
步骤9:开启或关闭麦克风
GME SDK 中麦克风默认是关闭的,在加入实时语音房间成功后,可以调用 EnableMic 接口打开或关闭麦克风。
gmeContext = ITMGContextGetInstance();gmeContext->GetAudioCtrl()->EnableMic(true);
gmeContext = ITMGContext.GetInstance(this);gmeContext.GetAudioCtrl().EnableMic(true);
gmeContext = [ITMGContext GetInstance];[[gmeContext GetAudioCtrl] EnableMic:YES];
步骤10:开启或关闭扬声器
GME SDK 中扬声器默认是关闭的,在加入实时语音房间成功后,可以调用 EnableSpeaker 接口打开或关闭麦克风。
gmeContext = ITMGContextGetInstance();gmeContext->GetAudioCtrl()->EnableSpeaker(true);
gmeContext = ITMGContext.GetInstance(this);gmeContext.GetAudioCtrl().EnableSpeaker(true);
gmeContext = [ITMGContext GetInstance];[[gmeContext GetAudioCtrl] EnableSpeaker:YES];
步骤11:处理房间内成员状态变化事件【可选】
当语音房间内其他用户的状态(有成员进入/退出房间、开始/停止发送音频数据)发生变化时,上层应用会收到 ITMG_MAIN_EVNET_TYPE_USER_UPDATE 事件,其中 data 字段包含两个信息:event_id 及 user_list,event_id用来表示具体是哪一种事件,user_list 里面包含了发生该事件的用户 ID。SDK 不提供房间内所有成员列表的获取接口,需要上层应用通过进入/退出房间事件来自己维护房间内成员列表。
event_id | 含义 | 应用侧维护内容 |
ITMG_EVENT_ID_USER_ENTER | 有成员进入房间,返回此时进房的 openid | 应用侧维护成员列表 |
ITMG_EVENT_ID_USER_EXIT | 有成员退出房间,返回此时退房的 openid | 应用侧维护成员列表 |
ITMG_EVENT_ID_USER_HAS_AUDIO | 有成员发送音频包,返回此时房间内说话的 openid,通过此事件可以判断用户是否说话,并展示声纹效果 | 应用侧维护成员列表 |
ITMG_EVENT_ID_USER_NO_AUDIO | 有成员停止发送音频包,返回此时房间内停止说话的 openid | 应用侧维护成员列表 |
// 重载OnEvent函数void OnEvent(ITMG_MAIN_EVENT_TYPE eventType, const char* data) {switch (eventType) {case ITMG_MAIN_EVNET_TYPE_USER_UPDATE: // 房间内成员状态发生变化// data字段是一个Json串,形式如:{"event_id":0, "user_list":[]}// 解析中data中的字段int event_id = GetItemFromJson(data, "event_id");switch (event_id ) {case ITMG_EVENT_ID_USER_ENTER:// 有成员进入房间break;case ITMG_EVENT_ID_USER_EXIT:// 有成员退出房间break;case ITMG_EVENT_ID_USER_HAS_AUDIO:// 有成员发送音频数据break;case ITMG_EVENT_ID_USER_NO_AUDIO:// 有成员停止发送音频数据break;default:break;}}break;}}
// 重载OnEvent函数public void OnEvent(ITMGType.ITMG_MAIN_EVENT_TYPE eventType, Intent data) {switch (eventType) {case ITMGType.ITMG_MAIN_EVENT_TYPE.ITMG_MAIN_EVNET_TYPE_USER_UPDATE: // 房间内成员状态发生变化int event_id= data.getIntExtra("event_id" , -1);String[] strUsers = data.getStringArrayExtra("user_list");if (event_id == ITMGType.ITMG_EVENT_ID_USER_UPDATE.ITMG_EVENT_ID_USER_ENTER.getNativeValue()) {// 有成员进入房间} else if (event_id == ITMGType.ITMG_EVENT_ID_USER_UPDATE.ITMG_EVENT_ID_USER_EXIT.getNativeValue()){// 有成员退出房间} else if (event_id == ITMGType.ITMG_EVENT_ID_USER_UPDATE.ITMG_EVENT_ID_USER_HAS_AUDIO.getNativeValue()){// 有成员发送音频数据} else if (event_id == ITMGType.ITMG_EVENT_ID_USER_UPDATE.ITMG_EVENT_ID_USER_NO_AUDIO.getNativeValue()){// 有成员停止发送音频数据}break;}}
// 重载OnEvent函数- (void)OnEvent:(ITMG_MAIN_EVENT_TYPE)eventType data:(NSDictionary *)data {switch (eventType) {case ITMG_MAIN_EVNET_TYPE_USER_UPDATE: // 进房完成事件int event_id = ((NSNumber *)[data objectForKey:@"event_id"]).intValue;NSMutableArray *user_list = [NSMutableArray arrayWithArray:[data objectForKey:@"user_list"]];switch (event_id) {case ITMG_EVENT_ID_USER_ENTER:// 有成员进入房间break;case ITMG_EVENT_ID_USER_EXIT:// 有成员退出房间break;case ITMG_EVENT_ID_USER_HAS_AUDIO:// 有成员发送音频数据break;case ITMG_EVENT_ID_USER_NO_AUDIO:// 有成员停止发送音频数据break;default:break;}break;}}
步骤12:退出实时语音房间
在使用完实时语音供后需要从房间里面退出时,调用 ExitRoom 接口即可。
gmeContext = ITMGContextGetInstance();gmeContext->ExitRoom();
gmeContext = ITMGContext.GetInstance(this);gmeContext.ExitRoom();
gmeContext = [ITMGContext GetInstance];[gmeContext ExitRoom];
步骤13:处理退房完成事件
退出实时语音房间是一个异步过程,GME SDK 会将进房结果通过事件回调的方式通知给上层应用,上层应用需要处理ITMG_MAIN_EVENT_TYPE_EXIT_ROOM事件。
// 重载OnEvent函数void OnEvent(ITMG_MAIN_EVENT_TYPE eventType, const char* data) {switch (eventType) {case ITMG_MAIN_EVENT_TYPE_EXIT_ROOM: // 退房完成事件// 退房完成break;}}
// 重载OnEvent函数public void OnEvent(ITMGType.ITMG_MAIN_EVENT_TYPE eventType, Intent data) {switch (eventType) {case ITMGType.ITMG_MAIN_EVENT_TYPE.ITMG_MAIN_EVENT_TYPE_EXIT_ROOM: // 退房完成事件// 退房完成break;}}
// 重载OnEvent函数- (void)OnEvent:(ITMG_MAIN_EVENT_TYPE)eventType data:(NSDictionary *)data {switch (eventType) {case ITMG_MAIN_EVENT_TYPE_EXIT_ROOM: // 退房完成事件// 退房完成break;}}
步骤14:反初始化SDK
在使用完 GME 的相关功能之后可以调用 Uninit 接口反初始化 SDK。
注意:
如果您在调用 Uninit 接口后会立即停止调用 Poll,需要在停止之前主动调用一次 Poll,将 SDK 内部的事件都清空,不然会有事件积累在 SDK 内部。
gmeContext = ITMGContextGetInstance();gmeContext->Uninit();
gmeContext = ITMGContext.GetInstance(this);gmeContext.Uninit();
gmeContext = [ITMGContext GetInstance];[gmeContext Uninit];
至此,GME 的实时语音供就集成进您的应用中了,如需使用其他功能,可参见相关 API 文档。