// 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); }
从 avformat_open_input 中解图ffmpeg准备decode默认参数的过程。,布布扣,bubuko.com
从 avformat_open_input 中解图ffmpeg准备decode默认参数的过程。
原文:http://www.cnblogs.com/zaboli/p/3760813.html