ffmpeg 推流 在H264中插入SPS、PPS 头

1. SPS PPS头的重要性
SPS PPS 头在编解码H264时非常重要,里面不仅包含了许多关于视频流的相关信息:比如宽高等。还标志着一个视频流的开头。

一般视频流前开头第一帧是SPS 第二帧是PPS 第三帧才是I帧。目前在网上看到加入SPS PPS头都是在解码时加入,解码时加入ffmpeg有个特别的过滤方法av_bitstream_filter_filter()。编码目前只能深入了解编码底层,找到I帧并且在I帧前插入SPS PPS头。否则即使你是使用ffmpeg编码的视频流,用ffplay播放也是缺少SPS PPS头的。

对于是应该在编码加入SPS PPS头还是解码加入,哪一边兼容其实不重要,我认为最标准的是两边都做这样的兼容,这样程序才更加强壮。作为推流端在推流时加入SPS PPS 头,拉流时判断是否找得到SPS PPS 头,若没有,则加上SPS PPS头。这样就程序就会健壮许多。

2.如何在判断推流时H264文件是否含有SPS PPS头。

使用ffmpeg编码,目前新版的ffmpeg编码接口是avcodec_send_frame()和avcodec_receive_packet() ,前者将存放YUV的AVFrame放入,后者将存放编好的H264 AVPacket取出。然后我们可以用fwrite在av_interleaved_write_frame()封装并发送前写二进制,然后推荐用Binary Viewer查看写的h264二进制文件。

FILE *file = fopen("C:\\Users\\admin\\Desktop\\123456.h264", "wb");//(放在编码和播放的循环外面)
fwrite(enc_packet.data, sizeof(enc_packet), 1, file);

但是不一定开头就是SPS和PPS或者I帧,而且这时一般是没有SPS 和 PPS头的,查看二进制文件时搜索一下00000001来查找。

SPS 十六进制值为:0x67
PPS 十六进制值为:0x68
I帧 十六进制值为:0x65
但是在I帧和SPS开头都会有00000001(PPS接在SPS后,是一体的)
如果找到00 00 00 01 67,则这一帧为I帧。
在这里插入图片描述

3.如何插入SPS PPS头
SPS和PPS头在你用ffmpeg编码时已经获取到了,存放在编码器上下文AVCodecContext的extradata中,可以从中取出并插入H264中。

前面说到了SPS和PPS是在I帧之前

所以我们就应该在每一个AVPacket的h264中查找i帧

AVPacket中有个重要的参数可以用来判断是否含有i帧:flags
用flag与AV_PKT_FLAG_KEY进行与操作就可以判断当前这一帧是否为I帧。
找到I帧后,在I帧前面插入extradata就完成插帧,记得扩大指针的大小。

//声明尽量不要在推流的while循环里做,避免内存泄漏
AVPacket enc_packet;
char* cPacketData = new char(oCodecCtx->extradata_size  * sizeof(char*));
uint8_t* cExtradata = (uint8_t *)malloc((100000) * sizeof(uint8_t));
...
...
...
...
...
...
if (enc_packet.flags &AV_PKT_FLAG_KEY)//找到带I帧的AVPacket
{
	cPacketData = (char*)enc_packet.data; 
	int n = 0;
	while ((char*)enc_packet.data)
	{
		//printf("%c\n", cPacketData[n]);
		if (cPacketData[n] == 0 && cPacketData[n + 1] == 0 && cPacketData[n + 2] == 0 && cPacketData[n + 3] == 1 && cPacketData[n + 4] == 101)
		{
		//找到I帧,插入SPS和PPS
		memcpy(cExtradata, oCodecCtx->extradata, oCodecCtx->extradata_size);
		memcpy(cExtradata + oCodecCtx->extradata_size, enc_packet.data, enc_packet.size);//&enc_packet.data[0]
		enc_packet.size += oCodecCtx->extradata_size;
		enc_packet.data = cExtradata;
		break;
		}
		if (n == enc_packet.size)
		{
		break;
		}
				cPacketData++;
				n++;
			}
			
		}

基本到这里都完成了插帧。

  • 9
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值