FFmpeg源碼(四)大軍未動糧草先行——avformat_open_input

寫在前面

本節主要分析avformat_open_input:打開輸入流的過程中所做的操作。


源碼解析

首先我們先給出avformat_open_input的完整代碼及詳解註釋(代碼中標註的兩個TODO的解析在後面):

avformat_open_input

/**
 * Open an input stream and read the header. The codecs are not opened.
 * The stream must be closed with avformat_close_input().
 * 打開輸入流並讀取頭部。 編解碼器未打開。
 * 必須使用avformat_close_input()關閉該流。
 *
 * @param ps Pointer to user-supplied AVFormatContext (allocated by avformat_alloc_context).
 *           May be a pointer to NULL, in which case an AVFormatContext is allocated by this
 *           function and written into ps.
 *           Note that a user-supplied AVFormatContext will be freed on failure.
 *           指向用戶提供的AVFormatContext的指針(由avformat_alloc_context分配)。
 *           可能是指向NULL的指針,在這種情況下,AVFormatContext由此函數分配並寫入ps。
 *           請注意,用戶提供的AVFormatContext將在失敗時釋放。
 *
 * @param url URL of the stream to open.
 *            要打開的流的URL。
 *
 * @param fmt If non-NULL, this parameter forces a specific input format.
 *            Otherwise the format is autodetected.
 *            如果爲非NULL,則此參數強制使用特定的輸入格式。
 *            否則,格式將被自動檢測。
 *
 * @param options  A dictionary filled with AVFormatContext and demuxer-private options.
 *                 On return this parameter will be destroyed and replaced with a dict containing
 *                 options that were not found. May be NULL.
 *                 一個充滿AVFormatContext和demuxer-private選項的字典。
 *                 返回時,此參數將被銷燬並替換爲包含未找到選項的字典。 可能是NULL。
 *
 * @return 0 on success, a negative AVERROR on failure.
 *
 * @note If you want to use custom IO, preallocate the format context and set its pb field.
 *       如果要使用自定義IO,請預先分配格式上下文並設置其pb字段。
 */
int avformat_open_input(AVFormatContext **ps, const char *url, AVInputFormat *fmt, AVDictionary **options);

