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:
wm4 2017-07-23 09:31:27 +02:00
parent 0ce3dce03a
commit 9e7665b21b
2 changed files with 129 additions and 20 deletions

View File

@ -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 (mpi->fmt.flags & MP_IMGFLAG_PAL)
plane_size[1] = MP_PALETTE_SIZE;
if (desc.flags & MP_IMGFLAG_PAL)
out_plane_size[1] = MP_PALETTE_SIZE;
size_t sum = 0;
for (int n = 0; n < MP_MAX_PLANES; n++)
sum += plane_size[n];
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];
}
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;
}

View File

@ -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);