在线教育 PC Demo 源码导读
工程概述
在线教育 Demo 是网易云信的一款针对目前市场比较热门的在线教育场景推出的解决方案。在方案中结合了网易云信 IM 能力的聊天室模型和网易云信的音视频能力的多人会议模型。在使用本解决方案之前请务必了解 IM即时通讯 的聊天室能力,和 音视频通话 的多人会议能力。 Demo使用Visual Studio 2013 Update5开发(必须使用Update5版本)。
Demo源码中目录结构中的C++封装层文件夹名字尾部携带_vs2010的为vs2010创建的工程项目,对应的不携带_vs2010的为vs2013创建的工程项目,即Demo目前加载的工程,该工程依赖Demo工程。
Demo的主要功能由Demo工程本身以及UI组件
工程共同完成,Demo工程和UI组件
工程具有相同的目录结构。UI组件
位于tool_kits\ui_component\ui_kit
目录,UI组件的
相关文档详见:云信UI组件
Demo源码版本历史
界面开发资料
网易云信Duilib使用说明
控件属性
界面布局介绍
会话多窗口实现介绍
从3.0.0版本开始,Demo对会话窗口的实现进行了升级,方便开发者快速开发类似QQ,微信等多窗口模式。
CEF开发指南
从3.4.0版本开始,Demo增加了Cef控件,方便开发者开发Web相关的功能模块。
Duillib高分屏(高DPI)支持
从3.5.0版本开始,Duilib增加了对高分屏的支持,方便在用户设置了DPI后保持软件界面的清晰效果。
目录结构
callback:注册到SDK的一些回调的处理
gui:所有功能的界面相关实现
module:所有功能的逻辑相关实现
util:一些公用的工具类
打包说明
开发者在打包自己的应用时,应确保将以下云信SDK相关文件打包进去。
nim.dll:云信SDK主要功能库文件。
nim_chatroom.dll:云信聊天室SDK主要功能库文件
nim_audio.dll:语音消息功能库文件。
nrtc.dll:音视频通话功能库文件。
nim_tools_http.dll:http功能库文件。
nim_audio_hook.dll: 负责辅助采集播放器音频,由nrtc.dll调用;放在用户程序目录下,x64位暂时不提供该Dll。
msvcr100.dll:SDK依赖的VS2010动态链接库。
msvcp100.dll:SDK依赖的VS2010动态链接库。
nim_conf:云信SDK配置文件目录,包含SDK版本控制等。
其他的文件及目录是应用程序相关的,开发者根据自己程序的使用情况选择是否打包。
image_ole.dll:图像显示库文件,支持在RichEdit组件中插入和显示图片。
translation.bin:中文翻译成拼音依赖的文件。
app_ver.dll:云信Demo应用程序版本控制,开发者请勿打包到自己的应用。
lang:Demo界面文案对照表,可支持多国语言。
res:Demo资源文件目录。
themes:Demo皮肤目录,包含XML配置文件和图片文件。
功能点指引
SDK C++封装层
因为SDK所有接口都是C接口,为了方便使用C++的同学使用,我们提供了nim_cpp_sdk
和nim_chatroom_cpp_sdk
静态库。静态库位于libs\nim_sdk_desktop\nim_cpp_sdk
和libs\nim_sdk_desktop\nim_chatroom_cpp_sdk
目录,它将C接口SDK封装为C++代码,demo和UI组件
都直接使用nim_cpp_sdk
和nim_chatroom_cpp_sdk
静态库的C++封装层代码。开发者可以直接在解决方案中导入nim_cpp_sdk
工程和nim_chatroom_cpp_sdk
工程。
封装层提供的包装文件如下:
nim_cpp_sdk\nim_sdk_cpp\api\nim_cpp_client.h: 全局管理功能;主要包括SDK初始化/清理、客户端登录/退出等功能
nim_cpp_sdk\nim_sdk_cpp\api\nim_cpp_data_sync.h: 数据同步相关接口
nim_cpp_sdk\nim_sdk_cpp\api\nim_cpp_friend.h: 好友功能,包含添加好友、删除好友、监听好友变化
nim_cpp_sdk\nim_sdk_cpp\api\nim_cpp_global.h: NIM SDK提供的一些全局接口;释放从SDK申请的内存
nim_cpp_sdk\nim_sdk_cpp\api\nim_cpp_msglog.h: 消息历史接口
nim_cpp_sdk\nim_sdk_cpp\api\nim_cpp_nos.h: NOS云存储服务接口;上传或下载文件资源
nim_cpp_sdk\nim_sdk_cpp\api\nim_cpp_rts.h: 白板功能
nim_cpp_sdk\nim_sdk_cpp\api\nim_cpp_session.h: 会话列表管理功能;主要包括查询会话列表、删除会话列表等功能
nim_cpp_sdk\nim_sdk_cpp\api\nim_cpp_sysmsg.h: 系统消息接口;主要包括查询系统消息、删除系统消息等功能
nim_cpp_sdk\nim_sdk_cpp\api\nim_cpp_talk.h: 聊天功能;主要包括发送消息、接收消息等功能
nim_cpp_sdk\nim_sdk_cpp\api\nim_cpp_team.h: 群组功能;主要包括查询群信息、查询群成员信息、加人、踢人等功能
nim_cpp_sdk\nim_sdk_cpp\api\nim_cpp_tool.h: 提供的一些工具接口,主要包括获取SDK里app account对应的app data目录,计算md5、语音转文字等
nim_cpp_sdk\nim_sdk_cpp\api\nim_cpp_vchat.h: 音视频(包括设备)相关接口
nim_cpp_sdk\nim_sdk_cpp\api\nim_cpp_user.h: 用户功能,包含黑名单设置、个人消息提醒设置、状态设置
nim_chatroom_cpp_sdk\nim_chatroom_cpp\api\nim_chatroom_cpp.h :聊天室操作的相关接口
除了nim_cpp_sdk
静态库,另外还提供了负责语音和http传输的dll的C++封装类:
- libs\nim_sdk_desktop\nim_cpp_sdk\nim_audio_cpp\nim_tools_audio_cpp_wrapper.h:提供的语音录制和播放接口
- libs\nim_sdk_desktop\nim_cpp_sdk\nim_http_cpp\nim_tools_http_cpp_wrapper.h:提供的http传输相关接口
SDK 初始化
SDK的初始化在main.cpp中InitNim方法进行。
示例:
//sdk能力参数(必填)
//string(db key必填,目前只支持最多32个字符的加密密钥!建议使用32个字符)
config.database_encrypt_key_ = "Netease";
std::string app_key = GetConfigValueAppKey();
// 载入云信sdk,初始化安装目录和用户目录
bool ret = nim::Client::Init(app_key, "NIM_LIVE", "", config);
assert(ret);
//聊天室初始化
ret = nim_chatroom::ChatRoom::Init("","");
assert(ret);
nim_ui::InitManager::GetInstance()->InitUiKit();
nim_chatroom::ChatroomCallback::InitChatroomCallback();
界面开发
云信PC demo以及UI组件的界面开发都依赖云信DuiLib库
,关于云信DuiLib库
的使用方法和注意事项,请参考:云信Duilib
登录
登录相关界面代码在gui/login
目录下,登录相关的控制逻辑已经封装到了UI组件的
,逻辑代码在callback\login\login_callback.cpp
文件中。登录界面可以直接调用UI组件
的登录函数,示例如下:
std::string username = "123456789";
std::string password = "123456789";
nim_ui::LoginManager::GetInstance()->DoLogin(username, password);
总体逻辑
登陆后由用户选择创建房间,或者加入房间。整体在线教育房间以聊天室为主体,在加入聊天室后,分别加入多人音视频和多人白板。然后由聊天室自定义消息和点对点自定义消息,实现用户之前的协议交互,达到连麦,切换文档等交互操作。
创建房间
Demo入口在MeetingFrontpage::CreateMyRoomInfo中发起,先通过http协议创建聊天室房间,然后创建多人会议房间。
加入房间
- 进入房间 创建房间后在MeetingFrontpage::OnCreateChannelCallback结果中加入聊天室,如果是加入他人创建的房间则在MeetingFrontpage::JoinRoom中加入聊天室。
- 加入多人音视频和多人白板 在加入聊天室成功后,在ChatroomForm::MeetingInit中加入多人音视频StartVChat及加入多人白板StartMultiBoard。
主持人及成员互动
- 流程
- 申请发言,成员 发送 p2p消息给 master
- 申请结果,主持人 发送 p2p消息给 成员,如果通过,主持人更新状态(全量)发送一条聊天室消息
- 有新成员进入聊天室,主持人发送 p2p消息给 成员
- 成员进入聊天室后,5S内没有收到聊天室的发言权限,主动发送 聊天室消息 给所有成员请求发言权限,有权限的成员发送 p2p消息(a自己的权限状态)给 请求者 ,此类消息a在收到主持人的状态通知前有效
- 成员退出聊天室,所有人更新权限列表(把退出的人从权限列表中删掉),主持人更新状态(全量)发送一条聊天室消息
消息协议 协议流程中通过p2p消息(点对点系统消息)及聊天室消息(聊天室自定义消息)来实现协议交互,其中:
enum MeetingOptType//command { Mot_AllStatus = 1, //主持人通知有权限的成员列表 《1》《2》 {"type":10,"data":{"room_id":"123","command":1,"uids":["a","b"]}} Mot_GetStatus = 2, //成员向所有人请求有权限的成员 《1》 {"type":10,"data":{"room_id":"123","command":2}} Mot_StatusResponse = 3, //有权限的成员向请求者返回自己有权限的通知 《2》 {"type":10,"data":{"room_id":"123","command":3,"uids":["myid"]}} Mot_SpeekRequest = 10, //向主持人请求连麦权限 《2》 {"type":10,"data":{"room_id":"123","command":10}} Mot_SpeekAccept = 11, //主持人同意连麦请求,主持人同时发送群发1消息 《2》 {"type":10,"data":{"room_id":"123","command":11}} Mot_SpeekReject = 12, //主持人拒绝或关闭连麦,主持人同时发送群发1消息 《2》 {"type":10,"data":{"room_id":"123","command":12}} Mot_SpeekRequestCancel = 13, //取消向主持人请求连麦权限 《2》 {"type":10,"data":{"room_id":"123","command":13}} };
- AllStatus根据流程会发 《1》聊天室消息来通知 及 《2》点对点消息
- GetStatus只会使用 《1》聊天室消息来通知
- 其他消息类型全部通过 《2》点对点消息 实现
协议发送 在源码中由ChatroomForm::SendP2PMeetingMsg封装p2p系统消息协议的发送,ChatroomForm::SendRoomMeetingMsg封装聊天室自定义消息
多人白板Demo协议
下面是白板通道传输的数据格式。
###与点对点白板的区别:
- 新增
rgb
颜色信息 - 不再限制每个
命令
的value
的个数(之前限制两个) 清空
命令只能由主播发,发广播包清空响应
由参与白板绘制的人发,发广播包- 新增
同步请求
和同步
命令,用于非主播加入房间以后,主播向他同步当前的白板的笔画信息
###协议描述
####包
调用SDK发送或者从SDK接收到的一条RTS数据,包
是一个字符串,由一条或多条命令
组成,命令
之间用封号隔开
命令1;命令2;命令3;
####命令
多人白板Demo的原子协议,每个命令
必须遵守以下格式:
type:value,value,value,…
每一个type
对应明确数量的value
,value
之间用逗号隔开
####type
命令
的类型,整数
type
和value
:
类型 | type | value,value,value,… |
---|---|---|
起始点 | 1 | x,y,rgb |
移动点 | 2 | x,y,rgb |
结束点 | 3 | x,y,rgb |
上一步 | 4 | |
包序号 | 5 | id |
清空 | 6 | |
清空响应 | 7 | |
同步请求 | 8 | |
同步 | 9 | uid,end |
同步准备 | 10 | |
同步准备响应 | 11 | |
标记 | 12 | x,y,rgb |
标记结束 | 13 | |
同步文档 | 14 | id(文档id),page_num(当前页数,1开始计算),page_count(总页数),type(状态通知:0,翻页操作:1) |
####说明
x y: 浮点型的相对坐标,值为相对于画板的长宽,例如下图的白板示意中 x 点的坐标为 x= 2/6 = 0.3333 y = 3/4 = 0.75
o | o | o | o | o | o |
---|---|---|---|---|---|
o | o | o | o | o | o |
o | x | o | o | o | o |
o | o | o | o | o | o |
rgb:整型的颜色值,例如一个颜色的RGB是 0xF3F6F9,rgb=15988473
上图的坐标点如果是一个移动点
,那这这个点的命令
为:
2:0.3333,0.75,15988473
包序号:可选,主要用来调试是否有丢包
清空:只能主播发,发广播包;主播清空所有笔画,同时发该命令,非主播收到该命令后,清空所有笔画;
清空响应:参与者收到清空命令后发清空响应
广播包;其他人收到该响应后,清空该参与者的笔画。该步骤用来避免清空过程中同时有人在画图可能会导致的数据不一致
同步请求: 非主播加入多人实时会话成功以后,发送该命令给主播
同步准备: 主播B多人白板登录成功(包含断网重新登录成功)后,发送该指令给所有其他人,然后再发同步
。其他人收到该消息后清空本地所有数据
同步准备响应: 非主播收到同步准备
后,回该消息给主播。该消息用于适配服务端录制回放功能,主播收到以后无需处理
同步:1. 主播B收到A同步请求
后,主播B把所有的用户ABCD的笔画的数据单播发送给用户A,每个用户的数据单独通过该协议发送;2.主播B发出同步准备
后,同步所有用户的数据给所有人
end=1表示该用户的数据发完了,例如C的数据可以一次发完:
8:uid_C,1;1:x,y,rgb;2:x,y,rgb;…;3:x,y,rgb;
end=0表示没发完,后面还有数据。例如C的数据量太大,超过SDK接口的发送限制,可以分多条发送,比如分3个包
发送:
包1,end=0
8:uid_C,0;1:x,y,rgb;2:x,y,rgb;…;3:x,y,rgb;
包2,end=0
8:uid_C,0;1:x,y,rgb;2:x,y,rgb;…;3:x,y,rgb;
包3,end=1
8:uid_C,1;1:x,y,rgb;2:x,y,rgb;…;3:x,y,rgb;
A在收完C所有的数据以后,清空C当前的数据,用同步的数据代替之。
标记: 主播发送标记消息,非主播收到后在界面显示标记图标,标记图标没有轨迹
标记结束: 主播发送标记结束后,非主播清除界面之前显示的标记