对海康28181摄像头PS流解码的支持(三)

  上次分析处理PS流的结构后,仔细观察了下具体的PS包结构,如下图:

 

在pes包中跳过pes header后即是h264的码流了,如图圆圈所示。头四个pes包携带h264帧类型即为sps,pps,sei和I帧,如图小圈标记的。解析代码如下:

 

#ifndef PROGRAM_STRAM_PARSER_H
#define PROGRAM_STRAM_PARSER_H
#include <stdio.h>
#include <vector>
enum enCurrentState
{
    enCurrentState_parsing_ph,
    enCurrentState_parsing_sh,
    enCureentState_parsing_pes
};

#define PACK_START_CODE          0x000001BA
#define SYSTEM_HEADER_START_CODE 0x000001BB
#define PACKET_START_CODE_PREFIX 0x00000100

#define PES_START_CODE_PREFIX    0x01

struct SRawVideoFrame
{
    SRawVideoFrame()
    {
        bMark = false;
        pRawVideoFrame = NULL;
        iRawVideoSize = 0;
        type = 0;
    }

    ~SRawVideoFrame()
    {
        if (NULL != pRawVideoFrame)
        {
            delete[] pRawVideoFrame;
            pRawVideoFrame = NULL;
        }
    }

    bool bMark;
    unsigned char* pRawVideoFrame;
    int iRawVideoSize;
    //0表示完整的一帧数据 1表示一帧数据的开头
    int type;
};

class C28181PsStreamParser
{
    public:
        C28181PsStreamParser()
        {}

        void SetData(unsigned char* pStreamData, int iSize)
        {
            m_pStreamData = pStreamData;
            m_iSize = iSize;
        }

        C28181PsStreamParser(unsigned char* pStreamData,int iSize) :m_enState(enCurrentState_parsing_ph),
            m_pStreamData(pStreamData),m_iSize(iSize), m_iOffset(0), m_iPayloadRemianLength(0)
        {
        }

        ~C28181PsStreamParser()
        {
            for (int i = 0; i < m_vecRawVideoFrames.size(); ++i)
            {
                if (NULL != m_vecRawVideoFrames[i])
                {
                    delete m_vecRawVideoFrames[i];
                    m_vecRawVideoFrames[i] = NULL;
                }
            }
        }

        //获取当前解析出来的数据帧
        void GetRawVideoFrames(std::vector<SRawVideoFrame*>& pVideoFrames)
        {
            pVideoFrames = m_vecRawVideoFrames;
        }

    public:
        int Parse(const unsigned char* pData);
        int GetRemainLength() { return m_iPayloadRemianLength; }
    private:
        static inline bool isPacketStartCode(unsigned code) 
        {
            return (code & 0xFFFFFF00) == PACKET_START_CODE_PREFIX
                && code > SYSTEM_HEADER_START_CODE;
        }

    private:
        int ParsePackHeader();
        void ParseSystemHeader();
        int PasePESPacket();
    private:
        unsigned int test4Bytes();
        void skipBytes(int num);
        unsigned short get2Bytes();
        unsigned char get1Byte();
        unsigned char* get3Bytes();

    private:
        enCurrentState m_enState;
        unsigned char* m_pStreamData;
        int m_iSize;
        int m_iBits;
        int m_iOffset;

        int m_iPayloadRemianLength;

        //unsigned char *m_pRawVideoData;
        //int m_iRawVideoDataSize;
        std::vector<SRawVideoFrame*> m_vecRawVideoFrames;
};
#endif

 

#include<string.h>
#include "programstreamparser.h"

int C28181PsStreamParser::Parse(const unsigned char* pData)
{
    do 
    {
        if (m_iOffset >= m_iSize)
        {//解析完毕就退出
            return 0;
        }

        switch (m_enState)
        {//状态机
            case enCurrentState_parsing_ph:
            {
                ParsePackHeader();
                break;
            }
            case enCurrentState_parsing_sh:
            {
                ParseSystemHeader();
                break;
            }
            case enCureentState_parsing_pes:
            {
                PasePESPacket();
                break;
            }
            default:
                break;
        }
    } while (1);
}

int C28181PsStreamParser::ParsePackHeader()
{
    int ioffset = 0;
    unsigned int first4Bytes;
    
    while (1)
    {
        first4Bytes = test4Bytes();
        if (PACK_START_CODE == first4Bytes)
        {//判断是否是头,跳过头
            skipBytes(4);
            break;
        }
        else if (first4Bytes == SYSTEM_HEADER_START_CODE)
        {
            m_enState = enCurrentState_parsing_sh;
            return 0;
        }
        else
        {

            m_enState = enCureentState_parsing_pes;
            return 0;
        }
    }

    //跳过9字节
    skipBytes(9);
    //取stuffing_length
    unsigned char* pLengthData = m_pStreamData + m_iOffset;
    unsigned char uLenghth = pLengthData[0] & 7;
    skipBytes(1);
    skipBytes(uLenghth);
    
    m_enState = enCurrentState_parsing_sh;
}

