华为云会议Windows SDK集成典型场景
引导式阅读
C++
华为云会议Windows SDK集成典型场景
作者
HDC.Cloud
上架时间
2023-11-16 06:55:04

华为云会议Windows SDK集成典型场景

1. 介绍

什么是华为云会议Windows SDK?

华为云会议服务为开发者提供Windows的客户端SDK,方便开发者将华为云会议的音频会议、视频会议、桌面共享能力快速集成到自己的应用中。

华为云会议的客户端SDK已经实现了会议中UI界面,包括会场画面显示,会议控制功能,桌面共享等,开发者只需要调用少量接口即可完成华为云会议能力的集成。

您将学会到什么

  • 在您自己的App中如何调用华为云会议终端SDK提供的接口,集成会议功能。

  • 每个接口调用时需要提供的必要参数。

您需要什么?

开发环境及技能要求:

  • 创建Visual Studio 2017工程项目。

  • 熟悉Visual Studio 2017。

  • 参考开发前准备

SDK下载:

运行终端要求:

  • 必须为Windows 10专业版。
  • CPU:i5-2400四核 3.1GHz及以上。
  • 内存:4GB及以上。

问题求助通道:

2. 集成Windows SDK的典型场景

接口调用概览

Windows SDK的接口分为主动调用接口、回调接口、消息通知接口。

  • 主动调用接口:App主动调用SDK的接口,用于完成某项任务。比如初始化接口Init。

  • 回调接口:由于主动调用接口和SDK内部任务处理采用异步处理方式,所有SDK某项任务处理完成后,以回调接口的方式通知App。比如初始化结果回调接口OnInitResult。

  • 消息通知接口:由华为云会议服务端触发或SDK内部触发的事件通知,SDK以消息通知接口的形式通知给App。比如会议来电通知OnConfIncoming。

图1 典型场景的接口调用时序图

说明:

  • 登录不是必须的步骤,在不登录的情况下也能通过会议ID和密码加入已经创建好的会议。

  • 开启共享、结束共享、离开会议、结束会议等接口,当前SDK的UI上已经提供了对应的功能,如果第三方的App不需要程序控制这些功能则无需调用对应接口。

  • 设置会场名(RenameSelf)接口在会议前和会议中都可以调用。

  • 会议开始后,OnConfInfo会上报多次。

场景1:初始化

描述

在第三方APP启动时,需要先初始化SDK,才能调用SDK的功能接口,该接口只需调用一次。

业务流程

SDK初始化时,先调用Init接口,然后处理回调函数OnInitResult和消息通知OnSdkStart。

接口调用
  • 构建数据结构HwmInitInfo。 定义派生类demoNotifyProc继承于基类HwmAgentNotify,新建派生类对象。
static demoNotifyProc *notifyObj = new demoNotifyProc();
  • 定义派生类demoCallbackProc继承于基类HwmAgentCallback,新建派生类对象。
static demoCallbackProc *callbackObj = new demoCallbackProc();
  • 构建其他变量。 调用Init接口,完成配置初始化,第1步中的数据作为参数。
处理回调函数

处理回调函数OnInitResult。

处理消息通知

处理消息通知OnSdkStart。

