热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

ffmpeg中AVBuffer的实现分析

本文简要整理了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; /**

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提供的接口直接访问内存指针,但是这样就无法做到安全的访问控制。

ffmpeg中AVBuffer的实现分析


推荐阅读
  • 二维码的实现与应用
    本文介绍了二维码的基本概念、分类及其优缺点,并详细描述了如何使用Java编程语言结合第三方库(如ZXing和qrcode.jar)来实现二维码的生成与解析。 ... [详细]
  • 本文介绍了如何通过C#语言调用动态链接库(DLL)中的函数来实现IC卡的基本操作,包括初始化设备、设置密码模式、获取设备状态等,并详细展示了将TextBox中的数据写入IC卡的具体实现方法。 ... [详细]
  • 本文将从基础概念入手,详细探讨SpringMVC框架中DispatcherServlet如何通过HandlerMapping进行请求分发,以及其背后的源码实现细节。 ... [详细]
  • Windows操作系统提供了Encrypting File System (EFS)作为内置的数据加密工具,特别适用于对NTFS分区上的文件和文件夹进行加密处理。本文将详细介绍如何使用EFS加密文件夹,以及加密过程中的注意事项。 ... [详细]
  • 回顾两年前春节期间的一个个人项目,该项目原本计划参加竞赛,但最终作为练习项目完成。独自完成了从编码到UI设计的全部工作,尽管代码量不大,但仍有一定的参考价值。本文将详细介绍该项目的背景、功能及技术实现。 ... [详细]
  • 如何在PHP中安装Xdebug扩展
    本文介绍了如何从PECL下载并编译安装Xdebug扩展,以及如何配置PHP和PHPStorm以启用调试功能。 ... [详细]
  • 本文探讨了在一个物理隔离的环境中构建数据交换平台所面临的挑战,包括但不限于数据加密、传输监控及确保文件交换的安全性和可靠性。同时,作者结合自身项目经验,分享了项目规划、实施过程中的关键决策及其背后的思考。 ... [详细]
  • importjava.io.*;importjava.util.*;publicclass五子棋游戏{staticintm1;staticintn1;staticfinalintS ... [详细]
  • 本文详细介绍了在 CentOS 系统中如何创建和管理 SWAP 分区,包括临时创建交换文件、永久性增加交换空间的方法,以及如何手动释放内存缓存。 ... [详细]
  • 本文探讨了程序员这一职业的本质,认为他们是专注于问题解决的专业人士。文章深入分析了他们的日常工作状态、个人品质以及面对挑战时的态度,强调了编程不仅是一项技术活动,更是个人成长和精神修炼的过程。 ... [详细]
  • 本文介绍了SIP(Session Initiation Protocol,会话发起协议)的基本概念、功能、消息格式及其实现机制。SIP是一种在IP网络上用于建立、管理和终止多媒体通信会话的应用层协议。 ... [详细]
  • 本文详细介绍了iOS应用的生命周期,包括各个状态及其转换过程中的关键方法调用。 ... [详细]
  • 项目风险管理策略与实践
    本文探讨了项目风险管理的关键环节,包括风险管理规划、风险识别、风险分析(定性和定量)、风险应对策略规划及风险控制。旨在通过系统的方法提升项目成功率,减少不确定因素对项目的影响。 ... [详细]
  • 在处理大数据量的SQL分页查询时,通常需要执行两次查询来分别获取数据和总记录数。本文介绍了一种优化方法,通过单次查询同时返回分页数据和总记录数,从而提高查询效率。 ... [详细]
  • 本文通过一个具体的实例,介绍如何利用TensorFlow框架来计算神经网络模型在多分类任务中的Top-K准确率。代码中包含了随机种子设置、模拟预测结果生成、真实标签生成以及准确率计算等步骤。 ... [详细]
author-avatar
手机用户2502897095
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有