本文參考雷神博文整理:https://blog.csdn.net/leixiaohua1020羔飞。
init_input它的主要工作就是打開(kāi)輸入的視頻數(shù)據(jù)并且探測(cè)視頻的格式琢蛤。該函數(shù)的定義位于libavformat\utils.c怔蚌,整體調(diào)用結(jié)構(gòu)如下(注:目前分析的版本是4.0.2,圖中函數(shù)調(diào)用名稱可能跟舊版本不對(duì)應(yīng))。
init_input()
/* 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;
//當(dāng)使用了自定義的AVIOContext的時(shí)候(AVFormatContext中的AVIOContext不為空朗伶,即
//s->pb!=NULL)好渠,如果指定了AVInputFormat就直接返回昨稼,如果沒(méi)有指定就
//調(diào)用av_probe_input_buffer2()推測(cè)AVInputFormat。這一情況出現(xiàn)的不算很多拳锚,但是當(dāng)我們
//從內(nèi)存中讀取數(shù)據(jù)的時(shí)候(需要初始化自定義的AVIOContext)假栓,就會(huì)執(zhí)行這一步驟。
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->format_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;
}
//在更一般的情況下霍掺,如果已經(jīng)指定了AVInputFormat匾荆,就直接返回;如果沒(méi)有
//指定AVInputFormat杆烁,就調(diào)用av_probe_input_format(NULL,…)根據(jù)文件路徑判斷文件格式牙丽。
//這里特意把a(bǔ)v_probe_input_format()的第1個(gè)參數(shù)寫成“NULL”,是為了強(qiáng)調(diào)這個(gè)時(shí)候?qū)嶋H上
//并沒(méi)有給函數(shù)提供輸入數(shù)據(jù)兔魂,此時(shí)僅僅通過(guò)文件路徑推測(cè)AVInputFormat剩岳。
if ((s->iformat && s->iformat->flags & AVFMT_NOFILE) ||
(!s->iformat && (s->iformat = av_probe_input_format2(&pd, 0, &score))))
return score;
//如果發(fā)現(xiàn)通過(guò)文件路徑判斷不出來(lái)文件格式,那么就需要打開(kāi)文件探測(cè)文件格式了入热,這個(gè)
//時(shí)候會(huì)首先調(diào)用io_open打開(kāi)文件拍棕,io_open在avformat_alloc_context()----->avformat_get_context_defaults(ic)----->s->io_open = io_open_default賦值。
//然后調(diào)用av_probe_input_buffer2()推測(cè)AVInputFormat勺良。
if ((ret = s->io_open(s, &s->pb, filename, AVIO_FLAG_READ | s->avio_flags, options)) < 0)
return ret;
if (s->iformat)
return 0;
return av_probe_input_buffer2(s->pb, &s->iformat, filename,
s, 0, s->format_probesize);
}
下面分析一下av_probe_input_format2()绰播,s->io_open(),av_probe_input_buffer2()這幾個(gè)函數(shù)
av_probe_input_format2()
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;
}
該函數(shù)用于根據(jù)輸入數(shù)據(jù)查找合適的AVInputFormat尚困。參數(shù)含義如下所示:
pd:存儲(chǔ)輸入數(shù)據(jù)信息的AVProbeData結(jié)構(gòu)體蠢箩。
is_opened:文件是否打開(kāi)。
score_max:判決AVInputFormat的門限值。只有某格式判決分?jǐn)?shù)大于該門限值的時(shí)候谬泌,函數(shù)才會(huì)返回該封裝格式滔韵,否則返回NULL。
該函數(shù)中涉及到一個(gè)結(jié)構(gòu)體AVProbeData掌实,用于存儲(chǔ)輸入文件的一些信息陪蜻,它的定義如下所示。
/**
* This structure contains the data a format has to probe a file.
*/
typedef struct AVProbeData {
const char *filename;
unsigned char *buf; /**< Buffer must have AVPROBE_PADDING_SIZE of extra allocated bytes filled with zero. */
int buf_size; /**< Size of buf except extra allocated bytes */
const char *mime_type; /**< mime_type, when known. */
} AVProbeData;
av_probe_input_format3()
av_probe_input_format3()是一個(gè)API函數(shù)贱鼻,聲明位于libavformat\avformat.h宴卖,如下所示。
/**
* Guess the file format.
*
* @param is_opened Whether the file is already opened; determines whether
* demuxers with or without AVFMT_NOFILE are probed.
* @param score_ret The score of the best detection.
*/
AVInputFormat *av_probe_input_format3(AVProbeData *pd, int is_opened, int *score_ret);
從函數(shù)聲明中可以看出邻悬,av_probe_input_format3()和av_probe_input_format2()的區(qū)別是函數(shù)的第3個(gè)參數(shù)不同:av_probe_input_format2()是一個(gè)分?jǐn)?shù)的門限值症昏,而av_probe_input_format3()是一個(gè)探測(cè)后的最匹配的格式的分?jǐn)?shù)值。
AVInputFormat *av_probe_input_format3(AVProbeData *pd, int is_opened,
int *score_ret)
{
AVProbeData lpd = *pd;
const AVInputFormat *fmt1 = NULL;
AVInputFormat *fmt = NULL;
int score, score_max = 0;
void *i = 0;
const static uint8_t zerobuffer[AVPROBE_PADDING_SIZE];
enum nodat {
NO_ID3,
ID3_ALMOST_GREATER_PROBE,
ID3_GREATER_PROBE,
ID3_GREATER_MAX_PROBE,
} nodat = NO_ID3;
if (!lpd.buf)
lpd.buf = (unsigned char *) zerobuffer;
//id3相關(guān)的父丰,這里先不管肝谭。MP3文件才有的
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) {
if (lpd.buf_size < 2LL*id3len + 16)
nodat = ID3_ALMOST_GREATER_PROBE;
lpd.buf += id3len;
lpd.buf_size -= id3len;
} else if (id3len >= PROBE_BUF_MAX) {
nodat = ID3_GREATER_MAX_PROBE;
} else
nodat = ID3_GREATER_PROBE;
}
while ((fmt1 = av_demuxer_iterate(&i))) {
if (!is_opened == !(fmt1->flags & AVFMT_NOFILE) && strcmp(fmt1->name, "image2"))
continue;
score = 0;
//如果AVInputFormat中包含read_probe(),就調(diào)用read_probe()函數(shù)獲取匹配分?jǐn)?shù)(這一方法
//如果結(jié)果匹配的話蛾扇,一般會(huì)獲得AVPROBE_SCORE_MAX的分值分苇,即100分)
if (fmt1->read_probe) {
score = fmt1->read_probe(&lpd);
if (score)
av_log(NULL, AV_LOG_TRACE, "Probing %s score:%d size:%d\n", fmt1->name, score, lpd.buf_size);
if (fmt1->extensions && av_match_ext(lpd.filename, fmt1->extensions)) {
switch (nodat) {
case NO_ID3:
score = FFMAX(score, 1);
break;
case ID3_GREATER_PROBE:
case ID3_ALMOST_GREATER_PROBE:
score = FFMAX(score, AVPROBE_SCORE_EXTENSION / 2 - 1);
break;
case ID3_GREATER_MAX_PROBE:
score = FFMAX(score, AVPROBE_SCORE_EXTENSION);
break;
}
}
} else if (fmt1->extensions) {
//如果不包含該函數(shù),就使用av_match_ext()函數(shù)比較輸入媒體的擴(kuò)展名和AVInputFormat的
//擴(kuò)展名是否匹配屁桑,如果匹配的話医寿,設(shè)定匹配分?jǐn)?shù)
//為AVPROBE_SCORE_EXTENSION(AVPROBE_SCORE_EXTENSION取值為50,即50分)蘑斧。
if (av_match_ext(lpd.filename, fmt1->extensions))
score = AVPROBE_SCORE_EXTENSION;
}
//使用av_match_name()比較輸入媒體的mime_type和AVInputFormat的mime_type靖秩,如果匹配
//的話,設(shè)定匹配分?jǐn)?shù)為AVPROBE_SCORE_MIME(AVPROBE_SCORE_MIME取值為75竖瘾,
//即75分)
if (av_match_name(lpd.mime_type, fmt1->mime_type)) {
if (AVPROBE_SCORE_MIME > score) {
av_log(NULL, AV_LOG_DEBUG, "Probing %s score:%d increased to %d due to MIME type\n", fmt1->name, score, AVPROBE_SCORE_MIME);
score = AVPROBE_SCORE_MIME;
}
}
if (score > score_max) {
score_max = score;
fmt = (AVInputFormat*)fmt1;
} else if (score == score_max)
fmt = NULL;
}
if (nodat == ID3_GREATER_PROBE)
score_max = FFMIN(AVPROBE_SCORE_EXTENSION / 2 - 1, score_max);
*score_ret = score_max;
return fmt;
}
av_probe_input_format3()根據(jù)輸入數(shù)據(jù)查找合適的AVInputFormat沟突。輸入的數(shù)據(jù)位于AVProbeData中。前文已經(jīng)提到過(guò)捕传,其中filename是文件路徑惠拭, buf存儲(chǔ)用于推測(cè)AVInputFormat的媒體數(shù)據(jù),最后還有個(gè)mime_type保存媒體的類型庸论。其中buf可以為空职辅,但是其后面無(wú)論如何都需要填充AVPROBE_PADDING_SIZE個(gè)0(AVPROBE_PADDING_SIZE取值為32,即32個(gè)0)聂示。
typedef struct AVProbeData {
const char *filename;
unsigned char *buf; /**< Buffer must have AVPROBE_PADDING_SIZE of extra allocated bytes filled with zero. */
int buf_size; /**< Size of buf except extra allocated bytes */
const char *mime_type; /**< mime_type, when known. */
} AVProbeData;
該函數(shù)最主要的部分是一個(gè)循環(huán)域携。該循環(huán)調(diào)用av_demuxer_iterate()遍歷FFmpeg中所有的AVInputFormat,并根據(jù)以下規(guī)則確定AVInputFormat和輸入媒體數(shù)據(jù)的匹配分?jǐn)?shù)(score鱼喉,反應(yīng)匹配程度)
(1)如果AVInputFormat中包含read_probe()秀鞭,就調(diào)用read_probe()函數(shù)獲取匹配分?jǐn)?shù)(這一方法如果結(jié)果匹配的話趋观,一般會(huì)獲得AVPROBE_SCORE_MAX的分值,即100分)锋边。如果不包含該函數(shù)皱坛,就使用av_match_ext()函數(shù)比較輸入媒體的擴(kuò)展名和AVInputFormat的擴(kuò)展名是否匹配,如果匹配的話豆巨,設(shè)定匹配分?jǐn)?shù)為AVPROBE_SCORE_EXTENSION(AVPROBE_SCORE_EXTENSION取值為50剩辟,即50分)。
(2)使用av_match_name()比較輸入媒體的mime_type和AVInputFormat的mime_type搀矫,如果匹配的話,設(shè)定匹配分?jǐn)?shù)為AVPROBE_SCORE_MIME(AVPROBE_SCORE_MIME取值為75刻肄,即75分)瓤球。
(3)如果該AVInputFormat的匹配分?jǐn)?shù)大于此前的最大匹配分?jǐn)?shù),則記錄當(dāng)前的匹配分?jǐn)?shù)為最大匹配分?jǐn)?shù)敏弃,并且記錄當(dāng)前的AVInputFormat為最佳匹配的AVInputFormat卦羡。
上述過(guò)程中涉及到以下幾個(gè)知識(shí)點(diǎn):
AVInputFormat->read_probe()
AVInputFormat中包含read_probe()是用于獲得匹配函數(shù)的函數(shù)指針,不同的封裝格式包含不同的實(shí)現(xiàn)函數(shù)麦到。位于libavformat\目錄下的flvdec.c绿饵,movdec.c等。
此函數(shù)作用一般是從文件中獲取文件頭的數(shù)據(jù)來(lái)匹配瓶颠。例如文件頭包含F(xiàn)LV或者M(jìn)OOV這些這段表明是flv文件或者mov文件等拟赊。
av_match_ext()
int av_match_ext(const char *filename, const char *extensions)
{
const char *ext;
if (!filename)
return 0;
//該函數(shù)返回 str 中最后一次出現(xiàn)字符 c 的位置。如果未找到該值粹淋,則函數(shù)返回一個(gè)空指針吸祟。
//例如返回“.mov”
ext = strrchr(filename, '.');
//av_match_name用于比較兩個(gè)格式的名稱。簡(jiǎn)單地說(shuō)就是比較字符串桃移。注意該函數(shù)的字符串是
//不區(qū)分大小寫的屋匕,字符都轉(zhuǎn)換為小寫進(jìn)行比較。
if (ext)
return av_match_name(ext + 1, extensions);
return 0;
}
av_match_name()
int av_match_name(const char *name, const char *names)
{
const char *p;
int len, namelen;
if (!name || !names)
return 0;
//strlen()用來(lái)計(jì)算指定的字符串s 的長(zhǎng)度借杰,不包括結(jié)束字符"\0"
namelen = strlen(name);
while (*names) {
int negate = '-' == *names;
//查找字符串names中首次出現(xiàn)','字符的位置
p = strchr(names, ',');
if (!p)
p = names + strlen(names);
names += negate;
len = FFMAX(p - names, namelen);
//比較name跟names的len長(zhǎng)度过吻,先轉(zhuǎn)成小寫來(lái)比較,相等的話返回0
//整個(gè)函數(shù)return 蔗衡!negate纤虽;
if (!av_strncasecmp(name, names, len) || !strncmp("ALL", names, FFMAX(3, p - names)))
return !negate;
names = p + (*p == ',');
}
return 0;
}
上述函數(shù)還有一點(diǎn)需要注意,其中使用了一個(gè)while()循環(huán)绞惦,用于搜索“,”廓推。這是因?yàn)镕Fmpeg中有些格式是對(duì)應(yīng)多種格式名稱的,例如MKV格式的解復(fù)用器(Demuxer)的定義如下翩隧。
AVInputFormat ff_matroska_demuxer = {
.name = "matroska,webm",
.long_name = NULL_IF_CONFIG_SMALL("Matroska / WebM"),
.extensions = "mkv,mk3d,mka,mks",
.priv_data_size = sizeof(MatroskaDemuxContext),
.read_probe = matroska_probe,
.read_header = matroska_read_header,
.read_packet = matroska_read_packet,
.read_close = matroska_read_close,
.read_seek = matroska_read_seek,
.mime_type = "audio/webm,audio/x-matroska,video/webm,video/x-matroska"
};
從代碼可以看出樊展,ff_matroska_demuxer中的name字段對(duì)應(yīng)“matroska,webm”呻纹,mime_type字段對(duì)應(yīng)“audio/webm,audio/x-matroska,video/webm,video/x-matroska”。av_match_name()函數(shù)對(duì)于這樣的字符串专缠,會(huì)把它按照“,”截?cái)喑梢粋€(gè)個(gè)的名稱雷酪,然后一一進(jìn)行比較。
io_open->io_open_default
在avformat_alloc_context()----->avformat_get_context_defaults(ic)----->s->io_open = io_open_default賦值.此函數(shù)后續(xù)再看涝婉,打開(kāi)本地文件或者URL基本都是進(jìn)入這里識(shí)別出AVInputFormat 跟AVIOContext哥力。
ffmpeg源碼分析4-io_open_default
av_probe_input_buffer2()
av_probe_input_buffer2()是一個(gè)API函數(shù),它根據(jù)輸入的媒體數(shù)據(jù)推測(cè)該媒體數(shù)據(jù)的AVInputFormat墩弯,聲明位于libavformat\avformat.h吩跋,如下所示。
/**
* Probe a bytestream to determine the input format. Each time a probe returns
* with a score that is too low, the probe buffer size is increased and another
* attempt is made. When the maximum probe size is reached, the input format
* with the highest score is returned.
*
* @param pb the bytestream to probe
* @param fmt the input format is put here
* @param filename the filename of the stream
* @param logctx the log context
* @param offset the offset within the bytestream to probe from
* @param max_probe_size the maximum probe buffer size (zero for default)
* @return the score in case of success, a negative value corresponding to an
* the maximal score is AVPROBE_SCORE_MAX
* AVERROR code otherwise
*/
int av_probe_input_buffer2(AVIOContext *pb, AVInputFormat **fmt,
const char *filename, void *logctx,
unsigned int offset, unsigned int max_probe_size);
av_probe_input_buffer2()參數(shù)的含義如下所示:
pb:用于讀取數(shù)據(jù)的AVIOContext渔工。
fmt:輸出推測(cè)出來(lái)的AVInputFormat锌钮。
filename:輸入媒體的路徑。
logctx:日志(沒(méi)有研究過(guò))引矩。
offset:開(kāi)始推測(cè)AVInputFormat的偏移量梁丘。
max_probe_size:用于推測(cè)格式的媒體數(shù)據(jù)的最大值。
返回推測(cè)后的得到的AVInputFormat的匹配分?jǐn)?shù)旺韭。
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;
int ret = 0, probe_size, buf_offset = 0;
int score = 0;
int ret2;
if (!max_probe_size)
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 (pb->av_class) {
uint8_t *mime_type_opt = NULL;
char *semi;
av_opt_get(pb, "mime_type", AV_OPT_SEARCH_CHILDREN, &mime_type_opt);
pd.mime_type = (const char *)mime_type_opt;
semi = pd.mime_type ? strchr(pd.mime_type, ';') : NULL;
if (semi) {
*semi = '\0';
}
}
#if 0
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);
}
#endif
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)
goto fail;
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)
goto fail;
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)
ret = AVERROR_INVALIDDATA;
fail:
/* Rewind. Reuse probe buffer to avoid seeking. */
ret2 = ffio_rewind_with_probe_data(pb, &buf, buf_offset);
if (ret >= 0)
ret = ret2;
av_freep(&pd.mime_type);
return ret < 0 ? ret : score;
}
av_probe_input_buffer2()首先需要確定用于推測(cè)格式的媒體數(shù)據(jù)的最大值max_probe_size氛谜。max_probe_size默認(rèn)為PROBE_BUF_MAX(PROBE_BUF_MAX取值為1 << 20,即1048576Byte区端,大約1MB)值漫。
在確定了max_probe_size之后,函數(shù)就會(huì)進(jìn)入到一個(gè)循環(huán)中织盼,調(diào)用avio_read()讀取數(shù)據(jù)并且使用av_probe_input_format2()(該函數(shù)前文已經(jīng)記錄過(guò))推測(cè)文件格式惭嚣。
肯定有人會(huì)奇怪這里為什么要使用一個(gè)循環(huán),而不是只運(yùn)行一次悔政?其實(shí)這個(gè)循環(huán)是一個(gè)逐漸增加輸入媒體數(shù)據(jù)量的過(guò)程晚吞。av_probe_input_buffer2()并不是一次性讀取max_probe_size字節(jié)的媒體數(shù)據(jù),我個(gè)人感覺(jué)可能是因?yàn)檫@樣做不是很經(jīng)濟(jì)谋国,因?yàn)橥茰y(cè)大部分媒體格式根本用不到1MB這么多的媒體數(shù)據(jù)槽地。因此函數(shù)中使用一個(gè)probe_size存儲(chǔ)需要讀取的字節(jié)數(shù),并且隨著循環(huán)次數(shù)的增加逐漸增加這個(gè)值芦瘾。函數(shù)首先從PROBE_BUF_MIN(取值為2048)個(gè)字節(jié)開(kāi)始讀取捌蚊,如果通過(guò)這些數(shù)據(jù)已經(jīng)可以推測(cè)出AVInputFormat,那么就可以直接退出循環(huán)了(參考for循環(huán)的判斷條件“!*fmt”)近弟;如果沒(méi)有推測(cè)出來(lái)缅糟,就增加probe_size的量為過(guò)去的2倍(參考for循環(huán)的表達(dá)式“probe_size << 1”),繼續(xù)推測(cè)AVInputFormat祷愉;如果一直讀取到max_probe_size字節(jié)的數(shù)據(jù)依然沒(méi)能確定AVInputFormat窗宦,則會(huì)退出循環(huán)并且返回錯(cuò)誤信息赦颇。
這個(gè)函數(shù)的用法是從內(nèi)存中讀取數(shù)據(jù)并偵測(cè)數(shù)據(jù)的格式,例如Winpcap抓取網(wǎng)絡(luò)上的RTP包赴涵,打算直接送給ffmpeg進(jìn)行解碼媒怯。一直沒(méi)能找到合適的方法。因?yàn)樽ト〉臄?shù)據(jù)包是存在內(nèi)存中的髓窜,所以無(wú)法傳遞給avformat_open_input()函數(shù)其路徑(根本沒(méi)有路徑= =)扇苞。當(dāng)然也可以將抓取的數(shù)據(jù)報(bào)存成文件,然后用ffmpeg打開(kāi)這個(gè)文件寄纵,但是這樣的話鳖敷,程序的就太難控制了。
后來(lái)經(jīng)過(guò)分析ffmpeg的源代碼程拭,發(fā)現(xiàn)其竟然是可以從內(nèi)存中讀取數(shù)據(jù)的定踱,代碼很簡(jiǎn)單,如下所示:
FILE *fp_open;
//讀取數(shù)據(jù)的回調(diào)函數(shù)-------------------------
//AVIOContext使用的回調(diào)函數(shù)哺壶!
//注意:返回值是讀取的字節(jié)數(shù)
//手動(dòng)初始化AVIOContext只需要兩個(gè)東西:內(nèi)容來(lái)源的buffer屋吨,和讀取這個(gè)Buffer到FFmpeg中的函數(shù)
//回調(diào)函數(shù)蜒谤,功能就是:把buf_size字節(jié)數(shù)據(jù)送入buf即可
//第一個(gè)參數(shù)(void *opaque)一般情況下可以不用
int fill_iobuffer(void * opaque,uint8_t *buf, int bufsize){
if(!feof(fp_open)){
int true_size=fread(buf,1,buf_size,fp_open);
return true_size;
}else{
return -1;
}
}
int main(){
...
fp_open=fopen("test.h264","rb+");
AVFormatContext *ic = NULL;
ic = avformat_alloc_context();
unsigned char * iobuffer=(unsigned char *)av_malloc(32768);
AVIOContext *avio =avio_alloc_context(iobuffer, 32768,0,NULL,fill_iobuffer,NULL,NULL);
ic->pb=avio;
err = avformat_open_input(&ic, "nothing", NULL, NULL);
...//解碼
}
關(guān)鍵要在avformat_open_input()之前初始化一個(gè)AVIOContext山宾,而且將原本的AVFormatContext的指針pb(AVIOContext類型)指向這個(gè)自行初始化AVIOContext。當(dāng)自行指定了AVIOContext之后鳍徽,avformat_open_input()里面的URL參數(shù)就不起作用了资锰。示例代碼開(kāi)辟了一塊空間iobuffer作為AVIOContext的緩存。
fill_iobuffer則是將數(shù)據(jù)讀取至iobuffer的回調(diào)函數(shù)阶祭。fill_iobuffer()形式(參數(shù)绷杜,返回值)是固定的,是一個(gè)回調(diào)函數(shù)濒募,如下所示(只是個(gè)例子鞭盟,具體怎么讀取數(shù)據(jù)可以自行設(shè)計(jì))。示例中回調(diào)函數(shù)將文件中的內(nèi)容通過(guò)fread()讀入內(nèi)存瑰剃。