• 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; inb_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;
    }
     

  • 相关阅读:
    MySQL数据库面试题总结(2022最新版)
    快速上手使用本地测试工具postman
    java计算机毕业设计校园共享单车系统源代码+系统+数据库+lw文档
    Zookeeper
    全新力作—C++ string类的模拟实现
    Metabase学习教程:仪表盘-3
    华为ENSP网络设备配置实战2(较为复杂的ospf)
    YOLO好像也没那么难?
    mysql8.0.28下载和安装详细教程,适配win11
    【1 操作系统概述】
  • 原文地址:https://blog.csdn.net/fireroll/article/details/77978478