void C28181PsStreamParser::ParseSystemHeader()
{
    unsigned StartCode = test4Bytes();
    if (StartCode != SYSTEM_HEADER_START_CODE)
    {
        m_enState = enCureentState_parsing_pes;
        return;
    }

    //跳过起始码
    skipBytes(4);
    unsigned short uHeaderLength = get2Bytes();
    //直接跳过
    skipBytes(uHeaderLength);
    
    m_enState = enCureentState_parsing_pes;
}


int C28181PsStreamParser::PasePESPacket()
{
    unsigned next4Bytes = test4Bytes();
    if (SYSTEM_HEADER_START_CODE == next4Bytes)
    {
        m_enState = enCurrentState_parsing_sh;
        return 0;
    }
    else if (PACK_START_CODE == next4Bytes)
    {
        m_enState = enCurrentState_parsing_ph;
        return 0;
    }
    else
    {
        unsigned char* pData = m_pStreamData + m_iOffset;
        if (pData[2] == 1 && pData[1] == 0 && pData[0] == 0)
        {
        }
        else
        {
            m_enState = enCurrentState_parsing_ph;
            return 0;
        }

    }

    //获取stream id
    unsigned char StreamID = get1Byte();
    unsigned char StreamNum = StreamID;

    //获取pes包长度
    unsigned short PESLength = get2Bytes();
    if (0xbc == StreamNum)
    {//这是一个packet stream map 
        skipBytes(2);
        //获取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);

    }
    else if (0xE0 == StreamNum)
    {//为视频数据
        //unsigned char* pData = get3Bytes();
        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(uPesHeaderLength);

        //payload数据的长度
        int iDataLength = PESLength - uPesHeaderLength - 3;
        //rtp包中还剩余的字节数
        int iRtpRemainLength = m_iSize - m_iOffset;
        if (iDataLength > iRtpRemainLength)
        {//payload的长度大于rtp包中剩余的字节数
            m_iPayloadRemianLength = iDataLength - iRtpRemainLength;
            m_enState = enCurrentState_parsing_ph;
            SRawVideoFrame *pVideoFrame = new SRawVideoFrame;
            pVideoFrame->bMark = false;
            pVideoFrame->pRawVideoFrame = new unsigned char[iRtpRemainLength];
            memcpy(pVideoFrame->pRawVideoFrame, m_pStreamData + m_iOffset, iRtpRemainLength);
            pVideoFrame->iRawVideoSize = iRtpRemainLength;
            pVideoFrame->type = 0;
            m_vecRawVideoFrames.push_back(pVideoFrame);

            m_iOffset += iRtpRemainLength;
            return m_iPayloadRemianLength;
        }
        else
        {//直接从该pes中取出一帧数据
            SRawVideoFrame *pVideoFrame = new SRawVideoFrame;
            pVideoFrame->pRawVideoFrame = new unsigned char[iDataLength];
            memcpy(pVideoFrame->pRawVideoFrame, m_pStreamData + m_iOffset, iDataLength);
            pVideoFrame->iRawVideoSize = iDataLength;
            pVideoFrame->bMark = true;
            pVideoFrame->type = 0;
            m_vecRawVideoFrames.push_back(pVideoFrame);

            m_iOffset += iDataLength;
        }
        
    }
    else if (0xC0 == StreamNum)
    {//为音频数据

    }

    m_enState = enCureentState_parsing_pes;
    return 0;
}

unsigned int C28181PsStreamParser::test4Bytes()
{
    unsigned char *ptr = m_pStreamData + m_iOffset;
    return (ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3];
}

void C28181PsStreamParser::skipBytes(int num)
{
    m_iOffset += num;
}

unsigned short C28181PsStreamParser::get2Bytes()
{
    unsigned char* ptr = m_pStreamData + m_iOffset;
    unsigned short ret = (ptr[0] << 8) | ptr[1];

    m_iOffset += 2;
    return ret;
}

unsigned char C28181PsStreamParser::get1Byte()
{
    unsigned char* ptr = m_pStreamData + m_iOffset;
    m_iOffset += 1;
    return ptr[0];
}

unsigned char* C28181PsStreamParser::get3Bytes()
{
    unsigned char* pData = new unsigned char[3];
    unsigned char* ptr = m_pStreamData + m_iOffset;
    memcpy(pData, ptr, 3);
    m_iOffset += 3;
    return pData;
}

 

以上代码只对视频流进行了解析,没有解析音频,处理音频也很简单,只要判断pes包中StreamNum值为0xC0即是音频包,在该例中,音频全部为g711A。当帧类型为I帧时,码流会被多个rtp包携带,所以涉及到组帧操作,在结构体SRawVideoFrame中的type字段用于表示当前是否为视频帧的开始部分。该海康设备发出的rtp流,每个rtp包都固定为1400个字节,所以I帧数据将近46个包,不同于FU-A的包,它不会添加FU-A的头,只是将数据分散到了多个rtp包中携带。通过长度判断数据包的个数后,直接拼接即可。需要注意的是在pes中带的h264流是带了nal头的,在一帧的开头需要去掉 nal头。

 

 

 

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

mo4776

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值