vaapivideomemory: disallow memory shares across buffers, use a copy.
[platform/upstream/gstreamer-vaapi.git] / gst / vaapi / gstvaapivideomemory.c
index 0e3b794..5033b93 100644 (file)
@@ -21,6 +21,8 @@
  */
 
 #include "gst/vaapi/sysdeps.h"
+#include <gst/vaapi/gstvaapisurfacepool.h>
+#include <gst/vaapi/gstvaapiimagepool.h>
 #include "gstvaapivideomemory.h"
 
 GST_DEBUG_CATEGORY_STATIC(gst_debug_vaapivideomemory);
@@ -35,6 +37,23 @@ GST_DEBUG_CATEGORY_STATIC(gst_debug_vaapivideomemory);
 /* --- GstVaapiVideoMemory                                              --- */
 /* ------------------------------------------------------------------------ */
 
+static void
+gst_vaapi_video_memory_reset_image(GstVaapiVideoMemory *mem);
+
+static guchar *
+get_image_data(GstVaapiImage *image)
+{
+    guchar *data;
+    VAImage va_image;
+
+    data = gst_vaapi_image_get_plane(image, 0);
+    if (!data || !gst_vaapi_image_get_image(image, &va_image))
+        return NULL;
+
+    data -= va_image.offsets[0];
+    return data;
+}
+
 static GstVaapiImage *
 new_image(GstVaapiDisplay *display, const GstVideoInfo *vip)
 {
@@ -51,13 +70,18 @@ ensure_image(GstVaapiVideoMemory *mem)
             GST_WARNING("failed to derive image, fallbacking to copy");
             mem->use_direct_rendering = FALSE;
         }
+        else if (gst_vaapi_surface_get_format(mem->surface) !=
+                 GST_VIDEO_INFO_FORMAT(mem->image_info)) {
+            gst_vaapi_object_replace(&mem->image, NULL);
+            mem->use_direct_rendering = FALSE;
+        }
     }
 
     if (!mem->image) {
-        GstVaapiDisplay * const display =
-            gst_vaapi_video_meta_get_display(mem->meta);
+        GstVaapiVideoAllocator * const allocator =
+            GST_VAAPI_VIDEO_ALLOCATOR_CAST(GST_MEMORY_CAST(mem)->allocator);
 
-        mem->image = new_image(display, mem->image_info);
+        mem->image = gst_vaapi_video_pool_get_object(allocator->image_pool);
         if (!mem->image)
             return FALSE;
     }
@@ -112,10 +136,9 @@ ensure_surface(GstVaapiVideoMemory *mem)
                 return FALSE;
             gst_vaapi_video_meta_set_surface_proxy(mem->meta, mem->proxy);
         }
-        mem->surface = GST_VAAPI_SURFACE_PROXY_SURFACE(mem->proxy);
     }
-    g_return_val_if_fail(mem->surface != NULL, FALSE);
-    return TRUE;
+    mem->surface = GST_VAAPI_SURFACE_PROXY_SURFACE(mem->proxy);
+    return mem->surface != NULL;
 }
 
 gboolean
