用ffmpeg提取mp4的h264码流写文件花屏

1、用ffmpeg提取mp4的h264码流写文件的方法网上有很多,不知道的请参考雷神博客:http://blog.csdn.net/leixiaohua1020/article/details/11800877

2、但是这么写出来的文件是有问题的,首先的确能播放,但是会有很大几率出现花屏

a、首先说说解决方案

其实很简单,也是利用av_bitstream_filter_filter方法把每个AVPacket处理下,如下:

	AVPacket tempPack;
	av_copy_packet(&tempPack, &pkt_in);
	av_bitstream_filter_filter(bsfc, pCodecCtx, NULL, &tempPack.data, &tempPack.size, tempPack.data, tempPack.size, 0);  
	fwrite(tempPack.data,pkt_in.size,1,fp_filter); 
b、再说说原因

在ffmpeg中,一个AVPacket中有可能包含不止一个nal,所以按雷神那么处理的话,同一个AVPacket中的第二个nal就没有添加其实标志00 00 00 01,如果刚好这个nal是其他nal参考的话,那么就会出现花屏。

c、在说说AVPacket.data中nal是如何存储的

在AVPacket.data中每个nal没有起始码00 00 00 01,其中nal的结构是起始4字节的nal数据长度(不包括这4字节),然后才是真正的nal数据。

如下图:

用ffmpeg提取mp4的h264码流写文件花屏_第1张图片

贴下示例代码:

/* 
 *写Nal测试
 *
 *缪国凯 Mickel
 *[email protected]
 *
 *本程序实现了读取H264编码视频保存为nal文件,其中有2种写法,第一种就是网上常见的在每个AVPacket的data前加nal头
 *第二种是用filter,此程序把每一个AVPacket都单独写成了一个nal,可以做每一帧的nal文件对比,如果想把所有帧
 *写成h264码流文件,自己改造程序不每帧新开个文件就好了。
 */

#include "stdafx.h"


#ifdef	__cplusplus
extern "C"
{
#endif
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"

#pragma comment(lib, "avcodec.lib")
#pragma comment(lib, "avformat.lib")
#pragma comment(lib, "avutil.lib")
#ifdef __cplusplus
};
#endif

#include 
#include 

AVFormatContext *ifmt_ctx = NULL;

#include 
int g_videoIndex = -1;

int openinputfile(const char* filename)
{
	int ret = 0;
	//open the input
	if ((ret = avformat_open_input(&ifmt_ctx, filename, NULL, NULL)) < 0)
	{
		printf("can not open input");
		return ret;
	}
	if ((ret = avformat_find_stream_info(ifmt_ctx, NULL)))
	{
		printf("can not find input stream info");
		return ret;
	}

	//open the decoder
	for (int i = 0; i < ifmt_ctx->nb_streams; i++)
	{
		if (ifmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
		{
			g_videoIndex = i;
			ret = avcodec_open2(ifmt_ctx->streams[i]->codec, 
				avcodec_find_decoder(ifmt_ctx->streams[i]->codec->codec_id), NULL);

			if (ret < 0)
			{
				printf("can not open decoder");
				return ret;
			}

		}
	}

	return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
	if (argc < 2)
	{
		return -1;
	}
	AVPacket pkt_in, pkt_out;
	AVFrame *frame = NULL;
	unsigned int stream_index;
	av_register_all();

	if (openinputfile(argv[1]) < 0)
	{
		printf("failed to open input file");
		goto end;
	}

	if (g_videoIndex == -1)
	{
		return -1;
	}
	
	FILE *fp_sps=fopen("testNal/testNal/sps.h264","wb+");
	FILE *fp_f_sps=fopen("testNal/testFilterNal/sps.h264","wb+");

	FILE *testMaFp = fopen("testMaual.h264","wb+");
	AVCodecContext *pCodecCtx= ifmt_ctx->streams[g_videoIndex]->codec;
	unsigned char *dummy=NULL;   //输入的指针
	int dummy_len;
	fwrite(pCodecCtx->extradata,pCodecCtx->extradata_size,1,fp_sps);
	AVBitStreamFilterContext* bsfc =  av_bitstream_filter_init("h264_mp4toannexb");    
	av_bitstream_filter_filter(bsfc, pCodecCtx, NULL, &dummy, &dummy_len, NULL, 0, 0);
	fwrite(pCodecCtx->extradata,pCodecCtx->extradata_size,1,fp_f_sps);
	
	fclose(fp_sps);
	fclose(fp_f_sps);
	
	fwrite(pCodecCtx->extradata,pCodecCtx->extradata_size,1,testMaFp);

	int totol_frame_index = 0;
	while(1)
	{
		if (av_read_frame(ifmt_ctx, &pkt_in) < 0)
		{
			break;
		}
		if (ifmt_ctx->streams[pkt_in.stream_index]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
		{
			//第一种写法,在每个frame前加nal头
			char tmpName[100];
			sprintf(tmpName, "testNal/testNal/frame_%d.h264", totol_frame_index);
			FILE *fp=fopen(tmpName,"wb+");
			if (fp)
			{
				char nal_start[]={0,0,0,1};  
				fwrite(nal_start,4,1,fp);  
				fwrite(pkt_in.data+4,pkt_in.size-4,1,fp);
				fclose(fp);
			}

			//第二种写法,用av_bitstream_filter_filter
			sprintf(tmpName, "testNal/testFilterNal/frame_%d.h264", totol_frame_index);
			FILE *fp_filter=fopen(tmpName,"wb+");
			if (fp_filter)
			{
				AVPacket tempPack;
				av_copy_packet(&tempPack, &pkt_in);
				av_bitstream_filter_filter(bsfc, pCodecCtx, NULL, &tempPack.data, &tempPack.size, tempPack.data, tempPack.size, 0);  
				fwrite(tempPack.data,pkt_in.size,1,fp_filter); 
				fclose(fp_filter);
			}
			totol_frame_index++;
		}
		continue;
	}

	av_bitstream_filter_close(bsfc);    
	free(dummy); 

	fclose(testMaFp);
end:
	avformat_close_input(&ifmt_ctx);

	getchar();
	return 0;
}


你可能感兴趣的:(ffmpeg)