ffmpeg的IO操作主要在libavformat库中实现,部分实现用到了libavutl中的工具。网上有一些介绍ffmpeg的IO的文章,但是有些比较老了,并且一些现在ffmpeg结构已经一些变化,比如ByteIOContext已经改名为AVIOContext,本文章主要介绍IO基础,以及一些ffmpeg对于一些内存操作的方法。
所谓IO就是数据的存取,主要的途径也就是文件或者网络。数据IO是基于文件格式的,与具体的编码标准无关。
ffmpeg对各种协议实现了封装,使用同样的接口,完成对不同数据的读取。比较屌。
ffmpeg所有的协议的注册是在av_register_all中完成的。协议保存在链表first_protocol中。
URLContext层次的操作主要有:url_open,url_read,url_write,url_seek,url_close等
AVIOContext层次的操作主要有:avio_open,avio_clse,avio_rxxx,avio_wxxx。事实上这是对URLContext层次的上层操作。或者更高层次的抽象封装。
avio_rxxx和avio_wxxx简介的调用ffurl_read,ffurl_write,实现读写操作,而avio_open根据文件名来实现avio_rxxx和avio_wxxx和不用媒介的操作函数ffurl_read,ffurl_write的绑定。
avio_rxxx和avio_wxxx实现的是对内存缓冲区中数据的操作,当缓冲区中数据不足或者缓冲区将要溢出时,调用flush_buffer和fill_buffer将数据把数据读到媒介中。
URLProtocol:用于各种数据传输协议,结构中定义了一系列的接口(回调函数),包括协议的打开,读,写,定位等。
typedef struct URLProtocol { const char *name;//协议名称 int (*url_open)( URLContext *h, const char *url, int flags);//打开协议的函数 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 (*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;URLContext 协议的上下文:可以看成某种协议的载体,在打开一个协议的时候,全局函数url_open会根据文件的前缀来判断使用的协议,并为该协议分配好资源,在调用url_connect来打开具体的协议,即调用prot->url_open
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 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, };
typedef struct AVIOContext { const AVClass *av_class; unsigned char *buffer; int buffer_size; unsigned char *buf_ptr; unsigned char *buf_end; void *opaque; 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; int must_flush; int eof_reached; int write_flag; 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; 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; int direct; int64_t bytes_read; int seek_count; int writeout_count; int orig_buffer_size; } AVIOContext;AVIOContext和URLContext的关系:
我们知道数据源头肯定是URLProtocol,而URLContext是对URLProtocol的封装,所以AVIOContext肯定和URLContext有某种关系。在url_fopen中,调用了url_fdopen对AVIOContext进行初始化。将read_packet,write_packet,seek_packet分别设置为ffurl_read, ffurl_write,ffurl_seek。
*s = avio_alloc_context(buffer, buffer_size, h->flags & AVIO_FLAG_WRITE, h, (void*)ffurl_read, (void*)ffurl_write, (void*)ffurl_seek);
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);//通过解析文件名,找到对应的操作集URLProtocol if (err < 0) return err; /* *调用ffio_init_context把URLProtocol中的ffurl_read, ffurl_write, ffurl_seek,注册到AVIOContext结构体中,成为read_packet, write_packet的回调函数 */ err = ffio_fdopen(s, h); if (err < 0) { ffurl_close(h); return err; } return 0; }
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; }
未完待续。。。。