OMCS 开发手册(08) -- 多人语音/视频
最新版的OMCS(V3.5)增加了对多人语音/视频聊天组的直接支持。在此版本之前,虽然,也可以基于OMCS开发语音聊天室和视频会议等多人聊天的功能,但是,那需要自己手动管理“聊天房间”、“聊天组”。而现在,这些手动工作的绝大部分可以交给OMCS了,OMCS内置了动态组,用来管理所有激活的聊天室。
一. 动态组
OMCS采用“动态组”的模式来实现多人语音/视频聊天组,所谓“动态组”,就是在运行时动态创建和销毁的组,其包含如下几层意思:
(1)当某个用户要加入一个不存在的动态组时,OMCS服务器会首先自动创建这个组,然后,再把用户放进这个组。
(2)当用户退出组、或掉线时,OMCS服务器会将该用户从对应的组中移除。
(3)当某个组中的最后一个人退出时,OMCS服务器会销毁这个组。
在服务端,这一切都是自动完成的,我们不需要额外编写代码。
二. OMCS.Passive.MultiChat 命名空间
在客户端,OMCS.Passive.MultiChat命名空间提供:IChatGroupEntrance接口、IChatGroup接口、IChatUnit类,通过这三个元素,我们便可以使用OMCS提供的对多人语音/视频聊天组的功能了。
1. IChatGroupEntrance
IChatGroupEntrance 是客户端使用多人语音/视频组的入口。
/// <summary> /// 语音视频聊天组入口。 /// </summary> public interface IChatGroupEntrance { /// <summary> /// 加入某个聊天组。如果目标组不存在,将自动创建目标组。 /// </summary> /// <param name="chatType">聊天组的类型。</param> /// <param name="chatGroupID">目标组ID。</param> IChatGroup Join(ChatType chatType ,string chatGroupID); /// <summary> /// 离开聊天组。如果掉线,也会自动从聊天组中退出。 /// </summary> /// <param name="chatType">聊天组的类型。</param> /// <param name="chatGroupID">目标组ID。</param> void Exit(ChatType chatType, string chatGroupID); }
OMCS将语音聊天组和视频聊天组是分开管理的,它们使用ChatType枚举来进行区分:
/// <summary> /// 聊天组的类型。 /// </summary> public enum ChatType { /// <summary> /// 语音聊天组。 /// </summary> Audio, /// <summary> /// 视频聊天组。 /// </summary> Video }
(1)我们可以通过OMCS客户端的核心组件 -- 多媒体管理器IMultimediaManager的ChatGroupEntrance属性获取到聊天组入口的引用。
(2)当调用IChatGroupEntrance 的Join方法加入某个聊天组,方法会返回一个IChatGroup引用,它代表了目标聊天组。
(3)语音聊天组和视频聊天组的ID可以相同,但是由于它们的类型(ChatType)不同,所以,它们仍然是不同的两个组。
(4)当调用Exit方法主动退出聊天组时,OMCS内部会自动释放该组内部持有的所有多媒体连接器实例(这些连接器实例位于即将介绍的IChatUnit内)。
2. IChatGroup
IChatGroup封装了一个聊天组的相关信息,其定义如下:
/// <summary> /// 封装一个聊天组的信息。 /// </summary> public interface IChatGroup { /// <summary> /// 当有新成员加入聊天组时,将触发此事件。 /// </summary> event CbGeneric<IChatUnit> SomeoneJoin; /// <summary> /// 当某成员掉线或离开聊天组时,触发此事件。 /// </summary> event CbGeneric<string> SomeoneExit; /// <summary> /// 聊天组的ID。 /// </summary> string GroupID { get; } /// <summary> /// 聊天组的类型。如果为语音聊天,则DynamicCameraConnector为null。 /// </summary> ChatType ChatType { get; } /// <summary> /// 自己的Unit对象(当第一次调用时创建)。 /// </summary> IChatUnit MyChatUnit { get; } /// <summary> /// 获取组成员的信息。 /// </summary> IChatUnit GetMember(string memberID); /// <summary> /// 获取组内除自己之外的其它成员的信息。 /// </summary> List<IChatUnit> GetOtherMembers(); }
(1)当有人加入或退出当前组时,IChatGroup会自动触发SomeoneJoin、SomeoneExit事件。
(2)GetOtherMembers方法将返回组内其它成员的信息,每个成员都对应着一个IChatUnit实例。
3.IChatUnit
IChatUnit 主要是封装了与目标组成员相关的麦克风连接器、摄像头连接器。
/// <summary> /// 用于封装聊天组一个成员的相关信息的单元。 /// </summary> public interface IChatUnit { /// <summary> /// 对应的组成员的ID。 /// </summary> string MemberID { get; } /// <summary> /// 是否有效?如果对应的组成员退出组或者掉线,则将返回false。 /// </summary> bool Valid { get; } /// <summary> /// 摄像头连接器。(可将其连接到对应组成员的摄像头)。如果为语音聊天,则DynamicCameraConnector为null。 /// </summary> DynamicCameraConnector DynamicCameraConnector { get; } /// <summary> /// 麦克风连接器。(可将其连接到对应组成员的麦克风) /// </summary> MicrophoneConnector MicrophoneConnector { get; } }
(1)特别注意:在通过IChatGroup获取到的IChatUnit时,其DynamicCameraConnector和MicrophoneConnector属性所代表的摄像头连接器及麦克风连接器都还没有与目标设备建立联系。
我们需要手动调用其BeginConnect方法,连接到该聊天成员的摄像头和麦克风设备。
同时,我们也可以预定其DynamicCameraConnector和MicrophoneConnector的种种事件和查看其种种属性,就像我们使用自己new的连接器组件一样。
而事实上也是:IChatUnit 仅仅是帮我们实例化了一下连接器组件而已,除此以外再没有做其它的任何动作。
(2)当组成员退出组或者掉线时,OMCS会自动断开IChatUnit中的连接器到目标设备的连接,并且将Valid属性设置为false。
三. 如何使用
假设我们要开发一个视频会议的系统,在这个系统中,登录的用户可以输入一个视频会议房间的RoomID,便可以加入该视频会议。那么,实现的步骤大致如下:
1. 初始化多媒体管理器IMultimediaManager。
2. 调用IMultimediaManager的IChatGroupEntrance属性的Join方法,把RoomID传进去。便会返回一个IChatGroup引用。
3. 遍历IChatGroup的GetOtherMembers方法返回的集合中的每个IChatUnit:
(1)为之创建一个UI控件,绑定到ChatUnit的DynamicCameraConnector,以显示成员的视频。
(2)预定IChatUnit的DynamicCameraConnector和MicrophoneConnector的相关事件,以获取所需的通知。
(3)调用IChatUnit的DynamicCameraConnector和MicrophoneConnector的BeginConnect方法,与该成员的设备进行连接。
4. 预定IChatGroup的SomeoneJoin、SomeoneExit事件。
(1)处理SomeoneJoin事件时,可与第3点一样。
(2)处理SomeoneExit事件时,只需在UI上将退出的成员对应的视频显示控件移除掉。
5. 当自己要退出视频会议时,调用IMultimediaManager的IChatGroupEntrance属性的Exit方法即可。
四. 扩展
OMCS内置的使用“动态组”模式对语音视/频聊天组的支持,只是最核心的支持,它仅仅封装了最纯粹的逻辑。如果需要实现更复杂的自定义业务逻辑,那就需要基于OMCS做更多的开发。
继续上面的例子,我们假设加入视频会议之前,需要先提交一个申请,在管理员批准之后,才能正式加入到视频会议中。那么类似这样的业务需求单靠OMCS提供的API是无法实现的。
那么怎么做了?
我们可以在外围利用类似ESFramework等通信技术实现这一业务逻辑,具体步骤可参考如下:
(1)当用户输入了视频会议的房间号,并点击“申请加入”按钮时,客户端通过ESFramework发一条消息给在线的该视频会议的管理员。
(2)管理员所在的客户端收到请求消息后,在UI上弹出一个询问框,管理员点击“同意”按钮时,当前客户端就发送一条回复消息给申请的用户。
(3)申请的用户收到同意的回复后,就可以调用IMultimediaManager的IChatGroupEntrance属性的Join方法,来继续第三点中叙述的流程了。
五.高级设置
1.广播丢帧控制 AllowDiscardFrameWhenBroadcast
在多人语音视频聊天时,相当于每个人的摄像头都会存在多个guest。而在实际的视频对话的时候,Owner会将摄像头采集到的视频编码后发送给服务器进行广播,由于每个guest到服务器之间的网络情况都不同,如果不允许广播丢帧,那么,对于那些网络慢的guest,在服务端就会累积很多待转发的视频帧,这就会导致服务端进程的内存占用不断增大。
IMultimediaManager的 Advanced的 AllowDiscardFrameWhenBroadcast属性用于控制是否允许服务端在广播视频时,在那些网络慢的guest通道上自动丢帧。如果该属性设置为true,将有效地保护服务端进程的内存占用。
2. 关键帧间隔 MaxInterval4CameraKeyFrame
承接上面的描述,如果AllowDiscardFrameWhenBroadcast设置为true,那么,IMultimediaManager的 Advanced的 MaxInterval4CameraKeyFrame属性的设置就比较关键了。该属性的值决定了:那些通道比较慢的guest观看视频时因为“丢弃某一帧”而导致的每次卡顿的最长时间。
需要平衡的是:
(1)MaxInterval4CameraKeyFrame设置得越大,关键帧越少,这样对带宽的占用越低。(一般而言,关键帧的字节数要远大于非关键帧)
(2)但是需要广播视频的场景中,MaxInterval4CameraKeyFrame设置得越小,因为网络慢而“丢弃某一帧”导致的卡顿时间就越短。
3.推荐设置
综合上面所说的,我们的建议是:(假设fps为20)
(1)在1对1的视频聊天系统中
客户端设置:IMultimediaManager的 Advanced的MaxInterval4CameraKeyFrame 设置为60(表示最大间隔);
IMultimediaManager 的 AutoAdjustCameraEncodeQuality 设置为 true。
IMultimediaManager的 Advanced的 AllowDiscardFrameWhenBroadcast 设置为 true。
服务端设置:将IMultimediaServer 的 Advanced 的几个属性设置如下:
Advanced.UnCompletedCount4DiscardVideoFrame = 40;
Advanced.UnCompletedCount4DiscardAudioFrame = 10 ;
Advanced.UnCompletedCount4DiscardDesktopFrame= 40;
(2)而在1对N或者N对N的系统中:
客户端设置:IMultimediaManager的 Advanced的 MaxInterval4CameraKeyFrame 设置为 60。
IMultimediaManager的 AutoAdjustCameraEncodeQuality 设置为 false。
IMultimediaManager的 Advanced的 AllowDiscardFrameWhenBroadcast 设置为 true。
IMultimediaManager的 CameraEncodeQuality 设置为一个能接受的视频质量底线的最大值(比如20)。
服务端设置:将IMultimediaServer 的 Advanced 的几个属性设置如下:
Advanced.UnCompletedCount4DiscardVideoFrame = 40 ;
Advanced.UnCompletedCount4DiscardAudioFrame = 10 ;
Advanced.UnCompletedCount4DiscardDesktopFrame= 40 ;
六. 相关Demo
最后,就具体如何使用OMCS提供的多人语音视频功能,我们专门写了两个针对性的demo,欢迎查看:
--------------------------------------------------------------------------------------------------------------------
阅读 更多OMCS开发手册系列文章 。
Q Q:168757008
官网: www.oraycn.com