FFmpeg源码剖析-通用:ffmpeg_parse_options()

Posted 北雨南萍

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了FFmpeg源码剖析-通用:ffmpeg_parse_options()相关的知识,希望对你有一定的参考价值。

ffmpeg_parse_options()函数位于ffmpeg_opt.c
 

1. 函数概述

它的功能主要有三个,
解析命令行参数;
打开输入文件,并解析数据,找到匹配每一个视频,音频,数据流的解码器;
打开输出文件,并设置好输出的视频,音频,数据流的编码器;

 

2. 函数调用结构图


图 ffmpeg_parse_options()函数调用结构


3. 代码分析
int ffmpeg_parse_options(int argc, char **argv)

    OptionParseContext octx;


    /* split the commandline into an internal representation */


    ret = split_commandline(&octx, argc, argv, options, groups,


                            FF_ARRAY_ELEMS(groups));


    /* apply global options */


    ret = parse_optgroup(NULL, &octx.global_opts);


    /* open input files */


    ret = open_files(&octx.groups[GROUP_INFILE], "input", open_input_file);


    /* open output files */


    ret = open_files(&octx.groups[GROUP_OUTFILE], "output", open_output_file);


    return ret;



它主要调用了两次open_files()函数,一次用于打开输入文件,一次用于打开输出文件。


3.1 open_files()
static int open_files(OptionGroupList *l, const char *inout,


                      int (*open_file)(OptionsContext*, const char*))



    int i, ret;




    for (i = 0; i < l->nb_groups; i++)


        OptionGroup *g = &l->groups[i];


        OptionsContext o;




        init_options(&o);


        o.g = g;




        ret = parse_optgroup(&o, g);


        if (ret < 0)


            av_log(NULL, AV_LOG_ERROR, "Error parsing options for %s file "


                   "%s.\\n", inout, g->arg);


            return ret;


       




        av_log(NULL, AV_LOG_DEBUG, "Opening an %s file: %s.\\n", inout, g->arg);


        ret = open_file(&o, g->arg);


        uninit_options(&o);


        if (ret < 0)


            av_log(NULL, AV_LOG_ERROR, "Error opening %s file %s.\\n",


                   inout, g->arg);


            return ret;


       


        av_log(NULL, AV_LOG_DEBUG, "Successfully opened the file.\\n");


   




    return 0;





用for循环,是当有多个输入文件时,需要一一打开;
其中open_file()是主要函数,它是一个函数指针,运行时调用的是指针所指向的函数。


3.2 open_input_file()
static int open_input_file(OptionsContext *o, const char *filename)

    InputFile *f;
    AVFormatContext *ic;
    AVInputFormat *file_iformat = NULL;
    int err, i, ret;
    int64_t timestamp;
    AVDictionary **opts;
    AVDictionary *unused_opts = NULL;
    AVDictionaryEntry *e = NULL;
    int orig_nb_streams;                     // number of streams before avformat_find_stream_info
    char *   video_codec_name = NULL;
    char *   audio_codec_name = NULL;
    char *subtitle_codec_name = NULL;
    char *    data_codec_name = NULL;
    int scan_all_pmts_set = 0;


    if (o->format)
        if (!(file_iformat = av_find_input_format(o->format)))
            av_log(NULL, AV_LOG_FATAL, "Unknown input format: '%s'\\n", o->format);
            exit_program(1);
       
   


    if (!strcmp(filename, "-"))
        filename = "pipe:";


    stdin_interaction &= strncmp(filename, "pipe:", 5) &&
                         strcmp(filename, "/dev/stdin");


    /* get default parameters from command line */
    ic = avformat_alloc_context();
    if (!ic)
        print_error(filename, AVERROR(ENOMEM));
        exit_program(1);
   