int avformat_open_input(AVFormatContext **ps, const char *filename,
                        ff_const59 AVInputFormat *fmt, AVDictionary **options)
{
    AVFormatContext *s = *ps;
    int i, ret = 0;
    AVDictionary *tmp = NULL;
    // ID3標籤是MP3音樂檔案中的歌曲附加訊息,它能夠在MP3中附加曲子的演出者、作者以及其它類別資訊,方便衆多樂曲的管理。
    // 缺少ID3標籤並不會影響 MP3的播放,但若沒有的話,管理音樂文件也會相當的麻煩。
    ID3v2ExtraMeta *id3v2_extra_meta = NULL;

    // 分配一個AVFormatContext TODO:1
    if (!s && !(s = avformat_alloc_context()))
        return AVERROR(ENOMEM);
    if (!s->av_class) {
        av_log(NULL, AV_LOG_ERROR, "Input context has not been properly allocated by avformat_alloc_context() and is not NULL either\n");
        return AVERROR(EINVAL);
    }

    // 輸入容器格式
    if (fmt)
        s->iformat = fmt;

    if (options)
        /**
         * Copy entries from one AVDictionary struct into another.
         * 將條目從一個AVDictionary結構複製到另一個。
         *
         * @param dst pointer to a pointer to a AVDictionary struct. If *dst is NULL,
         *            this function will allocate a struct for you and put it in *dst
         *            指向AVDictionary結構的指針。 如果* dst爲NULL,則此函數將重新分配一個結構並將其放入* dst
         *
         * @param src pointer to source AVDictionary struct
         *            指向源AVDictionary結構的指針
         *
         * @param flags flags to use when setting entries in *dst
         *              在* dst中設置條目時使用的標誌
         *
         * @note metadata is read using the AV_DICT_IGNORE_SUFFIX flag
         *       使用AV_DICT_IGNORE_SUFFIX標誌讀取元數據
         *
         * @return 0 on success, negative AVERROR code on failure. If dst was allocated
         *           by this function, callers should free the associated memory.
         *           如果dst由此函數分配,則調用者應釋放相關的內存。
         */
        // int av_dict_copy(AVDictionary **dst, const AVDictionary *src, int flags);
        av_dict_copy(&tmp, *options, 0);

    if (s->pb) // must be before any goto fail
        /**
         * I/O context.
         *
         * - demuxing: either set by the user before avformat_open_input() (then
         *             the user must close it manually) or set by avformat_open_input().
         * - muxing: set by the user before avformat_write_header(). The caller must
         *           take care of closing / freeing the IO context.
         *
         * Do NOT set this field if AVFMT_NOFILE flag is set in
         * iformat/oformat.flags. In such a case, the (de)muxer will handle
         * I/O in some other way and this field will be NULL.
         */
        // AVIOContext *pb;
        s->flags |= AVFMT_FLAG_CUSTOM_IO;

    if ((ret = av_opt_set_dict(s, &tmp)) < 0)
        /**
         * Set all the options from a given dictionary on an object.
         * 設置對象上給定字典的所有選項。
         *
         * @param obj a struct whose first element is a pointer to AVClass
         *            一個struct,其第一個元素是指向AVClass的指針
         *
         * @param options options to process. This dictionary will be freed and replaced
         *                by a new one containing all options not found in obj.
         *                Of course this new dictionary needs to be freed by caller
         *                with av_dict_free().
         *                需要處理的選項。 該字典將被釋放並替換爲包含obj中未找到的所有選項的新字典。
         *                當然這個新詞典需要被調用者用av_dict_free()釋放。
         *
         * @return 0 on success, a negative AVERROR if some option was found in obj,
         *         but could not be set.
         *
         * @see av_dict_copy()
         */
        // int av_opt_set_dict(void *obj, struct AVDictionary **options);
        goto fail;

    if (!(s->url = av_strdup(filename ? filename : ""))) {
        // 參考資料:https://blog.csdn.net/ice_ly000/article/details/90510494
        /**
         * Duplicate a string.
         *
         * //入參s指向需要拷貝的字符串
         * @param s String to be duplicated
         *
         * //返回一個指向新分配的內存,該內存拷貝了一份字符串,如果無法分配出空間,則返回NULL
         * @return Pointer to a newly-allocated string containing a
         *         copy of `s` or `NULL` if the string cannot be allocated
         * @see av_strndup()
         */
        // char *av_strdup(const char *s) av_malloc_attrib;
        goto fail;
    }

#if FF_API_FORMAT_FILENAME
FF_DISABLE_DEPRECATION_WARNINGS
    av_strlcpy(s->filename, filename ? filename : "", sizeof(s->filename));
FF_ENABLE_DEPRECATION_WARNINGS
#endif
    // 打開輸入文件,並在必要時探查格式 TODO:2
    if ((ret = init_input(s, filename, &tmp)) < 0)
        goto fail;
    s->probe_score = ret;

    // 拷貝白名單
    if (!s->protocol_whitelist && s->pb && s->pb->protocol_whitelist) {
        s->protocol_whitelist = av_strdup(s->pb->protocol_whitelist);
        if (!s->protocol_whitelist) {
            ret = AVERROR(ENOMEM);
            goto fail;
        }
    }

    // 拷貝黑名單
    if (!s->protocol_blacklist && s->pb && s->pb->protocol_blacklist) {
        s->protocol_blacklist = av_strdup(s->pb->protocol_blacklist);
        if (!s->protocol_blacklist) {
            ret = AVERROR(ENOMEM);
            goto fail;
        }
    }

    // 校驗當前要使用的格式是否在白名單上
    if (s->format_whitelist && av_match_list(s->iformat->name, s->format_whitelist, ',') <= 0) {
        av_log(s, AV_LOG_ERROR, "Format not on whitelist \'%s\'\n", s->format_whitelist);
        ret = AVERROR(EINVAL);
        goto fail;
    }

    // int64_t avio_skip(AVIOContext *s,int64_t offset)
    // Skip given number of bytes forward.
    //
    // Returns
    // new position or AVERROR.
    avio_skip(s->pb, s->skip_initial_bytes);

    /* Check filename in case an image number is expected. */
    // 來自於avformat.h的宏定義:AVFMT_NEEDNUMBER    0x0002 /**< Needs '%d' in filename. */
    if (s->iformat->flags & AVFMT_NEEDNUMBER) {
        if (!av_filename_number_test(filename)) {
            ret = AVERROR(EINVAL);
            goto fail;
        }
    }

    s->duration = s->start_time = AV_NOPTS_VALUE;

    /* Allocate private data. */
    // 爲AVInputFormat中的私有數據開闢空間
    if (s->iformat->priv_data_size > 0) {
        if (!(s->priv_data = av_mallocz(s->iformat->priv_data_size))) {
            ret = AVERROR(ENOMEM);
            goto fail;
        }
        if (s->iformat->priv_class) {
            *(const AVClass **) s->priv_data = s->iformat->priv_class;
            av_opt_set_defaults(s->priv_data);
            if ((ret = av_opt_set_dict(s->priv_data, &tmp)) < 0)
                goto fail;
        }
    }

    /* e.g. AVFMT_NOFILE formats will not have a AVIOContext */
    if (s->pb)
        // 將ID3v2標籤讀入指定的詞典並檢索支持的額外元數據
        ff_id3v2_read_dict(s->pb, &s->internal->id3v2_meta, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta);

    // 讀取header
    if (!(s->flags&AVFMT_FLAG_PRIV_OPT) && s->iformat->read_header)
        if ((ret = s->iformat->read_header(s)) < 0)
            goto fail;

    // 將ID3v2標籤數據存放到s->metadata
    if (!s->metadata) {
        s->metadata = s->internal->id3v2_meta;
        s->internal->id3v2_meta = NULL;
    } else if (s->internal->id3v2_meta) {
        int level = AV_LOG_WARNING;
        if (s->error_recognition & AV_EF_COMPLIANT)
            level = AV_LOG_ERROR;
        av_log(s, level, "Discarding ID3 tags because more suitable tags were found.\n");
        av_dict_free(&s->internal->id3v2_meta);
        if (s->error_recognition & AV_EF_EXPLODE)
            return AVERROR_INVALIDDATA;
    }

    // 解析id3
    if (id3v2_extra_meta) {
        if (!strcmp(s->iformat->name, "mp3") || !strcmp(s->iformat->name, "aac") ||
            !strcmp(s->iformat->name, "tta") || !strcmp(s->iformat->name, "wav")) {
            // 爲從ID3v2標頭提取的每個APIC(附加圖片)創建一個流。
            if ((ret = ff_id3v2_parse_apic(s, &id3v2_extra_meta)) < 0)
                goto fail;
            // 爲在ID3v2標頭中找到的所有CHAP標籤創建章節。
            if ((ret = ff_id3v2_parse_chapters(s, &id3v2_extra_meta)) < 0)
                goto fail;
            // 在ID3v2標頭中爲所有PRIV標籤添加元數據。
            if ((ret = ff_id3v2_parse_priv(s, &id3v2_extra_meta)) < 0)
                goto fail;
        } else
            av_log(s, AV_LOG_DEBUG, "demuxer does not support additional id3 data, skipping\n");
    }
    ff_id3v2_free_extra_meta(&id3v2_extra_meta);

    if ((ret = avformat_queue_attached_pictures(s)) < 0)
        goto fail;

    if (!(s->flags&AVFMT_FLAG_PRIV_OPT) && s->pb && !s->internal->data_offset)
        // 同avio_seek
        // #define avio_tell(s) avio_seek((s), 0, SEEK_CUR)
        // static av_always_inline int64_t avio_tell(AVIOContext *s)
        // {
        //     return avio_seek(s, 0, SEEK_CUR);
        // }
        s->internal->data_offset = avio_tell(s->pb);

    s->internal->raw_packet_buffer_remaining_size = RAW_PACKET_BUFFER_SIZE;

    // 更新AVFormatContext中流的信息
    update_stream_avctx(s);

    for (i = 0; i < s->nb_streams; i++)
        s->streams[i]->internal->orig_codec_id = s->streams[i]->codecpar->codec_id;

    // free options
    if (options) {
        av_dict_free(options);
        *options = tmp;
    }
    *ps = s;
    return 0;

// 統一fail處理:釋放空間
fail:
    ff_id3v2_free_extra_meta(&id3v2_extra_meta);
    av_dict_free(&tmp);
    if (s->pb && !(s->flags & AVFMT_FLAG_CUSTOM_IO))
        avio_closep(&s->pb);
    avformat_free_context(s);
    *ps = NULL;
    return ret;
}

