對海康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頭。

 

 

 

發佈了42 篇原創文章 · 獲贊 10 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章