好视通aPaaS SDK(application Platform as a Service 应用平台即服务SDK,简称应用SDK)是好视通将多年在远程教育、远程培训、远程招聘、应急指挥等行业积累的云服务能力提供给第三方开发者,帮助开发者快速的搭建全方位、低延迟、高质量的音视频解决方案。
好视通SDK方案可以与好视通行业方案结合满足各种场景需求,客户可以通过服务器API以及应用SDK对业务进行管理。同时该方案也可以与好视通行业方案的软件、硬件共同使用,客户可通过SDK与自身业务软件相结合,打造垂直领域的解决方案。
SDK主要提供以下功能
| 功能 | 功能说明 |
|---|---|
| 音视频 | 支持一对一、一对多的音视频交流,参会者支持同时打开多个摄像头 |
| 外设管理 | 允许参会者管理自己的音视频外设的参数配置 |
| 屏幕共享 | 支持Web端把桌面作为数据进行共享以及接收 |
| 文字聊天 | 允许参会者聊天或与其他人私聊 |
| 布局 | 允许切换布局来同时显示多路视频 |
| 参会者管理 | 获取当前会中参会人的信息、权限、音视频状态,以及对其进行管理 |
| 会控 | 收取会议中的事件并响应, 支持控制权限的申请 |
| 共享白板 | 支持创建和接收白板数据 |
1.1.1 获取SDK
dll 目录有Debug和Release两种编译方式的动态库和依赖文件,集成时需要全部拷贝到自己程序的运行目录。
lib 目录有Debug和Release两种编译方式的链接库,集成时需要放在自己工程设置的库目录路径中。
include 目录有集成时需要包含的头文件,使用时都放在同一个目录,包含ICommonsDef.h一个文件即可。
1.1.2 新建VS2015 工程




1.1.3配置工程
右键项目属性,Release,Debug 都设置成相应的配置路径。


注:常规配置下输出目录与目标文件名、扩展名与链接器配置中输出文件中目录、输出文件名、扩展名需要一一对应。
将从官网下载SDK包解压目录下dll 目录中Debug/Release 中的所有文件 dll、exe 文件拷入到输出路径对应的Debug/ Release目录。
注:
(1)Debug 需要从官网下载 SdkDemo解压后的 Debug 目录中拷贝下列文件和目录到自己工程输出的debug目录:

然后将SDK 解压的Debug 目录下的dll和exe文件 全部拷贝到debug 目录。
(2)Release 需要从 SdkDemo的 Release 拷贝下列文件和目录:
注:skin目录为SdkDemo皮肤文件,集成SDK不需要demo的皮肤文件。
(1) 右键工程属性,设置对应的头文件目录。
(2)将SDK include 目录拷贝到工程配置的头文件目录。

(1)右键工程,属性,设置lib 库目录。
(2) 拷贝SDK 目录的lib 到对应设置的lib目录(debug 和release 拷贝对应的lib目录)。

头文件包含以及lib引用示例代码:
#include "ICommonsDef.h" //SDK 相关包含 #ifdef _DEBUG #pragma comment(lib,"winsdkd.lib") //SDK 相关包含 #else #pragma comment(lib,"winsdk.lib") //SDK 相关包含 #endif
这样,基本集成完SDK相关文件,就可以在程序中调用SDK 接口,和使用SDK相关数据结构了。
1.1.4 常见问题以及解决方案

(1)Debug检查config.data、 meetingMgrCfg.data、UIConfig.xml、Resource目录下文件以及目录中res.xml、FMUIFrameWork.dll等SDK 动态库。
(2)Release检查config.data、 meetingMgrCfg.data、uiconfig.data、Resource目录下文件以及目录中
es.data、FMUIFrameWork.dll等SDK 动态库。
1.2.1 SDK文档常用语说明
文档常用语表:
| 常用语 | 含义 | 备注 |
|---|---|---|
| ISdkManager | SDK主接口,通过initSdk()初始化,通过getXxxManager()获取各模块接口 | |
| ISdkInterface::setEventHandler | 设置SDK基础事件处理 | 通过实现ISdkManagerEventHandler接口 |
| IXxxManager::setEventHandler | 各模块独立的事件处理 | 如ILoginManager对应ILoginEventHandler |
| Manager* | 表示SDK相关接口管理类 | |
| EventHandler | 各模块独立的事件处理虚函数接口 |
// 各模块对应的EventHandler接口 // ILoginEventHandler - 登录事件 // IAudioEventHandler - 音频事件 // IVideoEventHandler - 视频事件 // IMeetingEventHandler - 会议事件 // IUserEventHandler - 用户事件 // IChatEventHandler - 聊天事件 // IScreenShareEventHandler - 屏幕共享事件 // IMediaShareEventHandler - 多媒体共享事件 // IPermissionEventHandler - 权限事件 // IWhiteBoardEventHandler - 电子白板事件 // IContactsEventHandler - 通讯录事件 // ISdkManagerEventHandler - SDK基础事件
1.2.3 各模块EventHandler对应关系:
| 模块 | Manager接口 | EventHandler接口 |
|---|---|---|
| SDK基础 | ISdkManager | ISdkManagerEventHandler |
| 登录 | ILoginManager | ILoginEventHandler |
| 音频 | IAudioManager | IAudioEventHandler |
| 视频 | IVideoManager | IVideoEventHandler |
| 会议 | IMeetingManager | IMeetingEventHandler |
| 用户 | IUserManager | IUserEventHandler |
| 聊天 | IChatManager | IChatEventHandler |
| 屏幕共享 | IScreenShareManager | IScreenShareEventHandler |
| 多媒体共享 | IMediaShareManager | IMediaShareEventHandler |
| 权限 | IPermissionManager | IPermissionEventHandler |
| 电子白板 | IWhiteBoardManager | IWhiteBoardEventHandler |
| 通讯录 | IContactsManager | IContactsEventHandler |
FS_SDK::ISdkManager* m_SdkManager = nullptr; FS_SDK::ILoginManager* m_LoginManager = nullptr; FS_SDK::IAudioManager* m_AudioManager = nullptr; FS_SDK::IVideoManager* m_VideoManager = nullptr; FS_SDK::IChatManager* m_ChatManager = nullptr; FS_SDK::IMeetingManager* m_MeetingManager = nullptr; FS_SDK::IUserManager* m_UserManager = nullptr; FS_SDK::IScreenShareManager* m_ScreenShareManager = nullptr; FS_SDK::IPermissionManager* m_PermissionManager = nullptr; FS_SDK::IWhiteBoardManager* m_WBManager = nullptr; FS_SDK::IContactsManager* m_ContactsManager = nullptr; FS_SDK::IMediaShareManager* m_MediaShareManager = nullptr; // 创建SdkContext并初始化SDK FS_SDK::SdkContext context; context.logPath = _T("."); FS_SDK::ErrCode hR = FS_SDK::ISdkManager::initSdk(&context); if (FS_SDK::ERR_SUCCESS != hR) { g_pSuperLog->WriteLogMsg("initSdk Failed, Error Code %d\n", hR); return; } m_SdkManager = FS_SDK::ISdkManager::getInstance(); if (!m_SdkManager) { g_pSuperLog->WriteFile(_T("initSdk调用失败!\n")); return; } // 获取各模块Manager接口 m_LoginManager = m_SdkManager->getLoginManager(); m_AudioManager = m_SdkManager->getAudioManager(); m_VideoManager = m_SdkManager->getVideoManager(); m_ChatManager = m_SdkManager->getChatManager(); m_MeetingManager = m_SdkManager->getMeetingManager(); m_UserManager = m_SdkManager->getUserManager(); m_ScreenShareManager = m_SdkManager->getScreenShareManager(); m_PermissionManager = m_SdkManager->getPermissionManager(); m_WBManager = m_SdkManager->getWhiteBoardManager(); m_ContactsManager = m_SdkManager->getContactsManager(); m_MediaShareManager = m_SdkManager->getMediaShareManager(); if (!m_LoginManager || !m_AudioManager || !m_VideoManager || !m_MeetingManager || !m_UserManager || !m_ScreenShareManager || !m_ChatManager || !m_PermissionManager || !m_WBManager || !m_ContactsManager || !m_MediaShareManager) { g_pSuperLog->WriteFile(L"SDK 组件管理初始化失败\n"); return; }
// 实现各模块的EventHandler接口 class MyLoginEventHandler : public FS_SDK::ILoginEventHandler { public: virtual void onAccountLoginCompleted(FS_SDK::ErrCode err) override; virtual void onAnonymousLoginCompleted(FS_SDK::ErrCode err) override; virtual void onEnterRoomCompleted(FS_SDK::ErrCode err) override; virtual void onAccountLogout(FS_SDK::ErrCode err) override; virtual void onExitedRoom() override; virtual void onPreStartMeeting() override; virtual void onInitCompleted() override; }; // 同理实现其他模块的EventHandler class MyAudioEventHandler : public FS_SDK::IAudioEventHandler {/* ... */}; class MyVideoEventHandler : public FS_SDK::IVideoEventHandler {/* ... */}; class MyMeetingEventHandler : public FS_SDK::IMeetingEventHandler {/* ... */}; class MyUserEventHandler : public FS_SDK::IUserEventHandler {/* ... */}; class MyChatEventHandler : public FS_SDK::IChatEventHandler {/* ... */}; class MyScreenShareEventHandler : public FS_SDK::IScreenShareEventHandler {/* ... */}; class MyPermissionEventHandler : public FS_SDK::IPermissionEventHandler {/* ... */}; class MyWBEventHandler : public FS_SDK::IWhiteBoardEventHandler {/* ... */}; // 设置各模块的EventHandler MyLoginEventHandler m_loginHandler; MyAudioEventHandler m_audioHandler; MyVideoEventHandler m_videoHandler; MyMeetingEventHandler m_meetingHandler; MyUserEventHandler m_userHandler; MyChatEventHandler m_chatHandler; MyScreenShareEventHandler m_screenShareHandler; MyPermissionEventHandler m_permissionHandler; MyWBEventHandler m_wbHandler; m_LoginManager->setEventHandler(&m_loginHandler); m_AudioManager->setEventHandler(&m_audioHandler); m_VideoManager->setEventHandler(&m_videoHandler); m_MeetingManager->setEventHandler(&m_meetingHandler); m_UserManager->setEventHandler(&m_userHandler); m_ChatManager->setEventHandler(&m_chatHandler); m_ScreenShareManager->setEventHandler(&m_screenShareHandler); m_PermissionManager->setEventHandler(&m_permissionHandler); m_WBManager->setEventHandler(&m_wbHandler);
在onAccountLoginCompleted或onInitCompleted事件回调后设置服务器地址和ClientInfo。
示例代码:
// 设置服务器地址 FS_SDK::ErrCode hR = m_LoginManager->setServerAddr(_T("apaas.hst.com"), 443); if (hR != FS_SDK::ERR_SUCCESS) { g_pSuperLog->WriteFile(L"设置服务器地址失败!\n"); } // 设置ClientID和Secret hR = m_LoginManager->setClientInfo(_T("your_client_id"), _T("your_client_secret")); if (hR != FS_SDK::ERR_SUCCESS) { g_pSuperLog->WriteFile(L"设置ClientID或者Secret失败!\n"); }
示例代码:
// 账号密码登录: loginAccount(name, pwd, force) // force=true 表示强制挤下前一个登录 FS_SDK::ErrCode err = m_LoginManager->loginAccount((S_PTCHAR)username, (S_PTCHAR)pwd, true); if (err != FS_SDK::ERR_SUCCESS) { // 登录调用失败,登录结果通过 onAccountLoginCompleted 回调通知 }
示例代码:
// 会议室号/邀请码入会: loginRoom(nick, roomId, roomPwd) FS_SDK::ErrCode err = m_LoginManager->loginRoom((S_PTCHAR)nickname, roomId, (S_PTCHAR)roomPwd); if (err != FS_SDK::ERR_SUCCESS) { // 入会调用失败,结果通过 onEnterRoomCompleted 回调通知 }
登录结果通过ILoginEventHandler的相应回调通知:
// 账号登录完成回调 virtual void MyLoginEventHandler::onAccountLoginCompleted(FS_SDK::ErrCode err) override { if (err == FS_SDK::ERR_SUCCESS) { g_pSuperLog->WriteFile(L"账号登录成功\n"); } else { g_pSuperLog->WriteLogMsg("账号登录失败, err=%d\n", err); } } // 匿名登录(入会)完成回调 virtual void MyLoginEventHandler::onAnonymousLoginCompleted(FS_SDK::ErrCode err) override { if (err == FS_SDK::ERR_SUCCESS) { g_pSuperLog->WriteFile(L"匿名入会成功\n"); // 入会成功后可以获取本地用户ID S_UINT32 localUserId = m_UserManager->getLocalUser(); } } // 入会完成回调 virtual void MyLoginEventHandler::onEnterRoomCompleted(FS_SDK::ErrCode err) override { if (err == FS_SDK::ERR_SUCCESS) { g_pSuperLog->WriteFile(L"入会成功\n"); } } // SDK初始化完成,可以广播音视频等操作 virtual void MyLoginEventHandler::onInitCompleted() override { g_pSuperLog->WriteFile(L"SDK初始化完成,可以广播音视频\n"); } // 预开始会议 virtual void MyLoginEventHandler::onPreStartMeeting() override { // 准备好会中资源后,调用startMeeting() m_MeetingManager->startMeeting(); }
1、调用IMeetingManager::exitMeeting退出会议
2、当收到onExitedRoom事件后需要调用ISdkManager::releaseSdk接口
3、调用ILoginManager::logout退出登录
4、未登录时关闭程序需要调用releaseSdk()接口
// 退出会议 m_MeetingManager->exitMeeting(); // 退出登录 m_LoginManager->logout(); // 释放SDK m_SdkManager->releaseSdk();
音频状态通过 IAudioEventHandler::onUserAudioEvent 回调获取,其中 state 参数含义:0=未广播,1=正在申请,2=正在广播。
// IAudioEventHandler中获取音频状态 virtual void MyAudioEventHandler::onUserAudioEvent(S_UINT32 userId, S_BYTE state) override { g_pSuperLog->WriteLogMsg("用户%d音频状态变更: state=%d\n", userId, state); }
广播自己音频流程图如下: 
广播他人音频:

