国标PS流解包(解封装)代码

该代码最初的版本来自于互联网,首先感谢前辈无私分享的精神,这个PS流解析代码小巧和可读性好,是学习PS格式的一个很好的参考例子。但原来的代码有不少Bug,QuickGBLink在原先代码基础上做了很多问题修复和改进,现在对代码开源,希望能帮助更多国标开发者开发相关的功能。

实现的功能:

1. 解析PS包,从中提取出PES。

2. 解析PSM(节目流映射表),获得每个流的信息。

3. 对流格式做一些合法性判断和验证,检查数据是否完整。

4. 对实时流做了一些特殊处理,检查数据有无缺损或丢包,如果有则丢掉该块,从下一个PS头开始读起。

这个PS流解包的代码包含两个文件:streamdef.h, PsPacket.h,另外写了一个调用的例子(文件名:PsToEsConsole.cpp )。

streamdef.h文件

#ifndef _STREAMDEF_H_
#define _STREAMDEF_H_
#define MAX_PS_LENGTH  (1024*800)
#define MAX_PES_LENGTH (1024*1000)
#define MAX_ES_LENGTH  (0x100000)
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;

#pragma pack (1)

union littel_endian_size
{
	unsigned short int	length;
	unsigned char		byte[2];
};

struct pack_start_code
{
	unsigned char start_code[3];
	unsigned char stream_id[1];
};

typedef struct RTP_HEADER
{
#ifdef ORTP_BIGENDIAN
    uint16_t version:2;
    uint16_t padbit:1;
    uint16_t extbit:1;
    uint16_t cc:4;
    uint16_t markbit:1;
    uint16_t paytype:7;
#else
    uint16_t cc:4;
    uint16_t extbit:1;
    uint16_t padbit:1;
    uint16_t version:2;
    uint16_t paytype:7;  //负载类型
    uint16_t markbit:1;  //1表示前面的包为一个解码单元,0表示当前解码单元未结束
#endif
    uint16_t seq_number;  //序号
    uint32_t timestamp; //时间戳
    uint32_t ssrc;  //循环校验码
    //uint32_t csrc[16];
} RTP_header_t;
 
typedef struct ps_header{
    unsigned char pack_start_code[4];  //'0x000001BA'
 
    unsigned char system_clock_reference_base21:2;
    unsigned char marker_bit:1;
    unsigned char system_clock_reference_base1:3;
    unsigned char fix_bit:2;    //'01'
 
    unsigned char system_clock_reference_base22;
 
    unsigned char system_clock_reference_base31:2;
    unsigned char marker_bit1:1;
    unsigned char system_clock_reference_base23:5;
 
    unsigned char system_clock_reference_base32;
    unsigned char system_clock_reference_extension1:2;
    unsigned char marker_bit2:1;
    unsigned char system_clock_reference_base33:5; //system_clock_reference_base 33bit
 
    unsigned char marker_bit3:1;
    unsigned char system_clock_reference_extension2:7; //system_clock_reference_extension 9bit
 
    unsigned char program_mux_rate1;
 
    unsigned char program_mux_rate2;
    unsigned char marker_bit5:1;
    unsigned char marker_bit4:1;
    unsigned char program_mux_rate3:6;
 
    unsigned char pack_stuffing_length:3;
    unsigned char reserved:5;
}ps_header_t;  //14
 
typedef struct sh_header
{
    unsigned char system_header_start_code[4]; //32
 
    unsigned char header_length[2];            //16 uimsbf
 
    uint32_t marker_bit1:1;   //1  bslbf
    uint32_t rate_bound:22;   //22 uimsbf
    uint32_t marker_bit2:1;   //1 bslbf
    uint32_t audio_bound:6;   //6 uimsbf
    uint32_t fixed_flag:1;    //1 bslbf
    uint32_t CSPS_flag:1;     //1 bslbf
 
    uint16_t system_audio_lock_flag:1;  // bslbf
    uint16_t system_video_lock_flag:1;  // bslbf
    uint16_t marker_bit3:1;             // bslbf
    uint16_t video_bound:5;             // uimsbf
    uint16_t packet_rate_restriction_flag:1; //bslbf
    uint16_t reserved_bits:7;                //bslbf
    unsigned char reserved[6];
}sh_header_t; //18
 
