该代码最初的版本来自于互联网,首先感谢前辈无私分享的精神,这个PS流解析代码小巧和可读性好,是学习PS格式的一个很好的参考例子。但原来的代码有不少Bug,QuickGBLink在原先代码基础上做了很多问题修复和改进,现在对代码开源,希望能帮助更多国标开发者开发相关的功能。
实现的功能:
1. 解析PS包,从中提取出PES。
2. 解析PSM(节目流映射表),获得每个流的信息。
3. 对流格式做一些合法性判断和验证,检查数据是否完整。
4. 对实时流做了一些特殊处理,检查数据有无缺损或丢包,如果有则丢掉该块,从下一个PS头开始读起。
这个PS流解包的代码包含两个文件:streamdef.h, PsPacket.h,另外写了一个调用的例子(文件名:PsToEsConsole.cpp )。
#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
#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 : 定义控制台应用程序的入口点。
//
#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