ffmpeg源码分析之三avformat_open_input()上

URLProtocol ff_file_protocol = {
    .name                = "file",
    .url_open            = file_open,
    .url_read            = file_read,
    .url_write           = file_write,
    .url_seek            = file_seek,
    .url_close           = file_close,
    .url_get_file_handle = file_get_handle,
    .url_check           = file_check,
};


    经过第一讲之后 大家应该知道ffmpeg将所有编解码用到的东西都做成了一个个的链表 通过一个个全局的链表头指针来访问该链表 以供在涉及到具体的编解码器时用。

这一章就分析ffmpeg如何打开的视频文件。

  首先 提出来三个具体的结构,ffmpeg通过封装他们用于打开文件的相关操作 ,分别是URLProtocol URLContext与AVIOContext.

 1.URLProtocol该结构定义在libavformat/url.h文件中 结构如下:

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);
    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 priv_data_size;
    const AVClass *priv_data_class;
    int flags;
    int (*url_check)(URLContext *h, int mask);
} URLProtocol;

大家可以看到 在URLProtocol中 无非就是定义一些函数指针 呵呵 联想到第二讲中的使用C模拟C++的多态性质 不难想到 该结构肯定会用各个类型的具体函数来填充  后面会分析
2.URLContext结构定义在libavformat/url.h中 结构如下

typedef struct URLContext {
    const AVClass *av_class;    /**< information for av_log(). Set by url_open(). */
    struct URLProtocol *prot;
    void *priv_data;
    char *filename;             /**< specified URL */
    int flags;
    int max_packet_size;        /**< if non zero, the stream is packetized with this max packet size */
    int is_streamed;            /**< true if streamed (no seek possible), default = false */
    int is_connected;
    AVIOInterruptCB interrupt_callback;
} URLContext;

3.AVIOContext定义在libavformat/avio.h中 代码如下

typedef struct {
#if !FF_API_OLD_AVIO
    AVClass *av_class;
#endif
    unsigned char *buffer;  /**< Start of the buffer. */
    int buffer_size;        /**< Maximum buffer size */
    unsigned char *buf_ptr; /**< Current position in the buffer */
    unsigned char *buf_end; /**< End of the data, may be less than
                                 buffer+buffer_size if the read function returned
                                 less data than requested, e.g. for streams where
                                 no more data has been received yet. */
    void *opaque;           /**< A private pointer, passed to the read/write/seek/...
                                 functions. */
    int (*read_packet)(void *opaque, uint8_t *buf, int buf_size);
    int (*write_packet)(void *opaque, uint8_t *buf, int buf_size);
    int64_t (*seek)(void *opaque, int64_t offset, int whence);
    int64_t pos;            /**< position in the file of the current buffer */
    int must_flush;         /**< true if the next seek should flush */
    int eof_reached;        /**< true if eof reached */
    int write_flag;         /**< true if open for writing */
#if FF_API_OLD_AVIO
    attribute_deprecated int is_streamed;
#endif
    int max_packet_size;
    unsigned long checksum;
    unsigned char *checksum_ptr;
    unsigned long (*update_checksum)(unsigned long checksum, const uint8_t *buf, unsigned int size);
    int error;              /**< contains the error code or 0 if no error happened */
    int (*read_pause)(void *opaque, int pause);
    int64_t (*read_seek)(void *opaque, int stream_index,
                         int64_t timestamp, int flags);
    int seekable;

     int64_t maxsize;
} AVIOContext;

好了 这三个重要的结构代码我已经贴出来了  下面进入正题 分析avformat_open_input的流程

1.该函数定义在libavformat/utils.c中,代码如下:

//继av_register_all()后 第二步就是调用该函数 主要就是打开一个多媒体文件 获取到AVFormatContext的对象
int avformat_open_input(AVFormatContext **ps, const char *filename, AVInputFormat *fmt, AVDictionary **options)
{
    AVFormatContext *s = *ps;
    int ret = 0;
    AVFormatParameters ap = { { 0 } };
    AVDictionary *tmp = NULL;
	//avformat_alloc_context()位于libavformat/options.c中 主要是为AVFormatContext分配空间
    if (!s && !(s = avformat_alloc_context()))
        return AVERROR(ENOMEM);
    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;
//........下次分析下

其中关键在init_input()

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

    if (s->pb) {
        s->flags |= AVFMT_FLAG_CUSTOM_IO;
        if (!s->iformat)
            return av_probe_input_buffer(s->pb, &s->iformat, filename, s, 0, 0);
        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_format(&pd, 0))))
        return 0;

	//avio_open2定义在libavformat/aviobuf.c中 但是在avio.h中声明
	//Create and initialize a AVIOContext for accessing the resource indicated by url
    if ((ret = avio_open2(&s->pb, filename, AVIO_FLAG_READ,
                          &s->interrupt_callback, options)) < 0)
        return ret;
    if (s->iformat)
        return 0;
    return av_probe_input_buffer(s->pb, &s->iformat, filename, s, 0, 0);
}

