GB28181 码流解析( 五 )

本文档详细介绍了如何解析GB28181码流中的PS流,涉及PS流的结构,包括Pack包、PES Packet、系统头、PSM等,并提供了C++代码实现来解析音视频帧数据。
摘要由CSDN通过智能技术生成

                          GB28181 码流解析PS( 五 )

         通过上一节, 我们解析RTP取到了PS流, 本节继续解析PS, 获取到H264帧数据

         要解析PS流, 就需要研读《ISO/IEC 13818-1》标准文档,网上也有很多讲解PS流解析的, 但是介绍的

不是很清楚, 或者没有深入的理解,虽然也讲了一些, 但是总感觉差了一点东西, 所以我自己又详细阅读文档,

并测试通过, 在此介绍给大家, 希望能帮助到大家理解。

     

        

         

ps流由一个或多个音频,视频基础流组合而成, 这些音频,视频流按帧封装在PES Packet里面, 所以对PS流来说, 一个视频的PES Packet包含一个视频帧, 一个音频的PES Packet包含多个音频采样数据, 多个PES Packet组织成一个packs包, 多个packs组合成一个PS流。


这个逻辑关系需要搞清楚, 后面我们将以此为依据拆分出来一帧一帧的音视频帧数据。

接下来分析PS码流结构
一般PS如果作为文件存储容器格式, 会有一个结束标志 0x000001B9, GB28181是一个监控视频流不存在结束, 也就不会有结束标志
PS流由很多个Pack包组成, 每个Pack包有一个起始标志pack_start_code 0x000001BA, 跟着是9个固定字节的Pack包头, 然后跟着是填充字节长度和填充字节内容, 这个Pack包的包头后紧跟着是系统头 system_header_start_code 0x000001BB, 系统头内容很简单,系统头的长度 + 系统头内容, 系统头后面紧跟着是Program Stream Map (PSM), 这个map描述了ps里面包含的音频, 视频流的信息, psm的内容解析可以参考 ISO/IEC 13818-1 规范2.5.4, 也可以参考我提交的代码, PSM解析完了后, 后面就是我们要解析的音视频的PES Packet包了,pack包结构如下

视频 PES Packet包的起始码为 0x000001E0,  音频的 PES Packet包的起始码为 0x000001C0,

PES Packet包内容为PES packet length + PES Packet data,  PES Packet data = PES Packet Header + h264 data( 或者 audio data )PES Packet解析参考 ISO/IEC 13818-1 规范2.4.3.6

          通过上面的介绍, 大家就可以按照这个逻辑进行音视频码流解析了, 将关键代码粘贴如下:

// 输入参数为RTP解析后的PS数据字节, 及PS数据长度
int C28181PsStreamParser::Parse(unsigned char* pData, int size)
{
    SetData ( pData, size );
    do 
    {
        if (m_iOffset >= m_iSize)
        {
            //解析完毕就退出
            break;
        }

        ParseStartCode();

        switch (m_enState)
        {
        case enCurrentState_parsing_ph:
            {
                ParsePackHeader();
                break;
            }
        case enCurrentState_parsing_sh:
            {
                ParseSystemHeader();
                break;
            }
        case enCurrentState_parsing_sm:
            {
                ParseSystemMap();
                break;
            }
        case enCureentState_parsing_pes_video:
            {
                ParsePESVPacket();
                break;
            }
        case enCureentState_parsing_pes_audio:
            {
                ParsePESAPacket();
                break;
            }
        default:
            {
            }
            break;
        }
    } while (1);

    return 0;
}

int C28181PsStreamParser::ParseStartCode()
{
    // 状态检查,在丢包丢帧的情况下, 恢复状态机
    unsigned int first4Bytes;
    m_bStartPayload = true;

    if (  m_iSize - m_iOffset < 4 )
    {
        m_iOffset = m_iSize;
        return -1;
    }

    first4Bytes = test4Bytes();
    if ( PACK_START_CODE == first4Bytes )
    {
        // 跳过起始码
        skipBytes(4);
        m_enState = enCurrentState_parsing_ph;
    }
    else if ( SYSTEM_HEADER_START_CODE == first4Bytes )
    {
        // 跳过起始码
        skipBytes(4);
        m_enState = enCurrentState_parsing_sh;
    }
    else if ( SYSTEM_MAP == first4Bytes )
    {
        // 跳过起始码
        skipBytes(4);
        m_enState = enCurrentState_parsing_sm;
    }
    else if ( PES_VIDEO == first4Bytes )
    {
        // 跳过起始码
        skipBytes(4);
        m_enState = enCureentState_parsing_pes_video;
    }
    else if ( PES_AUDIO == first4Bytes )
    {
        // 跳过起始码
        skipBytes(4);
        m_enState = enCureentState_parsing_pes_audio;
    }
    else
    {
        if ( enCurrentState_parsing_none == m_enState )
        {
            // 跳过起始码
            skipBytes(4);
        }
    }

    return 0;
}