//注意: 该PSM表结构有误,PSM表结构是可变长度的,包含的流的数目不同,长度也不一样。从22处开始后面的字段内容在不同情况下会有变化,详情请看PS格式中关于PSM的结构定义
typedef struct psm_header{
    unsigned char promgram_stream_map_start_code[4];
 
    unsigned char program_stream_map_length[2];
 
    unsigned char program_stream_map_version:5;
    unsigned char reserved1:2;
    unsigned char current_next_indicator:1;
 
    unsigned char marker_bit:1;
    unsigned char reserved2:7;
 
    unsigned char program_stream_info_length[2];
    unsigned char elementary_stream_map_length[2];
	//11: 下面可能会有多段Stream的定义,Stream的数目依据上面的elementary_stream_map_length的长度决定,StreamNum = elementary_stream_map_length/4
    unsigned char stream_type;
    unsigned char elementary_stream_id;
    unsigned char elementary_stream_info_length[2];
	//22:
    unsigned char CRC_32[4];
    unsigned char reserved[16];
}psm_header_t; 

struct program_stream_map
{
	pack_start_code PackStart;
	littel_endian_size PackLength;
};
 
typedef struct pes_header
{
    unsigned char pes_start_code_prefix[3];
    unsigned char stream_id;
    unsigned short PES_packet_length;
}pes_header_t; //6
 
typedef struct optional_pes_header{
    unsigned char original_or_copy:1;
    unsigned char copyright:1;
    unsigned char data_alignment_indicator:1;
    unsigned char PES_priority:1;
    unsigned char PES_scrambling_control:2;
    unsigned char fix_bit:2;
 
    unsigned char PES_extension_flag:1;
    unsigned char PES_CRC_flag:1;
    unsigned char additional_copy_info_flag:1;
    unsigned char DSM_trick_mode_flag:1;
    unsigned char ES_rate_flag:1;
    unsigned char ESCR_flag:1;
    unsigned char PTS_DTS_flags:2;
 
    unsigned char PES_header_data_length;
}optional_pes_header_t;
 
#pragma pack ()
 
 
enum PSStatus
{
    ps_padding, //未知状态
    ps_ps,      //ps状态
    ps_sh,
    ps_psm,
    ps_pes,
    ps_pes_video,
    ps_pes_audio
};

typedef struct _program_map_info
{
    unsigned char stream_type;
    unsigned char elementary_stream_id;
}program_map_info;
 
#endif

PsPacket.h文件

#ifndef _PSUNPACKET_H_
#define _PSUNPACKET_H_

#include 
#include "streamdef.h"
#include 
using namespace std;

#define MAX_VIDEO_BUFSIZE  (200*1024)

struct ESFrameData
{
	unsigned char*	buffer;
	int				len;
	int             frame_num; //缓冲区中帧的数目
	unsigned char	cFrameType;			//帧的类型, 'I'、 'P'、 'B'
	unsigned int	bDropFrame;			//是否丢掉该帧,如果有丢包,则丢掉
	//PSStatus        ps_status;   //PS的类型
	 __int64  first_pts_time; //帧缓存里可能含有多帧,记录第一帧的时间戳
	 __int64 ptsTime; //帧缓存里最后一帧的时间戳
	 __int64 dtsTime;
	 unsigned char pts_dts_flag; //一个PS帧由多个PES帧组成,其中开头帧有时间戳,值为2(有PTS)或3(有PTS和DTS),而除开头帧之外的其他帧,该值为0,并且PTS和DTS都为0.
};


//#include 
//using namespace std::tr1;

#ifndef AV_RB16
#   define AV_RB16(x)                           \
    ((((const unsigned char*)(x))[0] << 8) |          \
    ((const unsigned char*)(x))[1])
#endif
 
 
static inline unsigned __int64 ff_parse_pes_pts(const unsigned char* buf) {
    return (unsigned __int64)(*buf & 0x0e) << 29 |
        (AV_RB16(buf+1) >> 1) << 15 |
        AV_RB16(buf+3) >> 1;
}
 
static unsigned __int64 get_pts(optional_pes_header* option)
{
    if(option->PTS_DTS_flags != 2 && option->PTS_DTS_flags != 3 && option->PTS_DTS_flags != 0)
    {
        return 0;
    }
    if((option->PTS_DTS_flags & 2) == 2)
    {
        unsigned char* pts = (unsigned char*)option + sizeof(optional_pes_header);
        return ff_parse_pes_pts(pts);
    }
    return 0;
}
 
