精华内容
下载资源
问答
  • ps解析H264

    2018-05-26 16:19:32
    把示例采用VS2017开发,C#语言实现PS流解析H264数据 可以参考博客:https://blog.csdn.net/g0415shenw/article/details/80385088
  • Gb28181之Ps流解析H264

    万次阅读 2018-05-20 20:30:54
    本文详细描述如何通过ps流解析H264码流。先研究下PSM(节目流映射),PSM头定义如下:这里找了一个标准的PS流里面的PSM数据进行研究分析:packet_start_code_prefix—24bit :00 00 01map_stream_id-8bit:BCprogram...

    gb28181发送码流选择PS流,PS流在封装H264的数据。本文详细描述如何通过ps流解析H264码流。

    *************************PSM流解析**************************************************

    先研究下PSM(节目流映射),PSM头定义如下:

    这里找了一个标准的PS流里面的PSM数据进行研究分析:


    packet_start_code_prefix—24bit :00 00 01

    map_stream_id-8bit:BC

    program_stream_map_length-16bit:00 5A  ----->90 此字段含义为紧随此字段还有90个字节,通过计算每行16*5+10=90,正确

    current_next_indicator—1bit:--->1    置于1时指示发送的节目流映射为当前有效。置于0它指示发送的节目流映射尚未有效并且下一个节目流映射表将生效

    reserved-2bit:---->11    预留位

    program_stream_map_version-5bit:--00000    整个节目流映射的版本号,每当节目流映射的定义改变时,该版本号必须增

    1模32,current_next_indicator置为1时,为当前有效的节目流映射的版本,当current_next_indicator置为0时,是下一个有效的节目流映射的版本。
    reserved-7bit--->1111111
    marker_bit-1bit--->1

    program_stream_info_length-16bit--->0x00 0x24--->36   指示紧随此字段的描述符的总长为36个字节,

    剩下的36个字节为:

    for (i = 0; i < N; i++) { 
    descriptor()                
    }                


    elementary_stream_map_length-16bit -->0x00 0x2C--->44  指示在此节目流映射中所有基本流信息的以字节为单位的总长度。

    剩下44个字节为:

    for (i = 0; i < N1; i++) {

    stream_type

    elementary_stream_id

    elementary_stream_info_length

    for (i = 0; i < N2; i++) {

    descriptor()}

    }

    stream_type_8bit--->1B  含义如下:

    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。

    elementary_stream_id-8bit--->E0     指示存储此基本流的PES包的PES包头内stream_id 字段的赋值elementary_stream_info_length-16bit--->00  10--->16  指示紧随此字段的描述符长度为16个字节

    这里一共花掉了1+1+2+16 =20个字节 而实际长度为44个字节 剩下的24个字节为什么呢?我们再分析剩下的24个字节。

    stream_type_8bit--->90  说明音频为G711

    elementary_stream_id-8bit--->C0 说明为音频

    elementary_stream_info_length-16bit--->00  0c--->12  指示紧随此字段的描述符长度为12个字节

    这里一共花掉了1+1+2+12 = 14个字节 而剩下的24-14=10个字节

    剩下的这10个字节,表示无法理解。由于这段视频取的是海康的私有码流,说明这段码流加了私有信息。这里从新取了一段公安一所的测试的PS流进行分析,如下:

    这段是符合上述标准的。

    CRC_32--->0x00 00 00 00

    **************************************************Pack_header流解析**************************************************





    ***************************我是分割线*********************************

    下面主要讲解如何把PS流解析出裸H264数据

    一个完整的ps流一般结构包括Pack_header +System_header+Program_stream_map+PES_pakcet(N个) 其中PES_pakcet可能有N个

    可以通过头的标识来判断当前的类型:

            public static readonly byte[] Pack_start_code = { 0x00, 0x00, 0x01, 0xBA };
            public static readonly byte[] System_header_start_code = { 0x00, 0x00, 0x01, 0xBB };
            public static readonly byte[] Pakcet_start_code_prefix = { 0x00, 0x00, 0x01 };
    		public static readonly byte Psm_map_stream_id = 0xBC;
    		public static readonly byte Pes_map_stream_id_video = 0xE0;
    		public static readonly byte Pes_map_stream_id_audio = 0xC0;

    我的思路是先解析Pack_header 在逐个解析System_header/Program_stream_map/PES_pakcet  到了PES_pakcet时候,要注意下面一包可能是Pack_header 也可能是PES_pakcet 。代码如下:

    static bool ParsingPs()
            {
                //find  Pack_header
                if (m_Buf.Count<4)
                {
                    return false;
                }
                if(packState == PackState.PES_pakcet)
                {
                    //find Pack_header
                    if(m_Buf[0] == PsDefine.Pack_start_code[0] && m_Buf[0 + 1] == PsDefine.Pack_start_code[1] &&
                            m_Buf[0 + 2] == PsDefine.Pack_start_code[2] && m_Buf[0 + 3] == PsDefine.Pack_start_code[3])
                    {
                        packState = PackState.Adding;
                    }
                }
                if (packState == PackState.Adding)
                {
                    bool bFinePacketHead = false;
                    for(int i =0;i<m_Buf.Count-4;i++)
                    {
                        if(m_Buf[i] == PsDefine.Pack_start_code[0] && m_Buf[i+1] == PsDefine.Pack_start_code[1]
                            && m_Buf[i+2] == PsDefine.Pack_start_code[2] && m_Buf[i+3] == PsDefine.Pack_start_code[3])
                        {
                            bFinePacketHead = true;
                            if (i>0)
                            {
                                m_Buf.RemoveRange(0, i);
                            }
                            break;
                        }
                    }
                    if(bFinePacketHead)
                    {
                        Pack_header pack_Header = new Pack_header();
                        if (!pack_Header.ParsingPackheader(m_Buf.ToArray()))
                        {
                            return false;
                        }
                        if(m_Buf.Count> pack_Header.Pack_headerCount + pack_Header.Pack_stuffing_lenth_3)
                        {
                            m_Buf.RemoveRange(0, pack_Header.Pack_headerCount + pack_Header.Pack_stuffing_lenth_3);
                            packState = PackState.Pack_header;
                        }
                        else
                        {
                            return false;
                        }
                       
                    }
                   
                }
                if (packState == PackState.Pack_header)
                {
                    //find System_header
                    if (m_Buf.Count < 4)
                    {
                        return false;
                    }
                    {
                        if (m_Buf[0] == PsDefine.System_header_start_code[0] && m_Buf[0 + 1] == PsDefine.System_header_start_code[1] &&
                            m_Buf[0 + 2] == PsDefine.System_header_start_code[2] && m_Buf[0 + 3] == PsDefine.System_header_start_code[3])
                        {
                            System_header system_Header = new System_header();
                            if (!system_Header.ParsingSystem_header(m_Buf.ToArray()))
                            {
                                return false;
                            }
                            if(m_Buf.Count> system_Header.Header_lenth+6)
                            {
                                m_Buf.RemoveRange(0, system_Header.Header_lenth + 6);
                            }
                            else
                            {
                                return false;
                            }
                        }
                    }
    
                    //find Program_stream_map
                    if (m_Buf.Count < 4)
                    {
                        return false;
                    }
                    {
                        if(m_Buf[0] == PsDefine.Pakcet_start_code_prefix[0] &&
                            m_Buf[0+1] == PsDefine.Pakcet_start_code_prefix[1] &&
                            m_Buf[0+2] == PsDefine.Pakcet_start_code_prefix[2] &&
                            m_Buf[0+3] == PsDefine.Psm_map_stream_id)
                        {
                            Program_stream_map program_Stream_Map = new Program_stream_map();
                            if(!program_Stream_Map.ParsingProgram_stream_map(m_Buf.ToArray()))
                            {
                                return false;
                            }
                            if (m_Buf.Count > program_Stream_Map.Program_steam_map_lenth_16 + 6)
                            {
                                m_Buf.RemoveRange(0, program_Stream_Map.Program_steam_map_lenth_16 + 6);
                            }
                            else
                            {
                                return false;
                            }
                        }
                    }
    
                    packState = PackState.PES_pakcet;
                }
                //find PES_pakcet
                if (m_Buf.Count < 4)
                {
                    return false;
                }
                if(m_Buf[0] == PsDefine.Pakcet_start_code_prefix[0] &&
                    m_Buf[1]== PsDefine.Pakcet_start_code_prefix[1] &&
                    m_Buf[2] == PsDefine.Pakcet_start_code_prefix[2])
                {
                    PES_pakcet pES_Pakcet = new PES_pakcet();
                    if(!pES_Pakcet.ParsingPES_pakcet(m_Buf.ToArray()))
                    {
                        return false;
                    }
                    if(m_Buf.Count < pES_Pakcet.PES_pakcet_lenth_16+6)
                    {
                        return false;
                    }
                    byte[] bPes = new byte[pES_Pakcet.PES_pakcet_lenth_16 + 6];
                    m_Buf.CopyTo(0, bPes, 0, bPes.Length);
                    m_Buf.RemoveRange(0,bPes.Length);
                         
                    if(bPes[3] == PsDefine.Pes_map_stream_id_video)
                    {
                        if(pES_Pakcet.PES_pakcet_lenth_16>0)
                        {
                            m_h264File.Write(bPes, 9 + bPes[8], bPes.Length - 9 - bPes[8]);
                            m_h264File.Flush();
                        }
                    }
                    else if(bPes[3] == PsDefine.Pes_map_stream_id_audio)
                    {
    
                    }
                    return true;
                }
                else
                {
                    packState = PackState.Adding;
                    return false;
                }
    
    
            }

    为了便于大家学习和交流,用vs2017开发,C#语言实现的ps解析为H264资源如下,欢迎大家提问:

    ps解析出H264










    展开全文
  • 解析H264的SPS信息

    万次阅读 热门讨论 2018-07-10 15:57:31
    在做音视频开发的时候,存在不解码视频帧的前提下需要获取视频宽高、帧率等信息,而H.264中的SPS数据可为我们提供这些相关的信息。在此之前,我们需要对一些协议和算法有一定的初步了解,后文中有完整的代码展示。H....

    在做音视频开发的时候,存在不解码视频帧的前提下需要获取视频宽高、帧率等信息,而H.264中的SPS数据可为我们提供这些相关的信息。在此之前,我们需要对一些协议和算法有一定的初步了解,后文中有完整的代码展示。

    H.264协议

    我们在此不讲解H.264协议,但是我们需要了解NAL单元语法(NAL unit syntax)、序列参数集数据语法(Sequence parameter set data syntax)、视频可用参数语法(VUI parameters syntax)。具体可去ITU-T官网下载H.264文档,我查看的是T-REC-H.264-201704-I!!PDF-E.pdf。

    Sequence parameter set data syntax

    VUI parameters syntax


    第一列:相关参数信息名称;

    第二列:规定条带数据可以至多划分为三种条带数据类别(这个可以不用理解);

    第三列中的u(n)表示:使用n位无符号整数表示,由n位bit换算得到,即从左到右读取n个bit位所表示的值;ue(v)表示:无符号指数哥伦布编码值;ue(v)表示:有符号指数哥伦布编码值。

    u(n)代码实现:

    static UINT u(sps_bit_stream *bs, BYTE bitCount)
    {
        UINT val = 0;
        for (BYTE i=0; i<bitCount; i++) {
            val <<= 1;
            if (eof(bs)) {
                val = 0;
                break;
            } else if (bs->data[bs->index / 8] & (0x80 >> (bs->index % 8))) {     //计算index所在的位是否为1
                val |= 1;
            }
            bs->index++;
        }
        
        return val;
    }

    指数哥伦布编码

    Golomb编码是一种无损的数据压缩方法,由数学家Solomon W.Golomb在1960年代发明。Golomb编码只能对非负整数进行编码,符号表中的符号出现的概率符合几何分布(Geometric Distribution)时,使用Golomb编码可以取得最优效果,也就是说Golomb编码比较适合小的数字比大的数字出现概率比较高的编码。它使用较短的码长编码较小的数字,较长的码长编码较大的数字。在此,我们只需要了解:无符号指数哥伦布编码ue(v)、有符号指数哥伦布编码se(v)。

    无符号指数哥伦布编码(UE)

    哥伦布编码的码字code_word由三部分组成:code_word = [M个0] + [1] + [Info]
    其中,Info是一个携带信息的M位数据,每个哥伦布码的长度为(2M+1)位,每个码字都可由code_num产生。
    根据码字code_word解码出code_num值的过程如下:
    1. 首先读入M位以"1"为结尾的0;
    2. 根据得到的M,读入接下来的M位Info数据;

    3. 根据这个公式得到计算结果code_num = Info – 1 + 2M

    代码实现:

    static UINT ue(sps_bit_stream *bs)
    {
        UINT zeroNum = 0;
        while (u(bs, 1) == 0 && !eof(bs) && zeroNum < 32) {
            zeroNum ++;
        }
        
        return (UINT)((1 << zeroNum) - 1 + u(bs, zeroNum));
    }

    有符号指数哥伦布编码(SE)

    有符号的指数哥伦布编码值是通过无符号的指数哥伦布编码的值通过换算得到的,其换算关系为:n = (-1)^(k+1) * ceil(k/2)。

    代码实现:

    INT se(sps_bit_stream *bs)
    {
        INT ueVal = (INT)ue(bs);
        double k = ueVal;
        
        INT seVal = (INT)ceil(k / 2);     //ceil:返回大于或者等于指定表达式的最小整数
        if (ueVal % 2 == 0) {       //偶数取反,即(-1)^(k+1)
            seVal = -seVal;
        }
        
        return seVal;
    }

    SPS参数分析

    视频宽高(Width、Height)获取

    从H.264协议文档中可以看出视频宽高涉及到的参数有:

    pic_width_in_mbs_minus1加1指定以宏块(16*16)为单位的每个解码图像的宽度,即PicWidthInSamplesL = (pic_width_in_mbs_minus1 + 1) * 16 ;


    pic_height_in_map_units_minus1:加1指定解码帧或场中的一个切片组的高度,即PicSizeInMapUnits = PicWidthInMbs * (pic_height_in_map_units_minus1 + 1) ;


    frame_mbs_only_flag:等于0指明了视频序列的编码图象可能是编码场或编码帧,等于1指明了每个编码视频序列的编码图像都是只含帧宏块的编码帧,即FrameHeightInMbs = ( 2 − frame_mbs_only_flag ) * PicHeightInMapUnits ;

    frame_cropping_flag:等于0表明不存在帧剪切偏移量,等于1表明在sps中下一个使用的帧剪切偏移量参数,即需要使用到frame_crop_left_offset、frame_crop_right_offset、frame_crop_top_offset、frame_crop_bottom_offset四个参数;

    frame_crop_left_offset、frame_crop_right_offset、frame_crop_top_offset、frame_crop_bottom_offset:左右上下帧裁剪偏移量,需要根据chroma format计算实际的视频宽高;


    chroma_format_idc:亮度取样对应的色度取样值,包括0~3,不存在时默认为1(即YUV420);


    根据上述参数的定义说明,计算视频分辨率存在多种情况:1、需要判断是否包含宏片的编码帧或场;2、需要判断视频帧是否被裁剪;3、需要判断采样类型。因此代码大致如下:

    UINT pic_width_in_mbs_minus1 = ue(&bs);     //第36位开始
    UINT pic_height_in_map_units_minus1 = ue(&bs);      //47
    UINT frame_mbs_only_flag = u(&bs, 1);
    
    info->width = (INT)(pic_width_in_mbs_minus1 + 1) * 16;
    info->height = (INT)(2 - frame_mbs_only_flag) * (pic_height_in_map_units_minus1 + 1) * 16;
    
    if (!frame_mbs_only_flag) {
        u(&bs, 1);      //mb_adaptive_frame_field_flag
    }
    
    u(&bs, 1);     //direct_8x8_inference_flag
    UINT frame_cropping_flag = u(&bs, 1);
    if (frame_cropping_flag) {
        UINT frame_crop_left_offset = ue(&bs);
        UINT frame_crop_right_offset = ue(&bs);
        UINT frame_crop_top_offset = ue(&bs);
        UINT frame_crop_bottom_offset= ue(&bs);
        
        //See 6.2 Source, decoded, and output picture formats
        INT crop_unit_x = 1;
        INT crop_unit_y = 2 - frame_mbs_only_flag;      //monochrome or 4:4:4
        if (chroma_format_idc == 1) {   //4:2:0
            crop_unit_x = 2;
            crop_unit_y = 2 * (2 - frame_mbs_only_flag);
        } else if (chroma_format_idc == 2) {    //4:2:2
            crop_unit_x = 2;
            crop_unit_y = 2 - frame_mbs_only_flag;
        }
        
        info->width -= crop_unit_x * (frame_crop_left_offset + frame_crop_right_offset);
        info->height -= crop_unit_y * (frame_crop_top_offset + frame_crop_bottom_offset);
    }

    视频帧率(FPS)获取

    视频帧率信息在SPS的VUI parameters syntax中,需要根据time_scale、fixed_frame_rate_flag计算得到:fps = time_scale / num_units_in_tick。但是需要判断参数timing_info_present_flag是否存在,若不存在表示FPS在信息流中无法获取。同时还存在另外一种情况:fixed_frame_rate_flag为1时,两个连续图像的HDR输出时间频率为单位,获取的fps是实际的2倍。

    UINT timing_info_present_flag = u(bs, 1);
    if (timing_info_present_flag) {
        UINT num_units_in_tick = u(bs, 32);
        UINT time_scale = u(bs, 32);
        UINT fixed_frame_rate_flag = u(bs, 1);
        
        info->fps = (UINT)((float)time_scale / (float)num_units_in_tick);
        if (fixed_frame_rate_flag) {
            info->fps = info->fps/2;
        }
    }

    SPS解析代码

    H264ParseSPS.h

    //
    //  H264ParseSPS.h
    //
    //  Created by lzj<lizhijian_21@163.com> on 2018/7/6.
    //  Copyright © 2018年 LZJ. All rights reserved.
    //
    
    #ifndef H264ParseSPS_h
    #define H264ParseSPS_h
    #ifdef __cplusplus
    extern "C"
    {
    #endif
    #include <stdio.h>
        
    typedef struct
    {
        unsigned int profile_idc;
        unsigned int level_idc;
        
        unsigned int width;
        unsigned int height;
        unsigned int fps;       //SPS中可能不包含FPS信息
    } sps_info_struct;
    
        
    /**
     解析SPS数据信息
    
     @param data SPS数据内容,需要Nal类型为0x7数据的开始(比如:67 42 00 28 ab 40 22 01 e3 cb cd c0 80 80 a9 02)
     @param dataSize SPS数据的长度
     @param info SPS解析之后的信息数据结构体
     @return success:1,fail:0
     
     */
    int h264_parse_sps(const unsigned char *data, unsigned int dataSize, sps_info_struct *info);
    
    #ifdef __cplusplus
    }
    #endif
    #endif /* H264ParseSPS_h */
    

    H264ParseSPS.c

    //
    //  H264ParseSPS.c
    //
    //  Created by lzj<lizhijian_21@163.com> on 2018/7/6.
    //  Copyright © 2018年 LZJ. All rights reserved.
    //
    //  See https://www.itu.int/rec/T-REC-H.264-201610-S
    //
    
    #include "H264ParseSPS.h"
    #include <string.h>
    #include <stdlib.h>
    #include <math.h>
    
    typedef unsigned char BYTE;
    typedef int INT;
    typedef unsigned int UINT;
    
    typedef struct
    {
        const BYTE *data;   //sps数据
        UINT size;          //sps数据大小
        UINT index;         //当前计算位所在的位置标记
    } sps_bit_stream;
    
    /**
     移除H264的NAL防竞争码(0x03)
    
     @param data sps数据
     @param dataSize sps数据大小
     */
    static void del_emulation_prevention(BYTE *data, UINT *dataSize)
    {
        UINT dataSizeTemp = *dataSize;
        for (UINT i=0, j=0; i<(dataSizeTemp-2); i++) {
            int val = (data[i]^0x0) + (data[i+1]^0x0) + (data[i+2]^0x3);    //检测是否是竞争码
            if (val == 0) {
                for (j=i+2; j<dataSizeTemp-1; j++) {    //移除竞争码
                    data[j] = data[j+1];
                }
                
                (*dataSize)--;      //data size 减1
            }
        }
    }
    
    static void sps_bs_init(sps_bit_stream *bs, const BYTE *data, UINT size)
    {
        if (bs) {
            bs->data = data;
            bs->size = size;
            bs->index = 0;
        }
    }
    
    /**
     是否已经到数据流最后
     
     @param bs sps_bit_stream数据
     @return 1:yes,0:no
     */
    static INT eof(sps_bit_stream *bs)
    {
        return (bs->index >= bs->size * 8);    //位偏移已经超出数据
    }
    
    /**
     读取从起始位开始的BitCount个位所表示的值
     
     @param bs sps_bit_stream数据
     @param bitCount bit位个数(从低到高)
     @return value
     */
    static UINT u(sps_bit_stream *bs, BYTE bitCount)
    {
        UINT val = 0;
        for (BYTE i=0; i<bitCount; i++) {
            val <<= 1;
            if (eof(bs)) {
                val = 0;
                break;
            } else if (bs->data[bs->index / 8] & (0x80 >> (bs->index % 8))) {     //计算index所在的位是否为1
                val |= 1;
            }
            bs->index++;  //递增当前起始位(表示该位已经被计算,在后面的计算过程中不需要再次去计算所在的起始位索引,缺点:后面每个bit位都需要去位移)
        }
        
        return val;
    }
    
    /**
     读取无符号哥伦布编码值(UE)
     #2^LeadingZeroBits - 1 + (xxx)
    
     @param bs sps_bit_stream数据
     @return value
     */
    static UINT ue(sps_bit_stream *bs)
    {
        UINT zeroNum = 0;
        while (u(bs, 1) == 0 && !eof(bs) && zeroNum < 32) {
            zeroNum ++;
        }
        
        return (UINT)((1 << zeroNum) - 1 + u(bs, zeroNum));
    }
    
    /**
     读取有符号哥伦布编码值(SE)
     #(-1)^(k+1) * Ceil(k/2)
     
     @param bs sps_bit_stream数据
     @return value
     */
    INT se(sps_bit_stream *bs)
    {
        INT ueVal = (INT)ue(bs);
        double k = ueVal;
        
        INT seVal = (INT)ceil(k / 2);     //ceil:返回大于或者等于指定表达式的最小整数
        if (ueVal % 2 == 0) {       //偶数取反,即(-1)^(k+1)
            seVal = -seVal;
        }
        
        return seVal;
    }
    
    /**
     视频可用性信息(Video usability information)解析
    
     @param bs sps_bit_stream数据
     @param info sps解析之后的信息数据及结构体
     @see E.1.1 VUI parameters syntax
     */
    void vui_para_parse(sps_bit_stream *bs, sps_info_struct *info)
    {
        UINT aspect_ratio_info_present_flag = u(bs, 1);
        if (aspect_ratio_info_present_flag) {
            UINT aspect_ratio_idc = u(bs, 8);
            if (aspect_ratio_idc == 255) {      //Extended_SAR
                u(bs, 16);      //sar_width
                u(bs, 16);      //sar_height
            }
        }
        
        UINT overscan_info_present_flag = u(bs, 1);
        if (overscan_info_present_flag) {
            u(bs, 1);       //overscan_appropriate_flag
        }
        
        UINT video_signal_type_present_flag = u(bs, 1);
        if (video_signal_type_present_flag) {
            u(bs, 3);       //video_format
            u(bs, 1);       //video_full_range_flag
            UINT colour_description_present_flag = u(bs, 1);
            if (colour_description_present_flag) {
                u(bs, 8);       //colour_primaries
                u(bs, 8);       //transfer_characteristics
                u(bs, 8);       //matrix_coefficients
            }
        }
        
        UINT chroma_loc_info_present_flag = u(bs, 1);
        if (chroma_loc_info_present_flag) {
            ue(bs);     //chroma_sample_loc_type_top_field
            ue(bs);     //chroma_sample_loc_type_bottom_field
        }
    
        UINT timing_info_present_flag = u(bs, 1);
        if (timing_info_present_flag) {
            UINT num_units_in_tick = u(bs, 32);
            UINT time_scale = u(bs, 32);
            UINT fixed_frame_rate_flag = u(bs, 1);
            
            info->fps = (UINT)((float)time_scale / (float)num_units_in_tick);
            if (fixed_frame_rate_flag) {
                info->fps = info->fps/2;
            }
        }
        
        UINT nal_hrd_parameters_present_flag = u(bs, 1);
        if (nal_hrd_parameters_present_flag) {
            //hrd_parameters()  //see E.1.2 HRD parameters syntax
        }
        
        //后面代码需要hrd_parameters()函数接口实现才有用
        UINT vcl_hrd_parameters_present_flag = u(bs, 1);
        if (vcl_hrd_parameters_present_flag) {
            //hrd_parameters()
        }
        if (nal_hrd_parameters_present_flag || vcl_hrd_parameters_present_flag) {
            u(bs, 1);   //low_delay_hrd_flag
        }
        
        u(bs, 1);       //pic_struct_present_flag
        UINT bitstream_restriction_flag = u(bs, 1);
        if (bitstream_restriction_flag) {
            u(bs, 1);   //motion_vectors_over_pic_boundaries_flag
            ue(bs);     //max_bytes_per_pic_denom
            ue(bs);     //max_bits_per_mb_denom
            ue(bs);     //log2_max_mv_length_horizontal
            ue(bs);     //log2_max_mv_length_vertical
            ue(bs);     //max_num_reorder_frames
            ue(bs);     //max_dec_frame_buffering
        }
    }
    
    //See 7.3.1 NAL unit syntax
    //See 7.3.2.1.1 Sequence parameter set data syntax
    INT h264_parse_sps(const BYTE *data, UINT dataSize, sps_info_struct *info)
    {
        if (!data || dataSize <= 0 || !info) return 0;
        INT ret = 0;
        
        BYTE *dataBuf = malloc(dataSize);
        memcpy(dataBuf, data, dataSize);        //重新拷贝一份数据,防止移除竞争码时对原数据造成影响
        del_emulation_prevention(dataBuf, &dataSize);
    
        sps_bit_stream bs = {0};
        sps_bs_init(&bs, dataBuf, dataSize);   //初始化SPS数据流结构体
        
        u(&bs, 1);      //forbidden_zero_bit
        u(&bs, 2);      //nal_ref_idc
        UINT nal_unit_type = u(&bs, 5);
    
        if (nal_unit_type == 0x7) {     //Nal SPS Flag
            info->profile_idc = u(&bs, 8);
            u(&bs, 1);      //constraint_set0_flag
            u(&bs, 1);      //constraint_set1_flag
            u(&bs, 1);      //constraint_set2_flag
            u(&bs, 1);      //constraint_set3_flag
            u(&bs, 1);      //constraint_set4_flag
            u(&bs, 1);      //constraint_set4_flag
            u(&bs, 2);      //reserved_zero_2bits
            info->level_idc = u(&bs, 8);
            
            ue(&bs);    //seq_parameter_set_id
            
            UINT chroma_format_idc = 1;     //摄像机出图大部分格式是4:2:0
            if (info->profile_idc == 100 || info->profile_idc == 110 || info->profile_idc == 122 ||
                info->profile_idc == 244 || info->profile_idc == 44 || info->profile_idc == 83 ||
                info->profile_idc == 86 || info->profile_idc == 118 || info->profile_idc == 128 ||
                info->profile_idc == 138 || info->profile_idc == 139 || info->profile_idc == 134 || info->profile_idc == 135) {
                chroma_format_idc = ue(&bs);
                if (chroma_format_idc == 3) {
                    u(&bs, 1);      //separate_colour_plane_flag
                }
                
                ue(&bs);        //bit_depth_luma_minus8
                ue(&bs);        //bit_depth_chroma_minus8
                u(&bs, 1);      //qpprime_y_zero_transform_bypass_flag
                UINT seq_scaling_matrix_present_flag = u(&bs, 1);
                if (seq_scaling_matrix_present_flag) {
                    UINT seq_scaling_list_present_flag[8] = {0};
                    for (INT i=0; i<((chroma_format_idc != 3)?8:12); i++) {
                        seq_scaling_list_present_flag[i] = u(&bs, 1);
                        if (seq_scaling_list_present_flag[i]) {
                            if (i < 6) {    //scaling_list(ScalingList4x4[i], 16, UseDefaultScalingMatrix4x4Flag[i])
                            } else {    //scaling_list(ScalingList8x8[i − 6], 64, UseDefaultScalingMatrix8x8Flag[i − 6] )
                            }
                        }
                    }
                }
            }
            
            ue(&bs);        //log2_max_frame_num_minus4
            UINT pic_order_cnt_type = ue(&bs);
            if (pic_order_cnt_type == 0) {
                ue(&bs);        //log2_max_pic_order_cnt_lsb_minus4
            } else if (pic_order_cnt_type == 1) {
                u(&bs, 1);      //delta_pic_order_always_zero_flag
                se(&bs);        //offset_for_non_ref_pic
                se(&bs);        //offset_for_top_to_bottom_field
                
                UINT num_ref_frames_in_pic_order_cnt_cycle = ue(&bs);
                INT *offset_for_ref_frame = (INT *)malloc((UINT)num_ref_frames_in_pic_order_cnt_cycle * sizeof(INT));
                for (INT i = 0; i<num_ref_frames_in_pic_order_cnt_cycle; i++) {
                    offset_for_ref_frame[i] = se(&bs);
                }
                free(offset_for_ref_frame);
            }
            
            ue(&bs);      //max_num_ref_frames
            u(&bs, 1);      //gaps_in_frame_num_value_allowed_flag
            
            UINT pic_width_in_mbs_minus1 = ue(&bs);     //第36位开始
            UINT pic_height_in_map_units_minus1 = ue(&bs);      //47
            UINT frame_mbs_only_flag = u(&bs, 1);
            
            info->width = (INT)(pic_width_in_mbs_minus1 + 1) * 16;
            info->height = (INT)(2 - frame_mbs_only_flag) * (pic_height_in_map_units_minus1 + 1) * 16;
            
            if (!frame_mbs_only_flag) {
                u(&bs, 1);      //mb_adaptive_frame_field_flag
            }
            
            u(&bs, 1);     //direct_8x8_inference_flag
            UINT frame_cropping_flag = u(&bs, 1);
            if (frame_cropping_flag) {
                UINT frame_crop_left_offset = ue(&bs);
                UINT frame_crop_right_offset = ue(&bs);
                UINT frame_crop_top_offset = ue(&bs);
                UINT frame_crop_bottom_offset= ue(&bs);
                
                //See 6.2 Source, decoded, and output picture formats
                INT crop_unit_x = 1;
                INT crop_unit_y = 2 - frame_mbs_only_flag;      //monochrome or 4:4:4
                if (chroma_format_idc == 1) {   //4:2:0
                    crop_unit_x = 2;
                    crop_unit_y = 2 * (2 - frame_mbs_only_flag);
                } else if (chroma_format_idc == 2) {    //4:2:2
                    crop_unit_x = 2;
                    crop_unit_y = 2 - frame_mbs_only_flag;
                }
                
                info->width -= crop_unit_x * (frame_crop_left_offset + frame_crop_right_offset);
                info->height -= crop_unit_y * (frame_crop_top_offset + frame_crop_bottom_offset);
            }
            
            UINT vui_parameters_present_flag = u(&bs, 1);
            if (vui_parameters_present_flag) {
                vui_para_parse(&bs, info);
            }
         
            ret = 1;
        }
        free(dataBuf);
        
        return ret;
    }
    代码中部分未用到的参数并没有获取并赋值,比如:u(&bs, 1);      //forbidden_zero_bit。如需获取相应的参数,只需将注释后面对应的变量值进行赋值即可,如:UINT forbidden_zero_bit = u(&bs, 1);

    h264_parse_sps接口中sps数据需要第一个字节以Nal类型为0x7作为开始,比如:67 42 00 28 ab 40 22 01 e3 cb cd c0 80 80 a9 02,解析得到的宽高为1080*1920,fps为0。

    参考文档:T-REC-H.264-201704-I!!PDF-E.pdfH264 SPS分析编码算法之指数哥伦布编码

    展开全文
  • ffmpeg--解析h264

    千次阅读 2017-02-18 15:56:11
    ff_h264_parser是FFmpeg的H.264解析器对应的AVCodecParser结构体。它的定义位于libavcodec\h264_parser.c,如下所示。 AVCodecParser ff_h264_parser = {  .codec_ids = {AV_CODEC_ID_H264 },  .priv_data_size

    ff_h264_parser

    ff_h264_parser是FFmpeg的H.264解析器对应的AVCodecParser结构体。它的定义位于libavcodec\h264_parser.c,如下所示。

    AVCodecParser ff_h264_parser = {

       .codec_ids      = {AV_CODEC_ID_H264 },

       .priv_data_size = sizeof(H264Context),

       .parser_init    = init,

       .parser_parse   = h264_parse,

       .parser_close   = close,

       .split          = h264_split,

    };

    从ff_h264_parser的定义可以看出:AVCodecParser初始化的函数指针parser_init()指向init()函数;解析数据的函数指针parser_parse()指向

    h264_parse()函数;销毁的函数指针parser_close()指向close()函数。

    h264_parse() [对应于AVCodecParser->parser_parse()]

    ff_h264_parser结构体中AVCodecParser的parser_parse()指向h264_parse()函数。该函数完成了AVCodecParser的解析工作(在这里就是H.264码流的解析工作)。h264_parse()的定义位于libavcodec\h264_parser.c,如下所示。

    //解析H.264码流

    //输出一个完整的NAL,存储于poutbuf中

    static int h264_parse(AVCodecParserContext*s,

                          AVCodecContext *avctx,

                          const uint8_t **poutbuf,int *poutbuf_size,

                          const uint8_t *buf, intbuf_size)

    {

       H264Context *h   =s->priv_data;

       ParseContext *pc = &h->parse_context;

       int next;

       //如果还没有解析过1帧,就调用这里解析extradata

       if (!h->got_first) {

           h->got_first = 1;

           if (avctx->extradata_size) {

               h->avctx = avctx;

               // must be done like in decoder, otherwise opening the parser,

               // letting it create extradata and then closing and opening again

               // will cause has_b_frames to be always set.

               // Note that estimate_timings_from_pts does exactly this.

               if (!avctx->has_b_frames)

                    h->low_delay = 1;

               //解析AVCodecContext的extradata

               ff_h264_decode_extradata(h, avctx->extradata,avctx->extradata_size);

           }

        }

       //输入的数据是完整的一帧?

        //这里通过设置flags的PARSER_FLAG_COMPLETE_FRAMES来确定

       if (s->flags & PARSER_FLAG_COMPLETE_FRAMES) {

                 //和缓存大小一样

           next = buf_size;

        }else {

                 //查找帧结尾(帧开始)位置

                 //以“起始码”为依据(0x000001或0x00000001)

           next = h264_find_frame_end(h, buf, buf_size);

           //组帧

           if (ff_combine_frame(pc, next, &buf, &buf_size) < 0) {

               *poutbuf      = NULL;

               *poutbuf_size = 0;

               return buf_size;

           }

     

           if (next < 0 && next != END_NOT_FOUND) {

               av_assert1(pc->last_index + next >= 0);

               h264_find_frame_end(h, &pc->buffer[pc->last_index + next],-next); // update state

           }

        }

       //解析NALU,从SPS、PPS、SEI等中获得一些基本信息。

       //此时buf中存储的是完整的1帧数据

       parse_nal_units(s, avctx, buf, buf_size);

     

       if (avctx->framerate.num)

           avctx->time_base = av_inv_q(av_mul_q(avctx->framerate,(AVRational){avctx->ticks_per_frame, 1}));

       if (h->sei_cpb_removal_delay >= 0) {

           s->dts_sync_point    =h->sei_buffering_period_present;

           s->dts_ref_dts_delta = h->sei_cpb_removal_delay;

           s->pts_dts_delta     =h->sei_dpb_output_delay;

        }else {

           s->dts_sync_point    = INT_MIN;

           s->dts_ref_dts_delta = INT_MIN;

           s->pts_dts_delta     = INT_MIN;

        }

     

        if (s->flags & PARSER_FLAG_ONCE) {

           s->flags &= PARSER_FLAG_COMPLETE_FRAMES;

        }

       //分割后的帧数据输出至poutbuf

       *poutbuf      = buf;

       *poutbuf_size = buf_size;

       return next;

    }

     

    从源代码可以看出,h264_parse()主要完成了以下3步工作:

    (1)如果是第一次解析,则首先调用ff_h264_decode_extradata()解析AVCodecContext的extradata(里面实际上存储了H.264的SPS、PPS)。
    (2)如果传入的flags 中包含PARSER_FLAG_COMPLETE_FRAMES,则说明传入的是完整的一帧数据,不作任何处理;如果不包含PARSER_FLAG_COMPLETE_FRAMES,则说明传入的不是完整的一帧数据而是任意一段H.264数据,则需要调用h264_find_frame_end()通过查找“起始码”(0x00000001或者0x000001)的方法,分离出完整的一帧数据。

    (3)调用parse_nal_units()完成了NALU的解析工作。

    下面分别看一下这3步中涉及到的函数:ff_h264_decode_extradata(),h264_find_frame_end(),parse_nal_units()。

    ff_h264_decode_extradata()

    ff_h264_decode_extradata()用于解析AVCodecContext的extradata(里面实际上存储了H.264的SPS、PPS)。ff_h264_decode_extradata()的定义如下所示。

    //解析extradata

    //最常见的就是解析AVCodecContext的extradata。其中extradata实际上存储的就是SPS、PPS

    int ff_h264_decode_extradata(H264Context*h, const uint8_t *buf, int size)

    {

       AVCodecContext *avctx = h->avctx;

       int ret;

     

        if (!buf || size <= 0)

           return -1;

     

       if (buf[0] == 1) {

           int i, cnt, nalsize;

           const unsigned char *p = buf;

     

           //AVC1 描述:H.264 bitstream without start codes.是不带起始码0×00000001的。MKV/MOV/FLV中的H.264属于这种类型

           //H264 描述:H.264 bitstream with start codes.是带有起始码0×00000001的。MPEGTS中的H.264,或者H.264裸流属于这种类型

           h->is_avc = 1;

           //数据量太小

           //随意测了一个视频

           //SPS: 30 Byte

           //PPS: 6 Byte

           if (size < 7) {

               av_log(avctx, AV_LOG_ERROR,

                       "avcC %d tooshort\n", size);

               return AVERROR_INVALIDDATA;

           }

           /* sps and pps in the avcC always have length coded with 2 bytes,

            * so put a fake nal_length_size = 2 while parsing them */

           h->nal_length_size = 2;

           // Decode sps from avcC

           //解码SPS

           cnt = *(p + 5) & 0x1f; // Number of sps

           p  += 6;

           for (i = 0; i < cnt; i++) {

               nalsize = AV_RB16(p) + 2;

               if(nalsize > size - (p-buf))

                   return AVERROR_INVALIDDATA;

               //解析

               ret = decode_nal_units(h, p, nalsize, 1);

               if (ret < 0) {

                    av_log(avctx, AV_LOG_ERROR,

                           "Decoding sps %dfrom avcC failed\n", i);

                    return ret;

               }

               p += nalsize;

           }

           // Decode pps from avcC

           //解码PPS

           cnt = *(p++); // Number of pps

           for (i = 0; i < cnt; i++) {

               nalsize = AV_RB16(p) + 2;

               if(nalsize > size - (p-buf))

                    return AVERROR_INVALIDDATA;

               ret = decode_nal_units(h, p, nalsize, 1);

               if (ret < 0) {

                    av_log(avctx, AV_LOG_ERROR,

                           "Decoding pps %dfrom avcC failed\n", i);

                    return ret;

               }

               p += nalsize;

           }

           // Store right nal length size that will be used to parse all other nals

           h->nal_length_size = (buf[4] & 0x03) + 1;

        }else {

           h->is_avc = 0;

           //解析

           ret = decode_nal_units(h, buf, size, 1);

           if (ret < 0)

               return ret;

        }

       return size;

    }

    从源代码中可以看出,ff_h264_decode_extradata()调用decode_nal_units()解析SPS、PPS信息。有关decode_nal_units()的源代码在后续文章中再进行分析。

    h264_find_frame_end()

    h264_find_frame_end()用于查找H.264码流中的“起始码”(start code)。在H.264码流中有两种起始码:0x000001和0x00000001。其中4Byte的长度的起始码最为常见。只有当一个完整的帧被编为多个slice的时候,包含这些slice的NALU才会使用3Byte的起始码。h264_find_frame_end()的定义位于libavcodec\h264_parser.c,如下所示。

    //查找帧结尾(帧开始)位置

    //

    //几种状态state:

    //2 - 找到1个0

    //1 - 找到2个0

    //0 - 找到大于等于3个0

    //4 - 找到2个0和1个1,即001(即找到了起始码)

    //5 - 找到至少3个0和1个1,即0001等等(即找到了起始码)

    //7 - 初始化状态

    //>=8 - 找到2个Slice Header

    //

    //关于起始码startcode的两种形式:3字节的0x000001和4字节的0x00000001

    //3字节的0x000001只有一种场合下使用,就是一个完整的帧被编为多个slice的时候,

    //包含这些slice的nalu使用3字节起始码。其余场合都是4字节的。

    //

    static int h264_find_frame_end(H264Context*h, const uint8_t *buf,

                                   int buf_size)

    {

       int i, j;

       uint32_t state;

       ParseContext *pc = &h->parse_context;

       int next_avc= h->is_avc ? 0 : buf_size;

     

    //   mb_addr= pc->mb_addr - 1;

       state = pc->state;

       if (state > 13)

           state = 7;

     

       if (h->is_avc && !h->nal_length_size)

           av_log(h->avctx, AV_LOG_ERROR, "AVC-parser: nal length sizeinvalid\n");

       //

       //每次循环前进1个字节,读取该字节的值

       //根据此前的状态state做不同的处理

       //state取值为4,5代表找到了起始码

       //类似于一个状态机,简单画一下状态转移图:

       //                           +-----+

       //                           |     |

       //                           v     |

       // 7--(0)-->2--(0)-->1--(0)-->0-(0)-+

       // ^        |        |       |

       // |       (1)      (1)     (1)

       // |        |        |       |

       // +--------+        v        v

       //                   4        5

       //

       for (i = 0; i < buf_size; i++) {

                 //超过了

           if (i >= next_avc) {

               int nalsize = 0;

               i = next_avc;

               for (j = 0; j < h->nal_length_size; j++)

                    nalsize = (nalsize << 8)| buf[i++];

               if (nalsize <= 0 || nalsize > buf_size - i) {

                    av_log(h->avctx,AV_LOG_ERROR, "AVC-parser: nal size %d remaining %d\n", nalsize,buf_size - i);

                    return buf_size;

               }

               next_avc = i + nalsize;

               state    = 5;

           }

           //初始state为7

           if (state == 7) {

                    //查找startcode的候选者?

                    //从一段内存中查找取值为0的元素的位置并返回

                    //增加i取值

               i += h->h264dsp.startcode_find_candidate(buf + i, next_avc - i);

               //因为找到1个0,状态转换为2

               if (i < next_avc)

                    state = 2;

           } else if (state <= 2) {      //找到0时候的state。包括1个0(状态2),2个0(状态1),或者3个及3个以上0(状态0)。

               if (buf[i] == 1)           //发现了一个1

                    state ^= 5;            //状态转换关系:2->7,1->4, 0->5。状态4代表找到了001,状态5代表找到了0001

                else if (buf[i])

                    state = 7;             //恢复初始

               else                       //发现了一个0

                    state >>= 1;           // 2->1, 1->0, 0->0

           } else if (state <= 5) {

                    //状态4代表找到了001,状态5代表找到了0001

                    //获取NALU类型

                    //NALU Header(1Byte)的后5bit

               int nalu_type = buf[i] & 0x1F;

     

               if (nalu_type == NAL_SEI || nalu_type == NAL_SPS ||

                    nalu_type == NAL_PPS ||nalu_type == NAL_AUD) {

                    //SPS,PPS,SEI类型的NALU

                    if (pc->frame_start_found){    //如果之前已找到了帧头

                        i++;

                        goto found;

                    }

               } else if (nalu_type == NAL_SLICE || nalu_type == NAL_DPA ||

                           nalu_type == NAL_IDR_SLICE){

                        //表示有sliceheader的NALU

                        //大于等于8的状态表示找到了两个帧头,但没有找到帧尾的状态

                    state += 8;

                    continue;

               }

               //上述两个条件都不满足,回归初始状态(state取值7)

               state = 7;

           } else {

               h->parse_history[h->parse_history_count++]= buf[i];

               if (h->parse_history_count>5) {

                    unsigned int mb, last_mb=h->parse_last_mb;

                    GetBitContext gb;

     

                    init_get_bits(&gb,h->parse_history, 8*h->parse_history_count);

                    h->parse_history_count=0;

                    mb=get_ue_golomb_long(&gb);

                    h->parse_last_mb= mb;

                    if (pc->frame_start_found) {

                        if (mb <= last_mb)

                            goto found;

                    } else

                        pc->frame_start_found =1;

                    state = 7;

               }

           }

        }

       pc->state = state;

       if (h->is_avc)

           return next_avc;

       //没找到

       return END_NOT_FOUND;

     

    found:

       pc->state             = 7;

       pc->frame_start_found = 0;

       if (h->is_avc)

           return next_avc;

       //state=4时候,state & 5=4

       //找到的是001(长度为3),i减小3+1=4,标识帧结尾

       //state=5时候,state & 5=5

       //找到的是0001(长度为4),i减小4+1=5,标识帧结尾

       return i - (state & 5) - 5 * (state > 7);

    }

    从源代码可以看出,h264_find_frame_end()使用了一种类似于状态机的方式查找起始码。函数中的for()循环每执行一遍,状态机的状态就会改变一次。该状态机主要包含以下几种状态:

    7 - 初始化状态
    2 - 找到1个0
    1 - 找到2个0
    0 - 找到大于等于3个0
    4 - 找到2个0和1个1,即001(即找到了起始码)
    5 - 找到至少3个0和1个1,即0001等等(即找到了起始码)
    >=8 - 找到2个Slice Header

    这些状态之间的状态转移图如下所示。图中粉红色代表初始状态,绿色代表找到“起始码”的状态。

        如图所示,h264_find_frame_end()初始化时候位于状态“7”;当找到1个“0”之后,状态从“7”变为“2”;在状态“2”下,如果再次找到1个“0”,则状态变为“1”;在状态“1”下,如果找到“1”,则状态变换为“4”,表明找到了“0x000001”起始码;在状态“1”下,如果找到“0”,则状态变换为“0”;在状态“0”下,如果找到“1”,则状态变换为“5”,表明找到了“0x000001”起始码。

     

    startcode_find_candidate()
        其中,在查找数据中第1个“0”的时候,使用了H264DSPContext结构体中的startcode_find_candidate()函数。startcode_find_candidate()除了包含C语言版本的函数外,还包含了ARMV6等平台下经过汇编优化的函数(估计效率会比C语言版本函数高一些)。C语言版本的函数ff_startcode_find_candidate_c()的定义很简单,位于libavcodec\startcode.c,如下所示。

    int ff_startcode_find_candidate_c(constuint8_t *buf, int size)

    {

       int i = 0;

       for (; i < size; i++)

           if (!buf[i])

               break;

       return i;

    }

    parse_nal_units()

    parse_nal_units()用于解析NALU,从SPS、PPS、SEI等中获得一些基本信息。在该函数中,根据NALU的不同,分别调用不同的函数进行具体的处理。parse_nal_units()的定义位于libavcodec\h264_parser.c,如下所示。

    /**

     *Parse NAL units of found picture and decode some basic information.

     *

     *@param s parser context.

     *@param avctx codec context.

     *@param buf buffer with field/frame data.

     *@param buf_size size of the buffer.

     */

    //解析NALU,从SPS、PPS、SEI等中获得一些基本信息。

    static inline intparse_nal_units(AVCodecParserContext *s,

                                     AVCodecContext *avctx,

                                      const uint8_t *const buf, int buf_size)

    {

       H264Context *h         =s->priv_data;

       int buf_index, next_avc;

       unsigned int pps_id;

       unsigned int slice_type;

       int state = -1, got_reset = 0;

       const uint8_t *ptr;

       int q264 = buf_size >=4 && !memcmp("Q264", buf, 4);

       int field_poc[2];

     

       /* set some sane default values */

       s->pict_type         =AV_PICTURE_TYPE_I;

       s->key_frame         = 0;

       s->picture_structure = AV_PICTURE_STRUCTURE_UNKNOWN;

     

        h->avctx = avctx;

       ff_h264_reset_sei(h);

       h->sei_fpa.frame_packing_arrangement_cancel_flag = -1;

     

       if (!buf_size)

           return 0;

     

       buf_index     = 0;

       next_avc      = h->is_avc ? 0 :buf_size;

       for (;;) {

           int src_length, dst_length, consumed, nalsize = 0;

     

           if (buf_index >= next_avc) {

               nalsize = get_avc_nalsize(h, buf, buf_size, &buf_index);

               if (nalsize < 0)

                    break;

               next_avc = buf_index + nalsize;

           } else {

               buf_index = find_start_code(buf, buf_size, buf_index, next_avc);

               if (buf_index >= buf_size)

                    break;

               if (buf_index >= next_avc)

                    continue;

           }

           src_length = next_avc - buf_index;

           //NALU Header (1 Byte)

           state = buf[buf_index];

           switch (state & 0x1f) {

           case NAL_SLICE:

           case NAL_IDR_SLICE:

               // Do not walk the whole buffer just to decode slice header

               if ((state & 0x1f) == NAL_IDR_SLICE || ((state >> 5) &0x3) == 0) {

                    /* IDR or disposable slice

                     * No need to decode many bytesbecause MMCOs shall not be present. */

                    if (src_length > 60)

                       src_length = 60;

               } else {

                    /* To decode up to MMCOs */

                    if (src_length > 1000)

                        src_length = 1000;

               }

               break;

           }

           //解析NAL Header,获得nal_unit_type等信息

           ptr = ff_h264_decode_nal(h, buf + buf_index, &dst_length,

                                     &consumed,src_length);

           if (!ptr || dst_length < 0)

               break;

     

           buf_index += consumed;

           //初始化GetBitContext

           //H264Context->gb

           //后面的解析都是从这里获取数据

           init_get_bits(&h->gb, ptr, 8 * dst_length);

           switch (h->nal_unit_type) {

           case NAL_SPS:

                    //解析SPS

               ff_h264_decode_seq_parameter_set(h);

               break;

           case NAL_PPS:

                    //解析PPS

               ff_h264_decode_picture_parameter_set(h, h->gb.size_in_bits);

               break;

           case NAL_SEI:

                    //解析SEI

               ff_h264_decode_sei(h);

               break;

           case NAL_IDR_SLICE:

                    //如果是IDR Slice

                     //赋值AVCodecParserContext的key_frame为1

               s->key_frame = 1;

     

               h->prev_frame_num        = 0;

               h->prev_frame_num_offset = 0;

               h->prev_poc_msb          =

               h->prev_poc_lsb          = 0;

           /* fall through */

           case NAL_SLICE:

                    //获取Slice的一些信息

                    //跳过first_mb_in_slice这一字段

               get_ue_golomb_long(&h->gb); // skip first_mb_in_slice

               //获取帧类型(I,B,P)

               slice_type   =get_ue_golomb_31(&h->gb);

               //赋值到AVCodecParserContext的pict_type(外部可以访问到)

               s->pict_type = golomb_to_pict_type[slice_type % 5];

               //关键帧

               if (h->sei_recovery_frame_cnt >= 0) {

                    /* key frame, sincerecovery_frame_cnt is set */

                        //赋值AVCodecParserContext的key_frame为1

                    s->key_frame = 1;

               }

               //获取 PPS ID

               pps_id = get_ue_golomb(&h->gb);

               if (pps_id >= MAX_PPS_COUNT) {

                    av_log(h->avctx,AV_LOG_ERROR,

                           "pps_id %u out ofrange\n", pps_id);

                    return -1;

               }

               if (!h->pps_buffers[pps_id]) {

                    av_log(h->avctx,AV_LOG_ERROR,

                           "non-existing PPS%u referenced\n", pps_id);

                    return -1;

               }

               h->pps = *h->pps_buffers[pps_id];

               if (!h->sps_buffers[h->pps.sps_id]) {

                    av_log(h->avctx,AV_LOG_ERROR,

                           "non-existing SPS%u referenced\n", h->pps.sps_id);

                    return -1;

               }

               h->sps       =*h->sps_buffers[h->pps.sps_id];

               h->frame_num = get_bits(&h->gb, h->sps.log2_max_frame_num);

     

               if(h->sps.ref_frame_count <= 1 && h->pps.ref_count[0]<= 1 && s->pict_type == AV_PICTURE_TYPE_I)

                    s->key_frame = 1;

               //获得“型”和“级”

               //赋值到AVCodecContext的profile和level

               avctx->profile = ff_h264_get_profile(&h->sps);

               avctx->level   =h->sps.level_idc;

     

               if (h->sps.frame_mbs_only_flag) {

                    h->picture_structure =PICT_FRAME;

               } else {

                    if (get_bits1(&h->gb)) {// field_pic_flag

                        h->picture_structure =PICT_TOP_FIELD + get_bits1(&h->gb); // bottom_field_flag

                    } else {

                        h->picture_structure =PICT_FRAME;

                    }

               }

     

               if (h->nal_unit_type == NAL_IDR_SLICE)

                    get_ue_golomb(&h->gb);/* idr_pic_id */

               if (h->sps.poc_type == 0) {

                    h->poc_lsb =get_bits(&h->gb, h->sps.log2_max_poc_lsb);

     

                    if (h->pps.pic_order_present== 1 &&

                        h->picture_structure ==PICT_FRAME)

                        h->delta_poc_bottom =get_se_golomb(&h->gb);

               }

     

               if (h->sps.poc_type == 1 &&

                   !h->sps.delta_pic_order_always_zero_flag) {

                    h->delta_poc[0] =get_se_golomb(&h->gb);

     

                    if (h->pps.pic_order_present== 1 &&

                        h->picture_structure == PICT_FRAME)

                        h->delta_poc[1] =get_se_golomb(&h->gb);

               }

     

               /* Decode POC of this picture.

                * The prev_ values needed for decoding POC of the next picture are notset here. */

               field_poc[0] = field_poc[1] = INT_MAX;

               ff_init_poc(h, field_poc, &s->output_picture_number);

     

               /* Continue parsing to check if MMCO_RESET is present.

                * FIXME: MMCO_RESET could appear in non-first slice.

                *        Maybe, we should parseall undisposable non-IDR slice of this

                *        picture untilencountering MMCO_RESET in a slice of it. */

               if (h->nal_ref_idc && h->nal_unit_type != NAL_IDR_SLICE) {

                    got_reset = scan_mmco_reset(s);

                    if (got_reset < 0)

                        return got_reset;

               }

     

               /* Set up the prev_ values for decoding POC of the next picture. */

               h->prev_frame_num        =got_reset ? 0 : h->frame_num;

               h->prev_frame_num_offset = got_reset ? 0 : h->frame_num_offset;

               if (h->nal_ref_idc != 0) {

                    if (!got_reset) {

                       h->prev_poc_msb =h->poc_msb;

                        h->prev_poc_lsb =h->poc_lsb;

                    } else {

                        h->prev_poc_msb = 0;

                        h->prev_poc_lsb =

                            h->picture_structure== PICT_BOTTOM_FIELD ? 0 : field_poc[0];

                    }

               }

               //包含“场”概念的时候,先不管

               if (h->sps.pic_struct_present_flag) {

                    switch (h->sei_pic_struct) {

                    case SEI_PIC_STRUCT_TOP_FIELD:

                    caseSEI_PIC_STRUCT_BOTTOM_FIELD:

                        s->repeat_pict = 0;

                        break;

                    case SEI_PIC_STRUCT_FRAME:

                    case SEI_PIC_STRUCT_TOP_BOTTOM:

                    case SEI_PIC_STRUCT_BOTTOM_TOP:

                        s->repeat_pict = 1;

                        break;

                    caseSEI_PIC_STRUCT_TOP_BOTTOM_TOP:

                    caseSEI_PIC_STRUCT_BOTTOM_TOP_BOTTOM:

                        s->repeat_pict = 2;

                        break;

                    case SEI_PIC_STRUCT_FRAME_DOUBLING:

                        s->repeat_pict = 3;

                        break;

                    caseSEI_PIC_STRUCT_FRAME_TRIPLING:

                        s->repeat_pict = 5;

                        break;

                    default:

                       s->repeat_pict =h->picture_structure == PICT_FRAME ? 1 : 0;

                        break;

                    }

               } else {

                    s->repeat_pict =h->picture_structure == PICT_FRAME ? 1 : 0;

               }

     

               if (h->picture_structure == PICT_FRAME) {

                    s->picture_structure =AV_PICTURE_STRUCTURE_FRAME;

                    if(h->sps.pic_struct_present_flag) {

                        switch(h->sei_pic_struct) {

                        case SEI_PIC_STRUCT_TOP_BOTTOM:

                        caseSEI_PIC_STRUCT_TOP_BOTTOM_TOP:

                            s->field_order =AV_FIELD_TT;

                            break;

                        caseSEI_PIC_STRUCT_BOTTOM_TOP:

                        case SEI_PIC_STRUCT_BOTTOM_TOP_BOTTOM:

                            s->field_order =AV_FIELD_BB;

                            break;

                        default:

                            s->field_order =AV_FIELD_PROGRESSIVE;

                            break;

                        }

                   } else {

                        if (field_poc[0] <field_poc[1])

                            s->field_order =AV_FIELD_TT;

                        else if (field_poc[0] >field_poc[1])

                            s->field_order =AV_FIELD_BB;

                        else

                            s->field_order =AV_FIELD_PROGRESSIVE;

                    }

               } else {

                    if (h->picture_structure ==PICT_TOP_FIELD)

                        s->picture_structure =AV_PICTURE_STRUCTURE_TOP_FIELD;

                    else

                        s->picture_structure =AV_PICTURE_STRUCTURE_BOTTOM_FIELD;

                    s->field_order =AV_FIELD_UNKNOWN;

               }

     

               return 0; /* no need to evaluate the rest */

           }

        }

       if (q264)

            return 0;

       /* didn't find a picture! */

       av_log(h->avctx, AV_LOG_ERROR, "missing picture in access unitwith size %d\n", buf_size);

       return -1;

    }


    从源代码可以看出,parse_nal_units()主要做了以下几步处理:

    (1)对于所有的NALU,都调用ff_h264_decode_nal解析NALU的Header,得到nal_unit_type等信息
    (2)根据nal_unit_type的不同,调用不同的解析函数进行处理。例如:

    a)解析SPS的时候调用ff_h264_decode_seq_parameter_set()

    b)解析PPS的时候调用ff_h264_decode_picture_parameter_set()

    c)解析SEI的时候调用ff_h264_decode_sei()

    d)解析IDR Slice /Slice的时候,获取slice_type等一些信息。

     

    ff_h264_decode_nal()

    ff_h264_decode_nal()用于解析NAL Header,获得nal_unit_type等信息。该函数的定义位于libavcodec\h264.c,如下所示。

    //解析NAL Header,获得nal_unit_type等信息

    const uint8_t*ff_h264_decode_nal(H264Context *h, const uint8_t *src,

                                      int*dst_length, int *consumed, int length)

    {

       int i, si, di;

       uint8_t *dst;

       int bufidx;

     

       // src[0]&0x80; // forbidden bit

       //

       // 1 byte NALU头

       // forbidden_zero_bit: 1bit

       // nal_ref_idc: 2bit

       // nal_unit_type: 5bit

       // nal_ref_idc指示NAL的优先级,取值0-3,值越高,代表NAL越重要

       h->nal_ref_idc   = src[0]>> 5;

       // nal_unit_type指示NAL的类型

       h->nal_unit_type = src[0] & 0x1F;

       //后移1Byte

       src++;

       //未处理数据长度减1

       length--;

     

       //起始码:0x000001

       //保留:0x000002

       //防止竞争:0x000003

       //既表示NALU的开始,又表示NALU的结束

       //STARTCODE_TEST这个宏在后面用到

       //得到length

       //length是指当前NALU单元长度,这里不包括nalu头信息长度(即1个字节)

    #define STARTCODE_TEST                                                 \

       if (i + 2 < length && src[i + 1] == 0 && src[i + 2]<= 3) {         \

           if (src[i + 2] != 3 && src[i + 2] != 0) {                       \

               /* 取值为1或者2(保留用),为起始码。startcode, so we must be past the end */\

               length = i;                                                \

           }                                                              \

           break;                                                         \

        }

     

    #if HAVE_FAST_UNALIGNED

    #define FIND_FIRST_ZERO                                                \

        if (i > 0 && !src[i])                                              \

           i--;                                                           \

       while (src[i])                                                     \

           i++

     

    #if HAVE_FAST_64BIT

        for(i = 0; i + 1 < length; i += 9) {

           if (!((~AV_RN64A(src + i) &

                   (AV_RN64A(src + i) -0x0100010001000101ULL)) &

                  0x8000800080008080ULL))

               continue;

           FIND_FIRST_ZERO;

           STARTCODE_TEST;

           i -= 7;

        }

    #else

       for (i = 0; i + 1 < length; i += 5) {

           if (!((~AV_RN32A(src + i) &

                   (AV_RN32A(src + i) -0x01000101U)) &

                  0x80008080U))

               continue;

           FIND_FIRST_ZERO;

           STARTCODE_TEST;

           i -= 3;

        }

    #endif

    #else

       for (i = 0; i + 1 < length; i += 2) {

           if (src[i])

               continue;

           if (i > 0 && src[i - 1] == 0)

               i--;

           //起始码检测

           STARTCODE_TEST;

        }

    #endif

     

       // use second escape buffer for inter data

       bufidx = h->nal_unit_type == NAL_DPC ? 1 : 0;

     

       av_fast_padded_malloc(&h->rbsp_buffer[bufidx],&h->rbsp_buffer_size[bufidx], length+MAX_MBPAIR_SIZE);

       dst = h->rbsp_buffer[bufidx];

     

       if (!dst)

           return NULL;

     

       if(i>=length-1){ //no escaped 0

           *dst_length= length;

           *consumed= length+1; //+1 for the header

           if(h->avctx->flags2 & CODEC_FLAG2_FAST){

               return src;

           }else{

               memcpy(dst, src, length);

               return dst;

           }

        }

     

       memcpy(dst, src, i);

       si = di = i;

       while (si + 2 < length) {

           // remove escapes (very rare 1:2^22)

           if (src[si + 2] > 3) {

               dst[di++] = src[si++];

               dst[di++] = src[si++];

           } else if (src[si] == 0 && src[si + 1] == 0 && src[si +2] != 0) {

               if (src[si + 2] == 3) { // escape

                    dst[di++]  = 0;

                    dst[di++]  = 0;

                    si        += 3;

                    continue;

               } else // next start code

                    goto nsc;

           }

     

           dst[di++] = src[si++];

        }

       while (si < length)

           dst[di++] = src[si++];

     

    nsc:

       memset(dst + di, 0, FF_INPUT_BUFFER_PADDING_SIZE);

     

       *dst_length = di;

       *consumed   = si + 1; // +1 forthe header

       /* FIXME store exact number of bits in the getbitcontext

        * (it is needed for decoding) */

       return dst;

    }

    从源代码可以看出,ff_h264_decode_nal()首先从NALU Header(NALU第1个字节)中解析出了nal_ref_idc,nal_unit_type字段的值。然后函数进入了一个for()循环进行起始码检测。
        起始码检测这里稍微有点复杂,其中包含了一个STARTCODE_TEST的宏。这个宏用于做具体的起始码的判断。这部分的代码还没有细看,以后有时间再进行补充。

    ff_h264_decode_seq_parameter_set()

    ff_h264_decode_seq_parameter_set()用于解析H.264码流中的SPS。该函数的定义位于libavcodec\h264_ps.c,如下所示。

    //解码SPS

    int ff_h264_decode_seq_parameter_set(H264Context*h)

    {

       int profile_idc, level_idc, constraint_set_flags = 0;

       unsigned int sps_id;

       int i, log2_max_frame_num_minus4;

     

       SPS *sps;

       //profile型,8bit

       //注意get_bits()

       profile_idc           =get_bits(&h->gb, 8);

       constraint_set_flags |= get_bits1(&h->gb) << 0;   // constraint_set0_flag

       constraint_set_flags |= get_bits1(&h->gb) << 1;   // constraint_set1_flag

       constraint_set_flags |= get_bits1(&h->gb) << 2;   // constraint_set2_flag

       constraint_set_flags |= get_bits1(&h->gb) << 3;   // constraint_set3_flag

       constraint_set_flags |= get_bits1(&h->gb) << 4;   // constraint_set4_flag

       constraint_set_flags |= get_bits1(&h->gb) << 5;   // constraint_set5_flag

       skip_bits(&h->gb, 2);                             // reserved_zero_2bits

       //level级,8bit

       level_idc = get_bits(&h->gb, 8);

       //该SPS的ID号,该ID号将被picture引用

       //注意:get_ue_golomb()

       sps_id    =get_ue_golomb_31(&h->gb);

     

       if (sps_id >= MAX_SPS_COUNT) {

           av_log(h->avctx, AV_LOG_ERROR, "sps_id %u out of range\n",sps_id);

           return AVERROR_INVALIDDATA;

        }

       //赋值给这个结构体

       sps = av_mallocz(sizeof(SPS));

       if (!sps)

           return AVERROR(ENOMEM);

       //赋值

       sps->sps_id               = sps_id;

       sps->time_offset_length   = 24;

       sps->profile_idc          =profile_idc;

       sps->constraint_set_flags = constraint_set_flags;

       sps->level_idc            =level_idc;

       sps->full_range           = -1;

     

       memset(sps->scaling_matrix4, 16, sizeof(sps->scaling_matrix4));

       memset(sps->scaling_matrix8, 16, sizeof(sps->scaling_matrix8));

       sps->scaling_matrix_present = 0;

       sps->colorspace = 2; //AVCOL_SPC_UNSPECIFIED

       //Profile对应关系

       if (sps->profile_idc == 100 || // High profile

           sps->profile_idc == 110 ||  //High10 profile

           sps->profile_idc == 122 ||  //High422 profile

           sps->profile_idc == 244 ||  //High444 Predictive profile

           sps->profile_idc ==  44 ||  // Cavlc444 profile

           sps->profile_idc ==  83 ||  // Scalable Constrained High profile (SVC)

           sps->profile_idc ==  86 ||  // Scalable High Intra profile (SVC)

           sps->profile_idc == 118 ||  //Stereo High profile (MVC)

           sps->profile_idc == 128 ||  // MultiviewHigh profile (MVC)

           sps->profile_idc == 138 ||  //Multiview Depth High profile (MVCD)

           sps->profile_idc == 144) {  //old High444 profile

     

                 //色度取样

                 //0代表单色

                 //1代表4:2:0

                 //2代表4:2:2

                 //3代表4:4:4

           sps->chroma_format_idc = get_ue_golomb_31(&h->gb);

           if (sps->chroma_format_idc > 3U) {

               avpriv_request_sample(h->avctx, "chroma_format_idc %u",

                                     sps->chroma_format_idc);

               goto fail;

           } else if (sps->chroma_format_idc == 3) {

               sps->residual_color_transform_flag = get_bits1(&h->gb);

               if (sps->residual_color_transform_flag) {

                    av_log(h->avctx,AV_LOG_ERROR, "separate color planes are not supported\n");

                    goto fail;

               }

           }

           //bit_depth_luma_minus8

           //加8之后为亮度颜色深度

           //该值取值范围应该在0到4之间。即颜色深度支持0-12bit

           sps->bit_depth_luma   =get_ue_golomb(&h->gb) + 8;

           //加8之后为色度颜色深度

           sps->bit_depth_chroma = get_ue_golomb(&h->gb) + 8;

           if (sps->bit_depth_chroma != sps->bit_depth_luma) {

               avpriv_request_sample(h->avctx,

                                     "Different chroma and luma bit depth");

               goto fail;

           }

           if (sps->bit_depth_luma > 14U || sps->bit_depth_chroma >14U) {

               av_log(h->avctx, AV_LOG_ERROR, "illegal bit depth value (%d,%d)\n",

                       sps->bit_depth_luma,sps->bit_depth_chroma);

               goto fail;

            }

           sps->transform_bypass = get_bits1(&h->gb);

           decode_scaling_matrices(h, sps, NULL, 1,

                                   sps->scaling_matrix4, sps->scaling_matrix8);

        }else {

                 //默认

           sps->chroma_format_idc = 1;

           sps->bit_depth_luma    = 8;

           sps->bit_depth_chroma  = 8;

        }

       //log2_max_frame_num_minus4为另一个句法元素frame_num服务

       //fram_num的解码函数是ue(v),函数中的v 在这里指定:

       //     v =log2_max_frame_num_minus4 + 4

       //从另一个角度看,这个句法元素同时也指明了frame_num 的所能达到的最大值:

       //     MaxFrameNum = 2^( log2_max_frame_num_minus4+ 4 )

       log2_max_frame_num_minus4 = get_ue_golomb(&h->gb);

       if (log2_max_frame_num_minus4 < MIN_LOG2_MAX_FRAME_NUM - 4 ||

           log2_max_frame_num_minus4 > MAX_LOG2_MAX_FRAME_NUM - 4) {

           av_log(h->avctx, AV_LOG_ERROR,

                   "log2_max_frame_num_minus4out of range (0-12): %d\n",

                   log2_max_frame_num_minus4);

           goto fail;

        }

       sps->log2_max_frame_num = log2_max_frame_num_minus4 + 4;

       //pic_order_cnt_type 指明了poc (picture order count) 的编码方法

       //poc标识图像的播放顺序。

       //由于H.264使用了B帧预测,使得图像的解码顺序并不一定等于播放顺序,但它们之间存在一定的映射关系

       //poc 可以由frame-num 通过映射关系计算得来,也可以索性由编码器显式地传送。

       //H.264 中一共定义了三种poc 的编码方法

       sps->poc_type = get_ue_golomb_31(&h->gb);

       //3种poc的编码方法

       if (sps->poc_type == 0) { // FIXME #define

           unsigned t = get_ue_golomb(&h->gb);

           if (t>12) {

               av_log(h->avctx, AV_LOG_ERROR, "log2_max_poc_lsb (%d) is out ofrange\n", t);

               goto fail;

           }

           sps->log2_max_poc_lsb = t + 4;

        }else if (sps->poc_type == 1) { // FIXME #define

           sps->delta_pic_order_always_zero_flag = get_bits1(&h->gb);

           sps->offset_for_non_ref_pic          = get_se_golomb(&h->gb);

           sps->offset_for_top_to_bottom_field  = get_se_golomb(&h->gb);

           sps->poc_cycle_length                = get_ue_golomb(&h->gb);

     

           if ((unsigned)sps->poc_cycle_length >=

               FF_ARRAY_ELEMS(sps->offset_for_ref_frame)) {

               av_log(h->avctx, AV_LOG_ERROR,

                       "poc_cycle_lengthoverflow %d\n", sps->poc_cycle_length);

               goto fail;

           }

     

           for (i = 0; i < sps->poc_cycle_length; i++)

               sps->offset_for_ref_frame[i] = get_se_golomb(&h->gb);

        }else if (sps->poc_type != 2) {

           av_log(h->avctx, AV_LOG_ERROR, "illegal POC type %d\n",sps->poc_type);

           goto fail;

        }

       //num_ref_frames 指定参考帧队列可能达到的最大长度,解码器依照这个句法元素的值开辟存储区,这个存储区用于存放已解码的参考帧,

       //H.264 规定最多可用16 个参考帧,因此最大值为16。

       sps->ref_frame_count = get_ue_golomb_31(&h->gb);

       if (h->avctx->codec_tag == MKTAG('S', 'M', 'V', '2'))

           sps->ref_frame_count = FFMAX(2, sps->ref_frame_count);

       if (sps->ref_frame_count > H264_MAX_PICTURE_COUNT - 2 ||

           sps->ref_frame_count > 16U) {

           av_log(h->avctx, AV_LOG_ERROR,

                   "too many reference frames%d\n", sps->ref_frame_count);

           goto fail;

        }

       sps->gaps_in_frame_num_allowed_flag = get_bits1(&h->gb);

       //加1后为图像宽(以宏块为单位)

       //以像素为单位图像宽度(亮度):width=mb_width*16

       sps->mb_width                      = get_ue_golomb(&h->gb) + 1;

       //加1后为图像高(以宏块为单位)

       //以像素为单位图像高度(亮度):height=mb_height*16

       sps->mb_height                     = get_ue_golomb(&h->gb) + 1;

       //检查一下

       if ((unsigned)sps->mb_width >= INT_MAX / 16 ||

           (unsigned)sps->mb_height >= INT_MAX / 16 ||

           av_image_check_size(16 * sps->mb_width,

                                16 *sps->mb_height, 0, h->avctx)) {

           av_log(h->avctx, AV_LOG_ERROR, "mb_width/heightoverflow\n");

           goto fail;

        }

     

       sps->frame_mbs_only_flag = get_bits1(&h->gb);

       if (!sps->frame_mbs_only_flag)

           sps->mb_aff = get_bits1(&h->gb);

       else

           sps->mb_aff = 0;

     

       sps->direct_8x8_inference_flag = get_bits1(&h->gb);

     

    #ifndef ALLOW_INTERLACE

       if (sps->mb_aff)

           av_log(h->avctx, AV_LOG_ERROR,

                   "MBAFF support notincluded; enable it at compile-time.\n");

    #endif

       //裁剪输出,没研究过

       sps->crop = get_bits1(&h->gb);

       if (sps->crop) {

           int crop_left   =get_ue_golomb(&h->gb);

            int crop_right  = get_ue_golomb(&h->gb);

           int crop_top    =get_ue_golomb(&h->gb);

           int crop_bottom = get_ue_golomb(&h->gb);

           int width  = 16 *sps->mb_width;

           int height = 16 * sps->mb_height * (2 - sps->frame_mbs_only_flag);

     

           if (h->avctx->flags2 & CODEC_FLAG2_IGNORE_CROP) {

               av_log(h->avctx, AV_LOG_DEBUG, "discarding sps cropping,original "

                                               "values are l:%d r:%d t:%d b:%d\n",

                       crop_left, crop_right,crop_top, crop_bottom);

     

               sps->crop_left   =

               sps->crop_right  =

               sps->crop_top    =

               sps->crop_bottom = 0;

           } else {

               int vsub   = (sps->chroma_format_idc == 1) ? 1 : 0;

               int hsub   =(sps->chroma_format_idc == 1 ||

                             sps->chroma_format_idc == 2) ? 1 : 0;

               int step_x = 1 << hsub;

               int step_y = (2 - sps->frame_mbs_only_flag) << vsub;

     

               if (crop_left & (0x1F >> (sps->bit_depth_luma > 8))&&

                    !(h->avctx->flags &CODEC_FLAG_UNALIGNED)) {

                    crop_left &= ~(0x1F>> (sps->bit_depth_luma > 8));

                    av_log(h->avctx, AV_LOG_WARNING,

                           "Reducing leftcropping to %d "

                           "chroma samples topreserve alignment.\n",

                           crop_left);

               }

     

               if (crop_left  >(unsigned)INT_MAX / 4 / step_x ||

                    crop_right >(unsigned)INT_MAX / 4 / step_x ||

                    crop_top   > (unsigned)INT_MAX / 4 / step_y ||

                    crop_bottom>(unsigned)INT_MAX / 4 / step_y ||

                    (crop_left + crop_right ) *step_x >= width ||

                    (crop_top  + crop_bottom) * step_y >= height

               ) {

                    av_log(h->avctx,AV_LOG_ERROR, "crop values invalid %d %d %d %d / %d %d\n", crop_left,crop_right, crop_top, crop_bottom, width, height);

                    goto fail;

                }

     

               sps->crop_left   =crop_left   * step_x;

               sps->crop_right  =crop_right  * step_x;

               sps->crop_top    =crop_top    * step_y;

               sps->crop_bottom = crop_bottom * step_y;

           }

        }else {

           sps->crop_left   =

           sps->crop_right  =

           sps->crop_top    =

           sps->crop_bottom =

           sps->crop        = 0;

        }

     

       sps->vui_parameters_present_flag = get_bits1(&h->gb);

       if (sps->vui_parameters_present_flag) {

           int ret = decode_vui_parameters(h, sps);

           if (ret < 0)

               goto fail;

        }

     

       if (!sps->sar.den)

           sps->sar.den = 1;

       //Debug的时候可以输出一些信息

       if (h->avctx->debug & FF_DEBUG_PICT_INFO) {

           static const char csp[4][5] = { "Gray", "420","422", "444" };

           av_log(h->avctx, AV_LOG_DEBUG,

                   "sps:%u profile:%d/%dpoc:%d ref:%d %dx%d %s %s crop:%u/%u/%u/%u %s %s%"PRId32"/%"PRId32" b%d reo:%d\n",

                   sps_id, sps->profile_idc,sps->level_idc,

                   sps->poc_type,

                   sps->ref_frame_count,

                   sps->mb_width,sps->mb_height,

                   sps->frame_mbs_only_flag ?"FRM" : (sps->mb_aff ? "MB-AFF" : "PIC-AFF"),

                   sps->direct_8x8_inference_flag? "8B8" : "",

                   sps->crop_left,sps->crop_right,

                   sps->crop_top,sps->crop_bottom,

                  sps->vui_parameters_present_flag ? "VUI" : "",

                   csp[sps->chroma_format_idc],

                   sps->timing_info_present_flag? sps->num_units_in_tick : 0,

                   sps->timing_info_present_flag? sps->time_scale : 0,

                   sps->bit_depth_luma,

                  sps->bitstream_restriction_flag ? sps->num_reorder_frames : -1

                   );

        }

       sps->new = 1;

     

       av_free(h->sps_buffers[sps_id]);

       h->sps_buffers[sps_id] = sps;

     

       return 0;

     

    fail:

       av_free(sps);

       return -1;

    }

    解析SPS源代码并不是很有“技术含量”。只要参考ITU-T的《H.264标准》就可以理解了,不再做过多详细的分析。

    ff_h264_decode_picture_parameter_set()

    ff_h264_decode_picture_parameter_set()用于解析H.264码流中的PPS。该函数的定义位于libavcodec\h264_ps.c,如下所示。

    //解码PPS

    intff_h264_decode_picture_parameter_set(H264Context *h, int bit_length)

    {

             //获取PPS ID

       unsigned int pps_id = get_ue_golomb(&h->gb);

       PPS *pps;

       SPS *sps;

       int qp_bd_offset;

       int bits_left;

     

       if (pps_id >= MAX_PPS_COUNT) {

           av_log(h->avctx, AV_LOG_ERROR, "pps_id %u out of range\n",pps_id);

           return AVERROR_INVALIDDATA;

        }

       //解析后赋值给PPS这个结构体

       pps = av_mallocz(sizeof(PPS));

       if (!pps)

           return AVERROR(ENOMEM);

       //该PPS引用的SPS的ID

       pps->sps_id = get_ue_golomb_31(&h->gb);

       if ((unsigned)pps->sps_id >= MAX_SPS_COUNT ||

           !h->sps_buffers[pps->sps_id]) {

           av_log(h->avctx, AV_LOG_ERROR, "sps_id %u out of range\n",pps->sps_id);

           goto fail;

        }

       sps = h->sps_buffers[pps->sps_id];

       qp_bd_offset = 6 * (sps->bit_depth_luma - 8);

       if (sps->bit_depth_luma > 14) {

           av_log(h->avctx, AV_LOG_ERROR,

                   "Invalid luma bitdepth=%d\n",

                   sps->bit_depth_luma);

           goto fail;

        }else if (sps->bit_depth_luma == 11 || sps->bit_depth_luma == 13) {

           av_log(h->avctx, AV_LOG_ERROR,

                   "Unimplemented luma bitdepth=%d\n",

                   sps->bit_depth_luma);

           goto fail;

        }

       //entropy_coding_mode_flag

       //0表示熵编码使用CAVLC,1表示熵编码使用CABAC

       pps->cabac             =get_bits1(&h->gb);

       pps->pic_order_present = get_bits1(&h->gb);

       pps->slice_group_count = get_ue_golomb(&h->gb) + 1;

       if (pps->slice_group_count > 1) {

           pps->mb_slice_group_map_type = get_ue_golomb(&h->gb);

           av_log(h->avctx, AV_LOG_ERROR, "FMO not supported\n");

           switch (pps->mb_slice_group_map_type) {

           case 0:

    #if 0

       |       for (i = 0; i <=num_slice_groups_minus1; i++)  |   |     |

       |           run_length[i]                               |1  |ue(v) |

    #endif

               break;

           case 2:

    #if 0

       |       for (i = 0; i <num_slice_groups_minus1; i++) { |  |      |

       |           top_left_mb[i]                              |1  |ue(v) |

       |          bottom_right_mb[i]                          |1  |ue(v) |

       |       }                                               |   |     |

    #endif

               break;

           case 3:

           case 4:

           case 5:

    #if 0

       |      slice_group_change_direction_flag               |1  |u(1) |

       |      slice_group_change_rate_minus1                  |1  |ue(v) |

    #endif

               break;

           case 6:

    #if 0

       |      slice_group_id_cnt_minus1                       |1  |ue(v) |

       |       for (i = 0; i <=slice_group_id_cnt_minus1; i++)|   |      |

       |           slice_group_id[i]                           |1 |u(v)  |

    #endif

               break;

           }

        }

       //num_ref_idx_l0_active_minus1 加1后指明目前参考帧队列的长度,即有多少个参考帧

       //读者可能还记得在SPS中有句法元素num_ref_frames 也是跟参考帧队列有关,它们的区

       //别是num_ref_frames 指明参考帧队列的最大值,解码器用它的值来分配内存空间;

       //num_ref_idx_l0_active_minus1 指明在这个队列中当前实际的、已存在的参考帧数目,这从它的名字

       //“active”中也可以看出来。

       pps->ref_count[0] = get_ue_golomb(&h->gb) + 1;

       pps->ref_count[1] = get_ue_golomb(&h->gb) + 1;

       if (pps->ref_count[0] - 1 > 32 - 1 || pps->ref_count[1] - 1> 32 - 1) {

           av_log(h->avctx, AV_LOG_ERROR, "reference overflow(pps)\n");

           goto fail;

        }

       //P Slice 是否使用加权预测?

       pps->weighted_pred                        =get_bits1(&h->gb);

       //B Slice 是否使用加权预测?

       pps->weighted_bipred_idc                  = get_bits(&h->gb, 2);

       //QP初始值。读取后需要加26

       pps->init_qp                              =get_se_golomb(&h->gb) + 26 + qp_bd_offset;

       //SP和SI的QP初始值(没怎么见过这两种帧)

       pps->init_qs                              =get_se_golomb(&h->gb) + 26 + qp_bd_offset;

       pps->chroma_qp_index_offset[0]            = get_se_golomb(&h->gb);

       pps->deblocking_filter_parameters_present = get_bits1(&h->gb);

       pps->constrained_intra_pred               = get_bits1(&h->gb);

       pps->redundant_pic_cnt_present           = get_bits1(&h->gb);

     

       pps->transform_8x8_mode = 0;

       // contents of sps/pps can change even if id doesn't, so reinit

       h->dequant_coeff_pps = -1;

       memcpy(pps->scaling_matrix4,h->sps_buffers[pps->sps_id]->scaling_matrix4,

              sizeof(pps->scaling_matrix4));

       memcpy(pps->scaling_matrix8,h->sps_buffers[pps->sps_id]->scaling_matrix8,

              sizeof(pps->scaling_matrix8));

     

       bits_left = bit_length - get_bits_count(&h->gb);

       if (bits_left > 0 && more_rbsp_data_in_pps(h, pps)) {

           pps->transform_8x8_mode = get_bits1(&h->gb);

           decode_scaling_matrices(h, h->sps_buffers[pps->sps_id], pps, 0,

                                   pps->scaling_matrix4, pps->scaling_matrix8);

           // second_chroma_qp_index_offset

           pps->chroma_qp_index_offset[1] = get_se_golomb(&h->gb);

        }else {

           pps->chroma_qp_index_offset[1] = pps->chroma_qp_index_offset[0];

        }

     

       build_qp_table(pps, 0, pps->chroma_qp_index_offset[0],sps->bit_depth_luma);

       build_qp_table(pps, 1, pps->chroma_qp_index_offset[1],sps->bit_depth_luma);

       if (pps->chroma_qp_index_offset[0] !=pps->chroma_qp_index_offset[1])

           pps->chroma_qp_diff = 1;

     

       if (h->avctx->debug & FF_DEBUG_PICT_INFO) {

           av_log(h->avctx, AV_LOG_DEBUG,

                   "pps:%u sps:%u %sslice_groups:%d ref:%u/%u %s qp:%d/%d/%d/%d %s %s %s %s\n",

                   pps_id, pps->sps_id,

                   pps->cabac ?"CABAC" : "CAVLC",

                   pps->slice_group_count,

                   pps->ref_count[0],pps->ref_count[1],

                   pps->weighted_pred ?"weighted" : "",

                   pps->init_qp,pps->init_qs, pps->chroma_qp_index_offset[0],pps->chroma_qp_index_offset[1],

                  pps->deblocking_filter_parameters_present ? "LPAR" :"",

                   pps->constrained_intra_pred ?"CONSTR" : "",

                  pps->redundant_pic_cnt_present ? "REDU" : "",

                   pps->transform_8x8_mode ?"8x8DCT" : "");

        }

     

       av_free(h->pps_buffers[pps_id]);

       h->pps_buffers[pps_id] = pps;

       return 0;

     

    fail:

       av_free(pps);

       return -1;

    }

    和解析SPS类似,解析PPS源代码并不是很有“技术含量”。只要参考ITU-T的《H.264标准》就可以理解,不再做过多详细的分析。

    ff_h264_decode_sei()

    ff_h264_decode_sei()用于解析H.264码流中的SEI。该函数的定义位于libavcodec\h264_sei.c,如下所示。

    //SEI补充增强信息单元

    int ff_h264_decode_sei(H264Context *h)

    {

       while (get_bits_left(&h->gb) > 16 &&show_bits(&h->gb, 16)) {

           int type = 0;

           unsigned size = 0;

           unsigned next;

           int ret  = 0;

     

           do {

               if (get_bits_left(&h->gb) < 8)

                    return AVERROR_INVALIDDATA;

               type += show_bits(&h->gb, 8);

           } while (get_bits(&h->gb, 8) == 255);

     

           do {

               if (get_bits_left(&h->gb) < 8)

                    return AVERROR_INVALIDDATA;

               size += show_bits(&h->gb, 8);

           } while (get_bits(&h->gb, 8) == 255);

     

           if (h->avctx->debug&FF_DEBUG_STARTCODE)

               av_log(h->avctx, AV_LOG_DEBUG, "SEI %d len:%d\n", type,size);

     

           if (size > get_bits_left(&h->gb) / 8) {

               av_log(h->avctx, AV_LOG_ERROR, "SEI type %d size %d truncated at%d\n",

                       type, 8*size,get_bits_left(&h->gb));

               return AVERROR_INVALIDDATA;

           }

           next = get_bits_count(&h->gb) + 8 * size;

     

           switch (type) {

           case SEI_TYPE_PIC_TIMING: // Picture timing SEI

               ret = decode_picture_timing(h);

               if (ret < 0)

                    return ret;

               break;

           case SEI_TYPE_USER_DATA_ITU_T_T35:

               if (decode_user_data_itu_t_t35(h, size) < 0)

                    return -1;

               break;

               //x264的编码参数信息一般都会存储在USER_DATA_UNREGISTERED

               //其他种类的SEI见得很少

           case SEI_TYPE_USER_DATA_UNREGISTERED:

               ret = decode_unregistered_user_data(h, size);

               if (ret < 0)

                    return ret;

               break;

           case SEI_TYPE_RECOVERY_POINT:

               ret = decode_recovery_point(h);

               if (ret < 0)

                    return ret;

               break;

           case SEI_TYPE_BUFFERING_PERIOD:

               ret = decode_buffering_period(h);

               if (ret < 0)

                    return ret;

               break;

           case SEI_TYPE_FRAME_PACKING:

               ret = decode_frame_packing_arrangement(h);

               if (ret < 0)

                   return ret;

               break;

           case SEI_TYPE_DISPLAY_ORIENTATION:

               ret = decode_display_orientation(h);

               if (ret < 0)

                    return ret;

               break;

           default:

               av_log(h->avctx, AV_LOG_DEBUG, "unknown SEI type %d\n",type);

           }

           skip_bits_long(&h->gb, next - get_bits_count(&h->gb));

     

           // FIXME check bits here

           align_get_bits(&h->gb);

        }

     

       return 0;

    }

    在《H.264官方标准》中,SEI的种类是非常多的。在ff_h264_decode_sei()中包含以下种类的SEI:

    SEI_TYPE_BUFFERING_PERIOD
    SEI_TYPE_PIC_TIMING
    SEI_TYPE_USER_DATA_ITU_T_T35
    SEI_TYPE_USER_DATA_UNREGISTERED
    SEI_TYPE_RECOVERY_POINT
    SEI_TYPE_FRAME_PACKING
    SEI_TYPE_DISPLAY_ORIENTATION

    其中的大部分种类的SEI信息我并没有接触过。唯一接触比较多的就是SEI_TYPE_USER_DATA_UNREGISTERED类型的信息了。使用X264编码视频的时候,会自动将配置信息以SEI_TYPE_USER_DATA_UNREGISTERED(用户数据未注册SEI)的形式写入码流。

    从ff_h264_decode_sei()的定义可以看出,该函数根据不同的SEI类型调用不同的解析函数。当SEI类型为SEI_TYPE_USER_DATA_UNREGISTERED的时候,就会调用decode_unregistered_user_data()函数。

     

    decode_unregistered_user_data()
    decode_unregistered_user_data()的定义如下所示。从代码可以看出该函数只是简单的提取了X264的版本信息。

    //x264的编码参数信息一般都会存储在USER_DATA_UNREGISTERED

    static intdecode_unregistered_user_data(H264Context *h, int size)

    {

       uint8_t user_data[16 + 256];

       int e, build, i;

     

       if (size < 16)

           return AVERROR_INVALIDDATA;

     

       for (i = 0; i < sizeof(user_data) - 1 && i < size; i++)

           user_data[i] = get_bits(&h->gb, 8);

       //user_data内容示例:x264 core 118

       //int sscanf(const char *buffer,const char *format,[argument ]...);

       //sscanf会从buffer里读进数据,依照format的格式将数据写入到argument里。

       user_data[i] = 0;

        e= sscanf(user_data + 16, "x264 - core %d", &build);

       if (e == 1 && build > 0)

           h->x264_build = build;

       if (e == 1 && build == 1 && !strncmp(user_data+16,"x264 - core 0000", 16))

           h->x264_build = 67;

     

       if (h->avctx->debug & FF_DEBUG_BUGS)

           av_log(h->avctx, AV_LOG_DEBUG, "userdata:\"%s\"\n", user_data + 16);

     

       for (; i < size; i++)

           skip_bits(&h->gb, 8);

     

       return 0;

    }

    解析Slice Header

    对于包含图像压缩编码的Slice,解析器(Parser)并不进行解码处理,而是简单提取一些Slice Header中的信息。该部分的代码并没有写成一个函数,而是直接写到了parse_nal_units()里面,截取出来如下所示。

    case NAL_IDR_SLICE:

                    //如果是IDR Slice

                    //赋值AVCodecParserContext的key_frame为1

               s->key_frame = 1;

     

               h->prev_frame_num        = 0;

               h->prev_frame_num_offset = 0;

               h->prev_poc_msb          =

               h->prev_poc_lsb          = 0;

           /* fall through */

           case NAL_SLICE:

                    //获取Slice的一些信息

                    //跳过first_mb_in_slice这一字段

               get_ue_golomb_long(&h->gb); // skip first_mb_in_slice

               //获取帧类型(I,B,P)

               slice_type   =get_ue_golomb_31(&h->gb);

               //赋值到AVCodecParserContext的pict_type(外部可以访问到)

               s->pict_type = golomb_to_pict_type[slice_type % 5];

               //关键帧

               if (h->sei_recovery_frame_cnt >= 0) {

                    /* key frame, sincerecovery_frame_cnt is set */

                        //赋值AVCodecParserContext的key_frame为1

                    s->key_frame = 1;

               }

               //获取 PPS ID

               pps_id = get_ue_golomb(&h->gb);

               if (pps_id >= MAX_PPS_COUNT) {

                    av_log(h->avctx,AV_LOG_ERROR,

                           "pps_id %u out ofrange\n", pps_id);

                    return -1;

               }

               if (!h->pps_buffers[pps_id]) {

                    av_log(h->avctx,AV_LOG_ERROR,

                           "non-existing PPS%u referenced\n", pps_id);

                    return -1;

               }

               h->pps = *h->pps_buffers[pps_id];

               if (!h->sps_buffers[h->pps.sps_id]) {

                    av_log(h->avctx,AV_LOG_ERROR,

                           "non-existing SPS%u referenced\n", h->pps.sps_id);

                    return -1;

               }

               h->sps       =*h->sps_buffers[h->pps.sps_id];

               h->frame_num = get_bits(&h->gb, h->sps.log2_max_frame_num);

     

               if(h->sps.ref_frame_count <= 1 && h->pps.ref_count[0]<= 1 && s->pict_type == AV_PICTURE_TYPE_I)

                    s->key_frame = 1;

               //获得“型”和“级”

               //赋值到AVCodecContext的profile和level

               avctx->profile = ff_h264_get_profile(&h->sps);

               avctx->level   =h->sps.level_idc;

     

               if (h->sps.frame_mbs_only_flag) {

                    h->picture_structure =PICT_FRAME;

               } else {

                    if (get_bits1(&h->gb)) {// field_pic_flag

                        h->picture_structure =PICT_TOP_FIELD + get_bits1(&h->gb); // bottom_field_flag

                    } else {

                        h->picture_structure =PICT_FRAME;

                    }

               }

     

               if (h->nal_unit_type == NAL_IDR_SLICE)

                    get_ue_golomb(&h->gb);/* idr_pic_id */

               if (h->sps.poc_type == 0) {

                    h->poc_lsb =get_bits(&h->gb, h->sps.log2_max_poc_lsb);

     

                    if (h->pps.pic_order_present== 1 &&

                        h->picture_structure ==PICT_FRAME)

                        h->delta_poc_bottom =get_se_golomb(&h->gb);

               }

     

               if (h->sps.poc_type == 1 &&

                   !h->sps.delta_pic_order_always_zero_flag) {

                    h->delta_poc[0] =get_se_golomb(&h->gb);

     

                    if (h->pps.pic_order_present== 1 &&

                        h->picture_structure ==PICT_FRAME)

                        h->delta_poc[1] =get_se_golomb(&h->gb);

               }

     

               /* Decode POC of this picture.

                * The prev_ values needed for decoding POC of the next picture are notset here. */

               field_poc[0] = field_poc[1] = INT_MAX;

               ff_init_poc(h, field_poc, &s->output_picture_number);

     

               /* Continue parsing to check if MMCO_RESET is present.

                * FIXME: MMCO_RESET could appear in non-first slice.

                *        Maybe, we should parse all undisposablenon-IDR slice of this

                *        picture untilencountering MMCO_RESET in a slice of it. */

               if (h->nal_ref_idc && h->nal_unit_type != NAL_IDR_SLICE) {

                    got_reset = scan_mmco_reset(s);

                    if (got_reset < 0)

                        return got_reset;

               }

     

               /* Set up the prev_ values for decoding POC of the next picture. */

               h->prev_frame_num        =got_reset ? 0 : h->frame_num;

               h->prev_frame_num_offset = got_reset ? 0 : h->frame_num_offset;

               if (h->nal_ref_idc != 0) {

                    if (!got_reset) {

                        h->prev_poc_msb =h->poc_msb;

                        h->prev_poc_lsb =h->poc_lsb;

                   } else {

                        h->prev_poc_msb = 0;

                        h->prev_poc_lsb =

                            h->picture_structure== PICT_BOTTOM_FIELD ? 0 : field_poc[0];

                    }

               }

    可以看出该部分代码提取了根据NALU Header、Slice Header中的信息赋值了一些字段,比如说AVCodecParserContext中的key_frame、pict_type,H264Context中的sps、pps、frame_num等等。

    展开全文
  • 投屏之flv解析H264

    2019-06-09 21:32:15
    前面研究完了flv的格式之后,顺便做一下flv中提取出H264的码流,就当练练手。 Flv格式如下: FlvHeader+TagSize+(Metadata,Video,Audio)tag+TagSize *******************编码已经完成,流程后面再补***********...

    前面研究完了flv的格式之后,顺便做一下flv中提取出H264的码流,就当练练手。

    Flv格式如下:

    FlvHeader+TagSize+(Metadata,Video,Audio)tag+TagSize

    *******************编码已经完成,流程后面再补***************

     

    展开全文
  • C++ 解析H264文件

    千次阅读 2016-12-21 15:11:00
    #include "stdafx.h" #include typedef enum { NALU_TYPE_SLICE = 1, NALU_TYPE_DPA = 2, NALU_TYPE_DPB = 3, NALU_TYPE_DPC = 4, NALU_TYPE_IDR = 5, NALU_TYPE_SEI = 6, NAL
  • 以读文件的方式解析H264裸码流

    千次阅读 2017-12-25 16:20:14
    H264 NALU类型标识NAL单元中的RBSP数据类型,其中,nal_unit_type为1, 2, 3, 4, 5及12的NAL单元称为VCL的NAL单元,其他类型的NAL单元为非VCL的NAL单元。 0:未规定 1:非IDR图像中不采用数据划分的片段 2:非...
  • 解析h264帧的sps和pps

    千次阅读 2018-04-17 08:45:09
    h264视频流只有I帧才有sps和pps头编码时候有一个参数选项,配置是否每个IDR前面都带有sps和pps信息。二 h264视频流的sps和pps中我们主要关注哪些信息?分辨率,profile类型,参考帧个数三 x264中的sps和pps结构体...
  • 磕磕绊绊的做了出来,也算为自己留个资料吧。先讲理论再上代码。...2. 找到ps包之后就要从它的格式入手开始解析,ps荷载h264是把一帧帧的数据打包传过来,一个完整的ps包会包含一帧的数据。 而h264的帧分为 i 帧和
  • ffmpeg解析h264文件,提取图片信息

    千次阅读 2018-08-28 16:30:06
    ffmpeg -i dw_20170906_001429_0.000000_0.000000/video_first.h264 -qscale:v 2 second/second_%08d.jpg
  • ffmpeg解析h264视频文件,保存为图片

    千次阅读 2019-12-02 21:47:58
    代码实现 #include <iostream> #include <cstdio> #ifdef __cplusplus extern "C" { ...libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libswscale/swsca...
  • 解析h264 sps中获取帧率和分辨率记录

    万次阅读 2015-03-12 23:13:08
    从live555中提取代码出来,可以成功获取分辨率和帧率,帧率主要是获取num_units_in_tick和time_scale两个参数;...如果h264的 profile是 main,那么在vui parameters里面, num_units_in_tick:1200 time_scale:60000,
  • Program Association Table :节目相关表 pid ==0x 00 Program Map Table: 节目映射表 有效载荷起始符: ptr [1] &=0x40 后>>4 即 看 47后面 0 4 6  0:没有pes头 else 有 ...1. 解析 PAT 找到 PMT pid。
  • 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分组头部
  • H264解析开源库H264Bitstream

    千次阅读 2016-10-26 21:46:44
    H264Bitstream:解析H264的开源库。对于理解H264码流结构有很好的帮助。 https://sourceforge.net/projects/h264bitstream/
  • H264解析工具

    2018-06-03 12:36:06
    win32控制台程序,H264裸流解析,丢包测试等功能,详情和使用说明移步:https://blog.csdn.net/u013752202
  • h264sps解析获取分辨率

    2019-05-17 17:21:47
    h264sps解析获取分辨率
  • h264格式解析

    千次阅读 2018-11-01 13:47:31
    以个人的理解解析一下h264的格式,无非就是一个一个的帧,在实际应用中,判断一个h264文件或者是h264流是否有问题可以按照以下格式解析来判断它的头部。 NAL头 + RBSP = 1帧 NAL头一共一字节   — — — ...
  • 函数h264_mp4toannexb_filter详解 1、ffmpeg中处理h264码流分为两种情况 a、没有extradata则直接把packet中的数据交给解码器 b、如果有extradata,则需要把sps和pps的数据分析出来,连同packet.data一起交给解码...
  • H264 sps pps解析

    热门讨论 2013-01-18 16:35:24
    可在linux下编译通过的c++ sps pps解析类。读取文件(包中有测试文件),解析结果显示在打印里。可自行修改。
  • H264的sps和pps解析源码

    热门讨论 2014-07-07 14:31:25
    本源码主要是完成了基于H264中的sps(0000000167)及pps(0000000168)的H264编码数据的解析 直接转换为结构体数据信息 方便使用 更能让大家对H264编码更深一步学习!如果有问题请联系我 谢谢!直接编译即可 请注意...
  • H264 sps pps 解析

    2017-08-08 10:39:31
    关于H264的结构解析,最基本的方法就是根据官方文档自己根据标准解析; 或者从网上直接找到开源函数解析出结构体; 这里介绍一个简单的方法:通过webrtc实现sps pps的解析; 当前的webrtc版本是58; ...
  • H264系列(4):h264协议帧头数据解析
  • H264解析

    千次阅读 2015-02-06 13:21:03
    H.264/AVC标准中,整个系统框架被分为了两个层面:视频编码层面(VCL)和网络抽象层面(NAL)。 VCL负责表示视频数据的内容,NAL则负责格式化数据并提供头信息,以保证数据适合各种信道和存储介质上的传输。 在VCL...
  • 文章目录解析的两种方式MediaCodec异步编码具体过程如下:1.配置Camera回调,获取preview的原始数据2.配置encoder和MediaFormat3.开启encoder4.获取到preview数据,并送如编码器5....H264格式编码是没有vps帧,是从...
  • h264 sps, pps解析代码

    2017-03-04 11:40:01
    纯c代码,在linux上验证,编译可直接运行。已验证分辨率: 模拟摄像头h264(海思编码):cif、2cif、d1 高清ipc h264编码数:cif, VGA、720P、1080P
  • 在webrtc中,解析H264的rtpFU_A的函数是: RtpDepacketizerH264::ParseFuaNalu 这里说明一下这个函数; static const size_t kNalHeaderSize = 1; static const size_t kFuAHeaderSize = 2; // NalHeaderSize + ...
  • H264-AAC 格式解析

    2020-03-27 10:15:27
    一、H264的一些概念 本文章不在于写多么专业的知识理论,只是为了记录自己的所见所闻并让初学者能从很简单快捷的认识到H264,不至于一说这些东西都高大上不容易理解。有句话说无人教入门很难,有人教三分钟即会。 ...
  • ffmpeg -i video_camera_2.h264 -s 960X604 -q:v 2 jpg/%8d.jpg
  • H264的解码解析

    千次阅读 2018-03-06 16:35:42
    1:nal的作用:简称网络抽象层,负责H264的格式化数据并且提供头信息,以保证数据适合各种信和存储介质上的传输。 nal的结构是: NAL头+RBSP(所谓的RBSP是原始编码数据后面加了结尾比特) =1帧数据 RBSP为数据块...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 19,934
精华内容 7,973
关键字:

解析h264