代碼雖然很長,但是邏輯很清晰,詳細的註釋已經加到了代碼之中。註釋中有兩個TODO,我們先來看一下這兩個TODO的地方:

TODO 1.avformat_alloc_context

avformat_alloc_context爲AVFormatContext分配空間,下面給出源碼:

/**
 * Allocate an AVFormatContext.
 * avformat_free_context() can be used to free the context and everything
 * allocated by the framework within it.
 * 分配一個AVFormatContext。
 * avformat_free_context()可用於釋放上下文以及框架在其中分配的所有內容。
 */
AVFormatContext *avformat_alloc_context(void);

AVFormatContext *avformat_alloc_context(void)
{
    AVFormatContext *ic;
    AVFormatInternal *internal;

    // 分配AVFormatContext空間
    ic = av_malloc(sizeof(AVFormatContext));
    if (!ic) return ic;

    // 分配AVFormatInternal空間
    internal = av_mallocz(sizeof(*internal));
    if (!internal) {
        av_free(ic);
        return NULL;
    }
    
    // 爲AVFormatContext設置默認值
    avformat_get_context_defaults(ic);
    ic->internal = internal;
    ic->internal->offset = AV_NOPTS_VALUE;
    ic->internal->raw_packet_buffer_remaining_size = RAW_PACKET_BUFFER_SIZE;
    ic->internal->shortest_end = AV_NOPTS_VALUE;

    return ic;
}

