1. 程式人生 > >FFMpeg分析:第一個函式avformat_open_input

FFMpeg分析:第一個函式avformat_open_input

在上篇文章中的demo中,main函式的流程裡呼叫的第一個函式就是avformat_open_input()。直觀看來,其最明顯的功能就是制定了要播放的檔名了。但是除了問價名之外還有幾個結構體作為了函式的引數。那麼這個函式的功能是什麼?又是怎麼完成的?一起慢慢研究。

先貼程式碼:

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);
    }
    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;
        }
    }

    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 */
    if (s->pb)
        ff_id3v2_read(s, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta);

    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);

    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;
}
函式的各個引數:

1、AVFormatContext **ps:指向使用者提供的結構體,一般可以將這個引數定義指向空然後傳遞到函式中,這樣avformat_open_input函式將會分配這個結構體的記憶體空間並初始化。

2、const char *filename:開啟視訊檔案的檔名。

3、AVInputFormat *fmt:如果這個引數不為空,則指定固定的輸入格式,否則自動檢測輸入格式;一般設為空即可。

4、AVDictionary **options:由AVFormatContext和demuxer-private options組成的字典結構,可設為空。

該函式中呼叫了init_input()函式實現開啟目標檔案和檢測檔案格式等操作,程式碼如下:

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->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 = 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);
}
根據條件的不同,該函式內部呼叫了av_probe_input_buffer2()或avio_open2()兩個函式。av_probe_input_buffer2()函式通過分析bitstream來確定輸入的格式,程式碼如下:
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 : "", NULL, -offset };
    unsigned char *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))) {

        if (probe_size < offset) {
            continue;
        }
        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 */
        }
        pd.buf_size = buf_offset += ret;
        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){
            if(score <= AVPROBE_SCORE_RETRY){ //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 */
    ret = ffio_rewind_with_probe_data(pb, &buf, pd.buf_size);

    return ret < 0 ? ret : score;
}
其中呼叫了av_probe_input_format2()函式確定輸入格式,其具體實現在函式av_probe_input_format3()中。

avio_open2()的程式碼如下:

int avio_open2(AVIOContext **s, const char *filename, int flags, const AVIOInterruptCB *int_cb, AVDictionary **options)
{
    URLContext *h;
    int err;

    err = ffurl_open(&h, filename, flags, int_cb, options);
    if (err < 0)
        return err;
    err = ffio_fdopen(s, h);
    if (err < 0) {
        ffurl_close(h);
        return err;
    }
    return 0;
}
該函式用於建立和初始化一個AVIOContext結構體,用於開啟指定的url指向的資源。各個引數的含義:

1、AVIOContext **s:用於指向返回的AVIOContext結構體的指標,呼叫時應設為NULL;

2、const char *url:指定資源的地址;考慮本地播放時,就是檔名的字串的地址;

3、int flags:標誌位,控制檔案開啟的方式;

4、const AVIOInterruptCB *int_cb:在協議層使用的中斷回撥指標;

5、AVDictionary **options:protocol-private options組成的字典元素,設為空即可。

avio_open2()呼叫了ffurl_open()函式,建立一個URLContext結構體用於獲取並開啟url指向的資源(分別呼叫ffurl_alloc()和ffurl_connect()建立和連結URLContext結構體);呼叫ffio_fdopen()函式建立AVIOContext()結構體並獲取URLContext結構體引用的資源(呼叫avio_alloc_context()實現);

整個函式的大致框圖如下:


在實際執行的過程中,url_read、url_write等函式都由一個URLProtocal實現,程式碼如下:

typedef struct URLProtocol 
{
    const char *name;
    int     (*url_open)( URLContext *h, const char *url, int flags);
    /**
     * This callback is to be used by protocols which open further nested
     * protocols. options are then to be passed to ffurl_open()/ffurl_connect()
     * for those nested protocols.
     */
    int     (*url_open2)(URLContext *h, const char *url, int flags, AVDictionary **options);

    /**
     * Read data from the protocol.
     * If data is immediately available (even less than size), EOF is
     * reached or an error occurs (including EINTR), return immediately.
     * Otherwise:
     * In non-blocking mode, return AVERROR(EAGAIN) immediately.
     * In blocking mode, wait for data/EOF/error with a short timeout (0.1s),
     * and return AVERROR(EAGAIN) on timeout.
     * Checking interrupt_callback, looping on EINTR and EAGAIN and until
     * enough data has been read is left to the calling function; see
     * retry_transfer_wrapper in avio.c.
     */
    int     (*url_read)( URLContext *h, unsigned char *buf, int size);
    int     (*url_write)(URLContext *h, const unsigned char *buf, int size);
    int64_t (*url_seek)( URLContext *h, int64_t pos, int whence);
    int     (*url_close)(URLContext *h);
    struct URLProtocol *next;
    int (*url_read_pause)(URLContext *h, int pause);
    int64_t (*url_read_seek)(URLContext *h, int stream_index,
                             int64_t timestamp, int flags);
    int (*url_get_file_handle)(URLContext *h);
    int (*url_get_multi_file_handle)(URLContext *h, int **handles,
                                     int *numhandles);
    int (*url_shutdown)(URLContext *h, int flags);
    int priv_data_size;
    const AVClass *priv_data_class;
    int flags;
    int (*url_check)(URLContext *h, int mask);
} URLProtocol;
針對不同的協議,分別定義了不同的URLPrococol物件來進行具體的操作,如:

檔案協議:

URLProtocol ff_pipe_protocol = {
    .name                = "pipe",
    .url_open            = pipe_open,
    .url_read            = file_read,
    .url_write           = file_write,
    .url_get_file_handle = file_get_handle,
    .url_check           = file_check,
    .priv_data_size      = sizeof(FileContext),
    .priv_data_class     = &pipe_class,
};
ftp協議:
URLProtocol ff_ftp_protocol = {
    .name                = "ftp",
    .url_open            = ftp_open,
    .url_read            = ftp_read,
    .url_write           = ftp_write,
    .url_seek            = ftp_seek,
    .url_close           = ftp_close,
    .url_get_file_handle = ftp_get_file_handle,
    .url_shutdown        = ftp_shutdown,
    .priv_data_size      = sizeof(FTPContext),
    .priv_data_class     = &ftp_context_class,
    .flags               = URL_PROTOCOL_FLAG_NETWORK,
};
http協議:
URLProtocol ff_http_protocol = {
    .name                = "http",
    .url_open2           = http_open,
    .url_read            = http_read,
    .url_write           = http_write,
    .url_seek            = http_seek,
    .url_close           = http_close,
    .url_get_file_handle = http_get_file_handle,
    .url_shutdown        = http_shutdown,
    .priv_data_size      = sizeof(HTTPContext),
    .priv_data_class     = &http_context_class,
    .flags               = URL_PROTOCOL_FLAG_NETWORK,
};
rtmp協議:
URLProtocol ff_librtmp_protocol =
 {
    .name                = "rtmp",
    .url_open            = rtmp_open,
    .url_read            = rtmp_read,
    .url_write           = rtmp_write,
    .url_close           = rtmp_close,
    .url_read_pause      = rtmp_read_pause,
    .url_read_seek       = rtmp_read_seek,
    .url_get_file_handle = rtmp_get_file_handle,
    .priv_data_size      = sizeof(LibRTMPContext),
    .priv_data_class     = &librtmp_class,
    .flags               = URL_PROTOCOL_FLAG_NETWORK,
};

相關推薦

FFMpeg分析第一函式avformat_open_input

在上篇文章中的demo中,main函式的流程裡呼叫的第一個函式就是avformat_open_input()。直觀看來,其最明顯的功能就是制定了要播放的檔名了。但是除了問價名之外還有幾個結構體作為了函式的引數。那麼這個函式的功能是什麼?又是怎麼完成的?一起慢慢研究。 先貼程

ArcGIS API for JavaScript學習(1)第一地圖

樣式表 參數 資源 charset 底層 arcgis 順序 api navi 1.簡介 ArcGIS API for JavaScript跟隨ArcGIS 9.3同時發布,是ESRI根據JavaScript技術實現的調用ArcGIS Server REST API接口的一

動畫《區塊鏈100問》第4集第一比特幣誕生啦!

ans 危機 lin 北京時間 wrapper 金融 con RM block 北京時間2009年1月4日,距離比特幣白皮書的發布已經過去3個月了。   終於,在這個偉大的日子裏,白皮書的作者中本聰在位於芬蘭赫爾辛基的一個小型服務器上,親手創建了第一個區塊——即比特幣的創

架構-Eureka第一方法

返回頂部 autowired sel bin pre html doctype 返回 ica ylbtech-架構-Eureka:第一個方法 工程介紹 Spring Cloud 工程目錄 model registry-center Servers tzxyf

TensorFlow入門之一第一機器學習Demo

版權宣告:本文為博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/geyunfei_/article/details/78782804 本文主要通過一個簡單的 Demo 介紹 TensorFlow 初級 API 的使用方法,因為自己也是初學者,因此本文的目

快應用入門第一快應用程式

簡介 快應用是九大手機廠商基於硬體平臺共同推出的新型應用生態,依附在各大應用市場。使用者無需下載安裝,即點即用,享受原生應用的效能體驗。2018年3月20日在北京推出“快應用”標準。該應用模組以標準js語法+重組css標籤+Flexbox佈局+mvvm模式。 第一

使用idea的時候遇到的n問題第一是關於註冊過期,怎麼才可以永久使用,第二是關於maven繫結到idea,第三。。。。第n。。

關於idea的n個問題,我也是網上到處找,然後發現下邊部落格幫我解決了相關問題: 問題一:一個是關於註冊過期,怎麼才可以永久使用 https://blog.csdn.net/f317363184/article/details/78808925?utm_source=blogxgwz4

LeetCode演算法題278第一錯誤的版本解析

你是產品經理,目前正在帶領一個團隊開發新的產品。不幸的是,你的產品的最新版本沒有通過質量檢測。由於每個版本都是基於之前的版本開發的,所以錯誤的版本之後的所有版本都是錯的。 假設你有 n 個版本 [1, 2, …, n],你想找出導致之後所有版本出錯的第一個錯誤的版本。 你可以通過呼叫

Python 資料分析第一篇 準備工作

一、安裝或升級Python包 1、安裝Anaconda中的Python包conda install package_name 或者pip install package_name ⽤conda update命令升級包conda update package_name 或者pip install --upg

Godot3遊戲引擎入門之二第一簡單的遊戲場景

一、前言 最近工作時間安排地非常緊湊,除了週日一天,已經沒有其他空閒時間了。不過到了 10 月份會慢慢恢復,目前我在抽出一點時間好好準備這個 Godot 系列,邊寫邊學習邊迎接Godot 3.1 版本的到來,也算是一件高興地事情,哈哈。 :sunglasses:

Django學習1第一頁面

Django學習1  Django是python後臺開發的一個熱門框架,對於一般web開發有較為簡潔,快速的特性。 step1:安裝 Django  鑑於anaconda的整合開發環境,我選擇在anconda中安裝Django,在環境中安裝Django需要

劍指offer-34第一只出現一次的字元

題目描述 在一個字串(0<=字串長度<=10000,全部由字母組成)中找到第一個只出現一次的字元,並返回它的位置, 如果沒有則返回 -1(需要區分大小寫). 思路 將每個字元放入對映表中,key為char ,value為每個char出現的次數。 程式碼 pub

爬蟲簡單入門第一簡單爬蟲

import requests # socket-->http-->requests response=requests.get('https://tieba.baidu.com/f?kw=%E6%B5%81%E6%B5%AA%E6%B1%89') print(response.text)

21 字串專題第一只出現一次的字元

0 引言 題目:在一個字串(0<=字串長度<=10000,全部由字母組成)中找到第一個只出現一次的字元,並返回它的位置, 如果沒有則返回 -1(需要區分大小寫). 1 抽象問題具體化 舉例:返回字串 “google”中第一個只出現一次的字元,如果沒有則返回-1. 解答:1. 遍歷

劍指Offer3第一只出現一次的字元

題目描述 在一個字串(0<=字串長度<=10000,全部由字母組成)中找到第一個只出現一次的字元,並返回它的位置, 如果沒有則返回 -1(需要區分大小寫). 解題思路 使用HashMap集合

劍指Offer32第一只出現一次的字元

思路: 利用雜湊表,雜湊表的下標是字元的ASCII值,值為字元出現的次數。 # -*- coding:utf-8 -*- class Solution: def FirstNotRepeatingChar(self, s): # write code here #建立

通證經濟大局觀(三十三)第一資本主義國家

城市的貨幣經濟,淘汰了貴族的莊園經濟,加速了貴族的沒落和兼併,促進了王權的崛起。 但王權和市民的蜜月期過了之後,國王無限制的權力又與信奉自由平等的市民階級格格不入。雙方之間必然產生衝突。 因為市民階級和資本主義是發展最快的力量,所以最終獲得勝利其實是歷史的必然。而具體在哪

劍指offer 34第一只出現一次的字元

遍歷 #include <iostream> #include <string> using namespace std; class Solution { public: int FirstNotRepeatingChar(string str) {

(87)--Python資料分析指數密度函式與指數分佈圖

# 指數密度函式與指數分佈圖 lambd = 0.5 x = np.arange(0,15,0.1) y = lambd*np.exp(-lambd*x) plt.plot(x,y) plt.title