int C28181PsStreamParser::ParsePackHeader()
{
    //跳过9字节
    skipBytes(9);

    //取stuffing_length
    unsigned char* pLengthData = m_pStreamData + m_iOffset;
    unsigned char uLenghth = pLengthData[0] & 7;
    skipBytes(1);
    skipBytes(uLenghth);
 
    return 0;
}

void C28181PsStreamParser::ParseSystemHeader()
{
    unsigned short uHeaderLength = get2Bytes();

    //直接跳过
    skipBytes(uHeaderLength);
}

void C28181PsStreamParser::ParseSystemMap()
{
    skipBytes(4);
    //获取ps_info_length
    unsigned short PSInfoLength = get2Bytes();
    //跳过ps_info
    skipBytes(PSInfoLength);
    //获取es_map_length
    unsigned short ESMapLength = get2Bytes();
    unsigned char StreamType = get1Byte();
    if (0x1B == StreamType)
    {//H264

    }

    //跳过es map的长度
    skipBytes(-1);
    skipBytes(ESMapLength);
    skipBytes(4);
}


int C28181PsStreamParser::ParsePESVPacket()
{
    if ( 0 == m_iPayloadRemianLength )
    {
        unsigned int PESLength = get2Bytes();
        unsigned char* pData = m_pStreamData + m_iOffset;
        // 取PES header data的长度
        unsigned char uPesHeaderLength = pData[2];
        // 取PTS_DTS flag
        unsigned char PTSDTSflag = pData[1];
        PTSDTSflag >>= 6;
        PTSDTSflag &= 0x3;

        skipBytes(3);
        skipBytes(uPesHeaderLength);

        // payload数据的长度
        int iDataLength = PESLength - uPesHeaderLength - 3;
        if ( m_nalSize + iDataLength > m_maxNalSize )
        {
            m_maxNalSize = m_nalSize + iDataLength;
            m_nalData = (unsigned char*)realloc ( m_nalData, m_maxNalSize );
        }

        // rtp包中还剩余的字节数
        int iRtpRemainLength = m_iSize - m_iOffset;

        if (iDataLength > iRtpRemainLength)
        {
            // payload的长度大于rtp包中剩余的字节数
            m_iPayloadRemianLength = iDataLength - iRtpRemainLength;

            int nalType = m_pStreamData[m_iOffset + 4] & 0x1f;
            if ( nalType == 1 || nalType == 5 )
            {
                m_bIsNalSlice = true;
                m_bIsKeyFrame = (5 == nalType ) ? true : false;
            }

            memcpy ( m_nalData + m_nalSize, m_pStreamData + m_iOffset,
                iRtpRemainLength );
            m_nalSize += iRtpRemainLength;
            m_iOffset += iRtpRemainLength;

            return m_iPayloadRemianLength;
        }
        else
        {
            int nalType = m_pStreamData[m_iOffset + 4] & 0x1f;
            if ( nalType == 1 || nalType == 5 )
            {
                m_bIsNalSlice = true;
                m_bIsKeyFrame = (5 == nalType ) ? true : false;
            }

            if ( 7 == nalType )
            {
                int width = 0;
                int height = 0;
                int fps = 0;
                if ( h264_decode_sps ( m_pStreamData + m_iOffset + 4, iDataLength - 4,
                    width, height, fps ) )
                {
                    m_width = width;
                    m_height = height;
                    m_fps = fps;
                }

                MDISLogInfo ( "width %d, height %d, fps %d",
                    m_width, m_height, m_fps );
            }

            memcpy ( m_nalData + m_nalSize, m_pStreamData + m_iOffset, iDataLength );
            m_nalSize += iDataLength;
            m_iOffset += iDataLength;
            m_iPayloadRemianLength = 0;
            m_enState = enCurrentState_parsing_none;

            return 0;
        }
    }
    else
    {
        int payloadSize = m_iSize - m_iOffset;
        if ( m_iPayloadRemianLength > payloadSize )
        {
            memcpy ( m_nalData + m_nalSize, m_pStreamData, payloadSize );
            m_iOffset += payloadSize;
            m_nalSize += payloadSize;
            m_iPayloadRemianLength -= payloadSize;
            return 0;
        }
        else
        {
            memcpy ( m_nalData + m_nalSize, m_pStreamData, m_iPayloadRemianLength );
            m_nalSize += m_iPayloadRemianLength;
            m_iOffset += m_iPayloadRemianLength;
            m_iPayloadRemianLength = 0;
            m_enState = enCurrentState_parsing_none;
        }     
    }

    return 0;
}


int C28181PsStreamParser::ParsePESAPacket()
{
    unsigned short DataLength = get2Bytes();
    skipBytes(DataLength);
    m_enState = enCurrentState_parsing_none;

    return 0;
}
 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值