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;
}