TRTC 云助手 Windows(快速接入指引)

本节汇总了一些常登录与登出创建销毁房间音视频推流音视频拉流。

相关产品
TRTC 云助手 产品页,含版本规格、价格与购买入口。

三、步骤一:开通服务

大班课场景通常需要依赖腾讯云 即时通信 IM 和 实时音视频 TRTC 两项付费 PaaS 服务构建,互动白板可选或自建。

1. 首先,你需要登录 实时音视频 TRTC 控制台 创建应用,完成后在 即时通信 IM 控制台 即会自动创建一个对应的体验版 IM 应用。你可根据需要选择升级 TRTC 及 IM 应用版本,例如旗舰版可解锁更多增值功能服务。

注意:说明:

2. 创建应用完毕之后,你可以在应用管理-应用概览栏目看到该应用的基本信息,其中需要你保管好 SDKAppID、SDKSecretKey 便于后续的使用,同时应避免密钥泄露造成流量盗刷。

四、步骤二:导入SDK

分别把 TRTC SDK 和 IM SDK 的 Windows 端 zip 压缩包下载到本地并解压,可在项目根目录下创建一个目录 thirdparty 用于存放所有 SDK,把 TRTC SDK 和 IM SDK 移动到 thirdparty 目录下备用。

1. 使用 QTCreator 集成。

// *.proINCLUDEPATH += $$PWD/thirdparty/TRTC_SDK/CPlusPlus/Win64/include \\ $$PWD/thirdparty/TRTC_SDK/CPlusPlus/Win64/include/TRTC \\ $$PWD/thirdparty/IM_SDK/includeLIBS += -L'$$PWD/thirdparty/TRTC_SDK/CPlusPlus/Win64/lib' -lliteav \\ -L'$$PWD/thirdparty/IM_SDK/lib/Win64' -lImSDK

2. 使用 Visual Studio 集成。

添加头文件目录,配置 – C/C++ – 常规 – 附加头文件目录。

$(SolutionDir)thirdparty/TRTC_SDK/CPlusPlus/Win64/include$(SolutionDir)thirdparty/TRTC_SDK/CPlusPlus/Win64/include/TRTC$(SolutionDir)thirdparty/IM_SDK/include

添加库文件目录,配置 – 链接 – 常规 – 附加库目录。

$(SolutionDir)thirdparty/TRTC_SDK/CPlusPlus/Win64/lib$(SolutionDir)thirdparty/IM_SDK/lib/Win64

引用库文件。

#pragma comment(lib,"liteav.lib")#pragma comment(lib,"ImSDK.lib")

六、步骤一:生成鉴权凭证

UserSig 是腾讯云设计的一种安全保护签名,目的是为了阻止恶意攻击者盗用你的云服务使用权。腾讯云实时音视频(TRTC)、即时通信(IM)等服务都采用了该套安全保护机制,TRTC 在进房时鉴权,IM 在登录时鉴权。

·调试跑通阶段:可以通过 客户端示例代码 和 控制台 两种方法计算生成 UserSig,仅用于调试测试。

·正式运行阶段:推荐安全等级更高的服务端计算 UserSig方案,防止客户端被逆向破解泄露密钥。

具体实现流程如下:

1. 你的 App 在调用 SDK 的初始化函数之前,首先要向你的服务器请求 UserSig。

2. 你的服务器根据 SDKAppID 和 UserID 计算 UserSig。

3. 服务器将计算好的 UserSig 返回给你的 App。

4. 你的 App 将获得的 UserSig 通过特定 API 传递给 SDK。

5. SDK 将 SDKAppID + UserID + UserSig 提交给腾讯云服务器进行校验。

6. 腾讯云校验 UserSig,确认合法性。

7. 校验通过后,会向 IM SDK 提供即时通信服务、TRTC SDK 提供实时音视频服务。

注意:说明:

八、API 时序图

1. IM SDK 初始化与添加事件监听器。

IMSDK 是函数式的回调方式,把它封装一层,封装成回调类,方便使用。

