深入理解FFmpeg AVBPrint

在FFmpeg的基础库中,有一个可以支持Buffer空间动态扩容的功能支持,在C99之前的C语言支持动态扩容相对比较麻烦,这个AVBPrint可以提取出来单独使用,比较方便,下面详细介绍一下其使用方式及实现。

AVBPrint 结构体的定义

AVBPrint的结构体定义主要用来做Buffer的存储于信息记录用,下面看一下代码的注释及结构体内容

/**

 * Buffer to print data progressively

 *

 * The string buffer grows as necessary and is always 0-terminated.

 * The content of the string is never accessed, and thus is

 * encoding-agnostic and can even hold binary data.

 *

 * Small buffers are kept in the structure itself, and thus require no

 * memory allocation at all (unless the contents of the buffer is needed

 * after the structure goes out of scope). This is almost as lightweight as

 * declaring a local "char buf[512]".

 *

 * The length of the string can go beyond the allocated size: the buffer is

 * then truncated, but the functions still keep account of the actual total

 * length.

 *

 * In other words, buf->len can be greater than buf->size and records the

 * total length of what would have been to the buffer if there had been

 * enough memory.

 *

 * Append operations do not need to be tested for failure: if a memory

 * allocation fails, data stop being appended to the buffer, but the length

 * is still updated. This situation can be tested with

 * av_bprint_is_complete().

 *

 * The size_max field determines several possible behaviours:

 *

 * size_max = -1 (= UINT_MAX) or any large value will let the buffer be

 * reallocated as necessary, with an amortized linear cost.

 *

 * size_max = 0 prevents writing anything to the buffer: only the total

 * length is computed. The write operations can then possibly be repeated in

 * a buffer with exactly the necessary size

 * (using size_init = size_max = len + 1).

 *

 * size_max = 1 is automatically replaced by the exact size available in the

 * structure itself, thus ensuring no dynamic memory allocation. The

 * internal buffer is large enough to hold a reasonable paragraph of text,

 * such as the current paragraph.

 */

FF_PAD_STRUCTURE(AVBPrint, 1024,

    char *str;         /**< string so far */

    unsigned len;      /**< length so far */

    unsigned size;     /**< allocated memory */

    unsigned size_max; /**< maximum allocated memory */

    char reserved_internal_buffer[1];

)

从注释中可以看到,AVBPrint的字符串长度是可以动态增加的。代码实现比较奇怪的部分是结构体声明,并不是常见的struct name {};格式,而是通过FF_PAD_STRUCTURE来声明的,这个声明也是一个宏,打开这个宏看一下定义实现:

/**

 * Define a structure with extra padding to a fixed size

 * This helps ensuring binary compatibility with future versions.

 */

#define FF_PAD_STRUCTURE(name, size, ...) \

struct ff_pad_helper_##name { __VA_ARGS__ }; \

typedef struct name { \

    __VA_ARGS__ \

    char reserved_padding[size - sizeof(struct ff_pad_helper_##name)]; \

} name;

其实只是声明的时候加了在编译时动态生成的内容,在成员变量列表之后追加了一个成员变量char reserved_padding;编译时这一段宏的内容为:

struct ff_pad_helper_AVBPrint {

    char *str;

    unsigned len;

    unsigned size;

    unsigned size_max;

    char reserved_internal_buffer[1];;

};

typedef struct ABPrint {

    char *str;

    unsigned len;

    unsigned size;

    unsigned size_max;

    char reserved_internal_buffer[1];

    char reserved_padding[1024 - sizeof(struct ff_pad_helper_AVBPrint )];

} AVBPrint;

关键的结构体介绍完毕。声明完毕之后,再介绍一下三个用在AVBPrint初始化时定义AVBPrint使用的初始方法的宏:

/**

 * Convenience macros for special values for av_bprint_init() size_max

 * parameter.

 */

#define AV_BPRINT_SIZE_UNLIMITED  ((unsigned)-1) // is converted to UINT_MAX, the largest limit possible.

#define AV_BPRINT_SIZE_AUTOMATIC  1  // is replaced by the maximum value for automatic storage; any large value means that the internal buffer will be reallocated as needed up to that limit;

#define AV_BPRINT_SIZE_COUNT_ONLY 0   //means do not write anything, just count the length;

常用的一般可以为AV_BPRINT_SIZE_UNLIMITED即可。

还有两个接口在使用AVBPrint时会经常用到:

#define av_bprint_room(buf) ((buf)->size – FFMIN((buf)->len, (buf)->size))

#define av_bprint_is_allocated(buf) ((buf)->str != (buf)->reserved_internal_buffer)

前置部分介绍完毕。接下来了解一下几个比较关键的接口。

av_bprint_init 接口

在使用bprint之前需要调用av_bprint_init进行bprint初始化,为AVBPrint申请一个空间,例如。

使用示例:

av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);

接口实现:

void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max)

