还是先看一下主函数吧:(省略了很多无关大雅的代码)
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; }
相关推荐
FFmpeg源码的深入解析可以帮助开发者和研究人员更好地理解和掌握这一多媒体处理领域的核心技术。 根据提供的文件信息,FFMPEG/FFPLAY源码剖析的书籍或文档可能包含了以下知识点: ### 第一章:概述 #### 1.1 ...
当我们在编译OpenCV 4.1.0版本时,可能会遇到与FFmpeg相关的错误,如“FFMPEG: Download failed: 6;"Couldn't resolve host name"”。这个错误意味着在下载或更新FFmpeg库的过程中,系统无法解析主机名,...
FFmpeg4Android:视频文件推流到nginx服务器(源码) RTMP推流器(Streamer)的在流媒体系统中的作用可以用下图表示。首先将视频数据以RTMP的形式发送到流媒体服务器端(Server,比如FMS,Red5,Wowza等),然后...
本篇文章将详细介绍FFmpeg的源码及其在Windows环境下的编译过程。 首先,我们需要了解FFmpeg的基本构成。FFmpeg 包含了多个组件,如libavcodec(编码/解码库)、libavformat(容器/格式库)、libavfilter(滤镜库)...
作者裁剪了ffplay, 只留下AVI解码播放, 详细分析了代码结构. 是学习ffmpeg不可多得的好资料. 源码包里有需要的库, 可直接使用vs编译,调试. 目前试过VS2013, 可以编译,调试. 其他版本没试过
源码分析是深入理解FFmpeg工作原理、学习编程技巧和自定义功能的重要途径。 FFmpeg的核心功能包括: 1. **编解码**:FFmpeg支持大量的音视频编码格式,如H.264、AV1、VP9、AAC、MP3等,通过libavcodec库实现。源码...
总结起来,这个"ffdoc"教程全面覆盖了FFmpeg的各个方面,包括基本的命令行用法、核心库的使用、滤镜系统、源码分析以及实时流处理。无论是对FFmpeg感兴趣的业余爱好者还是专业的音视频开发人员,都能从中受益匪浅。...
FFmpeg的核心包括一系列的库,如libavcodec(编码/解码)、libavformat(容器格式处理)、libavfilter(滤镜系统)和libavutil(通用工具函数)。这些库协同工作,允许开发者处理各种多媒体数据。 **NDK和Android...
在Android平台上,编译FFmpeg源码是一项技术性较强的任务,因为FFmpeg是一个跨平台的音频和视频处理库,主要用于多媒体文件的编码、解码、流处理等操作。本篇文章将详细阐述如何在Android环境下利用JNI接口编译...
参考MSYS2: ://www.msys2.org/ 吃豆人镜像: : pacman代理: : FFmpeg: : MinGW / FFmpeg安装指南: : VSCode: ://code.visualstudio.com/ VSCode的C / C ++: ://code.visualstudio.com/docs/languages/cpp ...
在本文中,我们将深入探讨FFmpeg的源码,以ffmpeg-2.0.2.tar.gz为例,理解其架构、核心组件及其实现的关键技术。 FFmpeg的核心组件包括: 1. **libavcodec**:这是FFmpeg的编码库,负责各种音频和视频编码格式的...
FFmpeg的原始API虽然强大,但对初学者来说可能较为复杂,因为它的接口设计面向底层操作,涉及大量结构体和回调函数。FFmpegWrapper通过创建高级的C++类,将这些复杂的接口进行了封装,提供了更符合面向对象编程习惯...
该资源系统介绍了FFmpeg相关知识。具体内容如下:1. FFmpeg的命令行使用篇:ffmpeg、ffprobe、ffplay,各大平台如何编译2. FFmpeg 工具使用篇:ffmpe、ffprobe、ffplay 常用命令,该章节直接可以牛刀小试下。3. ...
该解决方案包含了使用FFmpeg进行封装格式处理的各种例子: simplest ffmpeg demuxer:视音频分离器 simplest ffmpeg demuxer simple:视音频分离器(简化版) simplest ffmpeg muxer:视音频复用器 simplest ...
通过分析源码,开发者可以学习如何利用多核CPU的优势,以及如何与GPU进行交互,以提升音视频处理的性能。 FFmpeg的libavformat库处理各种多媒体容器格式,如MP4、FLV、MKV等。在源码中,我们可以看到如何解析容器中...
- 官方源码下载:访问FFmpeg官网获取最新源码。 - 编译安装:遵循官方文档的编译步骤,通常包括configure、make和make install三步。 - 包管理器安装:Linux系统下可使用apt、yum等包管理器进行安装。 综上所述...
FFmpeg 源码分析: 1. 文件结构:FFmpeg 源码组织有序,主要分为几个关键部分,如 libavcodec(编码库)、libavformat(格式库)、libavfilter(滤镜库)和 libavutil(通用工具库)。每个库都包含了大量的模块,...
2. libavutil是FFmpeg的基础库,提供了通用的工具和方法,包括数据类型定义、内存管理、数学函数等基础功能。在文档中,对libavutil中的common.h、bswap.h、rational.h、mathematics.h以及avutil.h等文件进行了功能...
使用性能分析工具(如gprof、valgrind)对FFmpeg源码进行分析,找出瓶颈,进行针对性优化。同时,建立全面的测试用例,确保优化后的代码既快又稳定。 通过深入学习和研究FFmpeg的源码,开发者不仅可以定制适合自己...