/* 这些参数没有设置的话,这段代码都不会执行*/
...
/* END */


    /* If not enough info to get the stream parameters, we decode the
       first frames to get it. (used in mpeg case for example) */
    /* 读取文件数据,并进行相应的流分析,找到匹配的解码器 */
    ret = avformat_find_stream_info(ic, opts);
    if (ret < 0)
        av_log(NULL, AV_LOG_FATAL, "%s: could not find codec parameters\\n", filename);
        if (ic->nb_streams == 0)
            avformat_close_input(&ic);
            exit_program(1);
       
   


    if (o->start_time_eof != AV_NOPTS_VALUE)
        if (ic->duration>0)
            o->start_time = o->start_time_eof + ic->duration;
        else
            av_log(NULL, AV_LOG_WARNING, "Cannot use -sseof, duration of %s not known\\n", filename);
   
    timestamp = (o->start_time == AV_NOPTS_VALUE) ? 0 : o->start_time;
    /* add the stream start time */
    if (!o->seek_timestamp && ic->start_time != AV_NOPTS_VALUE)
        timestamp += ic->start_time;


    /* if seeking requested, we execute it */
    if (o->start_time != AV_NOPTS_VALUE)
        int64_t seek_timestamp = timestamp;


        if (!(ic->iformat->flags & AVFMT_SEEK_TO_PTS))
            int dts_heuristic = 0;
            for (i=0; i<ic->nb_streams; i++)
                const AVCodecParameters *par = ic->streams[i]->codecpar;
                if (par->video_delay)
                    dts_heuristic = 1;
           
            if (dts_heuristic)
                seek_timestamp -= 3*AV_TIME_BASE / 23;
           
       
        ret = avformat_seek_file(ic, -1, INT64_MIN, seek_timestamp, seek_timestamp, 0);
        if (ret < 0)
            av_log(NULL, AV_LOG_WARNING, "%s: could not seek to position %0.3f\\n",
                   filename, (double)timestamp / AV_TIME_BASE);
       
   


    /* update the current parameters so that they match the one of the input stream */
    add_input_streams(o, ic);


    /* 在这个地方会打印出所有解析出来的输入文件的信息,如:
    Input #0, flv, from '/opt/ffmpeg/test-samples/cosmos-30fps.flv':
    Metadata:
    major_brand     : isom
    minor_version   : 1
    compatible_brands: isomavc1
    encoder         : Lavf56.4.101
    server          : 
    srs_primary     : 
    srs_authors     : 
    server_version  : 2.0.209
    Duration: 00:02:06.38, start: 0.010000, bitrate: 615 kb/s
    Stream #0:0: Audio: aac (LC), 48000 Hz, stereo, fltp
    Stream #0:1: Video: h264 (High), yuv420p(progressive), 640x480 [SAR 135:101 DAR 180:101], 30.30 fps, 30 tbr, 1k tbn, 60 tbc
1067        GROW_ARRAY(input_files, nb_input_files);
     */
    av_dump_format(ic, nb_input_files, filename, 0);


    GROW_ARRAY(input_files, nb_input_files);
    f = av_mallocz(sizeof(*f));
    if (!f)
        exit_program(1);
    input_files[nb_input_files - 1] = f;


    f->ctx        = ic;
    f->ist_index  = nb_input_streams - ic->nb_streams;
    f->start_time = o->start_time;
    f->recording_time = o->recording_time;
    f->input_ts_offset = o->input_ts_offset;
    f->ts_offset  = o->input_ts_offset - (copy_ts ? (start_at_zero && ic->start_time != AV_NOPTS_VALUE ? ic->start_time : 0) : timestamp);
    f->nb_streams = ic->nb_streams;
    f->rate_emu   = o->rate_emu;
    f->accurate_seek = o->accurate_seek;
    f->loop = o->loop;
    f->duration = 0;
    f->time_base = (AVRational) 1, 1 ;
#if HAVE_PTHREADS
    f->thread_queue_size = o->thread_queue_size > 0 ? o->thread_queue_size : 8;
#endif


    /* check if all codec options have been used */
    unused_opts = strip_specifiers(o->g->codec_opts);
    for (i = f->ist_index; i < nb_input_streams; i++)
        e = NULL;
        while ((e = av_dict_get(input_streams[i]->decoder_opts, "", e,
                                AV_DICT_IGNORE_SUFFIX)))
            av_dict_set(&unused_opts, e->key, NULL, 0);
   


    e = NULL;
    while ((e = av_dict_get(unused_opts, "", e, AV_DICT_IGNORE_SUFFIX)))
        const AVClass *class = avcodec_get_class();
        const AVOption *option = av_opt_find(&class, e->key, NULL, 0,
                                             AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ);
        const AVClass *fclass = avformat_get_class();
        const AVOption *foption = av_opt_find(&fclass, e->key, NULL, 0,
                                             AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ);
        if (!option || foption)
            continue;




        if (!(option->flags & AV_OPT_FLAG_DECODING_PARAM))
            av_log(NULL, AV_LOG_ERROR, "Codec AVOption %s (%s) specified for "
                   "input file #%d (%s) is not a decoding option.\\n", e->key,
                   option->help ? option->help : "", nb_input_files - 1,
                   filename);
            exit_program(1);
       


        av_log(NULL, AV_LOG_WARNING, "Codec AVOption %s (%s) specified for "
               "input file #%d (%s) has not been used for any stream. The most "
               "likely reason is either wrong type (e.g. a video option with "
               "no video streams) or that it is a private option of some decoder "
               "which was not actually used for any stream.\\n", e->key,
               option->help ? option->help : "", nb_input_files - 1, filename);
   
    av_dict_free(&unused_opts);


    for (i = 0; i < o->nb_dump_attachment; i++)
        int j;


        for (j = 0; j < ic->nb_streams; j++)
            AVStream *st = ic->streams[j];


            if (check_stream_specifier(ic, st, o->dump_attachment[i].specifier) == 1)
                dump_attachment(st, o->dump_attachment[i].u.str);
       
   


    for (i = 0; i < orig_nb_streams; i++)
        av_dict_free(&opts[i]);
    av_freep(&opts);


    input_stream_potentially_available = 1;


    return 0;



