// ps 為成功打開媒體文件后的上下文。filename為要打開的文件名。
// fmt為強制指定以何種格式打開文件,如果fmt為null,ffmpeg將自動分析媒體文件頭,以媒體文件指定的格式(解碼器和解碼器參數)打開。
// 該函數只分析解碼器、解碼器參數,並形成上下文,但不打開解碼器。
// 關於(*ps)->pb 信息如下:
/**
* AVFormatContext.pb
* 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.
*/
int avformat_open_input(AVFormatContext **ps, const char *filename,
AVInputFormat *fmt, AVDictionary **options)
{
AVFormatContext *s = *ps;
int ret = 0;
AVDictionary *tmp = NULL;
ID3v2ExtraMeta *id3v2_extra_meta = NULL;
// 申請上下文緩沖。
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);
}
// 如是入參fmt不為空,就強制媒體格式為他。
if (fmt)
s->iformat = fmt;
// 如果選項不過空,就指定。
if (options)
av_dict_copy(&tmp, *options, 0);
if ((ret = av_opt_set_dict(s, &tmp)) < 0)
goto fail;
// 初始化打開文件。
if ((ret = init_input(s, filename, &tmp)) < 0)
goto fail;
s->probe_score = ret;
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;
}
}
// #define AV_NOPTS_VALUE ((int64_t)UINT64_C(0x8000000000000000)) demuxer can provide the dts or pts
s->duration = s->start_time = AV_NOPTS_VALUE;
av_strlcpy(s->filename, filename ? filename : "", sizeof(s->filename));
/* 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 */
// id3v2 v2版一般位於mp3的開頭,可以存儲歌詞,該專輯的圖片等大容量的信息。
if (s->pb)
ff_id3v2_read(s, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta, 0);
if (!(s->flags&AVFMT_FLAG_PRIV_OPT) && s->iformat->read_header)
if ((ret = s->iformat->read_header(s)) < 0)
goto fail;
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;
} 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->data_offset)
s->data_offset = avio_tell(s->pb);
// #define RAW_PACKET_BUFFER_SIZE 2500000
s->raw_packet_buffer_remaining_size = RAW_PACKET_BUFFER_SIZE;
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_close(s->pb);
avformat_free_context(s);
*ps = NULL;
return ret;
}
static const char* format_to_name(void* ptr)
{
AVFormatContext* fc = (AVFormatContext*) ptr;
if(fc->iformat) return fc->iformat->name;
else if(fc->oformat) return fc->oformat->name;
else return "NULL";
}
static void *format_child_next(void *obj, void *prev)
{
AVFormatContext *s = obj;
if (!prev && s->priv_data &&
((s->iformat && s->iformat->priv_class) ||
s->oformat && s->oformat->priv_class))
return s->priv_data;
if (s->pb && s->pb->av_class && prev != s->pb)
return s->pb;
return NULL;
}
static const AVClass *format_child_class_next(const AVClass *prev)
{
AVInputFormat *ifmt = NULL;
AVOutputFormat *ofmt = NULL;
if (!prev)
return &ffio_url_class;
while ((ifmt = av_iformat_next(ifmt)))
if (ifmt->priv_class == prev)
break;
if (!ifmt)
while ((ofmt = av_oformat_next(ofmt)))
if (ofmt->priv_class == prev)
break;
if (!ofmt)
while (ifmt = av_iformat_next(ifmt))
if (ifmt->priv_class)
return ifmt->priv_class;
while (ofmt = av_oformat_next(ofmt))
if (ofmt->priv_class)
return ofmt->priv_class;
return NULL;
}
static AVClassCategory get_category(void *ptr)
{
AVFormatContext* s = ptr;
if(s->iformat) return AV_CLASS_CATEGORY_DEMUXER;
else return AV_CLASS_CATEGORY_MUXER;
}
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,
};
static void avformat_get_context_defaults(AVFormatContext *s)
{
memset(s, 0, sizeof(AVFormatContext));
s->av_class = &av_format_context_class;
av_opt_set_defaults(s);
}
AVFormatContext *avformat_alloc_context(void)
{
AVFormatContext *ic;
ic = av_malloc(sizeof(AVFormatContext));
if (!ic) return ic;
avformat_get_context_defaults(ic);
ic->internal = av_mallocz(sizeof(*ic->internal));
if (!ic->internal) {
avformat_free_context(ic);
return NULL;
}
return ic;
}
int av_filename_number_test(const char *filename)
{
char buf[1024];
return filename &&
(av_get_frame_filename(buf, sizeof(buf), filename, 1) >= 0);
}
AVInputFormat *av_probe_input_format3(AVProbeData *pd, int is_opened,
int *score_ret)
{
AVProbeData lpd = *pd;
AVInputFormat *fmt1 = NULL, *fmt;
int score, nodat = 0, score_max = 0;
const static uint8_t zerobuffer[AVPROBE_PADDING_SIZE];
if (!lpd.buf)
lpd.buf = 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) {
lpd.buf += id3len;
lpd.buf_size -= id3len;
} else if (id3len >= PROBE_BUF_MAX) {
nodat = 2;
} else
nodat = 1;
}
// av_register_all 注冊了所有的編解碼器,復合器等,更具標記或是擴展名分析出自動用哪個。
fmt = NULL;
while ((fmt1 = av_iformat_next(fmt1))) {
if (!is_opened == !(fmt1->flags & AVFMT_NOFILE))
continue;
score = 0;
if (fmt1->read_probe) {
score = fmt1->read_probe(&lpd);
if (fmt1->extensions && av_match_ext(lpd.filename, fmt1->extensions)) {
if (nodat == 0) score = FFMAX(score, 1);
else if (nodat == 1) score = FFMAX(score, AVPROBE_SCORE_EXTENSION / 2 - 1);
else score = FFMAX(score, AVPROBE_SCORE_EXTENSION);
}
} else if (fmt1->extensions) {
if (av_match_ext(lpd.filename, fmt1->extensions))
score = AVPROBE_SCORE_EXTENSION;
}
if (score > score_max) {
score_max = score;
fmt = fmt1;
} else if (score == score_max)
fmt = NULL;
}
if (nodat == 1)
score_max = FFMIN(AVPROBE_SCORE_EXTENSION / 2 - 1, score_max);
*score_ret = score_max;
return fmt;
}
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;
}
AVInputFormat *av_probe_input_format(AVProbeData *pd, int is_opened)
{
int score = 0;
return av_probe_input_format2(pd, is_opened, &score);
}
static int set_codec_from_probe_data(AVFormatContext *s, AVStream *st,
AVProbeData *pd)
{
static const struct {
const char *name;
enum AVCodecID id;
enum AVMediaType type;
} fmt_id_type[] = {
{ "aac", AV_CODEC_ID_AAC, AVMEDIA_TYPE_AUDIO },
{ "ac3", AV_CODEC_ID_AC3, AVMEDIA_TYPE_AUDIO },
{ "dts", AV_CODEC_ID_DTS, AVMEDIA_TYPE_AUDIO },
{ "eac3", AV_CODEC_ID_EAC3, AVMEDIA_TYPE_AUDIO },
{ "h264", AV_CODEC_ID_H264, AVMEDIA_TYPE_VIDEO },
{ "hevc", AV_CODEC_ID_HEVC, AVMEDIA_TYPE_VIDEO },
{ "loas", AV_CODEC_ID_AAC_LATM, AVMEDIA_TYPE_AUDIO },
{ "m4v", AV_CODEC_ID_MPEG4, AVMEDIA_TYPE_VIDEO },
{ "mp3", AV_CODEC_ID_MP3, AVMEDIA_TYPE_AUDIO },
{ "mpegvideo", AV_CODEC_ID_MPEG2VIDEO, AVMEDIA_TYPE_VIDEO },
{ 0 }
};
int score;
AVInputFormat *fmt = av_probe_input_format3(pd, 1, &score);
if (fmt && st->request_probe <= score) {
int i;
av_log(s, AV_LOG_DEBUG,
"Probe with size=%d, packets=%d detected %s with score=%d\n",
pd->buf_size, MAX_PROBE_PACKETS - st->probe_packets,
fmt->name, score);
for (i = 0; fmt_id_type[i].name; i++) {
if (!strcmp(fmt->name, fmt_id_type[i].name)) {
st->codec->codec_id = fmt_id_type[i].id;
st->codec->codec_type = fmt_id_type[i].type;
break;
}
}
}
return score;
}
/************************************************************/
/* input media file */
int av_demuxer_open(AVFormatContext *ic) {
int err;
if (ic->iformat->read_header) {
err = ic->iformat->read_header(ic);
if (err < 0)
return err;
}
if (ic->pb && !ic->data_offset)
ic->data_offset = avio_tell(ic->pb);
return 0;
}
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;
uint8_t *mime_type;
int ret = 0, probe_size, buf_offset = 0;
int score = 0;
if (!max_probe_size)
max_probe_size = PROBE_BUF_MAX;
else if (max_probe_size > PROBE_BUF_MAX)
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 (!*fmt && pb->av_class && av_opt_get(pb, "mime_type", AV_OPT_SEARCH_CHILDREN, &mime_type) >= 0 && mime_type) {
if (!av_strcasecmp(mime_type, "audio/aacp")) {
*fmt = av_find_input_format("aac");
}
av_freep(&mime_type);
}
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)
return ret;
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) {
av_free(buf);
return ret;
}
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. */
*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) {
av_free(buf);
return AVERROR_INVALIDDATA;
}
/* Rewind. Reuse probe buffer to avoid seeking. */
ret = ffio_rewind_with_probe_data(pb, &buf, buf_offset);
return ret < 0 ? ret : score;
}
int av_probe_input_buffer(AVIOContext *pb, AVInputFormat **fmt,
const char *filename, void *logctx,
unsigned int offset, unsigned int max_probe_size)
{
int ret = av_probe_input_buffer2(pb, fmt, filename, logctx, offset, max_probe_size);
return ret < 0 ? ret : 0;
}
/* 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;
// 一般這里不設置pb,要設置pb必須在avformat_open_input之前設置。所以這里不執行。
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->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;
}
// 一般也不設置iformat,需要讓ffmpeg自己去探測。
if ((s->iformat && s->iformat->flags & AVFMT_NOFILE) ||
(!s->iformat && (s->iformat = av_probe_input_format2(&pd, 0, &score))))
return score;
if ((ret = avio_open2(&s->pb, filename, AVIO_FLAG_READ | s->avio_flags,
&s->interrupt_callback, options)) < 0)
return ret;
if (s->iformat)
return 0;
return av_probe_input_buffer2(s->pb, &s->iformat, filename,
s, 0, s->probesize);
}