// IMWrapperCallback.hclass IMWrapperCallback{public: virtual void OnLogin(int errCode, const char* errMsg) = 0; virtual void OnLogout(int errCode, const char* errMsg) = 0; virtual void OnError(int code, const char* errMsg) = 0; virtual void OnCreateGroup(int errCode, const char* errMsg) = 0; virtual void OnJoinGroup(int errCode, const char* errMsg) = 0; virtual void OnRecvNewMsg(const char* msg) = 0; // ……};// IMWrapper.hclass IMWrapper{public: IMWrapper(); bool InitIM(const char* path); bool UnInitIM(); void SetCallback(IMWrapperCallback* callback); bool LoginIM(const char* id, const char* sig); bool LogoutIM(); bool CreateGroup(const char* group_name, const char* group_type, const char* group_id); bool JoinGroup(const char* group_id); bool SendGroupTextMsg(const char* group_id, const char* text);private: IMWrapperCallback* m_callback; std::string m_userId;};// IMWrapper.cppbool IMWrapper::InitIM(const char* path){ Json::Value json_value_init; json_value_init[kTIMSdkConfigLogFilePath] = path; int nRet = TIMInit(SDKAppID_IM, json_value_init.toStyledString().c_str()); if(nRet != TIM_SUCC){ return false; } TIMAddRecvNewMsgCallback([](const char* json_param, const void* user_data) { if(user_data != nullptr){ IMWrapper* wrapper = (IMWrapper*)user_data; if(wrapper->m_callback != nullptr){ wrapper->m_callback->OnRecvNewMsg(json_param); } } }, this); return true;}

注意:说明:

注意:如果你的应用生命周期跟 SDK 生命周期一致,退出应用前可以不进行反初始化。若你只在进入特定界面后才初始化 SDK,退出界面后不再使用,可以对 SDK 进行反初始化。

2. TRTC SDK 创建实例与设置事件监听器。

//SmallClass.h#include "ITRTCCloud.h"class SmallClass : public ITRTCCloudCallback{public: SmallClass(); ~SmallClass(); virtual void onWarning(TXLiteAVWarning warningCode, const char* warningMsg, void* extraInfo) override; virtual void onError(TXLiteAVError errCode, const char *errMsg, void *extraInfo) override; virtual void onEnterRoom(int result) override; virtual void onExitRoom(int reason) override; //……}SmallClass::SmallClass(){ getTRTCShareInstance()->addCallback(this);//创建单例模式,设置事件监听}SmallClass::~SmallClass(){ getTRTCShareInstance()->removeCallback(this);//取消事件监听 destroyTRTCShareInstance();//销毁实例}

注意:说明:

注意:建议监听 SDK 事件通知,对一些常见错误进行日志打印和处理,详情请参见 错误码表。

九、步骤三:登录与登出

初始化 IM SDK 后,你需要调用 SDK 登录接口验证账号身份,获得账号的功能使用权限。因此在使用其他功能之前,请务必确保登录成功,否则可能导致功能异常或不可用。如你仅需使用 TRTC 音视频服务,可忽略此步骤。

十、API 时序图

// 登录:userID 可自定义,userSig 即步骤一生成获取bool IMWrapper::LoginIM(const char* id, const char* sig){ m_userId = id; int nRet = TIMLogin(id, sig, [](int32_t code, const char* desc, const char* json_param, const void* user_data) { if(user_data != nullptr){ IMWrapper* wrapper = (IMWrapper*)user_data; if(wrapper->m_callback != nullptr){ wrapper->m_callback->OnLogin(code, desc); } } }, this); if(nRet != TIM_SUCC){ return false; } return true;}// 登出bool IMWrapper::LogoutIM(){ int nRet = TIMLogout([](int32_t code, const char* desc, const char* json_param, const void* user_data) { if(user_data != nullptr){ IMWrapper* wrapper = (IMWrapper*)user_data; if(wrapper->m_callback != nullptr){ wrapper->m_callback->OnLogout(code, desc); } } }, this); if(nRet != TIM_SUCC){ return false; } return true;}

注意:说明:

注意:如果你的应用生命周期跟 IM SDK 生命周期一致,退出应用前可以不登出。若你只在进入特定界面后才使用 IM SDK,退出界面后不再使用,可以进行登出操作和对 IM SDK 进行反初始化。

十二、API 时序图

1. 创建房间。

主播(房主)开播时需要创建房间,这里的“房间”概念对应 IM 中的“群组”。本例展示客户端创建 IM 群组的方式,实际也可在服务端创建。

