利用ffmpeg的API进行推流项目框架

本文详细介绍使用FFmpeg进行音视频推流的过程,包括输入输出格式上下文的初始化、音视频包的读取与处理等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先我们要确定我们要处理的对象,在用ffmpeg推视音频流的整个流程中,我们都需要围绕着输入AVFormatContext和输出AVFormatContext这两个结构体去处理。再者,我们需要两个结构体来指明输入输出视音频流的格式,即还需要一个AVInputFormat和AVOutputFormat。即以下这几个参数将会贯穿全文:

AVFormatContext  *ifmt_ctx, *ofmt_ctx;
AVInputFormat * ifmt;
AVOutputFormat * ofmt; 

接下来分析一下主要流程:

大方向上分为四个部分:

1、对ifmt_ctx初始化:需要对AVFormatContext中的AVInputFormat、AVIOContext、AVStream进行初始化。

2、对ofmt_ctx初始化:需要对AVOutputFormat进行初始化;由于我们只需要推流,不需要改变输入音视频流的编码格式,所以我们直接从输入Stream拿AVCodecContext给输出就行了。最后还要打开输出AVIOContext。

3、循环从ifmt_ctx中读出AVPacket,经过处理后写入ofmt_ctx中。

4、循环结束,释放ifmt_ctx和ofmt_ctx这些结构体。

 

下面详细分析每一步应该怎么做:

1、对ifmt_ctx初始化。

①得到输入AVInputFormat——ifmt,注意这里的AVInputFormat还没有跟ifmt_ctx有关联,要用avformat_open_input()函数,后文会说到。

ifmt = av_find_input_format("h264");     //AVInputFormat的初始化

②向OS申请一块内存buffer,写一个从输入缓冲区(也就是我们的音视频来源)读数据到buffer的函数,把这个函数指针和这个buffer都传给avio_alloc_context();得到一个AVIOContext,并赋给ifmt_ctx->pb。

unsigned char * iobuffer=(unsigned char *)av_malloc(32768);  
AVIOContext *avio =avio_alloc_context(iobuffer, 32768,0,pp,HISON_FILL_Buffer,NULL,NULL);
ifmt_ctx->pb=avio;    

③利用步骤①中得到ifmt来初始化ifmt_ctx中的AVInputFormat,注意这步同时为ifmt创建了AVStream,但是这个AVStream尚未初始化,第④步初始化AVStream。

avformat_open_input(&ifmt_ctx, NULL, ifmt, NULL);

④初始化ifmt_ctx中的每一条流AVStream。

avformat_find_stream_info(ifmt_ctx, 0);

 

2、ofmt_ctx的初始化

①创建输出AVFormatContext——ofmt_ctx,并对ofmt_ctx中的AVOutputFormat进行初始化。

avformat_alloc_output_context2(&ofmt_ctx, NULL, "flv", g_push_param->DestAddress);

这里通过格式名称"flv"来初始化AVOutputFormat,因为这样速度会比较快。

②为ofmt_ctx创建AVStream

out_stream = avformat_new_stream(ofmt_ctx, NULL);

③高版本使用avcodec_parameters_copy()来初始化输出AVCodec的上下文,而低版本则使用avcodec_copy_context()

avcodec_parameters_copy(out_stream->codecpar, in_codecpar);

// avcodec_copy_context(out_stream->codec, in_stream->codec);

④打开输出控制。

相对比,输入文件在avformat_open_input()打开了,而avformat_open_input()中就调用了avio_open();

avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE);

 

3、推流部分:

1)先往ofmt_ctx中写入封装头

avformat_write_header(ofmt_ctx, NULL);

2)循环体:

      ①从ifmt_ctx中读取一帧数据到AVPacket中。

av_read_frame(ifmt_ctx, &pkt);

      ②根据pkt中的stream_index确定现在是ofmt_ctx的哪一条流在工作。即确定in_stream和out_stream

      ③下面可以借鉴雷霄骅博客中的现有代码对out_stream中的pts、dts、duration、pos、延时进行设置。

if(pkt.pts==AV_NOPTS_VALUE){
			//Write PTS
			AVRational time_base1=ifmt_ctx->streams[videoindex]->time_base;
			//Duration between 2 frames (us)
			int64_t calc_duration=(double)AV_TIME_BASE/av_q2d(ifmt_ctx->streams[videoindex]->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);
		}
		//Important:Delay
		if(pkt.stream_index==videoindex){
			AVRational time_base=ifmt_ctx->streams[videoindex]->time_base;
			AVRational time_base_q={1,AV_TIME_BASE};
			int64_t pts_time = av_rescale_q(pkt.dts, time_base, time_base_q);
			int64_t now_time = av_gettime() - start_time;
			if (pts_time > now_time)
				av_usleep(pts_time - now_time);

		}

		in_stream  = ifmt_ctx->streams[pkt.stream_index];
		out_stream = ofmt_ctx->streams[pkt.stream_index];
		/* copy packet */
		//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;
		//Print to Screen
		if(pkt.stream_index==videoindex){
			printf("Send %8d video frames to output URL\n",frame_index);
			frame_index++;
		}

        ④把pkt间隔写入ofmt_ctx中。

av_interleaved_write_frame(ofmt_ctx, &pkt);

        ⑤继续循环之前要对pkt进行清理,以免影响下一次循环的数据。

av_free_packet(&pkt);

4、退出循环

读完ifmt_ctx中的数据时,退出循环,需要写封装的尾,也要释放以上这些数据结构。

av_write_trailer(ofmt_ctx);
end:
	avformat_close_input(&ifmt_ctx);
	/* close output */
	if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
		avio_close(ofmt_ctx->pb);
	avformat_free_context(ofmt_ctx);

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值