使用RTC的API来进行通信是一个非常简单的过程。
- 增强的客户端应用决定客户端通信平台的能力。
- 应用程序在通信期间使用首选的视音频设备。
- 应用程序发起通信会话。
- 在 RTC 层协调数据捕获、压缩和传输,这使得应用程序不用负责这一任务。使用哪一种音视频的编码解码器由通信双方的连接质量决定。
- 参与会话的应用程序接受、解压并重放被传输的数据。
图一、音视频会议例子界面
本文描述了怎样为一个应用程序添加 PC-to-PC 的 RTC 基本能力;我们假定你对使用COM对象开发Windows应用程序已经很熟悉。本文所讨论的源代码可在本文开始所给出的连接里获得。我们以后将会讨论PC-to-Phone、 出席和XML配置。
例子代码展示了使用实时通信API实现音视频会议、应用程序共享、白板、简单的点对点聊天和音视频调节向导的基本要素。其他RTC 支持但本文没有讨论的特征有回波抵消(AEC)、前向错误校验(FEC)、 带宽估计、动态抖动缓冲管理、自动增益控制(AGC)和服务质量(QC)控制算法。在《Microsoft Windows 实时通信(RTC)客户端的媒体支持》中描述了上述特征。
RTC客户端接口
所需头文件:
rtccore.h
你的应用程序需要通过 CoCreateInstance()来获得RTC接口,CLSID_RTCClient(GUID = {7a42ea29-a2b7-40c4-b091-f6f024aa89be})作为参数。一旦获得了接口,用Initialize()来初始化COM对象,以确定该平台的通信能力。
// Initialize the RTC COM object hr = CoCreateInstance (CLSID_RTCClient, NULL, CLSCTX_INPROC_SERVER, IID_IRTCClient, (LPVOID *)&m_pClient); // Initialize the client interface hr = m_pClient->Initialize();选择通信类型
下一步是选择首选的通信类型和相关设备(摄像机、麦克风等)。缺省配置是激活所有通信类型。如果会话的参与者能共享应用程序、传送即时消息和音视频会议,那么这些通信类型自动被激活。如果某一参与者不支持某种通信类型,那么所有的参与者都不能激活该类型。
m_pClient->SetPreferredMediaTypes ( RTCMT_ALL, VARIANT_TRUE );
会议参与者的平台性能和可用带宽决定了使用哪一种codec。
- 视频:Windows RTC 客户端支持分辨率为QCIF(176×144)的H.261和H.263 codecs。 这些比特率可变的codecs以6~125 Kbps传送视频数据。使用IRTCClient方法中的put_MaxBitRate和put_TemporalSpatialTradeOff有可能会影响到视频传送的空间和瞬时清晰度。
- 音频:Windows RTC客户端支持许多音频codec。音频codec由连接的两端共同决定。下表列出了所支持的音频codec。
CODEC | Sampling Rate (kHz) | Bit Rate (Kbps) | Frame Size (msec) |
---|---|---|---|
G.711 | 8 | 64 | 20 |
G.722.1 | 16 | 24 | 20 |
G.723 | 8 | 6.4 | 30, 60, or 90 |
GSM | 8 | 13 | 20 |
DVI4 | 8 | 32 | 20 |
SIREN | 16 | 16 | 20, or 40 |
调整通信设备
选择了首选的通信类型和相关设备之后,调整通信设备。RTC的API提供了向导对摄像机和麦克风进行调整。使用RTCClient中的方法InvokeTuningWizard()可调整它们的设置。
图2:摄像机调节向导
图3:麦克风调节向导
初始化会话
在应用程序与其他参与者连接之前,它必须能够处理会话中的RTC事件。PC-to-PC的通信中,应用程序捕获即时消息事件、音量事件、媒体事件、客户端消息事件和会话状态改变事件。下述代码展示了怎样创建一个事件过滤器来捕获RTC客户端的特定事件。
lEventMask设置了一组应用程序感兴趣的事件。(为获得全部的事件列表,可在 MSDN 站点搜索RTC_EVENT,这样可以获得关于每一事件的更多信息。)CRTCEvents 类在客户端之间分配事件。RTCEvents 对象在应用程序和 IRTCEventNotification 接口之间创建接口。所有的RTC事件由RTCEvents 类处理。
// Set the event filter to listen for RTC events // Using RTCEF_ALL will listen for all events // For the sample application, we will demonstrate how to set the // event listener for a limited set of events. long lEventMask = RTCEF_SESSION_STATE_CHANGE | RTCEF_MESSAGING | RTCEF_MEDIA | RTCEF_INTENSITY | RTCEF_CLIENT; hr = m_pClient->put_EventFilter( lEventMask ); // Create the event sink object m_pEvents = new CRTCEvents; // initialize the event handler hr = m_pEvents->Advise( m_pClient, m_hWnd ); // Set the listen mode for RTC client // RTCLM_BOTH opens the standard SIP port 5060, as well as // a dynamic port. hr = m_pClient->put_ListenForIncomingSessions(RTCLM_BOTH);音视频的媒体类型可在会话过程中添加或删除,因此客户端必须能监听这些类型的事件。在"处理实时流会话事件"这一节中可获得关于状态改变和事件处理的更多信息。
处理RTC事件
一旦事件处理者 IRTCEventNotification 接收器中进行了注册,接收和处理RTC事件就变得相当的容易了。当例子程序接收到RTC事件时,应用程序的事件处理者就对应用程序的消息处理者发一个消息。OnRTCEvent() 处理应用程序接收到的所有事件。
OnRTCEvent(UINT message, WPARAM wParam, LPARAM lParam) { // Based on the RTC_EVENT type, query for the // appropriate event interface and call a helper // method to handle the event switch ( wParam ) { ... case RTCE_MEDIA: { IRTCMediaEvent * pEvent = NULL; hr = pDisp->QueryInterface( IID_IRTCMediaEvent, (void **)&pEvent ); if (SUCCEEDED(hr)) { OnRTCMediaEvent(pEvent); SAFE_RELEASE(pEvent); } } break; ... }创建通信会话
当你在RTC中发起一个呼叫之前,你必须创建并且初始化一个会话。 然后你可以输入参与者的IP地址来发起一个呼叫。可可能通过属于一个e-mail 地址或者一个电话号码来激活一个会话。然而,这一功能需要一个SIP注册服务器,对它的讨论超出了本文的范围。参阅MSDN可获得关于SIP注册服务器的更多信息。
RTC目前还不支持多方视频通话,因此应用程序在初始化一个新会话之前,必须保证没有视频会议在进行。在它第一个发布版本中,Windows RTC客户端只支持多方电话会议,并不支持多方音视频会话和视频会议。
为与另一台PC通话,确定RTC会话类型并且使用IRTCSession接口创建一个同类型的会话。下列代码展示了如何创建会话。
HRESULT CAVDConfDlg::MakeCall(RTC_SESSION_TYPE enType, BSTR bstrURI) { ... // Create the session IRTCSession * pSession = NULL; hr = m_pClient->CreateSession(enType, NULL, NULL, 0, &pSession); // Add the participant to the session hr = pSession->AddParticipant(bstrURI, NULL, &m_Participant); ... return S_OK; }处理实时流会话事件
根据不同的会话类型,存在媒体事件、音量事件、即时消息事件和会话状态改变事件。
媒体事件
处理媒体事件需要得到媒体类型、事件类型和原因,然后发送消息给会话窗口。应用程序可以使用get_MediaType()从视频、音频、T120和实时传输协议(RTP)事件中接收消息。例子程序展示了如何获得媒体事件并将其发送给媒体对话框去处理。
void CAVDConfDlg::OnRTCMediaEvent(IRTCMediaEvent *pEvent) { ... hr = pEvent->get_MediaType(&lMediaType); hr = pEvent->get_EventType(&enType); hr = pEvent->get_EventReason(&enReason); if ((m_AVDlg) && (m_AVDlg.GetState () != RTCSS_IDLE)) { // Deliver the media state to the session window m_AVDlg.DeliverMedia(lMediaType, enType, enReason); } }音量事件
当扬声器或者麦克风的音量水平发生变化时产生音量事件。应用程序可使用get_Direction()函数获得发生改变的音频设备。一旦确定了设备,应用程序可获得设备的属性并处理改变。应用程序可通过slider控件来显示音量的改变,或者显示给用户一个音量表。
void CAVDConfDlg::OnRTCIntensityEvent(IRTCIntensityEvent *pEvent) { ... hr = pEvent->get_Direction(&enDevice); hr = pEvent->get_Level(&lLevel); hr = pEvent->get_Min(&lMin); hr = pEvent->get_Max(&lMax); if (m_AVDlg.GetState () != RTCSS_IDLE) { // Deliver the intensity state to the session window m_AVDlg.DeliverIntensity(enDevice, lLevel); } }即时消息事件
使用IRTCMessagingEvent可在会话参与者中传递即时消息。当一个消息事件产生时,应用程序必须获得会话和事件类型,得到相关会话中的参与者信息,以便能将消息传递给适当一方。事件处理者也能处理会话状态的改变。
HRESULT CAVDConfDlg::OnRTCMessagingEvent(IRTCMessagingEvent *pEvent) { ... hr = pEvent->get_Session(&pSession); hr = pEvent->get_EventType(&enType); hr = pEvent->get_Participant(&pParticipant); if (enType == RTCMSET_MESSAGE) { hr = pEvent->get_MessageHeader(&bstrContentType); hr = pEvent->get_Message(&bstrMessage); // Deliver the message to the session window if (m_cMessageDlg) m_cMessageDlg.DeliverMessage(pParticipant, bstrContentType, bstrMessage); } else if (enType == RTCMSET_STATUS) { hr = pEvent->get_UserStatus(&enStatus); // Deliver the user status to the session window m_cMessageDlg.DeliverUserStatus(pParticipant, enStatus); } return S_OK; }会话状态改变事件
会话状态改变事件的处理过程与其他RTC事件相同。会话状态的改变包括建立一个新的音/视频会话,或者通知客户端一个到来的即时消息。下列例子展示了当请求会话时所作的处理;客户端通过一阵铃声被通知,请求被应答,然后会话开始。
Void CAVDConfDlg::OnRTCSessionStateChangeEvent(IRTCSessionStateChangeEvent *pEvent) { ... hr = pEvent->get_State(&enState); hr = pEvent->get_Session(&pSession); switch ( enState ) { case RTCSS_INCOMING: { ... // This event is called when an incoming call occurs RTC_SESSION_TYPE enType; hr = pSession->get_Type(&enType); // Ring the bell m_pClient->PlayRing(RTCRT_PHONE, VARIANT_TRUE); // Accept the session hr = pSession->Answer(); } } ... }应用程序共享:
开启T120应用程序共享非常容易,只需调用 IRTCClient 接口的 StartT120Applet 方法。
hr = m_pClient->StartT120Applet ( RTCTA_APPSHARING );
白板支持:
在应用程序中支持白板,需要调用 StartT120Applet 方法,使用 RTCTA_WHITEBOARD 枚举作为参数。
hr = m_pClient->StartT120Applet ( RTCTA_WHITEBOARD );
关闭会话
要关闭一个会话,所有正在运行T120的应用程序必须被关闭。然后RTC 客户端接口调用ShutDown()并完成关闭会话的过程。
平台性能
使用RTC进行通信需要处理器具有适当的性能。下列例子中,一个1 GHz的Pentium? III处理器和一个2.2 GHz的Pentium 4处理器用于确定当使用RTC特征时处理器的利用率。下表描述了使用本文介绍的RTC特征时处理器的利用率。
任务 | P4 处理器 at 2.2-GHz (% CPU 占用率n)1 | P III 处理器 at 1.0-GHz (% CPU 占用率)2 |
---|---|---|
仅音/视频会议 | 9% | 22% |
添加程序共享(共享IE浏览器) | 10% | 35% |
增加白板 | 12% | 37% |
增加即时消息 | 12% | 37% |
- 注1:P4处理器配置:Intel?主板D850MV;256MB PC800 RDRAM,主板集成声卡, nVidia* GeForce*2 Ultra; Windows XP专业版
- 注2: P III处理器配置:Intel 主板VC820; 256MB PC133 SDRAM, nVidia* GeForce*2 Ultra, Creative* Sound Blaster* Live*, Windows XP 专业版
结论
通过使用实时通信客户端API,在 Windows XP 下开发通信工具已变得相当简单。开发者可迅速设计、配置和开发他们的应用程序。现有的音视频会议应用程序可通过添加 RTC 丰富的通信特征而获益。使用 RTC API 进行开发的程序也可以从一个统一的通信协议中获益。这提高了你的程序与其它文本消息和音视频会议程序互相合作的能力。将 RTC API 与 Intel 的处理器以及 Microsoft Windows XP 相结合,从而向最终用户传达一种创新的通信体验。