legal: add Intel copyright on modified files.
[profile/ivi/gstreamer-vaapi.git] / gst-libs / gst / vaapi / gstvaapiimage.c
index 3cc2251..f063b54 100644 (file)
@@ -1,21 +1,23 @@
 /*
  *  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
  */
 
 /**
@@ -69,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);
@@ -278,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);
@@ -740,15 +745,17 @@ 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;
@@ -768,6 +775,18 @@ _gst_vaapi_image_map(GstVaapiImage *image)
         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;
 }
 
@@ -895,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.
@@ -906,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;
 }