static unsigned __int64 get_dts(optional_pes_header* option)
{
    if(option->PTS_DTS_flags != 2 && option->PTS_DTS_flags != 3 && option->PTS_DTS_flags != 0)
    {
        return 0;
    }
    if((option->PTS_DTS_flags & 3) == 3)
    {
        unsigned char* dts = (unsigned char*)option + sizeof(optional_pes_header) + 5;
        return ff_parse_pes_pts(dts);
    }
    return 0;
}
 
bool inline is_ps_header(ps_header_t* ps)
{
    if(ps->pack_start_code[0] == 0 && ps->pack_start_code[1] == 0 && ps->pack_start_code[2] == 1 && ps->pack_start_code[3] == 0xBA)
        return true;
    return false;
}
bool inline is_sh_header(sh_header_t* sh)
{
    if(sh->system_header_start_code[0] == 0 && sh->system_header_start_code[1] == 0 && sh->system_header_start_code[2] == 1 && sh->system_header_start_code[3] == 0xBB)
        return true;
    return false;
}

//bool inline is_psm_header(psm_header_t* psm)
//{
//    if(psm->promgram_stream_map_start_code[0] == 0 && psm->promgram_stream_map_start_code[1] == 0 && psm->promgram_stream_map_start_code[2] == 1 && psm->promgram_stream_map_start_code[3] == 0xBC)
//        return true;
//    return false;
//}
bool inline is_psm_header(program_stream_map * psm)
{
	if(psm->PackStart.start_code[0] == 0 && psm->PackStart.start_code[1] == 0 && psm->PackStart.start_code[2] == 1 && psm->PackStart.stream_id[0] == 0xBC)
		return true;
	return false;
}

bool inline is_pes_video_header(pes_header_t* pes)
{
    if(pes->pes_start_code_prefix[0]==0 && pes->pes_start_code_prefix[1] == 0 && pes->pes_start_code_prefix[2] == 1 && (pes->stream_id == 0xE0 || pes->stream_id == 0xE2))
        return true;
    return false;
}
bool inline is_pes_audio_header(pes_header_t* pes)
{
    if(pes->pes_start_code_prefix[0]==0 && pes->pes_start_code_prefix[1] == 0 && pes->pes_start_code_prefix[2] == 1 && pes->stream_id == 0xC0)
        return true;
    return false;
}
 
bool inline is_pes_header(pes_header_t* pes)
{
    if(pes->pes_start_code_prefix[0]==0 && pes->pes_start_code_prefix[1] == 0 && pes->pes_start_code_prefix[2] == 1)
    {
        if(pes->stream_id == 0xC0 || (pes->stream_id == 0xE0 || pes->stream_id == 0xE2))
        {
            return true;
        }
    }
    return false;
}
PSStatus inline pes_type(pes_header_t* pes)
{
    if(pes->pes_start_code_prefix[0]==0 && pes->pes_start_code_prefix[1] == 0 && pes->pes_start_code_prefix[2] == 1)
    {
        if(pes->stream_id == 0xC0)
        {
            return ps_pes_audio;
        }
        else if(pes->stream_id == 0xE0 || pes->stream_id == 0xE2)
        {
            return ps_pes_video;
        }
    }
    return ps_padding;
}
 
static bool NextMpegStartCode(BYTE& code, char * pBuffer, int nLeftLen, int & nReadBytes)
{
	//BitByteAlign();
	int i = 0;
    nReadBytes = 0;

	DWORD dw = -1;
	do
	{
		if(nLeftLen-- == 0) return(false);
		dw = (dw << 8) | (BYTE)pBuffer[i++];
		nReadBytes+=1;;
	}
	while((dw&0xffffff00) != 0x00000100);
	code = (BYTE)(dw&0xff);
	return(true);
}



/*
_1 是否包含数据
_2 下一个PS状态
_3 数据指针
_4 数据长度
*/
//typedef std::tr1::tuple pes_tuple;

struct pes_tuple
{
	bool           bContainData;
	PSStatus       nStatus;
	pes_header_t * pPESPtr;
	bool  bDataLost; //是否丢包

	pes_tuple(bool bContain, PSStatus status, pes_header_t* pPes, bool bLost)
	{
      bContainData = bContain;
	  bDataLost  = bLost;
	  nStatus = status;
	  pPESPtr = pPes;
	}
};


/*
_1 是否包含数据
_2 数据类型
_3 PTS时间戳
_4 DTS时间戳
_5 数据指针
_6 数据长度
*/
//typedef std::tr1::tuple naked_tuple;

