的多媒体终端的设计与实现林韬北京邮电大学信息与通信工程学院,北京(100876)E-mail:nintendolintao@gmail.com摘要:随着多媒体通信技术的发展,SIP协议得到了越来越广泛的应用和支持。本文首先简要介绍了SIP协议和PJSIP开源协议栈,然后设计并实现了一个Windows平台下具有音视频通话功能的SIP多媒体终端,其中重点阐述了多媒体终端的设计思路以及两大功能模块:SIP引擎和视频引擎开发中的关键技术,包括视频媒体协商、视频数据的采集、编解码、传输和显示。最终实现的多媒体终端在局域网内的音视频通话测试中表现良好,基本满足设计要求。关键词:SIP;PJSIP;多媒体终端;音视频通话中图分类号:TP3931.引言随着计算机网络技术的飞速发展,人们越来越多地利用互联网进行实时的多媒体通信,其中包括即时消息、数据传输和音视频通话等。目前,国际上用于多媒体通信的信令协议主要有SIP和H.323,两者都对IP系统信令提出了完整的解决方案,但由于SIP具有简单、开放、灵活、可扩展等多方面明显优点,得到了业界的普遍认可,已成为下一代网络软交换体系中的重要协议,并将在未来多媒体通信中起着重要作用。在微软、北电、思科等厂商以及3GPP等标准制定机构的大力支持下,市场上出现了越来越多的SIP客户端软件、智能多媒体终端以及支持SIP协议的服务器和软交换设备[1]。本文基于开源的SIP协议栈——PJSIP,以H.263为视频编码格式,运用Windows平台下的网络编程、视频捕捉以及界面开发等相关技术,设计并实现了可进行实时音视频通信的SIP多媒体终端软件。2.PJSIP基于一个开放、成熟的SIP开源库进行开发不但可以大大提高效率,也能增强与其他SIP系统的兼容性。经过对几种常用SIP开源库的使用和比较,本文最终选择了PJSIP。PJSIP是用C编写的,相当优秀的一个SIP协议栈,其主要特征包括:(1)极具移植性。支持的平台有Windows、WindowsMobile、Linux、Unix、MacOSX、RTEMS、SymbianOS等。(2)非常小的存储空间。包含完整SIP功能的代码库仅150K。(3)高性能。采用优秀的内存分配机制,运行速度快。(4)支持众多的SIP特征和扩展。比如IM、presence、eventsubscription、calltransfer、PIDF等。(5)丰富的SIP文档和范例。PJSIP开源库由一系列功能库所组成,如图1所示,PJLIB是系统抽象层,PJLIB-UTIL提供有用的工具函数,PJNATH解决NAT穿越问题,PJMEDIA和PJMEDIA-CODEC负责SDP协商、媒体编码和媒体传输,PJSIP是核心SIP协议栈,PJSIP-SIMPLE实现Presence和即时消息,PJSIP-UA提供SIP用户代理库,PJSUA位于最高层,整合了下层模块的全部功能。PJSIP的每个功能库根据其所在的层次以及负责的功能都提供了丰富的编程接口,方便开发人员使用。消息处理层,如图2所示,从下往上依次是TRANSPORT层、ENDPOINT层、TRANSACTION层、UA层和DIALOG层。每个消息处理层以模块的形式注册到协议栈中,开发者也可以编写并添加自己的消息处理模块,对SIP消息进行解析或修改。当TRANSPORTMANAGER收到SIP消息包时,会把该SIPEVENT通知上层的ENDPOINT,而ENDPOINT会找到对应的接收者,先把EVENT传给TRANSACTIONLAYER,然后再传给UALAYER(传递的顺序由每个模块的优先权决定),如果UALAYER指定要处理TRANSACTION的EVENT,TRANSACTIONLAYER也会把解析后的EVENT传给UALAYER[2]。图2PJSIP的消息流图3.多媒体终端的设计3.1系统分析本文的SIP多媒体终端包括5大功能模块,如图3所示。图3多媒体终端的功能模块(1)即时聊天模块。通过发送MESSAGE消息包来传递即时信息以及编辑信息的状态,消息包来实时更新在线状态。(2)音视频通话模块。负责发起、结束和保持音视频通话。音频支持speex、GSM、G.711、G.722编码,采用RTP进行传输;视频支持H.263编码,采用UDP进行传输。(3)历史记录模块。包括聊天记录、通话记录和SIP消息记录。(4)用户和好友管理模块。添加、删除、修改用户和好友的信息,比如SIPURI、注册服务器地址、用户名和密码等。(5)参数配置模块。设置UDP、RTP端口,音视频编码的优先级等。3.2总体设计SIP多媒体终端的软件框架如图4所示,主要分为SIP引擎、视频引擎和应用程序界面。SIP引擎基于PJSIP开源库进行开发,完成所有SIP相关的操作、音频通话功能以及视频媒体能力协商。视频引擎主要负责视频数据的采集、编解码、传输和播放。应用程序界面与SIP引擎和视频引擎交互SIP事件消息和视频数据,并为用户提供了简单直观的使用窗口。图4多媒体终端的软件框架3.3界面设计本文采用Windows下的MFC类库进行界面编程,应用程序界面如图5所示,左上为信息输出框,左下为即时消息输入框,中间是好友列表和通话控制面板,右侧是可伸缩的视频显示窗,只有在视频通话时才会展开。图5多媒体终端的应用程序界面库进行封装,并根据具体应用提供了一些更为方便的接口和实用函数库。SIP引擎库主要包括CDQsipEngine、CDQsipAppConfig和CDQsipEngineObserver三个类。CDQsipEngine是SIP引擎库的核心,它定义了大部分SIP应用的函数,比如引擎的初始化、运行和销毁、登录与注销、发起与结束通话、发送即时消息以及调节音量等。CDQsipAppConfig是辅助CDQsipEngine运行的配置类,它负责用户、好友、通话等信息的保存与更新,以及对配置文件进行读取和写入。CDQsipEngineObserver是SIP引擎的观察者类,它是一个抽象接口。这是基于观察者设计模式的编程方式,SIP引擎在初始化时会设置一个观察者对象的指针,在本文中该指针指向应用程序界面中View对象,View类继承CDQsipEngineObserver抽象类并实现其定义的一系列与SIP引擎进行交互的纯虚函数,比如其中一个函数声明如下:voidEngineRecvIM(constpj_str_t*uri,constpj_str_t*content);其中pj_str_t是PJSIP库中定义的字符串结构。EngineRecvIM函数会在SIP引擎接收到新消息后被调用,所以View类在该函数的实现中可以将消息发送者的URI、消息内容以及接收时间有效的组织起来,显示在程序界面的输出框中上。使用观察者模式的好处是,SIP引擎可以完全独立与上面的应用层,无论是MFC界面或是控制台程序,只要实现了CDQsipEngineObserver抽象类,都能方便的与SIP引擎进行交互。为了描述音视频通话中终端设备支持的媒体格式的情况和特点,SIP使用会话描述协议(SDP)来进行媒体能力协商。PJSIP中的PJMEDIA库已经实现了音频通话的全部过程,包括音频格式和RTP端口的SDP协商,音频数据的采集、编码和传输等,所以本文只需要开发视频通话模块。SIP引擎的SDP媒体协商中需要增加对视频格式的支持,为了保持与PJSIP库的独立性,本文选择在协议栈中以注册方式添加单独的消息处理模块。pjsip_module是SIP模块的结构体,其成员变量包含on_rx_request、on_rx_response、on_tx_request、on_tx_response四个函数指针,分别代表接收请求和响应、发送请求和响应的处理函数,函数传入的参数是收到或发送的SIP消息结构体。视频模块在这些函数的实现中解析并修改SDP消息体,比如在发送的请求消息的SDP中添加支持H.263视频的媒体行文本:m=video6011RTP/UDP34a=rtpmap:34H263/90000其中,video表示媒体类型,6011表示UDP端口号,RTP/UDP是传输方式,34是H.263在RTP中定义的静态载荷类型[4],[5]。此外,还需要设置SIP模块的优先权,这决定着SIP消息的传递顺序。视频模块选择了传输层优先权,这样该模块就位于传输层之上,其他所有层之下,于是所有进出协议栈的SIP消息都必须经过视频模块的处理,而视频模块对于其上层的模块来说是“隐形”的。视频模块构造完成后通过pjsip_endpt_register_module函数将其注册到协议栈的ENDPOINT中。若视频媒体协商成功后,通话双方都获得了彼此的IP地址和视频端口,则打开视频引擎,开始视频通话。4.2视频引擎库视频引擎库主要负责视频数据的采集、编解码、传输和播放等。视频引擎的实现流程如所示,发送端采集USB摄像头的视频数据并显示在本地视频窗口中,同时用H.263Codec对采集到的数据进行编码,然后通过SOCKET以UDP方式发送到对端。接收端的SOCKET接收到视频帧后对其进行解码,然后显示在视频窗口中。图6视频引擎的实现流程视频引擎库主要包括CDQvideoEngine、VideoCapture和DSocket三个类。CDQvideoEngine提供视频引擎库对外的统一接口,内部使用VideoCapture对象和DSocket对象来分别提供视频采集和数据传输功能。4.2.1视频数据的采集VideoCapture类使用VFW(VideoforWindows)数字视频开发包的AVICap库来进行视频数据的采集。使用VFW进行视频捕捉首先需要调用函数capCreateCaptureWindow创建一个视频捕捉窗,之后的捕捉操作和参数设置都是对该捕捉窗调用相应的宏或发送消息来完成。在捕捉到视频图像后,多媒体终端需要在本地显示,并且对采集到的视频数据进行H.263编码后发送到远端,这需要通过函数capSetCallbackOnVideoStream注册一个回调函数,并在函数中实现以上功能,那么每当VFW采集到一帧图像后该函数就会被调用。当视频捕捉环境设置完毕后,则调用函数capCaptureSequenceNoFile开始视频捕捉。本文运用VFW进行视频捕捉的流程如图7所示。图7运用VFW进行视频捕捉的流程4.2.2H.263编解码H.263是由国际电联ITU-T制定的用于视频会议的低码率视频编码标准。与H.261相比,H.263具有更强的编码性能和纠错能力,原则上它只需要一半的带宽就可获得与H.261相同的视频质量,非常适合Internet环境下的视频传输。本文使用的是开源项目VideoNet中的H.263编解码库,该库将经典的TMN(TestModelNear-term)库从C移植到C++上来,并进行了修改和封装,具有良好的编解码性能,特别适用于实时环境。H.263支持5种分辨率,除了H.261所支持的QCIF和CIF外,还包括SQCIF、4CIF和16CIF。SQCIF的分辨率相当于QCIF的一半,而4CIF和16CIF分别为CIF的4倍和16倍,本文选择的是QCIF的分辨率模式。值得一提的是,H.263编解码库只支持YUV格式图像的编解码,所以在程序实现中需要将捕捉到的RGB格式图像转换到YUV格式后再进行编码。在接收端也需要将接收到的YUV数