OMCS开发手册(01) -- 多媒体设备管理器 一文,我们从Owner的角度详细描述了多媒体设备管理器的使用,本文我们将站在Guest的角度,描述OMCS中另一类组件/控件:多媒体连接器。多媒体连接器用于连接到任何一个在线的OMCS客户端所提供的多媒体设备。所有的连接器都是以Windows控件或组件的方式呈现的,且都实现了IMultimediaConnector接口。
一.IMultimediaConnector 接口
多媒体连接器的接口定义如下所示:
public interface IMultimediaConnector { /// <summary> /// 设备主人的UserID。 /// </summary> string OwnerID { get; } /// <summary> /// 与目标设备是否已连接? /// </summary> bool Connected { get; } /// <summary> /// 当调用BeginConnect连接Owner的设备时,如果Owner不在线,则等待对方上线的最长时间。 /// 单位:秒。默认值0。 /// </summary> int WaitOwnerOnlineSpanInSecs { get; set; } /// <summary> /// 目标多媒体设备的类型。 /// </summary> MultimediaDeviceType MultimediaDeviceType { get; } /// <summary> /// 尝试连接目标多媒体设备。 /// </summary> /// <param name="destUserID">目标用户的UserID</param> void BeginConnect(string destUserID); /// <summary> /// 与目标用户的多媒体设备断开连接,并释放通道。 /// </summary> void Disconnect(); /// <summary> /// 当连接目标多媒体设备的尝试(由BeginConnect发起)结束时,触发此事件。事件参数说明了连接的结果。 /// </summary> event CbGeneric<ConnectResult> ConnectEnded; /// <summary> /// 当与目标多媒体设备的连接断开时,触发该事件。 /// </summary> event CbGeneric<ConnectorDisconnectedType> Disconnected; }
1.连接
当使用连接器对象时,通常首先是将对应的控件/组件拖到窗体上,然后调用其BeginConnect方法尝试与目标用户的多媒体设备进行连接。连接结束时,无论是成功还是失败,都会触发ConnectEnded事件。我们可以根据ConnectEnded事件参数ConnectResult得知本次连接是成功还是失败。ConnectResult枚举定义如下:
public enum ConnectResult { Succeed, /// <summary> /// 等待回复超时 /// </summary> Timeout, /// <summary> /// 目标用户不在线 /// </summary> TargetUserOffline, /// <summary> /// 对方拒绝 /// </summary> Denied, /// <summary> /// 设备不存在或出错 /// </summary> DeviceInvalid , /// <summary> /// Owner的设备管理器还未完成初始化 /// </summary> MultimediaManagerNotInitialized, /// <summary> /// 出现异常 /// </summary> ExceptionOccured, }
注意,如果多媒体设备未被授权、或多媒体管理器未成功初始化、或当前连接器正在工作、或目标多媒体设备已经被连接,则BeginConnect方法将抛出相应的异常。
如果要连接的目标设备的Owner还未上线,WaitOwnerOnlineSpanInSecs属性则允许我们等它一段时间。在某些情况下,这可能是很有用的。我们来设想一下,通常基于OMCS开发的多媒体应用系统,除了有OMCS提供多媒体的部分,还会有其它业务逻辑,也就是说,除了有OMCS服务器存在,还会有处理业务逻辑的应用服务器存在。就像OMCS开发手册(00) -- 概述中的那个图所示的情况:
此时,客户端将有两个连接,一个连接指向OMCS服务器,另一个连接指向应用服务器。通常,客户端应该在成功登录了应用服务器之后,才会去连接OMCS服务器,这样就可能存在一个时间间隙 -- 即应用服务器已经连接成功,而OMCS服务器还未连接。如果在这个时候,其它Guest要访问当前客户端的多媒体设备,就会返回TargetUserOffline的结果而连接失败。如果将连接器的WaitOwnerOnlineSpanInSecs设置大于0,则连接器会在这段时间内不断轮询,等待Owner连上OMCS服务器。当Owner上线的时候,再去连接其多媒体设备。这样就解决了问题。当然,如果由于某些意外,导致Owner在WaitOwnerOnlineSpanInSecs时间内都还未连上OMCS服务器,则在等待时间结束时,连接器仍然返回TargetUserOffline的结果。
2.状态信息
MultimediaDeviceType 属性表示当前连接器要连接的目标多媒体设备的类型。
Connected 属性反应了当前连接器与多媒体设备之间的连接状态。
如果连接成功,OwnerID属性表示当前连接的是哪个用户的多媒体设备。
3.断开连接
我们可以调用Disconnect方法主动断开与目标多媒体设备的连接。当然,除了主动断开连接外,还有其它几种方式也会导致连接器到目标设备的连接断开(比如,网络断开)。而只要连接器与目标多媒体设备之间的连接断开,就会触发Disconnected事件,事件的参数ConnectorDisconnectedType说明了连接断开的原因。
public enum ConnectorDisconnectedType { /// <summary> /// Guest(连接器)掉线。 /// </summary> GuestOffline = 0, /// <summary> /// Owner(设备)掉线。 /// </summary> OwnerOffline, /// <summary> /// Guest(连接器)主动断开到设备的连接。 /// </summary> GuestActiveDisconnect, /// <summary> /// Owner(设备)主动断开Guest(连接器)到设备连接。 /// </summary> OwnerActiveDisconnect }
多媒体连接器断开共有四种原因:Guest掉线、Owner掉线、Guest主动断开、Owner主动断开。
当我们在正常工作的连接器实例上调用其Disconnect方法时,触发Disconnected事件的参数就是ConnectorDisconnectedType.GuestActiveDisconnect。
还记得在介绍多媒体设备管理器时,IMultimediaManager有重载的DisconnectGuest方法,如果Owner调用这个DisconnectGuest方法,那么在Guest这一方对应的连接器实例就会断开到目标设备的连接,而断开的原因就正是ConnectorDisconnectedType.OwnerActiveDisconnect。
二.四种多媒体连接器
OMCS提供了四种多媒体连接器:MicrophoneConnector(麦克风连接器)、CameraConnector/DynamicCameraConnector(摄像头连接器)、DesktopConnector/DynamicDesktopConnector(远程桌面连接器)、WhiteBoardConnector(电子白板连接器)。所有这些连接器都实现了IMultimediaConnector接口,所以,IMultimediaConnector定义的功能它们都是拥有的。
我们可以将这些连接器组件/控件添加到工具箱中:在VS的工具箱的空白地方右键快捷菜单 => 选择项,在弹出的“选择工具箱项”的窗体上,点击“浏览”按钮,选中OMCS.dll文件,再点击“确定”就可以了。
1.麦克风连接器
MicrophoneConnector是一个组件,没有UI元素,当然,它也不需要UI显示。
2.摄像头连接器
OMCS提供了两个摄像头连接器:CameraConnector和DynamicCameraConnector。它们的区别在于,CameraConnector是一个UI控件,直接在当前的UI上显示目标摄像头采集到的视频;DynamicCameraConnector是一个组件(没有UI),可以通过SetViewer方法动态地为其设置要在哪个UI上绘制。
/// <summary> /// 设置要显示视频的控件。必须要在UI线程中调用该方法。 /// </summary> /// <param name="newPanel">要绘制视频的控件。可以为null。</param> void SetViewer(Control newPanel);
像我们经常看到的视频聊天中的全屏显示功能,就可以采用DynamicCameraConnector实现,当用户点击全屏按钮时,将DynamicCameraConnector要绘制的表面设置为最前(Top)的窗体的表面就可以了。
除此之外,CameraConnector和DynamicCameraConnector还提供了以下特性:
/// <summary> /// 被绘制控件的背景色。 /// </summary> Color PanelColor { get; set; } /// <summary> /// 是否自动保持视频与音频同步。默认值为true。可以在运行时动态修改。 /// </summary> bool AutoSynchronizeVideoToAudio { get; set; } /// <summary> /// 连续多长时间没有新的帧绘制,就显示黑屏。单位:秒。 /// </summary> int MaxIdleSpan4BlackScreen { get; set; } /// <summary> /// 获取当前正在绘制的图像。 /// </summary> Bitmap GetCurrentImage();
PanelColor:用于设置被绘制控件的背景色,当没有视频图像显示时,将显示这种背景色。默认值为黑色。
MaxIdleSpan4BlackScreen:连续多长时间没有接收到新的视频帧时,就显示PanelColor所设置的背景色。默认值为5秒。这种情况经常在网络缓慢而导致视频帧延迟很大或连续被丢弃时出现。
GetCurrentImage:该方法可以将当前显示的视频帧保存为位图。使用该方法可以实现拍照功能。
AutoSynchronizeVideoToAudio:如果当前客户端连接了同一个Owner的摄像头和话筒,那么,CameraConnector/DynamicCameraConnector在播放视频时是否自动与音频保持同步。
一般,在网络非常顺畅的情况下,视频帧与音频帧按是照接收就立即播放的模式来进行的,这本来就是同步的。
但是,当网络存在抖动时,OMCS内部会自动启用抖动缓冲区(Jitter Buffer),这样就使得音频比视频的播放要稍慢一点(可能是几毫秒或几十毫秒,取决于网络抖动的幅度),而导致出现声音与画面不同步的情况。如果将AutoSynchronizeVideoToAudio设置为true,则OMCS会控制视频帧的播放,使其与音频始终保持一致。
3.远程桌面连接器
同摄像头连接器一样,OMCS也提供了两种远程桌面连接器:DesktopConnector和DynamicDesktopConnector。它们的区别也与两种摄像头连接器的区别一样。它的扩展特性有以下两个:
/// <summary> /// 被绘制控件的背景色。 /// </summary> Color PanelColor { get; set; } /// <summary> /// 是否仅仅允许查看远程桌面,但是不能进行操作。默认值为true。 /// </summary> bool WatchingOnly { get; set; } /// <summary> /// 是否在远程桌面上显示Owner的鼠标光标。默认值为true。 /// </summary> bool ShowMouseCursor { get; set; } /// <summary> /// 连续多长时间没有新的帧绘制,就显示黑屏。单位:秒。 /// </summary> int MaxIdleSpan4BlackScreen { get; set; } /// <summary> /// 获取当前正在绘制的图像。 /// </summary> Bitmap GetCurrentImage();
PanelColor、MaxIdleSpan4BlackScreen属性以及GetCurrentImage方法的含义与摄像头连接器的一样,不再重复。
WatchingOnly 属性用于控制guest是否可以操作远程桌面。将其设为false,就可以实现类似QQ的远程协助的功能。
ShowMouseCursor 属性用于控制是否在远程桌面上显示Owner的鼠标光标。比如,在远程教学系统中,可以将该属性设置为true,这样,每个guest都可以看到Owner鼠标指示的地方了。
4.电子白板连接器
OMCS的电子白板提供了常用的视图元素:像直线、曲线、箭头、矩形、三角形、椭圆、文字等;可修改边框颜色和填充颜色;可插入图片、截屏,可将整个白板保存为位图;并且支持激光笔等功能。
首先要强调一点,电子白板这个设备与其它的几个设备有个重要的区别:Owner的身份对于电子白板而言,更像是一个标志,而不是像前面三种设备一样,是实际设备的持有者。
就像一栋大楼一样,里面有很多个房间,而Owner的ID只是这个房间的门牌号码。如果多个guest连到了同一个Owner的电子白板,意味着多个guest进入了同一个房间,可以在同一个电子白板上相互协作。这些guest看到的是完全相同的内容,当一个guest修改电子白板的内容时,其它的guest可以同时看到这种改变。
基于此,所以Owner的掉线不会导致Guest的电子白板连接器断开,也就是说,WhiteBoardConnector的Disconnected事件的ConnectorDisconnectedType参数的值永远不会是OwnerOffline。但是,为了能找到目标房间在哪栋楼里(在OMCS服务器群集环境中,需要定位owner位于哪台服务器),电子白板连接器在连接Owner时,Owner必须在线,这一点与其它几个连接器是一致的。
电子白板连接器WhiteBoardConnector的扩展特性有以下三个:
/// <summary> /// 仅仅允许查看白板,但是不能进行操作。默认值为false。 /// </summary> bool WatchingOnly { get; set; } /// <summary> /// 是否开启自动重连的功能。默认值为true。 /// </summary> bool AutoReconnect { get; set; } /// <summary> /// 在白板的左上角位置插入图片。 /// </summary> /// <param name="img">被插入的图片</param> void InsertImage(Image img);
WatchingOnly 属性用于控制guest是否可以在电子白板上绘图等操作,还是只能观看。在现在流行的电子课堂中,通常只有老师可以操作电子白板,而学生只能观看电子白板。
AutoReconnect 是电子白板连接器特有的一个功能,以支持断线自动重连。当断线重连成功后,电子白板会从服务器下载最新的白板内容,并显示,以保证电子白板的实时性。
我们除了可以以Ctrl+V的方式向电子白板中插入图片外,还可以以编程的方式调用InsertImage方法向其中插入图片。比如,我们可以将CameraConnector的GetCurrentImage方法返回的图片插入到电子白板中。