1.  预备

视频

由一副副连续的图像构成,由于数据量比较大,因此为了节省带宽以及存储,就需要进行必要的压缩与解压缩,也就是编解码。

h264裸码流

对一个图像或者一个视频序列进行压缩,即产生码流,采用H264编码后形成的码流就是h264裸码流。

码流传输

发送端将H264裸码流打包后进行网络传输,接收端接收后进行组包还原裸码流,然后可以再进行存储,转发,或者播放等等相关的处理。

存储转发可以直接使用裸码流,播放则需要进行解码和显示处理

解码显示:       

一般会解成YUV数据,然后交给显示相关模块做处理,如使用openGL或者D3D等进行渲染(采用3d的方式来显示2d的图像称为渲染)

解码又分软件解码和硬件解码,

软解一般ffmpeg

硬解则各不同,由各硬件厂商开放sdk来处理,如:hisi,intel media sdk,nvida gpu,apple ios videotoolbox等。

2.  h264码流传输

类似发送网页文本数据有http一样,视频数据在网络上传输也有专门的网络协议来支持,如:rtsp,rtmp等

由于码流是一帧一帧的图片数据,所以传输的时候也是一帧帧来传输的,因此这里就会涉及到各种类型的帧处理了

I 帧: 参考帧或者关键帧,可以理解为是一帧完整画面,解码时只需要本帧数据就解码成一幅完整的图片,数据量比较大。

P帧: 差别帧,只有与前面的帧或I帧的差别数据,需要依赖前面已编码图象作为参考图象,才能解码成一幅完整的图片,数据量小。

B帧: 双向差别帧,也就是B帧记录的是本帧与前后帧的差别,需要依赖前后帧和I帧才能解码出一幅完整的图片,数据量小。

由于i帧比较大,已经超出mtu最大1500,所以需要拆包分片传输,这里说的拆包发送不是指发送超过1500的数据包时tcp的分段传输或者upd的ip分片传输,而是指rtp协议本身对264的拆包。

rtp打包后将数据交给tcp或者upd进行传输的时候就已经控制在1500以内,这样可以提高传输效率,避免一个I帧万一丢失就会造成花屏或者重传造成延时卡顿等等问题。

(顺便提一句,rtmp打包就比较简单,由于是基于tcp的协议,大包直接交给tcp去做分段传输,rtmp通过设置合适的trunk size去发送一帧帧数据)

既然要进行拆包发送与接收,就少不了需要相关的包结构以及打包组包了,继续。。。。。。

3.   H264在网络传输的单元:NALU

NALU结构:NALU header(1byte) + NALU payload

header 部分可以判断类型等信息,从右往左5个bit位

SPS:  0x67    header & 0x1F = 7  I Frame: 0x65  header & 0x1F = 5 
PPS:  0x68    header & 0x1F = 8   P Frame: 0x41   header & 0x1F = 1
SEI:  0x66    header & 0x1F = 6  

payload 部分可以简单理解为 编码后的264帧数据

详细可以去查阅 h264 NALU 语法结构

4.   h264的RTP打包

1.  单NALU:   P帧或者B帧比较小的包,直接将NALU打包成RTP包进行传输    RTP header(12bytes) + NALU header (1byte) + NALU payload

2.  多NALU:   特别小的包几个NALU放在一个RTP包中

3.  FUs(Fragment Units):   I帧长度超过MTU的,就必须要拆包组成RTP包了,有FU-A,FU-B

RTP header (12bytes)+ FU Indicator (1byte)  +  FU header(1 byte) + NALU payload

看到这里会不禁思考,

1. NALU头不见了,如何判断类型?实际上NALU头被分散填充到FU indicator和FU header里面了

bit位按照从左到右编号0-7来算,nalu头中0-2前三个bit放在FU indicator的0-2前三个bit中,后3-7五个bit放入FU header的后3-7五个中

//NALU header = (FU indicator & 0xe0) | (FU header & 0x1F)   取FU indicator前三和FU header后五  
headerStart[] = (headerStart[]&0xE0)|(headerStart[]&0x1F);  

因此查看I帧p帧类型,遇到FU分片的,直接看第二个字节,即Fu header后五位,这个跟直接看NALU头并无差异,一般有:0x85,0x05,0x45等等

2.  多个RTP包如何还原组合成回一个完整的I帧? 在FU header中有标记为判断

                         照旧从左到右,Fu header前两个bit表示start和end标记,start为1表示一个i帧分片开始,end为1表示一个i帧分片结束

3.   如何查看是一个I帧分片开始?

看第一个字节FU Indicator,照旧从左到右,Fu Indicator前三个bit是NALU头的前三个bit,后五位为类型FU-A:28(11100),FU-B:29(11101)

RTP抓包看下来整个字节是0x7c开头

如果不是分片,第一字节就是NALU头,如:0x67,0x68,0x41等

5. 抓包分析如下图:

--->RTP包中接收的264包是不含有0x00,0x00,0x00,0x01头的,这部分是rtp接收以后,另外再加上去的,解码的时候再做判断的

1. SPS

2.  I帧分片开始,第一个字节FU Indicator,0x7c, 后五位11100,28,FU-A

第二个字节FU Header,0x85,前两个bit start位1,end位0 表示 分片开始,后五个bit值5,I帧

I帧分片,0x7c开头,第二个字节0x05, FU Header start和end位 0,后五个bit值5,I帧

I帧分片结束,7c开头,第二个字节0x45,FU Headr,start 0,end1,后五个bit值5,I帧, Mark标记一帧结束

3.  p帧,第一个字节:0x41

4. P帧分片开始:第一个字节FU Indicator,0x7c, 后五位11100,28,FU-A

第二个字节FU Header,0x81, 前两个bit start位1,end位0 表示 分片开始,后五个bit 值是1,p帧

P帧分片结束:0x5c开头,第二个字节0x41,FU Headr,start 0,end1,后五个bit值1,P帧, Mark标记一帧结束

6.  参考live555相关的源码如下:

Boolean H264VideoRTPSource
::processSpecialHeader(BufferedPacket* packet,
unsigned& resultSpecialHeaderSize) {
unsigned char* headerStart = packet->data();
unsigned packetSize = packet->dataSize();
unsigned numBytesToSkip; // Check the 'nal_unit_type' for special 'aggregation' or 'fragmentation' packets:
if (packetSize < ) return False;
fCurPacketNALUnitType = (headerStart[]&0x1F); //FU Indicator后五位即NALU类型 0x1F = 0001 1111
switch (fCurPacketNALUnitType) {
case : { // STAP-A
numBytesToSkip = ; // discard the type byte
break;
}
case : case : case : { // STAP-B, MTAP16, or MTAP24
numBytesToSkip = ; // discard the type byte, and the initial DON
break;
}
case : case : { // // FU-A or FU-B
// For these NALUs, the first two bytes are the FU indicator and the FU header.
// If the start bit is set, we reconstruct the original NAL header into byte 1:
if (packetSize < ) return False;
unsigned char startBit = headerStart[]&0x80; //FU Header start标记位 0x80= 1000 0000
unsigned char endBit = headerStart[]&0x40; //FU Header End标记位 0x40= 0100 0000
if (startBit) {
fCurrentPacketBeginsFrame = True; headerStart[] = (headerStart[]&0xE0)|(headerStart[]&0x1F); //还原NALU头
numBytesToSkip = ;
} else {
// The start bit is not set, so we skip both the FU indicator and header:
fCurrentPacketBeginsFrame = False;
numBytesToSkip = ;
}
fCurrentPacketCompletesFrame = (endBit != );
break;
}
default: {
// This packet contains one complete NAL unit:
fCurrentPacketBeginsFrame = fCurrentPacketCompletesFrame =
True; //默认没有分片,完整的NALU
numBytesToSkip = ;
break;
}
} resultSpecialHeaderSize = numBytesToSkip;
return True;
}