從上面可以看出,最終又調用了avformat_get_context_defaults

/**
 * 設置初始io開關函數
 */
static void avformat_get_context_defaults(AVFormatContext *s)
{
    // reset
    memset(s, 0, sizeof(AVFormatContext));

    s->av_class = &av_format_context_class;

    s->io_open  = io_open_default;
    s->io_close = io_close_default;

    av_opt_set_defaults(s);
}

static const AVClass av_format_context_class = {
    .class_name     = "AVFormatContext",
    .item_name      = format_to_name,
    .option         = avformat_options,
    .version        = LIBAVUTIL_VERSION_INT,
    .child_next     = format_child_next,
    .child_class_next = format_child_class_next,
    .category       = AV_CLASS_CATEGORY_MUXER,
    .get_category   = get_category,
};

至此初始化部分就結束了,但是最後設置的io_open_default是什麼呢?它即是在通過判斷白名單打開資源文件,感興趣的可以看一下這部分:

static int io_open_default(AVFormatContext *s, AVIOContext **pb,
                           const char *url, int flags, AVDictionary **options)
{
    // decide log level
    int loglevel;

    if (!strcmp(url, s->url) ||
        s->iformat && !strcmp(s->iformat->name, "image2") ||
        s->oformat && !strcmp(s->oformat->name, "image2")
    ) {
        loglevel = AV_LOG_DEBUG;
    } else
        loglevel = AV_LOG_INFO;

    av_log(s, loglevel, "Opening \'%s\' for %s\n", url, flags & AVIO_FLAG_WRITE ? "writing" : "reading");

#if FF_API_OLD_OPEN_CALLBACKS
FF_DISABLE_DEPRECATION_WARNINGS
    if (s->open_cb)
        return s->open_cb(s, pb, url, flags, &s->interrupt_callback, options);
FF_ENABLE_DEPRECATION_WARNINGS
#endif

    return ffio_open_whitelist(pb, url, flags, &s->interrupt_callback, options, s->protocol_whitelist, s->protocol_blacklist);
}

int ffio_open_whitelist(AVIOContext **s, const char *filename, int flags,
                         const AVIOInterruptCB *int_cb, AVDictionary **options,
                         const char *whitelist, const char *blacklist
                        )
{
    URLContext *h;
    int err;

    // 核心邏輯
    err = ffurl_open_whitelist(&h, filename, flags, int_cb, options, whitelist, blacklist, NULL);
    if (err < 0)
        return err;
    err = ffio_fdopen(s, h);
    if (err < 0) {
        ffurl_close(h);
        return err;
    }
    return 0;
}

