avio_open2()

本文简单分析FFmpeg中一个常用的函数avio_open2()。该函数用于打开FFmpeg的输入输出文件。avio_open2()的声明位于libavformat\avio.h文件中,如下所示。/** * Create and initialize a AVIOContext for accessing the * resource indicated by url. * @note When the resource indicated by url has been opened in * read+write mode, the AVIOContext can be used only for writing. * * @param s Used to return the pointer to the created AVIOContext. * In case of failure the pointed to value is set to NULL. * @param url resource to access * @param flags flags which control how the resource indicated by url * is to be opened * @param int_cb an interrupt callback to be used at the protocols level * @param options A dictionary filled with protocol-private options. On return * this parameter will be destroyed and replaced with a dict containing options * that were not found. May be NULL. * @return >= 0 in case of success, a negative value corresponding to an * AVERROR code in case of failure */int avio_open2(AVIOContext **s, const char *url, int flags,const AVIOInterruptCB *int_cb, AVDictionary **options);avio_open2()函数参数的含义如下:s:函数调用成功之后创建的AVIOContext结构体。url:输入输出协议的地址(文件也是一种“广义”的协议,对于文件来说就是文件的路径)。flags:打开地址的方式。可以选择只读,只写,或者读写。取值如下。AVIO_FLAG_READ:只读。AVIO_FLAG_WRITE:只写。AVIO_FLAG_READ_WRITE:读写。int_cb:目前还没有用过。

options:目前还没有用过。

该函数最典型的例子可以参考:最简单的基于FFMPEG的视频编码器(YUV编码为H.264)

函数调用结构图首先贴出来最终分析得出的函数调用结构图,如下所示。

单击查看更清晰的图片

avio_open()有一个和avio_open2()“长得很像”的函数avio_open(),应该是avio_open2()的早期版本。avio_open()比avio_open2()少了最后2个参数。而它前面几个参数的含义和avio_open2()是一样的。从源代码中可以看出,avio_open()内部调用了avio_open2(),并且把avio_open2()的后2个参数设置成了NULL,因此它的功能实际上和avio_open2()是一样的。avio_open()源代码如下所示。int avio_open(AVIOContext **s, const char *filename, int flags){return avio_open2(s, filename, flags, NULL, NULL);}avio_open2()下面看一下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;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;}

从avio_open2()的源代码可以看出,它主要调用了2个函数:ffurl_open()和ffio_fdopen()。其中ffurl_open()用于初始化URLContext,ffio_fdopen()用于根据URLContext初始化AVIOContext。URLContext中包含的URLProtocol完成了具体的协议读写等工作。AVIOContext则是在URLContext的读写函数外面加上了一层“包装”(通过retry_transfer_wrapper()函数)。

URLProtocol和URLContext在查看ffurl_open()和ffio_fdopen()函数之前,首先查看一下URLContext和URLProtocol的定义。这两个结构体在FFmpeg的早期版本的SDK中是定义在头文件中可以直接使用的。但是近期的FFmpeg的SDK中已经找不到这两个结构体的定义了。FFmpeg把这两个结构体移动到了源代码的内部,变成了内部结构体。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);/*** 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;从URLProtocol的定义可以看出,其中包含了用于协议读写的函数指针。例如:url_open():打开协议。url_read():读数据。url_write():写数据。url_close():关闭协议。每种具体的协议都包含了一个URLProtocol结构体,例如:FILE协议(“文件”在FFmpeg中也被当做一种协议)的结构体ff_file_protocol的定义如下所示(位于libavformat\file.c)。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,.priv_data_size= sizeof(FileContext),.priv_data_class= &file_class,};在使用FILE协议进行读写的时候,调用url_open()实际上就是调用了file_open()函数,这里限于篇幅不再对file_open()的源代码进行分析。file_open()函数实际上调用了系统的打开文件函数open()。同理,调用url_read()实际上就是调用了file_read()函数;file_read()函数实际上调用了系统的读取文件函数read()。url_write(),url_seek()等函数的道理都是一样的。LibRTMP协议的结构体ff_librtmp_protocol的定义如下所示(位于libavformat\librtmp.c)。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,};UDP协议的结构体ff_udp_protocol的定义如下所示(位于libavformat\udp.c)。URLProtocol ff_udp_protocol = {.name= "udp",.url_open= udp_open,.url_read= udp_read,.url_write= udp_write,.url_close= udp_close,.url_get_file_handle = udp_get_file_handle,.priv_data_size= sizeof(UDPContext),.priv_data_class= &udp_context_class,.flags= URL_PROTOCOL_FLAG_NETWORK,};上文中简单介绍了URLProtocol结构体。下面看一下URLContext结构体。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;int64_t rw_timeout;/**< maximum time to wait for (network) read/write operation completion, in mcs */} URLContext;从代码中可以看出,URLProtocol结构体是URLContext结构体的一个成员。由于还没有对URLContext结构体进行详细研究,有关该结构体的代码不再做过多分析。ffurl_open()前文提到AVIOContext中主要调用了2个函数:ffurl_open()和ffio_fdopen()。其中ffurl_open()用于初始化URLContext,ffio_fdopen()用于根据URLContext初始化AVIOContext。下面首先看一下初始化URLContext的函数ffurl_open()。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 < 0)return ret;if (options && (*puc)->prot->priv_data_class &&(ret = av_opt_set_dict((*puc)->priv_data, options)) < 0)goto fail;if ((ret = av_opt_set_dict(*puc, options)) < 0)goto fail;ret = ffurl_connect(*puc, options);if (!ret)return 0;fail:ffurl_close(*puc);*puc = NULL;return ret;}

从代码中可以看出,ffurl_open()主要调用了2个函数:ffurl_alloc()和ffurl_connect()。ffurl_alloc()用于查找合适的URLProtocol,并创建一个URLContext;ffurl_connect()用于打开获得的URLProtocol。

以前我是个爱仰望天空的人,苍蓝的天空总是给我求生的勇气,

avio_open2()

相关文章:

你感兴趣的文章:

标签云: