summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2017-07-23 09:31:27 +0200
committerwm4 <wm4@nowhere>2017-07-23 09:31:27 +0200
commit9e7665b21b530cbbfeb187521dc9db78c2ca60db (patch)
tree1aa5b516a45c51cfaed1e59f200e8a1bbd8ba455
parent0ce3dce03aaea3e777ebf68504d5afb3f5e3f9e1 (diff)
downloadmpv-9e7665b21b530cbbfeb187521dc9db78c2ca60db.tar.bz2
mpv-9e7665b21b530cbbfeb187521dc9db78c2ca60db.tar.xz
mp_image: expose some image allocation code as helpers, refactor
Refactor the image allocation code, and expose part of it as helper code. This aims towards allowing callers to easily allocate mp_image references from custom-allocated linear buffers. This is exposing only as much as what should be actually required.
-rw-r--r--video/mp_image.c143
-rw-r--r--video/mp_image.h6
2 files changed, 129 insertions, 20 deletions
diff --git a/video/mp_image.c b/video/mp_image.c
index 281376b5f1..d4de39fe58 100644
--- a/video/mp_image.c
+++ b/video/mp_image.c
@@ -41,42 +41,145 @@
#define HAVE_OPAQUE_REF (LIBAVUTIL_VERSION_MICRO >= 100 && \
LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(55, 47, 100))
-static bool mp_image_alloc_planes(struct mp_image *mpi)
+// Determine strides, plane sizes, and total required size for an image
+// allocation. Returns total size on success, <0 on error. Unused planes
+// have out_stride/out_plane_size to 0, and out_plane_offset set to -1 up
+// until MP_MAX_PLANES-1.
+static int mp_image_layout(int imgfmt, int w, int h, int stride_align,
+ int out_stride[MP_MAX_PLANES],
+ int out_plane_offset[MP_MAX_PLANES],
+ int out_plane_size[MP_MAX_PLANES])
{
- assert(!mpi->planes[0]);
- assert(!mpi->bufs[0]);
+ struct mp_imgfmt_desc desc = mp_imgfmt_get_desc(imgfmt);
+ struct mp_image_params params = {.imgfmt = imgfmt, .w = w, .h = h};
- if (!mp_image_params_valid(&mpi->params) || mpi->fmt.flags & MP_IMGFLAG_HWACCEL)
- return false;
+ if (!mp_image_params_valid(&params) || desc.flags & MP_IMGFLAG_HWACCEL)
+ return -1;
// Note: for non-mod-2 4:2:0 YUV frames, we have to allocate an additional
// top/right border. This is needed for correct handling of such
// images in filter and VO code (e.g. vo_vdpau or vo_opengl).
- size_t plane_size[MP_MAX_PLANES];
for (int n = 0; n < MP_MAX_PLANES; n++) {
- int alloc_h = MP_ALIGN_UP(mpi->h, 32) >> mpi->fmt.ys[n];
- int line_bytes = (mp_image_plane_w(mpi, n) * mpi->fmt.bpp[n] + 7) / 8;
- mpi->stride[n] = FFALIGN(line_bytes, SWS_MIN_BYTE_ALIGN);
- plane_size[n] = mpi->stride[n] * alloc_h;
+ int alloc_w = mp_chroma_div_up(w, desc.xs[n]);
+ int alloc_h = MP_ALIGN_UP(h, 32) >> desc.ys[n];
+ int line_bytes = (alloc_w * desc.bpp[n] + 7) / 8;
+ out_stride[n] = MP_ALIGN_UP(line_bytes, stride_align);
+ out_plane_size[n] = out_stride[n] * alloc_h;
+ }
+ if (desc.flags & MP_IMGFLAG_PAL)
+ out_plane_size[1] = MP_PALETTE_SIZE;
+
+ int sum = 0;
+ for (int n = 0; n < MP_MAX_PLANES; n++) {
+ out_plane_offset[n] = out_plane_size[n] ? sum : -1;
+ sum += out_plane_size[n];
}
- if (mpi->fmt.flags & MP_IMGFLAG_PAL)
- plane_size[1] = MP_PALETTE_SIZE;
- size_t sum = 0;
- for (int n = 0; n < MP_MAX_PLANES; n++)
- sum += plane_size[n];
+ return sum;
+}
+
+// Return the total size needed for an image allocation of the given
+// configuration (imgfmt, w, h must be set). Returns -1 on error.
+// Assumes the allocation is already aligned on stride_align (otherwise you
+// need to add padding yourself).
+int mp_image_get_alloc_size(int imgfmt, int w, int h, int stride_align)
+{
+ int stride[MP_MAX_PLANES];
+ int plane_offset[MP_MAX_PLANES];
+ int plane_size[MP_MAX_PLANES];
+ return mp_image_layout(imgfmt, w, h, stride_align, stride, plane_offset,
+ plane_size);
+}
+
+// Fill the mpi->planes and mpi->stride fields of the given mpi with data
+// from buffer according to the mpi's w/h/imgfmt fields. See mp_image_from_buffer
+// aboud remarks how to allocate/use buffer/buffer_size.
+// This does not free the data. You are expected to setup refcounting by
+// setting mp_image.bufs before or after this function is called.
+// Returns true on success, false on failure.
+static bool mp_image_fill_alloc(struct mp_image *mpi, int stride_align,
+ void *buffer, int buffer_size)
+{
+ int stride[MP_MAX_PLANES];
+ int plane_offset[MP_MAX_PLANES];
+ int plane_size[MP_MAX_PLANES];
+ int size = mp_image_layout(mpi->imgfmt, mpi->w, mpi->h, stride_align,
+ stride, plane_offset, plane_size);
+ if (size < 0 || size > buffer_size)
+ return false;
+
+ int align = MP_ALIGN_UP((uintptr_t)buffer, stride_align) - (uintptr_t)buffer;
+ if (buffer_size - size < align)
+ return false;
+ uint8_t *s = buffer;
+ s += align;
+
+ for (int n = 0; n < MP_MAX_PLANES; n++) {
+ mpi->planes[n] = plane_offset[n] >= 0 ? s + plane_offset[n] : NULL;
+ mpi->stride[n] = stride[n];
+ }
+
+ return true;
+}
+
+// Create a mp_image from the provided buffer. The mp_image is filled according
+// to the imgfmt/w/h parameters, and respecting the stride_align parameter to
+// align the plane start pointers and strides. Once the last reference to the
+// returned image is destroyed, free(free_opaque, buffer) is called. (Be aware
+// that this can happen from any thread.)
+// The allocated size of buffer must be given by buffer_size. buffer_size should
+// be at least the value returned by mp_image_get_alloc_size(). If buffer is not
+// already aligned to stride_align, the function will attempt to align the
+// pointer itself by incrementing the buffer pointer until ther alignment is
+// achieved (if buffer_size is not large enough to allow aligning the buffer
+// safely, the function fails). To be safe, you may want to overallocate the
+// buffer by stride_align bytes, and include the overallocation in buffer_size.
+// Returns NULL on failure. On failure, the free() callback is not called.
+struct mp_image *mp_image_from_buffer(int imgfmt, int w, int h, int stride_align,
+ uint8_t *buffer, int buffer_size,
+ void *free_opaque,
+ void (*free)(void *opaque, uint8_t *data))
+{
+ struct mp_image *mpi = mp_image_new_dummy_ref(NULL);
+ mp_image_setfmt(mpi, imgfmt);
+ mp_image_set_size(mpi, w, h);
+
+ if (!mp_image_fill_alloc(mpi, stride_align, buffer, buffer_size))
+ goto fail;
+
+ mpi->bufs[0] = av_buffer_create(buffer, buffer_size, free, free_opaque, 0);
+ if (!mpi->bufs[0])
+ goto fail;
+
+ return mpi;
+
+fail:
+ talloc_free(mpi);
+ return NULL;
+}
+
+static bool mp_image_alloc_planes(struct mp_image *mpi)
+{
+ assert(!mpi->planes[0]);
+ assert(!mpi->bufs[0]);
+
+ int align = SWS_MIN_BYTE_ALIGN;
+
+ int size = mp_image_get_alloc_size(mpi->imgfmt, mpi->w, mpi->h, align);
+ if (size < 0)
+ return false;
// Note: mp_image_pool assumes this creates only 1 AVBufferRef.
- mpi->bufs[0] = av_buffer_alloc(FFMAX(sum, 1));
+ mpi->bufs[0] = av_buffer_alloc(size + align);
if (!mpi->bufs[0])
return false;
- uint8_t *data = mpi->bufs[0]->data;
- for (int n = 0; n < MP_MAX_PLANES; n++) {
- mpi->planes[n] = plane_size[n] ? data : NULL;
- data += plane_size[n];
+ if (!mp_image_fill_alloc(mpi, align, mpi->bufs[0]->data, mpi->bufs[0]->size)) {
+ av_buffer_unref(&mpi->bufs[0]);
+ return false;
}
+
return true;
}
diff --git a/video/mp_image.h b/video/mp_image.h
index 56ce2257f4..e949b9b81f 100644
--- a/video/mp_image.h
+++ b/video/mp_image.h
@@ -104,6 +104,12 @@ typedef struct mp_image {
int mp_chroma_div_up(int size, int shift);
+int mp_image_get_alloc_size(int imgfmt, int w, int h, int stride_align);
+struct mp_image *mp_image_from_buffer(int imgfmt, int w, int h, int stride_align,
+ uint8_t *buffer, int buffer_size,
+ void *free_opaque,
+ void (*free)(void *opaque, uint8_t *data));
+
struct mp_image *mp_image_alloc(int fmt, int w, int h);
void mp_image_copy(struct mp_image *dmpi, struct mp_image *mpi);
void mp_image_copy_gpu(struct mp_image *dst, struct mp_image *src);