关于dict模块
由于C语言不像C++和JAVA,没有key/value键值对的高级实现,所以FFmpeg专门写了一个dict模块(AVDictionary字典)用于简单的key/value存储。dict模块相对比较简单,其代码实现主要位于如下两个文件:
libavutil/dict.c
libavutil/dict.h
我们还是按照套路先分析头文件dict.h的内容。文件开头注释部分首先介绍了AVDictionary是个什么模块:
brief Simple key:value store
Dictionaries are used for storing key:value pairs. To create
an AVDictionary, simply pass an address of a NULL pointer to
av_dict_set(). NULL can be used as an empty dictionary wherever
a pointer to an AVDictionary is required.
Use av_dict_get() to retrieve an entry or iterate over all
entries and finally av_dict_free() to free the dictionary
and all its contents.
简言之,就是key:value对存储的。最简单的创建AVDictionary的方式就是给av_dict_set()传递NULL指针,默认创建一个空的字典,通过 av_dict_get()函数 取得字典,通过av_dict_free()函数释放字典。为此,还举了一个例子:
AVDictionary *d = NULL; // "create" an empty dictionary
AVDictionaryEntry *t = NULL;
av_dict_set(&d, "foo", "bar", 0); // add an entry
char *k = av_strdup("key"); // if your strings are already allocated,
char *v = av_strdup("value"); // you can avoid copying them like this
av_dict_set(&d, k, v, AV_DICT_DONT_STRDUP_KEY | AV_DICT_DONT_STRDUP_VAL);
while (t = av_dict_get(d, "", t, AV_DICT_IGNORE_SUFFIX)) {
<....> // iterate over all entries in d
}
av_dict_free(&d);
整个例子十分简洁,先定义两个字典相关的结构体,然后直接通过av_dict_set()函数将key/value对设置进去即可。av_strdup()函数是复制字符串的意思,即av_strdup()函数内部需要分配内存并拷贝字符串内容,以方便存储到字典中。AV_DICT_DONT_STRDUP_KEY和AV_DICT_DONT_STRDUP_VAL的意思,顾名思义,不需要字典内部复制字符串key/value,这些内存由字典调用者分配,而不是字典自己去分配。内存的使用一定要记得谁负责分配,又由谁负责释放,以免造成内存泄漏。
主要结构定义
首先是几个宏定义,
#define AV_DICT_MATCH_CASE 1 /*只返回完全匹配key的entry结构*/
#define AV_DICT_IGNORE_SUFFIX 2 /*返回第一个匹配key的entry,忽略key的相关前缀 */
#define AV_DICT_DONT_STRDUP_KEY 4 /*不复制key字符串*/
#define AV_DICT_DONT_STRDUP_VAL 8 /*不复制value字符串*/
#define AV_DICT_DONT_OVERWRITE 16 /*不覆盖已经存在的entry*/
#define AV_DICT_APPEND 32 /*如果entry已经存在,直接附加在后面,并且不添加任何分隔符*/
#define AV_DICT_MULTIKEY 64 /*允许在字典内存储多个相同key的值*/
接下来是结构体,其中AVDictionary的定义在dict.c中,
struct AVDictionary {
int count;
AVDictionaryEntry *elems;
};
typedef struct AVDictionaryEntry {
char *key;
char *value;
} AVDictionaryEntry;
typedef struct AVDictionary AVDictionary;
上述结构体也很好理解,AVDictionaryEntry就相当于一个key/value对,而AVDictionary则包括了多个key/value而已。这就类似于C++的std::pair,JAVA的map,当然这里要注意AVDictionary的key/value都是字符串类型。
接口函数
AVDictionaryEntry *av_dict_get(const AVDictionary *m, const char *key,
const AVDictionaryEntry *prev, int flags);
通过此函数获取一个key/value的键值对。如果要遍历整个字典,可以设置key为空字符串“”,并且设置flags标记为AV_DICT_IGNORE_SUFFIX。prev设置为前一个匹配的键值对,如果设置为NULL,那么函数则返回第一个匹配的键值对。
int av_dict_count(const AVDictionary *m);
此函数返回字典内键值对的总数。
int av_dict_set(AVDictionary **pm, const char *key, const char *value, int flags);
此函数完成key/value键值对设置到字典中,如果存在相同key的键值对,会覆盖。
int av_dict_set_int(AVDictionary **pm, const char *key, int64_t value, int flags);
此函数也是完成键值对的存储,但是value变成了整形。可是明明我们前面说了字典只能存储字符串啊?原来,在这个函数的内部实现里,通过snprintf将整数转成了字符串并通过av_dict_set()函数进行了存储。
int av_dict_parse_string(AVDictionary **pm, const char *str,
const char *key_val_sep, const char *pairs_sep,
int flags);
这个函数其实也是完成key/value对键值存储到字典中,但是key/value对是以字符串的形式传递到str中,并以key_val_sep和pairs_sep来做分隔符,分隔符的定义可以参考我们在option章节里的讲解(https://blog.csdn.net/ericbar/article/details/79872779)。
int av_dict_copy(AVDictionary **dst, const AVDictionary *src, int flags);
完成字典的拷贝,从src到dst。需要注意的是,如果dst是NULL,函数内部会主动分配内存并返回给dst。
void av_dict_free(AVDictionary **m);
此函数完成释放字典空间。
int av_dict_get_string(const AVDictionary *m, char **buffer,
const char key_val_sep, const char pairs_sep);
这个函数把字典里所有的键值对,以字符串形式(类似于JSON啦)组合存储到buffer指针内,同样的,key_val_sep和pairs_sep是建议的分隔符。需要注意的是,buffer的内存是由函数内部分配的,释放需要由调用者来完成。
总结
AVDictionary的代码实现,我们同样不做深入分析了,暂时只了解它的使用方法,遇到问题再深入探讨。