这个篇文档主要对ffmpeg 命令分析:主要分析ffmpeg 程序主要流程:
1. 分析命令行函数,解析出输入文件及输出文件
ffmpeg_parse_option
- int ffmpeg_parse_options(int argc, char **argv)
- {
- OptionParseContext octx;
- uint8_t error[128];
- int ret;
-
- memset(&octx, 0, sizeof(octx));
-
- /* split the commandline into an internal representation */
- ret = split_commandline(&octx, argc, argv, options, groups,
- FF_ARRAY_ELEMS(groups));
- if (ret < 0) {
- av_log(NULL, AV_LOG_FATAL, "Error splitting the argument list: ");
- goto fail;
- }
-
- /* apply global options */
- ret = parse_optgroup(NULL, &octx.global_opts);
- if (ret < 0) {
- av_log(NULL, AV_LOG_FATAL, "Error parsing global options: ");
- goto fail;
- }
-
- /* configure terminal and setup signal handlers */
- term_init();
-
- /* open input files */
- ret = open_files(&octx.groups[GROUP_INFILE], "input", open_input_file);
- if (ret < 0) {
- av_log(NULL, AV_LOG_FATAL, "Error opening input files: ");
- goto fail;
- }
-
- /* create the complex filtergraphs */
- ret = init_complex_filters();
- if (ret < 0) {
- av_log(NULL, AV_LOG_FATAL, "Error initializing complex filters.\n");
- goto fail;
- }
-
- /* open output files */
- ret = open_files(&octx.groups[GROUP_OUTFILE], "output", open_output_file);
- if (ret < 0) {
- av_log(NULL, AV_LOG_FATAL, "Error opening output files: ");
- goto fail;
- }
-
- check_filter_outputs();
-
- fail:
- uninit_parse_context(&octx);
- if (ret < 0) {
- av_strerror(ret, error, sizeof(error));
- av_log(NULL, AV_LOG_FATAL, "%s\n", error);
- }
- return ret;
- }
2. transcode 转码处理
- static int transcode(void)
- {
- int ret, i;
- AVFormatContext *os;
- OutputStream *ost;
- InputStream *ist;
- int64_t timer_start;
- int64_t total_packets_written = 0;
-
- ret = transcode_init(); // 转码初始化
- if (ret < 0)
- goto fail;
-
- if (stdin_interaction) {
- av_log(NULL, AV_LOG_INFO, "Press [q] to stop, [?] for help\n");
- }
-
- timer_start = av_gettime_relative();
-
- #if HAVE_THREADS
- if ((ret = init_input_threads()) < 0)
- goto fail;
- #endif
-
- while (!received_sigterm) {
- int64_t cur_time= av_gettime_relative();
-
- /* if 'q' pressed, exits */
- if (stdin_interaction)
- if (check_keyboard_interaction(cur_time) < 0)
- break;
-
- /* check if there's any stream where output is still needed */
- if (!need_output()) {
- av_log(NULL, AV_LOG_VERBOSE, "No more output streams to write to, finishing.\n");
- break;
- }
-
- ret = transcode_step();
- if (ret < 0 && ret != AVERROR_EOF) {
- av_log(NULL, AV_LOG_ERROR, "Error while filtering: %s\n", av_err2str(ret));
- break;
- }
-
- /* dump report by using the output first video and audio streams */
- print_report(0, timer_start, cur_time);
- }
- #if HAVE_THREADS
- free_input_threads();
- #endif
-
- /* at the end of stream, we must flush the decoder buffers */
- for (i = 0; i < nb_input_streams; i++) {
- ist = input_streams[i];
- if (!input_files[ist->file_index]->eof_reached) {
- process_input_packet(ist, NULL, 0);
- }
- }
- flush_encoders();
-
- term_exit();
-
- /* write the trailer if needed and close file */
- for (i = 0; i < nb_output_files; i++) {
- os = output_files[i]->ctx;
- if (!output_files[i]->header_written) {
- av_log(NULL, AV_LOG_ERROR,
- "Nothing was written into output file %d (%s), because "
- "at least one of its streams received no packets.\n",
- i, os->url);
- continue;
- }
- if ((ret = av_write_trailer(os)) < 0) {
- av_log(NULL, AV_LOG_ERROR, "Error writing trailer of %s: %s\n", os->url, av_err2str(ret));
- if (exit_on_error)
- exit_program(1);
- }
- }
-
- /* dump report by using the first video and audio streams */
- print_report(1, timer_start, av_gettime_relative());
-
- /* close each encoder */
- for (i = 0; i < nb_output_streams; i++) {
- ost = output_streams[i];
- if (ost->encoding_needed) {
- av_freep(&ost->enc_ctx->stats_in);
- }
- total_packets_written += ost->packets_written;
- if (!ost->packets_written && (abort_on_flags & ABORT_ON_FLAG_EMPTY_OUTPUT_STREAM)) {
- av_log(NULL, AV_LOG_FATAL, "Empty output on stream %d.\n", i);
- exit_program(1);
- }
- }
-
- if (!total_packets_written && (abort_on_flags & ABORT_ON_FLAG_EMPTY_OUTPUT)) {
- av_log(NULL, AV_LOG_FATAL, "Empty output\n");
- exit_program(1);
- }
-
- /* close each decoder */
- for (i = 0; i < nb_input_streams; i++) {
- ist = input_streams[i];
- if (ist->decoding_needed) {
- avcodec_close(ist->dec_ctx);
- if (ist->hwaccel_uninit)
- ist->hwaccel_uninit(ist->dec_ctx);
- }
- }
-
- hw_device_free_all();
-
- /* finished ! */
- ret = 0;
-
- fail:
- #if HAVE_THREADS
- free_input_threads();
- #endif
-
- if (output_streams) {
- for (i = 0; i < nb_output_streams; i++) {
- ost = output_streams[i];
- if (ost) {
- if (ost->logfile) {
- if (fclose(ost->logfile))
- av_log(NULL, AV_LOG_ERROR,
- "Error closing logfile, loss of information possible: %s\n",
- av_err2str(AVERROR(errno)));
- ost->logfile = NULL;
- }
- av_freep(&ost->forced_kf_pts);
- av_freep(&ost->apad);
- av_freep(&ost->disposition);
- av_dict_free(&ost->encoder_opts);
- av_dict_free(&ost->sws_dict);
- av_dict_free(&ost->swr_opts);
- av_dict_free(&ost->resample_opts);
- }
- }
- }
- return ret;
- }
2.1 transcode_step // 转换流程。
process_input
process_input_packet
- /* pkt = NULL means EOF (needed to flush decoder buffers) */
- static int process_input_packet(InputStream *ist, const AVPacket *pkt, int no_eof)
- {
- int ret = 0, i;
- int repeating = 0;
- int eof_reached = 0;
-
- AVPacket avpkt;
- if (!ist->saw_first_ts) {
- ist->dts = ist->st->avg_frame_rate.num ? - ist->dec_ctx->has_b_frames * AV_TIME_BASE / av_q2d(ist->st->avg_frame_rate) : 0;
- ist->pts = 0;
- if (pkt && pkt->pts != AV_NOPTS_VALUE && !ist->decoding_needed) {
- ist->dts += av_rescale_q(pkt->pts, ist->st->time_base, AV_TIME_BASE_Q);
- ist->pts = ist->dts; //unused but better to set it to a value thats not totally wrong
- }
- ist->saw_first_ts = 1;
- }
-
- if (ist->next_dts == AV_NOPTS_VALUE)
- ist->next_dts = ist->dts;
- if (ist->next_pts == AV_NOPTS_VALUE)
- ist->next_pts = ist->pts;
-
- if (!pkt) {
- /* EOF handling */
- av_init_packet(&avpkt);
- avpkt.data = NULL;
- avpkt.size = 0;
- } else {
- avpkt = *pkt;
- }
-
- if (pkt && pkt->dts != AV_NOPTS_VALUE) {
- ist->next_dts = ist->dts = av_rescale_q(pkt->dts, ist->st->time_base, AV_TIME_BASE_Q);
- if (ist->dec_ctx->codec_type != AVMEDIA_TYPE_VIDEO || !ist->decoding_needed)
- ist->next_pts = ist->pts = ist->dts;
- }
-
- // while we have more to decode or while the decoder did output something on EOF
- while (ist->decoding_needed) {
- int64_t duration_dts = 0;
- int64_t duration_pts = 0;
- int got_output = 0;
- int decode_failed = 0;
-
- ist->pts = ist->next_pts;
- ist->dts = ist->next_dts;
-
- switch (ist->dec_ctx->codec_type) {
- case AVMEDIA_TYPE_AUDIO:
- ret = decode_audio (ist, repeating ? NULL : &avpkt, &got_output,
- &decode_failed);
- break;
- case AVMEDIA_TYPE_VIDEO:
- ret = decode_video (ist, repeating ? NULL : &avpkt, &got_output, &duration_pts, !pkt,
- &decode_failed);
- if (!repeating || !pkt || got_output) {
- if (pkt && pkt->duration) {
- duration_dts = av_rescale_q(pkt->duration, ist->st->time_base, AV_TIME_BASE_Q);
- } else if(ist->dec_ctx->framerate.num != 0 && ist->dec_ctx->framerate.den != 0) {
- int ticks= av_stream_get_parser(ist->st) ? av_stream_get_parser(ist->st)->repeat_pict+1 : ist->dec_ctx->ticks_per_frame;
- duration_dts = ((int64_t)AV_TIME_BASE *
- ist->dec_ctx->framerate.den * ticks) /
- ist->dec_ctx->framerate.num / ist->dec_ctx->ticks_per_frame;
- }
-
- if(ist->dts != AV_NOPTS_VALUE && duration_dts) {
- ist->next_dts += duration_dts;
- }else
- ist->next_dts = AV_NOPTS_VALUE;
- }
-
- if (got_output) {
- if (duration_pts > 0) {
- ist->next_pts += av_rescale_q(duration_pts, ist->st->time_base, AV_TIME_BASE_Q);
- } else {
- ist->next_pts += duration_dts;
- }
- }
- break;
- case AVMEDIA_TYPE_SUBTITLE:
- if (repeating)
- break;
- ret = transcode_subtitles(ist, &avpkt, &got_output, &decode_failed);
- if (!pkt && ret >= 0)
- ret = AVERROR_EOF;
- break;
- default:
- return -1;
- }
-
- if (ret == AVERROR_EOF) {
- eof_reached = 1;
- break;
- }
-
- if (ret < 0) {
- if (decode_failed) {
- av_log(NULL, AV_LOG_ERROR, "Error while decoding stream #%d:%d: %s\n",
- ist->file_index, ist->st->index, av_err2str(ret));
- } else {
- av_log(NULL, AV_LOG_FATAL, "Error while processing the decoded "
- "data for stream #%d:%d\n", ist->file_index, ist->st->index);
- }
- if (!decode_failed || exit_on_error)
- exit_program(1);
- break;
- }
-
- if (got_output)
- ist->got_output = 1;
-
- if (!got_output)
- break;
-
- // During draining, we might get multiple output frames in this loop.
- // ffmpeg.c does not drain the filter chain on configuration changes,
- // which means if we send multiple frames at once to the filters, and
- // one of those frames changes configuration, the buffered frames will
- // be lost. This can upset certain FATE tests.
- // Decode only 1 frame per call on EOF to appease these FATE tests.
- // The ideal solution would be to rewrite decoding to use the new
- // decoding API in a better way.
- if (!pkt)
- break;
-
- repeating = 1;
- }
-
- /* after flushing, send an EOF on all the filter inputs attached to the stream */
- /* except when looping we need to flush but not to send an EOF */
- if (!pkt && ist->decoding_needed && eof_reached && !no_eof) {
- int ret = send_filter_eof(ist);
- if (ret < 0) {
- av_log(NULL, AV_LOG_FATAL, "Error marking filters as finished\n");
- exit_program(1);
- }
- }
-
- /* handle stream copy */
- if (!ist->decoding_needed && pkt) {
- ist->dts = ist->next_dts;
- switch (ist->dec_ctx->codec_type) {
- case AVMEDIA_TYPE_AUDIO:
- av_assert1(pkt->duration >= 0);
- if (ist->dec_ctx->sample_rate) {
- ist->next_dts += ((int64_t)AV_TIME_BASE * ist->dec_ctx->frame_size) /
- ist->dec_ctx->sample_rate;
- } else {
- ist->next_dts += av_rescale_q(pkt->duration, ist->st->time_base, AV_TIME_BASE_Q);
- }
- break;
- case AVMEDIA_TYPE_VIDEO:
- if (ist->framerate.num) {
- // TODO: Remove work-around for c99-to-c89 issue 7
- AVRational time_base_q = AV_TIME_BASE_Q;
- int64_t next_dts = av_rescale_q(ist->next_dts, time_base_q, av_inv_q(ist->framerate));
- ist->next_dts = av_rescale_q(next_dts + 1, av_inv_q(ist->framerate), time_base_q);
- } else if (pkt->duration) {
- ist->next_dts += av_rescale_q(pkt->duration, ist->st->time_base, AV_TIME_BASE_Q);
- } else if(ist->dec_ctx->framerate.num != 0) {
- int ticks= av_stream_get_parser(ist->st) ? av_stream_get_parser(ist->st)->repeat_pict + 1 : ist->dec_ctx->ticks_per_frame;
- ist->next_dts += ((int64_t)AV_TIME_BASE *
- ist->dec_ctx->framerate.den * ticks) /
- ist->dec_ctx->framerate.num / ist->dec_ctx->ticks_per_frame;
- }
- break;
- }
- ist->pts = ist->dts;
- ist->next_pts = ist->next_dts;
- }
- for (i = 0; i < nb_output_streams; i++) {
- OutputStream *ost = output_streams[i];
-
- if (!check_output_constraints(ist, ost) || ost->encoding_needed)
- continue;
-
- do_streamcopy(ist, ost, pkt); // packet 拷贝到输出文件
- }
-
- return !eof_reached;
- }
3. do_streamcopy
do_streamcopy
output_packet
write_packet(OutputFile *of, AVPacket *pkt, OutputStream *ost, int unqueue)
av_interleaved_write_frame
write_packets_common
write_packet(AVFormatContext *s, AVPacket *pkt)
s->oformat->write_packet
AVOutputFormat *oformat; mux 中函数: