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()函式實現開啟目標檔案和檢測檔案格式等操作,程式碼如下:
根據條件的不同,該函式內部呼叫了av_probe_input_buffer2()或avio_open2()兩個函式。av_probe_input_buffer2()函式通過分析bitstream來確定輸入的格式,程式碼如下: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); }
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個月了。 終於,在這個偉大的日子裏,白皮書的作者中本聰在位於芬蘭赫爾辛基的一個小型服務器上,親手創建了第一個區塊——即比特幣的創
C語言加密練習:第一個字母變成第26個字母,第i個字母變成第(26-i+1)個字母。非字母字符不變。要求編程序將密碼譯回原文,並輸出密碼和原文。
c語言 http () spa mage strlen str png for 1 int Afan(char a); 2 3 int main() 4 5 { 6 7 char arr[40] = {"aABX"}; 8 9 scanf("%s
架構-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