主頁 >  其他 > FFmpeg學習 avformat_open_input()函式分析

FFmpeg學習 avformat_open_input()函式分析

2021-02-05 10:18:21 其他

前言

本文分析avformat_open_input函式,該函式在libavformat包下,

/**
 * Open an input stream and read the header. 
 * The codecs are not opened.
 * The stream must be closed with avformat_close_input().
 *
 * @param ps Pointer to user-supplied AVFormatContext.
 * @param options  A dictionary filled with AVFormatContext and demuxer-private options.
 * 
 * @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.
 *
 * @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, 
						ff_const59 AVInputFormat *fmt, AVDictionary **options);												
  • 通讀avformat_open_input,主要功能是:
    1. 根據傳入的url確定了要使用的協議URLProtocol,比如http的或是file型別的協議;
    2. 然后按該協議打開檔案或建立連接,回圈從2048byte大小2的冪次遞增開始讀取資料,
    3. 然后再遍歷所有的AVInputFormat,確定iformat,即確定是個什么格式的資料,flv啊還是mp4啥的;
    4. 呼叫AVInputFormat#read_header讀取資料
檔案格式AVInputformat結構體檔案
flv格式ff_flv_demuxerflvdec.c
mp4格式ff_mov_demuxermov.c
mp3格式ff_mp3_demuxermp3dec.c

avformat_open_input函式流程圖如下:
在這里插入圖片描述

下面是各個分析各個函式,

utils.c#avformat_open_input

  • 主要作業:
    Open an input stream and read the header. The codecs are not opened. The stream must be closed with avformat_close_input().
    return 0 on success, a negative AVERROR on failure.
    If you want to use custom IO, preallocate the format context and set its pb field.
    打開流并根據格式資訊讀取資料頭創建相應的AVStream,但不打開解碼器,回傳0表示成功,
int avformat_open_input(AVFormatContext **ps, const char *filename, AVInputFormat *fmt, AVDictionary **options)
{
    AVFormatContext *s = *ps;
    int i, ret = 0;
    AVDictionary *tmp = NULL;

    // 沒賦值就賦值
    if (!s && !(s = avformat_alloc_context()))
        return AVERROR(ENOMEM);

    // 有format就設定
    if (fmt)
        s->iformat = fmt;

   // 在堆上分配,賦值s->url
    if (!(s->url = av_strdup(filename ? filename : ""))) {
        ret = AVERROR(ENOMEM);
        goto fail;
    }

    // init_input找到檔案格式format
    if ((ret = init_input(s, filename, &tmp)) < 0)
        goto fail;
    s->probe_score = ret;

    s->duration = s->start_time = AV_NOPTS_VALUE;

    // 讀取多媒體資料檔案頭,根據音視頻頻流創建相應的AVStream,
    if (!(s->flags&AVFMT_FLAG_PRIV_OPT) && s->iformat->read_header)
        if ((ret = s->iformat->read_header(s)) < 0)
            goto fail;

    for (i = 0; i < s->nb_streams; i++)
        s->streams[i]->internal->orig_codec_id = s->streams[i]->codecpar->codec_id;

    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);
    avformat_free_context(s);
    *ps = NULL;
    return ret;
}

utils.c#init_input

  • Open input file and probe the format if necessary.
    如果沒有指定iformat的話,則打開檔案并探測格式,

  • 主要作業:
    呼叫其它函式,找到iformat;

    1. 先根據filename探測一波iformat;
    2. 沒找到的話打開檔案讀取資料進行探測;
    3. 還沒找到的話再探測一波;
  • 在任意一步找到iformat即回傳給init_input,init_input會呼叫iformat的read_head創建相應的AVStream,

#define AVPROBE_SCORE_MAX       100
#define AVPROBE_SCORE_RETRY (AVPROBE_SCORE_MAX/4)

static int init_input(AVFormatContext *s, const char *filename,
                      AVDictionary **options)
{
    int ret;
    // 只將filename傳入
    AVProbeData pd = { filename, NULL, 0 }; 

    // 初始化25分的得分
    int score = AVPROBE_SCORE_RETRY;

    /* 
     * 自定義了IO的話
     *    如果沒指定iformat,就呼叫av_probe_input_buffer2推測iformat
     *    如果指定了iformat,直接回傳
     */    
    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;
    }

    /*
     * 1. 如果指定了iformat,則直接回傳
     *    如果沒指定iformat,則通過av_probe_input_format2來推測iformat,推測成功則回傳
     */
    if ((s->iformat && s->iformat->flags & AVFMT_NOFILE) ||
        (!s->iformat && (s->iformat = av_probe_input_format2(&pd, 0, &score))))
        return score;

    /*
     * 2. 第一步失敗,則通過io_open打開檔案并探測格式,找到了iformat就回傳成功
     */
    if ((ret = s->io_open(s, &s->pb, filename, AVIO_FLAG_READ | s->avio_flags, options)) < 0)
        return ret;

    if (s->iformat)
        return 0;
    
    /*
     * 3. 第二步沒找到iformat的話,再呼叫av_probe_input_buffer2推測
     */
    return av_probe_input_buffer2(s->pb, &s->iformat, filename,
                                 s, 0, s->format_probesize);
}

第一步:format.c#av_probe_input_format2

Guess the file format.
推測檔案格式;只有推測出來的得分值 > score_max,才將該format回傳,

  • 主要作業:
    呼叫av_probe_input_format3找到format,大于score_max則探測成功,否則探測失敗,

  • init_input呼叫中
    score_max為初始值25,is_opened為false,pd該結構體只有filename,
    因為該檔案沒有被打開過,所以只有filename這一個有效資訊,

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;

ff_const59 AVInputFormat *av_probe_input_format2(ff_const59 AVProbeData *pd, int is_opened, int *score_max)
{
    int score_ret;
    ff_const59 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;
}

format.c#av_probe_input_format3

Guess the file format,推薦iformat,是av_probe_input_format2的具體實作,
引數score_ret是推測的值,是要和score_max對比的,

  • 主要作業:
    • 第一次進來時,檔案沒有打開,pd.buf是NULL,賦值為zerobuffer,空轉?
    • 第二遍io_open填充buf后,走到這里遍歷所有格式,根據read_probe、擴展名、mime_type進行探測,如果有得分值 > 傳入的score_max的話,則找到了該format; 如果沒大于,則回傳null,
#define AVPROBE_SCORE_EXTENSION  50 ///< score for file extension
#define AVPROBE_SCORE_MIME       75 ///< score for file mime type
#define AVPROBE_SCORE_MAX       100 ///< maximum score

ff_const59 AVInputFormat *av_probe_input_format3(ff_const59 AVProbeData *pd, int is_opened, int *score_ret)
{
    AVProbeData lpd = *pd;
    const AVInputFormat *fmt1 = NULL;
    ff_const59 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) {
        // 初始化bug,32位元組,內容全是0,區域靜態呀
        lpd.buf = (unsigned char *) zerobuffer;
    }

    // 判斷是否是ID3v2格式,ID3是一種metadata容器,多應用于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;
    }

    /* 
     * demuxer_list是編譯出來的;
     * 遍歷所有格式,根據read_probe、擴展名、mime_type進行探測,如果有得分值 > 傳入的score_max的話,則找到了該format; 如果沒大于,則回傳null,
     * 
     * 1. 如果有read_probe方法,呼叫read_probe方法進行判斷,回傳該format的得分,如果檔案擴展名是該格式的話,再進行switch判斷;
     *    沒有read_probe方法,擴展名匹配的話則得分是AVPROBE_SCORE_EXTENSION 50
     * 2. mime_type匹配的話,則得分max(AVPROBE_SCORE_MIME75, score)
     * 3. 比較score_max得分值,超過則找到,沒超過則回傳null,
     */
    while ((fmt1 = av_demuxer_iterate(&i))) {
        // 如果一個檔案格式如果是AVFMT_NOFILE,對應init_input中的第一次探測的時候才能向下走,第二次檔案打開了則continue;
        if (!is_opened == !(fmt1->flags & AVFMT_NOFILE) && strcmp(fmt1->name, "image2"))
            continue;
        score = 0;
        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) {
            if (av_match_ext(lpd.filename, fmt1->extensions))
                score = AVPROBE_SCORE_EXTENSION;
        }
        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;
}

read_probe

遍歷demuxer_list,呼叫該格式的read_probe,
只有在打開檔案后呼叫這才有意義,即調過io_open后才給pb.buf賦了值,否則在空轉,

檔案格式對應的format結構體檔案
flv格式ff_flv_demuxerflvdec.c
mp4格式ff_mov_demuxermov.c
mp3格式ff_mp3_demuxermp3dec.c

可以參考檔案格式進行分析,

第二步:s->io_open,options.c#io_open_default

通過av_probe_input_format3遍歷所有格式依然沒有找到iformat時,則打開該檔案讀取資料再次進行探測,
AVFormatContext->io_open函式指標,賦值是在初始化時options.c#avformat_alloc_context中,

  • 主要作業:
    根據filename確定要使用的協議,然后初始化URLContext;
    url_open2/url_open打開檔案或建立連接,準備讀取資料;
    然后初始化AVIOContext;
// options.c
static int io_open_default(AVFormatContext *s, AVIOContext **pb,  const char *url, int flags, AVDictionary **options)
{
    av_log(s, loglevel, "Opening \'%s\' for %s\n", url, flags & AVIO_FLAG_WRITE ? "writing" : "reading");
    return ffio_open_whitelist(pb, url, flags, &s->interrupt_callback, options, s->protocol_whitelist, s->protocol_blacklist);
}

// aviobuf.c
int ffio_open_whitelist(AVIOContext **s, const char *filename, int flags, const AVIOInterruptCB *int_cb, AVDictionary **options, const char *whitelist, const char *blacklist ) {

    URLContext *h;
    *s = NULL;

    // 初始化URLContext
    ffurl_open_whitelist(&h, filename, flags, int_cb, options, whitelist, blacklist, NULL);
 
    // 初始化AVIOContext
    ffio_fdopen(s, h);
    return 0;
}

avio.c#ffio_open_whitelist

  • 主要作業:
    確定該輸入檔案是什么協議的,并初始化URLContext;
    然后按該協議打開該檔案;
int ffurl_open_whitelist(URLContext **puc, const char *filename, int flags, const AVIOInterruptCB *int_cb, AVDictionary **options, const char *whitelist, const char* blacklist, URLContext *parent) {
    ffurl_alloc(puc, filename, flags, int_cb);
    ffurl_connect(*puc, options);
    return 0;
}

int ffurl_alloc(URLContext **puc, const char *filename, int flags, const AVIOInterruptCB *int_cb) {
    const URLProtocol *p = NULL;
    p = url_find_protocol(filename);
    if (p)
       return url_alloc_for_protocol(puc, p, filename, flags, int_cb);
}

// 根據相應的協議,呼叫協議的open函式,創建連接或者打開檔案
int ffurl_connect(URLContext *uc, AVDictionary **options) {
    int err =
        uc->prot->url_open2 ? uc->prot->url_open2(uc, uc->filename, uc->flags, options) :
        uc->prot->url_open(uc, uc->filename, uc->flags);

    if (err)
        return err;
    uc->is_connected = 1;

    // 無法seek則設定is_streamed為true,表示no seek possible
    if ((uc->flags & AVIO_FLAG_WRITE) || !strcmp(uc->prot->name, "file"))
        if (!uc->is_streamed && ffurl_seek(uc, 0, SEEK_SET) < 0)
            uc->is_streamed = 1;

    return 0;
}

avio.c#url_find_protocol

  • 主要作業:
    根據傳入的filename,遍歷所有協議,確定傳入的檔案是什么協議的,
static const struct URLProtocol *url_find_protocol(const char *filename)
{
    const URLProtocol **protocols;
    char proto_str[128], proto_nested[128], *ptr;

    // 查找第一個非字母數字的字符下標
    size_t proto_len = strspn(filename, URL_SCHEME_CHARS);
    

    if (filename[proto_len] != ':' &&
        (strncmp(filename, "subfile,", 8) || !strchr(filename + proto_len + 1, ':')) ||
        is_dos_path(filename)){
        /* 
         * 檔案路徑,比如/storage/emulated/0/Android/data/com.baiiu.example/cache/test.mp4
         * 則proto_str是file
         */
        strcpy(proto_str, "file");
    } else {
        /* 
         * http://,rtmp:// 這樣的,則proto_str是http、rtmp
         */
        av_strlcpy(proto_str, filename,
                   FFMIN(proto_len + 1, sizeof(proto_str)));
    }

    // 把proto_str賦值給proto_nested,嵌套的如hls+http:// m3u8格式的
    av_strlcpy(proto_nested, proto_str, sizeof(proto_nested));
    if ((ptr = strchr(proto_nested, '+')))
        *ptr = '\0';

    // 獲取所有支持的protocols
    protocols = ffurl_get_protocols(NULL, NULL);
    if (!protocols)
        return NULL;
    
    int i;
    for (i = 0; protocols[i]; i++) {
        const URLProtocol *up = protocols[i];
        // 協議相同
        if (!strcmp(proto_str, up->name)) {
            av_freep(&protocols);
            return up;
        }

        // nested協議相同
        if (up->flags & URL_PROTOCOL_FLAG_NESTED_SCHEME &&
            !strcmp(proto_nested, up->name)) {
            av_freep(&protocols);
            return up;
        }
    }
    av_freep(&protocols);
    if (av_strstart(filename, "https:", NULL) || av_strstart(filename, "tls:", NULL))
        av_log(NULL, AV_LOG_WARNING, "https protocol not found, recompile FFmpeg with "
                                     "openssl, gnutls or securetransport enabled.\n");

    return NULL;
}

protocols.c#ffurl_get_protocols

  • 主要作業:
    獲取所有協議,回傳進行遍歷,找到filename對應的協議;
// 差不多這些協議吧,通過extern定義,是在別的檔案內定義的
extern const URLProtocol ff_file_protocol;
extern const URLProtocol ff_ftp_protocol;
extern const URLProtocol ff_hls_protocol;
extern const URLProtocol ff_http_protocol;
extern const URLProtocol ff_httpproxy_protocol;
extern const URLProtocol ff_https_protocol;
extern const URLProtocol ff_rtp_protocol;
extern const URLProtocol ff_tcp_protocol;
extern const URLProtocol ff_tls_protocol;
extern const URLProtocol ff_udp_protocol;
extern const URLProtocol ff_udplite_protocol;
extern const URLProtocol ff_librtmp_protocol;
extern const URLProtocol ff_librtmpe_protocol;
extern const URLProtocol ff_librtmps_protocol;
extern const URLProtocol ff_librtmpt_protocol;
extern const URLProtocol ff_librtmpte_protocol;

const URLProtocol **ffurl_get_protocols(const char *whitelist, const char *blacklist) {
    const URLProtocol **ret;
    int i, ret_idx = 0;

    ret = av_mallocz_array(FF_ARRAY_ELEMS(url_protocols), sizeof(*ret));
    if (!ret)
        return NULL;

    for (i = 0; url_protocols[i]; i++) {
        const URLProtocol *up = url_protocols[i];

        // 黑白名單過濾...

        ret[ret_idx++] = up;
    }

    return ret;
}

aviobuf.c#ffio_fdopen

初始化AVIOContext,
retry_transfer_wrapper是對ffurl_read、ffurl_write、ffurl_seek方法的封裝,讀取size長度的流資料,

int ffio_fdopen(AVIOContext **s, URLContext *h) {
    *s = avio_alloc_context(buffer, buffer_size, h->flags & AVIO_FLAG_WRITE, h,
                            (int (*)(void *, uint8_t *, int))  ffurl_read,
                            (int (*)(void *, uint8_t *, int))  ffurl_write,
                            (int64_t (*)(void *, int64_t, int))ffurl_seek);

    return 0;
}


// avio.c
int ffurl_read(URLContext *h, unsigned char *buf, int size)
{
    if (!(h->flags & AVIO_FLAG_READ))
        return AVERROR(EIO);
    return retry_transfer_wrapper(h, buf, size, 1, h->prot->url_read);
}

int ffurl_read_complete(URLContext *h, unsigned char *buf, int size)
{
    if (!(h->flags & AVIO_FLAG_READ))
        return AVERROR(EIO);
    return retry_transfer_wrapper(h, buf, size, size, h->prot->url_read);
}

int ffurl_write(URLContext *h, const unsigned char *buf, int size)
{
    if (!(h->flags & AVIO_FLAG_WRITE))
        return AVERROR(EIO);
    /* avoid sending too big packets */
    if (h->max_packet_size && size > h->max_packet_size)
        return AVERROR(EIO);

    return retry_transfer_wrapper(h, (unsigned char *)buf, size, size,
                                  (int (*)(struct URLContext *, uint8_t *, int))
                                  h->prot->url_write);
}

static inline int retry_transfer_wrapper(URLContext *h, uint8_t *buf,
                                         int size, int size_min,
                                         int (*transfer_func)(URLContext *h,
                                                              uint8_t *buf,
                                                              int size))
{
    int ret, len;
    int fast_retries = 5;
    int64_t wait_since = 0;

    len = 0;
    while (len < size_min) {
        if (ff_check_interrupt(&h->interrupt_callback))
            return AVERROR_EXIT;
        ret = transfer_func(h, buf + len, size - len);

        if (ret == AVERROR(EINTR))
            continue;

        if (h->flags & AVIO_FLAG_NONBLOCK)
            return ret;

        if (ret == AVERROR(EAGAIN)) {
            ret = 0;
            if (fast_retries) {
                fast_retries--;
            } else {
                if (h->rw_timeout) {
                    if (!wait_since)
                        wait_since = av_gettime_relative();
                    else if (av_gettime_relative() > wait_since + h->rw_timeout)
                        return AVERROR(EIO);
                }
                av_usleep(1000);
            }
        } else if (ret == AVERROR_EOF)
            return (len > 0) ? len : AVERROR_EOF;
        else if (ret < 0)
            return ret;
        if (ret) {
            fast_retries = FFMAX(fast_retries, 2);
            wait_since = 0;
        }
        len += ret;
    }
    return len;
}