{

    unsigned size_auto = (char *)buf + sizeof(*buf) - buf->reserved_internal_buffer;

    if (size_max == 1)

        size_max = size_auto;

    buf->str      = buf->reserved_internal_buffer;

    buf->len      = 0;

    buf->size     = FFMIN(size_auto, size_max);

    buf->size_max = size_max;

    *buf->str = 0;

    if (size_init > buf->size)

        av_bprint_alloc(buf, size_init - 1);

}

av_bprint_init_for_buffer 接口

在初始化AVBPrint时即设置好buffer内容,

使用示例:

AVBPrint b;

char buf[256];

av_bprint_init_for_buffer(&b, buf, sizeof(buf));

接口实现:

void av_bprint_init_for_buffer(AVBPrint *buf, char *buffer, unsigned size)

{

    buf->str      = buffer;

    buf->len      = 0;

    buf->size     = size;

    buf->size_max = size;

    *buf->str = 0;

}

av_bprint_alloc 接口

申请AVBPrint内存空间,内部是使用realloc来实现的。

使用示例:

在介绍av_bprint_init时可以查到;

接口实现:

static int av_bprint_alloc(AVBPrint *buf, unsigned room)

{

    char *old_str, *new_str;

    unsigned min_size, new_size;

    if (buf->size == buf->size_max)

        return AVERROR(EIO);

    if (!av_bprint_is_complete(buf))

        return AVERROR_INVALIDDATA; /* it is already truncated anyway */

    min_size = buf->len + 1 + FFMIN(UINT_MAX - buf->len - 1, room);

    new_size = buf->size > buf->size_max / 2 ? buf->size_max : buf->size * 2;

    if (new_size < min_size)

        new_size = FFMIN(buf->size_max, min_size);

    old_str = av_bprint_is_allocated(buf) ? buf->str : NULL;

    new_str = av_realloc(old_str, new_size);

    if (!new_str)

        return AVERROR(ENOMEM);

    if (!old_str)

        memcpy(new_str, buf->str, buf->len + 1);

    buf->str  = new_str;

    buf->size = new_size;

    return 0;

}

av_bprint_grow 接口

动态扩容AVBPrint的buffer的大小。

使用示例:

在介绍av_bprint_append_data接口实现时会看到;

接口实现:

static void av_bprint_grow(AVBPrint *buf, unsigned extra_len)

{

    /* arbitrary margin to avoid small overflows */

    extra_len = FFMIN(extra_len, UINT_MAX - 5 - buf->len);

    buf->len += extra_len;

    if (buf->size)

        buf->str[FFMIN(buf->len, buf->size - 1)] = 0;

}

通过代码可以看到,这里并未申请内存,而是对buffer的长度进行了扩容。这个扩容接口尝尝是与av_bprint_alloc配合使用。

av_bprintf 与 av_vbprintf 接口

类似sprintf于vsprintf的方式对AVBPrint进行操作。

使用示例:

AVBPrint buf;

av_bprint_init(&buf, 0, AV_BPRINT_SIZE_AUTOMATIC);

av_bprintf(&buf, “%d%c%d”, 1, ‘/‘, 15);

接口实现:

void av_bprintf(AVBPrint *buf, const char *fmt, ...)

{

    unsigned room;

    char *dst;

    va_list vl;

    int extra_len;

    while (1) {

        room = av_bprint_room(buf);

        dst = room ? buf->str + buf->len : NULL;

        va_start(vl, fmt);

        extra_len = vsnprintf(dst, room, fmt, vl);

        va_end(vl);

        if (extra_len <= 0)

            return;

        if (extra_len < room)

            break;

        if (av_bprint_alloc(buf, extra_len))

            break;

    }

    av_bprint_grow(buf, extra_len);

}

void av_vbprintf(AVBPrint *buf, const char *fmt, va_list vl_arg)

{

    unsigned room;

    char *dst;

    int extra_len;

    va_list vl;

    while (1) {

        room = av_bprint_room(buf);

        dst = room ? buf->str + buf->len : NULL;

        va_copy(vl, vl_arg);

        extra_len = vsnprintf(dst, room, fmt, vl);

        va_end(vl);

        if (extra_len <= 0)

            return;

        if (extra_len < room)

            break;

        if (av_bprint_alloc(buf, extra_len))

            break;

    }

    av_bprint_grow(buf, extra_len);

}

buffer空间不足时通过av_bprint_alloc动态扩容空间,然后通过av_bprint_grow扩充buffer长度后的size。

av_bprint_append_data 接口

在AVBPrint的缓存内容中增加内容。

使用示例:

av_bprint_init(&bprint, 64, AV_BPRINT_SIZE_UNLIMITED);

av_bprint_append_data(&bprint, &key_val_sep, 1);