示例代码
/** *初始化接口,拉起应用 */ int CdemoBeforeLoginDlg::Init() { //组装入参结构体 hwmsdkagent::HwmInitInfo initParam; memset(&initParam, 0, sizeof(HwmInitInfo)); //获取sdk路径,假设HwmSdk目录就在编译输出目录下 TCHAR szFilePath[HWM_MAX_FILE_PATH_LEN] = { 0 }; GetModuleFileName(NULL, szFilePath, HWM_MAX_FILE_PATH_LEN); (_tcsrchr(szFilePath, _T('\\')))[1] = 0; // 删除文件名,只获得路径字串 strncpy_s(initParam.exePath,szFilePath, HWM_MAX_FILE_PATH_LEN); string appId("MyApp"); strncpy_s(initParam.appId, appId.c_str(), HWM_MAX_APPID_LEN); initParam.notify = notifyObj; initParam.callback = callbackObj; int ret = hwmsdkagent::Init(&initParam); return ret; }

场景2:登录

描述

用华为云会议帐号登录华为云会议。创建会议等接口不支持在未登录状态下调用,若要使用完整的会议功能,必须先登录华为云会议。

业务流程

使用SDK登录时,先调用Login接口,然后处理回调函数OnLoginResult。

接口调用
  • 组装数据结构HwmLoginInfo。
  1. 选择合适的登录类型。
  2. 根据登录类型给对应的变量赋值。
  • 调用Login进行登录,第1步中的数据作为参数。
处理回调函数

处理回调函数OnLoginResult。

示例代码
/** * 登录处理 */ int demoLoginDlg::Login() { int ret; //设置帐号和密码 // 认证用的帐号和密码硬编码到代码中或者明文存储都有很大的安全风险,建议在配置文件或者环境变量中密文存放,使用时解密,确保安全; // 本示例以帐号和密码保存在环境变量中来实现身份认证为例,运行示例前请先在本地环境中设置环境变量HUAWEICLOUD_SDK_AK和HUAWEICLOUD_SDK_SK。 string account = getenv("CLOUD_SDK_ACCOUNT"); string password = getenv("CLOUD_SDK_PASSWORD"); //组装入参结构体 hwmsdkagent::HwmLoginInfo loginParam; memset(&loginParam, 0, sizeof(hwmsdkagent::HwmLoginInfo)); strncpy_s(loginParam.accountAndPasswordAuthInfo.account, account.c_str(), HWM_MAX_ACCOUNT_LEN); strncpy_s(loginParam.accountAndPasswordAuthInfo.password, password.c_str(), HWM_MAX_PASSWORD_LEN); loginParam.authType = hwmsdkagent::HWM_AUTH_TYPE_ACCOUNT_AND_PASSWORD; //调用登录接口 ret = hwmsdkagent::Login(&loginParam); return ret; }
/** * 登录接口回调 */ void demoCallbackProc::OnLoginResult(hwmsdk::HwmErrCode ret, const char* msg) { CString codeStr; codeStr.Format(_T("%d"), ret); string msgStr = CTools::UTF82MultiByte(msg); CString tips = _T("OnLoginResult code:") + codeStr + _T(", msg:") + CString(msgStr.c_str()); AfxMessageBox(tips); }

场景3:创建会议

描述

在使用华为云会议帐号登录后,可以调用创建会议接口创建立即会议。创建会议时可以携带与会人信息,也可以不携带。

业务流程

使用SDK创建立即会议时,先调用CreateConf接口,然后处理回调函数OnCreateConfResult和消息通知OnConfState、OnConfInfo。

接口调用
  1. 组装数据结构HwmCreateConfInfo。
  2. 组装数据结构HwmConfAttendee。
  3. 调用CreateConf开始创建,第1步中的数据作为参数。
处理回调函数

处理回调函数OnCreateConfResult。

处理消息通知

处理消息通知OnConfState。

处理消息通知

处理消息通知OnConfInfo。

示例代码
/** * 创建会议 */ int demoCreateConfWithAttendeeDlg::clickCreatConfWithAttendee() { int ret; hwmsdkagent::HwmCreateConfInfo data; memset(&data, 0, sizeof(hwmsdkagent::HwmCreateConfInfo)); //设置会议主题 string meetingSubjectStr = CTools::UNICODE2UTF(CString("我的会议")); strncpy_s(data.subject, meetingSubjectStr.c_str(), HWM_MAX_SUBJECT_LEN); //设置会议类型 data.mediaType = hwmsdkagent::HWM_VIDEO_AND_DATA //设置会议是否需要来宾密码 data.needPassword = true; //设置与会人 //获取与会人信息,实际使用是根据实际情况获取 CString tempCString; m_attendeesEdit.GetWindowText(tempCString); string tempString = CTools::UNICODE2UTF(tempCString); vector<string> list = CTools::split(tempString, ';'); vector<string> temp; int count = list.size(); int realCount = 0; if (count > 0) { //申请结构体内存 hwmsdkagent::HwmConfAttendee* participants; participants = (hwmsdkagent::HwmConfAttendee*)malloc(sizeof(hwmsdkagent::HwmConfAttendee) * count); if (participants == NULL) { return -1; } memset(participants, 0, sizeof(hwmsdkagent::HwmConfAttendee)*count); hwmsdkagent::HwmConfAttendee* participantsTemp = participants; for (int i = 0; i < count; i++) { temp = CTools::split(list[i], '-'); if (temp.size() == 2) { //name赋值 strncpy_s(participantsTemp->name, (char *)temp[0].c_str(), HWM_MAX_DISPLAY_NAME_LEN); //number赋值 strncpy_s(participantsTemp->number, (char *)temp[1].c_str(), HWM_MAX_NUMBER_LEN); //指针个数加1 realCount++; participantsTemp++; } } //调用SDK接口,创建会议 ret = hwmsdkagent::CreateConf(&data, participants, realCount); //释放内存空间 free(participants); participants = NULL; } else { //不携带与会人创建会议 ret = hwmsdkagent::CreateConf(&data, NULL, 0); } return ret; }
/** * 创建会议接口回调 */ void demoCallbackProc::OnCreateConfResult(hwmsdk::HwmErrCode ret, const char* msg) { CString codeStr; codeStr.Format(_T("%d"), ret); string msgStr = CTools::UTF82MultiByte(msg); CString tips = _T("OnCreateConfResult code:") + codeStr + _T(", msg:") + CString(msgStr.c_str()); AfxMessageBox(tips); }
/** * 会议状态消息通知 */ void demoNotifyProc::OnConfState(HwmConfStateInfo *confStateInfo) { CString str; str.Format(_T("%d"), confStateInfo->state); CString reason; reason.Format(_T("%d"), confStateInfo->reason); CString tips = _T("OnConfState state:") + str + _T(", reason:")+ reason; AfxMessageBox(tips); }
/** * 会议信息消息通知 */ void demoNotifyProc::OnConfInfo(HwmConfInfo *confInfo) { string urlStr = CTools::UTF82MultiByte(confInfo->url); string confIdStr = CTools::UTF82MultiByte(confInfo->confId); string chairmanPwdStr = CTools::UTF82MultiByte(confInfo->chairmanPwd); string generalPwdStr = CTools::UTF82MultiByte(confInfo->generalPwd); string subjectStr = CTools::UTF82MultiByte(confInfo->subject); CString role; role.Format(_T("%d"), confInfo->role); CString tips = _T("OnConfInfo confUrl:") + CString(urlStr.c_str()) + _T(", confId:") + CString(confIdStr.c_str()) + _T(", confRole:") + CString(role) + _T(", chairmanPwd:") + CString(chairmanPwdStr.c_str()) + _T(", generalPwd:") + CString(generalPwdStr.c_str()) + _T(", subject:") + CString(subjectStr.c_str()); AfxMessageBox(tips); }

场景4:添加与会人

描述

在会议中,可以调用添加与会人的接口把与会人加入到会议中。

业务流程

使用SDK添加与会人时,先调用AddAttendee接口,然后处理回调函数OnAddAttendeeResult。

接口调用
  1. 组装数据结构HwmConfAttendee。
  2. 调用AddAttendee开始创建,第1步中的数据作为参数。
处理回调函数

处理回调函数OnAddAttendeeResult。

示例代码
/** * 添加与会人 */ int demoAddAttendeeDlg::clickAddAttendee() { int ret; CString tempCString; //获取与会人信息,实际使用是根据实际情况获取 m_numbersEdit.GetWindowText(tempCString); string tempString = CTools::UNICODE2UTF(tempCString); vector<string> list = CTools::split(tempString, ';'); vector<string> temp; int count = list.size(); int realCount = 0; if (count > 0) { //申请结构体内存 hwmsdkagent::HwmConfAttendee* participants; participants = (hwmsdkagent::HwmConfAttendee*)malloc(sizeof(hwmsdkagent::HwmConfAttendee) * count); if (participants == NULL) { return -1; } memset(participants, 0, sizeof(hwmsdkagent::HwmConfAttendee)*count); hwmsdkagent::HwmConfAttendee* participantsTemp = participants; for (int i = 0; i < count; i++) { temp = CTools::split(list[i], '-'); if (temp.size() == 2) { //name赋值 strncpy_s(participantsTemp->name, (char *)temp[0].c_str(), HWM_MAX_DISPLAY_NAME_LEN); //number赋值 strncpy_s(participantsTemp->number, (char *)temp[1].c_str(), HWM_MAX_NUMBER_LEN); //指针个数加1 realCount++; participantsTemp++; } } ret = hwmsdkagent::AddAttendee(participants, realCount); //释放内存空间 free(participants); participants = NULL; } return ret; }
/** * 添加与会者接口回调 */ void demoCallbackProc::OnAddAttendeeResult(hwmsdk::HwmErrCode ret, const char* msg) { CString codeStr; codeStr.Format(_T("%d"), ret); string msgStr = CTools::UTF82MultiByte(msg); CString tips = _T("OnAddAttendeeResult code:") + codeStr + _T(", msg:") + CString(msgStr.c_str()); AfxMessageBox(tips); }

场景5:加入会议

描述

在华为云会议帐号登录后或者登录前,都可以使用该接口通过会议ID和密码加入会议。

业务流程

使用SDK加入已经存在的会议时,先调用JoinConfById接口,然后处理回调函数OnJoinConfByIdResult和消息通知OnConfState、OnConfInfo。

接口调用
  1. 组装数据结构HwmJoinConfByIdInfo。
  2. 调用JoinConfById开始创建,第1步中的数据作为参数。
处理回调函数

处理回调函数OnJoinConfByIdResult。

处理消息通知

处理消息通知OnConfState。

处理消息通知

处理消息通知OnConfInfo。

示例代码
// 通过会议ID加入会议 int demoJoinConfByIdDlg::clickJoinConfById() { // 填写会议ID和会议密码;未登录场景下加入会议时,也可以配置本会场的会场名称 string meetingID = CTools::UNICODE2UTF(CString("989156631")); string accessCode = CTools::UNICODE2UTF(CString("807766")); string participantName = CTools::UNICODE2UTF(CString("杭州会场")); hwmsdkagent::HwmJoinConfByIdInfo data; memset(&data, 0, sizeof(hwmsdkagent::HwmJoinConfByIdInfo)); strncpy_s(data.confId, meetingID.c_str(), HWM_MAX_CONF_ID_LEN); strncpy_s(data.password, accessCode.c_str(), HWM_MAX_PASSWORD_LEN); strncpy_s(data.name, participantName.c_str(), HWM_MAX_DISPLAY_NAME_LEN); int ret = hwmsdkagent::JoinConfById(&data); return ret; }

场景6:定制会中“邀请”按钮

描述

考虑到大部分使用SDK二次开发的场景下,第三方App不会使用华为云会议的通讯录,因此Windows SDK提供的会议界面上“邀请”按钮,只提供了一个按钮界面,并没有提供完整邀请与会者的功能,需要开发者自己实现。

业务流程

使用SDK实现“邀请”功能时,在SDK初始化完成后先调用Config接口,然后处理回调函数OnConfigResult。会议中,在“邀请”按钮单击后,再处理消息通知OnClickInjectBtn。

接口调用
  1. 在SDK初始化后,调用Config进行“邀请”按钮的定制。
处理回调函数

处理回调函数OnConfigResult。

处理消息通知

处理回调函数OnClickInjectBtn。

示例代码
/** * 配置邀请按钮 */ int hwmSDKConfigUI::hwmSDKStartUIConfig() { //配置“邀请”按钮 CString inviteBtn = ("{\"frame\":{\"confMenu\" : {\"toolBar\":{\"button\":[{\"id\":\"invite\",\"showAsAction\" : \"ifRoom\",\"isCustomizedClick\" : true}]}}}}"); string uiConfig CTools::UNICODE2UTF(inviteBtn); ret = hwmsdkagent::Config(uiConfig); return ret; }
/** * 配置接口回调 */ void demoCallbackProc::OnConfigResult(hwmsdk::HwmErrCode ret, const char* msg) { CString codeStr; codeStr.Format(_T("%d"), ret); string msgStr = CTools::UTF82MultiByte(msg); CString tips = _T("OnConfigResult code:") + codeStr + _T(", msg:") + CString(msgStr.c_str()); AfxMessageBox(tips); }
/** * 按钮点击消息通知 */ void notifyProc::OnClickInjectBtn(hwmsdkagent::HwmClickInjectBtn injectBtn, void* data, const char* id) { INT_PTR nRes; switch (injectBtn) // “邀请”按钮点击消息通知 case hwmsdkagent::HWM_CLICK_INJECT_BTN_INVITE: { // 显示带通讯录的邀请与会人界面 hwm_ui_demo_inviteDlg inviteDlg; nRes = inviteDlg.DoModal(); break; } }

场景7:注销登录

描述

当需要退出当前的华为云会议帐号的登录时,可以调用该接口完成已登录华为云帐号帐号的注。

业务流程

使用SDK注销登录时,先调用Logout接口,然后处理回调函数OnLogoutResult。

接口调用
  1. 调用Logout进行注销登录。
处理回调函数

处理回调函数OnLogoutResult。

示例代码
/** * 注销 */ void demoMainMenuDlg::OnBnClickedLogout() { //调用登录接口 int ret = hwmsdkagent::Logout(); if (hwmsdk::HWM_COMMON_SUCCESS != ret) { AfxMessageBox(_T("Logout error")); } }
/** * 注销接口回调 */ void demoCallbackProc::OnLogoutResult(hwmsdk::HwmErrCode ret, const char* msg) { CString codeStr; codeStr.Format(_T("%d"), ret); string msgStr = CTools::UTF82MultiByte(msg); CString tips = _T("OnLogoutResult code:") + codeStr + _T(", msg:") + CString(msgStr.c_str()); AfxMessageBox(tips); }

场景8:退出

描述

在第三方APP退出时,需要去初始化SDK,否则名称为HwmSdk.exe的进程(Windows SDK的主进程)一直在后台运行。

业务流程

需要退出SDK时,先调用Exit接口,然后处理回调函数OnExitResult。

接口调用
  1. 调用Exit退出SDK。
处理回调函数

处理回调函数OnExitResult。

示例代码
/** * 退出sdk */ void CdemoBeforeLoginDlg::OnBnClickedButtonExit() { int ret = hwmsdkagent::Exit(); if (hwmsdk::HWM_COMMON_SUCCESS != ret) { AfxMessageBox(_T("Exit error")); } }
/** * 退出回调 */ void demoCallbackProc::OnExitResult(hwmsdk::HwmErrCode ret, const char* msg) { CString codeStr; codeStr.Format(_T("%d"), ret); string msgStr = CTools::UTF82MultiByte(msg); CString tips = _T("OnExitResult code:") + codeStr + _T(", msg:") + CString(msgStr.c_str()); AfxMessageBox(tips); }

3. 参考文件