struct naked_tuple
{
	bool bContainData;
    unsigned char nStreamID;//0xE0-- 视频;0xC0--音频
	unsigned __int64 ptsTime;
	unsigned __int64 dtsTime;
	char*  pDataPtr;
	unsigned int nDataLen;
	bool  bDataLost; //是否丢包
	unsigned char pts_dts_flag; // 是否包含有PTS,DTS。如果包含PTS,值等于2;如果包含PTS和DTS,值等于3;如果没有包含PTS和DTS,值等于0

	naked_tuple(bool bContain, unsigned char nStreamType, unsigned __int64 pts,  unsigned char fpts_dts, unsigned __int64 dts, char* pData, unsigned int nLen, bool bLost)
	{
		bContainData	= bContain;
		nStreamID		= nStreamType;
		ptsTime			= pts;
		dtsTime         = dts;
		pDataPtr		= pData;
		nDataLen		= nLen;
		bDataLost       = bLost;
		pts_dts_flag = fpts_dts;
	}
};

class PSPacket
{
public:
    PSPacket()
    {
		m_pPSBuffer		= new char[MAX_PS_LENGTH];
		m_pPESBuffer	= new char[MAX_PES_LENGTH];
		m_pESBuffer		= new char[MAX_ES_LENGTH];

        m_status = ps_padding;
        m_nESLength = m_nPESIndicator = m_nPSWrtiePos = m_nPESLength = 0;

		m_bRealTime = false;
    }
	~PSPacket()
	{
		delete m_pPSBuffer;
        delete m_pPESBuffer;
		delete m_pESBuffer;
	}

	int GetWritePos()
	{
		return m_nPSWrtiePos;
	}

	int  GetReadPos()
	{
		return m_nPESIndicator;
	}

    void PSWrite(char* pBuffer, unsigned int sz)
    {
        if(m_nPSWrtiePos + sz < MAX_PS_LENGTH)
        {
            memcpy((m_pPSBuffer + m_nPSWrtiePos), pBuffer, sz);
            m_nPSWrtiePos += sz;
        }
        else
        {
            m_status = ps_padding;
            m_nESLength = m_nPESIndicator = m_nPSWrtiePos = m_nPESLength = 0;

			if(m_nPSWrtiePos + sz < MAX_PS_LENGTH)
			{
				memcpy((m_pPSBuffer + m_nPSWrtiePos), pBuffer, sz);
				m_nPSWrtiePos += sz;
			}
        }
 
    }

	//清空数据指针
	void  Empty()
	{
	    m_status = ps_padding;
        m_nESLength = m_nPESIndicator = m_nPSWrtiePos = m_nPESLength = 0;
		m_ProgramMap.clear();
	}

	//设置实时流的标志
	void SetRealTime(bool bRealTime)
	{
		m_bRealTime = bRealTime;
	}

	int GetEsData(ESFrameData * pVideoESFrame, ESFrameData * pAudioESFrame, BOOL & bDataLost)
	{
		int nRet = 0;
        int nStreamType = 0;
		unsigned char * pV = NULL; 
		unsigned char * pA = NULL;

		if (pVideoESFrame != NULL)
		{
			pV = pVideoESFrame->buffer;
			pVideoESFrame->frame_num = 0;
		}
		if (pAudioESFrame != NULL)
		{
			pA = pAudioESFrame->buffer;
			pAudioESFrame->frame_num = 0;
		}

		while(1)
		{

			naked_tuple es_packet = naked_payload();

			if(!es_packet.bContainData || es_packet.nDataLen == 0)
			{
				break;
			}

			if(es_packet.nStreamID == 0xE0 || es_packet.nStreamID == 0xE2)
			{
				if(pVideoESFrame != NULL)
				{
					ATLASSERT(pVideoESFrame->len + es_packet.nDataLen < MAX_VIDEO_BUFSIZE);

					if (pVideoESFrame->len + es_packet.nDataLen > MAX_VIDEO_BUFSIZE)
					{
						OutputDebugString("Data Len is over buffer size \n");
						break;
					}

					memcpy(pV, es_packet.pDataPtr, es_packet.nDataLen);
					pV += es_packet.nDataLen;
					pVideoESFrame->len += es_packet.nDataLen;

					pVideoESFrame->pts_dts_flag = es_packet.pts_dts_flag; //如果该值不为0,表示这是一个帧的帧头,并且带时间戳

					pVideoESFrame->ptsTime = es_packet.ptsTime;

					if(pVideoESFrame->frame_num == 0)
					     pVideoESFrame->first_pts_time = pVideoESFrame->ptsTime;

					pVideoESFrame->frame_num++;
				}

				if(es_packet.bDataLost)
					 bDataLost = TRUE;
				
				nStreamType |= 0x01;

				nRet = 1;
			}
			else if(es_packet.nStreamID == 0xC0)
			{
				if(pAudioESFrame != NULL)
				{
					ATLASSERT(pAudioESFrame->len + es_packet.nDataLen < 192*1024);

					memcpy(pA, es_packet.pDataPtr, es_packet.nDataLen);
					pA += es_packet.nDataLen;
					pAudioESFrame->len += es_packet.nDataLen;

					pAudioESFrame->ptsTime = es_packet.ptsTime;

					pAudioESFrame->pts_dts_flag = es_packet.pts_dts_flag;

					if (pAudioESFrame->frame_num == 0)
						pAudioESFrame->first_pts_time = pAudioESFrame->ptsTime;

					pAudioESFrame->frame_num++;
				}

				nStreamType |= 0x02;

				nRet = 1;
			}
		}

  //      if(nStreamType == 0)
		//{
		//	ATLTRACE("Error: GetEsData found no pes----------- \n");
		//}

		return nRet;
	}

    void RTPWrite(char* pBuffer, unsigned int sz)
    {
        char* data = (pBuffer + sizeof(RTP_header_t));
        unsigned int length =  sz - sizeof(RTP_header_t);
        if(m_nPSWrtiePos + length < MAX_PS_LENGTH)
        {
            memcpy((m_pPSBuffer + m_nPSWrtiePos), data, length);
            m_nPSWrtiePos += length;
        }
        else
        {
            m_status = ps_padding;
            m_nESLength = m_nPESIndicator = m_nPSWrtiePos = m_nPESLength = 0;

			if(m_nPSWrtiePos + length < MAX_PS_LENGTH)
			{
				memcpy((m_pPSBuffer + m_nPSWrtiePos), data, length);
				m_nPSWrtiePos += length;
			}
        }
    }

	//获取PS类型,用来区分视频和音频,注意函数声明为Static类型
	static PSStatus DetectPSType(BYTE * pBuffer, int nSize)
	{
		PSStatus _status = ps_padding;
		ps_header_t * _ps;
		pes_header_t * _pes;

		const int nEndPos = nSize - 1;
		int nReadPos = 0;

		while(nReadPos <  nEndPos)
		{
			int nRead = 0;
			BYTE b = 0;

			if(!NextMpegStartCode(b, (char*)pBuffer + nReadPos, nEndPos - nReadPos, nRead))
			{
				//ATLASSERT(0);
				break;
			}

			nReadPos += nRead - 4; //定位到开始码

			_ps = (ps_header_t*)(pBuffer + nReadPos);
			if(is_ps_header(_ps))
			{
				_status = ps_ps;
				nReadPos += sizeof(ps_header_t); continue;
			}

			if(b >= 0xbd && b < 0xf0) //合法的PES帧
			{
				_pes = (pes_header_t*)(pBuffer + nReadPos);

				//unsigned short PES_packet_length = ntohs(_pes->PES_packet_length);

				if (is_pes_header(_pes))
				{
					_status = ps_pes;
				}

				if(_status == ps_pes)
				{
					_status = pes_type(_pes);

					return _status;
				}
				else
				{
					nReadPos += 1;
				}

			} // if(b >= 0xbd && b < 0xf0)
			else
			{
				nReadPos += 4;
			}

		}//while

		//_status = ps_padding;

		return _status;
	}

    pes_tuple pes_payload()
    {
		//int nLastPos = m_nPESIndicator; //缓存旧的读取位置,如果下面读取不到指定类型的帧(ps_ps/ps_pes),则将读取指针要重新定位到旧的位置
     
		while(m_nPESIndicator <  m_nPSWrtiePos)
		{
			int nRead = 0;
            BYTE b = 0;

			if(!NextMpegStartCode(b, m_pPSBuffer + m_nPESIndicator, m_nPSWrtiePos - m_nPESIndicator, nRead))
			{
				//ATLASSERT(0);
				break;
			}

		    m_nPESIndicator += nRead - 4; //定位到开始码

			m_ps = (ps_header_t*)(m_pPSBuffer + m_nPESIndicator);
			if(is_ps_header(m_ps))
			{
				m_status = ps_ps;
				m_nPESIndicator += sizeof(ps_header_t); continue;
			}

			if(b >= 0xbd && b < 0xf0) //合法的PES帧
			{
		        m_pes = (pes_header_t*)(m_pPSBuffer + m_nPESIndicator);

				unsigned short PES_packet_length = ntohs(m_pes->PES_packet_length);

				if (is_pes_header(m_pes))
				{
					m_status = ps_pes; //修改当前状态为ps_pes
				}

				if(m_status == ps_pes)
				{
					if(b == 0xbe || b == 0xbf)
					{
						 m_nPESIndicator += PES_packet_length + sizeof(pes_header_t);
						 continue;
					}

					if(m_pes->stream_id == 0xC0)
					{
						int dummy = 1;
					}


					if((m_nPESIndicator + PES_packet_length + sizeof(pes_header_t)) <= m_nPSWrtiePos)
					{
						m_nPESLength = PES_packet_length + sizeof(pes_header_t);
						memcpy(m_pPESBuffer, m_pes, m_nPESLength);

						PSStatus status = pes_type(m_pes);

					   char* next = (m_pPSBuffer + m_nPESIndicator + sizeof(pes_header_t) + PES_packet_length);

						m_ps = (ps_header_t*)next;
						m_pes = (pes_header_t*)next;
		               
						 //定位下一个pes 或者 ps
						m_nPESIndicator += m_nPESLength;

						m_status = ps_pes;

						return pes_tuple(true, status, (pes_header_t*)m_pPESBuffer, false);  
					}
					else//PES帧数据不完整,缓冲区已经读到尾部
					{
						int remain = m_nPSWrtiePos - m_nPESIndicator;

						if(m_bRealTime) //实时流丢包的情况处理
						{
							m_nPESLength = /*PES_packet_length + sizeof(pes_header_t)*/remain;
							memcpy(m_pPESBuffer, m_pes, m_nPESLength);

							PSStatus status = pes_type(m_pes);

							m_ps = NULL;
							m_pes = NULL;
			         
							m_nPESIndicator = 0;
							m_nPSWrtiePos = 0;

							m_status = ps_pes;

							return pes_tuple(true, status, (pes_header_t*)m_pPESBuffer, true); 
						}
						else //把剩余数据移到缓冲区头部,留到下一次解析
						{

							if(m_nPESIndicator > 0) //增加条件判断
							{
							   memmove(m_pPSBuffer, m_pPSBuffer + m_nPESIndicator , remain);
							}
							m_nPSWrtiePos = remain; 
							m_nPESIndicator = 0;

							m_ps = (ps_header_t*)m_pPSBuffer;
							m_pes = (pes_header_t*)m_pPSBuffer;

							m_status = ps_pes;

							 return pes_tuple(false, m_status, NULL, false);
						}
					}
					
					
				}// if(m_status == ps_pes)
				else
				{
                    m_nPESIndicator += 1;
				}


			} // if(b >= 0xbd && b < 0xf0)
			else
			{
                //读取PSM表,获得流的数目和每个流的信息
				program_stream_map * psm_hdr = NULL;
				psm_hdr = (program_stream_map*)(m_pPSBuffer + m_nPESIndicator);
				if(is_psm_header(psm_hdr))
				{
					m_status = ps_psm;
				
					unsigned short psm_packet_len = ntohs(psm_hdr->PackLength.length);

					printf("psm map length: %d \n", psm_packet_len);

				    if((m_nPESIndicator + psm_packet_len + 2 + 4 ) > m_nPSWrtiePos ||  psm_packet_len > 36) //检查长度是否越界 | PSM表的长度是否为非法值
						break;

					unsigned char * p = (unsigned char*)psm_hdr + 6;

					p += 2;
					unsigned short  program_stream_info_length = ntohs(*(unsigned short *)p);
					p += 2;

					unsigned short elementary_stream_map_length = ntohs(*(unsigned short*)p);
					p += 2;

					int nStreamNum = elementary_stream_map_length/4;
					for(int i = 0; i < nStreamNum; i++)
					{
						unsigned char stream_type;
						unsigned char elementary_stream_id;
						unsigned short elementary_stream_info_length;

						stream_type = *(unsigned char*)p;
						elementary_stream_id = *(unsigned char*)(p+1);
						elementary_stream_info_length = ntohs(*(unsigned short*)(p+2));

						p += 4;
						p += elementary_stream_info_length;

						if(!FindESStreamInfo(elementary_stream_id))
						{
							program_map_info mapInfo;
							mapInfo.elementary_stream_id = elementary_stream_id;
							mapInfo.stream_type = stream_type;

							m_ProgramMap.push_back(mapInfo);
						}

					}

					m_nPESIndicator += psm_packet_len + 2 + 4;

					continue;
				}

				m_nPESIndicator += 4;
			}

		}//while

		m_nPSWrtiePos = 0; 
		m_nPESIndicator = 0;

		m_status = ps_padding;

END_FIND_PAYLOAD:
        return pes_tuple(false, ps_padding, NULL, false);
        
    }