/**
 * Create an URLContext for accessing to the resource indicated by
 * url, and open it.
 * 創建一個URLContext來訪問由url指示的資源,然後將其打開。
 *
 * @param puc pointer to the location where, in case of success, the
 * function puts the pointer to the created URLContext
 * 指向成功的情況下,函數將指針放置到創建的URLContext的位置的指針
 *
 * @param flags flags which control how the resource indicated by url
 * is to be opened
 * 標誌,用於控制如何打開由url指示的資源
 *
 * @param int_cb interrupt callback to use for the URLContext, may be
 * NULL
 * 用於URLContext的中斷回調,可能爲NULL
 *
 * @param options  A dictionary filled with protocol-private options. On return
 * this parameter will be destroyed and replaced with a dict containing options
 * that were not found. May be NULL.
 * 協議專用選項的字典。
 * 返回時,此參數將被銷燬並替換爲包含未找到的選項的dict。可能爲NULL。
 *
 * @param parent An enclosing URLContext, whose generic options should
 *               be applied to this URLContext as well.
 * 封閉的URLContext,其通用選項也應應用於此URLContext。
 *
 * @return >= 0 in case of success, a negative value corresponding to an
 * AVERROR code in case of failure
 * return> = 0,如果成功,則爲負值;如果失敗,則爲AVERROR
 */
int ffurl_open_whitelist(URLContext **puc, const char *filename, int flags,
               const AVIOInterruptCB *int_cb, AVDictionary **options,
               const char *whitelist, const char* blacklist,
               URLContext *parent);
               
int ffurl_open_whitelist(URLContext **puc, const char *filename, int flags,
                         const AVIOInterruptCB *int_cb, AVDictionary **options,
                         const char *whitelist, const char* blacklist,
                         URLContext *parent)
{
    AVDictionary *tmp_opts = NULL;
    AVDictionaryEntry *e;
    
    // alloc
    int ret = ffurl_alloc(puc, filename, flags, int_cb);
    if (ret < 0)
        return ret;
    if (parent)
        av_opt_copy(*puc, parent);
    if (options &&
        (ret = av_opt_set_dict(*puc, options)) < 0)
        goto fail;
    if (options && (*puc)->prot->priv_data_class &&
        (ret = av_opt_set_dict((*puc)->priv_data, options)) < 0)
        goto fail;

    if (!options)
        options = &tmp_opts;

    av_assert0(!whitelist ||
               !(e=av_dict_get(*options, "protocol_whitelist", NULL, 0)) ||
               !strcmp(whitelist, e->value));
    av_assert0(!blacklist ||
               !(e=av_dict_get(*options, "protocol_blacklist", NULL, 0)) ||
               !strcmp(blacklist, e->value));

    // 設置白名單,黑名單
    if ((ret = av_dict_set(options, "protocol_whitelist", whitelist, 0)) < 0)
        goto fail;

    if ((ret = av_dict_set(options, "protocol_blacklist", blacklist, 0)) < 0)
        goto fail;

    if ((ret = av_opt_set_dict(*puc, options)) < 0)
        goto fail;

    // url connect
    ret = ffurl_connect(*puc, options);

    if (!ret)
        return 0;
        
// 統一fail管理:關閉並置空
fail:
    ffurl_close(*puc);
    *puc = NULL;
    return ret;
}

TODO 2.init_input

這個函數的作用是打開輸入文件,並在必要時探查格式,即完善AVFormatContext中的AVIOContext和AVInputFormat。下面我們來看一下源碼:

/**
 * Open input file and probe the format if necessary.
 * 打開輸入文件,並在必要時探查格式
 */
static int init_input(AVFormatContext *s, const char *filename,
                      AVDictionary **options)
{
    int ret;
    AVProbeData pd = { filename, NULL, 0 };
    int score = AVPROBE_SCORE_RETRY;

    if (s->pb) {
        s->flags |= AVFMT_FLAG_CUSTOM_IO;
        if (!s->iformat)
            // 探測字節流以確定輸入格式
            return av_probe_input_buffer2(s->pb, &s->iformat, filename,
                                         s, 0, s->format_probesize);
        else if (s->iformat->flags & AVFMT_NOFILE)
            av_log(s, AV_LOG_WARNING, "Custom AVIOContext makes no sense and "
                                      "will be ignored with AVFMT_NOFILE format.\n");
        return 0;
    }

    // 猜測文件格式
    if ((s->iformat && s->iformat->flags & AVFMT_NOFILE) ||
        (!s->iformat && (s->iformat = av_probe_input_format2(&pd, 0, &score))))
        return score;

    if ((ret = s->io_open(s, &s->pb, filename, AVIO_FLAG_READ | s->avio_flags, options)) < 0)
        return ret;

    if (s->iformat)
        return 0;
    return av_probe_input_buffer2(s->pb, &s->iformat, filename,
                                 s, 0, s->format_probesize);
}

