summaryrefslogtreecommitdiffstats
path: root/video
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2012-12-12 00:43:36 +0100
committerwm4 <wm4@nowhere>2013-01-13 17:39:32 +0100
commit65a0b5fdc609dac739f6ad1681f845b78589777b (patch)
treefa3d53ceddd6efd34a74ceb61454c21b6bf3a53b /video
parenta8e69707f71f334daa4cfa461d88db9bc8e7fc7c (diff)
downloadmpv-65a0b5fdc609dac739f6ad1681f845b78589777b.tar.bz2
mpv-65a0b5fdc609dac739f6ad1681f845b78589777b.tar.xz
mp_image: refcounting helpers
Diffstat (limited to 'video')
-rw-r--r--video/mp_image.c238
-rw-r--r--video/mp_image.h52
2 files changed, 265 insertions, 25 deletions
diff --git a/video/mp_image.c b/video/mp_image.c
index e4e862208b..1de8abaed5 100644
--- a/video/mp_image.c
+++ b/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)
diff --git a/video/mp_image.h b/video/mp_image.h
index 69e9c87c19..c57d0a63e9 100644
--- a/video/mp_image.h
+++ b/video/mp_image.h
@@ -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;
+#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
+
+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);
-mp_image_t* new_mp_image(int w,int h);
-void free_mp_image(mp_image_t* mpi);
+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));
-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_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);