mirror of https://github.com/mpv-player/mpv
mp_image: refcounting helpers
This commit is contained in:
parent
a8e69707f7
commit
65a0b5fdc6
238
video/mp_image.c
238
video/mp_image.c
|
@ -21,18 +21,83 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "talloc.h"
|
||||
|
||||
#include "video/img_format.h"
|
||||
#include "video/mp_image.h"
|
||||
#include "video/sws_utils.h"
|
||||
#include "video/filter/vf.h"
|
||||
|
||||
#include "video/memcpy_pic.h"
|
||||
#include "libavutil/mem.h"
|
||||
#include "libavutil/common.h"
|
||||
|
||||
struct m_refcount {
|
||||
void *arg;
|
||||
// free() is called if refcount reaches 0.
|
||||
void (*free)(void *arg);
|
||||
// External refcounted object (such as libavcodec DR buffers). This assumes
|
||||
// that the actual data is managed by the external object, not by
|
||||
// m_refcount. The .ext_* calls use that external object's refcount
|
||||
// primitives. It usually doesn't make sense to set both .free and .ext_*.
|
||||
void (*ext_ref)(void *arg);
|
||||
void (*ext_unref)(void *arg);
|
||||
bool (*ext_is_unique)(void *arg);
|
||||
// Native refcount (there may be additional references if .ext_* are set)
|
||||
int refcount;
|
||||
};
|
||||
|
||||
// Only for checking API usage
|
||||
static int m_refcount_destructor(void *ptr)
|
||||
{
|
||||
struct m_refcount *ref = ptr;
|
||||
assert(ref->refcount == 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Starts out with refcount==1, caller can set .arg and .free and .ext_*
|
||||
static struct m_refcount *m_refcount_new(void)
|
||||
{
|
||||
struct m_refcount *ref = talloc_ptrtype(NULL, ref);
|
||||
*ref = (struct m_refcount) { .refcount = 1 };
|
||||
talloc_set_destructor(ref, m_refcount_destructor);
|
||||
return ref;
|
||||
}
|
||||
|
||||
static void m_refcount_ref(struct m_refcount *ref)
|
||||
{
|
||||
ref->refcount++;
|
||||
if (ref->ext_ref)
|
||||
ref->ext_ref(ref->arg);
|
||||
}
|
||||
|
||||
static void m_refcount_unref(struct m_refcount *ref)
|
||||
{
|
||||
assert(ref->refcount > 0);
|
||||
if (ref->ext_unref)
|
||||
ref->ext_unref(ref->arg);
|
||||
ref->refcount--;
|
||||
if (ref->refcount == 0) {
|
||||
if (ref->free)
|
||||
ref->free(ref->arg);
|
||||
talloc_free(ref);
|
||||
}
|
||||
}
|
||||
|
||||
static bool m_refcount_is_unique(struct m_refcount *ref)
|
||||
{
|
||||
if (ref->refcount > 1)
|
||||
return false;
|
||||
if (ref->ext_is_unique)
|
||||
return ref->ext_is_unique(ref->arg); // referenced only by us
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void mp_image_alloc_planes(mp_image_t *mpi) {
|
||||
assert(!mpi->refcount);
|
||||
// IF09 - allocate space for 4. plane delta info - unused
|
||||
if (mpi->imgfmt == IMGFMT_IF09) {
|
||||
mpi->planes[0]=av_malloc(mpi->bpp*mpi->width*(mpi->height+2)/8+
|
||||
|
@ -75,19 +140,8 @@ void mp_image_alloc_planes(mp_image_t *mpi) {
|
|||
mpi->flags|=MP_IMGFLAG_ALLOCATED;
|
||||
}
|
||||
|
||||
mp_image_t* alloc_mpi(int w, int h, unsigned long int fmt) {
|
||||
mp_image_t* mpi = new_mp_image(w,h);
|
||||
|
||||
mpi->width=FFALIGN(w, MP_STRIDE_ALIGNMENT);
|
||||
mp_image_setfmt(mpi,fmt);
|
||||
mp_image_alloc_planes(mpi);
|
||||
mpi->width=w;
|
||||
mp_image_setfmt(mpi,fmt); // reset chroma size
|
||||
|
||||
return mpi;
|
||||
}
|
||||
|
||||
void copy_mpi(mp_image_t *dmpi, mp_image_t *mpi) {
|
||||
void mp_image_copy(struct mp_image *dmpi, struct mp_image *mpi)
|
||||
{
|
||||
if(mpi->flags&MP_IMGFLAG_PLANAR){
|
||||
memcpy_pic(dmpi->planes[0],mpi->planes[0], MP_IMAGE_BYTES_PER_ROW_ON_PLANE(mpi, 0), mpi->h,
|
||||
dmpi->stride[0],mpi->stride[0]);
|
||||
|
@ -102,6 +156,11 @@ void copy_mpi(mp_image_t *dmpi, mp_image_t *mpi) {
|
|||
}
|
||||
}
|
||||
|
||||
void mp_image_copy_attributes(struct mp_image *dmpi, struct mp_image *mpi)
|
||||
{
|
||||
vf_clone_mpi_attributes(dmpi, mpi);
|
||||
}
|
||||
|
||||
void mp_image_setfmt(mp_image_t* mpi,unsigned int out_fmt){
|
||||
mpi->flags&=~(MP_IMGFLAG_PLANAR|MP_IMGFLAG_YUV|MP_IMGFLAG_SWAPPED);
|
||||
mpi->imgfmt=out_fmt;
|
||||
|
@ -231,7 +290,11 @@ static int mp_image_destructor(void *ptr)
|
|||
{
|
||||
mp_image_t *mpi = ptr;
|
||||
|
||||
if(mpi->flags&MP_IMGFLAG_ALLOCATED){
|
||||
if (mpi->refcount) {
|
||||
m_refcount_unref(mpi->refcount);
|
||||
}
|
||||
|
||||
if (mpi->flags & MP_IMGFLAG_ALLOCATED) {
|
||||
/* because we allocate the whole image at once */
|
||||
av_free(mpi->planes[0]);
|
||||
if (mpi->flags & MP_IMGFLAG_RGB_PALETTE)
|
||||
|
@ -241,16 +304,155 @@ static int mp_image_destructor(void *ptr)
|
|||
return 0;
|
||||
}
|
||||
|
||||
mp_image_t* new_mp_image(int w,int h){
|
||||
mp_image_t* mpi = talloc_zero(NULL, mp_image_t);
|
||||
// Image without format or allocated image data
|
||||
struct mp_image *mp_image_new_empty(int w, int h)
|
||||
{
|
||||
struct mp_image *mpi = talloc_zero(NULL, struct mp_image);
|
||||
talloc_set_destructor(mpi, mp_image_destructor);
|
||||
mpi->width=mpi->w=w;
|
||||
mpi->height=mpi->h=h;
|
||||
return mpi;
|
||||
}
|
||||
|
||||
void free_mp_image(mp_image_t* mpi){
|
||||
talloc_free(mpi);
|
||||
struct mp_image *mp_image_alloc(unsigned int imgfmt, int w, int h)
|
||||
{
|
||||
struct mp_image *mpi = mp_image_new_empty(w, h);
|
||||
|
||||
mpi->width = FFALIGN(w, MP_STRIDE_ALIGNMENT);
|
||||
mp_image_setfmt(mpi, imgfmt);
|
||||
mp_image_alloc_planes(mpi);
|
||||
mpi->width = w;
|
||||
mp_image_setfmt(mpi, imgfmt); // reset chroma size
|
||||
|
||||
mpi->flags &= ~MP_IMGFLAG_ALLOCATED;
|
||||
mpi->refcount = m_refcount_new();
|
||||
mpi->refcount->free = av_free;
|
||||
mpi->refcount->arg = mpi->planes[0];
|
||||
// NOTE: palette isn't free'd. Palette handling should be fixed instead.
|
||||
|
||||
return mpi;
|
||||
}
|
||||
|
||||
struct mp_image *mp_image_new_copy(struct mp_image *img)
|
||||
{
|
||||
struct mp_image *new = mp_image_alloc(img->imgfmt, img->w, img->h);
|
||||
mp_image_copy(new, img);
|
||||
mp_image_copy_attributes(new, img);
|
||||
|
||||
// Normally these are covered by the reference to the original image data
|
||||
// (like the AVFrame in vd_lavc.c), but we can't manage it on our own.
|
||||
new->qscale = NULL;
|
||||
new->qstride = 0;
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
// Make dst take over the image data of src, and free src.
|
||||
// This is basically a safe version of *dst = *src; free(src);
|
||||
// Only works with ref-counted images, and can't change image size/format.
|
||||
void mp_image_steal_data(struct mp_image *dst, struct mp_image *src)
|
||||
{
|
||||
assert(dst->imgfmt == src->imgfmt && dst->w == src->w && dst->h == src->h);
|
||||
assert(dst->refcount && src->refcount);
|
||||
|
||||
for (int p = 0; p < MP_MAX_PLANES; p++) {
|
||||
dst->planes[p] = src->planes[p];
|
||||
dst->stride[p] = src->stride[p];
|
||||
}
|
||||
mp_image_copy_attributes(dst, src);
|
||||
|
||||
m_refcount_unref(dst->refcount);
|
||||
dst->refcount = src->refcount;
|
||||
talloc_set_destructor(src, NULL);
|
||||
talloc_free(src);
|
||||
}
|
||||
|
||||
// Return a new reference to img. The returned reference is owned by the caller,
|
||||
// while img is left untouched.
|
||||
struct mp_image *mp_image_new_ref(struct mp_image *img)
|
||||
{
|
||||
if (!img->refcount)
|
||||
return mp_image_new_copy(img);
|
||||
|
||||
struct mp_image *new = talloc_ptrtype(NULL, new);
|
||||
talloc_set_destructor(new, mp_image_destructor);
|
||||
*new = *img;
|
||||
|
||||
m_refcount_ref(new->refcount);
|
||||
return new;
|
||||
}
|
||||
|
||||
// Return a reference counted reference to img. If the reference count reaches
|
||||
// 0, call free(free_arg). The data passed by img must not be free'd before
|
||||
// that. The new reference will be writeable.
|
||||
struct mp_image *mp_image_new_custom_ref(struct mp_image *img, void *free_arg,
|
||||
void (*free)(void *arg))
|
||||
{
|
||||
struct mp_image *new = talloc_ptrtype(NULL, new);
|
||||
talloc_set_destructor(new, mp_image_destructor);
|
||||
*new = *img;
|
||||
|
||||
new->flags &= ~MP_IMGFLAG_ALLOCATED;
|
||||
new->refcount = m_refcount_new();
|
||||
new->refcount->free = free;
|
||||
new->refcount->arg = free_arg;
|
||||
return new;
|
||||
}
|
||||
|
||||
// Return a reference counted reference to img. ref/unref/is_unique are used to
|
||||
// connect to an external refcounting API. It is assumed that the new object
|
||||
// has an initial reference to that external API.
|
||||
struct mp_image *mp_image_new_external_ref(struct mp_image *img, void *arg,
|
||||
void (*ref)(void *arg),
|
||||
void (*unref)(void *arg),
|
||||
bool (*is_unique)(void *arg))
|
||||
{
|
||||
struct mp_image *new = talloc_ptrtype(NULL, new);
|
||||
talloc_set_destructor(new, mp_image_destructor);
|
||||
*new = *img;
|
||||
|
||||
new->flags &= ~MP_IMGFLAG_ALLOCATED;
|
||||
new->refcount = m_refcount_new();
|
||||
new->refcount->ext_ref = ref;
|
||||
new->refcount->ext_unref = unref;
|
||||
new->refcount->ext_is_unique = is_unique;
|
||||
new->refcount->arg = arg;
|
||||
return new;
|
||||
}
|
||||
|
||||
bool mp_image_is_writeable(struct mp_image *img)
|
||||
{
|
||||
// if non ref-counted, it's writeable if the caller allocated the image
|
||||
if (!img->refcount)
|
||||
return img->flags & MP_IMGFLAG_ALLOCATED;
|
||||
return m_refcount_is_unique(img->refcount);
|
||||
}
|
||||
|
||||
// Make the image data referenced by img writeable. This allocates new data
|
||||
// if the data wasn't already writeable, and img->planes[] and img->stride[]
|
||||
// will be set to the copy.
|
||||
void mp_image_make_writeable(struct mp_image *img)
|
||||
{
|
||||
if (mp_image_is_writeable(img))
|
||||
return;
|
||||
|
||||
mp_image_steal_data(img, mp_image_new_copy(img));
|
||||
assert(mp_image_is_writeable(img));
|
||||
}
|
||||
|
||||
void mp_image_setrefp(struct mp_image **p_img, struct mp_image *new_value)
|
||||
{
|
||||
if (*p_img != new_value) {
|
||||
talloc_free(*p_img);
|
||||
*p_img = new_value ? mp_image_new_ref(new_value) : NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Mere helper function (mp_image can be directly free'd with talloc_free)
|
||||
void mp_image_unrefp(struct mp_image **p_img)
|
||||
{
|
||||
talloc_free(*p_img);
|
||||
*p_img = NULL;
|
||||
}
|
||||
|
||||
enum mp_csp mp_image_csp(struct mp_image *img)
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#ifndef MPLAYER_MP_IMAGE_H
|
||||
#define MPLAYER_MP_IMAGE_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
@ -104,10 +105,25 @@
|
|||
#define MP_IMGFIELD_BOTTOM 0x10
|
||||
#define MP_IMGFIELD_INTERLACED 0x20
|
||||
|
||||
/* Memory management:
|
||||
* - mp_image is a light-weight reference to the actual image data (pixels).
|
||||
* The actual image data is reference counted and can outlive mp_image
|
||||
* allocations. mp_image references can be created with mp_image_new_ref()
|
||||
* and free'd with talloc_free() (the helpers mp_image_setrefp() and
|
||||
* mp_image_unrefp() can also be used). The actual image data is free'd when
|
||||
* the last mp_image reference to it is free'd.
|
||||
* - Each mp_image has a clear owner. The owner can do anything with it, such
|
||||
* as changing mp_image fields. Instead of making ownership ambiguous by
|
||||
* sharing a mp_image reference, new references should be created.
|
||||
* - Write access to the actual image data is allowed only after calling
|
||||
* mp_image_make_writeable(), or if mp_image_is_writeable() returns true.
|
||||
* Conceptually, images can be changed by their owner only, and copy-on-write
|
||||
* is used to ensure that other references do not see any changes to the
|
||||
* image data. mp_image_make_writeable() will do that copy if required.
|
||||
*/
|
||||
typedef struct mp_image {
|
||||
unsigned int flags;
|
||||
unsigned char type;
|
||||
int number;
|
||||
unsigned char bpp; // bits/pixel. NOT depth! for RGB it will be n*8
|
||||
unsigned int imgfmt;
|
||||
int width,height; // internal to vf.c, do not use (stored dimensions)
|
||||
|
@ -128,18 +144,40 @@ typedef struct mp_image {
|
|||
int chroma_y_shift; // vertical
|
||||
enum mp_csp colorspace;
|
||||
enum mp_csp_levels levels;
|
||||
int usage_count;
|
||||
/* memory management */
|
||||
int number, usage_count; // used by old VF/DR and vdpau code only
|
||||
struct m_refcount *refcount;
|
||||
/* for private use by filter or vo driver (to store buffer id or dmpi) */
|
||||
void* priv;
|
||||
} mp_image_t;
|
||||
|
||||
void mp_image_setfmt(mp_image_t* mpi,unsigned int out_fmt);
|
||||
mp_image_t* new_mp_image(int w,int h);
|
||||
void free_mp_image(mp_image_t* mpi);
|
||||
#define alloc_mpi(w, h, fmt) mp_image_alloc(fmt, w, h)
|
||||
#define free_mp_image talloc_free
|
||||
#define new_mp_image mp_image_new_empty
|
||||
#define copy_mpi mp_image_copy
|
||||
|
||||
mp_image_t* alloc_mpi(int w, int h, unsigned long int fmt);
|
||||
void mp_image_alloc_planes(mp_image_t *mpi);
|
||||
void copy_mpi(mp_image_t *dmpi, mp_image_t *mpi);
|
||||
struct mp_image *mp_image_alloc(unsigned int fmt, int w, int h);
|
||||
void mp_image_copy(struct mp_image *dmpi, struct mp_image *mpi);
|
||||
void mp_image_copy_attributes(struct mp_image *dmpi, struct mp_image *mpi);
|
||||
struct mp_image *mp_image_new_copy(struct mp_image *img);
|
||||
struct mp_image *mp_image_new_ref(struct mp_image *img);
|
||||
bool mp_image_is_writeable(struct mp_image *img);
|
||||
void mp_image_make_writeable(struct mp_image *img);
|
||||
void mp_image_setrefp(struct mp_image **p_img, struct mp_image *new_value);
|
||||
void mp_image_unrefp(struct mp_image **p_img);
|
||||
|
||||
struct mp_image *mp_image_new_empty(int w, int h);
|
||||
void mp_image_setfmt(mp_image_t* mpi,unsigned int out_fmt);
|
||||
void mp_image_alloc_planes(struct mp_image *mpi);
|
||||
void mp_image_steal_data(struct mp_image *dst, struct mp_image *src);
|
||||
|
||||
struct mp_image *mp_image_new_custom_ref(struct mp_image *img, void *arg,
|
||||
void (*free)(void *arg));
|
||||
|
||||
struct mp_image *mp_image_new_external_ref(struct mp_image *img, void *arg,
|
||||
void (*ref)(void *arg),
|
||||
void (*unref)(void *arg),
|
||||
bool (*is_unique)(void *arg));
|
||||
|
||||
enum mp_csp mp_image_csp(struct mp_image *img);
|
||||
enum mp_csp_levels mp_image_levels(struct mp_image *img);
|
||||
|
|
Loading…
Reference in New Issue