处理他人音频申请:

主动发起广播代码示例如下:
// 广播用户音频 FS_SDK::ErrCode CSdkManager::BroadcastUserAudio(S_UINT32 userId) { g_pSuperLog->WriteLogMsg("广播用户%d音频\n", userId); FS_SDK::ErrCode hr = m_AudioManager->broadcastAudio(userId, true); if (hr == FS_SDK::ERR_SUCCESS) { g_pSuperLog->WriteFile(L"广播用户音频成功!\n"); } else if (hr == FS_SDK::ERR_REQUEST_PENDING) { g_pSuperLog->WriteFile(L"询问对方是否可以广播...等待对方回复\n"); } else { g_pSuperLog->WriteLogMsg("广播用户音频失败! ERR : %d\n", hr); } return hr; } // 停止广播用户音频 FS_SDK::ErrCode CSdkManager::StopBroadcastUserAudio(S_UINT32 userId) { g_pSuperLog->WriteLogMsg("停止广播用户%d音频\n", userId); FS_SDK::ErrCode hr = m_AudioManager->broadcastAudio(userId, false); if (hr == FS_SDK::ERR_SUCCESS) { g_pSuperLog->WriteFile(L"停止广播用户音频成功!\n"); } else { g_pSuperLog->WriteLogMsg("停止广播用户音频失败! ERR : %d\n", hr); } return hr; }
音频状态变化通过 IAudioEventHandler 回调通知:
// 用户音频广播状态变化 virtual void MyAudioEventHandler::onUserAudioEvent(S_UINT32 userId, S_BYTE state) override { g_pSuperLog->WriteLogMsg("音频状态变更 用户:%d, state:%d\n", userId, state); // state: 0=未广播, 1=申请中, 2=广播中 notify->OnAudioState(userId, state); }
当收到他人申请音频广播时,通过 IAudioEventHandler::onApplyAudioEvent 回调获取,然后调用 answerApplyAudioRight 同意或拒绝:
// 收到他人申请打开音频的回调 virtual void MyAudioEventHandler::onApplyAudioEvent(const FS_SDK::ApplyAVRightInfo* info) override { if (info) { g_pSuperLog->WriteLogMsg("收到用户%d的音频广播申请\n", info->userId); // 同意或拒绝,设置info中的agree字段 FS_SDK::ApplyAVRightInfo answerInfo = *info; answerInfo.agree = true; // 或 false拒绝 m_AudioManager->answerApplyAudioRight(&answerInfo); } }
// IVideoManager接口 /* broadcastVideo - 广播/停止广播视频 */ virtual ErrCode broadcastVideo(S_UINT32 userId, S_BYTE channel, S_BOOL state); /* applyBroadcastVideo - 申请/放弃广播视频 */ virtual ErrCode applyBroadcastVideo(S_BYTE channel, S_BOOL state); /* answerApplyVideoRight - 响应远程打开本地视频 */ virtual ErrCode answerApplyVideoRight(const ApplyAVRightInfo* info);
示例代码:
// 广播用户视频 FS_SDK::ErrCode CSdkManager::BroadcastUserVideo(S_UINT32 userId, S_BYTE channel) { g_pSuperLog->WriteLogMsg("广播用户%d视频, channel=%d\n", userId, channel); FS_SDK::ErrCode hr = m_VideoManager->broadcastVideo(userId, channel, true); if (hr == FS_SDK::ERR_SUCCESS) { g_pSuperLog->WriteFile(L"广播视频成功!\n"); } else if (hr == FS_SDK::ERR_REQUEST_PENDING) { g_pSuperLog->WriteFile(L"询问对方是否可以广播...等待对方回复\n"); } else { g_pSuperLog->WriteLogMsg("广播视频失败! ERR : %d\n", hr); } return hr; } // 视频状态变化回调 (IVideoEventHandler) virtual void MyVideoEventHandler::onUserVideoEvent(S_UINT32 userId, S_BYTE channel, S_BYTE state) override { g_pSuperLog->WriteLogMsg("视频状态变更 用户:%d, channel:%d, state:%d\n", userId, channel, state); notify->OnVideoState(userId, channel, state); } // 收到他人申请打开视频的回调 virtual void MyVideoEventHandler::onApplyVideoEvent(const FS_SDK::ApplyAVRightInfo* info) override { if (info) { g_pSuperLog->WriteLogMsg("收到用户%d的视频广播申请\n", info->userId); FS_SDK::ApplyAVRightInfo answerInfo = *info; answerInfo.agree = true; // 或 false拒绝 m_VideoManager->answerApplyVideoRight(&answerInfo); } }
广播自己视频流程:

广播他人视频
)
需要使用的接口(IScreenShareManager):
// IScreenShareManager接口 isScreenSharing(); //是否正在屏幕共享 startScreenShare(const ScreenShareInfo* info); //开始屏幕共享 stopScreenShare(S_UINT32 userId); //停止屏幕共享 //接收远程屏幕,只需要自己加窗口句柄 startRemoteScreenShareView(S_UINT32 userId, S_BYTE audioChannel, HWND wnd); //停止接收远程屏幕 stopRemoteScreenShareView(S_UINT32 userId, S_BYTE audioChannel);
流程图如下:
示例代码如下:
// 开启屏幕共享 FS_SDK::ErrCode CSdkManager::StartScreenShare() { // 检查是否已有屏幕共享 if (m_ScreenShareManager->isScreenSharing()) { g_pSuperLog->WriteFile(L"已有屏幕共享正在进行\n"); return FS_SDK::ERR_FAILED; } // 构造屏幕共享信息 FS_SDK::ScreenShareInfo info; // 设置info的相应参数... FS_SDK::ErrCode hr = m_ScreenShareManager->startScreenShare(&info); if (FS_SDK::ERR_SUCCESS != hr) { if (FS_SDK::ERR_NO_PRIVILEGE == hr || FS_SDK::ERR_NO_ADMIN_PRIVILEGE == hr) { g_pSuperLog->WriteFile(L"没有屏幕共享权限,请申请管理员!\n"); } else { g_pSuperLog->WriteLogMsg("开启屏幕共享失败! ERR : %d\n", hr); } return hr; } g_pSuperLog->WriteFile(L"开启屏幕共享成功!\n"); return hr; }
流程图如下:

停止共享示例代码如下:
// 停止屏幕共享 FS_SDK::ErrCode CSdkManager::StopScreenShare(S_UINT32 userId) { g_pSuperLog->WriteLogMsg("停止用户%d的屏幕共享\n", userId); FS_SDK::ErrCode hr = m_ScreenShareManager->stopScreenShare(userId); if (FS_SDK::ERR_SUCCESS != hr) { g_pSuperLog->WriteLogMsg("结束屏幕共享失败! ERR : %d\n", hr); } else { g_pSuperLog->WriteFile(L"结束屏幕共享成功!\n"); } return hr; }
流程图如下:
当收到他人的屏幕共享通知时,通过 IScreenShareEventHandler::onScreenShareEvent 回调获取,然后调用 startRemoteScreenShareView 接收:
// IScreenShareEventHandler中接收屏幕共享通知 virtual void MyScreenShareEventHandler::onScreenShareEvent(S_UINT32 userId, S_BYTE state, S_BYTE channel) override { g_pSuperLog->WriteLogMsg("屏幕共享通知:用户:%d, state:%d, channel:%d\n", userId, state, channel); if (state) // state不为0表示开始共享 { m_dwShareUserId = userId; HWND hWnd = m_pMeetingDataDlg->GetHWND(); m_ScreenShareManager->startRemoteScreenShareView(userId, channel, hWnd); } else // state为0表示停止共享 { m_ScreenShareManager->stopRemoteScreenShareView(userId, channel); } } // 共享结果回调 virtual void MyScreenShareEventHandler::onShareResult(FS_SDK::ErrCode err) override { if (err == FS_SDK::ERR_SUCCESS) { g_pSuperLog->WriteFile(L"屏幕共享操作成功\n"); } else { g_pSuperLog->WriteLogMsg("屏幕共享操作失败, err=%d\n", err); } }
当接收屏幕共享的窗口大小改变时,需要主动调用该接口刷新屏幕共享流。
// 更新屏幕共享渲染窗口 FS_SDK::ErrCode hR = m_ScreenShareManager->updateScreenShareWnd(hWnd);
使用 IWhiteBoardManager 管理白板。入会后需先调用 initialize() 初始化白板模块,然后调用 createWB 创建白板。
流程图如下:
创建白板示例代码:
// 1. 入会后初始化白板模块 FS_SDK::ErrCode hr = m_WhiteBoardManager->initialize(); if (hr != FS_SDK::ERR_SUCCESS) { g_pSuperLog->WriteLogMsg("初始化白板模块失败! ERR : %d\n", hr); return hr; } g_pSuperLog->WriteFile(L"白板模块初始化成功\n"); // 2. 创建空白板 S_SIZE wbSize; wbSize.cx = 1920; wbSize.cy = 1080; S_UINT32 wbId = m_WhiteBoardManager->createWB(0, wbSize, 0, nullptr); if (wbId > 0) { g_pSuperLog->WriteLogMsg("创建白板成功,wbId=%d\n", wbId); } // 3. 通过文档创建白板 // S_UINT32 wbId = m_WhiteBoardManager->createWBFromDocFile(0, guid, true);
// 关闭指定白板 m_WhiteBoardManager->closeWB(wbId); // 释放白板模块 (退出会议前) m_WhiteBoardManager->uninitialize();
白板事件通过 IWhiteBoardEventHandler 回调通知:
// 白板初始化完成 virtual void MyWBEventHandler::onWbInitialize(FS_SDK::ErrCode err) override { if (err == FS_SDK::ERR_SUCCESS) { g_pSuperLog->WriteFile(L"白板初始化成功,可以创建白板\n"); } } // 白板事件通知 (创建/关闭/翻页等) virtual void MyWBEventHandler::onWbEvent(S_UINT32 wbId, FS_SDK::WhiteBoardEvent event, S_UINT userData) override { g_pSuperLog->WriteLogMsg("白板事件 wbId=%d, event=%d\n", wbId, event); // 根据event类型处理业务逻辑 }
激活/取消激活白板:
// active为true表示激活,false表示取消激活 m_WhiteBoardManager->activeWB(wbId, true);
插件FMPrinter, 从SDK中的FMPrinter文件夹中获取;
插件如下:


