ffmpeg源碼分析3-init_input

本文參考雷神博文整理: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))。

1425470637_9894.jpg

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)存瑰剃。



最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末齿诉,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子晌姚,更是在濱河造成了極大的恐慌粤剧,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件挥唠,死亡現(xiàn)場(chǎng)離奇詭異抵恋,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)宝磨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門弧关,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)盅安,“玉大人,你說(shuō)我怎么就攤上這事梯醒】矶眩” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵茸习,是天一觀的道長(zhǎng)畜隶。 經(jīng)常有香客問(wèn)我,道長(zhǎng)号胚,這世上最難降的妖魔是什么籽慢? 我笑而不...
    開(kāi)封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮猫胁,結(jié)果婚禮上箱亿,老公的妹妹穿的比我還像新娘。我一直安慰自己弃秆,他們只是感情好届惋,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著菠赚,像睡著了一般脑豹。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上衡查,一...
    開(kāi)封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天瘩欺,我揣著相機(jī)與錄音,去河邊找鬼拌牲。 笑死俱饿,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的塌忽。 我是一名探鬼主播拍埠,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼土居!你這毒婦竟也來(lái)了枣购?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤装盯,失蹤者是張志新(化名)和其女友劉穎坷虑,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體埂奈,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡迄损,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了账磺。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片芹敌。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡痊远,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出氏捞,到底是詐尸還是另有隱情碧聪,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布液茎,位于F島的核電站逞姿,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏捆等。R本人自食惡果不足惜滞造,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望栋烤。 院中可真熱鬧谒养,春花似錦、人聲如沸明郭。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)薯定。三九已至始绍,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間沉唠,已是汗流浹背疆虚。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工苛败, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留满葛,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓罢屈,卻偏偏與公主長(zhǎng)得像嘀韧,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子缠捌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354