实时视频通话能够拉近人与人之间的距离,为用户提供沉浸式的交流体验,帮助你的 app 提高用户黏性。
本文介绍如何通过少量代码集成声网视频 SDK ,在你的 app 里实现高质量、低延迟的视频通话功能。
下图展示在 app 中实现声网视频通话的基本工作流程:
实现视频通话的步骤如下:
1. 设置角色
将用户角色均设置为主播。
2. 加入频道
调用 joinChannel
创建并加入频道。在 App ID 一致的前提下,传入相同频道名的用户会进入同一个频道。
3、4. 在频道内发布和订阅音视频
加入频道后,两位主播可以发布音视频并互相订阅。
Microsoft Visual Studio 2017 或以上版本
Windows 7 或以上版本的设备
C++ 11 或以上版本
有效的声网账户(免费注册)
计算机可以访问互联网。请确保你的网络环境未部署防火墙,否则可能无法正常使用声网服务。
一个有效的声网账号以及声网项目。请参考 开始使用声网平台 从声网控制台获得以下信息:
参考以下操作,在你的 app 中实现视频通话功能:
参考创建 C++ 控制台应用项目,在 Visual Studio 上创建一个 C++ 控制台应用项目。
参考以下步骤将视频 SDK 集成到你的项目中。
sdk
文件夹中的所有子文件夹复制到你的解决方案文件夹下。保证这些子文件夹和你的 sln
文件处于同一目录。在解决方案资源管理器窗口中,右击项目名称并点击属性进行以下配置,配置完成后点击确定。
$(SolutionDir)include
。$(SolutionDir)lib
。agora_rtc_sdk.dll.lib
。本节介绍如何使用声网视频 SDK 在你的 app 中实现视频通话功能。API 的调用时序见下图:
创建用户界面
为直观地体验视频通话,需根据应用场景创建用户界面(UI)。若项目中已有用户界面,直接查看初始化 IRtcEngine。
如果你想实现一个视频通话,推荐在 UI 上添加以下控件:
当你使用示例项目中的 UI 设计时,你将会看到如下界面:
初始化 IRtcEngine
在调用其他声网 API 前,需要创建并初始化 IRtcEngine
对象。
调用 createAgoraRtcEngine
方法和 initialize
方法并传入 App ID,初始化 IRtcEngine
。
你也可以根据需求,在初始化时实现其他功能,如注册用户加入频道和离开频道的回调。
// 创建一个 IRtcEngine 实例。
m_lpAgoraEngine = createAgoraRtcEngine();
RtcEngineContext ctx;
// 添加注册回调和事件。
ctx.eventHandler = &m_engineEventHandler;
// 输入你的 App ID。你可以在声网控制台获取你的项目的 App ID。
ctx.appId = "Your App ID";
// 初始化 IRtcEngine。
m_lpAgoraEngine->initialize(ctx);
// 继承 IRtcEngineEventHandler 类中的回调与事件。
class CAGEngineEventHandler :
public IRtcEngineEventHandler
{
public:
CAGEngineEventHandler();
~CAGEngineEventHandler();
void setMainWnd(HWND wnd);
HWND GetMsgReceiver() {return m_hMainWnd;};
// 注册 onJoinChannelSuccess 回调。
// 本地用户成功加入频道时,会触发该回调。
virtual void onJoinChannelSuccess(const char* channel, uid_t uid, int elapsed);
// 注册 onLeaveChannel 回调。
// 本地主播成功离开频道时,会触发该回调。
virtual void onLeaveChannel(const RtcStats& stat);
// 注册 onUserJoined 回调。
// 远端主播成功加入频道时,会触发该回调。
// 收到该回调后,需立即调用 setupRemoteVideo 设置远端主播的视图。
virtual void onUserJoined(uid_t uid, int elapsed) override;
// 注册 onUserOffline 回调。
// 远端主播离开频道或掉线时,会触发该回调。
virtual void onUserOffline(uid_t uid, USER_OFFLINE_REASON_TYPE reason);
private:
HWND m_hMainWnd;
};
设置本地视图
在加入频道前设置本地视图,以便用户在通话中看到本地图像。参考以下步骤设置本地视图:
// 启用视频模块。
m_lpAgoraEngine->enableVideo();
// 开始本地预览。
m_lprtcEngine->startPreview();
// 设置本地视图。
VideoCanvas vc;
vc.uid = 0;
vc.view = hVideoWnd;
vc.renderMode = RENDER_MODE_TYPE::RENDER_MODE_HIDDEN;
m_lpAgoraEngine->setupLocalVideo(vc);
加入频道
设置频道场景和用户角色,并使用临时 token 加入频道。
void CLiveBroadcastingDlg::OnBnClickedButtonJoinchannel()
{
if (!m_rtcEngine || !m_initialize)
return;
CString strInfo;
if (!m_joinChannel) {
CString strChannelName;
m_edtChannelName.GetWindowText(strChannelName);
if (strChannelName.IsEmpty()) {
AfxMessageBox(_T("Fill channel name first"));
return;
}
VideoEncoderConfiguration config;
if (m_cmbVideoEncoder.GetCurSel() < 3)
config.codecType = (VIDEO_CODEC_TYPE)(m_cmbVideoEncoder.GetCurSel() + 1);
else
config.codecType = (VIDEO_CODEC_TYPE)(m_cmbVideoEncoder.GetCurSel() + 2);
m_rtcEngine->setVideoEncoderConfiguration(config);
std::string szChannelId = cs2utf8(strChannelName);
ChannelMediaOptions options;
// 将频道场景设置为 LIVE_BROADCASTING
options.channelProfile = CHANNEL_PROFILE_LIVE_BROADCASTING;
// 设置用户角色为主播
options.clientRoleType = CLIENT_ROLE_TYPE(m_cmbRole.GetCurSel() + 1);
options.autoSubscribeAudio = true;
options.autoSubscribeVideo = true;
// 使用你在声网控制台生成的临时 token 加入频道,在这里传入你的项目的 token 和频道名。
if (0 == m_rtcEngine->joinChannel(APP_TOKEN, szChannelId.c_str(), 0, options)) {
strInfo.Format(_T("join channel %s, use ChannelMediaOptions"), getCurrentTime());
m_btnJoinChannel.EnableWindow(FALSE);
}
}
else {
if (0 == m_rtcEngine->leaveChannel()) {
strInfo.Format(_T("leave channel %s"), getCurrentTime());
m_btnJoinChannel.EnableWindow(FALSE);
}
}
m_lstInfo.InsertString(m_lstInfo.GetCount(), strInfo);
}
设置远端视图
在加入频道后,可通过调用 setupRemoteVideo
方法设置远端主播的视图。
远端主播成功加入频道后,SDK 会触发 onUserJoined
回调,该回调中会包含这个主播的 uid
信息。在该回调中调用 setupRemoteVideo
方法,传入获取到的 uid
,设置该主播的视图。
// 远端主播成功加入频道时,会触发该回调。
// 收到该回调后,需立即调用 setupRemoteVideo 设置远端主播的视图。
void CAGEngineEventHandler::onUserJoined(uid_t uid, int elapsed)
{
LPAGE_USER_JOINED lpData = new AGE_USER_JOINED;
lpData->uid = uid;
lpData->elapsed = elapsed;
if(m_hMainWnd != NULL)
::PostMessage(m_hMainWnd, WM_MSGID(EID_USER_JOINED), (WPARAM)lpData, 0);
}
VideoCanvas canvas;
canvas.renderMode = RENDER_MODE_FIT;
POSITION pos = m_listWndInfo.GetHeadPosition();
......
AGVIDEO_WNDINFO &agvWndInfo = m_listWndInfo.GetNext(pos);
canvas.uid = agvWndInfo.nUID;
canvas.view = m_wndVideo[nIndex].GetSafeHwnd();
agvWndInfo.nIndex = nIndex;
// 设置远端视图。
CAgoraObject::GetEngine()->setupRemoteVideo(canvas);
离开频道
根据场景需要,如结束通话、关闭 App 或 App 切换至后台时,调用 leaveChannel
离开当前通话频道。
void CLiveBroadcastingDlg::UnInitAgora()
{
if (m_rtcEngine) {
if(m_joinChannel)
// 离开频道
m_rtcEngine->leaveChannel();
m_lstInfo.InsertString(m_lstInfo.GetCount(), _T("leaveChannel"));
// 停止视频预览.
m_rtcEngine->stopPreview();
m_lstInfo.InsertString(m_lstInfo.GetCount(), _T("stopPreview"));
// 关闭视频模块.
m_rtcEngine->disableVideo();
m_lstInfo.InsertString(m_lstInfo.GetCount(), _T("disableVideo"));
// 释放 IRtcEngine.
m_rtcEngine->release(true);
m_lstInfo.InsertString(m_lstInfo.GetCount(), _T("release rtc engine"));
m_rtcEngine = NULL;
}
}
在测试或生产环境中,为确保通信安全,声网推荐使用 token 服务器来生成 token,详见使用 Token 鉴权。
本节提供更多参考信息。
声网在 Github 上提供一个开源的示例项目 API Example 供你参考。