FFMPEG avformat_open_input
生活随笔
收集整理的這篇文章主要介紹了
FFMPEG avformat_open_input
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
FFMPEG avformat_open_input
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
//參數ps包含一切媒體相關的上下文結構,有它就有了一切,本函數如果打開媒體成功, //會返回一個AVFormatContext的實例. //參數filename是媒體文件名或URL. //參數fmt是要打開的媒體格式的操作結構,因為是讀,所以是inputFormat.此處可以 //傳入一個調用者定義的inputFormat,對應命令行中的 -f xxx段,如果指定了它, //在打開文件中就不會探測文件的實際格式了,以它為準了. //參數options是對某種格式的一些操作,是為了在命令行中可以對不同的格式傳入 //特殊的操作參數而建的, 為了了解流程,完全可以無視它. int avformat_open_input(AVFormatContext **ps, const char *filename, AVInputFormat *fmt, AVDictionary **options) { AVFormatContext *s = *ps; int ret = 0; AVFormatParameters ap = { { 0 } }; AVDictionary *tmp = NULL; //創建上下文結構 if (!s && !(s = avformat_alloc_context())) return AVERROR(ENOMEM); //如果用戶指定了輸入格式,直接使用它 if (fmt) s->iformat = fmt; //忽略 if (options) av_dict_copy(&tmp, *options, 0); if ((ret = av_opt_set_dict(s, &tmp)) < 0) goto fail; //打開輸入媒體(如果需要的話),初始化所有與媒體讀寫有關的結構們,比如 //AVIOContext,AVInputFormat等等 if ((ret = init_input(s, filename)) < 0) goto fail; //執行完此函數后,s->pb和s->iformat都已經指向了有效實例.pb是用于讀寫數據的,它 //把媒體數據當做流來讀寫,不管是什么媒體格式,而iformat把pb讀出來的流按某種媒體格 //式進行分析,也就是說pb在底層,iformat在上層. //很多靜態圖像文件格式,都被當作一個格式處理,比如要打開.jpeg文件,需要的格式 //名為image2.此處還不是很了解具體細節,作不得準哦. /* 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; //上下文中保存下文件名 av_strlcpy(s->filename, filename, sizeof(s->filename)); /* 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 */ //從mp3文件中讀ID3數據并保存之. if (s->pb) ff_id3v2_read(s, ID3v2_DEFAULT_MAGIC); //讀一下媒體的頭部,在read_header()中主要是做某種格式的初始化工作,比如填充自己的 //私有結構,根據流的數量分配流結構并初始化,把文件指針指向數據區開始處等. if (!(s->flags & AVFMT_FLAG_PRIV_OPT) && s->iformat->read_header) if ((ret = s->iformat->read_header(s, &ap)) < 0) goto fail; //保存數據區開始的位置 if (!(s->flags & AVFMT_FLAG_PRIV_OPT) && s->pb && !s->data_offset) s->data_offset = avio_tell(s->pb); s->raw_packet_buffer_remaining_size = RAW_PACKET_BUFFER_SIZE; if (options) { av_dict_free(options); *options = tmp; } *ps = s; //執行成功 return 0; //執行失敗 fail: 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; }分析init_input()
//打開輸入媒體并填充其AVInputFormat結構 static int init_input(AVFormatContext *s, const char *filename) { int ret; AVProbeData pd = { filename, NULL, 0 }; //當調用者已指定了pb(數據取得的方式)--一般不會這樣. if (s->pb) { s->flags |= AVFMT_FLAG_CUSTOM_IO; if (!s->iformat) //如果已指定了pb但沒指定iformat,以pb讀取媒體數據進行探測,取得.取得iformat. return av_probe_input_buffer(s->pb, &s->iformat, filename, s, 0, 0); else if (s->iformat->flags & AVFMT_NOFILE) //如果已指定pb也指定了iformat,但是又指定了不需要文件(也包括URL指定的地址),這就矛盾了, //此時應是不需要pb的,因為不需操作文件,提示一下吧,也不算錯. 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_format(&pd, 0)))) //如果已指定了iformat并且不需要文件,也就不需要pb了,可以直接返回 //如果沒指定iformat,但是可以從文件名中猜出iformat,也成功. return 0; //如果從文件名中也猜不出媒體格式,則只能打開這個文件進行探測了,先打開文件 if ((ret = avio_open(&s->pb, filename, AVIO_FLAG_READ)) < 0) return ret; if (s->iformat) return 0; //再探測之 return av_probe_input_buffer(s->pb, &s->iformat, filename, s, 0, 0); }文件打開過程 avio_open
//打開一個地址指向的媒體 int avio_open(AVIOContext **s, const char *filename, int flags) { //URLContext代表一個URL地址指向的媒體文件,本地路徑也算一種.它封裝了 //操作一個媒體文件的相關數據,最重要的是prot變量,是URLProtocol型的. //prot代表一個特定的協義和協議操作函數們,URLContext包含不同的prot, //就可以通過URLContext使用不同的協議讀寫媒體數據,比如tcp,http,本地 //文件用file協議. URLContext *h; int err; //創建并初始化URLContext,其prot通過文件名確定.然后打開這個媒體文件 err = ffurl_open(&h, filename, flags); if (err < 0) return err; //其實文件已經在上邊真正打開了.這里只是填充AVIOContext.使它記錄下 //URLContext,以及填充讀寫數據的函數指針. err = ffio_fdopen(s, h); if (err < 0) { ffurl_close(h); return err; } return 0; }探測函數 av_probe_input_buffer
int av_probe_input_buffer(AVIOContext *pb, AVInputFormat **fmt, const char *filename, void *logctx, unsigned int offset, unsigned int max_probe_size) { AVProbeData pd = { filename ? filename : "", NULL, -offset }; unsigned char *buf = NULL; int ret = 0, probe_size; //計算最多探測數據的字節數 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) { return AVERROR(EINVAL); } if (offset >= max_probe_size) { return AVERROR(EINVAL); } //循環直到探測完指定的數據 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))) { int score = probe_size < max_probe_size ? AVPROBE_SCORE_MAX / 4 : 0; int buf_offset = (probe_size == PROBE_BUF_MIN) ? 0 : probe_size >> 1; void *buftmp; if (probe_size < offset) { continue; } /* read probe data */ //分配讀取數據存放的緩沖 buftmp = av_realloc(buf, probe_size + AVPROBE_PADDING_SIZE); if (!buftmp) { av_free(buf); return AVERROR(ENOMEM); } buf = buftmp; //利用pb讀數據到緩沖的剩余空間中 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 */ } pd.buf_size += ret; pd.buf = &buf[offset]; //緩沖中沒有數據的部分要清0 memset(pd.buf + pd.buf_size, 0, AVPROBE_PADDING_SIZE); /* guess file format */ //從一個打開的文件只探測媒體格式 *fmt = av_probe_input_format2(&pd, 1, &score); if (*fmt) { if (score <= AVPROBE_SCORE_MAX / 4) { //this can only be true in the last iteration 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 (!*fmt) { av_free(buf); return AVERROR_INVALIDDATA; } /* rewind. reuse probe buffer to avoid seeking */ //把探測時讀入的數據保存到pb中,為的是真正讀時直接利用之. if ((ret = ffio_rewind_with_probe_data(pb, buf, pd.buf_size)) < 0) av_free(buf); return ret; }總結
以上是生活随笔為你收集整理的FFMPEG avformat_open_input的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: FFMPEG AVDictionary
- 下一篇: C++ c_str()