第三步:utils.c#av_probe_input_buffer2

  • 主要作業:
    通過io_open打開檔案后,回圈按該協議進行讀取資料,從最小2048byte開始讀取,不斷擴充buf,直到探測到格式或達到1M為止,

  • 填充pb.buf后,呼叫av_probe_input_format2繼續探測iformat,此時buf的資料已經有值,便可以按照各個Inputformat確定score,

int av_probe_input_buffer2(AVIOContext *pb, ff_const59 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;

    // 默認的PROBE_BUF_MAX,1<<20 byte,1M
    if (!max_probe_size) {
        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);

    /*
     * 從PROBE_BUF_MIN 2048 byte開始,逐漸 *2擴大到max_probe_size,最大到1M+1 byte,
     * 直到探測出iformat為止、或者直到最大也沒探測出來,即探測失敗;
     */
    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.
         * 1. 重新分配buf大小,當前的probe_size + AVPROBE_PADDING_SIZE
         * 2. 根據io_open確定的protocol協議進行讀取資料
         *      用offset來控制讀了多少,從哪開始讀,不用重新讀取
         */
        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) {
            if (ret != AVERROR_EOF)
                goto fail;

            // 重置
            score = 0;
            ret   = 0;          /* error was end of file, nothing read */
        }
        buf_offset += ret; // 增加offset
        if (buf_offset < offset)
            continue;

        pd.buf_size = buf_offset - offset;
        pd.buf = &buf[offset]; // 賦值pb.buf

        memset(pd.buf + pd.buf_size, 0, AVPROBE_PADDING_SIZE);

        /*
         * Guess file format. 再次探測格式,is_opened為1,檔案已經打開了
         */
        *fmt = av_probe_input_format2(&pd, 1, &score);
    }

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

aviobuf.c#avio_read

  • 主要作業
    avio_read通過AVIOContext#read_packet讀取資料,回圈讀取到size大小后回傳,

  • AVIOContext#read_packet,是在ffio_fdopen函式中對AVIOContext進行初始化,
    實際呼叫在avio.c#ffurl_read
    ffurl_read方法實際上在呼叫相應prototol的url_read,

  • 參考上面ffurl_read解釋,最侄訓調到protocol里的url_read呼叫相應的protocol進行資料讀取,

// aviobuf.c
int avio_read(AVIOContext *s, unsigned char *buf, int size)
{
    int len, size1;

    size1 = size;
    while (size > 0) {
        len = FFMIN(s->buf_end - s->buf_ptr, size);
        if (len == 0 || s->write_flag) {
            if((s->direct || size > s->buffer_size) && !s->update_checksum) {
                len = read_packet_wrapper(s, buf, size);
                if (len == AVERROR_EOF) {
                    s->eof_reached = 1;
                    break;
                } else if (len < 0) {
                    s->eof_reached = 1;
                    s->error= len;
                    break;
                } else {
                    s->pos += len;
                    s->bytes_read += len;
                    size -= len;
                    buf += len;
                    // reset the buffer
                    s->buf_ptr = s->buffer;
                    s->buf_end = s->buffer/* + len*/;
                }
            } else {
                fill_buffer(s);
                len = s->buf_end - s->buf_ptr;
                if (len == 0)
                    break;
            }
        } else {
            memcpy(buf, s->buf_ptr, len);
            buf += len;
            s->buf_ptr += len;
            size -= len;
        }
    }
    if (size1 == size) {
        if (s->error)      return s->error;
        if (avio_feof(s))  return AVERROR_EOF;
    }
    return size1 - size;
}

static int read_packet_wrapper(AVIOContext *s, uint8_t *buf, int size) {
    return s->read_packet(s->opaque, buf, size);
}

參考雷神博客:
圖解FFMPEG打開媒體的函式avformat_open_input
FFmpeg 源代碼簡單分析:avformat_open_input()
FFmpeg源代碼簡單分析:avio_open2()

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/256705.html

標籤:其他

上一篇:D語言中的Cortex-M4系列中斷向量表處理

下一篇:verilog matlab fir 濾波器設計

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more