精华内容
下载资源
问答
  • RTP协议全解析(H264码流和PS

    万次阅读 多人点赞 2014-09-12 17:35:05
    1RTP Header解析 2、RTP荷载H264码流 2.1、单个NAL单元包 2.2、分片单元(FU-A) 3、RTP荷载PS 3.1、PS包头 3.2、系统标题 3.3、节目映射 3.4、PES分组头部

    这么多年过去,偶然发现这篇文章的阅读量已经接近20万,略感自豪。

    本想把代码公开了,但是在Gayhub上面发现了更加优秀的项目,分享给大家。

    GitHub - ireader/media-server: RTSP/RTP/RTMP/FLV/HLS/MPEG-TS/MPEG-PS/MPEG-DASH/MP4/fMP4/MKV/WebM

    感谢作者老陈,开源了这么好的东西,避免大家重复造轮子了。

    ---------------------------------------------------------------------以上为2020年更新-----------------------------------------------------------------------

    写在前面:RTP的解析,网上找了很多资料,但是都不全,所以我力图整理出一个比较全面的解析,

    其中借鉴了很多文章,我都列在了文章最后,在此表示感谢。

    互联网的发展离不开大家的无私奉献,我决定从我做起,希望大家支持。

    原创不易,转载请附上链接,谢谢RTP协议全解析(H264码流和PS流)_对牛乱弹琴-CSDN博客_ps流

    1、RTP Header解析

                                                                       

                                                                                        图1

    1)        V:RTP协议的版本号,占2位,当前协议版本号为2

    2)        P:填充标志,占1位,如果P=1,则在该报文的尾部填充一个或多个额外的八位组,它们不是有效载荷的一部分。

    3)        X:扩展标志,占1位,如果X=1,则在RTP报头后跟有一个扩展报头

    4)        CC:CSRC计数器,占4位,指示CSRC 标识符的个数

    5)        M: 标记,占1位,不同的有效载荷有不同的含义,对于视频,标记一帧的结束;对于音频,标记会话的开始。

    6)        PT: 有效荷载类型,占7位,用于说明RTP报文中有效载荷的类型,如GSM音频、JPEM图像等,在流媒体中大部分是用来区分音频流和视频流的,这样便于客户端进行解析。

    7)        序列号:占16位,用于标识发送者所发送的RTP报文的序列号,每发送一个报文,序列号增1。这个字段当下层的承载协议用UDP的时候,网络状况不好的时候可以用来检查丢包。同时出现网络抖动的情况可以用来对数据进行重新排序,序列号的初始值是随机的,同时音频包和视频包的sequence是分别记数的。

    8)        时戳(Timestamp):占32位,必须使用90 kHz 时钟频率。时戳反映了该RTP报文的第一个八位组的采样时刻。接收者使用时戳来计算延迟和延迟抖动,并进行同步控制。

    9)        同步信源(SSRC)标识符:占32位,用于标识同步信源。该标识符是随机选择的,参加同一视频会议的两个同步信源不能有相同的SSRC。

    10)    特约信源(CSRC)标识符:每个CSRC标识符占32位,可以有0~15个。每个CSRC标识了包含在该RTP报文有效载荷中的所有特约信源。

    注:基本的RTP说明并不定义任何头扩展本身,如果遇到X=1,需要特殊处理

    取一段码流如下:

    80 e0 00 1e 00 00 d2 f0 00 00 00 00 41 9b 6b 49 €?....??....A?kI

    e1 0f 26 53 02 1a ff06 59 97 1d d2 2e 8c 50 01 ?.&S....Y?.?.?P.

    cc 13 ec 52 77 4e e50e 7b fd 16 11 66 27 7c b4 ?.?RwN?.{?..f'|?

    f6 e1 29 d5 d6 a4 ef3e 12 d8 fd 6c 97 51 e7 e9 ??)????>.??l?Q??

    cfc7 5e c8 a9 51 f6 82 65 d6 48 5a 86 b0 e0 8c ??^??Q??e?HZ????

    其中,
    80               是V_P_X_CC
    e0               是M_PT
    00 1e          是SequenceNum
    把前两字节换成二进制如下
    1000 0000 1110 0000
    按顺序解释如下:
    10               是V;
    0                 是P;
    0                 是X;
    0000           是CC;
    1                 是M;
    110 0000    是PT;
    排版不如word看的清晰,大家凑合着看吧。

    2、RTP荷载H264码流

                                                                                图2

    荷载格式定义三个不同的基本荷载结构,接收者可以通过RTP荷载的第一个字节后5位(如图2)识别荷载结构。

    1)   单个NAL单元包:荷载中只包含一个NAL单元。NAL头类型域等于原始 NAL单元类型,即在范围1到23之间

    2)   聚合包:本类型用于聚合多个NAL单元到单个RTP荷载中。本包有四种版本,单时间聚合包类型A (STAP-A),单时间聚合包类型B (STAP-B),多时间聚合包类型(MTAP)16位位移(MTAP16), 多时间聚合包类型(MTAP)24位位移(MTAP24)。赋予STAP-A, STAP-B, MTAP16, MTAP24的NAL单元类型号分别是 24,25, 26, 27

    3)   分片单元:用于分片单个NAL单元到多个RTP包。现存两个版本FU-A,FU-B,用NAL单元类型 28,29标识

    常用的打包时的分包规则是:如果小于MTU采用单个NAL单元包,如果大于MTU就采用FUs分片方式。
    因为常用的打包方式就是单个NAL包和FU-A方式,所以我们只解析这两种。

    2.1、单个NAL单元包

                                                         图3

            定义在此的NAL单元包必须只包含一个。这意味聚合包和分片单元不可以用在单个NAL 单元包中。并且RTP序号必须符合NAL单元的解码顺序。NAL单元的第一字节和RTP荷载头第一个字节重合。如图3。

            打包H264码流时,只需在帧前面加上12字节的RTP头即可。

    2.2、分片单元(FU-A)

                                           图4

    分片只定义于单个NAL单元不用于任何聚合包。NAL单元的一个分片由整数个连续NAL单元字节组成。每个NAL单元字节必须正好是该NAL单元一个分片的一部分。相同NAL单元的分片必须使用递增的RTP序号连续顺序发送(第一和最后分片之间没有其他的RTP包)。相似,NAL单元必须按照RTP顺序号的顺序装配。

       当一个NAL单元被分片运送在分片单元(FUs)中时,被引用为分片NAL单元。STAPs,MTAPs不可以被分片。 FUs不可以嵌套。 即, 一个FU 不可以包含另一个FU。运送FU的RTP时戳被设置成分片NAL单元的NALU时刻。

       图 4 表示FU-A的RTP荷载格式。FU-A由1字节的分片单元指示(如图5),1字节的分片单元头(如图6),和分片单元荷载组成。

    S: 1 bit 当设置成1,开始位指示分片NAL单元的开始。当跟随的FU荷载不是分片NAL单元荷载的开始,开始位设为0。

    E: 1 bit 当设置成1, 结束位指示分片NAL单元的结束,即, 荷载的最后字节也是分片NAL单元的最后一个字节。当跟随的 FU荷载不是分片NAL单元的最后分片,结束位设置为0。

    R: 1 bit 保留位必须设置为0,接收者必须忽略该位

    打包时,原始的NAL头的前三位为FU indicator的前三位,原始的NAL头的后五位为FU header的后五位。
    取一段码流分析如下:

    80 60 01 0f 00 0e 10 00 00 0000 00 7c 85 88 82 €`..........|???

    00 0a 7f ca 94 05 3b7f 3e 7f fe 14 2b 27 26 f8 ...??.;.>.?.+'&?

    89 88 dd 85 62 e1 6dfc 33 01 38 1a 10 35 f2 14 ????b?m?3.8..5?.

    84 6e 21 24 8f 72 62f0 51 7e 10 5f 0d 42 71 12 ?n!$?rb?Q~._.Bq.

    17 65 62 a1 f1 44 dc df 4b 4a 38 aa 96 b7 dd 24 .eb??D??KJ8????$
    前12字节是RTP Header
    7c是FU indicator
    85是FU Header
    FU indicator(0x7C)和FU Header(0x85)换成二进制如下
    0111 1100 1000 0101
    按顺序解析如下:
    0                            是F
    11                          是NRI
    11100                    是FU Type,这里是28,即FU-A
    1                            是S,Start,说明是分片的第一包
    0                            是E,End,如果是分片的最后一包,设置为1,这里不是
    0                            是R,Remain,保留位,总是0
    00101                    是NAl Type,这里是5,说明是关键帧(不知道为什么是关键帧请自行谷歌)

    打包时,FUindicator的F、NRI是NAL Header中的F、NRI,Type是28;FU Header的S、E、R分别按照分片起始位置设置,Type是NAL Header中的Type。

    解包时,取FU indicator的前三位和FU Header的后五位,即0110 0101(0x65)为NAL类型。

    3、RTP荷载PS流

            针对H264 做如下PS 封装:每个IDR NALU 前一般都会包含SPS、PPS 等NALU,因此将SPS、PPS、IDR 的NALU 封装为一个PS 包,包括ps 头,然后加上PS system header,PS system map,PES header+h264 raw data。所以一个IDR NALU PS 包由外到内顺序是:PSheader| PS system header | PS system Map | PES header | h264 raw data。对于其它非关键帧的PS 包,就简单多了,直接加上PS头和PES 头就可以了。顺序为:PS header | PES header | h264raw data。以上是对只有视频video 的情况,如果要把音频Audio也打包进PS 封装,也可以。当有音频数据时,将数据加上PES header 放到视频PES 后就可以了。顺序如下:PS 包=PS头|PES(video)|PES(audio),再用RTP 封装发送就可以了。

            GB28181 对RTP 传输的数据负载类型有规定(参考GB28181 附录B),负载类型中96-127

            RFC2250 建议96 表示PS 封装,建议97 为MPEG-4,建议98 为H264

            即我们接收到的RTP 包首先需要判断负载类型,若负载类型为96,则采用PS 解复用,将音视频分开解码。若负载类型为98,直接按照H264 的解码类型解码。

            注:此方法不一定准确,取决于打包格式是否标准

    PS 包中的流类型(stream type)的取值如下:

    1)        MPEG-4 视频流: 0x10;

    2)        H.264 视频流: 0x1B;

    3)        SVAC 视频流: 0x80;

    4)        G.711 音频流: 0x90;

    5)        G.722.1 音频流: 0x92;

    6)        G.723.1 音频流: 0x93;

    7)        G.729 音频流: 0x99;

    8)       SVAC音频流: 0x9B。

    3.1、PS包头

                                                     图7


    1)        Pack start code:包起始码字段,值为0x000001BA的位串,用来标志一个包的开始。

    2)        System clock reference base,system clock reference extenstion:系统时钟参考字段。

    3)        Pack stuffing length :包填充长度字段,3 位整数,规定该字段后填充字节的个数

    80 60 53 1f 00 94 89 00 00 0000 00 00 00 01 ba €`S..??........?

    7e ff 3e fb 44 01 00 5f 6b f8 00 00 01 e0 14 53 ~.>?D.._k?...?.S

    80 80 05 2f bf cf bed1 1c 42 56 7b 13 58 0a 1e €€./????.BV{.X..

    08 b1 4f 33 69 35 0453 6d 33 a8 04 15 58 d9 21 .?O3i5.Sm3?..X?!

    9741 b9 f1 75 3d 94 2b 1f bc 0b b2 b4 97 bf 93 ?A??u=?+.?.?????

    前12位是RTP Header,这里不再赘述;

    000001ba是包头起始码;

    接下来的9位包括了SCR,SCRE,MUXRate,具体看图7

    最后一位是保留位(0xf8),定义了是否有扩展,二进制如下

    1111 1000

    前5位跳过,后3位指示了扩展长度,这里是0.

    3.2、系统标题


                                                               图8
    Systemheader当且仅当pack是第一个数据包时才存在,即PS包头之后就是系统标题。取值0x000001BB的位串,指出系统标题的开始,暂时不需要处理,读取Header Length直接跳过即可。

    3.3、节目映射流

    Systemheader当且仅当pack是第一个数据包时才存在,即系统标题之后就是节目流映射。取值0x000001BC的位串,指出节目流映射的开始,暂时不需要处理,读取Header Length直接跳过即可。前5字节的结构同系统标题,见图8。

    取一段码流分析系统标题和节目映射流

    00 00 01 ba 45 a9 d4 5c 34 0100 5f 6b f8 00 00  ...?E??\4.._k?..

    01 bb 00 0c 80 cc f5 04 e1 7f e0 e0 e8 c0 c0 20  .?..€??.?.?????

    00 00 01 bc 00 1e e1 ff00 00 00 18 1b e0 00 0c ...?..?......?..

    2a 0a 7f ff 00 00 0708 1f fe a0 5a 90 c0 00 00  *........??Z??..

    00 00 00 00 00 00 01 e0 7f e0 80 80 0521 6a 75  .......?.?€€.!ju

    前14个字节是PS包头(注意,没有扩展);

    接下来的00 00 01 bb是系统标题起始码;

    接下来的00 0c说明了系统标题的长度(不包括起始码和长度字节本身);

    接下来的12个字节是系统标题的具体内容,这里不做解析;

    继续看到00 00 01 bc,这是节目映射流起始码;

    紧接着的00 1e同样代表长度;

    跳过e1 ff,基本没用;

    接着2个字节代表program_stream_info长度,这里是00 00,即便有值,一般也可以直接跳过;

    接下来是00 18,代表基本流长度,说明了后面还有24个字节;

    接下来的1b,意思是H264编码格式;

    下一个字节e0,意思是视频流;

    接下里00 0c,同样代表接下的长度12个字节;

    跳过这12个字节,看到90,这是G.711音频格式;

    下一个字节是c0,代表音频流;

    接下来的00 00同样代表长度,这里是0;

    接下来4个字节是CRC,循环冗余校验。

    到这里节目映射流解析完毕。(好累)。

    原创不易,转载请附上链接,谢谢RTP协议全解析(H264码流和PS流)_对牛乱弹琴-CSDN博客_ps流

    好戏还在后头呢。

    3.4、PES分组头部

                                                             图9

    别被这么长的图吓到,其实原理相同,但是,你必须处理其中的每一位。

    1)        Packet start code prefix:值为0x000001的位串,它和后面的stream id 构成了标识分组开始的分组起始码,用来标志一个包的开始。

    2)        Stream id:在节目流中,它规定了基本流的号码和类型。0x(C0~DF)指音频,0x(E0~EF)为视频

    3)        PES packet length:16 位字段,指出了PES 分组中跟在该字段后的字节数目。值为0 表示PES 分组长度要么没有规定要么没有限制。这种情况只允许出现在有效负载包含来源于传输流分组中某个视频基本流的字节的PES 分组中。

    4)        PTS_DTS:2 位字段。当值为'10'时,PTS 字段应出现在PES 分组标题中;当值为'11'时,PTS 字段和DTS 字段都应出现在PES 分组标题中;当值为'00'时,PTS 字段和DTS 字段都不出现在PES分组标题中。值'01'是不允许的。

    5)        ESCR:1位。置'1'时表示ESCR 基础和扩展字段出现在PES 分组标题中;值为'0'表示没有ESCR 字段。

    6)        ESrate:1 位。置'1'时表示ES rate 字段出现在PES 分组标题中;值为'0'表示没有ES rate 字段。

    7)        DSMtrick mode:1 位。置'1'时表示有8 位特技方式字段;值为'0'表示没有该字段。

    8)        Additionalinfo:1 位。附加版权信息标志字段。置'1'时表示有附加拷贝信息字段;值为'0'表示没有该字段。

    9)        CRC:1 位。置'1'时表示CRC 字段出现在PES 分组标题中;值为'0'表示没有该字段。

    10)    Extensionflag:1 位标志。置'1'时表示PES 分组标题中有扩展字段;值为'0'表示没有该字段。

    PES header data length: 8 位。PES 标题数据长度字段。指出包含在PES 分组标题中的可选字段和任何填充字节所占用的总字节数。该字段之前的字节指出了有无可选字段。

    老规矩,上码流:

    00 00 01 e0 21 33 80 80 05 2b 5f df 5c 95 71 84 ...?!3€€.+_?\?q?

    aa e4 e9 e9 ec 40 cc17 e0 68 7b 23 f6 89 df 90 ?????@?.?h{#????

    a9d4 be 74 b9 67 ad 34 6d f0 92 0d 5a 48 dd 13 ???t?g?4m??.ZH?.

    00 00 01是起始码;

    e0是视频流;

    21 33 是帧长度;

    接下来的两个80 80见下面的二进制解析;

    下一个字节05指出了可选字段的长度,前一字节指出了有无可选字段;

    接下来的5字节是PTS;

    第7、8字节的二进制如下:

    1000 0000 1000 0000

    按顺序解析:

    第7个字节:

    10                         是标志位,必须是10;

    00                         是加扰控制字段,‘00’表示没有加密,剩下的01,10,11由用户自定义;

    0                           是优先级,1为高,0为低;

    0                           是数据对齐指示字段;

    0                           是版权字段;

    0                           是原始或拷贝字段。置'1'时表示相关PES分组有效负载的内容是原始的;'0'表示内容是一份拷贝;

    第8个字节:

    10                         是PTS_DTS字段,这里是10,表示有PTS,没有DTS;

    0                           是ESCR标志字段,这里为0,表示没有该段;

    0                           是ES速率标志字段,,这里为0,表示没有该段;

    0                           是DSM特技方式标志字段,,这里为0,表示没有该段;

    0                           是附加版权信息标志字段,,这里为0,表示没有该段;

    0                           是PESCRC标志字段,,这里为0,表示没有该段;

    0                           是PES扩展标志字段,,这里为0,表示没有该段;

    本段码流只有PTS,贴一下解析函数

    unsigned long parse_time_stamp (const unsigned char *p)
    {
        unsigned long b;
        //共33位,溢出后从0开始
        unsigned long val;
    
        //第1个字节的第5、6、7位
        b = *p++;
        val = (b & 0x0e) << 29;
    
        //第2个字节的8位和第3个字节的前7位
        b = (*(p++)) << 8;
        b += *(p++);
        val += ((b & 0xfffe) << 14);
    
        //第4个字节的8位和第5个字节的前7位
        b = (*(p++)) << 8;
        b += *(p++);
        val += ((b & 0xfffe) >> 1);
    
        return val;
    }

    其他字段可参考协议解析

    ps:

    遇到00 00 01 bd的,这个是海康私有流的标识,可以丢弃。丢弃之后就看不到原视频里移动侦测时闪烁的红框。

    ps:

    另外,有的hk摄像头回调然后解读出来的原始h.264码流,有的一包里只有分界符数据(nal_unit_type=9)或补充增强信息单元(nal_unit_type=6),如果直接送入解码器,有可能会出现问题,这里的处理方式要么丢弃这两个部分,要么和之后的数据合起来,再送入解码器里,如有遇到的朋友可以交流一下:)

    写在后面:

    第一次发原创,在这里感谢  @cmengwei  的无私帮助,提供了很多帮助,非常感谢。

    文档我都放在了我的资源里面,有1个下载积分,大家不要吝啬,绝对值得!

    《RTP Payload Format for H.264 Video》

    rfc3984(For264).txt_PS流-互联网文档类资源-CSDN下载

    《MPEG2-2(13818中文版)》

    MPEG2-2(13818中文版)_mpeg2-C++文档类资源-CSDN下载

    RTP荷载H264的代码参考:

    基于RTP的H264视频数据打包解包类_子鲲的专栏 -CSDN博客_h264 rtp 解包

    RTP荷载PS流的代码参考:

    http://www.pudn.com/downloads33/sourcecode/windows/multimedia/detail105823.html

    http://www.oschina.net/code/snippet_99626_23737

    请不要跟我要源码,参考我提供的这些,你足以写出一个可以正常运行的程序。

    授人以鱼不如授人以渔。

    其他参考:

    MPEG-2 PS流_duanbeibei的专栏-CSDN博客

    从海康7816的ps流里获取数据h264数据_wwyyxx26的专栏-CSDN博客

    原创不易,转载请附上链接,谢谢RTP协议全解析(H264码流和PS流)_对牛乱弹琴-CSDN博客_ps流

    彩蛋:这里竟然有源码😍😍😍

     👇👇👇👇👇

    👇👇👇👇👇

    🍞面包多作品

    展开全文
  • h264码流用于测试

    2018-03-24 21:45:32
    H264码流1080p单slice,H264码流1080p单slice,H264码流1080p单slice
  • Elecard H264.zip 分析H264码流的工具
  • h264 码流

    2018-01-16 15:00:57
    h264 码流包,一款手机软件中随意hook出的一部分数据,供分析使用,h264visa 直接打开,
  • 使用MP4v2库 封装,解码H264码流。 1.包含live555拉流操作。 2.mp4v2库 3.mp4muxer 把H264封装成MP4 4.MP4demuxer 从MP4文件读取h264码流 5.测试项目
  • H264码流分析工具

    千次阅读 2019-10-09 19:14:46
    使用该工具,可方便查看码流中NALU的结构,为我们学习和理解有很大帮助。 H264介绍 笔者直接参考:https://blog.csdn.net/chenchong_219/article/details/37990541 工具使用说明 打开工具 打开文件 查看帧...

    概述

    本文作为一个笔记,记录笔者学习H264码流的过程。重点记录使用工具分析H264码流。使用该工具,可方便查看码流中NALU的结构,为我们学习和理解有很大帮助。

    H264码流介绍

    笔者直接参考:https://blog.csdn.net/chenchong_219/article/details/37990541

    工具使用说明

    • 工具下载地址
      https://download.csdn.net/download/lyy901135/11839793
    • 打开工具
      在这里插入图片描述
      在这里插入图片描述
    • 打开文件
      在这里插入图片描述
    • 查看帧信息
      在这里插入图片描述
      上图可看到工具帮我们解析出H264码流中的帧情况,并注明了帧的起始地址。可根据该地址在二进制文件中查看。查看二进制推荐使用HexPad工具,可自行下载。

    查看H264码流PQ值

    在这里插入图片描述

    展开全文
  • H264码流

    2015-05-28 15:21:37
    帖子用资源.h264码流的数据包
  • H264码流分析软件

    2018-05-08 08:40:28
    非常好用的一款H264码流分析软件,不仅可以查看I帧间隔和每帧大小,还可以打开信息不全的H264视频帧
  • H264码流结构

    2013-08-10 00:22:02
    这份是H264码流结构的介绍,和H264的中文标准,其中码流结构介绍写的不错,比较直观
  • H264码流结构的分析

    2013-08-05 09:32:56
    H264码流结构的分析
  • H264码流解析.pdf

    2021-09-14 11:34:48
    H264码流解析.pdf
  • h264码流分析

    2015-07-03 09:20:34
    vs2013工程,直接输出到控制台,需要查看其它h264码流,只需要更改main函数中的filename。仿Elecard stream analyzer写的。有很多地方不足,但对于初学者可以借鉴学习。有的参数分析不全,有的没有分析。不过sps, ...
  • H264码流,ES

    2017-10-25 15:16:15
    一段h264的视频码流,包括sps pps i p b帧 分辨率为624 368 可以用streameyes打开
  • H264码流文件

    热门讨论 2011-09-20 20:54:38
    CMMB中截取的H264码流文件。封装模式是第二种。
  • live555实现h264码流RTSP传输
  • H264码流分析软件EStreamEyeTools 破解版。有破解安装步骤
  • Android MediaCodec 解码H264码流播放

    千次阅读 2018-08-05 22:43:40
    这篇博客讲解的是如何从摄像头从提取YUV画面色值,然后由MediaCodec进行编码压缩,最后生成的就是H264码流,我们先了解下H264码流格式。 可以看到一个个NALU单元组成了H264码流,NALU单元又包含头数据部分和帧...

    视频编解码,编的是什么码?解的又是什么码?有没有想过?现在主流的就是H264码流,Android 采集摄像头原始帧数据
    这篇博客讲解的是如何从摄像头从提取YUV画面色值,然后由MediaCodec进行编码压缩,最后生成的就是H264码流,我们先了解下H264码流格式。
    这里写图片描述
    可以看到一个个NALU单元组成了H264码流,NALU单元又包含头数据部分和帧数据部分。
    每一个头开始都包含0x 00000001或者0x000001.
    我选取了上面讲的采集摄像头画面进行编码后的H264文件,打开其字节文件,码流格式数据如下
    这里写图片描述
    可以看到手机编码后的码流每个NAL开头起始码为0x00000001
    所以我要做的工作就是提取出每一个NAL单元,然后送给MediaCodec进行解码。
    提取出NAL 单元的代码函数如下

         private byte[] getNALU() {
            try {
                int curpos = 0;
                //一般NAL不超过100000字节
                byte[] bb = new byte[100000];
                //先读取4个字节
                rf.read(bb, 0, 4);
                //判断是否是0x00000001开头
                if (findStartCode4(bb, 0)) {
                    curpos = 4;
                } else {
                    rf.seek(0);
                    rf.read(bb, 0, 3);
                    //判断是否是0x000001开头
                    if (findStartCode3(bb, 0)) {
                        curpos = 3;
                    }
                }
                //标志是否找到NAL单元开头
                boolean findNALStartCode = false;
                //下一个NAL单元的开始位置
                int nextNalStartPos = 0;
                //找到适合标记开头的长度
                int reWind = 0;
                while (!findNALStartCode) {
                    int hex = rf.read();
                    if (curpos >= bb.length) {
                        break;
                    }
                    bb[curpos++] = (byte) hex;
                    if (hex == -1) {
                        nextNalStartPos = curpos;
                    }
                    if (findStartCode4(bb, curpos - 4)) {
                        findNALStartCode = true;
                        reWind = 4;
                        nextNalStartPos = curpos - reWind;
    
                    } else if (findStartCode3(bb, curpos - 3)) {
                        findNALStartCode = true;
                        reWind = 3;
                        nextNalStartPos = curpos - reWind;
                    }
                }
                byte[] nal = new byte[nextNalStartPos];
                System.arraycopy(bb, 0, nal, 0, nextNalStartPos);
                long pos = rf.getFilePointer();
                long setPos = pos - reWind;
                //退回rewind长度字节
                rf.seek(setPos);
                return nal;
    
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }
    
        //find match "00 00 00 01"
        private boolean findStartCode4(byte[] bb, int offSet) {
            if (offSet < 0) {
                return false;
            }
            if (bb[offSet] == 0 && bb[offSet + 1] == 0 && bb[offSet + 2] == 0 && bb[offSet + 3] == 1) {
                return true;
            }
            return false;
        }
    
        //find match "00 00 01"
        private boolean findStartCode3(byte[] bb, int offSet) {
            if (offSet <= 0) {
                return false;
            }
            if (bb[offSet] == 0 && bb[offSet + 1] == 0 && bb[offSet + 2] == 1) {
                return true;
            }
            return false;
        }
    

    封装其数据,读取每一NAL单元

     /**
         * 读取每一帧数据
         * @param buffer
         * @return
         */
        public int readSampleData(ByteBuffer buffer) {
            byte[] nal = getNALU();
            buffer.put(nal);
            return nal.length;
        }

    MediaCodec开启解码线程,和 Android MediaCodec,MediaExtractor解码播放MP4文件中解码一样,将NAL单元数据送给解码器即可。

      /**
         *  解析播放H264码流
         */
        private class DecoderH264Thread extends Thread {
            long pts = 0;
    
            @Override
            public void run() {
                super.run();
                while (!isDecodeFinish) {
                    int inputIndex = mediaCodec.dequeueInputBuffer(-1);
                    if (inputIndex >= 0) {
                        ByteBuffer byteBuffer = mediaCodec.getInputBuffer(inputIndex);
                        int sampSize = DecodeH264File.getInstance().readSampleData(byteBuffer);
                        long time = computePresentationTime();
                        if (sampSize > 0 && time > 0) {
                            mediaCodec.queueInputBuffer(inputIndex, 0, sampSize, time, 0);
                            try {
                                sleep(30);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
    
                    }
                    BufferInfo bufferInfo = new BufferInfo();
                    int outIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);
                    if (outIndex >= 0) {
                        mediaCodec.releaseOutputBuffer(outIndex, true);
                    }
                }
            }
    
        }

    好了,到这里结束了,有什么不明白的,欢迎留言~~
    GitHub
    https://github.com/zxd1991/AndroidMedia

    展开全文
  • H264码流分析工具(H264Visa.rar)

    热门讨论 2011-09-29 16:59:43
    一个很好的h264码流分析工具,可以对h264文件进行结构分析及解码分析,是学习h264视频格式的最佳工具
  • 里面包含Elecard StreamEye Tools和H264Visa两个H.264码流分析工具,另外,还有几个h264格式的测试视频!
  • h264码流rtp传输demo

    2018-11-21 08:37:30
    h264码流rtp传输demo,H264 RTP封包代码实例,可以参考
  • H264码流格式

    千次阅读 2019-03-24 10:35:18
    h264码流格式 码流格式 … NAL头 RBSP NAL头 RBSP NAL头 RBSP … H264 传输 SPS SEI PPS I片 图像定界符 P片 P片 NAL头格式 start code ( 3 or 4 ) forbidden_zero_bit(1) nal_ref_idc (2) nal_...

    h264码流格式

    码流格式

    NAL头RBSPNAL头RBSPNAL头RBSP

    H264 传输

    SPSSEIPPSI片图像定界符P片P片

    NAL头格式

    start code ( 3 or 4 )forbidden_zero_bit(1)nal_ref_idc (2)nal_unit_type( 5)RBSP

    解释

    • start code,如果NALU对应的Slice为一帧的开始,则用4字节表示,即0x00000001;否则用3字节表示,0x000001

    • forbidden_zero_bit,禁止位,0正常,1错误

    • nal_ref_idc,重要级别,11表示非常重要

    • nal_unit_type,表示该NALU的类型

    • RBSP ,Raw Byte Sequence Payload,原始字节序列负荷

    • 为了使NALU主体不包括起始码,在编码时每遇到两个字节(连续)的0,就插入一字节0x03,以和起始码相区别。解码时,则将相应的0x03删除掉。

      nal_unit_typeNAL类型
      0未使用
      1不区分,非IDR图像的片
      2片分区A
      3片分区B
      4片分区C
      5IDR图像中的片
      6补充增强信息单元(SEI)
      7序列参数集(SPS)
      8图像参数集 (PPS)
      9分界符
      10序列结束
      11码流结束
      12填充
      12…23保留
      24…31未使用
      • 流结束符,表明该流中已经没有图像
      • 序列结束符,表明下一图像为IDR图像

    码流结构图

    image

    sps

    image

    • (1) profile_idc:
      标识当前H.264码流的profile。我们知道,H.264中定义了三种常用的档次profile:
      基准档次:baseline profile;
      主要档次:main profile;
      扩展档次:extended profile;
      在H.264的SPS中,第一个字节表示profile_idc,根据profile_idc的值可以确定码流符合哪一种档次。判断规律为:
      profile_idc = 66 → baseline profile;
      profile_idc = 77 → main profile;
      profile_idc = 88 → extended profile;
      在新版的标准中,还包括了High、High 10、High 4:2:2、High 4:4:4、High 10 Intra、High 4:2:2 Intra、High 4:4:4 Intra、CAVLC 4:4:4 Intra等,每一种都由不同的profile_idc表示。
      另外,constraint_set0_flag ~ constraint_set5_flag是在编码的档次方面对码流增加的其他一些额外限制性条件。
      在我们实验码流中,profile_idc = 0x42 = 66,因此码流的档次为baseline profile。
    • (2) level_idc
      标识当前码流的Level。编码的Level定义了某种条件下的最大视频分辨率、最大视频帧率等参数,码流所遵从的level由level_idc指定。
      当前码流中,level_idc = 0x1e = 30,因此码流的级别为3。
    • (3) seq_parameter_set_id
      表示当前的序列参数集的id。通过该id值,图像参数集pps可以引用其代表的sps中的参数。
    • (4) log2_max_frame_num_minus4
      用于计算MaxFrameNum的值。计算公式为MaxFrameNum = 2^(log2_max_frame_num_minus4 + 4)。MaxFrameNum是frame_num的上限值,frame_num是图像序号的一种表示方法,在帧间编码中常用作一种参考帧标记的手段。
    • (5) pic_order_cnt_type
      表示解码picture order count(POC)的方法。POC是另一种计量图像序号的方式,与frame_num有着不同的计算方法。该语法元素的取值为0、1或2。
    • (6) log2_max_pic_order_cnt_lsb_minus4
      用于计算MaxPicOrderCntLsb的值,该值表示POC的上限。计算方法为MaxPicOrderCntLsb = 2^(log2_max_pic_order_cnt_lsb_minus4 + 4)。
    • (7) max_num_ref_frames
      用于表示参考帧的最大数目。
    • (8) gaps_in_frame_num_value_allowed_flag
      标识位,说明frame_num中是否允许不连续的值。
    • (9) pic_width_in_mbs_minus1
      用于计算图像的宽度。单位为宏块个数,因此图像的实际宽度为:
      frame_width = 16 × (pic_width_in_mbs_minus1 + 1);
    • (10) pic_height_in_map_units_minus1
      使用PicHeightInMapUnits来度量视频中一帧图像的高度。PicHeightInMapUnits并非图像明确的以像素或宏块为单位的高度,而需要考虑该宏块是帧编码或场编码。PicHeightInMapUnits的计算方式为:
      PicHeightInMapUnits = pic_height_in_map_units_minus1 + 1;
    • (11) frame_mbs_only_flag
      标识位,说明宏块的编码方式。当该标识位为0时,宏块可能为帧编码或场编码;该标识位为1时,所有宏块都采用帧编码。根据该标识位取值不同,PicHeightInMapUnits的含义也不同,为0时表示一场数据按宏块计算的高度,为1时表示一帧数据按宏块计算的高度。
      按照宏块计算的图像实际高度FrameHeightInMbs的计算方法为:
      FrameHeightInMbs = ( 2 − frame_mbs_only_flag ) * PicHeightInMapUnits
    • (12) mb_adaptive_frame_field_flag
      标识位,说明是否采用了宏块级的帧场自适应编码。当该标识位为0时,不存在帧编码和场编码之间的切换;当标识位为1时,宏块可能在帧编码和场编码模式之间进行选择。
    • (13) direct_8x8_inference_flag
      标识位,用于B_Skip、B_Direct模式运动矢量的推导计算。
    • (14) frame_cropping_flag
      标识位,说明是否需要对输出的图像帧进行裁剪。
    • (15) vui_parameters_present_flag
      标识位,说明SPS中是否存在VUI信息。

    PPS

    image

    • (1) pic_parameter_set_id
      表示当前PPS的id。某个PPS在码流中会被相应的slice引用,slice引用PPS的方式就是在Slice header中保存PPS的id值。该值的取值范围为[0,255]。
    • (2) seq_parameter_set_id
      表示当前PPS所引用的激活的SPS的id。通过这种方式,PPS中也可以取到对应SPS中的参数。该值的取值范围为[0,31]。
    • (3) entropy_coding_mode_flag
      熵编码模式标识,该标识位表示码流中熵编码/解码选择的算法。对于部分语法元素,在不同的编码配置下,选择的熵编码方式不同。例如在一个宏块语法元素中,宏块类型mb_type的语法元素描述符为“ue(v) | ae(v)”,在baseline profile等设置下采用指数哥伦布编码,在main profile等设置下采用CABAC编码。
      标识位entropy_coding_mode_flag的作用就是控制这种算法选择。当该值为0时,选择左边的算法,通常为指数哥伦布编码或者CAVLC;当该值为1时,选择右边的算法,通常为CABAC。
    • (4) bottom_field_pic_order_in_frame_present_flag
      标识位,用于表示另外条带头中的两个语法元素delta_pic_order_cnt_bottom和delta_pic_order_cn是否存在的标识。这两个语法元素表示了某一帧的底场的POC的计算方法。
    • (5) num_slice_groups_minus1
      表示某一帧中slice group的个数。当该值为0时,一帧中所有的slice都属于一个slice group。slice group是一帧中宏块的组合方式,定义在协议文档的3.141部分。
    • (6) num_ref_idx_l0_default_active_minus1、num_ref_idx_l0_default_active_minus1
      表示当Slice Header中的num_ref_idx_active_override_flag标识位为0时,P/SP/B slice的语法元素num_ref_idx_l0_active_minus1和num_ref_idx_l1_active_minus1的默认值。
    • (7) weighted_pred_flag
      标识位,表示在P/SP slice中是否开启加权预测。
    • (8) weighted_bipred_idc
      表示在B Slice中加权预测的方法,取值范围为[0,2]。0表示默认加权预测,1表示显式加权预测,2表示隐式加权预测。
    • (9) pic_init_qp_minus26和pic_init_qs_minus26
      表示初始的量化参数。实际的量化参数由该参数、slice header中的slice_qp_delta/slice_qs_delta计算得到。
    • (10) chroma_qp_index_offset
      用于计算色度分量的量化参数,取值范围为[-12,12]。
    • (11) deblocking_filter_control_present_flag
      标识位,用于表示Slice header中是否存在用于去块滤波器控制的信息。当该标志位为1时,slice header中包含去块滤波相应的信息;当该标识位为0时,slice header中没有相应的信息。
    • (12) constrained_intra_pred_flag
      若该标识为1,表示I宏块在进行帧内预测时只能使用来自I和SI类型宏块的信息;若该标识位0,表示I宏块可以使用来自Inter类型宏块的信息。
    • (13) redundant_pic_cnt_present_flag
      标识位,用于表示Slice header中是否存在redundant_pic_cnt语法元素。当该标志位为1时,slice header中包含redundant_pic_cnt;当该标识位为0时,slice header中没有相应的信息。
    aligned(8) class AVCDecoderConfigurationRecord {
        unsigned int(8) configurationVersion = 1;
        unsigned int(8) AVCProfileIndication;
        unsigned int(8) profile_compatibility;
        unsigned int(8) AVCLevelIndication;
        bit(6) reserved = ‘111111’b;
        unsigned int(2) lengthSizeMinusOne;
        bit(3) reserved = ‘111’b;
     
        unsigned int(5) numOfSequenceParameterSets;
        for (i=0; i< numOfSequenceParameterSets; i++) {
            unsigned int(16) sequenceParameterSetLength ;
            bit(8*sequenceParameterSetLength) sequenceParameterSetNALUnit;
        }
     
        unsigned int(8) numOfPictureParameterSets;
        for (i=0; i< numOfPictureParameterSets; i++) {
            unsigned int(16) pictureParameterSetLength;
            bit(8*pictureParameterSetLength) pictureParameterSetNALUnit;
        }
    }
    
    

    profile

    • H264 主要包括Baseline, - - - Ext,Main,High这几种常用profile和一些特殊用途的profies,如Constrain baseline, SVC,MVC和一系列high-Fidelity profiles 等等,各种profile是根据不同的应用场景设计的,具体余下:
    • Baseline主要是用于可视电话,会议电视,无线通讯等实时通信。要实时,就要减少视频decode和display的时延,所以没有B frame;为了提高针对网络丢包的容错能力,特意添加了FMO,ASO和冗余slice;
    • Main用于数字广播电视和数字视频存储,侧重点在于提高压缩率,所以有了CABAC,MBAFF,Interlace,B frame等。
      Extend用于改进误码性能和码流切换(SP和SI slice),侧重于码流切换(SI,SP slice)和error resilience(数据分割)。
    • High主要用于高压缩效率和质量, 引入8x8 DCT,选择量化矩阵等。
    展开全文
  • h264码流类型

    千次阅读 2015-12-12 21:04:01
    h264码流分为rbsp和mp4两种格式,详细讲解这两种码流格式的区别及解码
  • Wireshark如何导出h264码流和h265码流

    千次阅读 2019-08-23 10:01:03
    导出264的方法请参考原文博客:https://blog.csdn.net/volvetandanny/article/details/47087641 插件下载地址:https://download.csdn.net/download/volvetandanny/8937395 导出265的方法原博客地址:https:/...
  • H264码流保存为YUV数据 通过H264Visa软件转换-附件资源
  • H264码流解码

    2011-09-28 17:41:03
    源代码代码为VC6.0工程,由于有少量的嵌入汇编代码,需要nasm.exe(网上搜一下,很好找的),在压缩包中还有两段标准的h264码流用于解码实验!

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 47,405
精华内容 18,962
关键字:

h264码流查看