    naked_tuple naked_payload()
    {
        naked_tuple tuple = naked_tuple(false, 0, 0, 0, 0, NULL, 0, false);
        do
        {
            pes_tuple t = pes_payload();
			if(! t.bContainData)
            {
                break;
            }
			PSStatus status = t.nStatus;
			pes_header_t* pes = t.pPESPtr;
            optional_pes_header* option = (optional_pes_header*)((char*)pes + sizeof(pes_header_t));
            if(option->PTS_DTS_flags != 2 && option->PTS_DTS_flags != 3 && option->PTS_DTS_flags != 0)
            {
                break;
            }
            unsigned __int64 pts = get_pts(option);
            unsigned __int64 dts = get_dts(option);
            unsigned char stream_id = pes->stream_id;
            unsigned short PES_packet_length = ntohs(pes->PES_packet_length); //不包括PES头的长度
            char* pESBuffer = ((char*)option + sizeof(optional_pes_header) + option->PES_header_data_length);
            int nESLength = 0;
			
			if(m_bRealTime && t.bDataLost)
			{
				nESLength = (int)m_nPESLength - sizeof(pes_header_t) - (sizeof(optional_pes_header) + option->PES_header_data_length);
			}
			else
			{
				nESLength = (int)PES_packet_length - (sizeof(optional_pes_header) + option->PES_header_data_length);
			}

			if(m_nESLength + nESLength > MAX_ES_LENGTH || nESLength < 0) //检查是否超出缓冲区大小
			{
				ATLASSERT(0);
				break;
			}

            memcpy(m_pESBuffer + m_nESLength, pESBuffer, nESLength);
            m_nESLength += nESLength;
            if((stream_id == 0xE0 || stream_id == 0xE2) && (status == ps_ps || status == ps_pes_video))
            {
				tuple = naked_tuple(true, 0xE0, pts, dts, option->PTS_DTS_flags, m_pESBuffer, m_nESLength, t.bDataLost);
                m_nESLength = 0;
            }
            else if(stream_id == 0xC0)
            {
                tuple = naked_tuple(true, 0xC0, pts, dts, option->PTS_DTS_flags, m_pESBuffer, m_nESLength, false);
                m_nESLength = 0;
            }
        } while (false);
        return tuple;
    }

	bool FindESStreamInfo(unsigned char stream_id)
	{
		std::vector::iterator it;
		for(it = m_ProgramMap.begin(); it != m_ProgramMap.end(); it++)
		{
			if(it->elementary_stream_id == stream_id)
			{
				return true;
			}
		}
		return false;
	}

private:
    PSStatus      m_status;                     //当前状态

    //char          m_pPSBuffer[MAX_PS_LENGTH];   //PS缓冲区
	char * m_pPSBuffer;

    unsigned int  m_nPSWrtiePos;                //PS写入位置
    unsigned int  m_nPESIndicator;              //PES指针
private:
   // char          m_pPESBuffer[MAX_PES_LENGTH]; //PES缓冲区
	char * m_pPESBuffer;

    unsigned int  m_nPESLength;                 //PES数据长度
private:
    ps_header_t*  m_ps;                         //PS头
    sh_header_t*  m_sh;                         //系统头
    //psm_header_t* m_psm;                        //节目流头
    pes_header_t* m_pes;                        //PES头
private:
   // char         m_pESBuffer[MAX_ES_LENGTH];    //裸码流
	char*				m_pESBuffer;

    unsigned int		m_nESLength;               //裸码流长度

	bool   m_bRealTime; //是否为实时流

public:
	std::vector m_ProgramMap;
};
 
#endif

PsToEsConsole.cpp文件

// PsToEsConsole.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include 
#include 
#include "PsPacket.h"

#include 

struct AppParam
{
	char  srcFile[256];
	char  dstFile[256];
};

AppParam g_appParam;


void processParams(int argc, char* argv[], AppParam *param)
{
	int paramNums;
	
    memset(param, 0, sizeof(AppParam));

	for (int i=1; isrcFile);
			if (paramNums != 1)
				continue;
		}

       else if((_stricmp(argv[i], "-d") == 0) && (idstFile);
			if (paramNums != 1)
				continue;

		}
	}
}


