`

ffmpeg源码分析:transcode()函数

 
阅读更多

 

还是先看一下主函数吧:(省略了很多无关大雅的代码)

 

int main(int argc, char **argv)  
{  
    OptionsContext o = { 0 };  
    int64_t ti;  
  
    //与命令行分析有关的结构的初始化,下面不再罗嗦  
    reset_options(&o, 0);  
  
    //设置日志级别  
    av_log_set_flags(AV_LOG_SKIP_REPEATED);  
    parse_loglevel(argc, argv, options);  
  
    if (argc > 1 && !strcmp(argv[1], "-d"))  {  
        run_as_daemon = 1;  
        av_log_set_callback(log_callback_null);  
        argc--;  
        argv++;  
    }  
  
    //注册组件们  
    avcodec_register_all();  
#if CONFIG_AVDEVICE  
    avdevice_register_all();  
#endif  
#if CONFIG_AVFILTER  
    avfilter_register_all();  
#endif  
    av_register_all();  
    //初始化网络,windows下需要  
    avformat_network_init();  
  
    show_banner();  
  
    term_init();  
  
    //分析命令行输入的参数们  
    parse_options(&o, argc, argv, options, opt_output_file);  
  
    //文件的转换就在此函数中发生  
    if (transcode(output_files, nb_output_files, input_files, nb_input_files)< 0)  
        exit_program(1);  
  
    exit_program(0);  
    return 0;  
}  

 

 

下面是transcode()函数,转换就发生在它里面.不废话,看注释吧,应很详细了

static int transcode(
		OutputFile *output_files,//输出文件数组
		int nb_output_files,//输出文件的数量
		InputFile *input_files,//输入文件数组
		int nb_input_files)//输入文件的数量
{
	int ret, i;
	AVFormatContext *is, *os;
	OutputStream *ost;
	InputStream *ist;
	uint8_t *no_packet;
	int no_packet_count = 0;
	int64_t timer_start;
	int key;

	if (!(no_packet = av_mallocz(nb_input_files)))
		exit_program(1);

	//设置编码参数,打开所有输出流的编码器,打开所有输入流的解码器,写入所有输出文件的文件头,于是准备好了
	ret = transcode_init(output_files, nb_output_files, input_files,nb_input_files);
	if (ret < 0)
		goto fail;

	if (!using_stdin){
		av_log(NULL, AV_LOG_INFO, "Press [q] to stop, [?] for help\n");
	}

	timer_start = av_gettime();

	//循环,直到收到系统信号才退出
	for (; received_sigterm == 0;)
	{
		int file_index, ist_index;
		AVPacket pkt;
		int64_t ipts_min;
		double opts_min;
		int64_t cur_time = av_gettime();

		ipts_min = INT64_MAX;
		opts_min = 1e100;
		/* if 'q' pressed, exits */
		if (!using_stdin)
		{
			//先查看用户按下了什么键,跟据键做出相应的反应
			static int64_t last_time;
			if (received_nb_signals)
				break;
			/* read_key() returns 0 on EOF */
			if (cur_time - last_time >= 100000 && !run_as_daemon){
				key = read_key();
				last_time = cur_time;
			}else{
<span>			</span>.................................
		}

		/* select the stream that we must read now by looking at the
		 smallest output pts */
		//下面这个循环的目的是找一个最小的输出pts(也就是离当前最近的)的输出流
		file_index = -1;
		for (i = 0; i < nb_output_streams; i++){
			OutputFile *of;
			int64_t ipts;
			double opts;
			ost = &output_streams[i];//循环每一个输出流
			of = &output_files[ost->file_index];//输出流对应的输出文件
			os = output_files[ost->file_index].ctx;//输出流对应的FormatContext
			ist = &input_streams[ost->source_index];//输出流对应的输入流

			if (ost->is_past_recording_time || //是否过了录制时间?(可能用户指定了一个录制时间段)
					no_packet[ist->file_index]|| //对应的输入流这个时间内没有数据?
					(os->pb && avio_tell(os->pb) >= of->limit_filesize))//是否超出了录制范围(也是用户指定的)
				continue;//是的,符合上面某一条,那么再看下一个输出流吧

			//判断当前输入流所在的文件是否可以使用(我也不很明白)
			opts = ost->st->pts.val * av_q2d(ost->st->time_base);
			ipts = ist->pts;
			if (!input_files[ist->file_index].eof_reached)	{
				if (ipts < ipts_min){
					//每找到一个pts更小的输入流就记录下来,这样循环完所有的输出流时就找到了
					//pts最小的输入流,及输入文件的序号
					ipts_min = ipts;
					if (input_sync)
						file_index = ist->file_index;
				}
				if (opts < opts_min){
					opts_min = opts;
					if (!input_sync)
						file_index = ist->file_index;
				}
			}

			//难道下面这句话的意思是:如果当前的输出流已接收的帧数,超出用户指定的输出最大帧数时,
			//则当前输出流所属的输出文件对应的所有输出流,都算超过了录像时间?
			if (ost->frame_number >= ost->max_frames){
				int j;
				for (j = 0; j < of->ctx->nb_streams; j++)
					output_streams[of->ost_index + j].is_past_recording_time =	1;
				continue;
			}
		}
		/* if none, if is finished */
		if (file_index < 0)	{
			//如果没有找到合适的输入文件
			if (no_packet_count){
				//如果是因为有的输入文件暂时得不到数据,则还不算是结束
				no_packet_count = 0;
				memset(no_packet, 0, nb_input_files);
				usleep(10000);
				continue;
			}
			//全部转换完成了,跳出大循环
			break;
		}

		//从找到的输入文件中读出一帧(可能是音频也可能是视频),并放到fifo队列中
		is = input_files[file_index].ctx;
		ret = av_read_frame(is, &pkt);
		if (ret == AVERROR(EAGAIN))	{
			//此时发生了暂时没数据的情况
			no_packet[file_index] = 1;
			no_packet_count++;
			continue;
		}

		//下文判断是否有输入文件到最后了
		if (ret < 0){
			input_files[file_index].eof_reached = 1;
			if (opt_shortest)
				break;
			else
				continue;
		}

		no_packet_count = 0;
		memset(no_packet, 0, nb_input_files);

		if (do_pkt_dump){
			av_pkt_dump_log2(NULL, AV_LOG_DEBUG, &pkt, do_hex_dump,
					is->streams[pkt.stream_index]);
		}
		/* the following test is needed in case new streams appear
		 dynamically in stream : we ignore them */
		//如果在输入文件中遇到一个忽然冒出的流,那么我们不鸟它
		if (pkt.stream_index >= input_files[file_index].nb_streams)
			goto discard_packet;

		//取得当前获得的帧对应的输入流
		ist_index = input_files[file_index].ist_index + pkt.stream_index;
		ist = &input_streams[ist_index];
		if (ist->discard)
			goto discard_packet;

		//重新鼓捣一下帧的时间戳
		if (pkt.dts != AV_NOPTS_VALUE)
			pkt.dts += av_rescale_q(input_files[ist->file_index].ts_offset,
					AV_TIME_BASE_Q, ist->st->time_base);
		if (pkt.pts != AV_NOPTS_VALUE)
			pkt.pts += av_rescale_q(input_files[ist->file_index].ts_offset,
					AV_TIME_BASE_Q, ist->st->time_base);

		if (pkt.pts != AV_NOPTS_VALUE)
			pkt.pts *= ist->ts_scale;
		if (pkt.dts != AV_NOPTS_VALUE)
			pkt.dts *= ist->ts_scale;

		if (pkt.dts != AV_NOPTS_VALUE && ist->next_pts != AV_NOPTS_VALUE
				&& (is->iformat->flags & AVFMT_TS_DISCONT))
		{
			int64_t pkt_dts = av_rescale_q(pkt.dts, ist->st->time_base,
					AV_TIME_BASE_Q);
			int64_t delta = pkt_dts - ist->next_pts;
			if ((delta < -1LL * dts_delta_threshold * AV_TIME_BASE
					|| (delta > 1LL * dts_delta_threshold * AV_TIME_BASE
							&& ist->st->codec->codec_type
									!= AVMEDIA_TYPE_SUBTITLE)
					|| pkt_dts + 1 < ist->pts) && !copy_ts)
			{
				input_files[ist->file_index].ts_offset -= delta;
				av_log(	NULL,	AV_LOG_DEBUG,
						"timestamp discontinuity %"PRId64", new offset= %"PRId64"\n",
						delta, input_files[ist->file_index].ts_offset);
				pkt.dts -= av_rescale_q(delta, AV_TIME_BASE_Q,	ist->st->time_base);
				if (pkt.pts != AV_NOPTS_VALUE)
					pkt.pts -= av_rescale_q(delta, AV_TIME_BASE_Q,	ist->st->time_base);
			}
		}

		//把这一帧转换并写入到输出文件中
		if (output_packet(ist, output_streams, nb_output_streams, &pkt) < 0){
			av_log(NULL, AV_LOG_ERROR,
					"Error while decoding stream #%d:%d\n",
					ist->file_index, ist->st->index);
			if (exit_on_error)
				exit_program(1);
			av_free_packet(&pkt);
			continue;
		}

discard_packet:
		av_free_packet(&pkt);

		/* dump report by using the output first video and audio streams */
		print_report(output_files, output_streams, nb_output_streams, 0,
				timer_start, cur_time);
	}

	//文件处理完了,把缓冲中剩余的数据写到输出文件中
	for (i = 0; i < nb_input_streams; i++){
		ist = &input_streams[i];
		if (ist->decoding_needed){
			output_packet(ist, output_streams, nb_output_streams, NULL);
		}
	}
	flush_encoders(output_streams, nb_output_streams);

	term_exit();

	//为输出文件写文件尾(有的不需要).
	for (i = 0; i < nb_output_files; i++){
		os = output_files[i].ctx;
		av_write_trailer(os);
	}

	/* dump report by using the first video and audio streams */
	print_report(output_files, output_streams, nb_output_streams, 1,
			timer_start, av_gettime());

	//关闭所有的编码器
	for (i = 0; i < nb_output_streams; i++){
		ost = &output_streams[i];
		if (ost->encoding_needed){
			av_freep(&ost->st->codec->stats_in);
			avcodec_close(ost->st->codec);
		}
#if CONFIG_AVFILTER
		avfilter_graph_free(&ost->graph);
#endif
	}

	//关闭所有的解码器
	for (i = 0; i < nb_input_streams; i++){
		ist = &input_streams[i];
		if (ist->decoding_needed){
			avcodec_close(ist->st->codec);
		}
	}

	/* finished ! */
	ret = 0;

	fail: av_freep(&bit_buffer);
	av_freep(&no_packet);

	if (output_streams)	{
		for (i = 0; i < nb_output_streams; i++)	{
			ost = &output_streams[i];
			if (ost)	{
				if (ost->stream_copy)
					av_freep(&ost->st->codec->extradata);
				if (ost->logfile){
					fclose(ost->logfile);
					ost->logfile = NULL;
				}
				av_fifo_free(ost->fifo); /* works even if fifo is not
				 initialized but set to zero */
				av_freep(&ost->st->codec->subtitle_header);
				av_free(ost->resample_frame.data[0]);
				av_free(ost->forced_kf_pts);
				if (ost->video_resample)
					sws_freeContext(ost->img_resample_ctx);
				swr_free(&ost->swr);
				av_dict_free(&ost->opts);
			}
		}
	}
	return ret;
}

 

 

原文地址:http://blog.csdn.net/niu_gao/article/details/7175421

分享到:
评论

相关推荐

    FFmpeg基础库编程开发

    9.3.4 Mplayer源码分析 429 第十章 开发实例 436 第十一章 mp4文件封装协议分析 436 11.1 概述 436 11.2 mp4的物理结构 436 11.3 数据的组织结构 437 11.4 mp4的时间结构 437 11.5 文件结构分析 438 11.5.1 File ...

    ffmpeg基础开发资料自总结

    9.3.4 Mplayer 源码分析 409 第十章 开发实例 416 第十一章 mp4 文件封装协议分析 416 11.1 概述 416 11.2 mp4 的物理结构 416 11.3 数据的组织结构 417 11.4 mp4 的时间结构 417 11.5 文件结构分析 418 11.5.1 File...

    ffmpeg基础编程WORD文档

    - 流程图和源码剖析有助于理解其工作原理。 #### 七、应用开发 - **7.1 ffmpeg库的使用:编码** - 如何使用FFmpeg库进行视频和音频的编码。 #### 八、关键函数介绍 - **avformat_open_input**:打开一个输入...

    最简单的基于FFMPEG的转码器(源代码)

    通过分析和实践这个简单的FFMPEG转码器源代码,开发者能够深入理解FFmpeg的工作原理,并在此基础上开发更复杂的多媒体处理应用,如视频编辑软件、直播推流服务等。同时,学习FFmpeg还能帮助开发者应对不同的多媒体...

    ffmpeg基础开发

    - FFmpeg库可以通过源码编译的方式安装。 - 编译选项可以定制化所需的功能。 **3.2 流媒体数据流程讲解** - 数据流从输入设备或文件读取,经过解码、滤镜处理后,再编码并输出到文件或设备。 **3.3 简单应用** -...

    php sg11扩展 各个版本

    对于更复杂的任务,如转码,可以使用`ffmpeg_transcode()`函数,它允许设置输出格式、质量、编码参数等。 对于PHP Sg11扩展在不同版本上的兼容性问题,开发者需要注意每个PHP版本的API变化。例如,从PHP5到PHP8,...

Global site tag (gtag.js) - Google Analytics