这里调用了avio_open2()定义在libavformat/aviobuf.c中

int avio_open2(AVIOContext **s, const char *filename, int flags,
               const AVIOInterruptCB *int_cb, AVDictionary **options)
{
    URLContext *h;
    int err;
	//定义在libavformat/avio.c
    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;
}

ffurl_open()定义在libavformat/avio.c中

int ffurl_open(URLContext **puc, const char *filename, int flags,
               const AVIOInterruptCB *int_cb, AVDictionary **options)
{
    int ret = ffurl_alloc(puc, filename, flags, int_cb);
    if (ret)
        return ret;
    if (options && (*puc)->prot->priv_data_class &&
        (ret = av_opt_set_dict((*puc)->priv_data, options)) < 0)
        goto fail;
    ret = ffurl_connect(*puc, options);
    if (!ret)
        return 0;
fail:
    ffurl_close(*puc);
    *puc = NULL;
    return ret;
}

这里面调用了两个重要的函数 一个是ffurl_alloc 请看

int ffurl_alloc(URLContext **puc, const char *filename, int flags,
                const AVIOInterruptCB *int_cb)
{
    URLProtocol *up = NULL;
    char proto_str[128], proto_nested[128], *ptr;
    size_t proto_len = strspn(filename, URL_SCHEME_CHARS);

    if (!first_protocol) {
        av_log(NULL, AV_LOG_WARNING, "No URL Protocols are registered. "
                                     "Missing call to av_register_all()?\n");
    }

    if (filename[proto_len] != ':' &&  filename[proto_len] != ',' || is_dos_path(filename))
        strcpy(proto_str, "file");
    else
        av_strlcpy(proto_str, filename, FFMIN(proto_len+1, sizeof(proto_str)));

    if ((ptr = strchr(proto_str, ',')))
        *ptr = '\0';
    av_strlcpy(proto_nested, proto_str, sizeof(proto_nested));
    if ((ptr = strchr(proto_nested, '+')))
        *ptr = '\0';

    while (up = ffurl_protocol_next(up)) {
        if (!strcmp(proto_str, up->name))
            return url_alloc_for_protocol (puc, up, filename, flags, int_cb);
        if (up->flags & URL_PROTOCOL_FLAG_NESTED_SCHEME &&
            !strcmp(proto_nested, up->name))
            return url_alloc_for_protocol (puc, up, filename, flags, int_cb);
    }
    *puc = NULL;
    return AVERROR(ENOENT);
}

哈哈 在ffurl_alloc中就要寻找之前在av_register_all()中串起来的链表的一个节点了 这里的依据是根据传入的filename的类型来判断是本地文件还是来自于网络流 我用的是本地文件 故其找到的是定义在libavformat/file.c中的一个urlprotocol结构


URLProtocol ff_file_protocol = {
    .name                = "file",
    .url_open            = file_open,
    .url_read            = file_read,
    .url_write           = file_write,
    .url_seek            = file_seek,
    .url_close           = file_close,
    .url_get_file_handle = file_get_handle,
    .url_check           = file_check,
};

再看ffurl_connect

int ffurl_connect(URLContext* uc, AVDictionary **options)
{
    int err =
#if !FF_API_OLD_AVIO
        uc->prot->url_open2 ? uc->prot->url_open2(uc, uc->filename, uc->flags, options) :
#endif
        uc->prot->url_open(uc, uc->filename, uc->flags);
    if (err)
        return err;
    uc->is_connected = 1;
    //We must be careful here as ffurl_seek() could be slow, for example for http
    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;
}

在这里调用了URLProtocol中的url_open 而url_open指针指向了file.c中的file_open()函数  成功的指向了本地文件的操作 。ee

简要总结一下 URLContext像一个容器 封装了打开文件或者网络流的一些公共属性 URLProtocol是一个多态的基类 封装了一些操作 AVIOContext更像是一些私有的数据

这些会在下文中继续分析

   另外,FFMPEG中有很多这样的三元组 掌握他们会对你理解ffmpeg有很大的帮助 同时也能对你在面向过程的设计当中增加一个设计多态的一个工具

 

 

累了   明天继续。。。。。。

 


 

 

 


 

 


 

 

 

 


 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(ffmpeg源码分析之三avformat_open_input()上)