bool IMWrapper::CreateGroup(const char* name, const char* type, const char* id){ Json::Value param; //群id param[kTIMCreateGroupParamGroupId] = id; //群类型 if (strcmp(type, "Public") == 0) { param[kTIMCreateGroupParamGroupType] = kTIMGroup_Public; } else if(strcmp(type, "Work") == 0) { param[kTIMCreateGroupParamGroupType] = kTIMGroup_Private; } else if(strcmp(type, "Meeting") == 0) { param[kTIMCreateGroupParamGroupType] = kTIMGroup_ChatRoom; } else if(strcmp(type, "AVChatRoom") == 0) { param[kTIMCreateGroupParamGroupType] = kTIMGroup_AVChatRoom; //邀请进群方式 param[kTIMCreateGroupParamApproveOption] = kTIMGroupAddOpt_Forbid;//AnyJoin } //群名称 param[kTIMCreateGroupParamGroupName] = name; std::string createParams = param.toStyledString(); int nRet = TIMGroupCreate(createParams.c_str(), [](int32_t code, const char* desc, const char* json_params, const void* user_data) { if(user_data != nullptr){ IMWrapper* wrapper = (IMWrapper*)user_data; if(wrapper->m_callback != nullptr){ wrapper->m_callback->OnCreateGroup(code, desc); } } }, this); if(nRet != TIM_SUCC){ return false; } return true;}

注意:说明:

2. 进入房间。

·加入 IM 群组。

bool IMWrapper::JoinGroup(const char* id){ int nRet = TIMGroupJoin(id, "Want Join Group", [](int32_t code, const char* desc, const char* json_param, const void* user_data) { if(user_data != nullptr){ IMWrapper* wrapper = (IMWrapper*)user_data; if(wrapper->m_callback != nullptr){ wrapper->m_callback->OnJoinGroup(code, desc); } } }, this); if(nRet != TIM_SUCC){ return false; } return true;}

·进入 TRTC 房间。

// 进入房间void SmallClass::EnterSmallClass(String roomId, String userId, String userName, int roleType, String userSig){ TRTCParams param; param.sdkAppId = SDKAppID_TRTC; param.strRoomId = roomID.c_str(); param.userId = userID.c_str(); param.userSig = userSig.c_str(); getTRTCShareInstance()->enterRoom(param, TRTCAppSceneVideoCall);}// 进房结果事件回调void SmallClass::onEnterRoom(int result){ if (result > 0) { // 进房成功 } else { // 进房失败 }}

3. 退出房间。

// 退出房间void SmallClass::ExitSmallClass(){ getTRTCShareInstance()->exitRoom();}// 退房结果事件回调void SmallClass::onExitRoom(int reason){ // 0:主动调用 exitRoom 退出房间;1:被服务器踢出当前房间;2:当前房间整个被解散}

十三、步骤五:音频流管理

TRTC SDK 默认为自动订阅音频流逻辑,用户进房会自动开始播放远端用户的声音。如有手动订阅音频流的需求,请参考 设置订阅模式。

1. 老师端推流。

// 在进房成功后,开始推音频和视频流void SmallClass::onEnterRoom(int result){ if (result > 0) { // 进房成功 getTRTCShareInstance()->startLocalAudio(TRTCAudioQualitySpeech); getTRTCShareInstance()->startLocalPreview(hwndView); } else { // 进房失败 }}// 上课中途,暂停推流void SmallClass::onButtonClicked(){ getTRTCShareInstance()->muteLocalAudio(true); getTRTCShareInstance()->muteLocalVideo(TRTCVideoStreamTypeBig, true);}// 上课结束,停止推流void SmallClass::ExitSmallClass(){ getTRTCShareInstance()->stopLocalAudio(); getTRTCShareInstance()->stopLocalPreview(); getTRTCShareInstance()->exitRoom();}

2. 学生端拉流。

当收到远端流事件回调后,则可以开始拉流。

void SmallClass::onUserVideoAvailable(const char* userId, bool available){ if(available){ if(userId == 老师){ getTRTCShareInstance()->startRemoteView(userId, TRTCVideoStreamTypeBig, hwndView); //声音是自动接收的,不需要手工调用 } }}

十五、课堂状态管理

在小班课的教学过程中,有比较多状态需要进行管理,例如开始上课的时间点、屏幕分享状态、白板锁定状态、成员列表的静音状态等。这些状态需要进行有效的管理和同步,以确保教学过程的顺畅性和一致性。

为了实现课堂状态的管理,业务方可以在自己的后台中,使用 Redis 做一个状态缓存管理模块。这个模块可以用来存储和管理房间所有的状态信息,当客户端上报各种状态事件时,把这些事件缓存起来,以便后续查询和同步。当房间解散或者课堂结束后,把对应的房间缓存清空。

在课堂开始前,进入房间的学生一般不需要同步状态;课堂开始后,进入房间的学生则可以从业务后台获取当前房间的各种状态,这样就可以与先进入房间的学生状态保持一致了。

十六、API 时序图

以开始上课、结束上课状态管理为例。

// 老师端void SmallClass::onButtonClicked(){ m_isStartClass = !m_isStartClass; if(m_isStartClass){// 开始上课 // 发送群消息,广播状态 m_imWrapper.SendGroupMsg(groupId, msg); // 上报状态到自己的业务后台 m_roomStatus.SetStartTime(groupId, time); } else{// 上课结束 // 发送群消息,广播状态 m_imWrapper.SendGroupMsg(groupId, msg); // 上报状态到自己的业务后台 m_roomStatus.SetStartTime(groupId, time) }}// 学生端void SmallClass::onStartClass(msg){ // 计时器开始}void SmallClass::onEndClass(msg){ // 计时器结束}

十七、小班1V6视频窗口管理

在小班课场景中,老师窗口通常会固定在第一个位置,学生自己的窗口则会放在第二个位置,其他学生的窗口则按照他们进入房间的顺序排列。这种排列方式可以让老师更加方便地管理课堂,同时也方便学生进行互动和交流。

为了实现这一功能,每个用户都需要自己维护本地的窗口列表排序。当用户进房时,初始化本地列表管理,从业务后台获取到老师的 userId,自己的 userId,把列表的第一个位置设置为老师的 userId,第二个位置设置为自己的 userId,其他位置暂时留空。

当有用户进房时,从 TRTC 的 onRemoteUserEnterRoom 回调接口中获取到进房的 userId,通过 userId 去查找窗口列表,如果查找不到,则获取一个空闲的窗口。

初始化窗口列表:

void SmallClass::initListUserView(){ clearListUserView(); for (int i = 0; i < 7; i++) { UserVideoView *item = new UserVideoView; item->index = i; item->view = new VideoView(); item->view->setPosition(x+(i*w), y, w, h); if(i == 0){// 老师窗口固定在最左边 item->view->setUserInfo("teacherId", "teacherName"); } else if(i == 1){// 学生自己的窗口 if(m_userRole == Role_Student){ item->view->setUserInfo(m_userID, m_userName); } } m_listUserView.push_back(item); }}

当有远端流事件时,可以从 onUserVideoAvailable 回调获取 userId,获取对应的视频窗口。

void SmallClass::onUserVideoAvailable(const char* userId, bool available){ std::string strUserId = userId; if(available && (!strUserId.empty())){ VideoView *videoView = getVideoViewFromId(strUserId); if(videoView){ getTRTCShareInstance()->startRemoteView(userId, TRTCVideoStreamTypeBig, videoView->hwnd); } }}

十八、静音管理

上课过程中,老师常常需要静音全体学生,或者取消全体学生禁言,或单独对某个学生禁言。

当需要静音某个学生时,老师通过 IM 发送自定义的群消息,自定义的消息中包含学生 userId 和禁音状态。

收到群消息的学生,取出 userId,如果不是当前用户则忽略;如果是当前用户,则调用 muteLocalAudio接口静音本地声音,静音成功后,发送一条自定义群消息,广播静音成功的状态,其他用户收到这条消息后,就可以在 UI 上同步对应的状态了。

十九、API 时序图

void SmallClass::onMuteMic(std::string userId, bool mute){ if(userId == m_userID) getTRTCShareInstance()->muteLocalAudio(mute);}

20、网络质量实时回调

使用 onStatistics 获取推流及拉流的质量参数,可以通过丢包率判断当前网络质量,然后在视频窗口用网络信号图标表示当前网络质量。

这里判断的值为参考值,具体情况可以结合业务本身对网络情况的容忍度来设定范围。

void SmallClass::onStatistics (const TRTCStatistics& statistics){ // local unsigned int localLoss = statistics.upLoss; if(localLoss<3){ m_TeacherVideo.setQuality(3); }else if(localLoss>=3 && localLoss<5){ m_TeacherVideo.setQuality(2); }else if(localLoss>=5){ m_TeacherVideo.setQuality(1); } // remote int count = statistics.remoteStatisticsArraySize; TRTCRemoteStatistics* remoteStatisticsArray = statistics.remoteStatisticsArray; for(int i = 0; i < count; i++){ unsigned int remoteLoss = remoteStatisticsArray->videoPacketLoss; userId = remoteStatisticsArray->userId; if(remoteLoss<3){ m_StudentVideo[userId].setQuality(3); }else if(remoteLoss>=3 && remoteLoss<5){ m_StudentVideo[userId].setQuality(2); }else if(remoteLoss>=5){ m_StudentVideo[userId].setQuality(1); } remoteStatisticsArray++; }}

22、小班课 CPU 消耗高问题

1vN 的小班课,要解码多路视频,如果是自研电子白板,有可能会用到大量 WebView 加载白板教材,当机器性能跟不上,很容易导致 CPU 高。

以下是一组软解、D3D 硬解、OpenGL 硬解在 Windows 端下的测试,可查看 CPU 和 GPU 消耗对比。

渲染方式 帧率 分辨率 CPU (取最高值) GPU(取最高值)
D3D Texture 15fps 640×480 0.3% 1.7%
D3D Surface 15fps 640×480 0% 4.5%
GDI 双缓冲 15fps 640×480 0.7% 0%
OpenGL 15fps 640×480 0.7% 3.2%

·GPU 利用率越高、CPU 使用率越小,则方案越优。

·当使用自定义渲染的方式来显示视频,Windows 建议优先用 D3D 方式解码。

赞 (0)
上一篇 4天前
下一篇 4天前