legal: add Intel copyright on modified files.
[profile/ivi/gstreamer-vaapi.git] / gst-libs / gst / vaapi / gstvaapiimage.c
index 5da91fd..f063b54 100644 (file)
@@ -1,38 +1,41 @@
 /*
  *  gstvaapiimage.c - VA image abstraction
  *
- *  gstreamer-vaapi (C) 2010 Splitted-Desktop Systems
+ *  Copyright (C) 2010-2011 Splitted-Desktop Systems
+ *  Copyright (C) 2011 Intel Corporation
  *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
+ *  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 program is distributed in the hope that it will be useful,
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ *  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:gst-vaapi-image
- * @short_description:
+ * SECTION:gstvaapiimage
+ * @short_description: VA image abstraction
  */
 
 #include "config.h"
 #include <string.h>
+#include "gstvaapicompat.h"
 #include "gstvaapiutils.h"
 #include "gstvaapiimage.h"
-#include <va/va_backend.h>
+#include "gstvaapi_priv.h"
 
 #define DEBUG 1
 #include "gstvaapidebug.h"
 
-G_DEFINE_TYPE(GstVaapiImage, gst_vaapi_image, G_TYPE_OBJECT);
+G_DEFINE_TYPE(GstVaapiImage, gst_vaapi_image, GST_VAAPI_TYPE_OBJECT);
 
 #define GST_VAAPI_IMAGE_GET_PRIVATE(obj)                \
     (G_TYPE_INSTANCE_GET_PRIVATE((obj),                 \
@@ -40,7 +43,6 @@ G_DEFINE_TYPE(GstVaapiImage, gst_vaapi_image, G_TYPE_OBJECT);
                                  GstVaapiImagePrivate))
 
 struct _GstVaapiImagePrivate {
-    GstVaapiDisplay    *display;
     VAImage             internal_image;
     VAImage             image;
     guchar             *image_data;
@@ -56,9 +58,7 @@ struct _GstVaapiImagePrivate {
 enum {
     PROP_0,
 
-    PROP_DISPLAY,
     PROP_IMAGE,
-    PROP_IMAGE_ID,
     PROP_FORMAT,
     PROP_WIDTH,
     PROP_HEIGHT
@@ -71,7 +71,7 @@ enum {
     } while (0)
 
 static gboolean
-_gst_vaapi_image_map(GstVaapiImage *image);
+_gst_vaapi_image_map(GstVaapiImage *image, GstVaapiImageRaw *raw_image);
 
 static gboolean
 _gst_vaapi_image_unmap(GstVaapiImage *image);
@@ -132,6 +132,7 @@ vaapi_image_is_linear(const VAImage *va_image)
     case VA_FOURCC('I','4','2','0'):
         data_size = width * height + 2 * width2 * height2;
         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'):
@@ -148,55 +149,50 @@ vaapi_image_is_linear(const VAImage *va_image)
 static void
 gst_vaapi_image_destroy(GstVaapiImage *image)
 {
-    GstVaapiImagePrivate * const priv = image->priv;
+    GstVaapiDisplay * const display = GST_VAAPI_OBJECT_DISPLAY(image);
+    VAImageID image_id;
     VAStatus status;
 
     _gst_vaapi_image_unmap(image);
 
-    GST_DEBUG("image 0x%08x", priv->internal_image.image_id);
+    image_id = GST_VAAPI_OBJECT_ID(image);
+    GST_DEBUG("image %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS(image_id));
 
-    if (priv->internal_image.image_id != VA_INVALID_ID) {
-        GST_VAAPI_DISPLAY_LOCK(priv->display);
-        status = vaDestroyImage(
-            GST_VAAPI_DISPLAY_VADISPLAY(priv->display),
-            priv->internal_image.image_id
-        );
-        GST_VAAPI_DISPLAY_UNLOCK(priv->display);
+    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 0x%08x\n",
-                      priv->internal_image.image_id);
-        priv->internal_image.image_id = VA_INVALID_ID;
-    }
-
-    if (priv->display) {
-        g_object_unref(priv->display);
-        priv->display = NULL;
+            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, GstVaapiImageFormat format)
 {
+    GstVaapiDisplay * const display = GST_VAAPI_OBJECT_DISPLAY(image);
     GstVaapiImagePrivate * const priv = image->priv;
     const VAImageFormat *va_format;
     VAStatus status;
 
-    if (!gst_vaapi_display_has_image_format(priv->display, format))
+    if (!gst_vaapi_display_has_image_format(display, format))
         return FALSE;
 
     va_format = gst_vaapi_image_format_get_va_format(format);
     if (!va_format)
         return FALSE;
 
-    GST_VAAPI_DISPLAY_LOCK(priv->display);
+    GST_VAAPI_DISPLAY_LOCK(display);
     status = vaCreateImage(
-        GST_VAAPI_DISPLAY_VADISPLAY(priv->display),
+        GST_VAAPI_DISPLAY_VADISPLAY(display),
         (VAImageFormat *)va_format,
         priv->width,
         priv->height,
         &priv->internal_image
     );
-    GST_VAAPI_DISPLAY_UNLOCK(priv->display);
+    GST_VAAPI_DISPLAY_UNLOCK(display);
     if (status != VA_STATUS_SUCCESS ||
         priv->internal_image.format.fourcc != va_format->fourcc)
         return FALSE;
@@ -211,6 +207,7 @@ gst_vaapi_image_create(GstVaapiImage *image)
     GstVaapiImagePrivate * const priv = image->priv;
     GstVaapiImageFormat format = priv->format;
     const VAImageFormat *va_format;
+    VAImageID image_id;
 
     if (!priv->create_image)
         return (priv->image.image_id != VA_INVALID_ID &&
@@ -232,6 +229,7 @@ gst_vaapi_image_create(GstVaapiImage *image)
             return FALSE;
     }
     priv->image = priv->internal_image;
+    image_id    = priv->image.image_id;
 
     if (priv->format != priv->internal_format) {
         switch (priv->format) {
@@ -248,9 +246,10 @@ gst_vaapi_image_create(GstVaapiImage *image)
             break;
         }
     }
-
-    GST_DEBUG("image 0x%08x", priv->image.image_id);
     priv->is_linear = vaapi_image_is_linear(&priv->image);
+
+    GST_DEBUG("image %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS(image_id));
+    GST_VAAPI_OBJECT_ID(image) = image_id;
     return TRUE;
 }
 
@@ -274,9 +273,6 @@ gst_vaapi_image_set_property(
     GstVaapiImagePrivate * const priv  = image->priv;
 
     switch (prop_id) {
-    case PROP_DISPLAY:
-        priv->display = g_object_ref(g_value_get_object(value));
-        break;
     case PROP_IMAGE: {
         const VAImage * const va_image = g_value_get_boxed(value);
         if (va_image)
@@ -284,13 +280,16 @@ gst_vaapi_image_set_property(
         break;
     }
     case PROP_FORMAT:
-        priv->format = g_value_get_uint(value);
+        if (priv->create_image)
+            priv->format = g_value_get_uint(value);
         break;
     case PROP_WIDTH:
-        priv->width = g_value_get_uint(value);
+        if (priv->create_image)
+            priv->width = g_value_get_uint(value);
         break;
     case PROP_HEIGHT:
-        priv->height = g_value_get_uint(value);
+        if (priv->create_image)
+            priv->height = g_value_get_uint(value);
         break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
@@ -309,15 +308,9 @@ gst_vaapi_image_get_property(
     GstVaapiImage * const image = GST_VAAPI_IMAGE(object);
 
     switch (prop_id) {
-    case PROP_DISPLAY:
-        g_value_set_pointer(value, gst_vaapi_image_get_display(image));
-        break;
     case PROP_IMAGE:
         g_value_set_boxed(value, &image->priv->image);
         break;
-    case PROP_IMAGE_ID:
-        g_value_set_uint(value, gst_vaapi_image_get_id(image));
-        break;
     case PROP_FORMAT:
         g_value_set_uint(value, gst_vaapi_image_get_format(image));
         break;
@@ -358,20 +351,6 @@ gst_vaapi_image_class_init(GstVaapiImageClass *klass)
     object_class->get_property = gst_vaapi_image_get_property;
     object_class->constructed  = gst_vaapi_image_constructed;
 
-    /**
-     * GstVaapiImage:display:
-     *
-     * The #GstVaapiDisplay this image is bound to.
-     */
-    g_object_class_install_property
-        (object_class,
-         PROP_DISPLAY,
-         g_param_spec_object("display",
-                             "Display",
-                             "The GstVaapiDisplay this image is bound to",
-                             GST_VAAPI_TYPE_DISPLAY,
-                             G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
-
     g_object_class_install_property
         (object_class,
          PROP_IMAGE,
@@ -381,20 +360,6 @@ gst_vaapi_image_class_init(GstVaapiImageClass *klass)
                             VAAPI_TYPE_IMAGE,
                             G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
 
-    /**
-     * GstVaapiImage:id:
-     *
-     * The underlying #VAImageID of the image.
-     */
-    g_object_class_install_property
-        (object_class,
-         PROP_IMAGE_ID,
-         g_param_spec_uint("id",
-                           "VA image id",
-                           "The underlying VA image id",
-                           0, G_MAXUINT32, VA_INVALID_ID,
-                           G_PARAM_READABLE));
-
     g_object_class_install_property
         (object_class,
          PROP_WIDTH,
@@ -434,7 +399,6 @@ gst_vaapi_image_init(GstVaapiImage *image)
     GstVaapiImagePrivate *priv = GST_VAAPI_IMAGE_GET_PRIVATE(image);
 
     image->priv                   = priv;
-    priv->display                 = NULL;
     priv->image_data              = NULL;
     priv->width                   = 0;
     priv->height                  = 0;
@@ -485,6 +449,7 @@ gst_vaapi_image_new(
     image = g_object_new(
         GST_VAAPI_TYPE_IMAGE,
         "display", display,
+        "id",      GST_VAAPI_ID(VA_INVALID_ID),
         "format",  format,
         "width",   width,
         "height",  height,
@@ -530,6 +495,7 @@ gst_vaapi_image_new_with_image(GstVaapiDisplay *display, VAImage *va_image)
     image = g_object_new(
         GST_VAAPI_TYPE_IMAGE,
         "display", display,
+        "id",      GST_VAAPI_ID(va_image->image_id),
         "image",   va_image,
         NULL
     );
@@ -551,13 +517,13 @@ gst_vaapi_image_new_with_image(GstVaapiDisplay *display, VAImage *va_image)
  *
  * Return value: the underlying VA image id
  */
-VAImageID
+GstVaapiID
 gst_vaapi_image_get_id(GstVaapiImage *image)
 {
     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), VA_INVALID_ID);
     g_return_val_if_fail(image->priv->is_constructed, VA_INVALID_ID);
 
-    return image->priv->image.image_id;
+    return GST_VAAPI_OBJECT_ID(image);
 }
 
 /**
@@ -651,23 +617,6 @@ _gst_vaapi_image_set_image(GstVaapiImage *image, const VAImage *va_image)
 }
 
 /**
- * gst_vaapi_image_get_display:
- * @image: a #GstVaapiImage
- *
- * Returns the #GstVaapiDisplay this @image is bound to.
- *
- * Return value: the parent #GstVaapiDisplay object
- */
-GstVaapiDisplay *
-gst_vaapi_image_get_display(GstVaapiImage *image)
-{
-    g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), NULL);
-    g_return_val_if_fail(image->priv->is_constructed, FALSE);
-
-    return image->priv->display;
-}
-
-/**
  * gst_vaapi_image_get_format:
  * @image: a #GstVaapiImage
  *
@@ -679,7 +628,7 @@ GstVaapiImageFormat
 gst_vaapi_image_get_format(GstVaapiImage *image)
 {
     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
-    g_return_val_if_fail(image->priv->is_constructed, FALSE);
+    g_return_val_if_fail(image->priv->is_constructed, 0);
 
     return image->priv->format;
 }
@@ -696,7 +645,7 @@ guint
 gst_vaapi_image_get_width(GstVaapiImage *image)
 {
     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
-    g_return_val_if_fail(image->priv->is_constructed, FALSE);
+    g_return_val_if_fail(image->priv->is_constructed, 0);
 
     return image->priv->width;
 }
@@ -713,7 +662,7 @@ guint
 gst_vaapi_image_get_height(GstVaapiImage *image)
 {
     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
-    g_return_val_if_fail(image->priv->is_constructed, FALSE);
+    g_return_val_if_fail(image->priv->is_constructed, 0);
 
     return image->priv->height;
 }
@@ -796,29 +745,48 @@ gst_vaapi_image_map(GstVaapiImage *image)
     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
     g_return_val_if_fail(image->priv->is_constructed, FALSE);
 
-    return _gst_vaapi_image_map(image);
+    return _gst_vaapi_image_map(image, NULL);
 }
 
 gboolean
-_gst_vaapi_image_map(GstVaapiImage *image)
+_gst_vaapi_image_map(GstVaapiImage *image, GstVaapiImageRaw *raw_image)
 {
+    GstVaapiImagePrivate * const priv = image->priv;
+    GstVaapiDisplay *display;
     void *image_data;
     VAStatus status;
+    guint i;
 
     if (_gst_vaapi_image_is_mapped(image))
         return TRUE;
 
-    GST_VAAPI_DISPLAY_LOCK(image->priv->display);
+    display = GST_VAAPI_OBJECT_DISPLAY(image);
+    if (!display)
+        return FALSE;
+
+    GST_VAAPI_DISPLAY_LOCK(display);
     status = vaMapBuffer(
-        GST_VAAPI_DISPLAY_VADISPLAY(image->priv->display),
+        GST_VAAPI_DISPLAY_VADISPLAY(display),
         image->priv->image.buf,
         &image_data
     );
-    GST_VAAPI_DISPLAY_UNLOCK(image->priv->display);
+    GST_VAAPI_DISPLAY_UNLOCK(display);
     if (!vaapi_check_status(status, "vaMapBuffer()"))
         return FALSE;
 
     image->priv->image_data = image_data;
+
+    if (raw_image) {
+        const VAImage * const va_image = &priv->image;
+        raw_image->format     = priv->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] = image_data + va_image->offsets[i];
+            raw_image->stride[i] = va_image->pitches[i];
+        }
+    }
     return TRUE;
 }
 
@@ -843,17 +811,22 @@ gst_vaapi_image_unmap(GstVaapiImage *image)
 gboolean
 _gst_vaapi_image_unmap(GstVaapiImage *image)
 {
+    GstVaapiDisplay *display;
     VAStatus status;
 
     if (!_gst_vaapi_image_is_mapped(image))
         return FALSE;
 
-    GST_VAAPI_DISPLAY_LOCK(image->priv->display);
+    display = GST_VAAPI_OBJECT_DISPLAY(image);
+    if (!display)
+        return FALSE;
+
+    GST_VAAPI_DISPLAY_LOCK(display);
     status = vaUnmapBuffer(
-        GST_VAAPI_DISPLAY_VADISPLAY(image->priv->display),
+        GST_VAAPI_DISPLAY_VADISPLAY(display),
         image->priv->image.buf
     );
-    GST_VAAPI_DISPLAY_UNLOCK(image->priv->display);
+    GST_VAAPI_DISPLAY_UNLOCK(display);
     if (!vaapi_check_status(status, "vaUnmapBuffer()"))
         return FALSE;
 
@@ -941,10 +914,320 @@ gst_vaapi_image_get_data_size(GstVaapiImage *image)
     return image->priv->image.data_size;
 }
 
+static gboolean
+init_image_from_buffer(GstVaapiImageRaw *raw_image, GstBuffer *buffer)
+{
+    GstStructure *structure;
+    GstCaps *caps;
+    GstVaapiImageFormat format;
+    guint width2, height2, size2;
+    gint width, height;
+    guchar *data;
+    guint32 data_size;
+
+    data      = GST_BUFFER_DATA(buffer);
+    data_size = GST_BUFFER_SIZE(buffer);
+    caps      = GST_BUFFER_CAPS(buffer);
+
+    if (!caps)
+        return FALSE;
+
+    format = gst_vaapi_image_format_from_caps(caps);
+
+    structure = gst_caps_get_structure(caps, 0);
+    gst_structure_get_int(structure, "width",  &width);
+    gst_structure_get_int(structure, "height", &height);
+
+    /* XXX: copied from gst_video_format_get_row_stride() -- no NV12? */
+    raw_image->format = format;
+    raw_image->width  = width;
+    raw_image->height = height;
+    width2  = (width + 1) / 2;
+    height2 = (height + 1) / 2;
+    size2   = 0;
+    switch (format) {
+    case GST_VAAPI_IMAGE_NV12:
+        raw_image->num_planes = 2;
+        raw_image->pixels[0]  = data;
+        raw_image->stride[0]  = GST_ROUND_UP_4(width);
+        size2                += height * raw_image->stride[0];
+        raw_image->pixels[1]  = data + size2;
+        raw_image->stride[1]  = raw_image->stride[0];
+        size2                += height2 * raw_image->stride[1];
+        break;
+    case GST_VAAPI_IMAGE_YV12:
+    case GST_VAAPI_IMAGE_I420:
+        raw_image->num_planes = 3;
+        raw_image->pixels[0]  = data;
+        raw_image->stride[0]  = GST_ROUND_UP_4(width);
+        size2                += height * raw_image->stride[0];
+        raw_image->pixels[1]  = data + size2;
+        raw_image->stride[1]  = GST_ROUND_UP_4(width2);
+        size2                += height2 * raw_image->stride[1];
+        raw_image->pixels[2]  = data + size2;
+        raw_image->stride[2]  = raw_image->stride[1];
+        size2                += height2 * raw_image->stride[2];
+        break;
+    case GST_VAAPI_IMAGE_ARGB:
+    case GST_VAAPI_IMAGE_RGBA:
+    case GST_VAAPI_IMAGE_ABGR:
+    case GST_VAAPI_IMAGE_BGRA:
+        raw_image->num_planes = 1;
+        raw_image->pixels[0]  = data;
+        raw_image->stride[0]  = width * 4;
+        size2                += height * raw_image->stride[0];
+        break;
+    default:
+        g_error("could not compute row-stride for %" GST_FOURCC_FORMAT,
+                GST_FOURCC_ARGS(format));
+        return FALSE;
+    }
+
+    if (size2 != data_size) {
+        g_error("data_size mismatch %d / %u", size2, data_size);
+        if (size2 > data_size)
+            return FALSE;
+    }
+    return TRUE;
+}
+
+/* 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 += dst_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 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 + src_image->width > src_image->width ||
+            rect->y >= src_image->height ||
+            rect->y + src_image->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_VAAPI_IMAGE_NV12:
+        copy_image_NV12(dst_image, src_image, rect);
+        break;
+    case GST_VAAPI_IMAGE_YV12:
+    case GST_VAAPI_IMAGE_I420:
+        copy_image_YV12(dst_image, src_image, rect);
+        break;
+    case GST_VAAPI_IMAGE_ARGB:
+    case GST_VAAPI_IMAGE_RGBA:
+    case GST_VAAPI_IMAGE_ABGR:
+    case GST_VAAPI_IMAGE_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
+)
+{
+    GstVaapiImagePrivate *priv;
+    GstVaapiImageRaw dst_image, src_image;
+    gboolean success;
+
+    g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
+    g_return_val_if_fail(image->priv->is_constructed, FALSE);
+    g_return_val_if_fail(GST_IS_BUFFER(buffer), FALSE);
+
+    priv = image->priv;
+
+    if (!init_image_from_buffer(&dst_image, buffer))
+        return FALSE;
+    if (dst_image.format != priv->format)
+        return FALSE;
+    if (dst_image.width != priv->width || dst_image.height != priv->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
+ * @buffer: a #GstBuffer
+ * @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(GST_VAAPI_IS_IMAGE(image), FALSE);
+    g_return_val_if_fail(image->priv->is_constructed, 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.
@@ -952,108 +1235,73 @@ gst_vaapi_image_get_data_size(GstVaapiImage *image)
  * Return value: %TRUE on success
  */
 gboolean
-gst_vaapi_image_update_from_buffer(GstVaapiImage *image, GstBuffer *buffer)
+gst_vaapi_image_update_from_buffer(
+    GstVaapiImage     *image,
+    GstBuffer         *buffer,
+    GstVaapiRectangle *rect
+)
 {
     GstVaapiImagePrivate *priv;
-    GstStructure *structure;
-    GstCaps *caps;
-    GstVaapiImageFormat format;
-    gint width, height;
-    guint offsets[3], pitches[3], widths[3], heights[3];
-    guint i, j;
-    guchar *data;
-    guint32 data_size;
+    GstVaapiImageRaw dst_image, src_image;
+    gboolean success;
 
     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
     g_return_val_if_fail(image->priv->is_constructed, FALSE);
     g_return_val_if_fail(GST_IS_BUFFER(buffer), FALSE);
 
-    priv      = image->priv;
-    data      = GST_BUFFER_DATA(buffer);
-    data_size = GST_BUFFER_SIZE(buffer);
-    caps      = GST_BUFFER_CAPS(buffer);
+    priv = image->priv;
 
-    if (!caps)
+    if (!init_image_from_buffer(&src_image, buffer))
+        return FALSE;
+    if (src_image.format != priv->format)
+        return FALSE;
+    if (src_image.width != priv->width || src_image.height != priv->height)
         return FALSE;
 
-    format = gst_vaapi_image_format_from_caps(caps);
-    if (format != priv->format)
+    if (!_gst_vaapi_image_map(image, &dst_image))
         return FALSE;
 
-    structure = gst_caps_get_structure(caps, 0);
-    gst_structure_get_int(structure, "width",  &width);
-    gst_structure_get_int(structure, "height", &height);
-    if (width != priv->width || height != priv->height)
+    success = copy_image(&dst_image, &src_image, rect);
+
+    if (!_gst_vaapi_image_unmap(image))
         return FALSE;
 
-    if (!gst_vaapi_image_map(image))
+    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(GST_VAAPI_IS_IMAGE(image), FALSE);
+    g_return_val_if_fail(image->priv->is_constructed, FALSE);
+
+    if (!_gst_vaapi_image_map(image, &dst_image))
         return FALSE;
 
-    if (priv->is_linear && data_size == priv->image.data_size)
-        memcpy(priv->image_data, data, data_size);
-    else {
-        /* XXX: copied from gst_video_format_get_row_stride() -- no NV12? */
-        const guint width2  = (width  + 1) / 2;
-        const guint height2 = (height + 1) / 2;
-        guint size2;
-        switch (format) {
-        case GST_VAAPI_IMAGE_NV12:
-            offsets[0] = 0;
-            pitches[0] = GST_ROUND_UP_4(width);
-            widths [0] = width;
-            heights[0] = height;
-            offsets[1] = offsets[0] + height * pitches[0];
-            pitches[1] = pitches[0];
-            widths [1] = width2 * 2;
-            heights[1] = height2;
-            size2      = offsets[1] + height2 * pitches[1];
-            break;
-        case GST_VAAPI_IMAGE_YV12:
-        case GST_VAAPI_IMAGE_I420:
-            offsets[0] = 0;
-            pitches[0] = GST_ROUND_UP_4(width);
-            widths [0] = width;
-            heights[0] = height;
-            offsets[1] = offsets[0] + height * pitches[0];
-            pitches[1] = GST_ROUND_UP_4(GST_ROUND_UP_2(width) / 2);
-            widths [1] = width2;
-            heights[1] = height2;
-            offsets[2] = offsets[1] + height2 * pitches[1];
-            pitches[2] = pitches[1];
-            widths [2] = width2;
-            heights[2] = height2;
-            size2      = offsets[2] + height2 * pitches[2];
-            break;
-        case GST_VAAPI_IMAGE_ARGB:
-        case GST_VAAPI_IMAGE_RGBA:
-        case GST_VAAPI_IMAGE_ABGR:
-        case GST_VAAPI_IMAGE_BGRA:
-            offsets[0] = 0;
-            pitches[0] = width * 4;
-            widths [0] = width * 4;
-            heights[0] = height;
-            size2      = offsets[0] + height * pitches[0];
-            break;
-        default:
-            g_error("could not compute row-stride for %" GST_FOURCC_FORMAT,
-                    GST_FOURCC_ARGS(format));
-            break;
-        }
-        if (size2 != data_size)
-            g_error("data_size mismatch %d / %u", size2, data_size);
-        for (i = 0; i < priv->image.num_planes; i++) {
-            guchar *src = data + offsets[i];
-            guchar *dst = priv->image_data + priv->image.offsets[i];
-            for (j = 0; j < heights[i]; j++) {
-                memcpy(dst, src, widths[i]);
-                src += pitches[i];
-                dst += priv->image.pitches[i];
-            }
-        }
-    }
+    success = copy_image(&dst_image, src_image, rect);
 
-    if (!gst_vaapi_image_unmap(image))
+    if (!_gst_vaapi_image_unmap(image))
         return FALSE;
 
-    return TRUE;
+    return success;
 }