int GetH246FromPs(const char * szPsFile, const char * szEsFile, int *h264length)
{
	PSPacket psDemux;
    //psDemux.SetRealTime(true); //如果输入的是实时流,需要设置这个开关

	psDemux.m_ProgramMap.clear();

	FILE * poutfile =  NULL;	
    FILE * pinfile = NULL;

	if(pinfile == NULL)
	{
		if (NULL == (pinfile = fopen(szPsFile, "rb")))
		{
			printf("Error: Open inputfilename file error\n");
			return -1;
		}
	}

	if(poutfile == NULL)
	{
		if (NULL == (poutfile = fopen(szEsFile, "wb")))
		{
			printf("Error: Open outputfilename file error\n");
			return -1;
		}
	}

   fseek(pinfile, 0, SEEK_END);  
   int  file_len = ftell(pinfile);  

   fseek(pinfile, 0, SEEK_SET);  

   	char* buffer = NULL;
	int length = 0;

	const int read_chunk_size = 64*1024;

	buffer = new char[read_chunk_size];
	memset(buffer, 0, read_chunk_size);

	int nLeftLen = file_len;

    int nReadLen = 0;
	int nVFrameNum = 0;
	int nAFrameNum = 0;

	*h264length = 0;

	DWORD dwStartTick = GetTickCount();

	while(nLeftLen > 0)
	{
		nReadLen = min(read_chunk_size, nLeftLen); //每次从文件读64K
	
		fread(buffer, 1, nReadLen, pinfile); //将文件数据读进Buffer

	    psDemux.PSWrite(buffer, nReadLen);

		int nOutputVideoEsSize = 0;

		while(1)
		{

			naked_tuple es_packet = psDemux.naked_payload();
			if(!es_packet.bContainData || es_packet.nDataLen == 0)
				break;

			if(es_packet.nStreamID == 0xE0)
			{
				int fwrite_number = fwrite(es_packet.pDataPtr, 1, es_packet.nDataLen, poutfile);
				*h264length += es_packet.nDataLen;
				nOutputVideoEsSize += es_packet.nDataLen;

				printf("[%x]VFrameNum: %d, EsLen: %d, pts: %lld \n", es_packet.nStreamID, nVFrameNum++, es_packet.nDataLen, es_packet.ptsTime);
			}
			else
			{
				//printf("[%x]AFrameNum: %d, EsLen: %d \n", es_packet.nStreamID, nAFrameNum++, es_packet.nDataLen);
			}
		}

		//ATLTRACE("WritePos: %d, ReadPos: %d, OutputEsSize: %d \n",  psDemux.GetWritePos(), psDemux.GetReadPos(), nOutputVideoEsSize);

		nLeftLen -= nReadLen;
	}
	///

	int nSize = psDemux.m_ProgramMap.size();
	for(int i=0; i= 2 )
	{
		//processParams(argc, argv, &g_appParam);

        //命令行的参数: 第一个参数---PS文件路径, 第二个参数---ES文件路径
		 _tcscpy(g_appParam.srcFile, __argv[1]);
		
		 _tcscpy(g_appParam.dstFile, __argv[2]);
	}
	else
	{
		printf("input parameter invalid \n");
		return 0;
	}

#else
	strcpy(g_appParam.srcFile, "G:\\videos\\InputFile.ps");
	strcpy(g_appParam.dstFile, "G:\\videos\\out.h264");
#endif

	int  nH264Length = 0;
	int nRet = GetH246FromPs(g_appParam.srcFile, g_appParam.dstFile, &nH264Length);
	if(nRet > 0)
	{
		printf("Extract H264 file succeeded, length: %d \n", nH264Length);
	}
	else
	{
		printf("Error: GetH264FromPs return %d \n", nRet);
	}

	return 0;
}

如果你要开发PS打包+RTP发送的功能,可以参考本博客的另外一篇博文:

《国标PS流打包和RTP发送代码》: https://blog.csdn.net/QuickGBLink/article/details/103434753

关于PS流格式的一些学习资料:

https://www.cnblogs.com/yuweifeng/p/9717354.html

https://blog.csdn.net/g0415shenw/article/details/80385088

https://blog.csdn.net/y601500359/article/details/97649112

 

 

 

 

 

你可能感兴趣的:(国标,开源,GB28181,PS解包,PS流解析)