/*视频RTP负载数据的处理, 提取摄像头NAL信息*/
void RTPDecoder::DoH264Decode(LPVOID param)
{
ASSERT(param);
rtspLinker*linker = (rtspLinker*)param;
RtpDecoderInfo* decoderStatusInfo = &(linker->m_VideoRtpDecoderStatus);
RTP_FIX_HEADER* pkt = (RTP_FIX_HEADER*)m_data;
pkt->sequence_number = ntohs(pkt->sequence_number);
pkt->timestamp = ntohl(pkt->timestamp);
pkt->ssrc = ntohl(pkt->ssrc);
BYTE* rtpPayloadStart = &m_data[sizeof(RTP_FIX_HEADER)+pkt->cc * 4]; //skip fix-header
if (pkt->x){
RTP_EXT_HEADER* pkt_ext = (RTP_EXT_HEADER*)m_data;
rtpPayloadStart += sizeof(RTP_FIX_HEADER)+pkt_ext->extDataCnt * 4;//skip ext-header
}
int nal_data_len = (&m_data[m_dataLen] - rtpPayloadStart);
if (pkt->p){
//负载长度去掉填充字节, 若填料比特被设置,则此包包含一到多个附加在末端的填充比特,填充比特不算作负载的一部分。填充的最后一个字节指明可以忽略多少个填充比特
nal_data_len -= m_data[m_dataLen - 1]/*填充字节数量, 注意m_data[m_dataLen - 1]包含了m_data[m_dataLen - 1]字节本身*/;
}
bool bRtpStampTimeChanged =false; //时间标签发生变化
if(0 == decoderStatusInfo->frame_dataLen) {
decoderStatusInfo->preRtpStampTime = pkt->timestamp;
decoderStatusInfo->preNalType = 0xFF;
decoderStatusInfo->preRtpSN = pkt->sequence_number-1;
}
//TRACE("pretmsamp=%u, curtmsamp = %u....................................\n\n", decoderStatusInfo->preRtpSN , pkt->sequence_number);
//TRACE("pretmsamp=%u, curtmsamp = %u....................................\n\n", decoderStatusInfo->preRtpStampTime , pkt->timestamp);
//时间标签或者NAL变化,上一帧已经结束,
if (decoderStatusInfo->preRtpStampTime != pkt->timestamp)
{
bRtpStampTimeChanged = true;
decoderStatusInfo->preRtpStampTime = pkt->timestamp;
//TRACE("bRtpStampTimeChanged= true\n");
}
/*
1-23 NAL单元 单个 NAL 单元包 这些值的H24保留使用的,RTP包头用到这些值,就是单一NAL单元模式
24 STAP-A 单一时间的组合包
25 STAP-B 单一时间的组合包
26 MTAP16 多个时间的组合包
27 MTAP24 多个时间的组合包
28 FU-A 分片的单元 h264仅用1-23,24以后的用在RTP H264负载类型头中 , FU-A类型使用最多
29 FU-B 分片的单元
30-31 没有定义
*/
RTP_FU_INDICATOR* fu_indicator = (RTP_FU_INDICATOR*)rtpPayloadStart;
RTP_FU_HEADER* fu_header = (RTP_FU_HEADER*)(rtpPayloadStart + 1);
BYTE *nalstart = NULL; //nalstart指向000001, 如果是该NAL是贞的开始,则nalstart指向00 000001
BYTE *temp = NULL;
UINT8 preNalType = decoderStatusInfo->preNalType;
bool bNalTypeChanged =false; //NAL type发生变化
if(fu_indicator->fu_type<24){
//TRACE("preNalType=%x, curNalType = %x....................................\n\n" , decoderStatusInfo->preNalType, fu_indicator->fu_type);
if(fu_indicator->fu_type != decoderStatusInfo->preNalType){
//TRACE("bNalTypeChanged= true\n");
bNalTypeChanged = true; //时间标签或者NAL变化,上一帧已经结束,
decoderStatusInfo->preNalType = fu_indicator->fu_type;
}
}
else{
//TRACE("preNalType=%x, curNalType = %x....................................\n\n" , decoderStatusInfo->preNalType, fu_header->nalType);
if(fu_header->nalType != decoderStatusInfo->preNalType){
//TRACE("bNalTypeChanged= true\n");
bNalTypeChanged = true; //时间标签或者NAL变化,上一帧已经结束,
decoderStatusInfo->preNalType = fu_header->nalType;
}
}
static FILE* fH264 = NULL;
if (!fH264) fopen_s(&fH264, "e:\\1080P.h264", "wb");
ASSERT(fH264);
//Slice种的三种编码模式:I_slice、P_slice、B_slice
//贞类型变化, 前一帧数据肯定已经结束
if(bNalTypeChanged||bRtpStampTimeChanged){
if(decoderStatusInfo->frame_dataLen){
TSPacker * tsPacker = new TSPacker(linker, decoderStatusInfo->frame_buf, decoderStatusInfo->frame_dataLen, pkt->timestamp);
linker->m_rtpTsPackerThreadPool->PushJob(tsPacker, linker);
TRACE("$$$$$$$$$$>>>>>>>>>>>>>>>>>========NAL[%d]===========LEN[%d]============xxxxxxxxxxxxxxxx=======...[%d]....[%d]\n", decoderStatusInfo->preNalType, decoderStatusInfo->frame_dataLen, bNalTypeChanged, bRtpStampTimeChanged);
// if(preNalType == NALU_TYPE_PPS||preNalType == NALU_TYPE_SPS||preNalType == NALU_TYPE_IDR)
//if(NALU_TYPE_IDR == preNalType){
// TRACE("$$$$$$$$$$>>>>>>>>>>>>>>>>>========NAL[%d]===========LEN[%d]============xxxxxxxxxxxxxxxx=======...[%d]....[%d]\n", decoderStatusInfo->preNalType, decoderStatusInfo->frame_dataLen, bNalTypeChanged, bRtpStampTimeChanged);
//}
UINT8* p = (UINT8*)decoderStatusInfo->frame_buf;
TRACE("%02x %02x %02x %02x %02x \n", p[0],p[1],p[2],p[3],p[4]);
//时间标签或者NAL变化,上一帧已经结束,把上一帧NAL数据写入H264文件
fwrite(decoderStatusInfo->frame_buf, 1, decoderStatusInfo->frame_dataLen, fH264); //向文件写入一个完整的贞数据
fflush(fH264);
//fH264=NULL;
decoderStatusInfo->frame_dataLen=0;
}
}
if (fu_indicator->fu_type < 24){
//经过跟踪, 大华的RTP流中包含了PPS和SPS(他们的 时间标签用的是后面IDR贞的时间标签, 时间标签间隔3600)
//这种单一NAL分片包, 打包时去除 "00 00 01" 或 "00 00 00 01" 的开始码, 把其他数据封包的 RTP 包即可, 所以还原时, 自己加上000001 或者 00000001 , 对于pps和sps, 加上00 000001
//TRACE("%d %08x++++++++++++++++++++++++++ %d,%d,%02x %02x %02x %02x %02x %02x %02x \n", nal_data_len, pkt->timestamp
// , fu_indicator->forbidden_zero_bit, fu_indicator->nal_ref_idc, fu_indicator->fu_type
// , ptr[1], ptr[2], ptr[3], ptr[4], ptr[5], ptr[6]); //这里打印信息证实了FU-A类型的包最多,
//TRACE("LINE#%d ============================= spsGot[%d]\n", __LINE__, decoderStatusInfo->spsGot);
//H264裸流文件的第一个NAL必须是SPS参数集合
if(1 == decoderStatusInfo->spsGot || NALU_TYPE_SPS == fu_indicator->fu_type)
{
if(0 == decoderStatusInfo->spsGot)
{
decoderStatusInfo->spsGot =1;
TRACE("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~pkt->sequence_number = %d\n", pkt->sequence_number);
}
//NAL数据增加前缀
nalstart= temp= rtpPayloadStart - 4; //倒退4个字节,fu_indicator(相当于NALTYPE字节)前增加NAL前缀000001 或 00 000001
nal_data_len += 4;
//frame-start-flag
*temp++ = 0x00;
//fill nal-start-code
*temp++ = 0x00;
*temp++ = 0x00;
*temp++ = 0x01;
//TRACE("LINE#%d ============================= spsGot[%d]\n", __LINE__, decoderStatusInfo->spsGot);
if(bNalTypeChanged||bRtpStampTimeChanged){
//增加NAL前缀00 000001
ASSERT(decoderStatusInfo->frame_bufSize >(decoderStatusInfo->frame_dataLen + nal_data_len));
memcpy(&decoderStatusInfo->frame_buf[decoderStatusInfo->frame_dataLen], nalstart, nal_data_len);
decoderStatusInfo->frame_dataLen += nal_data_len;
//TRACE("++++++++++++++++++++++++ addLen %d\n", decoderStatusInfo->frame_dataLen);
}
else{
//增加NAL前缀 000001
nalstart++;
nal_data_len--;
ASSERT(decoderStatusInfo->frame_bufSize >(decoderStatusInfo->frame_dataLen + nal_data_len));
memcpy(&decoderStatusInfo->frame_buf[decoderStatusInfo->frame_dataLen], nalstart, nal_data_len);
decoderStatusInfo->frame_dataLen += nal_data_len;
//TRACE("++++++++++++++++++++++++ addLen %d\n", decoderStatusInfo->frame_dataLen);
}
ASSERT(decoderStatusInfo->frame_dataLen);
}
//TRACE("LINE#%d..¥¥¥¥¥¥¥¥¥¥.....single nal_type[%d].........................%d, spsGot[%d]\n", __LINE__, decoderStatusInfo->preNalType, decoderStatusInfo->frame_dataLen, decoderStatusInfo->spsGot);
//if(0==decoderStatusInfo->frame_dataLen&&decoderStatusInfo->spsGot)
//{
//TRACE("LINE#%d..¥¥¥¥¥¥¥¥¥¥.....single nal_type[%d].........................%d, spsGot[%d]\n", __LINE__, decoderStatusInfo->preNalType, decoderStatusInfo->frame_dataLen, decoderStatusInfo->spsGot);
//}
}
else if(decoderStatusInfo->spsGot){
// I/P/B= 0x01/0x05 也就是说只判断naltype = 0x01/0x05是判断不出来I/P/B帧类型的,需要到slice层去判断用到“熵编码”具体的“熵编码”内容请看:“H.264官方中文版.pdf”.
//经过抓包大华摄像头,当采用FU-A分片, 只存在一个分片,下1个NAL的fu-header的
// TRACE("%d %08x++++++++++++++++++++++++++%d %d,%d,%02x s%d------e%d %02x %02x %02x %02x %02x %02x\n", nal_data_len, pkt->timestamp
// , pkt->sequence_number, fu_indicator->forbidden_zero_bit, fu_indicator->nal_ref_idc, fu_header->nalType, fu_header->nalStart, fu_header->nalEnd
// , ptr[2], ptr[3], ptr[4], ptr[5], ptr[6], ptr[7]); //这里打印信息证实了FU-A类型的包最多
//这里打印信息证实了FU-A类型的包最多
//TRACE("++++++++++++++++++++++++++%d %d,%d,%02x s %d------%d\n", pkt->sequence_number, fu_indicator->forbidden_zero_bit, fu_indicator->nal_ref_idc, fu_header->nalType, fu_header->nalStart, fu_header->nalEnd); //这里打印信息证实了FU-A类型的包最多
//打包时去除了 "00 00 01" 或 "00 00 00 01" 的开始码, 如果贞的第一个NAL, 在还原时添加前缀00 000001, 否则添加前缀 000001
/*
表示支持的封包模式.
当 packetization-mode 的值为 0 时或不存在时, 必须使用单一 NALU 单元模式.
当 packetization-mode 的值为 1 时必须使用非交错(non-interleaved)封包模式. -------------------- NALU必须是严格按照解码顺序传输
当 packetization-mode 的值为 2 时必须使用交错(interleaved)封包模式.
“非交错模式”:NALU必须是严格按照解码顺序传输,也就是说,假设1s中连续的24帧分别标记为:frame1,frame2...,frame24,则传输必须严格按frame1,frame2...,frame24这个顺序传输。该模式可以分包(指应用层的分包,并非指传输层)。
“交错模式” :NALU可以不按照解码顺序传输,也就是说,假设1s中连续的24帧分别标记为:frame1,frame2...,frame24,则传输顺序可以是frame15,frame7,frame9...。该模式可以分包(指应用层的分包,并非指传输层)。
*/
if(fu_header->nalStart) //这是NAL的第一个RTP封包--需要增加NAL前缀
{
//重建NALHead
NAL_HEADER* pNalHead = (NAL_HEADER*) fu_header;
pNalHead->forbidden_zero_bit = fu_indicator->forbidden_zero_bit;
pNalHead->nal_ref_idc = fu_indicator->nal_ref_idc;
//倒退3个字节,fu_header(相当于NALTYPE字节)前增加NAL前缀000001 或 00 000001
nalstart= temp= rtpPayloadStart - 3;
nal_data_len += 3;
//frame-start-flag
*temp++ = 0x00;
//fill nal-start-code
*temp++ = 0x00;
*temp++ = 0x00;
*temp++ = 0x01;
if(bNalTypeChanged||bRtpStampTimeChanged){
//增加NAL前缀00 000001
ASSERT(decoderStatusInfo->frame_bufSize >(decoderStatusInfo->frame_dataLen + nal_data_len));
memcpy(&decoderStatusInfo->frame_buf[decoderStatusInfo->frame_dataLen], nalstart, nal_data_len);
decoderStatusInfo->frame_dataLen += nal_data_len;
//TRACE("LINE#%d......fua[%d].........s[%d]................nal_data_len = %d\n", __LINE__, decoderStatusInfo->preNalType, fu_header->nalStart,nal_data_len);
}
else{
nalstart++;
nal_data_len--;
//增加NAL前缀000001
ASSERT(decoderStatusInfo->frame_bufSize >(decoderStatusInfo->frame_dataLen + nal_data_len));
memcpy(&decoderStatusInfo->frame_buf[decoderStatusInfo->frame_dataLen], nalstart, nal_data_len);
decoderStatusInfo->frame_dataLen += nal_data_len;
//TRACE("LINE#%d......fua[%d].........s[%d]................nal_data_len = %d\n", __LINE__, decoderStatusInfo->preNalType, fu_header->nalStart,nal_data_len);
}
}
else if(decoderStatusInfo->frame_dataLen>0){ //fu_header->nalStart=0时, 如果decoderStatusInfo->frame_dataLen==0 可以肯定是出现RTP掉包了
nalstart= rtpPayloadStart;
nalstart +=2; //跳过 fu_indicator + fu_header字节, 后面就是NAL的数据
nal_data_len -=2;
ASSERT(decoderStatusInfo->frame_dataLen);
ASSERT(decoderStatusInfo->frame_bufSize >(decoderStatusInfo->frame_dataLen + nal_data_len));
memcpy(&decoderStatusInfo->frame_buf[decoderStatusInfo->frame_dataLen], nalstart, nal_data_len);
decoderStatusInfo->frame_dataLen += nal_data_len;
//TRACE("LINE#%d.......fua[%d].......s[%d].................nal_data_len = %d\n", __LINE__, decoderStatusInfo->preNalType, fu_header->nalStart, nal_data_len);
}
ASSERT(decoderStatusInfo->frame_dataLen);
//TRACE("LINE#%d..@@@@@@@@.....fua[%d].........................%d\n", __LINE__, decoderStatusInfo->preNalType, decoderStatusInfo->frame_dataLen);
}
decoderStatusInfo->preRtpSN++;
if(decoderStatusInfo->preRtpSN != pkt->sequence_number){
TRACE("LINE#%d..@@@@@@@@.....fua[%d].........................%d\n", __LINE__, decoderStatusInfo->preNalType, decoderStatusInfo->frame_dataLen);
}
//工作结束,线程池任务销毁
delete this;
}