H264 RTP包解析的更多相关文章

  1. RTP 包格式 详细解析

    H.264 视频 RTP 负载格式 1. 网络抽象层单元类型 (NALU) NALU 头由一个字节组成, 它的语法如下: +---------------+      |0|1|2|3|4|5|6|7 ...

  2. 使用Lua编写Wireshark插件解析KCP UDP包,解析视频RTP包

    前段时间写了一个局域网音视频通话的程序,使用开源 KCP 来实现可靠UDP传输. 通过研究发现KCP在发包时,会在数据包前面加上它自己的头.如果数据包较小,KCP可能会把多个数据包合成一个包发送,提高 ...

  3. h264 RTP STAP-A单时间聚合包

    参考官方文档:http://www.rosoo.net/Files/UpFiles/RsProduct/avtools/2009-4/2009491562537854.txt 聚合包的RTP荷载格式的 ...

  4. 用实例分析H264 RTP payload

    用实例分析H264 RTP payload H264的RTP中有三种不同的基本负载(Single NAL,Non-interleaved,Interleaved) 应用程序可以使用第一个字节来识别. ...

  5. FU-A分包方式,以及从RTP包里面得到H.264数据和AAC数据的方法。。

    [原创] RFC3984是H.264的baseline码流在RTP方式下传输的规范,这里只讨论FU-A分包方式,以及从RTP包里面得到H.264数据和AAC数据的方法. 1.单个NAL包单元 12字节 ...

  6. Live555中RTP包的打包与发送过程分析

    这里主要分析一下,live555中关于RTP打包发送的部分.在处理完PLAY命令之后,就开始发送RTP数据包了(其实在发送PLAY命令的response包之前,就会发送一个RTP包,这里传输就已经开始 ...

  7. NALU数据打RTP包流程详解

    最近在看RTP发送H264数据的文章,感觉很乱,没有比较清晰易懂的教程,自己整理了一下各种资料,备忘! --------Part A  ---- 先说说H264数据,H264在网络传输的是NALU(N ...

  8. rtp包格式

    转载一篇帮助我了解h264 rtp的文档,地址http://www.cppblog.com/czanyou/archive/2009/12/25/67940.html 当packetization-m ...

  9. (转)live555学习笔记10-h264 RTP传输详解(2)

    参考: 1,live555学习笔记10-h264 RTP传输详解(2) http://blog.csdn.net/niu_gao/article/details/6936108 2,H264 sps ...

随机推荐

  1. PHP项目实现手机端和PC端的页面切换

    目前访问页面的要切换成手机端和PC端,原理是通过对设备作出判断,显示不同的功能和页面. 如果手机端和PC端的功能结构不相同,一般会写两套系统,一套适用于PC端,一套适用于手机端. 如果功能相同,则只需 ...

  2. ASP.NET MVC之如何看待内置配置来提高性能优化(四)

    前言 前几篇我们比较基础的讲了下MVC中的知识,这一节我们穿插点知识,讲讲MVC中我们可以提高性能的办法. Razor视图引擎优化(优化一) 我们知道默认情况下配置MVC去解析一个视图会首先约定通过查 ...

  3. PowerDesigner设计表时显示注释选项

    选定编辑的表,[右键]->[Properties]->[Columns]->[Customize Columns and Filter]->[Comment]->[OK] ...

  4. 简单说一下printf(&quot;%*s%s&quot;,xx,xx,xx);或printf(&quot;%*s\n&quot;,xx,xx);

    大家还记得这个例子吗 #include "public.h" int main() { ; printf("%4d\n",a); ; } 这个输出结果为:    ...

  5. OOP多态和继承要点

         早期绑定和多态 C#函数重载的签名规则是用参数的类型和数量判断,而不是函数的名字. 函数返回值不作为重载签名. 修饰符不作为签名的一部分,如static 同函数中,多个参数名称要唯一 ref ...

  6. poj3041 二分图最小顶点覆盖

    Asteroids Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 17237   td>Accepted: 9375 ...

  7. 7款值得你心动的HTML5动画和游戏

    1.HTML5 Canvas粒子效果文字动画特效 之前我们分享过很多超酷的文字特效,其中也有利用HTML5和CSS3的.今天我们要来分享一款基于HTML5 Canvas的文字特效,输入框中输入想要展示 ...

  8. 安装app到Simulator

    1.安装brew 打开命令行,执行以下命令: ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install ...

  9. google map 定位

    在map初始化的过程中,得到当前经纬度,完成初始化地图,通过HTML5中的Geolocation实现,具体参考:http://www.jb51.net/html5/71556.html 1.获取当前地 ...

  10. IOT相关协议

    MQTT协议的入门 入门教程; 发布/订阅(Pub/Sub)模式,方便消息在传感器之间传递; 这意味着发布者和订阅者之间并不需要直接建立联系; 消息类型 MQTT拥有14种不同的消息类型: CONNE ...