av_bprint_finalize(&bprint, buffer);

接口实现:

void av_bprint_append_data(AVBPrint *buf, const char *data, unsigned size)

{

    unsigned room, real_n;

    while (1) {

        room = av_bprint_room(buf);

        if (size < room)

            break;

        if (av_bprint_alloc(buf, size))

            break;

    }

    if (room) {

        real_n = FFMIN(size, room - 1);

        memcpy(buf->str + buf->len, data, real_n);

    }

    av_bprint_grow(buf, size);

}

av_bprint_finalize 接口

AVBPrint接口使用完成。这个接口比较特别,在使用这个接口之前,最好判断一下操作AVBPrint期间扩容buffer时是否生效,可以使用接口av_bprint_is_complete来进行判断,如果有失败,那么可以通过传递NULL给av_bprint_finalize来释放AVBPrint空间。

使用示例:

if (!av_bprint_is_complete(&header)) {

    av_bprint_finalize(&header, NULL);

} else {

    av_bprint_finalize(&header, &header_str);

}

接口实现:

int av_bprint_finalize(AVBPrint *buf, char **ret_str)

{

    unsigned real_size = FFMIN(buf->len + 1, buf->size);

    char *str;

    int ret = 0;

    if (ret_str) {

        if (av_bprint_is_allocated(buf)) {

            str = av_realloc(buf->str, real_size);

            if (!str)

                str = buf->str;

            buf->str = NULL;

        } else {

            str = av_malloc(real_size);

            if (str)

                memcpy(str, buf->str, real_size);

            else

                ret = AVERROR(ENOMEM);

        }

        *ret_str = str;

    } else {

        if (av_bprint_is_allocated(buf))

            av_freep(&buf->str);

    }

    buf->size = real_size;

    return ret;

}

AVBPrint完整使用示例

关于AVBPrint接口部分介绍到这里已经完毕,下面来看一个完整的使用AVBPrint的常见应用实例:

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include "buffer_print.h"

static int toupper(int c)

{

    if (c >= 'a' && c <= 'z') {

        c ^= 0x20;

    }

    return c;

}

int stristart(const char *str, const char *pfx, const char **ptr)

{

    while (*pfx && toupper((unsigned)*pfx) == toupper((unsigned)*str)) {

        pfx++;

        str++;

    }

    if (!*pfx && ptr) {

        *ptr = str;

    }

    return !*pfx;

}

char *stristr(const char *s1, const char *s2)

{

    if (!*s2) {

        return (char*)(intptr_t)s1;

    }

    do {

        if (stristart(s1, s2, NULL)) {

            return (char*)(intptr_t)s1;

        }

    } while (*s1++);

    return NULL;

}

char *string_replace(const char *str, const char *from, const char *to)

{

    char *ret = NULL;

    const char *pstr2, *pstr = str;

    size_t tolen = strlen(to), fromlen = strlen(from);

    BPrint pbuf;

    bprint_init(&pbuf, 1, BPRINT_SIZE_UNLIMITED);

    while ((pstr2 = stristr(pstr, from))) {

        bprint_append_data(&pbuf, pstr, pstr2 - pstr);

        pstr = pstr2 + fromlen;

        bprint_append_data(&pbuf, to, tolen);

    }

    bprint_append_data(&pbuf, pstr, strlen(pstr));

    if (!bprint_is_complete(&pbuf)) {

        bprint_finalize(&pbuf, NULL);

    } else {

        bprint_finalize(&pbuf, &ret);

    }

    return ret;

}

int main(int argc, char *argv[])

{

    const char *haystack = "Education consists mainly in what we have unlearned.";

    const char * const needle[] = {"learned.", "unlearned.", "Unlearned"};

    char *ptr;

#define TEST_STRING_REPLACE(haystack, needle, expected) \

    ptr = string_replace(haystack, needle, "instead"); \

    if (ptr == NULL) { \

        printf("error, received null pointer!\n"); \

    } else { \

        if (strcmp(ptr, expected) != 0) \

            printf( "expected: %s, received: %s\n", expected, ptr); \

        free(ptr); \

    }

    printf("Test start\n");

    TEST_STRING_REPLACE(haystack, needle [0], "Education consists mainly in what we have uninstead");

    TEST_STRING_REPLACE(haystack, needle [1], “Education consists mainly in what we have instead”);

    TEST_STRING_REPLACE(haystack, needle [2], “Education consists mainly in what we have instead.”);

    TEST_STRING_REPLACE(haystack, needle [1], “Education consists mainly in what we have instead”);

    printf(“Test end\n”);

    return 0;

}

本例子是使用AVBPrint实现一个字符串替换的工作。到这里,使用示例介绍完毕。

作者:悟空

版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。

(0)

相关推荐

发表回复

登录后才能评论