低延时高RTSP兼容的EasyPlayer-RTSP-win解决H.264一帧多个nal单元录像花屏问题方案
  7fobZ3HfJuFM 2023年11月19日 21 0

我们来讲解一下关于H264编码格式中的一帧多nal(Network Abstract Layer, 即网络抽象层),关于H264和NAL,这里引用一段话来科普一下:

在H.264/AVC视频编码标准中,整个系统框架被分为了两个层面:视频编码层面(VCL)和网络抽象层面(NAL)。其中,前者负责有效表示视频数据的内容,而后者则负责格式化数据并提供头信息,以保证数据适合各种信道和存储介质上的传输。因此我们平时的每帧数据就是一个NAL单元(SPS与PPS除外)。在实际的H264数据帧中,往往帧前面带有00 00 00 01 或 00 00 01分隔符,一般来说编码器编出的首帧数据为PPS与SPS,接着为I帧……

一般情况下,一个H264帧直接以00 00 00 01开头作为一个NAL作为网络传输单元,而在有些H264的编码器则编码出来的H264帧包含了多个NAL,这个时候每个分片的NAL(注意是分片的)则是是以00 00 01开头作为网络传输单元,经过分片的NAL数据量更小,从而更加方便进行网络。

但是,我们在接收到带有多个NAL的H264帧的时候进行写MP4则不能简单是只通过将头部的00 00 00 01标志转换从AVC的长度标识,而需要将所有的00 00 00 01和00 00 01都需要转换成该NAL单元的长度,否则就会出现视频解码只能播放头部一小部分,其他部分全部花屏的情况,如下图所示:

说了这么多,大家是否明白了呢,如果不明白的(文字描述比较虚),我们直接看代码实现:

int EasyMP4Writer::WriteMp4File(unsigned char* pdata, int datasize, bool keyframe, long nTimestamp, int nWidth, int nHeight) { if (nTimestamp0||(pdataNULL)||datasize<=0) { return -1; } int inlen=datasize; unsigned charpin=pdata; int outlen=0; unsigned charpout=NULL; bool bend = false;

int datalen=0; bool bSPSOrPPS = false;

int iOutLen = datasize;

unsigned char* pRealData = new unsigned char[datasize<<1]; int nRealDataSize = 0; memset(pRealData,0x00, datasize+4); do { int nal_start = 0; int nal_end = 0; outlen = find_nal_unit(pin,inlen, &nal_start, &nal_end ); if (outlen<=0) { break; } pout = pin+nal_start; if(pout!=NULL) { unsigned char naltype = ( pout[0] & 0x1F);

if (naltype==0x07)//0x67
	{

// m_psps=pout; // m_spslen=outlen; //pout[0] = 0x67; if(m_bwritevideoinfofalse) { m_psps = new unsigned char[outlen]; memcpy(m_psps, pout, outlen); m_spslen=outlen; } bSPSOrPPS = true; } else if (naltype0x08)//0x68 { // m_ppps=pout; // m_ppslen=outlen; //pout[0] = 0x68; if(m_bwritevideoinfofalse) { m_ppps = new unsigned char[outlen];//outlen memcpy(m_ppps, pout, outlen); m_ppslen = outlen; } bSPSOrPPS = true; } // else if (pout[0] == 0x06)//SEI // { // // } // else { memcpy(pRealData+nRealDataSize, &outlen, 4); //写入头4个字节nal内容的长度(H264数据的长度) unsigned char byte0 = pRealData[nRealDataSize+3]; unsigned char byte1 = pRealData[nRealDataSize+2]; pRealData[nRealDataSize+3] = pRealData[nRealDataSize+0]; pRealData[nRealDataSize+2] = pRealData[nRealDataSize+1]; pRealData[nRealDataSize+1] = byte1; pRealData[nRealDataSize+0] = byte0;

nRealDataSize += 4;

memcpy(pRealData+nRealDataSize, pout, outlen);
		nRealDataSize += outlen;
	}

	inlen=inlen-outlen-(pout-pin);
	pin=pout+outlen;
}

} while (bend!=true);

if (m_bwritevideoinfofalse&&m_ppps&&m_psps) { // PPS末尾的0过滤,否则VLC可能播放不出来 [12/22/2015 Dingshuai] int nPPSSize = m_ppslen; int nZeroCount = 0; for (int nI = nPPSSize-1; nI>=0; nI--) { if (m_ppps[nI] == 0x00) { nZeroCount++; } else { break; } } m_ppslen = m_ppslen-nZeroCount; WriteH264SPSandPPS(m_psps,m_spslen,m_ppps,m_ppslen,nWidth,nHeight); m_bwritevideoinfo = true; } if (m_bwritevideoinfofalse||nRealDataSize<=0 ) { return 0;//获取sps pps失败 } 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. // if(/bSPSOrPPS/pout[0]==0x67 || pout[0]==0x68) // { // return 0; // } WriteH264Frame(pRealData, nRealDataSize, keyframe, nTimestamp);//左移4单位,加上数据长度头?

if (pRealData) { delete []pRealData; pRealData = NULL; }

return true; 1. 2. 3. 4. 5. 6. 7. }

其中find_nal_unit()函数是从H264帧中分析出以00 00 00 01和00 00 01开头的NAL单元,然后直接填充成该NAL单元的长度,注意字节顺序为大端顺序: //写入头4个字节==nal内容的长度(H264数据的长度)

unsigned char byte0 = pRealData[nRealDataSize+3]; unsigned char byte1 = pRealData[nRealDataSize+2]; pRealData[nRealDataSize+3] = pRealData[nRealDataSize+0]; pRealData[nRealDataSize+2] = pRealData[nRealDataSize+1]; pRealData[nRealDataSize+1] = byte1; pRealData[nRealDataSize+0] = byte0; 将NAL长度拷贝到AVC的缓冲区内,紧接着数据部分拷贝: memcpy(pRealData+nRealDataSize, pout, outlen); 最后,组装成完成的avc帧之后写入MP4,播放的时候就不会有花屏,马赛克的情况出现了。


©著作权归作者所有:来自51CTO博客作者爱穿格子的程序员的原创作品,请联系作者获取转载授权,否则将追究法律责任 低延时高RTSP兼容的EasyPlayer-RTSP-win解决H.264一帧多个nal单元录像花屏问题方案 https://blog.51cto.com/u_16291532/7758779

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月19日 0

暂无评论

推荐阅读
  gBkHYLY8jvYd   2023年12月09日   8   0   0 cii++数据
7fobZ3HfJuFM