3.2.1 avformat_find_stream_info()
int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options)

    int i, count = 0, ret = 0, j;
    int64_t read_size;
    AVStream *st;
    AVCodecContext *avctx;
    AVPacket pkt1, *pkt;
    int64_t old_offset  = avio_tell(ic->pb);
    // new streams might appear, no options for those
    int orig_nb_streams = ic->nb_streams;
    int flush_codecs;
    int64_t max_analyze_duration = ic->max_analyze_duration;
    int64_t max_stream_analyze_duration;
    int64_t max_subtitle_analyze_duration;
    int64_t probesize = ic->probesize;
    int eof_reached = 0;
    int *missing_streams = av_opt_ptr(ic->iformat->priv_class, ic->priv_data, "missing_streams");


    flush_codecs = probesize > 0;


    av_opt_set(ic, "skip_clear", "1", AV_OPT_SEARCH_CHILDREN);


    max_stream_analyze_duration = max_analyze_duration;
    max_subtitle_analyze_duration = max_analyze_duration;


    /* 如果没有在命令行参数中设置 max_analyze_duration, 
       则设置max_stream_analyze_duration ,max_analyze_duration 为系统默认最大的分析时长(默认值为5000000)
       设置 max_subtitle_analyze_duration 为30000000.
       AV_TIME_BASE是FFmpeg内部的时钟基准的整数表示, 
       #define AV_TIME_BASE 1000000
     */
    if (!max_analyze_duration)
        max_stream_analyze_duration =
        max_analyze_duration        = 5*AV_TIME_BASE;
        max_subtitle_analyze_duration = 30*AV_TIME_BASE;


        /* 如果输入文件是FLV格式,则需要设置更长的分析时间
        if (!strcmp(ic->iformat->name, "flv"))
            max_stream_analyze_duration = 90*AV_TIME_BASE;
        if (!strcmp(ic->iformat->name, "mpeg") || !strcmp(ic->iformat->name, "mpegts"))
            max_stream_analyze_duration = 7*AV_TIME_BASE;
   


    if (ic->pb)
        av_log(ic, AV_LOG_DEBUG, "Before avformat_find_stream_info() pos: %"PRId64" bytes read:%"PRId64" seeks:%d nb_streams:%d\\n",
               avio_tell(ic->pb), ic->pb->bytes_read, ic->pb->seek_count, ic->nb_streams);


    /* 读取文件的数据到AVPacket,解析出每个流对应的解码器,
       像flv文件,可能要读120个TAG */
    read_size = 0;


    for (;;)


        int analyzed_all_streams;
        if (ff_check_interrupt(&ic->interrupt_callback))
            ret = AVERROR_EXIT;
            av_log(ic, AV_LOG_DEBUG, "interrupted\\n");
            break;
       


        /* check if one codec still needs to be handled */
        /* 需要分析出每一个流所应该匹配的解码器 */
        for (i = 0; i < ic->nb_streams; i++)
            int fps_analyze_framecount = 20;


            st = ic->streams[i];
            if (!has_codec_parameters(st, NULL))
                break;
            /* If the timebase is coarse (like the usual millisecond precision
             * of mkv), we need to analyze more frames to reliably arrive at
             * the correct fps. */
            if (av_q2d(st->time_base) > 0.0005)
                fps_analyze_framecount *= 2;
            if (!tb_unreliable(st->internal->avctx))
                fps_analyze_framecount = 0;
            if (ic->fps_probe_size >= 0)
                fps_analyze_framecount = ic->fps_probe_size;
            if (st->disposition & AV_DISPOSITION_ATTACHED_PIC)
                fps_analyze_framecount = 0;
            /* variable fps and no guess at the real fps */
            if (!(st->r_frame_rate.num && st->avg_frame_rate.num) &&
                st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
                int count = (ic->iformat->flags & AVFMT_NOTIMESTAMPS) ?
                    st->info->codec_info_duration_fields/2 :
                    st->info->duration_count;
                if (count < fps_analyze_framecount)
                    break;
           
            if (st->parser && st->parser->parser->split &&
                !st->internal->avctx->extradata)
                break;
            if (st->first_dts == AV_NOPTS_VALUE &&
                !(ic->iformat->flags & AVFMT_NOTIMESTAMPS) &&
                st->codec_info_nb_frames < ((st->disposition & AV_DISPOSITION_ATTACHED_PIC) ? 1 : ic->max_ts_probe) &&
                (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ||
                 st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO))
                break;
       
        analyzed_all_streams = 0;
        if (!missing_streams || !*missing_streams)
        if (i == ic->nb_streams)
            analyzed_all_streams = 1;
            /* NOTE: If the format has no header, then we need to read some
             * packets to get most of the streams, so we cannot stop here. */
            if (!(ic->ctx_flags & AVFMTCTX_NOHEADER))
                /* If we found the info for all the codecs, we can stop. */
                ret = count;
                av_log(ic, AV_LOG_DEBUG, "All info found\\n");
                flush_codecs = 0;
                break;
           
       
        /* We did not get all the codec info, but we read too much data. */
        if (read_size >= probesize)
            ret = count;
            av_log(ic, AV_LOG_DEBUG,
                   "Probe buffer size limit of %"PRId64" bytes reached\\n", probesize);
            for (i = 0; i < ic->nb_streams; i++)
                if (!ic->streams[i]->r_frame_rate.num &&
                    ic->streams[i]->info->duration_count <= 1 &&
                    ic->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
                    strcmp(ic->iformat->name, "image2"))
                    av_log(ic, AV_LOG_WARNING,
                           "Stream #%d: not enough frames to estimate rate; "
                           "consider increasing probesize\\n", i);
            break;
       




        /* NOTE: A new stream can be added there if no header in file
         * (AVFMTCTX_NOHEADER). */
        /* 从文件中读取数据包到AVPacket中,并进行分析 */
        ret = read_frame_internal(ic, &pkt1);
        if (ret == AVERROR(EAGAIN))
            continue;


        if (ret < 0)
            /* EOF or error*/
            eof_reached = 1;
            break;
       


        pkt = &pkt1;


        if (!(ic->flags & AVFMT_FLAG_NOBUFFER))
            /* 将pkt添加到ic->internal->packet_buffer的链表尾部(即ic->internal->packet_buffer_end)之后,*/
            ret = add_to_pktbuf(&ic->internal->packet_buffer, pkt,
                                &ic->internal->packet_buffer_end, 0);
            if (ret < 0)
                goto find_stream_info_err;
       


        st = ic->streams[pkt->stream_index];
        if (!(st->disposition & AV_DISPOSITION_ATTACHED_PIC))
            read_size += pkt->size;


        avctx = st->internal->avctx;
        if (!st->internal->avctx_inited)
            ret = avcodec_parameters_to_context(avctx, st->codecpar);
            if (ret < 0)
                goto find_stream_info_err;
            st->internal->avctx_inited = 1;
       




#if FF_API_R_FRAME_RATE
        if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
            ff_rfps_add_frame(ic, st, pkt->dts);
#endif




        /* If still no information, we try to open the codec and to
         * decompress the frame. We try to avoid that in most cases as
         * it takes longer and uses more memory. For MPEG-4, we need to
         * decompress for QuickTime.
         *
         * If AV_CODEC_CAP_CHANNEL_CONF is set this will force decoding of at
         * least one frame of codec data, this makes sure the codec initializes
         * the channel configuration and does not only trust the values from
         * the container. */
        try_decode_frame(ic, st, pkt,
                         (options && i < orig_nb_streams) ? &options[i] : NULL);


        if (ic->flags & AVFMT_FLAG_NOBUFFER)
            av_packet_unref(pkt);


        st->codec_info_nb_frames++;
        count++;
    // END OF for(;;)




    if (eof_reached)
        int stream_index;
        for (stream_index = 0; stream_index < ic->nb_streams; stream_index++)
            st = ic->streams[stream_index];
            avctx = st->internal->avctx;
            if (!has_codec_parameters(st, NULL))
                const AVCodec *codec = find_probe_decoder(ic, st, st->codecpar->codec_id);
                if (codec && !avctx->codec)
                    if (avcodec_open2(avctx, codec, (options && stream_index < orig_nb_streams) ? &options[stream_index] : NULL) < 0)
                        av_log(ic, AV_LOG_WARNING,
                            "Failed to open codec in av_find_stream_info\\n");
               
           


            // EOF already reached while reading the stream above.
            // So continue with reoordering DTS with whatever delay we have.
            if (ic->internal->packet_buffer && !has_decode_delay_been_guessed(st))
                update_dts_from_pts(ic, stream_index, ic->internal->packet_buffer);
           
       
   


    if (flush_codecs)
        AVPacket empty_pkt = 0 ;
        int err = 0;
        av_init_packet(&empty_pkt);


        for (i = 0; i < ic->nb_streams; i++)


            st = ic->streams[i];


            /* flush the decoders */
            if (st->info->found_decoder == 1)
                do
                    err = try_decode_frame(ic, st, &empty_pkt,
                                            (options && i < orig_nb_streams)
                                            ? &options[i] : NULL);
                while (err > 0 && !has_codec_parameters(st, NULL));


                if (err < 0)
                    av_log(ic, AV_LOG_INFO,
                        "decoding for stream %d failed\\n", st->index);
               
           
       
   


    // close codecs which were opened in try_decode_frame()
    for (i = 0; i < ic->nb_streams; i++)
        st = ic->streams[i];
        avcodec_close(st->internal->avctx);
   


    ff_rfps_calculate(ic);


    for (i = 0; i < ic->nb_streams; i++)
        st = ic->streams[i];
        avctx = st->internal->avctx;


        if (avctx->codec_type == AVMEDIA_TYPE_VIDEO)
            if (avctx->codec_id == AV_CODEC_ID_RAWVIDEO && !avctx->codec_tag && !avctx->bits_per_coded_sample)
                uint32_t tag= avcodec_pix_fmt_to_codec_tag(avctx->pix_fmt);
                if (avpriv_find_pix_fmt(avpriv_get_raw_pix_fmt_tags(), tag) == avctx->pix_fmt)
                    avctx->codec_tag= tag;
           


            /* estimate average framerate if not set by demuxer */
            if (st->info->codec_info_duration_fields &&
                !st->avg_frame_rate.num &&
                st->info->codec_info_duration)
                int best_fps      = 0;
                double best_error = 0.01;


                if (st->info->codec_info_duration        >= INT64_MAX / st->time_base.num / 2||
                    st->info->codec_info_duration_fields >= INT64_MAX / st->time_base.den ||
                    st->info->codec_info_duration        < 0)
                    continue;
                av_reduce(&st->avg_frame_rate.num, &st->avg_frame_rate.den,
                          st->info->codec_info_duration_fields * (int64_t) st->time_base.den,
                          st->info->codec_info_duration * 2 * (int64_t) st->time_base.num, 60000);


                /* Round guessed framerate to a "standard" framerate if it's
                 * within 1% of the original estimate. */
                for (j = 0; j < MAX_STD_TIMEBASES; j++)
                    AVRational std_fps = get_std_framerate(j), 12 * 1001 ;
                    double error       = fabs(av_q2d(st->avg_frame_rate) /
                                              av_q2d(std_fps) - 1);


                    if (error < best_error)
                        best_error = error;
                        best_fps   = std_fps.num;
                   
               
                if (best_fps)
                    av_reduce(&st->avg_frame_rate.num, &st->avg_frame_rate.den,
                              best_fps, 12 * 1001, INT_MAX);
           


            if (!st->r_frame_rate.num)
                if (    avctx->time_base.den * (int64_t) st->time_base.num
                    <= avctx->time_base.num * avctx->ticks_per_frame * (int64_t) st->time_base.den)
                    st->r_frame_rate.num = avctx->time_base.den;
                    st->r_frame_rate.den = avctx->time_base.num * avctx->ticks_per_frame;
                else
                    st->r_frame_rate.num = st->time_base.den;
                    st->r_frame_rate.den = st->time_base.num;
               
           
            if (st->display_aspect_ratio.num && st->display_aspect_ratio.den)
                AVRational hw_ratio = avctx->height, avctx->width ;
                st->sample_aspect_ratio = av_mul_q(st->display_aspect_ratio,
                                                   hw_ratio);
           
        else if (avctx->codec_type == AVMEDIA_TYPE_AUDIO)
            if (!avctx->bits_per_coded_sample)
                avctx->bits_per_coded_sample =
                    av_get_bits_per_sample(avctx->codec_id);
            // set stream disposition based on audio service type
            switch (avctx->audio_service_type)
            case AV_AUDIO_SERVICE_TYPE_EFFECTS:
                st->disposition = AV_DISPOSITION_CLEAN_EFFECTS;
                break;
            case AV_AUDIO_SERVICE_TYPE_VISUALLY_IMPAIRED:
                st->disposition = AV_DISPOSITION_VISUAL_IMPAIRED;
                break;
            case AV_AUDIO_SERVICE_TYPE_HEARING_IMPAIRED:
                st->disposition = AV_DISPOSITION_HEARING_IMPAIRED;
                break;
            case AV_AUDIO_SERVICE_TYPE_COMMENTARY:
                st->disposition = AV_DISPOSITION_COMMENT;
                break;
            case AV_AUDIO_SERVICE_TYPE_KARAOKE:
                st->disposition = AV_DISPOSITION_KARAOKE;
                break;
           
       
   


    if (probesize)
        estimate_timings(ic, old_offset);


    av_opt_set(ic, "skip_clear", "0", AV_OPT_SEARCH_CHILDREN);


    if (ret >= 0 && ic->nb_streams)
        /* We could not have all the codec parameters before EOF. */
        ret = -1;
    for (i = 0; i < ic->nb_streams; i++)
        const char *errmsg;
        st = ic->streams[i];


        /* if no packet was ever seen, update context now for has_codec_parameters */
        if (!st->internal->avctx_inited)
            if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
                st->codecpar->format == AV_SAMPLE_FMT_NONE)
                st->codecpar->format = st->internal->avctx->sample_fmt;
            ret = avcodec_parameters_to_context(st->internal->avctx, st->codecpar);
            if (ret < 0)
                goto find_stream_info_err;
       
        if (!has_codec_parameters(st, &errmsg))
            char buf[256];
            avcodec_string(buf, sizeof(buf), st->internal->avctx, 0);
            av_log(ic, AV_LOG_WARNING,
                   "Could not find codec parameters for stream %d (%s): %s\\n"
                   "Consider increasing the value for the 'analyzeduration' and 'probesize' options\\n",
                   i, buf, errmsg);
        else
            ret = 0;
       
   


    compute_chapters_end(ic);


    /* update the stream parameters from the internal codec contexts */
    for (i = 0; i < ic->nb_streams; i++)
        st = ic->streams[i];


        if (st->internal->avctx_inited)
            int orig_w = st->codecpar->width;
            int orig_h = st->codecpar->height;
            ret = avcodec_parameters_from_context(st->codecpar, st->internal->avctx);
            if (ret < 0)
                goto find_stream_info_err;
            // The decoder might reduce the video size by the lowres factor.
            if (av_codec_get_lowres(st->internal->avctx) && orig_w)
                st->codecpar->width = orig_w;
                st->codecpar->height = orig_h;
           
       


        st->internal->avctx_inited = 0;
   


