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\n", av_buffer_get_ref_count(br));
av_buffer_ref(br);
printf("ref count %d\n", av_buffer_get_ref_count(br));
av_buffer_unref(br);
printf("ref count %d\n", av_buffer_get_ref_count(br));
av_buffer_unref(br);
printf("ref count %d\n", av_buffer_get_ref_count(br));

2 小结

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

时间: 2024-08-29 17:15:56

ffmpeg中AVBuffer的实现分析的相关文章

ffmpeg中av_log的实现分析

[时间:2017-10] [状态:Open] [关键词:ffmpeg,avutil,av_log, 日志输出] 0 引言 FFmpeg的libavutil中的日志输出的接口整体比较少,但是功能还是不错的,对于后续自己实现日志模块还是值得参考的.本文就libavutil中的日志模块部分的实现做一个简要的整理.希望可以达到解释清楚的目的. 注意:本部分的主要代码位于libavtuil/log.h.libavutil/log.c. 1 AVClass的定义部分 log接口的输出是依赖于AVClass的

ffmpeg中关于ffplay部分的概要分析-1

ffplay是ffmpeg中的一个播放音视频流的功能,现简要对其进行分析: 1. 图1是ffplay的主干代码流程 1)在stream_open函数之前,主要是对入参的一些分析,所有codec,demux以及相关protocool的注册 注册两个信号量:一个是给中断使用,一个是给终端操作使用(比如键盘,鼠标等的一些动作) 2)在stream_open函数中,先是对video,audio,subtitle三个format的消息队列进行初始化, 再对收发数据的队列进行初始化.然后调用SDL_Crea

(转)ffmpeg 中 av_read_frame_internal分析

作者: chenwei1983    时间: 2012-3-5 04:21 PM标题: ffmpeg 中 av_read_frame_internal分析                            原出处:http://www.chinavideo.org/viewthread.php?action=printable&tid=13846av_read_frame_internal 在ffmpeg中实现了将format格式的packet,最终转换成一帧帧的es流packet,并解析填

ffmpeg.c函数结构简单分析(画图)

前一阵子研究转码的时候看了FFmpeg的源代码.由于ffmpeg.c的代码相对比较长,而且其中有相当一部分是AVFilter有关的代码(这一部分一直不太熟),因此之前学习FFmpeg的时候一直也没有好好看一下其源代码.最近正好看了看AVFilter的知识,顺便就看了下FFmpeg的源代码,在这里画图理一下它的结构.目前好多地方还没有弄明白,等到以后慢慢完善了. 先说明一下自己画的结构图的规则:图中仅画出了比较重要的函数之间的调用关系.粉红色的函数是FFmpeg编解码类库(libavcodec,l

ffmpeg 中av_rescale_rnd 的含义

http://blog.csdn.net/fireroll/article/details/8485482 一.函数声明: int64_t av_rescale_rnd(int64_t a, int64_t b, int64_t c, enum AVRounding rnd); 直接看代码, 它的作用是计算 "a * b / c" 的值并分五种方式来取整. 用在FFmpeg中, 则是将以 "时钟基c" 表示的 数值a 转换成以 "时钟基b" 来表

音视频框架ffmpeg源码的简要分析

感谢http://m.2cto.com/kf/201201/116171.html这里一系列的文章 最新版的ffmpeg中发现了一个新的东西:avconv,而且ffmpeg.c与avconv.c一个模样,一研究才发现是libav下把ffmpeg改名为avconv了. 到底libav与ffmpeg现在是什么个关系?我也搞得希里糊涂的,先不管它了. ffmpeg的主要功能是音视频的转换和处理.其功能之强大已经到了匪夷所思的地步(有点替它吹了).它的主要特点是能做到把多个输入文件中的任意几个流重新组合

最简单的基于FFMPEG的转码程序分析 +ffmpga代码简析(转 +总结)

模块:  libavcodec    - 编码解码器         libavdevice   - 输入输出设备的支持         libavfilter   - 视音频滤镜支持         libavformat   - 视音频等格式的解析         libavutil     - 工具库         libpostproc   - 后期效果处理         libswscale    - 图像颜色.尺寸转换 1. ffmpga代码简析 1.1 av_log() av_

DEBUG模式下, 内存中的变量地址分析

测试函数的模板实现 [cpp] view plain copy /// @file my_template.h /// @brief 测试数据类型用的模板实现 #ifndef MY_TEMPLATE_H_2016_0123_1226 #define MY_TEMPLATE_H_2016_0123_1226 template<int iArySize> void fnTestDataType() { char szBuf[iArySize] = {'\0'}; unsigned short wT

linux中mmap系统调用原理分析与实现

参考文章:http://blog.csdn.net/shaoguangleo/article/details/5822110 linux中mmap系统调用原理分析与实现 1.mmap系统调用(功能)      void* mmap ( void * addr , size_t len , int prot , int flags ,int fd , off_t offset )      内存映射函数mmap, 负责把文件内容映射到进程的虚拟内存空间, 通过对这段内存的读取和修改,来实现对文件的