@@ -123,7 +146,7 @@ gst_video_meta_map_vaapi_memory(GstVideoMeta *meta, guint plane,
     GstMapInfo *info, gpointer *data, gint *stride, GstMapFlags flags)
 {
     GstVaapiVideoMemory * const mem =
-        GST_VAAPI_VIDEO_MEMORY_CAST(gst_buffer_get_memory(meta->buffer, 0));
+        GST_VAAPI_VIDEO_MEMORY_CAST(gst_buffer_peek_memory(meta->buffer, 0));
 
     g_return_val_if_fail(mem, FALSE);
     g_return_val_if_fail(GST_VAAPI_IS_VIDEO_ALLOCATOR(mem->parent_instance.
@@ -142,10 +165,14 @@ gst_video_meta_map_vaapi_memory(GstVideoMeta *meta, guint plane,
             goto error_ensure_image;
 
         // Check that we can actually map the surface, or image
-        if ((flags & GST_MAP_READWRITE) != GST_MAP_WRITE &&
+        if ((flags & GST_MAP_READWRITE) == GST_MAP_READWRITE &&
             !mem->use_direct_rendering)
             goto error_unsupported_map;
 
+        // Load VA image from surface
+        if ((flags & GST_MAP_READ) && !mem->use_direct_rendering)
+            gst_vaapi_surface_get_image(mem->surface, mem->image);
+
         if (!gst_vaapi_image_map(mem->image))
             goto error_map_image;
         mem->map_type = GST_VAAPI_VIDEO_MEMORY_MAP_TYPE_PLANAR;
@@ -196,7 +223,7 @@ gst_video_meta_unmap_vaapi_memory(GstVideoMeta *meta, guint plane,
     GstMapInfo *info)
 {
     GstVaapiVideoMemory * const mem =
-        GST_VAAPI_VIDEO_MEMORY_CAST(gst_buffer_get_memory(meta->buffer, 0));
+        GST_VAAPI_VIDEO_MEMORY_CAST(gst_buffer_peek_memory(meta->buffer, 0));
 
     g_return_val_if_fail(mem, FALSE);
     g_return_val_if_fail(GST_VAAPI_IS_VIDEO_ALLOCATOR(mem->parent_instance.
@@ -242,8 +269,9 @@ gst_vaapi_video_memory_new(GstAllocator *base_allocator,
         return NULL;
 
     vip = &allocator->image_info;
-    gst_memory_init(&mem->parent_instance, 0, gst_object_ref(allocator), NULL,
-        GST_VIDEO_INFO_SIZE(vip), 0, 0, GST_VIDEO_INFO_SIZE(vip));
+    gst_memory_init(&mem->parent_instance, GST_MEMORY_FLAG_NO_SHARE,
+        gst_object_ref(allocator), NULL, GST_VIDEO_INFO_SIZE(vip), 0,
+        0, GST_VIDEO_INFO_SIZE(vip));
 
     mem->proxy = NULL;
     mem->surface_info = &allocator->surface_info;
@@ -261,60 +289,123 @@ static void
 gst_vaapi_video_memory_free(GstVaapiVideoMemory *mem)
 {
     mem->surface = NULL;
+    gst_vaapi_video_memory_reset_image(mem);
     gst_vaapi_surface_proxy_replace(&mem->proxy, NULL);
-    gst_vaapi_object_replace(&mem->image, NULL);
     gst_vaapi_video_meta_unref(mem->meta);
     gst_object_unref(GST_MEMORY_CAST(mem)->allocator);
     g_slice_free(GstVaapiVideoMemory, mem);
 }
 
 void
+gst_vaapi_video_memory_reset_image(GstVaapiVideoMemory *mem)
+{
+    GstVaapiVideoAllocator * const allocator =
+        GST_VAAPI_VIDEO_ALLOCATOR_CAST(GST_MEMORY_CAST(mem)->allocator);
+
+    if (mem->use_direct_rendering)
+        gst_vaapi_object_replace(&mem->image, NULL);
+    else if (mem->image) {
+        gst_vaapi_video_pool_put_object(allocator->image_pool, mem->image);
+        mem->image = NULL;
+    }
+}
+
+void
 gst_vaapi_video_memory_reset_surface(GstVaapiVideoMemory *mem)
 {
     mem->surface = NULL;
+    gst_vaapi_video_memory_reset_image(mem);
     gst_vaapi_surface_proxy_replace(&mem->proxy, NULL);
-    if (mem->use_direct_rendering)
-        gst_vaapi_object_replace(&mem->image, NULL);
     gst_vaapi_video_meta_set_surface_proxy(mem->meta, NULL);
 }
 
 static gpointer
 gst_vaapi_video_memory_map(GstVaapiVideoMemory *mem, gsize maxsize, guint flags)
 {
-    if (mem->map_type &&
-        mem->map_type != GST_VAAPI_VIDEO_MEMORY_MAP_TYPE_SURFACE)
-        goto error_incompatible_map;
+    gpointer data;
 
     if (mem->map_count == 0) {
-        gst_vaapi_surface_proxy_replace(&mem->proxy,
-            gst_vaapi_video_meta_get_surface_proxy(mem->meta));
+        switch (flags & GST_MAP_READWRITE) {
+        case 0:
+            // No flags set: return a GstVaapiSurfaceProxy
+            gst_vaapi_surface_proxy_replace(&mem->proxy,
+                gst_vaapi_video_meta_get_surface_proxy(mem->meta));
+            if (!mem->proxy)
+                goto error_no_surface_proxy;
+            mem->map_type = GST_VAAPI_VIDEO_MEMORY_MAP_TYPE_SURFACE;
+            break;
+        case GST_MAP_READ:
+            // Only read flag set: return raw pixels
+            if (!ensure_surface(mem))
+                goto error_no_surface;
+            if (!ensure_image(mem))
+                goto error_no_image;
+            if (!mem->use_direct_rendering)
+                gst_vaapi_surface_get_image(mem->surface, mem->image);
+            if (!gst_vaapi_image_map(mem->image))
+                goto error_map_image;
+            mem->map_type = GST_VAAPI_VIDEO_MEMORY_MAP_TYPE_LINEAR;
+            break;
+        default:
+            goto error_unsupported_map;
+        }
+    }
+
+    switch (mem->map_type) {
+    case GST_VAAPI_VIDEO_MEMORY_MAP_TYPE_SURFACE:
         if (!mem->proxy)
             goto error_no_surface_proxy;
-        mem->map_type = GST_VAAPI_VIDEO_MEMORY_MAP_TYPE_SURFACE;
+        data = mem->proxy;
+        break;
+    case GST_VAAPI_VIDEO_MEMORY_MAP_TYPE_LINEAR:
+        if (!mem->image)
+            goto error_no_image;
+        data = get_image_data(mem->image);
+        break;
+    default:
+        goto error_unsupported_map_type;
     }
     mem->map_count++;
-    return mem->proxy;
+    return data;
 
     /* ERRORS */
-error_incompatible_map:
-    GST_ERROR("failed to map memory to a GstVaapiSurfaceProxy");
+error_unsupported_map:
+    GST_ERROR("unsupported map flags (0x%x)", flags);
+    return NULL;
+error_unsupported_map_type:
+    GST_ERROR("unsupported map type (%d)", mem->map_type);
     return NULL;
 error_no_surface_proxy:
     GST_ERROR("failed to extract GstVaapiSurfaceProxy from video meta");
     return NULL;
+error_no_surface:
+    GST_ERROR("failed to extract VA surface from video buffer");
+    return NULL;
+error_no_image:
+    GST_ERROR("failed to extract VA image from video buffer");
+    return NULL;
+error_map_image:
+    GST_ERROR("failed to map VA image");
+    return NULL;
 }
 
 static void
 gst_vaapi_video_memory_unmap(GstVaapiVideoMemory *mem)
 {
-    if (mem->map_type &&
-        mem->map_type != GST_VAAPI_VIDEO_MEMORY_MAP_TYPE_SURFACE)
-        goto error_incompatible_map;
-
-    if (--mem->map_count == 0) {
-        gst_vaapi_surface_proxy_replace(&mem->proxy, NULL);
+    if (mem->map_count == 1) {
+        switch (mem->map_type) {
+        case GST_VAAPI_VIDEO_MEMORY_MAP_TYPE_SURFACE:
+            gst_vaapi_surface_proxy_replace(&mem->proxy, NULL);
+            break;
+        case GST_VAAPI_VIDEO_MEMORY_MAP_TYPE_LINEAR:
+            gst_vaapi_image_unmap(mem->image);
+            break;
+        default:
+            goto error_incompatible_map;
+        }
         mem->map_type = 0;
     }
+    mem->map_count--;
     return;
 
     /* ERRORS */
@@ -327,13 +418,22 @@ static GstVaapiVideoMemory *
 gst_vaapi_video_memory_copy(GstVaapiVideoMemory *mem,
     gssize offset, gssize size)
 {
+    GstVaapiVideoMeta *meta;
     GstMemory *out_mem;
+    gsize maxsize;
 
-    if (offset != 0 || size != -1)
+    /* XXX: this implements a soft-copy, i.e. underlying VA surfaces
+       are not copied */
+    (void)gst_memory_get_sizes(GST_MEMORY_CAST(mem), NULL, &maxsize);
+    if (offset != 0 || (size != -1 && (gsize)size != maxsize))
         goto error_unsupported;
 
-    out_mem = gst_vaapi_video_memory_new(mem->parent_instance.allocator,
-        mem->meta);
+    meta = gst_vaapi_video_meta_copy(mem->meta);
+    if (!meta)
+        goto error_allocate_memory;
+
+    out_mem = gst_vaapi_video_memory_new(GST_MEMORY_CAST(mem)->allocator, meta);
+    gst_vaapi_video_meta_unref(meta);
     if (!out_mem)
         goto error_allocate_memory;
     return GST_VAAPI_VIDEO_MEMORY_CAST(out_mem);
@@ -402,6 +502,7 @@ gst_vaapi_video_allocator_finalize(GObject *object)
         GST_VAAPI_VIDEO_ALLOCATOR_CAST(object);
 
     gst_vaapi_video_pool_replace(&allocator->surface_pool, NULL);
+    gst_vaapi_video_pool_replace(&allocator->image_pool, NULL);
 
     G_OBJECT_CLASS(gst_vaapi_video_allocator_parent_class)->finalize(object);
 }
@@ -454,12 +555,8 @@ gst_video_info_update_from_image(GstVideoInfo *vip, GstVaapiImage *image)
     g_return_val_if_fail(num_planes == GST_VIDEO_INFO_N_PLANES(vip), FALSE);
 
     /* Determine the base data pointer */
-    data = gst_vaapi_image_get_plane(image, 0);
-    for (i = 1; i < num_planes; i++) {
-        const guchar * const plane = gst_vaapi_image_get_plane(image, i);
-        if (data > plane)
-            data = plane;
-    }
+    data = get_image_data(image);
+    g_return_val_if_fail(data != NULL, FALSE);
     data_size = gst_vaapi_image_get_data_size(image);
 
     /* Check that we don't have disjoint planes */
@@ -507,12 +604,12 @@ gst_vaapi_video_allocator_new(GstVaapiDisplay *display, const GstVideoInfo *vip)
             image = gst_vaapi_surface_derive_image(surface);
             if (!image)
                 break;
-            if (GST_VAAPI_IMAGE_FORMAT(image) != GST_VIDEO_INFO_FORMAT(vip))
-                break;
             if (!gst_vaapi_image_map(image))
                 break;
             allocator->has_direct_rendering = gst_video_info_update_from_image(
                 &allocator->surface_info, image);
+            if (GST_VAAPI_IMAGE_FORMAT(image) != GST_VIDEO_INFO_FORMAT(vip))
+               allocator->has_direct_rendering = FALSE;
             gst_vaapi_image_unmap(image);
             GST_INFO("has direct-rendering for %s surfaces: %s",
                      GST_VIDEO_INFO_FORMAT_STRING(&allocator->surface_info),
@@ -527,11 +624,11 @@ gst_vaapi_video_allocator_new(GstVaapiDisplay *display, const GstVideoInfo *vip)
     allocator->surface_pool = gst_vaapi_surface_pool_new(display,
         &allocator->surface_info);
     if (!allocator->surface_pool)
-        goto error_create_pool;
+        goto error_create_surface_pool;
 
     allocator->image_info = *vip;
     if (GST_VIDEO_INFO_FORMAT(vip) == GST_VIDEO_FORMAT_ENCODED)
-        gst_video_info_set_format(&allocator->image_info, GST_VIDEO_FORMAT_NV12,
+        gst_video_info_set_format(&allocator->image_info, GST_VIDEO_FORMAT_I420,
             GST_VIDEO_INFO_WIDTH(vip), GST_VIDEO_INFO_HEIGHT(vip));
 
     if (allocator->has_direct_rendering)
@@ -548,13 +645,24 @@ gst_vaapi_video_allocator_new(GstVaapiDisplay *display, const GstVideoInfo *vip)
         } while (0);
         gst_vaapi_object_unref(image);
     }
+
+    allocator->image_pool = gst_vaapi_image_pool_new(display,
+        &allocator->image_info);
+    if (!allocator->image_pool)
+        goto error_create_image_pool;
     return GST_ALLOCATOR_CAST(allocator);
 
     /* ERRORS */
-error_create_pool:
+error_create_surface_pool:
     {
         GST_ERROR("failed to allocate VA surface pool");
         gst_object_unref(allocator);
         return NULL;
     }
+error_create_image_pool:
+    {
+        GST_ERROR("failed to allocate VA image pool");
+        gst_object_unref(allocator);
+        return NULL;
+    }
 }