基于 Agora SDK 实现 Windows 端的一对一视频通话
前提条件
- Microsoft Visual Studio 2017 或以上版本
- 支持 Windows 7 或以上版本的 Windows 设备
- 有效的 Agora 账户(免费注册)
注:如果你的网络环境部署了防火墙,请根据声网文档中心的「应用企业防火墙限制」打开相关端口。
设置开发环境
本节介绍如何创建项目,并将 Agora SDK 集成至你的项目中。
创建 Windows 项目
参考以下步骤创建一个 Windows 项目。若已有 Windows 项目,直接查看集成 SDK。
创建 Windows 项目
- 打开 Microsoft Visual Studio 并点击新建项目。
- 进入新建项目窗口,选择项目类型为 MFC 应用程序,输入项目名称,选择项目存储路径,并点击确认。
- 进入MFC 应用程序窗口,选择应用程序类型为基于对话框,并点击完成。
集成 SDK
参考以下步骤将 Agora SDK 集成到你的项目中。
- 配置项目文件
- 根据应用场景,从 官网获取最新 SDK,解压并打开。
- 将下载包中的 sdk 文件夹复制到你的项目文件夹下。
- 配置项目属性
在解决方案资源管理器窗口中,右击项目名称并点击属性进行以下配置,配置完成后点击确定。
- 进入 C/C++ > 常规 > 附加包含目录菜单,点击编辑,并在弹出窗口中输入 $(SolutionDir)include。
- 进入链接器 > 常规 > 附加库目录菜单,点击编辑,并在弹出窗口中输入 $(SolutionDir)。
- 进入链接器 > 输入 > 附加依赖项菜单,点击编辑,并在弹出窗口中输入 agora_rtc_sdk.lib。
实现音视频通话
本节介绍如何实现音视频通话。视频通话的 API 调用时序见下图:
1. 创建用户界面
根据场景需要,为你的项目创建音视频通话的用户界面。若已有用户界面,可以直接初始化 IRtcEngine。
如果你想实现一个视频通话,我们推荐你添加如下 UI 元素:
- 本地视频窗口
- 远端视频窗口
- 结束通话按钮
当你使用示例项目中的 UI 设计时,你将会看到如下界面:
2. 初始化 IRtcEngine
在调用其他 Agora API 前,需要创建并初始化 IRtcEngine 对象。
你需要在该步骤中填入项目的 App ID。请参考如下步骤在控制台(console.agora.io)创建 Agora 项目并获取 App ID:
- 登录控制台,点击左侧导航栏的项目管理图标
。
- 点击创建,按照屏幕提示设置项目名,选择一种鉴权机制,然后点击提交。
- 在项目管理页面,你可以获取该项目的 App ID。
调用 createAgoraRtcEngine
和 initialize
方法,传入获取到的 App ID,即可初始化 IRtcEngine
。
你还可以根据场景需要,在初始化时注册想要监听的回调事件,如本地用户加入频道,及解码远端用户视频首帧等。
CAgoraObject *CAgoraObject::GetAgoraObject(LPCTSTR lpAppId)
{ if (m_lpAgoraObject == NULL)
m_lpAgoraObject = new CAgoraObject(); if (m_lpAgoraEngine == NULL) // 创建实例。
m_lpAgoraEngine = (IRtcEngine *)createAgoraRtcEngine(); if (lpAppId == NULL) return m_lpAgoraObject;
RtcEngineContext ctx; // 添加注册回调和事件。
ctx.eventHandler = &m_EngineEventHandler;#ifdef UNICODE
char szAppId[128];
::WideCharToMultiByte(CP_ACP, 0, lpAppId, -1, szAppId, 128, NULL, NULL); // 输入你的 App ID。
ctx.appId = szAppId;#else
ctx.appId = lpAppId;#endif
// 初始化 IRtcEngine。
m_lpAgoraEngine->initialize(ctx); return m_lpAgoraObject;
}
// 继承 IRtcEngineEventHandler 类中的回调与事件。class CAGEngineEventHandler :
public IRtcEngineEventHandler
{public:
CAGEngineEventHandler(void);
~CAGEngineEventHandler(void); void SetMsgReceiver(HWND hWnd = NULL); HWND GetMsgReceiver() {return m_hMainWnd;}; // 注册 onJoinChannelSuccess 回调。
// 本地用户成功加入频道时,会触发该回调。
virtual void onJoinChannelSuccess(const char* channel, uid_t uid, int elapsed); // 注册 onLeaveChannel 回调。
// 本地用户成功离开频道时,会触发该回调。
virtual void onLeaveChannel(const RtcStats& stat); // 注册 onFirstRemoteVideoDecoded 回调。
// SDK 接收到第一帧远端视频并成功解码时,会触发该回调。
// 可以在该回调中调用 setupRemoteVideo 方法设置远端视图。
virtual void onFirstRemoteVideoDecoded(uid_t uid, int width, int height, int elapsed); // 注册 onUserOffline 回调。
// 远端用户离开频道或掉线时,会触发该回调。
virtual void onUserOffline(uid_t uid, USER_OFFLINE_REASON_TYPE reason);private:
HWND m_hMainWnd;
};
3. 设置本地视图
如果你想实现一个语音通话,可以直接跳过这一步,查看下一步「加入频道」。
成功初始化 IRtcEngine 对象后,需要在加入频道前设置本地视图,以便在通话中看到本地图像。参考以下步骤设置本地视图:
- 调用
enableVideo
方法启用视频模块。 - 调用
setupLocalVideo
方法设置本地视图。
// 启用视频模块。m_lpAgoraObject->GetEngine()->enableVideo();// 设置本地视图。VideoCanvas vc;
vc.uid = 0;
vc.view = m_wndLocal.GetSafeHwnd();
vc.renderMode = RENDER_MODE_FIT;
m_lpAgoraObject->GetEngine()->setupLocalVideo(vc);
4. 加入频道
完成初始化和设置本地视图后(视频通话场景),你就可以调用 joinChannel
方法加入频道。你需要在该方法中传入如下参数:
channelName
: 传入能标识频道的频道 ID。输入频道 ID 相同的用户会进入同一个频道。- token:传入能标识用户角色和权限的 Token。可设为如下一个值:
注:若项目已启用 App 证书,请使用 Token。
-
NULL
-
临时 Token。临时 Token 服务有效期为 24 小时。你可以在控制台里生成一个临时 Token,详情请在文档中心搜索「获取临时 Token」。
-
在你的服务器端生成的 Token。在安全要求高的场景下,我们推荐你使用此种方式生成的 Token。
-
uid
: 本地用户的 ID。数据类型为整型,且频道内每个用户的uid
必须是唯一的。若将uid
设为 0,则 SDK 会自动分配一个uid
,并在onJoinChannelSuccess
回调中报告。
更多的参数设置注意事项请参考声网文档中心 joinChannel
接口中的参数描述。
// 加入频道。BOOL CAgoraObject::JoinChannel(LPCTSTR lpChannelName, UINT nUID,LPCTSTR lpToken)
{ int nRet = 0;#ifdef UNICODE
CHAR szChannelName[128];
::WideCharToMultiByte(CP_UTF8, 0, lpChannelName, -1, szChannelName, 128, NULL, NULL); char szToken[128];
::WideCharToMultiByte(CP_UTF8, 0, lpToken, -1, szToken, 128, NULL, NULL); if(0 == _tcslen(lpToken))
nRet = m_lpAgoraEngine->joinChannel(NULL, szChannelName, NULL, nUID); else
nRet = m_lpAgoraEngine->joinChannel(szToken, szChannelName, NULL, nUID);#else
if(0 == _tcslen(lpToken))
nRet = m_lpAgoraEngine->joinChannel(NULL, lpChannelName, NULL, nUID); else
nRet = m_lpAgoraEngine->joinChannel(lpToken, lpChannelName, NULL, nUID);#endif
if (nRet == 0)
m_strChannelName = lpChannelName; return nRet == 0 ? TRUE : FALSE;
}
5. 设置远端视图
视频通话中,通常你也需要看到其他用户。在加入频道后,可通过调用 setupRemoteVideo
方法设置远端用户的视图。
远端用户成功加入频道后,SDK 会触发 onFirstRemoteVideoDecoded
回调,该回调中会包含这个远端用户的 uid
信息。在该回调中调用 setupRemoteVideo
方法,传入获取到的 uid
,设置远端用户的视图。
// SDK 接收到第一帧远端视频并成功解码时,会触发该回调。// 在该回调中调用 setupRemoteVideo 方法设置远端视图。LRESULT CAgoraTutorialDlg::OnFirstRemoteVideoDecoded(WPARAM wParam, LPARAM lParam)
{
LPAGE_FIRST_REMOTE_VIDEO_DECODED lpData = (LPAGE_FIRST_REMOTE_VIDEO_DECODED)wParam;
VideoCanvas vc;
vc.renderMode = RENDER_MODE_FIT;
vc.uid = lpData->uid;
vc.view = m_wndRemote.GetSafeHwnd(); // 设置远端视图。
m_lpAgoraObject->GetEngine()->setupRemoteVideo(vc); delete lpData; return 0;
}
6. 离开频道
根据场景需要,如结束通话、关闭 App 或 App 切换至后台时,调用 leaveChannel
离开当前通话频道。
BOOL CAgoraObject::LeaveChannel()
{
m_lpAgoraEngine->stopPreview(); // 离开频道。
int nRet = m_lpAgoraEngine->leaveChannel(); return nRet == 0 ? TRUE : FALSE;
} void CAgoraObject::CloseAgoraObject()
{ if(m_lpAgoraEngine != NULL) // 释放 IRtcEngine 对象。
m_lpAgoraEngine->release(); if(m_lpAgoraObject != NULL) delete m_lpAgoraObject;
m_lpAgoraEngine = NULL;
m_lpAgoraObject = NULL;
}
示例代 码
你可以在 Agora-Windows-Tutorial-1to1 示例项目的 AgoraTutorialDlg.cpp 文件中查看完整的源码和代码逻辑。
运行项目
你可以在 Windows 设备中运行此项目。当你成功开始视频通话时,你可以同时看到本地和远端的视图。