FFmpeg封装格式处理:视音频复用器(muxer)不同格式中数据有无bsf的差异->-> Filter bitstream_filter实现格式转换:h264_mp4toannexb

在这里插入图片描述
在这里插入图片描述
视音频复用器:从输入文件中挑出需要的视频/音频流,再合并放到一个文件中

不同封装格式中的数据存放方式不同,其差异需要一个av_bitstream_filter_filter(AVBitStreamFilterContext.....)进行转换
PS1:mp4(没有bsf)->annexb(有bsf)的转换需要用到名称为“h264_mp4toannexb”的bitstream filter。  
【效果:1、Add SPS,PPS in front of IDR frame
  2、Add start code ("0,0,0,1") in front of NALU】
PS2:对于某些封装格式(例如MP4/FLV/MKV等)中的AAC,需要用到名称为“aac_adtstoasc”的bitstream filter。
【应该某些编解码器只认toannexb,不认mp4格式】



/*
FIX: H.264 in some container format (FLV, MP4, MKV etc.) need 
"h264_mp4toannexb" bitstream filter (BSF)
  *Add SPS,PPS in front of IDR frame
  *Add start code ("0,0,0,1") in front of NALU
H.264 in some container (MPEG2TS) don't need this BSF.
*/
/**
 * 本程序可以将视频码流和音频码流打包到一种封装格式中。
 * 程序中将AAC编码的音频码流和H.264编码的视频码流打包成MPEG2TS封装格式的文件。
 * 需要注意的是本程序并不改变视音频的编码格式。 不涉及编解码 输入输出使用的是相同的编解码上下文
 */
#include <stdio.h>
#include <libavformat/avformat.h>

#define USE_H264BSF 0 	//'1': Use H.264 Bitstream Filter 
#define USE_AACBSF 0	//'1': Use AAC Bitstream Filter 
 
int main(int argc, char* argv[])
{
	
	AVFormatContext *ifmt_ctx_v = NULL, *ifmt_ctx_a = NULL,*ofmt_ctx = NULL;
	AVPacket pkt;
	int ret, i;
	int videoindex_v=-1,videoindex_out=-1;
	int audioindex_a=-1,audioindex_out=-1;
	int frame_index=0;
	int64_t cur_pts_v=0,cur_pts_a=0;
 

	const char *in_filename_v = "cuc_ieschool.h264";	//itput file URL
	const char *in_filename_a = "huoyuanjia.mp3";		//itput file URL
	const char *out_filename = "cuc_ieschool.mp4";		//Output file URL
	
	av_register_all();
	//Input
	avformat_open_input(&ifmt_ctx_v, in_filename_v, 0, 0)
	avformat_find_stream_info(ifmt_ctx_v, 0)
	avformat_open_input(&ifmt_ctx_a, in_filename_a, 0, 0)
	avformat_find_stream_info(ifmt_ctx_a, 0)
	
//Output
	avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_filename);
	AVOutputFormat *ofmt = ofmt_ctx->oformat;
	
	
	
//Create output AVStream according to input AVStream	
	for (i = 0; i < ifmt_ctx_v->nb_streams; i++) {
		if(ifmt_ctx_v->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO)
		{
			AVStream *in_stream = ifmt_ctx_v->streams[i];
			AVStream *out_stream = avformat_new_stream(ofmt_ctx, in_stream->codec->codec);   //输入流的编解码器->->创建输出流的编解码器
			videoindex_v=i;
			videoindex_out=out_stream->index;
			avcodec_copy_context(out_stream->codec, in_stream->codec)    
			out_stream->codec->codec_tag = 0;
			//有些格式需要头head
			if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
			break;
		}
	}
	for (i = 0; i < ifmt_ctx_a->nb_streams; i++) {
		if(ifmt_ctx_a->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO)
		{
			AVStream *in_stream = ifmt_ctx_a->streams[i];
			AVStream *out_stream = avformat_new_stream(ofmt_ctx, in_stream->codec->codec);
			avcodec_copy_context(out_stream->codec, in_stream->codec)
			audioindex_a=i;
			audioindex_out=out_stream->index;
			out_stream->codec->codec_tag = 0;
			if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
			break;
		}
	}
	//av_dump_format

	//Open output file
	if (!(ofmt->flags & AVFMT_NOFILE)) 		avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE)




	avformat_write_header(ofmt_ctx, NULL)		//Write file header
 