使用时,安装时运行 FMPrinter.exe 即可
eg:在 Inno Setup 打包脚本中[Run]节点下写入
[Files]
Source: "{#ComonBinDir}\ZX\FMPrinter\*"; DestDir: "{userappdata}\{#MyPrinterDirName}"; Flags: ignoreversion recursesubdirs createallsubdirs uninsneveruninstall;Check:CheckVersion
;{#ComonBinDir}\ZX\FMPrinter 打包的那个路径下的所有文件拷贝到 安装的路径中
[Run]
Filename: "{userappdata}\{#MyPrinterDirName}\FMPrinter.exe";Parameters: "-u";Flags: runhidden;Check:CheckVersion
;相当于cmd FMPrinter.exe -u
Filename: "{userappdata}\{#MyPrinterDirName}\FMPrinter.exe";Flags: runhidden;Check:CheckVersion
;{userappdata}\{#MyPrinterDirName}\FMPrinter.exe 表示安装的插件所在位置,相当于绝对路径
如果只是开发阶段,只需要在命令行中运行即可;eg:
安装的时候也是只需要像在命令行执行安装FMPrinter插件即可
注:
1、如果没有安装此插件,无法进行文档转换;相当于共享文档功能不可用
2、要使用共享office等相关文档,需安装office或者WPS
//会议室窗口布局类 struct RoomWndState { //区域布局风格 enum AreaLayoutStyle { AREA_LAYOUT_STYLE_TILE = 0,//平铺风格类型 AREA_LAYOUT_STYLE_TABLE = 1,//tab切换风格类型 AREA_LAYOUT_STYLE_SPLIT = 2,//分屏风格类型 AREA_LAYOUT_STYLE_POP = 3 //弹出风格类型 }; //会议室窗口布局模式 enum LayoutMode { LAYOUT_MODE_NORMAL = 1, //默认模式 LAYOUT_MODE_DATA, //数据模式 LAYOUT_MODE_VIDEO, //视频固定模式(1~64分屏) LAYOUT_MODE_DATAFULL, //视频模式全屏 }; //分屏风格 enum SplitStyle { SPLIT_STYLE_AUTO = 0, //默认自动 SPLIT_STYLE_1 = 1, //一分屏 SPLIT_STYLE_2 = 2, SPLIT_STYLE_P_IN_P = 3, //画中画 SPLIT_STYLE_4 = 4, SPLIT_STYLE_6 = 6, SPLIT_STYLE_9 = 9, SPLIT_STYLE_12 = 12, SPLIT_STYLE_16 = 16, SPLIT_STYLE_25 = 25, SPLIT_STYLE_36 = 36, SPLIT_STYLE_49 = 49, SPLIT_STYLE_64 = 64, SPLIT_STYLE_NO = 50, //无分屏 SPLIT_STYLE_1_FOCUS = 1001, //一焦点 SPLIT_STYLE_2_FOCUS = 1002, //2焦点 }; //数据类型 enum DataType { DATA_TYPE_CONTAINER = 0, //默认,容器类型 DATA_TYPE_WB = 1, //白板 DATA_TYPE_APPSHARE = 2, //屏幕共享 DATA_TYPE_WEB = 3, //Web协同浏览 DATA_TYPE_MEDIASHARE = 4, //媒体共享 DATA_TYPE_VOTE = 5, //电子投票 DATA_TYPE_VIDEO = 6, //视频 DATA_TYPE_VIDEOPOLLING = 7 //U视频轮巡 }; //数据块信息 struct DataBlock { S_BYTE pos = 0; //索引顺序 DataType data_type = DATA_TYPE_CONTAINER; //类型 S_UINT32 data_id = 0; //业务数据ID S_UINT32 user_data = 0; //用户自定义类型 }; typedef LVector<DataBlock> DataBlockVector; //数据区域信息 struct AreaData { S_BYTE id = 0; S_BYTE screen_id = 0; //所属屏ID AreaLayoutStyle style = AREA_LAYOUT_STYLE_TILE; //区域布局样式 S_UINT32 user_data = 0; //自定义字段 DataBlockVector data_block_vector; //数据块列表 }; struct FullArea { AreaLayoutStyle style = AREA_LAYOUT_STYLE_TILE; //全屏区域风格 S_UINT32 user_data = 0;//自定义字段 typedef LVector<S_BYTE> IDVector; IDVector id_set; //全屏区域显示的区域ID集合 }; S_BYTE screen_id = 0; //屏幕ID LayoutMode layout_mode = LAYOUT_MODE_NORMAL; //布局模式 FullArea full_area; //全屏区域 AreaData tab_area; //tab风格区域 AreaData split_area; //split风格区域 DataBlock full_data_block; //单个全屏的数据项 }; typedef LVector<RoomWndState> RoomWndStateVector; // 会议布局信息 struct RoomLayoutInfo { S_BOOL valid_full_mode;//保持全屏模式 S_UINT32 user; //同步者 RoomWndStateVector wnd_state; //窗口布局 };
通过 IMeetingEventHandler::OnLayoutEvent 回调接收布局同步信息,然后解析 RoomWndState:
// IMeetingEventHandler中接收布局事件 virtual void MyMeetingEventHandler::OnLayoutEvent(const FS_SDK::RoomLayoutInfo& layout) override { g_pSuperLog->WriteLogMsg("收到布局同步 user=%d\n", layout.user); if (layout.wnd_state.size() > 0) { // 解析窗口布局 ModifyVideoPos(layout.wnd_state[0]); } } // 解析视频显示顺序 void CMeetingLayoutDlg::ModifyVideoPos(const FS_SDK::RoomWndState& state) { for (auto &videopos : state.split_area.data_block_vector) { if (videopos.data_type != FS_SDK::RoomWndState::DATA_TYPE_VIDEO) continue; S_UINT32 dwUserID = videopos.data_id; S_BYTE bMediaID = videopos.user_data; int nPos = videopos.pos; g_pSuperLog->WriteLogMsg("视频: 用户id:%d, 通道:%d POS:%d\n", dwUserID, bMediaID, nPos); m_VideoRelay.SetVideoID(nPos, dwUserID, bMediaID); } RelayWindow(); }
详情见SdkDemo CMeetingLayoutDlg类中相关处理。
调用 IMeetingManager::broadcastLayout 广播布局:
void CMeetingLayoutDlg::BroadcastLayout() { FS_SDK::RoomLayoutInfo layoutinfo; FS_SDK::RoomWndState wndstate; FS_SDK::RoomWndState::AreaData split_area; (auto& video : m_lsVideo) { FS_SDK::RoomWndState::DataBlock videodata; videodata.data_id = video->GetVideoUserID(); videodata.user_data = video->GetVideoMediaID(); videodata.data_type = FS_SDK::RoomWndState::DATA_TYPE_VIDEO; split_area.data_block_vector.push_back(videodata); } split_area.id = DEFAULT_SPLIT_AREA_ID; wndstate.split_area = split_area; layoutinfo.valid_full_mode = true; layoutinfo.user = m_UserManager->getLocalUser(); layoutinfo.wnd_state.push_back(wndstate); // broadcastLayout广播布局 FS_SDK::ErrCode hR = m_MeetingManager->broadcastLayout(layoutinfo); if (FS_SDK::ERR_SUCCESS != hR) { g_pSuperLog->WriteFile(L"同步布局失败\n"); } }
| 权限 | 管理员 | 主持人 | 参会人 | 备注 |
|---|---|---|---|---|
| 可多种权限同时具有 | 一场会议可存在多个 | 一场会议最多存在一个 | 一场会议可存在多个 | |
| 请出会议室 | 可以 | 无权限 | 无权限 | |
| 音视频控制 | 控制他人与自己 | 控制他人与自己 | 仅能控制自己 | 与后台设置相关 |
| 修改昵称/名称 | 修改他人与自己 | 仅修改自己 | 仅修改自己 | 与后台设置相关 |
| 申请主持人 | 直接成为主持人 | 无 | 需原主持人转让,无主持人自动成为主持人 | |
| 授予主持人 | 可以 | 可以,准确来说是转让 | 无权限 | |
| 同步布局 | 除分享外不能同步 | 默认同步 | 除分享外不能同步 | |
| 录制 | 有权限 | 有权限 | 所有人录制时有权限 | |
| 权限设置 | 仅PC能设置 | 仅PC能设置 | 无权限 | |
| 管理员可成为主持人,此时具有双重身份,两个角色的权限 |
调用 IMeetingManager::applyToBeHost 申请主持人,角色改变后通过 IMeetingEventHandler::onPresenterStateEvent 回调通知。
// 申请主持人 m_MeetingManager->applyToBeHost(true); // 主持人角色变化回调 virtual void MyMeetingEventHandler::onPresenterStateEvent(S_UINT32 userId, S_BYTE state) override { g_pSuperLog->WriteLogMsg("主持人角色变化 userId=%d, state=%d\n", userId, state); }
调用 IMeetingManager::applyToBeManager 申请管理员,管理员角色变化通过 IMeetingEventHandler::onManagerStateEvent / onManagerApplyEvent 回调通知。
// 申请管理员 m_MeetingManager->applyToBeManager(pwd); // 管理员申请结果回调 virtual void MyMeetingEventHandler::onManagerApplyEvent(FS_SDK::ErrCode err) override { g_pSuperLog->WriteLogMsg("管理员申请结果 err=%d\n", err); } // 管理员角色变化回调 virtual void MyMeetingEventHandler::onManagerStateEvent(S_UINT32 userId, S_BYTE state) override { g_pSuperLog->WriteLogMsg("管理员角色变化 userId=%d, state=%d\n", userId, state); }
// IMeetingManager录制相关接口 // 获取录制参数 virtual RecordParam getLocalRecordParam(); // 设置录制参数 virtual ErrCode setLocalRecordParam(const RecordParam& param); // 开启录制 - path为录制路径,name为文件名(可为默认路径) virtual ErrCode startLocalRecord(S_PTCHAR path, S_PTCHAR name); // 暂停/继续录制 virtual ErrCode pauseLocalRecord(S_BOOL pause); // 停止录制 virtual ErrCode stopLocalRecord();
struct RecordParam { enum class RecordType : BYTE { RECORDTYPE_MP4 = 2, //mp4 RECORDTYPE_WMV = 3, //wmv RECORDTYPE_WMA = 4 //wma }; enum class RecordQuality : BYTE { RECORDQUALITY_LOW = 1, //录制质量低 RECORDQUALITY_MEDIUM = 2, //录制质量中 RECORDQUALITY_HIGH = 3 //录制质量高 }; enum class RecordRect : BYTE { RECORDRECT_FULL = 1, //录制当前桌面窗口 RECORDRECT_SELF = 2, //录制程序窗口 RECORDRECT_SPECIAL = 3 //录制指定区域 }; RecordType record_type; //保存录制文件格式类型 RecordQuality record_quality; //质量 RecordRect record_rect; //需要录制的方式 HWND capture_hwnd; //录制的窗口句柄 RECT record_area; //录制区域 };
当 RecordRect 值为 RECORDRECT_SELF时传入 HWND capture_hwnd,程序会录制该句柄区域;
当 RecordRect 值为 RECORDRECT_FULL时,表示录制整个桌面(主屏幕)
当 RecordRect 值为 RECORDRECT_SPECIAL时, 传入 RECT record_area 值,录制该屏幕区域。
注:当 RecordType 为 RECORDTYPE_WMA 时,仅录制音频,并且是录制会中的音频,如果会中无人发言,则录制文件中是没有声音的。
录制插件在SDK中,内容如下:
安装时需要注册录制插件,可将录制插件ThirdComponent 目录与程序放在同一个文件夹;
开发时,不安装,只需要在插件目录运行ThridCtrlReg.exe即可
注: 未注册改插件,无法使用录制功能
Inno Setup 打包示例:
;解决录制的问题
Filename: {#MyCommonDirName}\ThirdComponent\ThridCtrlReg.exe; Flags: runhidden
// 判断录制权限 bool canRecord = m_PermissionManager->checkRoomPermission(FS_SDK::ROOM_LOCAL_RECORD); if (!canRecord) { g_pSuperLog->WriteFile(L"没有权限录制会议!\n"); return; } tstring path; // 获取录制参数 FS_SDK::RecordParam recordParam = m_MeetingManager->getLocalRecordParam(); FS_SDK::ErrCode hR = FS_SDK::ERR_FAIL; switch (recordParam.record_rect) { case FS_SDK::RecordParam::RecordRect::RECORDRECT_SELF: recordParam.capture_hwnd = m_hNotifyWnd; m_MeetingManager->setLocalRecordParam(recordParam); hR = m_MeetingManager->startLocalRecord(path.c_str(), nullptr); break; case FS_SDK::RecordParam::RecordRect::RECORDRECT_FULL: hR = m_MeetingManager->startLocalRecord(path.c_str(), nullptr); break; case FS_SDK::RecordParam::RecordRect::RECORDRECT_SPECIAL: recordParam.record_area = { 0, 0, 800, 600 }; m_MeetingManager->setLocalRecordParam(recordParam); hR = m_MeetingManager->startLocalRecord(path.c_str(), nullptr); break; default: recordParam.record_rect = FS_SDK::RecordParam::RecordRect::RECORDRECT_FULL; m_MeetingManager->setLocalRecordParam(recordParam); hR = m_MeetingManager->startLocalRecord(path.c_str(), nullptr); break; } if (FS_SDK::ERR_SUCCESS == hR) { UpdateRecordState(RECORDING); } else { UpdateRecordState(RECORDSTOP); g_pSuperLog->WriteLogMsg("录制会议失败! err=%d\n", hR); } // 暂停/继续录制 m_MeetingManager->pauseLocalRecord(true); // 暂停 m_MeetingManager->pauseLocalRecord(false); // 继续 // 停止录制 m_MeetingManager->stopLocalRecord();
通讯录相关功能通过 IContactsManager 管理:
// IContactsManager主要接口 // 异步查询企业通讯录信息 virtual ErrCode queryCompanyUsers(S_UINT32 size); // 获取组织架构信息 virtual DeptNodes getDeptNodes(); // 获取所有联系人信息 virtual ContactInfoList getAllContact(); // 邀请用户入会 virtual ErrCode inviteUsers(const S_UINT32* users, S_UINT32 sz, MeetingInvitation* invitation); // 同意/拒绝邀请 virtual ErrCode acceptMeetingInvite(S_UINT32 hostUserId, S_UINT32 inviteId, bool isAccept); // 放弃邀请 virtual ErrCode abandonInvite(S_UINT32 inviteId, const S_UINT32* users, S_UINT32 sz);
// 组织架构信息 (DeptNodes,树状结构) // 通过 getDeptNodes() 获取 // 联系人信息 (ContactInfoList) // 通过 getAllContact() 获取 // 邀请信息 struct MeetingInvitation { S_UINT32 inviteId = 0; // 邀请ID S_UINT32 inviterUserId = 0; // 邀请者ID FString inviterUserName; // 邀请者名称 S_UINT32 meetingId = 0; // 会议ID // ... 其他字段 };
事件通过 IContactsEventHandler 回调通知:
// 通讯录查询完成 virtual void MyContactsEventHandler::onQueryCompanyUsersCompleted() override { g_pSuperLog->WriteFile(L"通讯录查询完成\n"); // 获取组织架构 FS_SDK::DeptNodes depts = m_ContactsManager->getDeptNodes(); // 获取所有联系人 FS_SDK::ContactInfoList contacts = m_ContactsManager->getAllContact(); } // 收到邀请 virtual void MyContactsEventHandler::onMeetingInviteIncome(const FS_SDK::MeetingInvitation& invitation) override { g_pSuperLog->WriteLogMsg("收到邀请 inviteId=%d, inviter=%d\n", invitation.inviteId, invitation.inviterUserId); // 同意或拒绝 m_ContactsManager->acceptMeetingInvite(invitation.inviterUserId, invitation.inviteId, true); } // 邀请被接受/拒绝 virtual void MyContactsEventHandler::onMessageInviteResponse(S_UINT32 inviteId, S_UINT32 userId, FS_SDK::ErrCode code) override { g_pSuperLog->WriteLogMsg("邀请响应 inviteId=%d, userId=%d, code=%d\n", inviteId, userId, code); } // 邀请被取消 virtual void MyContactsEventHandler::onMeetingInviteAbandoned(S_UINT32 inviteId, FS_SDK::ErrCode reason) override { g_pSuperLog->WriteLogMsg("邀请被取消 inviteId=%d, reason=%d\n", inviteId, reason); }
成功登录会前在线后SDK立即请求组织架构、企业用户列表数据并保存在SDK层,应用层可调用 queryCompanyUsers 异步查询,查询完成后通过 IContactsEventHandler::onQueryCompanyUsersCompleted 回调通知,然后调用 getDeptNodes 和 getAllContact 获取数据。
// 异步查询通讯录 m_ContactsManager->queryCompanyUsers(0); // 查询完成后在 onQueryCompanyUsersCompleted 回调中获取数据
会前和会中均可调用 inviteUsers 对通讯录在线用户发起邀请,会前发起邀请后SDK将首先创建即时会议并立即入会,然后将选择的用户邀请到该会议室。会中则直接将选择的用户邀请到当前会议室。
// 邀请用户 S_UINT32 userIds[] = { 1001, 1002, 1003 }; FS_SDK::MeetingInvitation invitation; m_ContactsManager->inviteUsers(userIds, 3, &invitation);
邀请被接受/拒绝则由SDK层响应后通过 IContactsEventHandler::onMessageInviteResponse 回调通知应用层。
邀请时序:
详情见demo。
void InitAndGetManagers() { // 1. 创建 SDK 实例 FS_SDK::ISdkManager* sdk = FS_SDK::ISdkManager::getInstance(); // 2. 初始化 SDK FS_SDK::SdkContext ctx; ctx.appId = _T("your_app_id"); ctx.appVer = _T("1.0.0"); ctx.rootPath = _T("C:\\sdk_data"); FS_SDK::ErrCode ec = sdk->initSdk(&ctx); if (ec != FS_SDK::ERR_SUCCESS) { // 初始化失败处理 return; } // 3. 设置全局事件处理 class MyGlobalHandler : public FS_SDK::ISdkManagerEventHandler { void onConnectServerFailed() override { } void onNetReconnect() override { } void onNetReconnectSucess() override { } void onNetworkAborted() override { } }; // ISdkInterface::setEventHandler // 需通过 ISdkInterface 指针设置 // 4. 获取各模块管理器 FS_SDK::ILoginManager* loginMgr = sdk->getLoginManager(); FS_SDK::IAudioManager* audioMgr = sdk->getAudioManager(); FS_SDK::IVideoManager* videoMgr = sdk->getVideoManager(); FS_SDK::IMeetingManager* meetMgr = sdk->getMeetingManager(); FS_SDK::IUserManager* userMgr = sdk->getUserManager(); FS_SDK::IChatManager* chatMgr = sdk->getChatManager(); FS_SDK::IContactsManager* contMgr = sdk->getContactsManager(); FS_SDK::IScreenShareManager* ssMgr = sdk->getScreenShareManager(); FS_SDK::IMediaShareManager* msMgr = sdk->getMediaShareManager(); FS_SDK::IPermissionManager* permMgr = sdk->getPermissionManager(); FS_SDK::IWhiteBoardManager* wbMgr = sdk->getWhiteBoardManager(); }
sdk->setParam(_T("log_level"), _T("3")); sdk->setParam(_T("enable_encrypt"), _T("1"));
S_UINT32 taskId = sdk->uploadLog(_T("C:\\logs\\sdk_log.zip"), FS_SDK::UploadLogSource::MANUAL); // 结果通过 onUploadLog 回调通知
void Cleanup()
{
FS_SDK::ISdkManager* sdk = FS_SDK::ISdkManager::getInstance();
sdk->releaseSdk();
}
class MyEventHandler : public FS_SDK::IEventHandler { void onConnectServerFailed() override { // 连接服务器失败,提示用户重试 } void onNetReconnect() override { // 网络断开,正在重连 } void onNetReconnectSucess() override { // 重连成功 } void onNetworkAborted() override { // 网络无法恢复,需要重新初始化 } void onUploadLog(S_UINT32 taskId, S_INT32 code, S_PCHAR msg) override { // 日志上传结果 } }; MyEventHandler g_handler; void SetupEventHandler(FS_SDK::ISdkInterface* sdkIf) { sdkIf->setEventHandler(&g_handler); }
FS_SDK::ErrCode ec = m_LoginManager->setServerAddr(
_T("apaas.hst.com"),
443
);
// 设置多个服务器地址,用分号分隔 FS_SDK::ErrCode ec = m_LoginManager->setServerAddrEx( _T("apaas1.hst.com:443;apaas2.hst.com:8443") );
FS_SDK::ErrCode ec = m_LoginManager->setClientInfo(
_T("your_client_id"),
_T("your_client_secret")
);
FS_SDK::ErrCode ec = m_LoginManager->loginAccount(
(S_PTCHAR)_T("zhangsan"),
(S_PTCHAR)_T("password123"),
true // 强制挤下前一个登录
);
if (ec != FS_SDK::ERR_SUCCESS) {
// 调用失败,结果通过 onAccountLoginCompleted 回调通知
}
// 使用第三方鉴权 token 登录(需 CES 后台对接过) FS_SDK::ErrCode ec = m_LoginManager->loginAccountWithToken( (S_PTCHAR)_T("user_id_from_3rd"), (S_PTCHAR)_T("token_from_3rd_auth"), true );
// 匿名入会,直接通过会议号登录 FS_SDK::ErrCode ec = m_LoginManager->loginRoom( (S_PTCHAR)_T("nickname"), 123456, (S_PTCHAR)_T("room123") );
// 账号登录后,通过会议号入会 FS_SDK::ErrCode ec = m_LoginManager->enterRoom( true, // mine: 是否我的会议室 123456, // roomId: 会议号 (S_PTCHAR)_T("pwd123") // roomPwd: 会议密码 );
// 取消正在进行的入会操作
m_LoginManager->cancelEnterRoom();
// 退出登录
m_LoginManager->logout();
void MyLoginEventHandler::onAccountLoginCompleted(FS_SDK::ErrCode err) override { if (err == FS_SDK::ERR_SUCCESS) { // 账号登录成功,可以调用 enterRoom 入会 g_pLoginMgr->enterRoom(false, 123456, _T("")); } else { // 登录失败处理 } }
void MyLoginEventHandler::onAnonymousLoginCompleted(FS_SDK::ErrCode err) override { if (err == FS_SDK::ERR_SUCCESS) { // 匿名登录(入会)成功 } }
void MyLoginEventHandler::onEnterRoomCompleted(FS_SDK::ErrCode err) override { if (err == FS_SDK::ERR_SUCCESS) { // 入会成功,可以开始会议操作 // 初始化白板等 } }
void MyLoginEventHandler::onInitCompleted() override { // 会中资源初始化完成,可以广播音视频等操作 }
void MyLoginEventHandler::onPreStartMeeting() override { // 准备好会中资源后,调用 startMeeting() 开始会议 g_pMeetingMgr->startMeeting(); }
void MyLoginEventHandler::onExitedRoom() override { // 已退会,需调用 releaseSdk // 然后调用 logout g_pLoginMgr->logout(); FS_SDK::ISdkManager::getInstance()->releaseSdk(); }
void MyLoginEventHandler::onUpdateServerCompleted(FS_SDK::ErrCode err) override { // 更新服务器地址完成 }
void MyLoginEventHandler::onAccountLogout(FS_SDK::ErrCode err) override { // 账号已登出 }
void MyLoginEventHandler::onAnonymousLoginPaasCompleted(FS_SDK::ErrCode err) override { // 匿名登录 PaaS 完成 }
void MyLoginEventHandler::onWaitingForAllowToEnter(FS_SDK::ErrCode err) override { // 等待主持人同意进入会议 }
// 获取扬声器列表 FS_SDK::AudioDeviceList speakers = m_AudioManager->getSpeakerDeviceList(); for (auto& dev : speakers) { _tprintf(_T("Speaker[%d]: %s\n"), dev.index, dev.name); } // 获取麦克风列表 FS_SDK::AudioDeviceList mics = m_AudioManager->getMicrophoneDeviceList();
S_INT32 curSpeaker = m_AudioManager->getSpeakerDevice(); S_INT32 curMic = m_AudioManager->getMicrophoneDevice();
m_AudioManager->setSpeakerDevice(0); // 设置第一个扬声器 m_AudioManager->setMicrophoneDevice(1); // 设置第二个麦克风
m_AudioManager->setSpeakerDeviceEnable(true); // 启用扬声器 m_AudioManager->setMicrophoneEnable(true); // 启用麦克风
S_INT32 vol, maxVol, minVol; m_AudioManager->getSpeakerVolume(&vol, &maxVol, &minVol); _tprintf(_T("Current speaker vol: %d/%d\n"), vol, maxVol); m_AudioManager->setSpeakerVolume(80); // 设置为 80
S_INT32 micVol, micMax, micMin;
m_AudioManager->getMicrophoneVolume(&micVol, &micMax, &micMin);
m_AudioManager->setMicrophoneVolume(70);
// 获取音频参数 S_INT32 param; m_AudioManager->getAudioParam(FS_SDK::AudioParamKey::AUDIO_PARAM_BITRATE, param); // 设置音频参数 m_AudioManager->setAudioParam(FS_SDK::AudioParamKey::AUDIO_PARAM_BITRATE, 64000);
// 广播本端音频(自己申请广播) FS_SDK::ErrCode ec = m_AudioManager->applyBroadcastAudio(true); // 申请广播 // ec = m_AudioManager->applyBroadcastAudio(false); // 取消广播
// 广播远端用户音频(主持人操作) FS_SDK::ErrCode ec = m_AudioManager->broadcastAudio(userId, true); if (ec == FS_SDK::ERR_REQUEST_PENDING) { // 需要等待对方应答 } else if (ec == FS_SDK::ERR_SUCCESS) { // 成功 }
// 在 onApplyAudioEvent 中回应 void MyAudioEventHandler::onApplyAudioEvent(const FS_SDK::ApplyAVRightInfo* info) override { FS_SDK::ApplyAVRightInfo answer = *info; answer.agree = true; // 同意对方广播本端音频 g_pAudioMgr->answerApplyAudioRight(&answer); }
// 开始接收某用户音频(非自动接收模式) m_AudioManager->startRemoteAudio(userId); // 停止接收 m_AudioManager->stopRemoteAudio(userId);
S_INT32 energy;
m_AudioManager->getUserEnergy(userId, &energy);
_tprintf(_T("User %d audio energy: %d\n"), userId, energy);
// 监听本地采集的原始 PCM 数据 m_AudioManager->setLocalAudioDataObserver(true); // 数据在 IAudioEventHandler::onLocalAudioCapData 中获取
S_INT32 capE, capMax, capMin; m_AudioManager->getCapEnergy(&capE, &capMax, &capMin); S_INT32 playE, playMax, playMin; m_AudioManager->getPlayEnergy(&playE, &playMax, &playMin);
void MyAudioEventHandler::onCaptureDeviceChanged() override { // 采集设备变化,刷新设备列表 } void MyAudioEventHandler::onPlayDeviceChanged() override { // 播放设备变化 }
void MyAudioEventHandler::onUserAudioEvent(S_UINT32 userId, S_BYTE state) override { // state: 0=未广播, 1=申请中, 2=广播中 switch (state) { case 0: _tprintf(_T("User %d stopped broadcasting\n"), userId); break; case 1: _tprintf(_T("User %d requesting audio\n"), userId); break; case 2: _tprintf(_T("User %d is broadcasting audio\n"), userId); break; } }
void MyAudioEventHandler::onApplyAudioEvent(const FS_SDK::ApplyAVRightInfo* info) override { // 对方申请打开本端音频 FS_SDK::ApplyAVRightInfo answer = *info; // UI 弹窗询问用户,然后设置 agree answer.agree = true; g_pAudioMgr->answerApplyAudioRight(&answer); }
void MyAudioEventHandler::onLocalAudioCapData(S_PBYTE data, S_UINT32 dataLen) override { // 本地采集的 PCM 音频数据,可以写入文件或做处理 // 需先调用 setLocalAudioDataObserver(true) }
FS_SDK::VideoDeviceList devices = m_VideoManager->getVideoDeviceList(); for (auto& dev : devices) { _tprintf(_T("Camera[%d]: %s\n"), dev.channel, dev.name); }
// 设置默认视频设备 m_VideoManager->setDefaultVideoDevice(0); // 获取默认视频设备 S_BYTE ch = m_VideoManager->getDefaultVideoDevice(); // 判断是否为默认设备 FS_SDK::ErrCode ec = m_VideoManager->isDefaultVideoDevice(ch); if (ec == FS_SDK::ERR_SUCCESS) { // 是默认设备 }
// 获取视频参数 S_INT32 fps; m_VideoManager->getVideoParam(0, FS_SDK::VideoParamKey::VIDEO_PARAM_FPS, fps); // 设置视频参数 m_VideoManager->setVideoParam(0, FS_SDK::VideoParamKey::VIDEO_PARAM_FPS, 30); m_VideoManager->setVideoParam(0, FS_SDK::VideoParamKey::VIDEO_PARAM_BITRATE, 1500000); m_VideoManager->setVideoParam(0, FS_SDK::VideoParamKey::VIDEO_PARAM_RESOLUTION, 2); // 720p
// 开始本端视频预览 HWND hPreviewWnd = GetDlgItem(IDC_LOCAL_VIDEO); m_VideoManager->startLocalVideoPreview(0, hPreviewWnd); // 停止预览 m_VideoManager->stopLocalVideoPreview(0);
// 接收远端用户视频 HWND hRemoteWnd = GetDlgItem(IDC_REMOTE_VIDEO); m_VideoManager->startCheckVideoView(userId, 0, hRemoteWnd); // 停止接收 m_VideoManager->stopCheckVideoView(userId, 0);
// 暂停/恢复接收远端视频 m_VideoManager->pauseVideo(userId, 0, true); // 暂停 m_VideoManager->pauseVideo(userId, 0, false); // 恢复
// 设置视频显示模式 m_VideoManager->setDisplayMode(userId, 0, FS_SDK::DisplayMode::DISPLAY_MODE_FIT); // 适应 // m_VideoManager->setDisplayMode(userId, 0, FS_SDK::DisplayMode::DISPLAY_MODE_FILL); // 填充
// 手机端切换摄像头
m_VideoManager->cameraToggle();
// 广播本端视频 m_VideoManager->applyBroadcastVideo(0, true); // 申请广播 m_VideoManager->applyBroadcastVideo(0, false); // 停止广播
// 广播远端用户视频(主持人操作) m_VideoManager->broadcastVideo(userId, 0, true);
void MyVideoEventHandler::onApplyVideoEvent(const FS_SDK::ApplyAVRightInfo* info) override { FS_SDK::ApplyAVRightInfo answer = *info; answer.agree = true; g_pVideoMgr->answerApplyVideoRight(&answer); }
// 视频截图 m_VideoManager->saveRawPicture( userId, 0, _T("C:\\screenshots"), _T("user_1234.jpg") );
// 监听视频数据 m_VideoManager->setVideoFrameDataObserver(userId, 0, true); // 数据在 onUserVideoData 回调中获取
// 监听解码后原始视频帧(YUV) m_VideoManager->setVideoFrameRawDataObserver(userId, 0, true); // 数据在 onUserVideoRawData 回调中获取
// 监听本地摄像头采集原始帧 m_VideoManager->setLocalVideoFrameRawDataObserver(0, true); // 数据在 onLocalVideoRawData 回调中获取
// 添加 RTSP 设备 S_INT32 devId = m_VideoManager->addRtspDevice( _T("Camera1"), _T("rtsp://admin:12345@192.168.1.100:554/stream") ); // 移除 RTSP 设备 m_VideoManager->removeRtspDevice(devId);
// 开始本地摄像头录制 m_VideoManager->startLocalCameraRecord( _T("C:\\recordings"), 0, true, // is_record_audio 60 // spit_time (分钟分割) ); // 停止录制 m_VideoManager->stopLocalCameraRecord(0); // 查询录制状态 S_BOOL isRecording = m_VideoManager->isLocalCameraRecording(0);
// 设置新的渲染窗口 m_VideoManager->setRenderView(userId, 0, hNewWnd);
void MyVideoEventHandler::onVideoDeviceChanged() override { // 视频设备变化,刷新设备列表 }
void MyVideoEventHandler::onUserVideoEvent(S_UINT32 userId, S_BYTE channel, S_BYTE state) override { // state: 0=停止, 1=申请中, 2=广播中 _tprintf(_T("User %d video state: %d\n"), userId, state); }
void MyVideoEventHandler::onApplyVideoEvent(const FS_SDK::ApplyAVRightInfo* info) override { // 对方申请打开本端视频 }
void MyVideoEventHandler::onUserVideoData(S_UINT32 userId, S_BYTE ch, S_PBYTE data, S_UINT32 len) override { } void MyVideoEventHandler::onUserVideoRawData(S_UINT32 userId, S_BYTE ch, S_PBYTE data, S_UINT32 len, S_UINT32 fourcc, S_SIZE wh, S_INT32 rot) override { } void MyVideoEventHandler::onLocalVideoRawData(S_BYTE ch, S_PBYTE data, S_UINT32 len, S_UINT32 fourcc, S_SIZE wh, S_INT32 rot) override { }
void MyVideoEventHandler::onAddRtspResult(FS_SDK::ErrCode err, S_UINT32 devId) override { if (err == FS_SDK::ERR_SUCCESS) { _tprintf(_T("RTSP device added, id=%u\n"), devId); } }
void MyVideoEventHandler::onLocalDeviceRecordFail(S_UINT32 devId) override { _tprintf(_T("Local camera record failed, dev=%u\n"), devId); }
m_MeetingManager->queryRoomList(1, 20, _T("")); // 结果在 IMeetingEventHandler::onQueryRoomList 中获取
m_MeetingManager->queryRoomListOfSchedule(1, 20, _T(""), 7, 0);
m_MeetingManager->queryRoomListOfInstant();
S_INT32 taskId = m_MeetingManager->createInstantMeeting(0); // 结果在 onCreateInstantMeeting 中获取
m_MeetingManager->queryRoomShareInfo(123456);
// 查询入会记录 m_MeetingManager->queryJoinMeetingRecords(50); // 插入入会记录 m_MeetingManager->insertJoinMeetingRecord(_T("room_id:123456")); // 移除指定记录 m_MeetingManager->removeJoinMeetingRecord(1); // 清空所有记录 m_MeetingManager->clearJoinMeetingRecords();
FS_SDK::MeetingInfo info = m_MeetingManager->getMeetingInfo();
_tprintf(_T("Room: %s, ID: %u\n"), info.roomName, info.roomId);
m_MeetingManager->queryClientSupportAbility(userId);
// 申请成为管理员 m_MeetingManager->applyToBeManager(_T("admin_pwd")); // 放弃管理员身份 m_MeetingManager->abandonTheManager();
// 授予远端用户管理员权限 m_MeetingManager->grantTheManagerForRemoteUser(userId, true);
// 申请成为主持人 m_MeetingManager->applyToBeHost(true); // 放弃主持人身份 m_MeetingManager->abandonTheHost();
// 同意远端成为主持人 m_MeetingManager->agreeToBeHost(userId, true);
// 授予远端用户主持人权限 m_MeetingManager->grantTheHostForRemoteUser(userId, true);
m_MeetingManager->startLocalRecord(
_T("C:\\recordings"),
_T("meeting_20240725")
);
m_MeetingManager->pauseLocalRecord(true); // 暂停 m_MeetingManager->pauseLocalRecord(false); // 恢复
m_MeetingManager->stopLocalRecord();
FS_SDK::RecordParam param; param.format = FS_SDK::RecordParam::MP4; param.fps = 15; param.includeAudio = true; m_MeetingManager->setLocalRecordParam(param);
FS_SDK::RecordParam param = m_MeetingManager->getLocalRecordParam();
FS_SDK::RoomLayoutInfo layout; layout.mode = FS_SDK::LayoutMode::GALLERY; m_MeetingManager->broadcastLayout(layout);
m_MeetingManager->kickUser(userId); // 请出用户
m_MeetingManager->quietMeeting(true); // 静音会场,允许再次广播
m_MeetingManager->enableWaterMark(true); // 开启水印 m_MeetingManager->enableWaterMark(false); // 关闭水印
m_MeetingManager->startCloudRecord(true); // 开始云录制 m_MeetingManager->startCloudRecord(false); // 停止云录制
m_MeetingManager->EnableSoundTouch(true); // 开启变声
// 私聊用户(语音私聊) m_MeetingManager->privateTalk(userId, true); // 发起私聊 m_MeetingManager->privateTalk(userId, false); // 结束私聊
// 在 onPreStartMeeting 回调后调用
m_MeetingManager->startMeeting();
// 退出会议(仅本端退出)
m_MeetingManager->exitMeeting();
// 关闭会议(所有人退出)
m_MeetingManager->closeMeeting();
FS_SDK::UserList users; users.push_back(targetUserId); m_MeetingManager->sendTransdataMsg(users, _T("hello")); // 传空 UserList 发送给所有人
// 发起投票 FS_SDK::VoteInfo vote; vote.title = _T("Do you agree?"); m_MeetingManager->startVote(vote); // 停止投票 m_MeetingManager->stopVote(voteId); // 公开结果 m_MeetingManager->publicVoteResult(voteId);
// 快速签到应答 m_MeetingManager->quickRollCallAck(srcUser, dstUser, _T("guid-from-notification"));
FS_SDK::RoomUserInfoList users = m_UserManager->getAllUsers(); for (auto& u : users) { _tprintf(_T("User %u: %s (offline=%d)\n"), u.userId, u.nickName, u.isOffline); }
const FS_SDK::RoomUserInfo* info = m_UserManager->getUserInfo(userId); if (info) { _tprintf(_T("Nick: %s\n"), info->nickName); }
S_UINT32 count = m_UserManager->getOnlineUserCount();
_tprintf(_T("Online users: %u\n"), count);
S_UINT32 myId = m_UserManager->getLocalUser();
_tprintf(_T("My user ID: %u\n"), myId);
S_BOOL isAdmin = m_UserManager->isManager(userId); S_BOOL isHost = m_UserManager->isHost(userId);
m_UserManager->setNickName(myId, _T("New Nickname"));
// 发送给所有人 m_ChatManager->sendChatMessage(0, _T("Hello everyone!")); // 发送给指定用户 m_ChatManager->sendChatMessage(userId, _T("Hi there!"));
// 审批聊天消息 m_ChatManager->checkChatMessage(msgId, true); // 通过 m_ChatManager->checkChatMessage(msgId, false); // 拒绝
S_INT duration = m_ChatManager->getChatMessageRecallDuration();
// -1 表示服务器不支持,0 表示未开启
m_ChatManager->recallChatMessage(_T("msg_id_to_recall"));
FS_SDK::ContactInfoList contacts = m_ContactsManager->getAllContact();
m_ContactsManager->queryCompanyUsers(100); // 异步,结果在 onQueryCompanyUsersCompleted 中
FS_SDK::DeptNodes nodes = m_ContactsManager->getDeptNodes();
FS_SDK::GroupInfoList groups = m_ContactsManager->getGroupInfoList(FS_SDK::ContactsGroupType::ALL); m_ContactsManager->queryGroupInfo(FS_SDK::ContactsGroupType::ALL);
FS_SDK::GroupInfo group; group.groupId = 1; group.name = _T("Team A"); m_ContactsManager->mondifyGroupInfo(group, false); // false=编辑, true=创建
m_ContactsManager->removeGroup(groupId);
FS_SDK::GroupInfo info; info.groupId = 1; info.userIds.push_back(userId); m_ContactsManager->addGroupUser(info); // 添加用户 m_ContactsManager->removeGroupUser(info); // 移除用户
// 同意/拒绝会议邀请 m_ContactsManager->acceptMeetingInvite(hostUserId, inviteId, true);
// 邀请通讯录用户入会 S_UINT32 users[] = { userId1, userId2 }; FS_SDK::MeetingInvitation inv; m_ContactsManager->inviteUsers(users, 2, &inv);
S_UINT32 users[] = { userId1 };
m_ContactsManager->abandonInvite(inviteId, users, 1);
FS_SDK::ScreenShareInfo info; info.monitorIndex = 0; // 显示器索引 info.captureCursor = true; // 捕获鼠标 info.includeAudio = true; // 包含音频 FS_SDK::ErrCode ec = m_ScreenShareManager->startScreenShare(&info);
m_ScreenShareManager->stopScreenShare(0); // 0 = 停止本端共享
// 开始接收远端屏幕共享 HWND hScreenShareWnd = GetDlgItem(IDC_SCREEN_SHARE); m_ScreenShareManager->startRemoteScreenShareView(userId, 0, hScreenShareWnd); // 停止接收 m_ScreenShareManager->stopRemoteScreenShareView(userId, 0);
S_BOOL isSharing = m_ScreenShareManager->isScreenSharing();
// 请求远程控制 m_ScreenShareManager->queryVncControl(true); // 请求 m_ScreenShareManager->queryVncControl(false); // 释放 // 共享端收回远程控制 m_ScreenShareManager->recoverVncControl(); // 同意/拒绝远程控制 m_ScreenShareManager->allowVncControl(true);
// 在 startRemoteScreenShareView 之前调用 m_ScreenShareManager->setVncVideoRawDataObserver(true); // 数据在 onVncVideoRawData 中获取(仅 FSP 路径有效)
m_MediaShareManager->stopMediaShare(0, 0); // 结束本端多媒体共享
m_MediaShareManager->pauseMediaShare(0, 0, true); // 暂停 m_MediaShareManager->pauseMediaShare(0, 0, false); // 恢复
// 开始接收远端多媒体 HWND hMediaWnd = GetDlgItem(IDC_MEDIA_SHARE); m_MediaShareManager->startRemoteMediaShareView(userId, 0, hMediaWnd); // 停止接收 m_MediaShareManager->stopRemoteMediaShareView(userId, 0);
FS_SDK::ErrCode ec = m_MediaShareManager->isPureMusic(userId, 0); if (ec == FS_SDK::ERR_SUCCESS) { // 是纯音乐共享 }
S_BOOL isSharing = m_MediaShareManager->isMediaSharing();
S_BOOL hasPerm = m_PermissionManager->checkUserPermission(
FS_SDK::UserPermission::CAN_BROADCAST_AUDIO,
userId
);
m_PermissionManager->configUserPermission(
FS_SDK::UserPermission::CAN_BROADCAST_AUDIO,
true, // 赋予权限
userId
);
FS_SDK::UserRole role = m_PermissionManager->checkRolePermission(
FS_SDK::RolePermission::CAN_ROLE_BROADCAST_AUDIO
);
m_PermissionManager->configRolePermission(
FS_SDK::RolePermission::CAN_ROLE_BROADCAST_AUDIO,
FS_SDK::UserRole::HOST
);
m_PermissionManager->checkRoomPermission(
FS_SDK::RoomPermission::WHITEBOARD,
0 // rights_index, 白板 0-6
);
// 结果在 IPermissionEventHandler::onCheckRoomPermission 中
// 请求白板权限 m_PermissionManager->requestRoomPermission( FS_SDK::RoomPermission::WHITEBOARD, 0, // rights_index false // free: false=请求, true=释放 );
// 锁定白板权限,其他人无法请求 m_PermissionManager->lockRoomPermission( FS_SDK::RoomPermission::WHITEBOARD, 0, false // unlock: false=锁定, true=解锁 );
// 入会后初始化白板 m_WBManager->initialize(); // 释放白板模块 m_WBManager->uninitialize();
// 创建空白白板 S_UINT32 wbId = m_WBManager->createWB( 0, // rightsIndex S_SIZE{1920, 1080}, // sz 0, // size (0=默认) nullptr // file (nullptr=空白板) );
// 通过共享文档创建白板 S_UINT32 wbId = m_WBManager->createWBFromDocFile( 0, _T("doc_guid_from_server"), true // shared: 是否共享给所有人 );
m_WBManager->closeWB(wbId); // 关闭指定白板 m_WBManager->closeAllWB(); // 关闭所有白板
m_WBManager->activeWB(wbId, true); // 激活白板 m_WBManager->activeWB(wbId, false); // 取消激活
m_WBManager->setWBPage(wbId, 0); // 切换到第 0 页
m_WBManager->setWBZoom(wbId, 100); // 100% 缩放
m_WBManager->setWBBkColor(wbId, 0xFFFFFF); // 白色背景
m_WBManager->setWBIndicator(wbId, S_POINT{100, 200});
m_WBManager->setWBScroll(wbId, S_POINT{50, 50});
m_WBManager->rotateWB(wbId, true); // 顺时针旋转 90 度 m_WBManager->rotateWB(wbId, false); // 逆时针旋转 90 度
S_INT32 count = m_WBManager->getCount();
_tprintf(_T("Open whiteboards: %d\n"), count);
FS_SDK::WhiteBoardGraphicsObj obj; obj.type = FS_SDK::WhiteBoardGraphicsObj::RECTANGLE; obj.x = 100; obj.y = 100; obj.w = 200; obj.h = 150; obj.color = 0xFF0000; // 红色 m_WBManager->addWBObject(wbId, 0, obj);
m_WBManager->delWBObject(wbId, 0, objId);
以下是一个完整的 EventHandler 设置示例:
// ======== 登录事件 ======== class MyLoginHandler : public FS_SDK::ILoginEventHandler { void onAccountLoginCompleted(FS_SDK::ErrCode err) override { } void onAnonymousLoginCompleted(FS_SDK::ErrCode err) override { } void onEnterRoomCompleted(FS_SDK::ErrCode err) override { } void onInitCompleted() override { } void onPreStartMeeting() override { } void onExitedRoom() override { } void onUpdateServerCompleted(FS_SDK::ErrCode err) override { } void onAccountLogout(FS_SDK::ErrCode err) override { } void onAnonymousLoginPaasCompleted(FS_SDK::ErrCode err) override { } void onWaitingForAllowToEnter(FS_SDK::ErrCode err) override { } }; // ======== 音频事件 ======== class MyAudioHandler : public FS_SDK::IAudioEventHandler { void onCaptureDeviceChanged() override { } void onPlayDeviceChanged() override { } void onUserAudioEvent(S_UINT32 userId, S_BYTE state) override { } void onApplyAudioEvent(const FS_SDK::ApplyAVRightInfo* info) override { } void onLocalAudioCapData(S_PBYTE data, S_UINT32 dataLen) override { } }; // ======== 视频事件 ======== class MyVideoHandler : public FS_SDK::IVideoEventHandler { void onVideoDeviceChanged() override { } void onUserVideoEvent(S_UINT32 userId, S_BYTE ch, S_BYTE state) override { } void onApplyVideoEvent(const FS_SDK::ApplyAVRightInfo* info) override { } void onUserVideoData(S_UINT32 userId, S_BYTE ch, S_PBYTE d, S_UINT32 l) override { } void onUserVideoRawData(S_UINT32 userId, S_BYTE ch, S_PBYTE d, S_UINT32 l, S_UINT32 fc, S_SIZE wh, S_INT32 r) override { } void onLocalVideoRawData(S_BYTE ch, S_PBYTE d, S_UINT32 l, S_UINT32 fc, S_SIZE wh, S_INT32 r) override { } void onAddRtspResult(FS_SDK::ErrCode err, S_UINT32 devId) override { } void onLocalDeviceRecordFail(S_UINT32 devId) override { } }; // ======== 会议事件 ======== class MyMeetingHandler : public FS_SDK::IMeetingEventHandler { void onQueryRoomList(S_INT32 code, S_PCHAR msg, const FS_SDK::MeetingInfo* infos, S_UINT32 sz, S_UINT32 total) override { } void onQueryRoomListOfInstant(S_INT32 code, S_PCHAR msg, const FS_SDK::MeetingInfo* infos, S_UINT32 sz) override { } void onCreateInstantMeeting(S_INT32 tid, S_INT32 code, S_PCHAR msg, const FS_SDK::MeetingInfo* info) override { } void onQueryRoomShareInfo(S_INT32 tid, S_INT32 code, S_PCHAR msg, const FS_SDK::MeetingShareInfo* info) override { } void onQueryJoinMeetingRecords(S_INT32 code, S_PCHAR msg, const FS_SDK::JoinMeetingRecord* r, S_UINT32 sz) override { } void onManagerApplyEvent(FS_SDK::ErrCode err) override { } void onManagerStateEvent(S_UINT32 userId, S_BYTE state) override { } void onPresenterStateEvent(S_UINT32 userId, S_BYTE state) override { } void onDataSharerStateEvent(S_UINT32 userId, S_BYTE state) override { } void onPrivateTalkStateEvent(S_UINT32 src, S_UINT32 dst, S_BYTE state) override { } void onPrivateTalkInterrupted(S_UINT32 src, S_UINT32 dst, S_UINT32 mgr) override { } void OnLayoutEvent(const FS_SDK::RoomLayoutInfo& layout) override { } void onKickUserEvent(S_UINT32 userId) override { } void onMeetingClosed() override { } void onMeetingSilent(S_UINT32 srcId) override { } void onMeetingWaterMarkState(S_BYTE state, S_INT32 time) override { } void onServerRecordEvent(S_UINT32 srcId, S_BYTE state, FS_SDK::ErrCode err) override { } void onSoundTouchEvent(S_BYTE state) override { } void onUserMsgIncome(S_UINT32 srcId, S_PCHAR msg) override { } void onClientSupportAbility(S_UINT32 userId, S_UINT ability) override { } void onUpdateVoteInfo(const FS_SDK::VoteInfo& info) override { } void onStartSubtitle(const FS_SDK::SubtitleInfo& info) override { } void onStopSubtitle() override { } void onStartQuickRollCall(S_UINT32 src, const FS_SDK::FString& guid, S_UINT32 dur) override { } void onStopQuickRollCall(S_UINT32 src, const FS_SDK::FString& guid) override { } void onStartManualRollCall(S_UINT32 src, const FS_SDK::FString& guid, FS_SDK::ManualRollCallType type) override { } void onVideoPollingState(const FS_SDK::VideoPollingState& state) override { } void onWebSendNotify(S_UINT32 userId, S_PTCHAR op, S_PTCHAR appBackNotify) override { } }; // ======== 用户事件 ======== class MyUserHandler : public FS_SDK::IUserEventHandler { void onUserEntered(S_UINT32 userId) override { } void onOffineUser(S_UINT32 userId) override { } void onUserLeft(S_UINT32 userId) override { } void onUserInfoUpdated(S_UINT32 userId) override { } }; // ======== 聊天事件 ======== class MyChatHandler : public FS_SDK::IChatEventHandler { void onUserChatEvent(const FS_SDK::ChatMsgInfo* msg) override { } void onCheckChatMessageResult(S_UINT32 msgNum, S_UINT32 uid, S_BOOL result) override { } void onRecallChatMessageResult(S_PTCHAR msgId, S_UINT32 result) override { } void onChatMessageRecalled(S_PTCHAR msgId, S_UINT32 srcUserId) override { } void onSystemChatMessage(S_UINT32 msgNum, S_PTCHAR content, S_BOOL isMsgBox) override { } }; // ======== 设置事件处理器 ======== void SetupEventHandlers() { FS_SDK::ISdkManager* sdk = FS_SDK::ISdkManager::getInstance(); // 每个模块通过 setEventHandler 绑定对应的事件处理器 sdk->getLoginManager()->setEventHandler(&g_loginHandler); sdk->getAudioManager()->setEventHandler(&g_audioHandler); sdk->getVideoManager()->setEventHandler(&g_videoHandler); sdk->getMeetingManager()->setEventHandler(&g_meetingHandler); sdk->getUserManager()->setEventHandler(&g_userHandler); sdk->getChatManager()->setEventHandler(&g_chatHandler); // ... 其他模块同理 }
// 全局变量 FS_SDK::ISdkManager* g_pSdkManager = nullptr; FS_SDK::ILoginManager* g_pLoginMgr = nullptr; FS_SDK::IMeetingManager* g_pMeetingMgr = nullptr; FS_SDK::IWhiteBoardManager* g_pWBMgr = nullptr; // 事件处理器实例 MyLoginHandler g_loginHandler; MyMeetingHandler g_meetingHandler; void LoginAndJoinMeeting() { // 1. 获取 SDK 实例并初始化 g_pSdkManager = FS_SDK::ISdkManager::getInstance(); FS_SDK::SdkContext ctx; ctx.appId = _T("com.example.myapp"); ctx.appVer = _T("1.0.0"); FS_SDK::ErrCode ec = g_pSdkManager->initSdk(&ctx); if (ec != FS_SDK::ERR_SUCCESS) return; // 2. 获取各管理器 g_pLoginMgr = g_pSdkManager->getLoginManager(); g_pMeetingMgr = g_pSdkManager->getMeetingManager(); g_pWBMgr = g_pSdkManager->getWhiteBoardManager(); // 3. 设置事件处理器 g_pLoginMgr->setEventHandler(&g_loginHandler); g_pMeetingMgr->setEventHandler(&g_meetingHandler); // 4. 设置服务器地址和 ClientInfo g_pLoginMgr->setServerAddr(_T("apaas.hst.com"), 443); g_pLoginMgr->setClientInfo(_T("client_id"), _T("client_secret")); // 5. 账号登录 g_pLoginMgr->loginAccount( (S_PTCHAR)_T("zhangsan"), (S_PTCHAR)_T("mypassword"), true ); // 登录结果在 MyLoginHandler::onAccountLoginCompleted 中处理 } // 在 onAccountLoginCompleted 中 void MyLoginHandler::onAccountLoginCompleted(FS_SDK::ErrCode err) override { if (err != FS_SDK::ERR_SUCCESS) { // 登录失败 return; } // 登录成功,入会 g_pLoginMgr->enterRoom(false, 123456, _T("")); } // 在 onEnterRoomCompleted 中 void MyLoginHandler::onEnterRoomCompleted(FS_SDK::ErrCode err) override { if (err != FS_SDK::ERR_SUCCESS) return; // 入会成功,初始化白板 g_pWBMgr->initialize(); } // 退出流程 void LogoutAndExit() { g_pMeetingMgr->exitMeeting(); g_pLoginMgr->logout(); g_pSdkManager->releaseSdk(); }≡