• ffmpeg中AVBuffer的实现分析


    [时间:2017-10] [状态:Open]
    [关键词:ffmpeg,avutil,avbuffer, 引用计数]

    0 引言

    AVBuffer是ffmpeg提供的基于引用计数的智能指针的一个实现版本。
    FFmpeg中很多结构体是基于AVBuffer实现的,比如AVFrame、AVPacket。

    AVBuffer实现

    主要实现文件位于libavutil中的buffer.h、buffer_internal.h、buffer.c三个文件中。其中最主要的是两个结构体AVBufferRefAVBuffer。其实AVBufferRef作为AVBuffer的wrapper,也只能通过其访问AVBuffer内部的结构,这样才能保证引用计数可以正常工作。
    先看看AVBuffer的定义:

    struct AVBuffer {
        uint8_t *data; /**< data described by this buffer */
        int      size; /**< size of data in bytes */
    
        /** number of existing AVBufferRef instances referring to this buffer */
        atomic_uint refcount;
    
        /** a callback for freeing the data */
        void (*free)(void *opaque, uint8_t *data);
    
        /** an opaque pointer, to be used by the freeing callback */
        void *opaque;
    
        /** A combination of BUFFER_FLAG_* */
        int flags;
    };
    

    AVBufferRef定义如下:

    typedef struct AVBufferRef {
        AVBuffer *buffer;
    
        uint8_t *data;
        int      size;
    } AVBufferRef;
    

    avutil中主要提供了以下几个创建AVBuffer的接口:

    AVBufferRef *av_buffer_alloc(int size);
    AVBufferRef *av_buffer_allocz(int size);
    AVBufferRef *av_buffer_create(uint8_t *data, int size,
                                  void (*free)(void *opaque, uint8_t *data),
                                  void *opaque, int flags);
    int av_buffer_realloc(AVBufferRef **buf, int size);
    

    1 创建AVBuffer

    主要逻辑位于av_buffer_create中,其他接口都是调用该接口实现的。先看看其他几个函数的实现代码:

    AVBufferRef *av_buffer_alloc(int size)
    {
        AVBufferRef *ret = NULL;
        uint8_t    *data = NULL;
    	// 这里创建内存区域的指针
        data = av_malloc(size);
        if (!data)
            return NULL;
    	// 这里创建AVBuffer对象
        ret = av_buffer_create(data, size, av_buffer_default_free, NULL, 0);
        if (!ret)
            av_freep(&data);
    
        return ret;
    }
    
    AVBufferRef *av_buffer_allocz(int size)
    {
        AVBufferRef *ret = av_buffer_alloc(size);
        if (!ret)
            return NULL;
    	// 相比av_buffer_alloc添加了重置内存的操作
        memset(ret->data, 0, size);
        return ret;
    }
    
    int av_buffer_realloc(AVBufferRef **pbuf, int size)
    {
        AVBufferRef *buf = *pbuf;
        uint8_t *tmp;
    	// 如果未分配内存,则分配一个
        if (!buf) {
            /* allocate a new buffer with av_realloc(), so it will be reallocatable
             * later */
            uint8_t *data = av_realloc(NULL, size);
            if (!data)
                return AVERROR(ENOMEM);
    
            buf = av_buffer_create(data, size, av_buffer_default_free, NULL, 0);
            if (!buf) {
                av_freep(&data);
                return AVERROR(ENOMEM);
            }
    
            buf->buffer->flags |= BUFFER_FLAG_REALLOCATABLE;
            *pbuf = buf;
    
            return 0;
        } else if (buf->size == size)// 长度够用,不用重新分配
            return 0;
    
    	// 需要重新分配,检查下内存实际情况
        if (!(buf->buffer->flags & BUFFER_FLAG_REALLOCATABLE) ||
            !av_buffer_is_writable(buf) || buf->data != buf->buffer->data) {
            /* cannot realloc, allocate a new reallocable buffer and copy data */
            AVBufferRef *new = NULL;
    
            av_buffer_realloc(&new, size);
            if (!new)
                return AVERROR(ENOMEM);
    
            memcpy(new->data, buf->data, FFMIN(size, buf->size));
    
            buffer_replace(pbuf, &new);
            return 0;
        }
    
        tmp = av_realloc(buf->buffer->data, size);
        if (!tmp)
            return AVERROR(ENOMEM);
    
        buf->buffer->data = buf->data = tmp;
        buf->buffer->size = buf->size = size;
        return 0;
    }
    

    下面是最主要的逻辑,关于av_buffer_create实现。

    AVBufferRef *av_buffer_create(uint8_t *data, int size,
        void (*free)(void *opaque, uint8_t *data), void *opaque, int flags)
    {
        AVBufferRef *ref = NULL;
        AVBuffer    *buf = NULL;
    	// 创建AVBuffer,并初始化之
        buf = av_mallocz(sizeof(*buf));
        if (!buf)
            return NULL;
    
        buf->data     = data;
        buf->size     = size;
        buf->free     = free ? free : av_buffer_default_free;
        buf->opaque   = opaque;
    	// ffmpeg提供的原子初始化函数
        atomic_init(&buf->refcount, 1);
    
        if (flags & AV_BUFFER_FLAG_READONLY)
            buf->flags |= BUFFER_FLAG_READONLY;
    
    	// 创建AVBufferRef的内存区域,并初始化
        ref = av_mallocz(sizeof(*ref));
        if (!ref) {
            av_freep(&buf);
            return NULL;
        }
    
        ref->buffer = buf;
        ref->data   = data;
        ref->size   = size;
    
        return ref;
    }
    

    这里提供了一个默认的内存区域释放函数,实际上就是调用av_free,如下:

    void av_buffer_default_free(void *opaque, uint8_t *data)
    {
        av_free(data);
    }
    

    其他主要的函数就是访问AVBuffer的一些成员和添加、删除引用计数。

    AVBufferRef *av_buffer_ref(AVBufferRef *buf)
    {
        AVBufferRef *ret = av_mallocz(sizeof(*ret));
    
        if (!ret)
            return NULL;
    
        *ret = *buf;
    	// 引用计数+1
        atomic_fetch_add_explicit(&buf->buffer->refcount, 1, memory_order_relaxed);
    
        return ret;
    }
    
    static void buffer_replace(AVBufferRef **dst, AVBufferRef **src)
    {
        AVBuffer *b;
    
        b = (*dst)->buffer;
    
        if (src) {
            **dst = **src;
            av_freep(src);
        } else
            av_freep(dst);
    	// 引用计数-1,如果是最后一个引用计数,则释放内存区域
        if (atomic_fetch_add_explicit(&b->refcount, -1, memory_order_acq_rel) == 1) {
            b->free(b->opaque, b->data);
            av_freep(&b);
        }
    }
    
    void av_buffer_unref(AVBufferRef **buf)
    {
        if (!buf || !*buf)
            return;
    
    	// 引用计数和NULL交换
        buffer_replace(buf, NULL);
    }
    
    int av_buffer_get_ref_count(const AVBufferRef *buf)
    {
    	// 获取AVBuffer的引用计数
        return atomic_load(&buf->buffer->refcount);
    }
    

    整体来说实现相对简单,主要依赖ffmpeg中所提供的诸多原子操作机制。AVBuffer的基本用法代码如下:

    AVBufferRef * br = av_buffer_alloc(100);
    printf("ref count %d
    ", av_buffer_get_ref_count(br));
    av_buffer_ref(br);
    printf("ref count %d
    ", av_buffer_get_ref_count(br));
    av_buffer_unref(br);
    printf("ref count %d
    ", av_buffer_get_ref_count(br));
    av_buffer_unref(br);
    printf("ref count %d
    ", av_buffer_get_ref_count(br));
    

    2 小结

    ffmpeg中的AVBuffer实现逻辑较简单,其核心就是一个代理模式的实现。你可以绕过ffmpeg提供的接口直接访问内存指针,但是这样就无法做到安全的访问控制。

  • 相关阅读:
    vs快捷键
    深入理解react和redux
    eclispe下maven项目报错解决方式
    ios fixed浮层 光标下移解决方案
    jQuery的extend()用法
    连续数字数值转换成逗号分隔
    css 清除浮动
    恶补JavaScript第二篇
    恶补JavaScript第一篇
    中文英文左右padding一致两端对齐实现(转载)
  • 原文地址:https://www.cnblogs.com/tocy/p/ffmpeg-libavutil-avbuffer-imp.html
Copyright © 2020-2023  润新知