#if USE_H264BSF
	AVBitStreamFilterContext* h264bsfc =  av_bitstream_filter_init("h264_mp4toannexb"); 
#endif
#if USE_AACBSF
	AVBitStreamFilterContext* aacbsfc =  av_bitstream_filter_init("aac_adtstoasc"); 
#endif
 
	while (1) 
	{
		AVFormatContext *ifmt_ctx;
		int stream_index;
		AVStream *in_stream, *out_stream;
 
		//Get an AVPacket 要先判断应该去拿音频or视频?----------------
		if(av_compare_ts(cur_pts_v,ifmt_ctx_v->streams[videoindex_v]->time_base,
						 cur_pts_a,ifmt_ctx_a->streams[audioindex_a]->time_base	) <= 0)
		{
			ifmt_ctx=ifmt_ctx_v;
			stream_index=videoindex_out;
			if(av_read_frame(ifmt_ctx, &pkt) >= 0){
				do{
					in_stream  = ifmt_ctx->streams[pkt.stream_index];
					out_stream = ofmt_ctx->streams[stream_index];
 
					if(pkt.stream_index==videoindex_v){
						//FIX:No PTS (Example: Raw H.264)
						//Simple Write PTS
						if(pkt.pts==AV_NOPTS_VALUE){
							AVRational time_base1=in_stream->time_base;
							//Duration between 2 frames
							//每帧所占时间间隔(如1800)=AV_TIME_BAS【#define AV_TIME_BASE   1000000】/帧率(如50帧/秒)
							int64_t calc_duration=(double)AV_TIME_BASE/av_q2d(in_stream->r_frame_rate);
							
							
							//Parameters
							pkt.pts=(double)(frame_index*calc_duration)/(double)(av_q2d(time_base1)*AV_TIME_BASE);
							pkt.dts=pkt.pts;
							pkt.duration=(double)calc_duration/(double)(av_q2d(time_base1)*AV_TIME_BASE);
							frame_index++;
						}
 
						cur_pts_v=pkt.pts;
						break;
					}
				}while(av_read_frame(ifmt_ctx, &pkt) >= 0);
			}else{
				break;
			}
		}
		else
		{
			ifmt_ctx=ifmt_ctx_a;
			stream_index=audioindex_out;
			if(av_read_frame(ifmt_ctx, &pkt) >= 0)
			{
				do{
					in_stream  = ifmt_ctx->streams[pkt.stream_index];
					out_stream = ofmt_ctx->streams[stream_index];
 
					if(pkt.stream_index==audioindex_a){
 
						//FIX:No PTS
						//Simple Write PTS
						if(pkt.pts==AV_NOPTS_VALUE){
							//Write PTS
							AVRational time_base1=in_stream->time_base;
							//Duration between 2 frames (us)
							int64_t calc_duration=(double)AV_TIME_BASE/av_q2d(in_stream->r_frame_rate);
							//Parameters
							pkt.pts=(double)(frame_index*calc_duration)/(double)(av_q2d(time_base1)*AV_TIME_BASE);
							pkt.dts=pkt.pts;
							pkt.duration=(double)calc_duration/(double)(av_q2d(time_base1)*AV_TIME_BASE);
							frame_index++;
						}
						cur_pts_a=pkt.pts;
						break;
					}
				}while(av_read_frame(ifmt_ctx, &pkt) >= 0);
			}else{
				break;
			}
 
		}
 
#if USE_H264BSF
		av_bitstream_filter_filter(h264bsfc, in_stream->codec, NULL, &pkt.data, &pkt.size, pkt.data, pkt.size, 0);
#endif
#if USE_AACBSF
		av_bitstream_filter_filter(aacbsfc, out_stream->codec, NULL, &pkt.data, &pkt.size, pkt.data, pkt.size, 0);
#endif
 
 
		//Convert PTS/DTS
		pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
		pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
		pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
		pkt.pos = -1;
		pkt.stream_index=stream_index;
 
		//printf("Write 1 Packet. size:%5d\tpts:%lld\n",pkt.size,pkt.pts);
	
		av_interleaved_write_frame(ofmt_ctx, &pkt)		//Write
		av_free_packet(&pkt);
 
	}
			
	av_write_trailer(ofmt_ctx);  //Write file trailer
 
