寫在前面
本節主要分析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.