ChinaFFmpeg

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 8271|回复: 0

[源码] FFmpeg 源代码分析 - libavutil部分 - bprint

[复制链接]
发表于 2017-4-3 23:34:18 | 显示全部楼层 |阅读模式

AVPrint 结构体的定义
AVPrint的结构体定义主要用来做Buffer的存储于信息记录用,下面看一下代码的注释及结构体内容
[C] 纯文本查看 复制代码
/**
 * 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来声明的,这个声明也是一个宏,打开这个宏看一下定义实现:
[C] 纯文本查看 复制代码
/**
 * 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;编译时这一段宏的内容为:
[C] 纯文本查看 复制代码
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使用的初始方法的宏:
[C] 纯文本查看 复制代码
/**
 * 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时会经常用到,一个是用来或者Buffer剩余空间大小的,一个是用来判断是否申请过内存的
[C] 纯文本查看 复制代码
#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申请一个空间,例如:
使用示例:
[C] 纯文本查看 复制代码
av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);


接口实现:
[C] 纯文本查看 复制代码
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内容,
使用示例:

[C] 纯文本查看 复制代码
AVBPrint b;
char buf[256];
av_bprint_init_for_buffer(&b, buf, sizeof(buf));

接口实现:

[C] 纯文本查看 复制代码
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来实现的。
使用示例:
[C] 纯文本查看 复制代码
在介绍av_bprint_init时可以查到;


接口实现:

[C] 纯文本查看 复制代码
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的大小。

使用示例:

[C] 纯文本查看 复制代码
在介绍av_bprint_append_data接口实现时会看到;

接口实现:

[C] 纯文本查看 复制代码
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进行操作。

使用示例:

[C] 纯文本查看 复制代码
AVBPrint buf;
av_bprint_init(&buf, 0, AV_BPRINT_SIZE_AUTOMATIC);
av_bprintf(&buf, "%d%c%d", 1, ‘/‘, 15);


接口实现:

[C] 纯文本查看 复制代码
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的缓存内容中增加内容。

使用示例:

[C] 纯文本查看 复制代码
av_bprint_init(&bprint, 64, AV_BPRINT_SIZE_UNLIMITED);
av_bprint_append_data(&bprint, &key_val_sep, 1);
av_bprint_finalize(&bprint, buffer);


接口实现:

[C] 纯文本查看 复制代码
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空间。

使用示例:

[C] 纯文本查看 复制代码
if (!av_bprint_is_complete(&header)) {
    av_bprint_finalize(&header, NULL);
} else {
    av_bprint_finalize(&header, &header_str);
}


接口实现:

[C] 纯文本查看 复制代码
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的现实应用实例:

[C] 纯文本查看 复制代码
#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实现一个字符串替换的工作。到这里,使用示例介绍完毕


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|Archiver|ChinaFFmpeg

GMT+8, 2024-4-20 18:01 , Processed in 0.058795 second(s), 18 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表