gstreamer/gst-libs/gst/vaapi/gstvaapiimage.c
Wind Yuan 89321cd3c4 image: add support for raw YUY2/UYVY image copies.
Implement raw image copies for YUY2 format. Add support for UYVY format
too, with the same copy function as for YUY2. Even though components
ordering differs, copying line strides is essentially the same.

https://bugzilla.gnome.org/show_bug.cgi?id=703939
https://bugzilla.gnome.org/show_bug.cgi?id=703940

Signed-off-by: Gwenole Beauchesne <gwenole.beauchesne@intel.com>
2013-07-11 19:09:53 +02:00

1118 lines
29 KiB
C

/*
* gstvaapiimage.c - VA image abstraction
*
* Copyright (C) 2010-2011 Splitted-Desktop Systems
* Copyright (C) 2011-2012 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
/**
* SECTION:gstvaapiimage
* @short_description: VA image abstraction
*/
#include "sysdeps.h"
#include <string.h>
#include "gstvaapicompat.h"
#include "gstvaapiutils.h"
#include "gstvaapiimage.h"
#include "gstvaapiobject_priv.h"
#define DEBUG 1
#include "gstvaapidebug.h"
typedef struct _GstVaapiImageClass GstVaapiImageClass;
/**
* GstVaapiImage:
*
* A VA image wrapper
*/
struct _GstVaapiImage {
/*< private >*/
GstVaapiObject parent_instance;
VAImage internal_image;
VAImage image;
guchar *image_data;
GstVideoFormat internal_format;
GstVideoFormat format;
guint width;
guint height;
guint is_linear : 1;
};
/**
* GstVaapiImageClass:
*
* A VA image wrapper class
*/
struct _GstVaapiImageClass {
/*< private >*/
GstVaapiObjectClass parent_class;
};
#define SWAP_UINT(a, b) do { \
guint v = a; \
a = b; \
b = v; \
} while (0)
static gboolean
_gst_vaapi_image_map(GstVaapiImage *image, GstVaapiImageRaw *raw_image);
static gboolean
_gst_vaapi_image_unmap(GstVaapiImage *image);
static gboolean
_gst_vaapi_image_set_image(GstVaapiImage *image, const VAImage *va_image);
/*
* VAImage wrapper
*/
static gboolean
vaapi_image_is_linear(const VAImage *va_image)
{
guint i, width, height, width2, height2, data_size;
for (i = 1; i < va_image->num_planes; i++)
if (va_image->offsets[i] < va_image->offsets[i - 1])
return FALSE;
width = va_image->width;
height = va_image->height;
width2 = (width + 1) / 2;
height2 = (height + 1) / 2;
switch (va_image->format.fourcc) {
case VA_FOURCC('N','V','1','2'):
case VA_FOURCC('Y','V','1','2'):
case VA_FOURCC('I','4','2','0'):
data_size = width * height + 2 * width2 * height2;
break;
case VA_FOURCC('Y','U','Y','2'):
case VA_FOURCC('U','Y','V','Y'):
data_size = 2 * width * height;
break;
case VA_FOURCC('Y','8','0','0'):
data_size = width * height;
break;
case VA_FOURCC('A','Y','U','V'):
case VA_FOURCC('A','R','G','B'):
case VA_FOURCC('R','G','B','A'):
case VA_FOURCC('A','B','G','R'):
case VA_FOURCC('B','G','R','A'):
case VA_FOURCC('X','R','G','B'):
case VA_FOURCC('R','G','B','X'):
case VA_FOURCC('X','B','G','R'):
case VA_FOURCC('B','G','R','X'):
data_size = 4 * width * height;
break;
default:
g_error("FIXME: incomplete formats %" GST_FOURCC_FORMAT,
GST_FOURCC_ARGS(va_image->format.fourcc));
break;
}
return va_image->data_size == data_size;
}
static void
gst_vaapi_image_destroy(GstVaapiImage *image)
{
GstVaapiDisplay * const display = GST_VAAPI_OBJECT_DISPLAY(image);
VAImageID image_id;
VAStatus status;
_gst_vaapi_image_unmap(image);
image_id = GST_VAAPI_OBJECT_ID(image);
GST_DEBUG("image %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS(image_id));
if (image_id != VA_INVALID_ID) {
GST_VAAPI_DISPLAY_LOCK(display);
status = vaDestroyImage(GST_VAAPI_DISPLAY_VADISPLAY(display), image_id);
GST_VAAPI_DISPLAY_UNLOCK(display);
if (!vaapi_check_status(status, "vaDestroyImage()"))
g_warning("failed to destroy image %" GST_VAAPI_ID_FORMAT,
GST_VAAPI_ID_ARGS(image_id));
GST_VAAPI_OBJECT_ID(image) = VA_INVALID_ID;
}
}
static gboolean
_gst_vaapi_image_create(GstVaapiImage *image, GstVideoFormat format)
{
GstVaapiDisplay * const display = GST_VAAPI_OBJECT_DISPLAY(image);
const VAImageFormat *va_format;
VAStatus status;
if (!gst_vaapi_display_has_image_format(display, format))
return FALSE;
va_format = gst_video_format_to_va_format(format);
if (!va_format)
return FALSE;
GST_VAAPI_DISPLAY_LOCK(display);
status = vaCreateImage(
GST_VAAPI_DISPLAY_VADISPLAY(display),
(VAImageFormat *)va_format,
image->width,
image->height,
&image->internal_image
);
GST_VAAPI_DISPLAY_UNLOCK(display);
if (status != VA_STATUS_SUCCESS ||
image->internal_image.format.fourcc != va_format->fourcc)
return FALSE;
image->internal_format = format;
return TRUE;
}
static gboolean
gst_vaapi_image_create(GstVaapiImage *image, GstVideoFormat format,
guint width, guint height)
{
const VAImageFormat *va_format;
VAImageID image_id;
image->format = format;
image->width = width;
image->height = height;
if (!_gst_vaapi_image_create(image, format)) {
switch (format) {
case GST_VIDEO_FORMAT_I420:
format = GST_VIDEO_FORMAT_YV12;
break;
case GST_VIDEO_FORMAT_YV12:
format = GST_VIDEO_FORMAT_I420;
break;
default:
format = 0;
break;
}
if (!format || !_gst_vaapi_image_create(image, format))
return FALSE;
}
image->image = image->internal_image;
image_id = image->image.image_id;
if (image->format != image->internal_format) {
switch (image->format) {
case GST_VIDEO_FORMAT_YV12:
case GST_VIDEO_FORMAT_I420:
va_format = gst_video_format_to_va_format(image->format);
if (!va_format)
return FALSE;
image->image.format = *va_format;
SWAP_UINT(image->image.offsets[1], image->image.offsets[2]);
SWAP_UINT(image->image.pitches[1], image->image.pitches[2]);
break;
default:
break;
}
}
image->is_linear = vaapi_image_is_linear(&image->image);
GST_DEBUG("image %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS(image_id));
GST_VAAPI_OBJECT_ID(image) = image_id;
return TRUE;
}
static void
gst_vaapi_image_init(GstVaapiImage *image)
{
image->internal_image.image_id = VA_INVALID_ID;
image->internal_image.buf = VA_INVALID_ID;
image->image.image_id = VA_INVALID_ID;
image->image.buf = VA_INVALID_ID;
}
static void
gst_vaapi_image_class_init(GstVaapiImageClass *klass)
{
GstVaapiObjectClass * const object_class =
GST_VAAPI_OBJECT_CLASS(klass);
object_class->init = (GstVaapiObjectInitFunc)gst_vaapi_image_init;
}
#define gst_vaapi_image_finalize gst_vaapi_image_destroy
GST_VAAPI_OBJECT_DEFINE_CLASS_WITH_CODE(
GstVaapiImage,
gst_vaapi_image,
gst_vaapi_image_class_init(&g_class))
/**
* gst_vaapi_image_new:
* @display: a #GstVaapiDisplay
* @format: a #GstVideoFormat
* @width: the requested image width
* @height: the requested image height
*
* Creates a new #GstVaapiImage with the specified format and
* dimensions.
*
* Return value: the newly allocated #GstVaapiImage object
*/
GstVaapiImage *
gst_vaapi_image_new(
GstVaapiDisplay *display,
GstVideoFormat format,
guint width,
guint height
)
{
GstVaapiImage *image;
g_return_val_if_fail(width > 0, NULL);
g_return_val_if_fail(height > 0, NULL);
GST_DEBUG("format %s, size %ux%u", gst_video_format_to_string(format),
width, height);
image = gst_vaapi_object_new(gst_vaapi_image_class(), display);
if (!image)
return NULL;
if (!gst_vaapi_image_create(image, format, width, height))
goto error;
return image;
error:
gst_vaapi_object_unref(image);
return NULL;
}
/**
* gst_vaapi_image_new_with_image:
* @display: a #GstVaapiDisplay
* @va_image: a VA image
*
* Creates a new #GstVaapiImage from a foreign VA image. The image
* format and dimensions will be extracted from @va_image. This
* function is mainly used by gst_vaapi_surface_derive_image() to bind
* a VA image to a #GstVaapiImage object.
*
* Return value: the newly allocated #GstVaapiImage object
*/
GstVaapiImage *
gst_vaapi_image_new_with_image(GstVaapiDisplay *display, VAImage *va_image)
{
GstVaapiImage *image;
g_return_val_if_fail(va_image, NULL);
g_return_val_if_fail(va_image->image_id != VA_INVALID_ID, NULL);
g_return_val_if_fail(va_image->buf != VA_INVALID_ID, NULL);
GST_DEBUG("VA image 0x%08x, format %" GST_FOURCC_FORMAT ", size %ux%u",
va_image->image_id,
GST_FOURCC_ARGS(va_image->format.fourcc),
va_image->width, va_image->height);
image = gst_vaapi_object_new(gst_vaapi_image_class(), display);
if (!image)
return NULL;
if (!_gst_vaapi_image_set_image(image, va_image))
goto error;
return image;
error:
gst_vaapi_object_unref(image);
return NULL;
}
/**
* gst_vaapi_image_get_id:
* @image: a #GstVaapiImage
*
* Returns the underlying VAImageID of the @image.
*
* Return value: the underlying VA image id
*/
GstVaapiID
gst_vaapi_image_get_id(GstVaapiImage *image)
{
g_return_val_if_fail(image != NULL, VA_INVALID_ID);
return GST_VAAPI_OBJECT_ID(image);
}
/**
* gst_vaapi_image_get_image:
* @image: a #GstVaapiImage
* @va_image: a VA image
*
* Fills @va_image with the VA image used internally.
*
* Return value: %TRUE on success
*/
gboolean
gst_vaapi_image_get_image(GstVaapiImage *image, VAImage *va_image)
{
g_return_val_if_fail(image != NULL, FALSE);
if (va_image)
*va_image = image->image;
return TRUE;
}
/*
* _gst_vaapi_image_set_image:
* @image: a #GstVaapiImage
* @va_image: a VA image
*
* Initializes #GstVaapiImage with a foreign VA image. This function
* will try to "linearize" the VA image. i.e. making sure that the VA
* image offsets into the data buffer are in increasing order with the
* number of planes available in the image.
*
* This is an internal function used by gst_vaapi_image_new_with_image().
*
* Return value: %TRUE on success
*/
gboolean
_gst_vaapi_image_set_image(GstVaapiImage *image, const VAImage *va_image)
{
GstVideoFormat format;
VAImage alt_va_image;
const VAImageFormat *alt_va_format;
format = gst_video_format_from_va_format(&va_image->format);
if (format == GST_VIDEO_FORMAT_UNKNOWN)
return FALSE;
image->internal_image = *va_image;
image->internal_format = format;
image->is_linear = vaapi_image_is_linear(va_image);
image->image = *va_image;
image->format = format;
image->width = va_image->width;
image->height = va_image->height;
GST_VAAPI_OBJECT_ID(image) = va_image->image_id;
/* Try to linearize image */
if (!image->is_linear) {
switch (format) {
case GST_VIDEO_FORMAT_I420:
format = GST_VIDEO_FORMAT_YV12;
break;
case GST_VIDEO_FORMAT_YV12:
format = GST_VIDEO_FORMAT_I420;
break;
default:
format = 0;
break;
}
if (format &&
(alt_va_format = gst_video_format_to_va_format(format))) {
alt_va_image = *va_image;
alt_va_image.format = *alt_va_format;
SWAP_UINT(alt_va_image.offsets[1], alt_va_image.offsets[2]);
SWAP_UINT(alt_va_image.pitches[1], alt_va_image.pitches[2]);
if (vaapi_image_is_linear(&alt_va_image)) {
image->image = alt_va_image;
image->format = format;
image->is_linear = TRUE;
GST_DEBUG("linearized image to %s format",
gst_video_format_to_string(format));
}
}
}
return TRUE;
}
/**
* gst_vaapi_image_get_format:
* @image: a #GstVaapiImage
*
* Returns the #GstVideoFormat the @image was created with.
*
* Return value: the #GstVideoFormat
*/
GstVideoFormat
gst_vaapi_image_get_format(GstVaapiImage *image)
{
g_return_val_if_fail(image != NULL, 0);
return image->format;
}
/**
* gst_vaapi_image_get_width:
* @image: a #GstVaapiImage
*
* Returns the @image width.
*
* Return value: the image width, in pixels
*/
guint
gst_vaapi_image_get_width(GstVaapiImage *image)
{
g_return_val_if_fail(image != NULL, 0);
return image->width;
}
/**
* gst_vaapi_image_get_height:
* @image: a #GstVaapiImage
*
* Returns the @image height.
*
* Return value: the image height, in pixels.
*/
guint
gst_vaapi_image_get_height(GstVaapiImage *image)
{
g_return_val_if_fail(image != NULL, 0);
return image->height;
}
/**
* gst_vaapi_image_get_size:
* @image: a #GstVaapiImage
* @pwidth: return location for the width, or %NULL
* @pheight: return location for the height, or %NULL
*
* Retrieves the dimensions of a #GstVaapiImage.
*/
void
gst_vaapi_image_get_size(GstVaapiImage *image, guint *pwidth, guint *pheight)
{
g_return_if_fail(image != NULL);
if (pwidth)
*pwidth = image->width;
if (pheight)
*pheight = image->height;
}
/**
* gst_vaapi_image_is_linear:
* @image: a #GstVaapiImage
*
* Checks whether the @image has data planes allocated from a single
* buffer and offsets into that buffer are in increasing order with
* the number of planes.
*
* Return value: %TRUE if image data planes are allocated from a single buffer
*/
gboolean
gst_vaapi_image_is_linear(GstVaapiImage *image)
{
g_return_val_if_fail(image != NULL, FALSE);
return image->is_linear;
}
/**
* gst_vaapi_image_is_mapped:
* @image: a #GstVaapiImage
*
* Checks whether the @image is currently mapped or not.
*
* Return value: %TRUE if the @image is mapped
*/
static inline gboolean
_gst_vaapi_image_is_mapped(GstVaapiImage *image)
{
return image->image_data != NULL;
}
gboolean
gst_vaapi_image_is_mapped(GstVaapiImage *image)
{
g_return_val_if_fail(image != NULL, FALSE);
return _gst_vaapi_image_is_mapped(image);
}
/**
* gst_vaapi_image_map:
* @image: a #GstVaapiImage
*
* Maps the image data buffer. The actual pixels are returned by the
* gst_vaapi_image_get_plane() function.
*
* Return value: %TRUE on success
*/
gboolean
gst_vaapi_image_map(GstVaapiImage *image)
{
g_return_val_if_fail(image != NULL, FALSE);
return _gst_vaapi_image_map(image, NULL);
}
gboolean
_gst_vaapi_image_map(GstVaapiImage *image, GstVaapiImageRaw *raw_image)
{
GstVaapiDisplay *display;
VAStatus status;
guint i;
if (_gst_vaapi_image_is_mapped(image))
goto map_success;
display = GST_VAAPI_OBJECT_DISPLAY(image);
if (!display)
return FALSE;
GST_VAAPI_DISPLAY_LOCK(display);
status = vaMapBuffer(
GST_VAAPI_DISPLAY_VADISPLAY(display),
image->image.buf,
(void **)&image->image_data
);
GST_VAAPI_DISPLAY_UNLOCK(display);
if (!vaapi_check_status(status, "vaMapBuffer()"))
return FALSE;
map_success:
if (raw_image) {
const VAImage * const va_image = &image->image;
raw_image->format = image->format;
raw_image->width = va_image->width;
raw_image->height = va_image->height;
raw_image->num_planes = va_image->num_planes;
for (i = 0; i < raw_image->num_planes; i++) {
raw_image->pixels[i] = (guchar *)image->image_data +
va_image->offsets[i];
raw_image->stride[i] = va_image->pitches[i];
}
}
return TRUE;
}
/**
* gst_vaapi_image_unmap:
* @image: a #GstVaapiImage
*
* Unmaps the image data buffer. Pointers to pixels returned by
* gst_vaapi_image_get_plane() are then no longer valid.
*
* Return value: %TRUE on success
*/
gboolean
gst_vaapi_image_unmap(GstVaapiImage *image)
{
g_return_val_if_fail(image != NULL, FALSE);
return _gst_vaapi_image_unmap(image);
}
gboolean
_gst_vaapi_image_unmap(GstVaapiImage *image)
{
GstVaapiDisplay *display;
VAStatus status;
if (!_gst_vaapi_image_is_mapped(image))
return TRUE;
display = GST_VAAPI_OBJECT_DISPLAY(image);
if (!display)
return FALSE;
GST_VAAPI_DISPLAY_LOCK(display);
status = vaUnmapBuffer(
GST_VAAPI_DISPLAY_VADISPLAY(display),
image->image.buf
);
GST_VAAPI_DISPLAY_UNLOCK(display);
if (!vaapi_check_status(status, "vaUnmapBuffer()"))
return FALSE;
image->image_data = NULL;
return TRUE;
}
/**
* gst_vaapi_image_get_plane_count:
* @image: a #GstVaapiImage
*
* Retrieves the number of planes available in the @image. The @image
* must be mapped for this function to work properly.
*
* Return value: the number of planes available in the @image
*/
guint
gst_vaapi_image_get_plane_count(GstVaapiImage *image)
{
g_return_val_if_fail(image != NULL, 0);
g_return_val_if_fail(_gst_vaapi_image_is_mapped(image), 0);
return image->image.num_planes;
}
/**
* gst_vaapi_image_get_plane:
* @image: a #GstVaapiImage
* @plane: the requested plane number
*
* Retrieves the pixels data to the specified @plane. The @image must
* be mapped for this function to work properly.
*
* Return value: the pixels data of the specified @plane
*/
guchar *
gst_vaapi_image_get_plane(GstVaapiImage *image, guint plane)
{
g_return_val_if_fail(image != NULL, NULL);
g_return_val_if_fail(_gst_vaapi_image_is_mapped(image), NULL);
g_return_val_if_fail(plane < image->image.num_planes, NULL);
return image->image_data + image->image.offsets[plane];
}
/**
* gst_vaapi_image_get_pitch:
* @image: a #GstVaapiImage
* @plane: the requested plane number
*
* Retrieves the line size (stride) of the specified @plane. The
* @image must be mapped for this function to work properly.
*
* Return value: the line size (stride) of the specified plane
*/
guint
gst_vaapi_image_get_pitch(GstVaapiImage *image, guint plane)
{
g_return_val_if_fail(image != NULL, 0);
g_return_val_if_fail(_gst_vaapi_image_is_mapped(image), 0);
g_return_val_if_fail(plane < image->image.num_planes, 0);
return image->image.pitches[plane];
}
/**
* gst_vaapi_image_get_data_size:
* @image: a #GstVaapiImage
*
* Retrieves the underlying image data size. This function could be
* used to determine whether the image has a compatible layout with
* another image structure.
*
* Return value: the whole image data size of the @image
*/
guint
gst_vaapi_image_get_data_size(GstVaapiImage *image)
{
g_return_val_if_fail(image != NULL, 0);
return image->image.data_size;
}
#if GST_CHECK_VERSION(1,0,0)
#include <gst/video/gstvideometa.h>
static gboolean
init_image_from_video_meta(GstVaapiImageRaw *raw_image, GstVideoMeta *vmeta)
{
GST_FIXME("map from GstVideoMeta + add fini_image_from_buffer()");
return FALSE;
}
static gboolean
init_image_from_buffer(GstVaapiImageRaw *raw_image, GstBuffer *buffer)
{
GstVideoMeta * const vmeta = gst_buffer_get_video_meta(buffer);
return vmeta ? init_image_from_video_meta(raw_image, vmeta) : FALSE;
}
#else
static gboolean
init_image_from_buffer(GstVaapiImageRaw *raw_image, GstBuffer *buffer)
{
GstCaps *caps;
guchar *data;
guint32 data_size;
GstVideoInfo vi;
guint i, frame_size;
data = GST_BUFFER_DATA(buffer);
data_size = GST_BUFFER_SIZE(buffer);
caps = GST_BUFFER_CAPS(buffer);
if (!caps)
return FALSE;
if (!gst_video_info_from_caps(&vi, caps))
goto error_unsupported_caps;
/* Check for compatible data size */
frame_size = GST_VIDEO_INFO_SIZE(&vi);
if (frame_size != data_size)
goto error_incompatible_size;
raw_image->format = GST_VIDEO_INFO_FORMAT(&vi);
raw_image->width = GST_VIDEO_INFO_WIDTH(&vi);
raw_image->height = GST_VIDEO_INFO_HEIGHT(&vi);
raw_image->num_planes = GST_VIDEO_INFO_N_PLANES(&vi);
for (i = 0; i < raw_image->num_planes; i++) {
raw_image->pixels[i] = data + GST_VIDEO_INFO_PLANE_OFFSET(&vi, i);
raw_image->stride[i] = GST_VIDEO_INFO_PLANE_STRIDE(&vi, i);
}
return TRUE;
/* ERRORS */
error_unsupported_caps:
GST_ERROR("unsupported caps %" GST_PTR_FORMAT, caps);
return FALSE;
error_incompatible_size:
GST_ERROR("incompatible frame size (%u) with buffer size (%u)",
frame_size, data_size);
return FALSE;
}
#endif
/* Copy N lines of an image */
static inline void
memcpy_pic(
guchar *dst,
guint dst_stride,
const guchar *src,
guint src_stride,
guint len,
guint height
)
{
guint i;
for (i = 0; i < height; i++) {
memcpy(dst, src, len);
dst += dst_stride;
src += src_stride;
}
}
/* Copy NV12 images */
static void
copy_image_NV12(
GstVaapiImageRaw *dst_image,
GstVaapiImageRaw *src_image,
const GstVaapiRectangle *rect
)
{
guchar *dst, *src;
guint dst_stride, src_stride;
/* Y plane */
dst_stride = dst_image->stride[0];
dst = dst_image->pixels[0] + rect->y * dst_stride + rect->x;
src_stride = src_image->stride[0];
src = src_image->pixels[0] + rect->y * src_stride + rect->x;
memcpy_pic(dst, dst_stride, src, src_stride, rect->width, rect->height);
/* UV plane */
dst_stride = dst_image->stride[1];
dst = dst_image->pixels[1] + (rect->y / 2) * dst_stride + (rect->x & -2);
src_stride = src_image->stride[1];
src = src_image->pixels[1] + (rect->y / 2) * src_stride + (rect->x & -2);
memcpy_pic(dst, dst_stride, src, src_stride, rect->width, rect->height / 2);
}
/* Copy YV12 images */
static void
copy_image_YV12(
GstVaapiImageRaw *dst_image,
GstVaapiImageRaw *src_image,
const GstVaapiRectangle *rect
)
{
guchar *dst, *src;
guint dst_stride, src_stride;
guint i, x, y, w, h;
/* Y plane */
dst_stride = dst_image->stride[0];
dst = dst_image->pixels[0] + rect->y * dst_stride + rect->x;
src_stride = src_image->stride[0];
src = src_image->pixels[0] + rect->y * src_stride + rect->x;
memcpy_pic(dst, dst_stride, src, src_stride, rect->width, rect->height);
/* U/V planes */
x = rect->x / 2;
y = rect->y / 2;
w = rect->width / 2;
h = rect->height / 2;
for (i = 1; i < dst_image->num_planes; i++) {
dst_stride = dst_image->stride[i];
dst = dst_image->pixels[i] + y * dst_stride + x;
src_stride = src_image->stride[i];
src = src_image->pixels[i] + y * src_stride + x;
memcpy_pic(dst, dst_stride, src, src_stride, w, h);
}
}
/* Copy YUY2 images */
static void
copy_image_YUY2(
GstVaapiImageRaw *dst_image,
GstVaapiImageRaw *src_image,
const GstVaapiRectangle *rect
)
{
guchar *dst, *src;
guint dst_stride, src_stride;
/* YUV 4:2:2, full vertical resolution */
dst_stride = dst_image->stride[0];
dst = dst_image->pixels[0] + rect->y * dst_stride + rect->x * 2;
src_stride = src_image->stride[0];
src = src_image->pixels[0] + rect->y * src_stride + rect->x * 2;
memcpy_pic(dst, dst_stride, src, src_stride, rect->width * 2, rect->height);
}
/* Copy RGBA images */
static void
copy_image_RGBA(
GstVaapiImageRaw *dst_image,
GstVaapiImageRaw *src_image,
const GstVaapiRectangle *rect
)
{
guchar *dst, *src;
guint dst_stride, src_stride;
dst_stride = dst_image->stride[0];
dst = dst_image->pixels[0] + rect->y * dst_stride + rect->x;
src_stride = src_image->stride[0];
src = src_image->pixels[0] + rect->y * src_stride + rect->x;
memcpy_pic(dst, dst_stride, src, src_stride, 4 * rect->width, rect->height);
}
static gboolean
copy_image(
GstVaapiImageRaw *dst_image,
GstVaapiImageRaw *src_image,
const GstVaapiRectangle *rect
)
{
GstVaapiRectangle default_rect;
if (dst_image->format != src_image->format ||
dst_image->width != src_image->width ||
dst_image->height != src_image->height)
return FALSE;
if (rect) {
if (rect->x >= src_image->width ||
rect->x + rect->width > src_image->width ||
rect->y >= src_image->height ||
rect->y + rect->height > src_image->height)
return FALSE;
}
else {
default_rect.x = 0;
default_rect.y = 0;
default_rect.width = src_image->width;
default_rect.height = src_image->height;
rect = &default_rect;
}
switch (dst_image->format) {
case GST_VIDEO_FORMAT_NV12:
copy_image_NV12(dst_image, src_image, rect);
break;
case GST_VIDEO_FORMAT_YV12:
case GST_VIDEO_FORMAT_I420:
copy_image_YV12(dst_image, src_image, rect);
break;
case GST_VIDEO_FORMAT_YUY2:
case GST_VIDEO_FORMAT_UYVY:
copy_image_YUY2(dst_image, src_image, rect);
break;
case GST_VIDEO_FORMAT_ARGB:
case GST_VIDEO_FORMAT_RGBA:
case GST_VIDEO_FORMAT_ABGR:
case GST_VIDEO_FORMAT_BGRA:
copy_image_RGBA(dst_image, src_image, rect);
break;
default:
GST_ERROR("unsupported image format for copy");
return FALSE;
}
return TRUE;
}
/**
* gst_vaapi_image_get_buffer:
* @image: a #GstVaapiImage
* @buffer: a #GstBuffer
* @rect: a #GstVaapiRectangle expressing a region, or %NULL for the
* whole image
*
* Transfers pixels data contained in the @image into the #GstBuffer.
* Both image structures shall have the same format.
*
* Return value: %TRUE on success
*/
gboolean
gst_vaapi_image_get_buffer(
GstVaapiImage *image,
GstBuffer *buffer,
GstVaapiRectangle *rect
)
{
GstVaapiImageRaw dst_image, src_image;
gboolean success;
g_return_val_if_fail(image != NULL, FALSE);
g_return_val_if_fail(GST_IS_BUFFER(buffer), FALSE);
if (!init_image_from_buffer(&dst_image, buffer))
return FALSE;
if (dst_image.format != image->format)
return FALSE;
if (dst_image.width != image->width || dst_image.height != image->height)
return FALSE;
if (!_gst_vaapi_image_map(image, &src_image))
return FALSE;
success = copy_image(&dst_image, &src_image, rect);
if (!_gst_vaapi_image_unmap(image))
return FALSE;
return success;
}
/**
* gst_vaapi_image_get_raw:
* @image: a #GstVaapiImage
* @dst_image: a #GstVaapiImageRaw
* @rect: a #GstVaapiRectangle expressing a region, or %NULL for the
* whole image
*
* Transfers pixels data contained in the @image into the #GstVaapiImageRaw.
* Both image structures shall have the same format.
*
* Return value: %TRUE on success
*/
gboolean
gst_vaapi_image_get_raw(
GstVaapiImage *image,
GstVaapiImageRaw *dst_image,
GstVaapiRectangle *rect
)
{
GstVaapiImageRaw src_image;
gboolean success;
g_return_val_if_fail(image != NULL, FALSE);
if (!_gst_vaapi_image_map(image, &src_image))
return FALSE;
success = copy_image(dst_image, &src_image, rect);
if (!_gst_vaapi_image_unmap(image))
return FALSE;
return success;
}
/**
* gst_vaapi_image_update_from_buffer:
* @image: a #GstVaapiImage
* @buffer: a #GstBuffer
* @rect: a #GstVaapiRectangle expressing a region, or %NULL for the
* whole image
*
* Transfers pixels data contained in the #GstBuffer into the
* @image. Both image structures shall have the same format.
*
* Return value: %TRUE on success
*/
gboolean
gst_vaapi_image_update_from_buffer(
GstVaapiImage *image,
GstBuffer *buffer,
GstVaapiRectangle *rect
)
{
GstVaapiImageRaw dst_image, src_image;
gboolean success;
g_return_val_if_fail(image != NULL, FALSE);
g_return_val_if_fail(GST_IS_BUFFER(buffer), FALSE);
if (!init_image_from_buffer(&src_image, buffer))
return FALSE;
if (src_image.format != image->format)
return FALSE;
if (src_image.width != image->width || src_image.height != image->height)
return FALSE;
if (!_gst_vaapi_image_map(image, &dst_image))
return FALSE;
success = copy_image(&dst_image, &src_image, rect);
if (!_gst_vaapi_image_unmap(image))
return FALSE;
return success;
}
/**
* gst_vaapi_image_update_from_raw:
* @image: a #GstVaapiImage
* @src_image: a #GstVaapiImageRaw
* @buffer: a #GstBuffer
* @rect: a #GstVaapiRectangle expressing a region, or %NULL for the
* whole image
*
* Transfers pixels data contained in the #GstVaapiImageRaw into the
* @image. Both image structures shall have the same format.
*
* Return value: %TRUE on success
*/
gboolean
gst_vaapi_image_update_from_raw(
GstVaapiImage *image,
GstVaapiImageRaw *src_image,
GstVaapiRectangle *rect
)
{
GstVaapiImageRaw dst_image;
gboolean success;
g_return_val_if_fail(image != NULL, FALSE);
if (!_gst_vaapi_image_map(image, &dst_image))
return FALSE;
success = copy_image(&dst_image, src_image, rect);
if (!_gst_vaapi_image_unmap(image))
return FALSE;
return success;
}