代碼調用了av_probe_input_buffer2 & av_probe_input_format2:

/**
 * Probe a bytestream to determine the input format. Each time a probe returns
 * with a score that is too low, the probe buffer size is increased and another
 * attempt is made. When the maximum probe size is reached, the input format
 * with the highest score is returned.
 * 探測字節流以確定輸入格式。
 * 每次探針返回的分數過低時,探針緩衝區的大小都會增加,並會進行另一次嘗試。
 * 當達到最大探針大小時,將返回得分最高的輸入格式。
 *
 * @param pb the bytestream to probe 要探測的字節流
 * @param fmt the input format is put here 輸入格式
 * @param url the url of the stream 流的url
 * @param logctx the log context
 * @param offset the offset within the bytestream to probe from 要從中探測的字節流中的偏移量
 * @param max_probe_size the maximum probe buffer size (zero for default) 最大探針緩衝區大小(默認爲0)
 * @return the score in case of success, a negative value corresponding to an
 *         the maximal score is AVPROBE_SCORE_MAX
 * AVERROR code otherwise
 */
int av_probe_input_buffer2(AVIOContext *pb, ff_const59 AVInputFormat **fmt,
                           const char *url, void *logctx,
                           unsigned int offset, unsigned int max_probe_size);
                           
/**
 * Guess the file format.
 * 猜測文件格式
 *
 * @param pd        data to be probed 要探查的數據
 * @param is_opened Whether the file is already opened; determines whether
 *                  demuxers with or without AVFMT_NOFILE are probed.
 *                  文件是否已經打開;確定demuxers是否具有已探測的AVFMT_NOFILE。
 *
 * @param score_max A probe score larger that this is required to accept a
 *                  detection, the variable is set to the actual detection
 *                  score afterwards.
 *                  大於接受檢測所需的探針分數,此後將變量設置爲實際檢測分數。
 *                  If the score is <= AVPROBE_SCORE_MAX / 4 it is recommended
 *                  to retry with a larger probe buffer.
 */
ff_const59 AVInputFormat *av_probe_input_format2(ff_const59 AVProbeData *pd, int is_opened, int *score_max);

知識拓展

1.AVFormatInternal結構體解析
struct AVFormatInternal {
    /**
     * Number of streams relevant for interleaving.
     * Muxing only.
     * 相關交錯的流的數量
     * 僅用於混合流
     */
    int nb_interleaved_streams;

    /**
     * This buffer is only needed when packets were already buffered but
     * not decoded, for example to get the codec parameters in MPEG
     * streams.
     * 僅當數據包已經緩衝但尚未解碼時才需要此緩衝區,例如,以獲取MPEG流中的編解碼器參數。
     */
    struct AVPacketList *packet_buffer;
    struct AVPacketList *packet_buffer_end;

    /* av_seek_frame() support */
    int64_t data_offset; /**< offset of the first packet */

    /**
     * Raw packets from the demuxer, prior to parsing and decoding.
     * This buffer is used for buffering packets until the codec can
     * be identified, as parsing cannot be done without knowing the
     * codec.
     * 來自解複用器的原始數據包,然後進行解析和解碼。
     * 此緩衝區用於緩衝數據包,直到可以識別編解碼器爲止,因爲在不知道編解碼器的情況下無法進行解析。
     */
    struct AVPacketList *raw_packet_buffer;
    struct AVPacketList *raw_packet_buffer_end;
    /**
     * Packets split by the parser get queued here.
     * 解析器拆分的數據包在此處排隊。
     */
    struct AVPacketList *parse_queue;
    struct AVPacketList *parse_queue_end;
    /**
     * Remaining size available for raw_packet_buffer, in bytes.
     */
#define RAW_PACKET_BUFFER_SIZE 2500000
    int raw_packet_buffer_remaining_size;

    /**
     * Offset to remap timestamps to be non-negative.
     * Expressed in timebase units.
     * 將時間戳重新映射爲非負值的偏移量。
     * 以timebase單位表示。
     * @see AVStream.mux_ts_offset
     */
    int64_t offset;

    /**
     * Timebase for the timestamp offset.
     * 時間戳偏移量的時基。
     */
    AVRational offset_timebase;

#if FF_API_COMPUTE_PKT_FIELDS2
    int missing_ts_warning;
#endif

    int inject_global_side_data;

    int avoid_negative_ts_use_pts;

    /**
     * Timestamp of the end of the shortest stream.
     * 最短流結束的時間戳。
     */
    int64_t shortest_end;

    /**
     * Whether or not avformat_init_output has already been called
     * 是否已調用avformat_init_output
     */
    int initialized;

    /**
     * Whether or not avformat_init_output fully initialized streams
     * avformat_init_output是否已完全初始化了流
     */
    int streams_initialized;

    /**
     * ID3v2 tag useful for MP3 demuxing
     * ID3v2標籤,可用於MP3解複用
     */
    AVDictionary *id3v2_meta;

    /*
     * Prefer the codec framerate for avg_frame_rate computation.
     * 使用avg_frame_rate進行計算時,想要控制的編解碼幀率
     */
    int prefer_codec_framerate;
};
2.avformat_init_output
/**
 * Allocate the stream private data and initialize the codec, but do not write the header.
 * May optionally be used before avformat_write_header to initialize stream parameters
 * before actually writing the header.
 * If using this function, do not pass the same options to avformat_write_header.
 * 分配流私有數據並初始化編解碼器,但不要寫入頭部。
 * 可以選擇在avformat_write_header之前使用,以在實際寫入頭部之前初始化流參數。
 * 如果使用此功能,請勿將相同的選項傳遞給avformat_write_header。
 *
 * @param s Media file handle, must be allocated with avformat_alloc_context().
 *          Its oformat field must be set to the desired output format;
 *          Its pb field must be set to an already opened AVIOContext.
 *          媒體文件句柄必須使用avformat_alloc_context()分配。
 *          必須將其oformat字段設置爲所需的輸出格式。
 *          必須將其pb字段設置爲已打開的AVIOContext。
 *
 * @param options  An AVDictionary filled with AVFormatContext and muxer-private options.
 *                 On return this parameter will be destroyed and replaced with a dict containing
 *                 options that were not found. May be NULL.
 *                 一個充滿AVFormatContext和muxer-private選項的AVDictionary。
 *                 返回時,此參數將被銷燬並替換爲包含未找到的選項的dict。 可能爲NULL。
 *
 * @return AVSTREAM_INIT_IN_WRITE_HEADER on success if the codec requires avformat_write_header to fully initialize,
 *         AVSTREAM_INIT_IN_INIT_OUTPUT  on success if the codec has been fully initialized,
 *         negative AVERROR on failure.
 *
 * @see av_opt_find, av_dict_set, avio_open, av_oformat_next, avformat_write_header.
 */
av_warn_unused_result
int avformat_init_output(AVFormatContext *s, AVDictionary **options);
3.av_strlcpy
/**
 * Copy the string src to dst, but no more than size - 1 bytes, and
 * null-terminate dst.
 * 將字符串src複製到dst,但不超過(size - 1)個字節,null終止目標緩衝區
 *
 * This function is the same as BSD strlcpy().
 * 此函數與BSD strlcpy()相同。
 *
 * @param dst destination buffer 目標緩衝區
 * @param src source string 源字符串
 * @param size size of destination buffer 目標緩衝區的大小
 * @return the length of src src的長度
 *
 * @warning since the return value is the length of src, src absolutely
 * _must_ be a properly 0-terminated string, otherwise this will read beyond
 * the end of the buffer and possibly crash.
 * 因爲返回值是src的長度,src一定且必須是一個正確的以0結尾的字符串,否則函數將讀取超出緩衝區的末尾的位置,可能崩潰
 */
size_t av_strlcpy(char *dst, const char *src, size_t size);

總結

經過了一系列的操作,avformat_open_input完成了打開文件並解析文件中的各個參數,最終將解析出的所有信息放入了AVFormatContext中,供後續的操作使用。

If you like this, it is written by Johnny Deng.
If not, then I don’t know who wrote if.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章