欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Gb28181之Ps流解析H264

程序员文章站 2022-07-06 12:57:36
...

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

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

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

Gb28181之Ps流解析H264

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

Gb28181之Ps流解析H264

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()                
}                

Gb28181之Ps流解析H264

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()}

}

Gb28181之Ps流解析H264

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个字节。

Gb28181之Ps流解析H264

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个字节

Gb28181之Ps流解析H264

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

Gb28181之Ps流解析H264

这段是符合上述标准的。

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