#if USE_H264BSF
	av_bitstream_filter_close(h264bsfc);
#endif
#if USE_AACBSF
	av_bitstream_filter_close(aacbsfc);
#endif
 
end:
	avformat_close_input(&ifmt_ctx_v);
	avformat_close_input(&ifmt_ctx_a);
	/* close output */
	if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
		avio_close(ofmt_ctx->pb);
	avformat_free_context(ofmt_ctx);
	if (ret < 0 && ret != AVERROR_EOF) {
		printf( "Error occurred.\n");
		return -1;
	}
	return 0;
}
 

h264有两种封装:
一、annexb模式,传统模式,有startcode,SPS和PPS是在ES中
二、mp4模式,一般mp4 mkv会有,没有startcode,SPS和PPS以及其它信息被封装在container中,每一个frame前面是这个frame的长度

很多解码器只支持annexb这种模式,因此需要将mp4做转换:

【在ffmpeg中用h264_mp4toannexb_filter可以做转换】

实现:初始化 + 过滤转换 + 释放关闭

1、注册AVBitStreamFilterContext

AVBitStreamFilterContext *h264bsfc; 
if (ifmt_ctx->streams[i]->codec->codec_id == AV_CODEC_ID_H264) 
{
	//这里注意:"h264_mp4toannexb",一定是这个字符串<-<-定义于h264_mp4toannexb_bsf.c
	h264bsfc = av_bitstream_filter_init("h264_mp4toannexb");
	//Create and initialize a bitstream filter context given a bitstream filter name.
	if (h264bsfc == NULL){printf("H264 mp4 to annexb失败!\n");return;}
}

2、使用AVBitStreamFilterContext转换AVpacket:
/*
	*每个AVPacket的data添加了H.264的NALU的起始码{0,0,0,1}
	*每个IDR帧数据前面添加了SPS和PPS
*/
/*也可以av_bitstream_filter_filter的处理源m&目标均为pkt 这样比较简洁*/
AVPacket pkt;
AVPacket TmpPkt;

av_read_frame(ifmt_ctx, &pkt)if (VideoIndex == pkt.stream_index)
{
	uint8_t *out_data = NULL;
	int out_size = 0;
 
	av_bitstream_filter_filter(h264bsfc,
						ofmt_ctx->streams[pkt.stream_index]->codec, NULL, 
						&out_data, &out_size,								//填充后的数据指针
						pkt.data, pkt.size, pkt.flags & AV_PKT_FLAG_KEY);	//处理前的指针
 
	av_init_packet(&TmpPkt);
	av_packet_copy_props(&TmpPkt, &pkt);
	av_packet_from_data(&TmpPkt, out_data, out_size);
	TmpPkt.size = out_size;
 
	av_packet_unref(&pkt);
	av_copy_packet(&pkt, &TmpPkt);
	av_packet_unref(&TmpPkt);
}

3、释放AVBitStreamFilterContext
if (h264bsfc != NULL)
{
	av_bitstream_filter_close(h264bsfc);
	h264bsfc = NULL;
}
发布了81 篇原创文章 · 获赞 1 · 访问量 2911

猜你喜欢

转载自blog.csdn.net/qq_42024067/article/details/103384897