mirror of https://github.com/mpv-player/mpv
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.
This commit is contained in:
parent
0ce3dce03a
commit
9e7665b21b
143
video/mp_image.c
143
video/mp_image.c
|
@ -41,42 +41,145 @@
|
||||||
#define HAVE_OPAQUE_REF (LIBAVUTIL_VERSION_MICRO >= 100 && \
|
#define HAVE_OPAQUE_REF (LIBAVUTIL_VERSION_MICRO >= 100 && \
|
||||||
LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(55, 47, 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]);
|
struct mp_imgfmt_desc desc = mp_imgfmt_get_desc(imgfmt);
|
||||||
assert(!mpi->bufs[0]);
|
struct mp_image_params params = {.imgfmt = imgfmt, .w = w, .h = h};
|
||||||
|
|
||||||
if (!mp_image_params_valid(&mpi->params) || mpi->fmt.flags & MP_IMGFLAG_HWACCEL)
|
if (!mp_image_params_valid(¶ms) || desc.flags & MP_IMGFLAG_HWACCEL)
|
||||||
return false;
|
return -1;
|
||||||
|
|
||||||
// Note: for non-mod-2 4:2:0 YUV frames, we have to allocate an additional
|
// 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
|
// top/right border. This is needed for correct handling of such
|
||||||
// images in filter and VO code (e.g. vo_vdpau or vo_opengl).
|
// 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++) {
|
for (int n = 0; n < MP_MAX_PLANES; n++) {
|
||||||
int alloc_h = MP_ALIGN_UP(mpi->h, 32) >> mpi->fmt.ys[n];
|
int alloc_w = mp_chroma_div_up(w, desc.xs[n]);
|
||||||
int line_bytes = (mp_image_plane_w(mpi, n) * mpi->fmt.bpp[n] + 7) / 8;
|
int alloc_h = MP_ALIGN_UP(h, 32) >> desc.ys[n];
|
||||||
mpi->stride[n] = FFALIGN(line_bytes, SWS_MIN_BYTE_ALIGN);
|
int line_bytes = (alloc_w * desc.bpp[n] + 7) / 8;
|
||||||
plane_size[n] = mpi->stride[n] * alloc_h;
|
out_stride[n] = MP_ALIGN_UP(line_bytes, stride_align);
|
||||||
|
out_plane_size[n] = out_stride[n] * alloc_h;
|
||||||
}
|
}
|
||||||
if (mpi->fmt.flags & MP_IMGFLAG_PAL)
|
if (desc.flags & MP_IMGFLAG_PAL)
|
||||||
plane_size[1] = MP_PALETTE_SIZE;
|
out_plane_size[1] = MP_PALETTE_SIZE;
|
||||||
|
|
||||||
size_t sum = 0;
|
int sum = 0;
|
||||||
for (int n = 0; n < MP_MAX_PLANES; n++)
|
for (int n = 0; n < MP_MAX_PLANES; n++) {
|
||||||
sum += plane_size[n];
|
out_plane_offset[n] = out_plane_size[n] ? sum : -1;
|
||||||
|
sum += out_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.
|
// 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])
|
if (!mpi->bufs[0])
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
uint8_t *data = mpi->bufs[0]->data;
|
if (!mp_image_fill_alloc(mpi, align, mpi->bufs[0]->data, mpi->bufs[0]->size)) {
|
||||||
for (int n = 0; n < MP_MAX_PLANES; n++) {
|
av_buffer_unref(&mpi->bufs[0]);
|
||||||
mpi->planes[n] = plane_size[n] ? data : NULL;
|
return false;
|
||||||
data += plane_size[n];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -104,6 +104,12 @@ typedef struct mp_image {
|
||||||
|
|
||||||
int mp_chroma_div_up(int size, int shift);
|
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);
|
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(struct mp_image *dmpi, struct mp_image *mpi);
|
||||||
void mp_image_copy_gpu(struct mp_image *dst, struct mp_image *src);
|
void mp_image_copy_gpu(struct mp_image *dst, struct mp_image *src);
|
||||||
|
|
Loading…
Reference in New Issue