FFmpeg-avio_list_dir
文件夹读取分为4个步骤:
-
创建文件读取的上下文对象
URLContext
,用来保存记录文件读取过程中所有环境变量和源数据 -
根据文件查找相对应的文件协议
URLProtocol
,这里操作的是本地文件,所以是ff_file_protocol
对象,不同的文件格式所对应的文件协议的实现也不一样,如ff_https_protocol
对文件的操作主要是通过网络处理的 -
通过
URLContext`内部的priv_data(FileContext)提供文件的处理方式,如blocSize,seekable -
通过URLContext->port(URLProtocol)操作文件并返回需要的数据
图解
URLContext
它是一个文件的上下文对象,主要负责提供文件处理过程的状态信息
typedef struct URLContext {
const AVClass *av_class; //用来提供上下文日志信息的打印,以及提供AVOptions
const struct URLProtocol *prot; //文件协议对象,遵守文件的基本协议如`open/close/read`
void *priv_data; //它是一个私有的文件操作对象,在URLProtocol的具体实现方法中读取并记录文件信息
char *filename; //文件的路径信息
int flags; //标志位,如`AVIO_FLAG_READ`,`AVIO_FLAG_WRITE`,还有很多,具体搜索`AVIO_FLAG`关键字
int max_packet_size; //读取的最大Packet Size
int is_streamed; //是否正在传输数据
int is_connected; //当打开了文件,持有了文件描述符则标记为链接
AVIOInterruptCB interrupt_callback; //文件读取被终端
int64_t rw_timeout; //读取/写入/网络链接 网络链接的超时时间,mcs表示
const char *protocol_whitelist; //白名单协议
const char *protocol_blacklist; //黑名单协议
int min_packet_size; //同max_packet_size相反
} URLContext;
URLProtocol
它是一个文件协议对象,定义了文件相关的协议方法,FFmpeg在处理文件时会去全局的文件协议注册表里查找合适的协议来处理文件,在这个example中使用的是ff_file_protocol来操作文件
const 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,
.url_delete = file_delete,
.url_move = file_move,
.priv_data_size = sizeof(FileContext), //FileContext对应URLContext的priv_data,提供操作的配置选项信息以及保文件描述符
.priv_data_class = &file_class, //为文件提供AVOptions,初始化FileContext
.url_open_dir = file_open_dir,
.url_read_dir = file_read_dir,
.url_close_dir = file_close_dir,
.default_whitelist = "file,crypto,data"
};
FileContext
它是根据文件协议的priv_data_size
和priv_data_class
创建的, priv_data_size
定义了它的大小,用于分配空间,priv_data_class
则定义了它的AVOptions属性,用于设置FileContext
初始化属性,如下:
typedef struct FileContext {
const AVClass *class; //关连的AVClass,此示例中关联的是上文提到的`file_class`,它也是AVClass类型
int fd; //文件描述符
int trunc; //尾部
int blocksize; //读取的块大小
int follow; //跟踪正在写入的文件
int seekable; //设置文件是否可以查找指定位置,游标
#if HAVE_DIRENT_H
DIR *dir; //调用系统api返回的Dir指针,记录了文件夹信息
#endif
} FileContext;
URLContext->prvi_data
prvi_data对应的是FileContext
对象,它描述的是定义文件处理的相关选项信息,如读取块的大小,是否可以查找指定位置,同时也会记录文件描述符,配合URRLProtocol
的协议方法处理文件.
URLContext->prvi_data(FileContext)是如何和ff_file_protocol->priv_data_class
(AVClass)关联的?
- 在通过URLProtocol读取文件文件信息的时候URLContext会指定将
priv_data
转换为FileContext
static int file_open_dir(URLContext *h)
{
#if HAVE_LSTAT
FileContext *c = h->priv_data;
c->dir = opendir(h->filename);
if (!c->dir)
return AVERROR(errno);
return 0;
#else
return AVERROR(ENOSYS);
#endif /* HAVE_LSTAT */
}
- 在初始化
URLContext
的port
(URLProtocol)文件协议对象的时候,会将priv_data
和file_class
关联,并初始化priv_data(FileContext)中的属性
static int url_alloc_for_protocol(URLContext **puc, const URLProtocol *up,
const char *filename, int flags,
const AVIOInterruptCB *int_cb)
{ ...
if (up->priv_data_size) {
...
*(const AVClass **)uc->priv_data = up->priv_data_class;
....
av_opt_set_defaults(uc->priv_data);
file_class
同时也提供的AVOptions
设置到FileContext
中
static const AVClass file_class = {
.class_name = "file",
.item_name = av_default_item_name,
.option = file_options,
.version = LIBAVUTIL_VERSION_INT,
};
static const AVOption file_options[] = {
{ "truncate", ...
{ "blocksize", ...
{ "follow", ...
{ "seekable", ...
};
-
priv_data_class是一个可选配置,此处是为了给这个文件操作的上下文
URLContext->priv_data(FileContext)
设置file文件的默认参数,其实就是初始化附值 -
除了默认的初始化复制,还可以通过外界传入
AVOptions
来覆盖默认的初始化赋值
int avio_open_dir(AVIODirContext **s, const char *url, AVDictionary **options)
{
URLContext *h = NULL;
AVIODirContext *ctx = NULL;
...
if (h->prot->url_open_dir && h->prot->url_read_dir && h->prot->url_close_dir) {
if (options && h->prot->priv_data_class &&
(ret = av_opt_set_dict(h->priv_data, options)) < 0)
AVClass的作用
在这个文件信息读取的示例中,已有2个类第一个属性为AVClass
URLContext - AVClass:
FileContext - AVClass: 这里关联的是 file_class,为FileContext提供了默认的AVOptions,初始化文件读写的一些属性,如blocSize,seekable,truncate
用于描述一个AVClass上下文的结构体对象这是一个抽象的结构体对象,第一个属性只想结构体本身,其它的 XXContext类第一个属性也是 AVClass就遵守了这个AVClass协议,基于首地址指针相同的原理,Context和AVClass可以进行类型互转,从而很方便的进行log输出以及从AVClass中设置数据(AVOptions)设置
从Dart语言设计角度来看,它就像是给 Context对象加了一个 mixin的方法并提供了一个抽象类.
/**
* Describe the class of an AVClass context structure. That is an
* arbitrary struct of which the first field is a pointer to an
* AVClass struct (e.g. AVCodecContext, AVFormatContext etc.).
*/
typedef struct AVClass {
/**
* The name of the class; usually it is the same name as the
* context structure type to which the AVClass is associated.
*/
const char* class_name;
/**
* A pointer to a function which returns the name of a context
* instance ctx associated with the class.
*/
const char* (*item_name)(void* ctx);
/**
* a pointer to the first option specified in the class if any or NULL
*
* @see av_set_default_options()
*/
const struct AVOption *option;
/**
* LIBAVUTIL_VERSION with which this structure was created.
* This is used to allow fields to be added without requiring major
* version bumps everywhere.
*/
int version;
/**
* Offset in the structure where log_level_offset is stored.
* 0 means there is no such variable
*/
int log_level_offset_offset;
/**
* Offset in the structure where a pointer to the parent context for
* logging is stored. For example a decoder could pass its AVCodecContext
* to eval as such a parent context, which an av_log() implementation
* could then leverage to display the parent context.
* The offset can be NULL.
*/
int parent_log_context_offset;
/**
* Return next AVOptions-enabled child or NULL
*/
void* (*child_next)(void *obj, void *prev);
/**
* Category used for visualization (like color)
* This is only set if the category is equal for all objects using this class.
* available since version (51 << 16 | 56 << 8 | 100)
*/
AVClassCategory category;
/**
* Callback to return the category.
* available since version (51 << 16 | 59 << 8 | 100)
*/
AVClassCategory (*get_category)(void* ctx);
/**
* Callback to return the supported/allowed ranges.
* available since version (52.12)
*/
int (*query_ranges)(struct AVOptionRanges **, void *obj, const char *key, int flags);
/**
* Iterate over the AVClasses corresponding to potential AVOptions-enabled
* children.
*
* @param iter pointer to opaque iteration state. The caller must initialize
* *iter to NULL before the first call.
* @return AVClass for the next AVOptions-enabled child or NULL if there are
* no more such children.
*
* @note The difference between child_next and this is that child_next
* iterates over _already existing_ objects, while child_class_iterate
* iterates over _all possible_ children.
*/
const struct AVClass* (*child_class_iterate)(void **iter);
} AVClass;