当前位置:网站首页>FFmpeg源码分析:avformat_open_input
FFmpeg源码分析:avformat_open_input
2022-08-04 05:25:00 【vivianluomin】
avformat_open_input(),该函数用于打开多媒体数据并且获取一些信息,它的声明位于libavformat/avformat.h:
/** * Open an input stream and read the header. The codecs are not opened. * 打开输入流,并且读取header。codecs不会被打开。 * 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. * * * @param url URL of the stream to open. * @param fmt If non-NULL, this parameter forces a specific input format. * Otherwise the format is autodetected. * @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. * * @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. */
int avformat_open_input(AVFormatContext **ps, const char *url, AVInputFormat *fmt, AVDictionary **options);
它的定义在libavformat/utils.c中:
int avformat_open_input(AVFormatContext **ps, const char *filename,
AVInputFormat *fmt, AVDictionary **options)
{
AVFormatContext *s = *ps;
int i, ret = 0;
AVDictionary *tmp = NULL;
ID3v2ExtraMeta *id3v2_extra_meta = NULL;
if (!s && !(s = avformat_alloc_context())) //如果s没有初始化,先初始化s
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)
av_dict_copy(&tmp, *options, 0);
if (s->pb) // must be before any goto fail
s->flags |= AVFMT_FLAG_CUSTOM_IO;
if ((ret = av_opt_set_dict(s, &tmp)) < 0)
goto fail;
if (!(s->url = av_strdup(filename ? filename : ""))) {
ret = AVERROR(ENOMEM);
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
// 1. 重点在看这里:
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;
}
avio_skip(s->pb, s->skip_initial_bytes);
/* Check filename in case an image number is expected. */
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. */
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)
ff_id3v2_read_dict(s->pb, &s->internal->id3v2_meta, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta);
if (!(s->flags&AVFMT_FLAG_PRIV_OPT) && s->iformat->read_header)
//重点2
if ((ret = s->iformat->read_header(s)) < 0)
goto fail;
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;
}
if (id3v2_extra_meta) {
if (!strcmp(s->iformat->name, "mp3") || !strcmp(s->iformat->name, "aac") ||
!strcmp(s->iformat->name, "tta")) {
if ((ret = ff_id3v2_parse_apic(s, &id3v2_extra_meta)) < 0)
goto fail;
if ((ret = ff_id3v2_parse_chapters(s, &id3v2_extra_meta)) < 0)
goto fail;
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)
s->internal->data_offset = avio_tell(s->pb);
s->internal->raw_packet_buffer_remaining_size = RAW_PACKET_BUFFER_SIZE;
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;
if (options) {
av_dict_free(options);
*options = tmp;
}
*ps = s;
return 0;
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;
}
重点1: init_input:
它主要的工作就是打开视频数据并且探测视频的格式。定义位于libavformat\utils.c
/* 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 };
//用于判断一个AVInputFormat是否合适,
//AVPROBE_SCORE_RETRY = AVPROBE_SCORE_MAX/4 = 25
int score = AVPROBE_SCORE_RETRY;
//当使用了自定义的AVIOContext的时候,
if (s->pb) {
s->flags |= AVFMT_FLAG_CUSTOM_IO;
//如果指定了AVInputFormat就直接返回;
//否则就调用av_probe_input_buffer2 重点3
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;
}
//一般情况下,如果指定了AVInputFormat,就直接返回。
//如果没有指定,就调用av_probe_input_format2,根据文件路径推测AVInputFormat,重点4.
if ((s->iformat && s->iformat->flags & AVFMT_NOFILE) ||
(!s->iformat && (s->iformat = av_probe_input_format2(&pd, 0, &score))))
return score;
//如果发现通过文件路径判断不出文件格式,那么就调用io_open来探测文件格式 重点4
if ((ret = s->io_open(s, &s->pb, filename, AVIO_FLAG_READ | s->avio_flags, options)) < 0)
return ret;
if (s->iformat)
return 0;
//最后调用av_probe_input_buffer2来推测AVInputFormat 重点3
return av_probe_input_buffer2(s->pb, &s->iformat, filename,
s, 0, s->format_probesize);
}
下面就一个一个重点的看
重点3:av_probe_input_buffer2
根据媒体数据推测媒体数据的AVInput,声明位于libavformat\avformat.h:
/** * 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 输出推测的AVInputFormat * @param url the url of the stream * @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) * @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, AVInputFormat **fmt,
const char *url, void *logctx,
unsigned int offset, unsigned int max_probe_size);
定义位于libavformat\format.c:
int av_probe_input_buffer2(AVIOContext *pb, AVInputFormat **fmt,
const char *filename, void *logctx,
unsigned int offset, unsigned int max_probe_size)
{
AVProbeData pd = {
filename ? filename : "" };
uint8_t *buf = NULL;
int ret = 0, probe_size, buf_offset = 0;
int score = 0;
int ret2;
if (!max_probe_size)
max_probe_size = PROBE_BUF_MAX;
else if (max_probe_size < PROBE_BUF_MIN) {
av_log(logctx, AV_LOG_ERROR,
"Specified probe size value %u cannot be < %u\n", max_probe_size, PROBE_BUF_MIN);
return AVERROR(EINVAL);
}
if (offset >= max_probe_size)
return AVERROR(EINVAL);
if (pb->av_class) {
uint8_t *mime_type_opt = NULL;
char *semi;
av_opt_get(pb, "mime_type", AV_OPT_SEARCH_CHILDREN, &mime_type_opt);
pd.mime_type = (const char *)mime_type_opt;
semi = pd.mime_type ? strchr(pd.mime_type, ';') : NULL;
if (semi) {
*semi = '\0';
}
}
for (probe_size = PROBE_BUF_MIN; probe_size <= max_probe_size && !*fmt;
probe_size = FFMIN(probe_size << 1,
FFMAX(max_probe_size, probe_size + 1))) {
score = probe_size < max_probe_size ? AVPROBE_SCORE_RETRY : 0;
/* Read probe data. */
if ((ret = av_reallocp(&buf, probe_size + AVPROBE_PADDING_SIZE)) < 0)
goto fail;
//读取数据
if ((ret = avio_read(pb, buf + buf_offset,
probe_size - buf_offset)) < 0) {
/* Fail if error was not end of file, otherwise, lower score. */
if (ret != AVERROR_EOF)
goto fail;
score = 0;
ret = 0; /* error was end of file, nothing read */
}
buf_offset += ret;
if (buf_offset < offset)
continue;
pd.buf_size = buf_offset - offset;
pd.buf = &buf[offset];
memset(pd.buf + pd.buf_size, 0, AVPROBE_PADDING_SIZE);
/* Guess file format. */
//推测数据:重点5
*fmt = av_probe_input_format2(&pd, 1, &score);
if (*fmt) {
/* This can only be true in the last iteration. */
if (score <= AVPROBE_SCORE_RETRY) {
av_log(logctx, AV_LOG_WARNING,
"Format %s detected only with low score of %d, "
"misdetection possible!\n", (*fmt)->name, score);
} else
av_log(logctx, AV_LOG_DEBUG,
"Format %s probed with size=%d and score=%d\n",
(*fmt)->name, probe_size, score);
#if 0
FILE *f = fopen("probestat.tmp", "ab");
fprintf(f, "probe_size:%d format:%s score:%d filename:%s\n", probe_size, (*fmt)->name, score, filename);
fclose(f);
#endif
}
}
if (!*fmt)
ret = AVERROR_INVALIDDATA;
fail:
/* Rewind. Reuse probe buffer to avoid seeking. */
ret2 = ffio_rewind_with_probe_data(pb, &buf, buf_offset);
if (ret >= 0)
ret = ret2;
av_freep(&pd.mime_type);
return ret < 0 ? ret : score;
}
在这段函数中,我们只需要关注重点4,在读取完数据后推测数据。
重点5:av_probe_input_format2
声明在libavformat\avformat.h:
/** * 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. * @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. */
AVInputFormat *av_probe_input_format2(AVProbeData *pd, int is_opened, int *score_max);
其中的结构体AVProbeData,用于存储输入文件的一些信息:
/** * This structure contains the data a format has to probe a file. */
typedef struct AVProbeData {
const char *filename;
unsigned char *buf; /**< Buffer must have AVPROBE_PADDING_SIZE of extra allocated bytes filled with zero. */
int buf_size; /**< Size of buf except extra allocated bytes */
const char *mime_type; /**< mime_type, when known. */
} AVProbeData;
av_probe_input_format2()函数的定义位于libavformat\format.c:
AVInputFormat *av_probe_input_format2(AVProbeData *pd, int is_opened, int *score_max)
{
int score_ret;
AVInputFormat *fmt = av_probe_input_format3(pd, is_opened, &score_ret);
if (score_ret > *score_max) {
*score_max = score_ret;
return fmt;
} else
return NULL;
}
可以看到,其实主要是调用了av_probe_input_format3函数:
AVInputFormat *av_probe_input_format3(AVProbeData *pd, int is_opened,
int *score_ret)
{
AVProbeData lpd = *pd;
const AVInputFormat *fmt1 = NULL;
AVInputFormat *fmt = NULL;
int score, score_max = 0;
void *i = 0;
const static uint8_t zerobuffer[AVPROBE_PADDING_SIZE];
enum nodat {
NO_ID3,
ID3_ALMOST_GREATER_PROBE,
ID3_GREATER_PROBE,
ID3_GREATER_MAX_PROBE,
} nodat = NO_ID3;
if (!lpd.buf)
lpd.buf = (unsigned char *) zerobuffer;
if (lpd.buf_size > 10 && ff_id3v2_match(lpd.buf, ID3v2_DEFAULT_MAGIC)) {
int id3len = ff_id3v2_tag_len(lpd.buf);
if (lpd.buf_size > id3len + 16) {
if (lpd.buf_size < 2LL*id3len + 16)
nodat = ID3_ALMOST_GREATER_PROBE;
lpd.buf += id3len;
lpd.buf_size -= id3len;
} else if (id3len >= PROBE_BUF_MAX) {
nodat = ID3_GREATER_MAX_PROBE;
} else
nodat = ID3_GREATER_PROBE;
}
//遍历FFmpeg中所有的AVInputFormat
while ((fmt1 = av_demuxer_iterate(&i))) {
if (!is_opened == !(fmt1->flags & AVFMT_NOFILE) && strcmp(fmt1->name, "image2"))
continue;
score = 0;
//如果AVInputFormat包含read_probe
if (fmt1->read_probe) {
//获取匹配分数,重点6
score = fmt1->read_probe(&lpd);
if (score)
av_log(NULL, AV_LOG_TRACE, "Probing %s score:%d size:%d\n", fmt1->name, score, lpd.buf_size);
//比较输入媒体的扩展名和AVIputFormat的扩展名是否匹配,重点7
if (fmt1->extensions && av_match_ext(lpd.filename, fmt1->extensions)) {
switch (nodat) {
case NO_ID3:
score = FFMAX(score, 1);
break;
case ID3_GREATER_PROBE:
case ID3_ALMOST_GREATER_PROBE:
score = FFMAX(score, AVPROBE_SCORE_EXTENSION / 2 - 1);
break;
case ID3_GREATER_MAX_PROBE:
score = FFMAX(score, AVPROBE_SCORE_EXTENSION);
break;
}
}
} else if (fmt1->extensions) {
//比较输入媒体的扩展名和AVIputFormat的扩展名是否匹配,重点7
if (av_match_ext(lpd.filename, fmt1->extensions))
score = AVPROBE_SCORE_EXTENSION;//50
}
//比较输入媒体的mime_type和AVInputFormat的mime_type是否匹配,重点 8
if (av_match_name(lpd.mime_type, fmt1->mime_type)) {
if (AVPROBE_SCORE_MIME > score) {
av_log(NULL, AV_LOG_DEBUG, "Probing %s score:%d increased to %d due to MIME type\n", fmt1->name, score, AVPROBE_SCORE_MIME);
score = AVPROBE_SCORE_MIME;//75
}
}
//比较分数,得到最合适的AVInputFormat
if (score > score_max) {
score_max = score;
fmt = (AVInputFormat*)fmt1;
} else if (score == score_max)
fmt = NULL;
}
if (nodat == ID3_GREATER_PROBE)
score_max = FFMIN(AVPROBE_SCORE_EXTENSION / 2 - 1, score_max);
*score_ret = score_max;
return fmt;
}
重点6:
read_probe,用于获得匹配函数的函数指针,不同的封装格式包含不同的实现函数。以FLV封装格式为例:定义在libavformat\flvdec.c
AVInputFormat ff_flv_demuxer = {
.name = "flv",
.long_name = NULL_IF_CONFIG_SMALL("FLV (Flash Video)"),
.priv_data_size = sizeof(FLVContext),
.read_probe = flv_probe,
.read_header = flv_read_header,
.read_packet = flv_read_packet,
.read_seek = flv_read_seek,
.read_close = flv_read_close,
.extensions = "flv",
.priv_class = &flv_class,
};
其中read_probe对应的是flv_probe:
static int flv_probe(AVProbeData *p)
{
return probe(p, 0);
}
可以看到,其实就是调用了probe函数:
static int probe(AVProbeData *p, int live)
{
const uint8_t *d = p->buf;
//偏移5
unsigned offset = AV_RB32(d + 5);
if (d[0] == 'F' &&
d[1] == 'L' &&
d[2] == 'V' &&
d[3] < 5 && d[5] == 0 &&
offset + 100 < p->buf_size &&
offset > 8) {
//比较前10个字节,对于FLV文件来说,最终is_live 为 0
int is_live = !memcmp(d + offset + 40, "NGINX RTMP", 10);
if (live == is_live)
return AVPROBE_SCORE_MAX;//100
}
return 0;
}
该函数做了以下工作:
- 获取第6至第9字节的数据,并且做大小端的转换,然后存入offset变量。之所以要进行大小端转换是因为FLV是以“大端”方式存储数据,而操作系统是以“小端”方式存储数据,这一转换主要通过AV_RB32()函数实现。AV_RB32()是一个宏定义,其对应的函数是av_bswap32()。
- 检查开头3个字符(Signature)是否为“FLV”。
- 第4个字节(Version)小于5。
- 第6个字节(Headersize的第1个字节?)为0。
- offset取值大于8。
对照着FLV文件头的格式:
重点7:av_match_ext
用于比较文件的后缀。定义在format.c中:
int av_match_ext(const char *filename, const char *extensions)
{
const char *ext;
if (!filename)
return 0;
//得到‘.‘最后出现的位置
ext = strrchr(filename, '.');
if (ext)
return av_match_name(ext + 1, extensions);
return 0;
}
可以看到,其实最后调用的还是av_match_name函数
重点8:av_match_name
用于比较两个格式的名称,定义于libavutil/avstring.c:
int av_match_name(const char *name, const char *names)
{
const char *p;
int len, namelen;
if (!name || !names)
return 0;
namelen = strlen(name);
while (*names) {
int negate = '-' == *names;
p = strchr(names, ',');
if (!p)
p = names + strlen(names);
names += negate;
len = FFMAX(p - names, namelen);
if (!av_strncasecmp(name, names, len) || !strncmp("ALL", names, FFMAX(3, p - names)))
return !negate;
names = p + (*p == ',');
}
return 0;
}
上述函数还有一点需要注意,其中使用了一个while()循环,用于搜索“,”。这是因为FFmpeg中有些格式是对应多种格式名称的,例如MKV格式的解复用器(Demuxer)的定义如下。
AVInputFormat ff_matroska_demuxer = {
.name = "matroska,webm",
.long_name = NULL_IF_CONFIG_SMALL("Matroska / WebM"),
.extensions = "mkv,mk3d,mka,mks",
.priv_data_size = sizeof(MatroskaDemuxContext),
.read_probe = matroska_probe,
.read_header = matroska_read_header,
.read_packet = matroska_read_packet,
.read_close = matroska_read_close,
.read_seek = matroska_read_seek,
.mime_type = "audio/webm,audio/x-matroska,video/webm,video/x-matroska"
};
从代码可以看出,ff_matroska_demuxer中的name字段对应“matroska,webm”,mime_type字段对应“audio/webm,audio/x-matroska,video/webm,video/x-matroska”。av_match_name()函数对于这样的字符串,会把它按照“,”截断成一个个的名称,然后一一进行比较。
重点4 io_open
它是AVFormatContext在初始化时定义的函数:
static void avformat_get_context_defaults(AVFormatContext *s)
{
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);
}
io_open_default定义在avformat/options.c中:
```java
static int io_open_default(AVFormatContext *s, AVIOContext **pb,
const char *url, int flags, AVDictionary **options)
{
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;
}
可以看到,这个函数中,主要调用了两个函数ffurl_open_whitelist和ffio_fdopen,其中ffurl_open_whitelist用于初始化URLContext,并且打开它。ffio_fdopen用于根据URLContext初始化AVIOContext。URLContext中包含的URLProtocol完成了具体的协议读写工作。AVIOContext只是在URLContext的读写函数上加了一层包装。
重点2 AVInputFormat-> read_header()
在调用完init_input完成基本的初始化并且推测出相应的AVInputFormat之后,回调用read_header方法读取媒体文件的文件头,并且完成相关的初始化。
read_header是一个位于AVInputFormat结构体中的一个函数指针,对于不同的封装格式,回调用不同的read_header。例如,当属入的视频的封装格式为FLV的时候,会到FLV的AVInputFormat中的read_header,同样FLV的AVInputFormat定义位于libavformat\flvdec.c:
AVInputFormat ff_flv_demuxer = {
.name = "flv",
.long_name = NULL_IF_CONFIG_SMALL("FLV (Flash Video)"),
.priv_data_size = sizeof(FLVContext),
.read_probe = flv_probe,
.read_header = flv_read_header,
.read_packet = flv_read_packet,
.read_seek = flv_read_seek,
.read_close = flv_read_close,
.extensions = "flv",
.priv_class = &flv_class,
};
可以看到,read_header指向了flv_read_header函数:
static int flv_read_header(AVFormatContext *s)
{
int flags;
FLVContext *flv = s->priv_data;
int offset;
int pre_tag_size = 0;
//跳过4个字节
avio_skip(s->pb, 4);
//读取一个字节,第5个字节中的第0位和第2位,分别表示 video 与 audio 存在的情况.
flags = avio_r8(s->pb);
flv->missing_streams = flags & (FLV_HEADER_FLAG_HASVIDEO | FLV_HEADER_FLAG_HASAUDIO);
s->ctx_flags |= AVFMTCTX_NOHEADER;
//读取4个字节
offset = avio_rb32(s->pb);
avio_seek(s->pb, offset, SEEK_SET);
/* Annex E. The FLV File Format * E.3 TheFLVFileBody * Field Type Comment * PreviousTagSize0 UI32 Always 0 * */
pre_tag_size = avio_rb32(s->pb);
if (pre_tag_size) {
av_log(s, AV_LOG_WARNING, "Read FLV header error, input file is not a standard flv format, first PreviousTagSize0 always is 0\n");
}
s->start_time = 0;
flv->sum_flv_tag_size = 0;
flv->last_keyframe_stream_index = -1;
return 0;
}
总结
可以看到,avformat_open_input的主要主要就是根据输入的文件,创建AVFormatContext,根据文件的头格式,后缀找到合适的AVInputFormat,并初始化AVIOContext,完成读写协议,然后读取文件头。完成AVFormatContext的初始化。
边栏推荐
- 企业需要知道的5个 IAM 最佳实践
- 4.1 声明式事务之JdbcTemplate
- Can 't connect to MySQL server on' localhost3306 '(10061) simple solutions
- [Cocos] cc.sys.browserType可能的属性
- Use Patroni callback script to bind VIP pit
- 梳理CamStyle、PTGAN、SPGAN、StarGAN
- C专家编程 第4章 令人震惊的事实:数组和指针并不相同 4.2 我的代码为什么无法运行
- 渗透测试(PenTest)基础指南
- 部署LVS-DR群集【实验】
- MySQL日期函数
猜你喜欢

【问题解决】同一机器上Flask部署TensorRT报错记录

EntityComponentSystemSamples学习笔记

少年成就黑客,需要这些技能

Programming hodgepodge (4)

Towards Real-Time Multi-Object Tracking (JDE)

Summary of MySQL database interview questions (2022 latest version)

部署LVS-DR群集【实验】

4.3 Annotation-based declarative transactions and XML-based declarative transactions

如何低成本修bug?测试左移给你答案

腾讯136道高级岗面试题:多线程+算法+Redis+JVM
随机推荐
Embedded system driver primary [3] - _IO model in character device driver foundation
C Expert Programming Chapter 4 The Shocking Fact: Arrays and Pointers Are Not the Same 4.5 Other Differences Between Arrays and Pointers
Teenage Achievement Hackers Need These Skills
Will the 2023 PMP exam use the new version of the textbook?Reply is here!
自动化测试的成本高效果差,那么自动化测试的意义在哪呢?
7、特殊SQL的执行
读者让我总结一波 redis 面试题,现在肝出来了
谷粒商城-基础篇(项目简介&项目搭建)
部署LVS-DR群集【实验】
[SemiDrive source code analysis] [MailBox inter-core communication] 47 - Analysis of RPMSG_IPCC_RPC mode limit size of single transmission and limit bandwidth test
MySQL日期函数
9. Dynamic SQL
Plus版SBOM:流水线物料清单PBOM
Grain Mall - Basics (Project Introduction & Project Construction)
Redis common interview questions
编程大杂烩(四)
【一步到位】Jenkins的安装、部署、启动(完整教程)
触觉智能分享-SSD20X实现升级显示进度条
The difference between px, em, and rem
Delphi-C端有趣的菜单操作界面设计