find_stream_info_err:
    for (i = 0; i < ic->nb_streams; i++)
        st = ic->streams[i];
        if (st->info)
            av_freep(&st->info->duration_error);
        av_freep(&ic->streams[i]->info);
   
    if (ic->pb)
        av_log(ic, AV_LOG_DEBUG, "After avformat_find_stream_info() pos: %"PRId64" bytes read:%"PRId64" seeks:%d frames:%d\\n",
               avio_tell(ic->pb), ic->pb->bytes_read, ic->pb->seek_count, count);
    return ret;





3.3 open_output_file()
static int open_output_file(OptionsContext *o, const char *filename)

    AVFormatContext *oc;
    int i, j, err;
    AVOutputFormat *file_oformat;
    OutputFile *of;
    OutputStream *ost;
    InputStream  *ist;
    AVDictionary *unused_opts = NULL;
    AVDictionaryEntry *e = NULL;




    /* 初始化 OutputFile *of */
    GROW_ARRAY(output_files, nb_output_files);
    of = av_mallocz(sizeof(*of));
    if (!of)
        exit_program(1);
    output_files[nb_output_files - 1] = of;


    of->ost_index      = nb_output_streams;
    of->recording_time = o->recording_time;
    of->start_time     = o->start_time;
    of->limit_filesize = o->limit_filesize;
    of->shortest       = o->shortest;
    av_dict_copy(&of->opts, o->g->format_opts, 0);


    if (!strcmp(filename, "-"))
        filename = "pipe:";


    /* 初始化 AVFormatContext *oc */
    err = avformat_alloc_output_context2(&oc, NULL, o->format, filename);
    if (!oc)
        print_error(filename, err);
        exit_program(1);
   


    of->ctx = oc;
    if (o->recording_time != INT64_MAX)
        oc->duration = o->recording_time;


    file_oformat= oc->oformat;
    oc->interrupt_callback = int_cb;






    /* ffserver seeking with date=... needs a date reference */
    if (!strcmp(file_oformat->name, "ffm") &&
        av_strstart(filename, "http:", NULL))
        int err = parse_option(o, "metadata", "creation_time=now", options);
        if (err < 0)
            print_error(filename, err);
            exit_program(1);
       
   


    if (!strcmp(file_oformat->name, "ffm") && !override_ffserver &&
        av_strstart(filename, "http:", NULL))
       ...
    else if (!o->nb_stream_maps)
        char *subtitle_codec_name = NULL;
        /* pick the "best" stream of each type */


        /* video: highest resolution */
        if (!o->video_disable && av_guess_codec(oc->oformat, NULL, filename, NULL, AVMEDIA_TYPE_VIDEO) != AV_CODEC_ID_NONE)
            int area = 0, idx = -1;
            int qcr = avformat_query_codec(oc->oformat, oc->oformat->video_codec, 0);
            for (i = 0; i < nb_input_streams; i++)
                int new_area;
                ist = input_streams[i];
                new_area = ist->st->codecpar->width * ist->st->codecpar->height + 100000000*!!ist->st->codec_info_nb_frames;
                if((qcr!=MKTAG('A', 'P', 'I', 'C')) && (ist->st->disposition & AV_DISPOSITION_ATTACHED_PIC))
                    new_area = 1;
                if (ist->st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
                    new_area > area)
                    if((qcr==MKTAG('A', 'P', 'I', 'C')) && !(ist->st->disposition & AV_DISPOSITION_ATTACHED_PIC))
                        continue;
                    area = new_area;
                    idx = i;
               
           
            /* 初始化输出视频流的结构体*/
            if (idx >= 0)
                new_video_stream(o, oc, idx);
       


        /* audio: most channels */
        if (!o->audio_disable && av_guess_codec(oc->oformat, NULL, filename, NULL, AVMEDIA_TYPE_AUDIO) != AV_CODEC_ID_NONE)
            int best_score = 0, idx = -1;
            for (i = 0; i < nb_input_streams; i++)
                int score;
                ist = input_streams[i];
                score = ist->st->codecpar->channels + 100000000*!!ist->st->codec_info_nb_frames;
                if (ist->st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
                    score > best_score)
                    best_score = score;
                    idx = i;
               
           
           /* 初始化输出音频流的结构体*/
            if (idx >= 0)
                new_audio_stream(o, oc, idx);
       
   


