当前位置:文档之家› 基于RTP协议的打包及解包

基于RTP协议的打包及解包

H.264视频在android手机端的解码与播放

文/南京邮电大学张永芹龚建荣摘要:本文实现了手机终端通过移动无线网络与媒体服务器进行通信,并就开发过程中的几个技术难点的解决方法进行了说明。首先详细分析说明了rtp 打包,解包的流程,这是视频传输的基础;然后在RTP传输过程中,针对发送数据快而处理速度慢的问题,采用多线程并发机制予以解决;面对大量,而且不稳定的数据包,本文针对各个环节自己的特点,设计了多级缓冲处理机制,使得视频播放更加流畅、平稳。接着,对于分析和解码的先后次序的问题,则采用线程协作的思想,利用消费者,生产者模式,保证了视频数据的时序性。最后对于视频解码部分,则利用现有解码方法进行平台移植,深度简化代码,合理处理c

层和java层的分工。最后实践证明,采用本文提供的方法,视频传输、播放成功,而且android手机端视频播放延时短,流畅,平稳。

关键词:H.264 RTP 多级缓冲线程协作 android

随着无线网络和智能手机的发展,智能手机与人们日常生活联系越来越紧密,娱乐、商务应用、金融应用、交通出行各种功能的软件大批涌现,使得人们的生活丰富多彩、快捷便利,也让它成为人们生活中不可取代的一部分。其中,多媒体由于其直观性和实时性,应用范围越来越广,视频的解码与播放也就成为研究的热点。

H.264标准技术日渐成熟,采用了统一的VLC符号编码,高精度、多模式的位移估计,基于4×4块的整数变换、分层的编码语法等。这些措施使得H.264算法具有很高的编码效率,在相同的重建图像质量下,能够比H.263节约50%左右的码率。而且H.264的码流结构网络适应性强,增加了差错恢复能力。正好适用于带宽受限,差错率高的无线网络。

本文结合ffmpeg开源代码中的解码方法,采用多线程接收数据包,多级缓冲数据,接收和解码并行双线程操作等方法,缓解了由于传输的数据量大、速度快而导致的数据堵塞、解码出错、视频画面迟钝、延迟等问题。使得h.264视频的传输速度快,稳定性好。最终实现了pc端到android手机端的视频传输,以及在android手机端的解码播放。

该技术可以应用于视频会议、视频监控等应用中。

一、 H.264视频传输播放系统的总体结构

H.264视频传输播放系统分为服务器端和客户端2个部分,服务器端负责读取H.264的视频数据,并且以RTP/RTCP格式打包发送给客户端,并且接受客户端的反馈,对传输速度等作相应的控制。Android手机客户端主要完成从服务器端接收实时码流数据,经过缓冲,进行视频数据解析,然后送去解码,最后在手机上显示播放。服务器端采用c语言实现,客户端主要用java语言实现。

二、关键技术及其实现

1.基于RTP协议的打包及解包

(1)单个NAL打包

H.264NALU单元常由[start code][NALU header][NALU payload]三部分组成,其中start code 用于标志一个NALU单元的开始,必须是“00000001”或者是“000001”,打包时去掉开始码,把其他数据打包到RTP包就可以了。(2)分片打包

由于1500个字节是IP数据报的长度的上限,去除20个字节的数据报首部,1480字节是用来存放UDP数据报的。所以当一帧中的字节数超过这个数值时,

我们必须将其分片打包。而且UDP在传输的过程中也要由包头开销,所以将RTP 包的最大字节数定位1400字节。

需要分片的包格式有所区别,首先说明下分片的格式:

FU指示字节有以下格式:

+---------------+

|0|1|2|3|4|5|6|7|

+-+-+-+-+-+-+-+-+

|F|NRI| Type |

+---------------+

FU指示字节的类型28,29表示FU-A和FU-B。NRI域的值必须根据要分片的NAL单元NRI的值设置。

FU头的格式如下:

+---------------+

|0|1|2|3|4|5|6|7|

+-+-+-+-+-+-+-+-+

|S|E|R| Type |

+---------------+

S:开始位(1bit),当设置为1,开始位指示分片NAL单元的开始。第一个分片包设为1,其他的分片设置为0。

E:结束位(1bit),当设置为1,结束位指示分片NAL单元的结束,即,FU 荷载是最后分片时设置为1,其他时候设置为0。

R:保留位(1bit),必须设置为0。

Type:5bit

(3)打包和解包的流程分析:

打包:

分片时详细说明:

①第一个FU-A包的FU indicator 是这么设置的:F=NALU头中的F,NRI=NALU 头中的NRI,Type=28 FU header: S=1,E=0,R=0,Type=NALU头中的Type;

②中间的FU-A包的FU indicator是这么设置的:F=NALU头中的F,NRI=NALU 头中的NRI,Type=28 FU header: S=0,E=0,R=0,Type=NALU头中的Type;

③尾FU-A包的FU indicator是这么设置的:F=NALU头中的F,NRI=NALU头中的NRI,Type=28 FU header: S=0,E=1,R=0,Type=NALU头中的Type。

解包:

下面我们针对RTP解包时对待分片进行分类的代码实现做分析:

byte startBit=(byte)(recbuf[13]&0x80); byte

endBit=(byte)(recbuf[13]&0x40);

①如果,startBit==-128,这包是分片的首包。

NalBuf[4]=(byte) ((recbuf[12]&0xE0)+(recbuf[13]&0x1F)); 这句用于重建组合NAL单元类型

②如果(startBit==0)&&(endBit==0),这包是分片的中间部分。

③如果 endBit==64 ,这包是分片尾部。

当分类清楚,就可以对各部分做相应的处理,如图中分析的那样。

2.码流管理机制

(1) 码流的接收。在发送端码流发送很快的情况下,由于接收端不仅要接收码流,还要进行分析,解码,这个处理需要一个较长的过程,如果接收端顺序执行这个过程的话,会导致无法完整接收发送端的包、出现丢包,由此而带来的是解码错误、无法正常播放视频、甚至程序奔溃等严重错误。针对这个问题我们采取并发的处理机制予以解决。线程并发存在的一个意义就是为了提高运行在单处理器上的速度。在java中我们采用java.util.concurrent包中的执行器(Executor)来管理线程Thread对象。我们创建20个线程,也就是向SingleThreadExecutor 提交了20个任务,这些任务将排好队,每个任务会在下一个任务开始之前运行结束,每个任务都是按照他们被提交的顺序,在下一个任务开始之前完成。这样不仅实现了快速的接收而且还保证了接收到的包顺序是正确的。通过这样的处理后,接收和分析解码可以被分成两个部分,我们可以把接收到的数据暂时存放在缓冲区,然后就可以接着去接收下一包数据,不用等着分析、解码完成后才去接收下一包数据。这样做大大提高了接收效率,同时避免了丢包问题。

(2) 视频数据解析和解码。由于采用了并发的机制,接收到的数据不止一包,所以对接收到的数据应该做怎样合理的处理,成为我们接下来的难点。我们需要保证的仍然是数据包的顺序,还且每次只能处理一包,这里涉及到一个线程之间的协作问题。我们采用消费者生产者这种线程协作模式来做处理。我们将从存放数据的缓冲区中按顺序取到的包经过分析后放入另外一个缓冲区,通知解码程序可以进行从此缓冲区中获得数据解码,然后分析视频数据的程序进入等待。解码完成后,通知分析视频数据的程序继续进行视频数据分析,同时解码程序又进入等

相关主题