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数据。
如下图:
贴下示例代码:
/*
*写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;
}