OMCS 开发手册(01) -- 多媒体设备管理器
我们在前面一篇文章中提到:任何一个OMCS的Client都有两种身份,Owner和Guest。多媒体设备管理器就是工作于OMCS客户端,并以Owner的身份管理本地所有的多媒体设备的。多媒体设备管理器对象是OMCS在客户端的核心对象,它会根据guest的请求自动启动或停止某个多媒体设备。
一.多媒体设备
像本地的摄像头、麦克风、电子白板等都属于多媒体设备,多媒体设备的类型使用枚举MultimediaDeviceType表示:
/// <summary> /// 多媒体设备的类型。 /// </summary> public enum MultimediaDeviceType { /// <summary> /// 摄像头。 /// </summary> Camera = 0,
/// <summary> /// 话筒。 /// </summary> Microphone,
/// <summary> /// 桌面。 /// </summary> Desktop,
/// <summary> /// 电子白板。 /// </summary> WhiteBoard }
目前的OMCS支持MultimediaDeviceType定义的4种多媒体设备类型,所有的多媒体设备都由多媒体管理器IMultimediaManager统一管理。
(1) 客户端以Owner身份提供本地的多媒体设备供其它客户端访问。
(2) 各种类型的多媒体设备对应的类Class都是internal的,属于OMCS的内部对象,开发人员不需要对其进行任何编程。取而代之的是,开发人员可以通过IMultimediaManager来间接获取多媒体设备的有关状态和信息。
(3)如何获取当前电脑的摄像头、麦克风、声卡的硬件设备信息了?OMCS提供了工具类,具体可参考 OMCS 开发手册(11) -- 深入摄像头、麦克风、扬声器。
二.多媒体设备管理器详解
作为OMCS客户端的核心对象,多媒体管理器的主要职责为:
(1)管理本地的所有多媒体设备实例,设置设备参数,以及在合适的时间启动或停止某个多媒体设备。
(2)与OMCS服务器通信,并管理与OMCS服务器之间连接的状态。
(3)创建P2P通道。在多媒体连接器发起到目标设备的连接请求时,同时异步创建到目标Owner的双向P2P通道。
上述这些职责,可以通过多媒体设备管理器的接口OMCS.Passive.IMultimediaManager的定义体现出来。
1.属性
IMultimediaManager接口中所有的属性定义如下:
/// <summary> /// 已连接的多媒体服务器的地址。 /// </summary> AgileIPEndPoint ServerIPE {get; } /// <summary> /// 当前登录用户的ID。 /// </summary> string CurrentUserID { get; } /// <summary> /// 当前多媒体管理器是否可用?(与OMCS服务器成功连接?并且设备管理器已经初始化完成?) /// </summary> bool Available { get; } /// <summary> /// 当前多媒体管理器是否正在工作? /// 如果没有任何guest连接到本地多媒体设备,并且自己也没有连接到别人的设备,则返回false。否则返回true。 /// </summary> bool Working { get; } /// <summary> /// 掉线后,是否自动重连。(如果要set该属性,则必须在调用Initialize方法之前设置才有效。) /// </summary> bool AutoReconnect { get; set; } /// <summary> /// 设备访问控制器,用于控制哪些guest能访问自己的设备(麦克风、摄像头、桌面)。如果不需要控制,则设置为null。默认值:null。 /// </summary> IAccessController AccessController { get; set; } /// <summary> /// 语音视频聊天组入口。(只有多媒体管理器初始化成功之后,该属性才有效) /// </summary> IChatGroupEntrance ChatGroupEntrance { get; } /// <summary> /// 语音消息控制器。在当前多媒体设备管理器初始化成功之前,该属性将返回null。 /// 注意:在正式使用语音消息控制器之前,先要调用IAudioMessageController的Initialize方法将其初始化。 /// </summary> IAudioMessageController AudioMessageController { get; } /// <summary> /// OMCS高级控制选项。如果要修改AdvancedOptions的某些属性值,必须在调用Initialize方法之前设置才有效。 /// </summary> AdvancedOptions Advanced { get; } /// <summary> /// 用于注入自定义的声音采集器工厂。(必须在调用Initialize方法之前设置才有效。) /// </summary> IAudioCapturerFactory AudioCapturerFactory { get; set; } /// <summary> /// 要使用的麦克风的索引。可以在运行时动态修改。 /// </summary> int MicrophoneDeviceIndex { get; set; } /// <summary> /// 要使用的扬声器的索引。可以在运行时动态修改。 /// </summary> int SpeakerIndex { get; set; } /// <summary> /// 是否将话筒采集到的音频输出给Guest。(必须在初始化完成之后设置才有效,可动态修改。) /// 如果为true,表示输出;否则,表示将采集到的音频数据丢弃,不发送给guest。默认值为true。(比如在视频会议中,只将发言人的OutputAudio设为true,以减少带宽和避免杂音) /// </summary> bool OutputAudio { get; set; } /// <summary> /// 输出音量的放大系数。取值范围1~10。默认值1(表示不放大)。可以在运行时动态修改。 /// </summary> int VolumeAmplifyFactor { get; set; } /// <summary> /// 是否静音(来自任何owner的声音都不播放)。 /// </summary> bool Mute { get; set; } /// <summary> /// OMCS使用的音源输入模式。默认值为OnlyMicrophone。 /// </summary> AudioInputMode AudioInputMode { get; set; } /// <summary> /// 当AudioInputMode为BothSoundcardAndMicrophone或BothExtendAndMicrophone时,用于控制声卡或自定义扩展音源的声音的放大系数。默认值为0.5。(一般声卡或自定义扩展音源播放的是作为背景音乐存在的) /// </summary> float SoundcardVolumeCoef { get; set; } /// <summary> /// 刚刚播放的音频帧(10ms的PCM数据)。 /// </summary> event CbGeneric<byte[]> AudioPlayed; /// <summary> /// 刚刚从麦克风或自定义声音采集器采集的音频帧(10ms的PCM数据)。 /// </summary> event CbGeneric<byte[]> AudioCaptured; /// <summary> /// 如果连接到了多个人的麦克风,此事件用于定时通知最大声音的说话者。参数:SpeakerID - 声音的分贝值。 /// 当没有人说话时,SpeakerID 为 null。 /// 注:只有当Advanced.AudioMixedStrategy的值不是AudioMixedStrategy.All时,才会触发此事件。 /// </summary> event CbGeneric<string, int> MaxVoiceSpeakerNotified; /// <summary> /// 用于注入自定义的视频采集器工厂。(必须在调用Initialize方法之前设置才有效。) /// </summary> IVideoCapturerFactory VideoCapturerFactory { set; } /// <summary> /// 要使用的摄像头的索引。默认值0。可以在运行时动态修改。 /// 【如果在初始化之前将CameraDeviceIndex设置为0,那么框架将自动选择第一个有效的摄像头,这意味着初始化完成后,CameraDeviceIndex的值可能不再是0。】 /// </summary> int CameraDeviceIndex { get; set; } /// <summary> /// 摄像头采集视频的大小。如果当前摄像头不支持指定的尺寸,则会自动匹配到最相近的分辨率。可以在运行时动态修改。 /// </summary> Size CameraVideoSize { get; set; } /// <summary> /// 摄像头的最大帧频。默认值:15。(必须在调用Initialize方法之前设置才有效。) /// </summary> int MaxCameraFrameRate { get; set; } /// <summary> /// 摄像头采集的视频的编码质量。取值0~31,取值越小,质量越高。可以在运行时动态修改。 /// </summary> int CameraEncodeQuality { get; set; } /// <summary> /// [从Owner的角度]是否根据音频反馈以及视频丢帧情况自动调整视频编码质量。默认值为true。(以保证音频连贯性)。可以在运行时动态修改。 /// </summary> bool AutoAdjustCameraEncodeQuality { get; set; } /// <summary> /// 是否将摄像头集到的视频输出给Guest。 默认值为true。(必须在初始化完成之后设置才有效,可动态修改。) /// 如果为true,表示输出;否则,表示将采集到的视频数据丢弃,不发送给guest。默认值为true。 /// </summary> bool OutputVideo { get; set; } /// <summary> /// 当摄像头切换完成以后,触发此事件。参数为:切换之前摄像头索引 - 切换后的摄像头索引 /// </summary> event CbGeneric<int, int> CameraIndexChanged; /// <summary> /// 切换摄像头【异步调用】。如果切换成功,将触发CameraIndexChanged事件。 /// </summary> /// <param name="deviceIndex">要切换到的目标摄像头的索引</param> void ChangeCameraDeviceAsyn(int deviceIndex); /// <summary> /// 桌面的最大帧频。默认值:10。(必须在调用Initialize方法之前设置才有效。) /// </summary> int MaxDesktopFrameRate { get; set; } /// <summary> /// 是否将桌面图像输出给Guest。默认值为true。(必须在初始化完成之后设置才有效,可动态修改。) /// </summary> bool OutputDesktop { get; set; } /// <summary> /// 允许guest操作桌面。默认值为true。(必须在初始化完成之后设置才有效,可动态修改。) /// </summary> bool AllowControlDesktop { get; set; } /// <summary> /// 本地桌面的编码质量。取值0~31,取值越小,越清晰。可以在运行时动态修改。 /// </summary> int DesktopEncodeQuality { get; set; } /// <summary> /// 要捕捉的屏幕的区域。 /// 如果set为null,则表示捕捉整个屏幕。可以在运行时动态修改。【桌面采集器会将长和宽修正为8的整数倍】 /// </summary> Rectangle? DesktopRegion { get; set; } /// <summary> /// 用于缓存白板课件的根目录。默认值为运行目录下的"Coursewares"文件夹。 /// </summary> string CoursewaresRootPath { get; set; } /// <summary> /// 用于注入自定义的图片转换器工厂。(必须在调用Initialize方法之前设置才有效。) /// </summary> IImageConverterFactory ImageConverterFactory { get; set; } /// <summary> /// [从Owner的角度]Owner发送帧数据给Guest时,通道的选择模型。默认值为P2PChannelFirst。必须在初始化前设置才有效。 /// </summary> ChannelMode ChannelMode { get; set; } /// <summary> /// 记录OMCS日志的文件路径,默认值为在运行目录下的OmcsLog.txt文件。(运行目录下可能没有写权限)。设为null(且OmcsLogger也设为null),表示不记录任何日志。 /// </summary> string OmcsLogPath { get; set; } /// <summary> /// 如果被赋非null值,则OmcsLogPath属性将无效。(必须在调用Initialize方法之前设置才有效。) /// </summary> IAgileLogger OmcsLogger { set; } /// <summary> /// 是否记录安全日志。默认值为false。 /// </summary> bool SecurityLogEnabled { get; set; }
(1)基础属性
ServerIPE 只读属性显示了当前多媒体管理器连接的OMCS服务器的地址。
CurrentUserID 只读属性可以获取当前登录的用户的UserID。只有在多媒体管理器初始化(Initialize方法)成功之后,该属性才有效。
Available 属性显示了当前多媒体管理器是否与OMCS服务器成功建立了连接并完成了初始化,而处于可用状态。只有该属性为true时,多媒体管理器才可以正常使用。
AudioMessageController 属性是语音消息控制器,在使用之前,先要调用IAudioMessageController的Initialize方法将其初始化。具体可参见:OMCS 开发手册(10) -- 语音消息。
OmcsLogPath 多媒体管理器会捕获内部产生的所有异常,并将其记录到OmcsLogPath指定的日志文件。其默认是运行目录下的OmcsLog.txt文件,但是,如果我们将基于OMCS的客户端程序安装在C盘,那么在win7下以普通身份运行起来后,是没有权限写运行目录的,这时,我们通常将日志路径设在“我的文档”的目录下面。
ChannelMode 属性用于控制通道选择模型。即当存在P2P通道时,那么多媒体数据传送的路径就有两种:经服务器中转、或直接经P2P通道传送。ChannelMode用于控制使用哪条通道传送多媒体数据。
(2)语音视频相关
CameraDeviceIndex、MicrophoneDeviceIndex、SpeakerIndex 属性用于分别设置要使用的本地摄像头、麦克风、以及扬声器的索引。
如果当前机器接有多个摄像头或者话筒设备,则可以通过这几个属性来明确指定要使用哪个设备。在大多数情况下,这两个属性设为0即可,其默认值也是0。
OutputAudio和OutputVideo 属性用于控制是否输出采集到的音频/视频数据给guest。
比如在视频会议中,我们可以只将当前发言人的OutputAudio设为true,而将其它成员的OutputAudio设为false,以减少带宽和避免杂音。
CameraVideoSize 用于设定摄像头采集视频的大小,现在的摄像头通常都支持很多种视频采集尺寸,后面的文章会详细介绍如何获取摄像头所支持的视频尺寸列表。
CameraEncodeQuality 属性用于设定摄像头视频的编码质量。我们可以根据网络状态动态调节它们的值,以控制带宽的使用。
AutoAdjustCameraEncodeQuality属性用于控制是否开启视频质量自动调节功能。
AudioInputMode 属性用于设定OMCS使用的音源输入模式。
SoundcardVolumeCoef 属性用于控制声卡或自定义扩展音源的声音的放大系数。 当AudioInputMode设置为BothSoundcardAndMicrophone或BothExtendAndMicrophone时,才有效。
OMCS内部内置了一套音频/视频优化策略,其可以根据音频反馈以及视频丢帧的情况自动调整视频编码质量,以优先保证音频的清晰与流畅。如果该属性设为true,将开启这套内置策略。如果该属性开启,则CameraEncodeQuality属性的设置,将不再有效;但是我们仍然可以通过CameraEncodeQuality属性读取到当前视频的编码质量。
AudioCapturerFactory、VideoCapturerFactory 属性分别用于注入自定义的音频/视频采集器,比如视频图像来自于网络摄像头或者特殊的视频采集卡(如AV-878采集卡)。具体可参见:OMCS 开发手册(07) -- 扩展音视频输入。
MaxCameraFrameRate 属性用于设定摄像头的最大帧频。只有在多媒体管理器初始化(Initialize方法)之前设置,该属性才有效。
ChatGroupEntrance 属性用于对多人语音/视频聊天组进行直接支持。具体可参见:OMCS 开发手册(08) -- 多人语音/视频。
上述的很多属性可以在运行的过程中进行修改,这就提供了类似动态切换设备的能力,具体可参考 OMCS 开发手册(11) -- 深入摄像头、麦克风、扬声器。
(3)远程桌面相关
OutputDesktop 同 OutputAudio和OutputVideo 属性的作用一样。
AllowControlDesktop 是对Guest操作当前Owner桌面的总控制,如果AllowControlDesktop为false,即使Guest端的桌面连接器的WatchingOnly为false,Guest仍然不能操作桌面。
DesktopEncodeQuality 是桌面视频的编码质量。同CameraEncodeQuality不一样,DesktopEncodeQuality不会根据网络状况自动调节。
DesktopRegion 属性允许我们只共享桌面的一部分(比如,仅仅共享某个应用程序的窗口)给Guest,这样可以节省带宽。
MaxDesktopFrameRate 属性用于设定桌面的最大帧频,只有在多媒体管理器初始化(Initialize方法)之前设置,该属性才有效。
(4)电子白板相关
CoursewaresRootPath和ImageConverterFactory用于电子白板的课件功能。
当客户端从服务器下载课件文件时,CoursewaresRootPath指定了用于存储这些白板课件文件的根目录。
ImageConverterFactory 用于注入自定义的图片转换器工厂,OMCS通过它可以将目标类型的课件转换成图片以供电子白板使用。
关于如何实现ImageConverterFactory接口,可参考 OMCS 使用技巧 -- 扩展电子白板支持课件的类型:word、pdf、ppt 。
2.方法
IMultimediaManager接口中所有的方法定义如下:
/// <summary> /// 与多媒体服务器建立连接,并初始化本地多媒体管理器。 /// 如果与服务器连接失败,将抛出网络异常。 /// </summary> /// <param name="userID">当前登录的用户ID。</param> /// <param name="password">当前登录的用户的密码。</param> /// <param name="serverIP">OMCS服务器IP</param> /// <param name="serverPort">OMCS服务器端口</param> void Initialize(string userID,string password, string serverIP, int serverPort); /// <summary> /// 主动断开来访者guest到本地多媒体设备的连接。 /// </summary> /// <param name="guestID">来访者的用户ID</param> /// <param name="deviceType">设备类型</param> /// <param name="notifyGuest">是否通知对方。如果通知对方,对方的连接器将触发Disconnected事件。</param> void DisconnectGuest(string guestID, MultimediaDeviceType deviceType ,bool notifyGuest); /// <summary> /// 主动断开所有来访者到本地多媒体设备的连接。 /// </summary> /// <param name="notifyGuest">是否通知对方。如果通知对方,对方的连接器将触发Disconnected事件。</param> void DisconnectGuest(bool notifyGuest); /// <summary> /// 获取所有连接到当前多媒体设备的Guest列表。 /// </summary> List<string> GetGuests(MultimediaDeviceType deviceType); /// <summary> /// 查看已经连接了哪些用户的指定设备? /// </summary> List<string> GetOwners(MultimediaDeviceType deviceType); /// <summary> /// 查询本地的某设备是否正在工作? /// </summary> /// <param name="deviceType">设备类型</param> /// <returns>工作中?</returns> bool DeviceIsWorking(MultimediaDeviceType deviceType); /// <summary> /// 和目标用户之间是否打通了P2P通道? /// </summary> /// <param name="destUserID">目标用户的ID</param> bool IsP2PChannelExist(string destUserID); /// <summary> /// 目标用户是否在线 /// </summary> /// <param name="userID">目标用户的ID</param> bool IsUserOnline(string userID); /// <summary> /// 切换摄像头【异步调用】。如果切换成功,将触发CameraIndexChanged事件。 /// </summary> /// <param name="deviceIndex">要切换到的目标摄像头的索引</param> void ChangeCameraDeviceAsyn(int deviceIndex);
在使用多媒体管理器之前,首先要调用Initialize方法将其进行初始化,初始化将做如下几件事情:
(1)与目标OMCS服务器建立连接。
(2)使用参数传入的帐号密码进行登录。
(3)如果登录成功,则初始化本地的各个多媒体设备。
注意,如果与服务器连接失败,或者帐号密码错误,则将抛出相应的异常。只有Initialize方法成功返回后,其它的方法才能被正常调用。
DisconnectGuest 方法用于Owner主动断开某个guest或所有guest到本地某设备的连接,该方法为Owner提供了一种主动性的权利。
IsP2PChannelExis方法用于检查和目标用户之间是否打通了P2P通道,如果P2P通道存在,则后续的音视频数据将直接走P2P通道,而不是经过服务器中转。
IsUserOnline方法用于检查目标用户是否在线。
ChangeCameraDeviceAsyn 方法用于切换摄像头。切换成功触发CameraIndexChanged事件。
剩下的几个方法,像DeviceIsWorking、GetGuests、GetOwners等,比较容易理解,就不再赘述。
3.事件
IMultimediaManager接口中所有的事件定义如下:
/// <summary> /// 当与目标媒体服务器的连接断开时,触发此事件。事件参数:目标多媒体服务器的地址。 /// </summary> event CbGeneric<IPEndPoint> ConnectionInterrupted; /// <summary> /// 当与目标媒体服务器重连成功时,触发此事件。事件参数:目标多媒体服务器的地址。 /// </summary> event CbGeneric<IPEndPoint> ConnectionRebuildSucceed; /// <summary> /// 当某个guest连接到当前设备时,触发此事件。参数为guestID - MultimediaDeviceType /// </summary> event CbGeneric<string, MultimediaDeviceType> DeviceConnected; /// <summary> /// 当某个guest从当前设备断开时,触发此事件。参数为guestID - MultimediaDeviceType /// </summary> event CbGeneric<string, MultimediaDeviceType> DeviceDisconnected; /// <summary> /// 当本地多媒体设备报错时,会触发该事件。参数为 MultimediaDeviceType - DeviceIndex - 错误描述 /// </summary> event CbGeneric<MultimediaDeviceType, int, string> DeviceErrorOccurred; /// <summary> /// 当连接器与目标设备之间的连接断开时,触发此事件。参数为: 断开的连接器 - 断开的原因 /// </summary> event CbGeneric<IMultimediaConnector, ConnectorDisconnectedType> ConnectorDisconnected; /// <summary> /// 当多媒体管理器关闭并释放时,触发此事件。 /// </summary> event CbGeneric Disposed; /// <summary> /// 刚刚播放的音频帧(10ms的PCM数据)。 /// </summary> event CbGeneric<byte[]> AudioPlayed; /// <summary> /// 刚刚从麦克风或自定义声音采集器采集的音频帧(10ms的PCM数据)。 /// </summary> event CbGeneric<byte[]> AudioCaptured; /// <summary> /// 如果连接到了多个人的麦克风,此事件用于定时通知最大声音的说话者。参数:SpeakerID - 声音的分贝值。 /// 当没有人说话时,SpeakerID 为 null。 /// 注:只有当Advanced.AudioMixedStrategy的值不是AudioMixedStrategy.All时,才会触发此事件。 /// </summary> event CbGeneric<string, int> MaxVoiceSpeakerNotified; /// <summary> /// 当摄像头切换完成以后,触发此事件。参数为:切换之前摄像头索引 - 切换后的摄像头索引 /// </summary> event CbGeneric<int, int> CameraIndexChanged;
多媒体管理器初始化成功后,与OMCS服务器的长连接就建立了。当与OMCS服务器的连接断开时,将触发ConnectionInterrupted事件。特别要注意:当ConnectionInterrupted事件触发时,本地的所有多媒体设备都将停止工作。
多媒体管理器内部自动使用了断线重连机制,当网络恢复后,会自动重连OMCS服务器,如果重连成功,将会触发ConnectionRebuildSucceed事件。我们可以通过ConnectionInterrupted事件、ConnectionRebuildSucceed事件、以及Available属性来监控多媒体管理器与OMCS服务器之间的连接状态。
当本地多媒体设备报错时,会触发事件DeviceErrorOccurred。并通过事件第三个参数 来查找错误原因。
当多媒体连接器与目标设备之间的连接断开时,会触发事件ConnectorDisconnected。并通过参数 ConnectorDisconnectedType> 来查找断开原因。
当多媒体管理器关闭并释放时,触发Disposed事件。
AudioPlayed事件用于暴露刚刚播放的音频帧。
AudioCaptured事件用于暴露刚刚从麦克风或自定义声音采集器采集的音频帧。
MaxVoiceSpeakerNotified事件。 如果连接到了多个人的麦克风,此事件用于定时通知最大声音的说话者。只有当Advanced.AudioMixedStrategy的值不是AudioMixedStrategy.All时,才会触发此事件。
CameraIndexChanged 当摄像头切换完成以后,触发此事件。
当某个guest连接到本地的某多媒体设备时,将触发DeviceConnected事件;当某个guest与本地的某多媒体设备断开时,将触发DeviceDisconnected事件。这两个事件的参数都指明了设备的类型和guest的UserID。
三.Singleton模式
可以想象,一般在一个进程中,我们只需要一个多媒体管理器实例。那么,我们可以以Singleton模式来使用多媒体管理器。
在设计OMCS时,IMultimediaManager接口的实现类MultimediaManager是internal的,在OMCS外部,我们看不到它的存在,所以也就没有办法new一个MultimediaManager实例。OMCS已经为我们提供了MultimediaManagerFactory静态类,让我们更方便地以单件模式使用多媒体管理器实例,其GetSingleton方法直接返回这个单件。
本文主要从作为设备的Owner的角度出发,讨论了多媒体设备管理器有哪些职责,以及其提供的API是如何工作的。下篇文章,我们将从作为Guest的角度触发,讨论如何使用多媒体连接器。
--------------------------------------------------------------------------------------------------------------------
阅读 更多OMCS开发手册系列文章 。
Q Q:168757008
官网: www.oraycn.com