#if FF_API_LAVF_AVCTX
    for (i = nb_output_streams - oc->nb_streams; i < nb_output_streams; i++) //for all streams of this output file
        AVDictionaryEntry *e;
        ost = output_streams[i];


        if ((ost->stream_copy || ost->attachment_filename)
            && (e = av_dict_get(o->g->codec_opts, "flags", NULL, AV_DICT_IGNORE_SUFFIX))
            && (!e->key[5] || check_stream_specifier(oc, ost->st, e->key+6)))
            if (av_opt_set(ost->st->codec, "flags", e->value, 0) < 0)
                exit_program(1);
   
#endif


    if (!oc->nb_streams && !(oc->oformat->flags & AVFMT_NOSTREAMS))
        av_dump_format(oc, nb_output_files - 1, oc->filename, 1);
        av_log(NULL, AV_LOG_ERROR, "Output file #%d does not contain any stream\\n", nb_output_files - 1);
        exit_program(1);
   


    /* check if all codec options have been used */
    unused_opts = strip_specifiers(o->g->codec_opts);
    for (i = of->ost_index; i < nb_output_streams; i++)
        e = NULL;
        while ((e = av_dict_get(output_streams[i]->encoder_opts, "", e,
                                AV_DICT_IGNORE_SUFFIX)))
            av_dict_set(&unused_opts, e->key, NULL, 0);
   


    e = NULL;
    while ((e = av_dict_get(unused_opts, "", e, AV_DICT_IGNORE_SUFFIX)))
        const AVClass *class = avcodec_get_class();
        const AVOption *option = av_opt_find(&class, e->key, NULL, 0,
                                             AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ);
        const AVClass *fclass = avformat_get_class();
        const AVOption *foption = av_opt_find(&fclass, e->key, NULL, 0,
                                              AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ);
        if (!option || foption)
            continue;




        if (!(option->flags & AV_OPT_FLAG_ENCODING_PARAM))
            av_log(NULL, AV_LOG_ERROR, "Codec AVOption %s (%s) specified for "
                   "output file #%d (%s) is not an encoding option.\\n", e->key,
                   option->help ? option->help : "", nb_output_files - 1,
                   filename);
            exit_program(1);
       


        // gop_timecode is injected by generic code but not always used
        if (!strcmp(e->key, "gop_timecode"))
            continue;


        av_log(NULL, AV_LOG_WARNING, "Codec AVOption %s (%s) specified for "
               "output file #%d (%s) has not been used for any stream. The most "
               "likely reason is either wrong type (e.g. a video option with "
               "no video streams) or that it is a private option of some encoder "
               "which was not actually used for any stream.\\n", e->key,
               option->help ? option->help : "", nb_output_files - 1, filename);
   
    av_dict_free(&unused_opts);


    /* set the decoding_needed flags and create simple filtergraphs */
    for (i = of->ost_index; i < nb_output_streams; i++)
        OutputStream *ost = output_streams[i];


        if (ost->encoding_needed && ost->source_index >= 0)
            InputStream *ist = input_streams[ost->source_index];
            ist->decoding_needed |= DECODING_FOR_OST;


            if (ost->st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ||
                ost->st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
                err = init_simple_filtergraph(ist, ost);
                if (err < 0)
                    av_log(NULL, AV_LOG_ERROR,
                           "Error initializing a simple filtergraph between streams "
                           "%d:%d->%d:%d\\n", ist->file_index, ost->source_index,
                           nb_output_files - 1, ost->st->index);
                    exit_program(1);
               
           
       
   


    /* check filename in case of an image number is expected */
    if (oc->oformat->flags & AVFMT_NEEDNUMBER)
        if (!av_filename_number_test(oc->filename))
            print_error(oc->filename, AVERROR(EINVAL));
            exit_program(1);
       
   


    if (!(oc->oformat->flags & AVFMT_NOSTREAMS) && !input_stream_potentially_available)
        av_log(NULL, AV_LOG_ERROR,
               "No input streams but output needs an input stream\\n");
        exit_program(1);
   


    if (!(oc->oformat->flags & AVFMT_NOFILE))
        /* test if it already exists to avoid losing precious files */
        assert_file_overwrite(filename);


        /* open the file */
        if ((err = avio_open2(&oc->pb, filename, AVIO_FLAG_WRITE,
                              &oc->interrupt_callback,
                              &of->opts)) < 0)
            print_error(filename, err);
            exit_program(1);
       
    else if (strcmp(oc->oformat->name, "image2")==0 && !av_filename_number_test(filename))
        assert_file_overwrite(filename);


    if (o->mux_preload)
        av_dict_set_int(&of->opts, "preload", o->mux_preload*AV_TIME_BASE, 0);
   
    oc->max_delay = (int)(o->mux_max_delay * AV_TIME_BASE);


    /* copy metadata */
    for (i = 0; i < o->nb_metadata_map; i++)
        char *p;
        int in_file_index = strtol(o->metadata_map[i].u.str, &p, 0);


        if (in_file_index >= nb_input_files)
            av_log(NULL, AV_LOG_FATAL, "Invalid input file index %d while processing metadata maps\\n", in_file_index);
            exit_program(1);
       
        copy_metadata(o->metadata_map[i].specifier, *p ? p + 1 : p, oc,
                      in_file_index >= 0 ?
                      input_files[in_file_index]->ctx : NULL, o);
   


    /* copy chapters */
    if (o->chapters_input_file >= nb_input_files)
        if (o->chapters_input_file == INT_MAX)
            /* copy chapters from the first input file that has them*/
            o->chapters_input_file = -1;
            for (i = 0; i < nb_input_files; i++)
                if (input_files[i]->ctx->nb_chapters)
                    o->chapters_input_file = i;
                    break;
               
        else
            av_log(NULL, AV_LOG_FATAL, "Invalid input file index %d in chapter mapping.\\n",
                   o->chapters_input_file);
            exit_program(1);
       
   
    if (o->chapters_input_file >= 0)
        copy_chapters(input_files[o->chapters_input_file], of,
                      !o->metadata_chapters_manual);


    /* copy global metadata by default */
    if (!o->metadata_global_manual && nb_input_files)
        av_dict_copy(&oc->metadata, input_files[0]->ctx->metadata,
                     AV_DICT_DONT_OVERWRITE);
        if(o->recording_time != INT64_MAX)
            av_dict_set(&oc->metadata, "duration", NULL, 0);
        av_dict_set(&oc->metadata, "creation_time", NULL, 0);
   
    if (!o->metadata_streams_manual)
        for (i = of->ost_index; i < nb_output_streams; i++)
            InputStream *ist;
            if (output_streams[i]->source_index < 0)         /* this is true e.g. for attached files */
                continue;
            ist = input_streams[output_streams[i]->source_index];
            av_dict_copy(&output_streams[i]->st->metadata, ist->st->metadata, AV_DICT_DONT_OVERWRITE);
            if (!output_streams[i]->stream_copy)
                av_dict_set(&output_streams[i]->st->metadata, "encoder", NULL, 0);
                if (ist->autorotate)
                    av_dict_set(&output_streams[i]->st->metadata, "rotate", NULL, 0);
           
       


    /* process manually set programs */
    for (i = 0; i < o->nb_program; i++)
        const char *p = o->program[i].u.str;
        int progid = i+1;
        AVProgram *program;


        while(*p)
            const char *p2 = av_get_token(&p, ":");
            const char *to_dealloc = p2;
            char *key;
            if (!p2)
                break;


            if(*p) p++;


            key = av_get_token(&p2, "=");
            if (!key || !*p2)
                av_freep(&to_dealloc);
                av_freep(&key);
                break;
           
            p2++;


            if (!strcmp(key, "program_num"))
                progid = strtol(p2, NULL, 0);
            av_freep(&to_dealloc);
            av_freep(&key);
       


        program = av_new_program(oc, progid);


        p = o->program[i].u.str;
        while(*p)
            const char *p2 = av_get_token(&p, ":");
            const char *to_dealloc = p2;
            char *key;
            if (!p2)
                break;
            if(*p) p++;


            key = av_get_token(&p2, "=");
            if (!key)
                av_log(NULL, AV_LOG_FATAL,
                       "No '=' character in program string %s.\\n",
                       p2);
                exit_program(1);
           
            if (!*p2)
                exit_program(1);
            p2++;


            if (!strcmp(key, "title"))
                av_dict_set(&program->metadata, "title", p2, 0);
            else if (!strcmp(key, "program_num"))
            else if (!strcmp(key, "st"))
                int st_num = strtol(p2, NULL, 0);
                av_program_add_stream_index(oc, progid, st_num);
            else
                av_log(NULL, AV_LOG_FATAL, "Unknown program key %s.\\n", key);
                exit_program(1);
           
            av_freep(&to_dealloc);
            av_freep(&key);
       
   


    /* process manually set metadata */
    for (i = 0; i < o->nb_metadata; i++)
        AVDictionary **m;
        char type, *val;
        const char *stream_spec;
        int index = 0, j, ret = 0;


        val = strchr(o->metadata[i].u.str, '=');
        if (!val)
            av_log(NULL, AV_LOG_FATAL, "No '=' character in metadata string %s.\\n",
                   o->metadata[i].u.str);
            exit_program(1);
       
        *val++ = 0;


        parse_meta_type(o->metadata[i].specifier, &type, &index, &stream_spec);
        if (type == 's')
            for (j = 0; j < oc->nb_streams; j++)
                ost = output_streams[nb_output_streams - oc->nb_streams + j];
                if ((ret = check_stream_specifier(oc, oc->streams[j], stream_spec)) > 0)
                    av_dict_set(&oc->streams[j]->metadata, o->metadata[i].u.str, *val ? val : NULL, 0);
                    if (!strcmp(o->metadata[i].u.str, "rotate"))
                        ost->rotate_overridden = 1;
                   
                else if (ret < 0)
                    exit_program(1);
           
       
        else
            switch (type)
            case 'g':
                m = &oc->metadata;
                break;
            case 'c':
                if (index < 0 || index >= oc->nb_chapters)
                    av_log(NULL, AV_LOG_FATAL, "Invalid chapter index %d in metadata specifier.\\n", index);
                    exit_program(1);
               
                m = &oc->chapters[index]->metadata;
                break;
            case 'p':
                if (index < 0 || index >= oc->nb_programs)
                    av_log(NULL, AV_LOG_FATAL, "Invalid program index %d in metadata specifier.\\n", index);
                    exit_program(1);
               
                m = &oc->programs[index]->metadata;
                break;
            default:
                av_log(NULL, AV_LOG_FATAL, "Invalid metadata specifier %s.\\n", o->metadata[i].specifier);
                exit_program(1);
           
            av_dict_set(m, o->metadata[i].u.str, *val ? val : NULL, 0);
       
   


    return 0;

 

以上是关于FFmpeg源码剖析-通用:ffmpeg_parse_options()的主要内容,如果未能解决你的问题,请参考以下文章

FFmpeg源码结构图 - 解码

Spring源码核心剖析

FFmpeg源码简单分析:结构体成员管理系统-AVOption

FFmpeg源码简单分析:libswscale的sws_scale()

AbstractQueuedSynchronizer AQS框架源码剖析

ffplay源码剖析(3.2.4 + sdl2):函数调用流程图