lavu: add a new API for reference-counted data buffers.
This commit is contained in:
194
libavutil/buffer.c
Normal file
194
libavutil/buffer.c
Normal file
@ -0,0 +1,194 @@
|
||||
/*
|
||||
* This file is part of Libav.
|
||||
*
|
||||
* Libav is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* Libav is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with Libav; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "atomic.h"
|
||||
#include "buffer_internal.h"
|
||||
#include "common.h"
|
||||
#include "mem.h"
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
buf->refcount = 1;
|
||||
|
||||
if (flags & AV_BUFFER_FLAG_READONLY)
|
||||
buf->flags |= BUFFER_FLAG_READONLY;
|
||||
|
||||
ref = av_mallocz(sizeof(*ref));
|
||||
if (!ref) {
|
||||
av_freep(&buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ref->buffer = buf;
|
||||
ref->data = data;
|
||||
ref->size = size;
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
void av_buffer_default_free(void *opaque, uint8_t *data)
|
||||
{
|
||||
av_free(data);
|
||||
}
|
||||
|
||||
AVBufferRef *av_buffer_alloc(int size)
|
||||
{
|
||||
AVBufferRef *ret = NULL;
|
||||
uint8_t *data = NULL;
|
||||
|
||||
data = av_malloc(size);
|
||||
if (!data)
|
||||
return NULL;
|
||||
|
||||
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;
|
||||
|
||||
memset(ret->data, 0, size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
AVBufferRef *av_buffer_ref(AVBufferRef *buf)
|
||||
{
|
||||
AVBufferRef *ret = av_mallocz(sizeof(*ret));
|
||||
|
||||
if (!ret)
|
||||
return NULL;
|
||||
|
||||
*ret = *buf;
|
||||
|
||||
avpriv_atomic_int_add_and_fetch(&buf->buffer->refcount, 1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void av_buffer_unref(AVBufferRef **buf)
|
||||
{
|
||||
AVBuffer *b;
|
||||
|
||||
if (!buf || !*buf)
|
||||
return;
|
||||
b = (*buf)->buffer;
|
||||
av_freep(buf);
|
||||
|
||||
if (!avpriv_atomic_int_add_and_fetch(&b->refcount, -1)) {
|
||||
b->free(b->opaque, b->data);
|
||||
av_freep(&b);
|
||||
}
|
||||
}
|
||||
|
||||
int av_buffer_is_writable(const AVBufferRef *buf)
|
||||
{
|
||||
if (buf->buffer->flags & AV_BUFFER_FLAG_READONLY)
|
||||
return 0;
|
||||
|
||||
return avpriv_atomic_int_add_and_fetch(&buf->buffer->refcount, 0) == 1;
|
||||
}
|
||||
|
||||
int av_buffer_make_writable(AVBufferRef **pbuf)
|
||||
{
|
||||
AVBufferRef *newbuf, *buf = *pbuf;
|
||||
|
||||
if (av_buffer_is_writable(buf))
|
||||
return 0;
|
||||
|
||||
newbuf = av_buffer_alloc(buf->size);
|
||||
if (!newbuf)
|
||||
return AVERROR(ENOMEM);
|
||||
|
||||
memcpy(newbuf->data, buf->data, buf->size);
|
||||
av_buffer_unref(pbuf);
|
||||
*pbuf = newbuf;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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)) {
|
||||
/* 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));
|
||||
|
||||
av_buffer_unref(pbuf);
|
||||
*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;
|
||||
}
|
Reference in New Issue
Block a user