1. 论文工作进展情况
在本题目中,到目前为止,已经按照开题报告中的进度,按照计划开展整个项目的实施。但是从整体来看,整体进度还是有些落后。其中主要原因是之前对于项目整体实现方案的确定上面走了一些弯路。在相关技术的学习上也遇到了一些困难,主要是在FFmpeg开源视频解决方案的学习。另外,由于个人工作的原因,也牵扯了一定的时间和精力。
基于目前的情况,在下一步的工作中要重点加快项目整体的进度,同时开始论文的撰写工作。
2. 论文工作中已采用的原理、手段、技术方案
在本选题中,基于移动平台的视频对讲系统主要关注与如何通过WAP和Internet来实现对视频和音频的传输。在整个过程中,一方面要保证数据传输过程中的准确性,避免延迟的出现,实现双向实时通讯,另一方面要满足一定程度的适应性,能够在相对复杂的网络环境下,完成对于视频音频数据的传输。为了满足以上要求,在本选题中主要有这样几个需要研究和解决的问题:一是如何通过移动终端设备对外界环境视频音频进行采集,二是如何有效的对采集到的视频音频数据进行编码解码,三是通过WAP和Internet对数据进行传输。在相关的方面,目前采用了如下一些原理和技术方法:
2.1. 移动平台视频采集技术
在Android系统中,Camera部分的具体功能由本地代码库libui中的Camera Service类调用下层的Camera硬件抽象层来实现,通过Camera类来为下层提供接口,CameraService类与Camera之间利用IPC(Inter- Process Communication,进程间通信)机制来进行通知、数据交换和回调。其中包含如下一些类:
Ø Camera类是系统本地代码对一上层的接口,其中定义了对Camera进行控制和数据流的传输的接口函数,控制部分包括设备的开始、停止、对焦和参数设置等,数据传输包括预览照片、视频的数据流等,以函数指针的形式对上层进行回调。
Ø ICameraClient类被ICamera所继承,主要实现回调的功能,即通过被继承将回调函数指针传给下层。
Ø ICameraService类通过传入一个ICameraClient类来获取一个ICamera类型的接口,这个接日是实际的Camera实现。
Ø ICamera类实际上是一个接口,需要CameraService类进行实现,其中封装的功能基本上与Camera类相对应,区别是不包含回调函数的设置。
Ø CameraService是Camera系统的主要实现部分,主要实现ICameraService和ICamera两个接口,并通过调用Camera硬件抽象层来完成功能。
图表 1 Camera系统类层次结构
2.2. H.264编码标准
H.264是ITU-T的视频编码专家组和ISO的活动图像编码专家组联合制定的新的编码标准。H.264主要优势在于高压缩比、码率低、图像质量高、容错能力强;网络适应性强、应用范围广。
H.264的压缩系统包括视频编码层(VCL,Video Coding Layer)和网络提取层(NAL,Network Abstraction Layer)两部分,如图2所示。VCL和NAL分别完成高效率编码和网络友好性的任务。VCL包括基于块的运动补偿、混合编码和一些新特性。NAL从VCL获得数据,包括头信息、段结构信息和实际载荷,并将它们正确地映射到下层的传输协议上。NAL层的引入大大提高了H.264适应复杂信道的能力。
图表 2 H.264分层结构
视频编码层实现视频数据压缩编码和解码。它采用变换编码,并使用了空间和时间预测的混合编码。每一帧视频数据划分为片、宏块、子块、块。网络提取层负责对视频数据进行封装打包后使其在网络中传送。VCL中的数据要封装至NAL单元中才可以传输或存储,NAL头中包含了数据载荷类型,NAL的RBSP(原始字节序列层)中存储了视频图像的相关信息,RBSP是封装于网络抽象的数据,包括视频编码数据和控制数据,它是NAL的基本传输单元。NAL单元格式如图3所示。
图表 3 NAL单元格式
H.264的主流编解码器一般都可以对YUV格式的图像进行编码,并且支持两种格式的H.264文件输出:RTP格式和Annex B格式。H.264的码流结构如图4所示
图表 4 H.264码流结构
2.3. FFmpeg开源视频解决方案
2.3.1. FFmpeg简介
FFmpeg 是一个集录制、转换、音/视频编码解码功能为一体的完整的开源解决方案。FFmpeg 是在linux 操作平台下开发的编解码解决方案,由于具有高可靠的移植性和高质量的编解码代码库,通过移植我们可以在几乎所有的操作系统中编译和使用它。FFmpeg 支持MPEG、DivX、MPEG4、AC3、DV、FLV等40多种编码,AVI、MPEG、OGG、Matroska、ASF等90多种解码。FFmpeg根目录下有libavcodec、libavformat 和libavutil等子目录。其中在libavcodec 子目录中用于存放各个encode/decode模块,libavformat子目录中用于存放muxer/demuxer模块,libavutil子目录中用于存放内存操作等辅助性模块。
2.3.2. FFmpeg主要数据结构
1) AVFormatContext
AVFormatContext数据结构是ffmpeg 库在格式变化过程中完成输入和输出功能、并且保存在转换过程中的相关数据的一个主要数据结构。任何一个输入和输出文件信息,都会保存在下面定义的全局变量类型的指针数组中。
static AVFormatContext *output_files[MAX_FILES];
static AVFormatContext *input_files[MAX_FILES];
对于数据的输入和输出来说,因为它们使用的是同一个结构体,所以特别需要对该结构中如下定义的iformat或oformat成员分别进行赋值。
struct AVInputFormat *iformat;
struct AVOutputFormat *oformat;
对一个AVFormatContext来讲,这二个成员的值不能同时存在,也就是说一个AVFormatContext不能同时包含有demuxer和muxer信息。在main()主函数开头的parse_options()函数中获取到匹配的muxer和demuxer信息之后,根据所传入的argv参数,初始化每一个输入和输出的AVFormatContext数据结构,并将其保存在相对应的output_files和input_files 指针数组中。在av_encode()函数中,output_files和input_files是仅仅被用做传入函数的参数。除此之外,在别的地方基本没有没有使用到。
2) AVCodecContext
储存AVCodec的指针和与codec相关的数据,比如video的width、height,audio的sample rate 等数据。AVCodecContext中的codec_type,codec_id这两个变量相对于encoder/decoder的匹配来说,尤其显得更为重要。
enum CodecType codec_type;
enum CodecID codec_id;
如上面所示,codec_type保存的是CODEC_TYPE_VIDEO,CODEC_TYPE_AUDIO等媒体类型,codec_id保存的是CODEC_ID_FLV1,CODEC_ID_VP6F等编码方式。 在此以支持flv格式作为演示的对象,在前面所述的av_open_input_file() 函数中,在匹配到没有错误的AVInputFormat demuxer后,通过使用av_open_input_stream()函数来调用AVInputFormat的read_header 接口来运行flvdec.c 文件中的flv_read_header()函数。在flv_read_header()函数内部,根据文件头中的相关数据,生成相对应的视频或音频AVStream,并可以正确的设置AVStream中AVCodecContext数据结构中的codec_type值。codec_id值是在解码过程中依据flv_read_packet()函数执行时所需要的每一个packet头中的数据来设置的。
3) AVStream
AVStream数据结构保存同数据流相关的编解码器,数据段等媒体信息。比较重要的有下面两个成员:
AVCodecContext *codec; /**< codec context */
void *priv_data;
其中codec指针中保存的就是上节所说对的encoder或decoder数据结构。priv_data指针中存储的是与具体编解码流有关系的数据,如下面的代码所示,在ASF的解码过程中,priv_data指针里面保存的就是ASFStream结构的数据。
AVStream *st;
ASFStream *asf_st;
st->priv_data = asf_st;
4) AVInputStream/ AVOutputStream
依据输入和输出数据流的不同,上面所说的AVStream 数据结构都是封装在AVInputStream和AVOutputStream数据结构中,并且在av_encode( )函数中进行使用。 AVInputStream结构中还同时保存与时间有关的相关信息。 AVOutputStream结构中还保留着与音视频同步等有关的信息。
5) AVPacket
AVPacket数据结构的定义如下所示,其主要是用来保存获取到的packet数据。
typedef struct AVPacket {
int64_t pts;
int64_t dts;
uint8_t *data;
int size;
int stream_index;
int flags;
int duration;
void (*destruct)(struct AVPacket *);
void *priv;
int64_t pos;
} AVPacket;
在av_encode()这个函数中,调用AVInputFormat的(*read_packet)(struct AVFormatContext *, AVPacket *pkt)接口,获取到输入文件的一帧数据并且保存在当前输入AVFormatContext的AVPacket成员中。
2.4. 基于SIP的语音通话技术
SIP(Session Initiation Protocol,会话发起协议)是由IETF的网络工作组于2002年设计和提出的。它在TCP/IP网络结构中处于应用层,被设计用来发起、管理和终止一个多媒体的会话,即通话一方对另一方的呼叫和另一方的回答等。SIP协议不依赖下面的运输层协议,可以在UDP和TCP等传输层协议上运行,用来产生双一方或多方的多媒体会话。相比于H.323协议的一些缺点,诸如设备昂贵、没有开源协议栈等,SIP具有简单、良好的可扩展性和与IP网络连接紧密等优点,并且在Internet有免费的开放源代码的SIP协议栈。
SIP类似于HTTP,是一种基于文本的协议,在SIP中一共定义了六种报文,分别是INVITE报文用于发起呼叫、ACK报文用于呼叫确认、BYE报文用于终止会一话、OPTIONS报文用于查询代理服务器设置、CANCEL报文用于取消呼叫和REGISTER报文用于向服务器注册,如图7所示。
图表 5 SIP报文
基于SIP协议进行通信的双方需要使用SIP格式的地址来作为身份的标识,一个SIP地址必须采用“sip:"+用户名+“@”+地址的格式,其中地址可以是IP地址、电子邮件地址、电话号码或其他一些类型的地址,如sip:yjs_ideal@192.168.0.52、sip:yjs_ideal@dlut.edu.cn、sip:yjs_ideal@408-864-8900等都是合法的SIP地址。
在SIP协议中,用户通过代理服务器来执行注册和会话发起等操作,用户的信息储存在注册服务器中。一次完整的基于SIP的通信过程如图8所示。在会话建立阶段,SIP协议采用类似TCP三次握手的方式来建立会话。会话发起者向代理服务器发送一个INVITE报文,代理服务器收到请求后根据INVITE报文中的SIP地址向注册服务器查询,注册服务器查找自己的用户表并向代理服务器发送一个应答,其中包含了被叫方的实际地址,代理服务器接收到应答之后将主叫方的INVITE报文转发给被叫方;被叫方接收到请求报文后,如果接受本次会话,则经过代理服务器向主叫方发送一个应答报文;主叫方接收到应答报文之后,马上向被叫方发送一个ACK报文进行确认,会话建立成功。会话建立后,会话双方就可以通过IP网络进行数据和多媒体的通信。会话双方的任何一方都可以作为本次会话的终止者,任何一方如果想结束本次会话,都可以向对方发送一个BYE报文,会话终止。
图表 6 SIP通信过程
3. 得到的结论、成果及新见解
在本题目中,到目前为止所取得的结论、成果如下:
了解并掌握H.264协议编解码原理和方法。
了解并初步掌握SIP协议的基本原理和实现方法。并在Android平台上实现了基于SIP协议的音频通话功能。能够在Android平台上实现利用SIP协议,以三次握手方式发起一次回话,并对回话进行管理控制。
了解并初步掌握FFmpeg开源视频解决方案的内部结构和工作流程。目前实现了对FFmpeg开源数据包在Windows平台下的编译工作。现在已经可以利用FFmpeg开源视频解决方案实现对视频的编解码工作,实现终端之间的视频通话功能。目前正在进行对FFmpeg从Windows平台到Android平台的移植工作。
4. 存在的问题和拟采取的解决办法
在本题目中,目前所存在的问题主要如下:
在FFmpeg的移植过程中,还存在着一定的问题。无法编译生成有效可用的动态链接库。针对这一问题,计划在下一步主要研究有关Android平台移植方面的知识,如交叉编译、.mk文件的编写等。
在目前的终端之间的视频通话过程中,还存在着音视频不同步的情况。有时会出现音频有延迟的情况。针对这一问题,在下一步的研究中重点研究和学习音视频同步的解决方法,如时间戳技术等。
5. 下一步的工作计划
1) 完成FFmpeg开源视频解决方案在Android平台上的移植,解决音视频不同步的情况,进一步完成完善整个视频通话系统。
2) 在导师的指导下,完成论文的撰写工作。