option('transcode', type : 'feature', value : 'auto', description : 'Transcode plugin')
option('ttml', type : 'feature', value : 'auto', description : 'TTML subtitle parser and renderer plugin')
option('uvch264', type : 'feature', value : 'auto', description : 'UVC compliant H.264 camera source plugin')
+option('va', type : 'feature', value : 'auto', description: 'VA-API new plugin')
option('voaacenc', type : 'feature', value : 'auto', description : 'AAC audio encoder plugin')
option('voamrwbenc', type : 'feature', value : 'auto', description : 'AMR-WB audio encoder plugin')
option('vulkan', type : 'feature', value : 'auto', description : 'Vulkan video sink plugin')
subdir('tinyalsa')
subdir('uvch264')
subdir('v4l2codecs')
+subdir('va')
subdir('wasapi')
subdir('wasapi2')
subdir('winks')
subdir('winscreencap')
-
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2020 Igalia, S.L.
+ * Author: Víctor Jáquez <vjaquez@igalia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstvaallocator.h"
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <va/va_drmcommon.h>
+
+#include "gstvacaps.h"
+#include "gstvavideoformat.h"
+
+#define GST_CAT_DEFAULT gst_va_memory_debug
+GST_DEBUG_CATEGORY_STATIC (gst_va_memory_debug);
+
+struct _GstVaDmabufAllocator
+{
+ GstDmaBufAllocator parent;
+
+ /* queue for disposable surfaces */
+ GstAtomicQueue *queue;
+ GstVaDisplay *display;
+
+ GstMemoryMapFunction parent_map;
+};
+
+static void _init_debug_category (void);
+
+#define gst_va_dmabuf_allocator_parent_class dmabuf_parent_class
+G_DEFINE_TYPE_WITH_CODE (GstVaDmabufAllocator, gst_va_dmabuf_allocator,
+ GST_TYPE_DMABUF_ALLOCATOR, _init_debug_category ());
+
+typedef struct _GstVaBufferSurface GstVaBufferSurface;
+struct _GstVaBufferSurface
+{
+ GstVideoInfo info;
+ VASurfaceID surface;
+ volatile gint ref_count;
+};
+
+static void
+_init_debug_category (void)
+{
+#ifndef GST_DISABLE_GST_DEBUG
+ static volatile gsize _init = 0;
+
+ if (g_once_init_enter (&_init)) {
+ GST_DEBUG_CATEGORY_INIT (gst_va_memory_debug, "vamemory", 0, "VA memory");
+ g_once_init_leave (&_init, 1);
+ }
+#endif
+}
+
+static gboolean
+_destroy_surfaces (GstVaDisplay * display, VASurfaceID * surfaces,
+ gint num_surfaces)
+{
+ VADisplay dpy = gst_va_display_get_va_dpy (display);
+ VAStatus status;
+
+ g_return_val_if_fail (num_surfaces > 0, FALSE);
+
+ gst_va_display_lock (display);
+ status = vaDestroySurfaces (dpy, surfaces, num_surfaces);
+ gst_va_display_unlock (display);
+ if (status != VA_STATUS_SUCCESS) {
+ GST_ERROR ("vaDestroySurfaces: %s", vaErrorStr (status));
+ return FALSE;
+ }
+
+ return TRUE;
+
+}
+
+static gboolean
+_create_surfaces (GstVaDisplay * display, guint rt_format, guint fourcc,
+ guint width, guint height, gint usage_hint, VASurfaceID * surfaces,
+ guint num_surfaces)
+{
+ VADisplay dpy = gst_va_display_get_va_dpy (display);
+ /* *INDENT-OFF* */
+ VASurfaceAttrib attrs[] = {
+ {
+ .type = VASurfaceAttribUsageHint,
+ .flags = VA_SURFACE_ATTRIB_SETTABLE,
+ .value.type = VAGenericValueTypeInteger,
+ .value.value.i = usage_hint,
+ },
+ {
+ .type = VASurfaceAttribPixelFormat,
+ .flags = VA_SURFACE_ATTRIB_SETTABLE,
+ .value.type = VAGenericValueTypeInteger,
+ .value.value.i = fourcc,
+ },
+ {
+ .type = VASurfaceAttribMemoryType,
+ .flags = VA_SURFACE_ATTRIB_SETTABLE,
+ .value.type = VAGenericValueTypeInteger,
+ .value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_VA,
+ },
+ };
+ /* *INDENT-ON* */
+ VAStatus status;
+
+ g_return_val_if_fail (num_surfaces > 0, FALSE);
+
+ gst_va_display_lock (display);
+ status = vaCreateSurfaces (dpy, rt_format, width, height, surfaces,
+ num_surfaces, attrs, G_N_ELEMENTS (attrs));
+ gst_va_display_unlock (display);
+ if (status != VA_STATUS_SUCCESS) {
+ GST_ERROR ("vaCreateSurfaces: %s", vaErrorStr (status));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+_export_surface_to_dmabuf (GstVaDisplay * display, VASurfaceID surface,
+ guint32 flags, VADRMPRIMESurfaceDescriptor * desc)
+{
+ VADisplay dpy = gst_va_display_get_va_dpy (display);
+ VAStatus status;
+
+ gst_va_display_lock (display);
+ status = vaExportSurfaceHandle (dpy, surface,
+ VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2, flags, desc);
+ gst_va_display_unlock (display);
+ if (status != VA_STATUS_SUCCESS) {
+ GST_ERROR ("vaExportSurfaceHandle: %s", vaErrorStr (status));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+_destroy_image (GstVaDisplay * display, VAImageID image_id)
+{
+ VADisplay dpy = gst_va_display_get_va_dpy (display);
+ VAStatus status;
+
+ gst_va_display_lock (display);
+ status = vaDestroyImage (dpy, image_id);
+ gst_va_display_unlock (display);
+ if (status != VA_STATUS_SUCCESS) {
+ GST_ERROR ("vaDestroyImage: %s", vaErrorStr (status));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+_get_derive_image (GstVaDisplay * display, VASurfaceID surface, VAImage * image)
+{
+ VADisplay dpy = gst_va_display_get_va_dpy (display);
+ VAStatus status;
+
+ gst_va_display_lock (display);
+ status = vaDeriveImage (dpy, surface, image);
+ gst_va_display_unlock (display);
+ if (status != VA_STATUS_SUCCESS) {
+ GST_WARNING ("vaDeriveImage: %s", vaErrorStr (status));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+_create_image (GstVaDisplay * display, GstVideoFormat format, gint width,
+ gint height, VAImage * image)
+{
+ VADisplay dpy = gst_va_display_get_va_dpy (display);
+ const VAImageFormat *va_format;
+ VAStatus status;
+
+ va_format = gst_va_image_format_from_video_format (format);
+ if (!va_format)
+ return FALSE;
+
+ gst_va_display_lock (display);
+ status =
+ vaCreateImage (dpy, (VAImageFormat *) va_format, width, height, image);
+ gst_va_display_unlock (display);
+ if (status != VA_STATUS_SUCCESS) {
+ GST_ERROR ("vaCreateImage: %s", vaErrorStr (status));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+_get_image (GstVaDisplay * display, VASurfaceID surface, VAImage * image)
+{
+ VADisplay dpy = gst_va_display_get_va_dpy (display);
+ VAStatus status;
+
+ gst_va_display_lock (display);
+ status = vaGetImage (dpy, surface, 0, 0, image->width, image->height,
+ image->image_id);
+ gst_va_display_unlock (display);
+ if (status != VA_STATUS_SUCCESS) {
+ GST_ERROR ("vaGetImage: %s", vaErrorStr (status));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+_sync_surface (GstVaDisplay * display, VASurfaceID surface)
+{
+ VADisplay dpy = gst_va_display_get_va_dpy (display);
+ VAStatus status;
+
+ gst_va_display_lock (display);
+ status = vaSyncSurface (dpy, surface);
+ gst_va_display_unlock (display);
+ if (status != VA_STATUS_SUCCESS) {
+ GST_WARNING ("vaSyncSurface: %s", vaErrorStr (status));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+_map_buffer (GstVaDisplay * display, VABufferID buffer, gpointer * data)
+{
+ VADisplay dpy = gst_va_display_get_va_dpy (display);
+ VAStatus status;
+
+ gst_va_display_lock (display);
+ status = vaMapBuffer (dpy, buffer, data);
+ gst_va_display_unlock (display);
+ if (status != VA_STATUS_SUCCESS) {
+ GST_WARNING ("vaMapBuffer: %s", vaErrorStr (status));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+_unmap_buffer (GstVaDisplay * display, VABufferID buffer)
+{
+ VADisplay dpy = gst_va_display_get_va_dpy (display);
+ VAStatus status;
+
+ gst_va_display_lock (display);
+ status = vaUnmapBuffer (dpy, buffer);
+ gst_va_display_unlock (display);
+ if (status != VA_STATUS_SUCCESS) {
+ GST_WARNING ("vaUnmapBuffer: %s", vaErrorStr (status));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+_put_image (GstVaDisplay * display, VASurfaceID surface, VAImage * image)
+{
+ VADisplay dpy = gst_va_display_get_va_dpy (display);
+ VAStatus status;
+
+ if (!_sync_surface (display, surface))
+ return FALSE;
+
+ gst_va_display_lock (display);
+ status = vaPutImage (dpy, surface, image->image_id, 0, 0, image->width,
+ image->height, 0, 0, image->width, image->height);
+ gst_va_display_unlock (display);
+ if (status != VA_STATUS_SUCCESS) {
+ GST_ERROR ("vaPutImage: %s", vaErrorStr (status));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static GQuark
+gst_va_buffer_surface_quark (void)
+{
+ static gsize surface_quark = 0;
+
+ if (g_once_init_enter (&surface_quark)) {
+ GQuark quark = g_quark_from_string ("GstVaBufferSurface");
+ g_once_init_leave (&surface_quark, quark);
+ }
+
+ return surface_quark;
+}
+
+static GQuark
+gst_va_drm_mod_quark (void)
+{
+ static gsize drm_mod_quark = 0;
+
+ if (g_once_init_enter (&drm_mod_quark)) {
+ GQuark quark = g_quark_from_string ("DRMModifier");
+ g_once_init_leave (&drm_mod_quark, quark);
+ }
+
+ return drm_mod_quark;
+}
+
+static gpointer
+gst_va_dmabuf_mem_map (GstMemory * gmem, gsize maxsize, GstMapFlags flags)
+{
+ GstVaDmabufAllocator *self = GST_VA_DMABUF_ALLOCATOR (gmem->allocator);
+ VASurfaceID surface = gst_va_memory_get_surface (gmem, NULL);
+
+ _sync_surface (self->display, surface);
+
+ /* @TODO: if mapping with flag GST_MAP_VASURFACE return the
+ * VA_SURFACE_ID.
+ * if mapping and drm_modifers are not lineal, use vaDeriveImage */
+#ifndef GST_DISABLE_GST_DEBUG
+ {
+ guint64 *drm_mod;
+
+ drm_mod = gst_mini_object_get_qdata (GST_MINI_OBJECT (gmem),
+ gst_va_drm_mod_quark ());
+ GST_TRACE_OBJECT (self, "DRM modifiers: %#lx", *drm_mod);
+ }
+#endif
+
+ return self->parent_map (gmem, maxsize, flags);
+}
+
+static void
+gst_va_dmabuf_allocator_dispose (GObject * object)
+{
+ GstVaDmabufAllocator *self = GST_VA_DMABUF_ALLOCATOR (object);
+
+ gst_clear_object (&self->display);
+ gst_atomic_queue_unref (self->queue);
+
+ G_OBJECT_CLASS (dmabuf_parent_class)->dispose (object);
+}
+
+static void
+gst_va_dmabuf_allocator_free (GstAllocator * allocator, GstMemory * mem)
+{
+ GstVaDmabufAllocator *self = GST_VA_DMABUF_ALLOCATOR (allocator);
+ GstVaBufferSurface *buf;
+
+ /* first close the dmabuf fd */
+ GST_ALLOCATOR_CLASS (dmabuf_parent_class)->free (allocator, mem);
+
+ while ((buf = gst_atomic_queue_pop (self->queue))) {
+ GST_LOG_OBJECT (self, "Destroying surface %#x", buf->surface);
+ _destroy_surfaces (self->display, &buf->surface, 1);
+ g_slice_free (GstVaBufferSurface, buf);
+ }
+}
+
+static void
+gst_va_dmabuf_allocator_class_init (GstVaDmabufAllocatorClass * klass)
+{
+ GstAllocatorClass *allocator_class = GST_ALLOCATOR_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = gst_va_dmabuf_allocator_dispose;
+ allocator_class->free = gst_va_dmabuf_allocator_free;
+}
+
+static void
+gst_va_dmabuf_allocator_init (GstVaDmabufAllocator * self)
+{
+ self->queue = gst_atomic_queue_new (2);
+
+ self->parent_map = GST_ALLOCATOR (self)->mem_map;
+ GST_ALLOCATOR (self)->mem_map = gst_va_dmabuf_mem_map;
+}
+
+GstAllocator *
+gst_va_dmabuf_allocator_new (GstVaDisplay * display)
+{
+ GstVaDmabufAllocator *self;
+
+ g_return_val_if_fail (GST_IS_VA_DISPLAY (display), NULL);
+
+ self = g_object_new (GST_TYPE_VA_DMABUF_ALLOCATOR, NULL);
+ self->display = gst_object_ref (display);
+ gst_object_ref_sink (self);
+
+ return GST_ALLOCATOR (self);
+}
+
+static GstVaBufferSurface *
+_create_buffer_surface (GstVaDmabufAllocator * self, VASurfaceID surface,
+ GstVideoFormat format, gint width, gint height)
+{
+ GstVaBufferSurface *buf = g_slice_new (GstVaBufferSurface);
+
+ g_atomic_int_set (&buf->ref_count, 0);
+ buf->surface = surface;
+ gst_video_info_set_format (&buf->info, format, width, height);
+
+ return buf;
+}
+
+static inline goffset
+_get_fd_size (gint fd)
+{
+ return lseek (fd, 0, SEEK_END);
+}
+
+static gboolean
+gst_va_memory_dispose (GstMiniObject * mini_object)
+{
+ GstMemory *mem = GST_MEMORY_CAST (mini_object);
+ GstVaDmabufAllocator *self = GST_VA_DMABUF_ALLOCATOR (mem->allocator);
+ GstVaBufferSurface *buf;
+
+ buf = gst_mini_object_get_qdata (mini_object, gst_va_buffer_surface_quark ());
+ if (buf && g_atomic_int_dec_and_test (&buf->ref_count))
+ gst_atomic_queue_push (self->queue, buf);
+
+ return TRUE;
+}
+
+gboolean
+gst_va_dmabuf_setup_buffer (GstAllocator * allocator, GstBuffer * buffer,
+ GstVaAllocationParams * params)
+{
+ GstVaBufferSurface *buf;
+ GstVaDmabufAllocator *self = GST_VA_DMABUF_ALLOCATOR (allocator);
+ GstVideoFormat format;
+ VADRMPRIMESurfaceDescriptor desc = { 0, };
+ VASurfaceID surface;
+ guint32 i, fourcc, rt_format, export_flags;
+
+ g_return_val_if_fail (GST_IS_VA_DMABUF_ALLOCATOR (allocator), FALSE);
+ g_return_val_if_fail (params, FALSE);
+
+ format = GST_VIDEO_INFO_FORMAT (¶ms->info);
+ fourcc = gst_va_fourcc_from_video_format (format);
+ rt_format = gst_va_chroma_from_video_format (format);
+ if (fourcc == 0 || rt_format == 0) {
+ GST_ERROR_OBJECT (allocator, "Unsupported format: %s",
+ gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (¶ms->info)));
+ return FALSE;
+ }
+
+ if (!_create_surfaces (self->display, rt_format, fourcc,
+ GST_VIDEO_INFO_WIDTH (¶ms->info),
+ GST_VIDEO_INFO_HEIGHT (¶ms->info), params->usage_hint, &surface,
+ 1))
+ return FALSE;
+
+ /* Each layer will contain exactly one plane. For example, an NV12
+ * surface will be exported as two layers */
+ export_flags = VA_EXPORT_SURFACE_SEPARATE_LAYERS
+ | VA_EXPORT_SURFACE_READ_WRITE;
+ if (!_export_surface_to_dmabuf (self->display, surface, export_flags, &desc))
+ goto failed;
+
+ g_assert (GST_VIDEO_INFO_N_PLANES (¶ms->info) == desc.num_layers);
+
+ if (fourcc != desc.fourcc) {
+ GST_ERROR ("Unsupported fourcc: %" GST_FOURCC_FORMAT,
+ GST_FOURCC_ARGS (desc.fourcc));
+ goto failed;
+ }
+
+ buf = _create_buffer_surface (self, surface, format, desc.width, desc.height);
+
+ for (i = 0; i < desc.num_objects; i++) {
+ gint fd = desc.objects[i].fd;
+ gsize size = desc.objects[i].size > 0 ?
+ desc.objects[i].size : _get_fd_size (fd);
+ GstMemory *mem = gst_dmabuf_allocator_alloc (allocator, fd, size);
+ guint64 *drm_mod = g_new (guint64, 1);
+
+ gst_buffer_append_memory (buffer, mem);
+
+ GST_MINI_OBJECT (mem)->dispose = gst_va_memory_dispose;
+
+ g_atomic_int_add (&buf->ref_count, 1);
+ gst_mini_object_set_qdata (GST_MINI_OBJECT (mem),
+ gst_va_buffer_surface_quark (), buf, NULL);
+
+ *drm_mod = desc.objects[i].drm_format_modifier;
+ gst_mini_object_set_qdata (GST_MINI_OBJECT (mem), gst_va_drm_mod_quark (),
+ drm_mod, g_free);
+ }
+
+ for (i = 0; i < desc.num_layers; i++) {
+ g_assert (desc.layers[i].num_planes == 1);
+ GST_VIDEO_INFO_PLANE_OFFSET (&buf->info, i) = desc.layers[i].offset[0];
+ GST_VIDEO_INFO_PLANE_STRIDE (&buf->info, i) = desc.layers[i].pitch[0];
+ }
+
+ GST_VIDEO_INFO_SIZE (&buf->info) = gst_buffer_get_size (buffer);
+ GST_LOG_OBJECT (self, "Created surface %#x [%dx%d] size %" G_GSIZE_FORMAT,
+ buf->surface, GST_VIDEO_INFO_WIDTH (&buf->info),
+ GST_VIDEO_INFO_HEIGHT (&buf->info), GST_VIDEO_INFO_SIZE (&buf->info));
+
+ params->info = buf->info;
+
+ return TRUE;
+
+failed:
+ {
+ _destroy_surfaces (self->display, &surface, 1);
+ return FALSE;
+ }
+}
+
+gboolean
+gst_va_dmabuf_try (GstAllocator * allocator, GstVaAllocationParams * params)
+{
+ GstBuffer *buffer = gst_buffer_new ();
+ GstMapInfo map_info;
+ gboolean ret;
+
+ ret = gst_va_dmabuf_setup_buffer (allocator, buffer, params);
+ if (ret) {
+ /* XXX: radeonsi for kadaveri cannot map dmabufs to user space */
+ if (!gst_buffer_map (buffer, &map_info, GST_MAP_READWRITE)) {
+ GST_WARNING_OBJECT (allocator,
+ "DMABuf backend cannot map frames to user space.");
+ }
+ gst_buffer_unmap (buffer, &map_info);
+ }
+ gst_buffer_unref (buffer);
+
+ return ret;
+}
+
+/*===================== GstVaAllocator / GstVaMemory =========================*/
+
+struct _GstVaAllocator
+{
+ GstAllocator parent;
+
+ GstVaDisplay *display;
+ gboolean use_derived;
+};
+
+typedef struct _GstVaMemory GstVaMemory;
+struct _GstVaMemory
+{
+ GstMemory parent;
+
+ GstVideoInfo info;
+ VASurfaceID surface;
+ GstVideoFormat surface_format;
+ VAImage image;
+ gpointer mapped_data;
+
+ GstMapFlags prev_mapflags;
+ volatile gint map_count;
+
+ gboolean is_derived;
+ gboolean is_dirty;
+ GMutex lock;
+};
+
+G_DEFINE_TYPE_WITH_CODE (GstVaAllocator, gst_va_allocator, GST_TYPE_ALLOCATOR,
+ _init_debug_category ());
+
+static void
+gst_va_allocator_dispose (GObject * object)
+{
+ GstVaAllocator *self = GST_VA_ALLOCATOR (object);
+
+ gst_clear_object (&self->display);
+
+ G_OBJECT_CLASS (gst_va_allocator_parent_class)->dispose (object);
+}
+
+static void
+_va_free (GstAllocator * allocator, GstMemory * mem)
+{
+ GstVaAllocator *self = GST_VA_ALLOCATOR (allocator);
+ GstVaMemory *va_mem = (GstVaMemory *) mem;
+
+ GST_LOG_OBJECT (self, "Destroying surface %#x", va_mem->surface);
+
+ _destroy_surfaces (self->display, &va_mem->surface, 1);
+ g_mutex_clear (&va_mem->lock);
+
+ g_slice_free (GstVaMemory, va_mem);
+}
+
+static void
+gst_va_allocator_class_init (GstVaAllocatorClass * klass)
+{
+ GstAllocatorClass *allocator_class = GST_ALLOCATOR_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = gst_va_allocator_dispose;
+ allocator_class->free = _va_free;
+}
+
+static inline void
+_clean_mem (GstVaMemory * mem)
+{
+ memset (&mem->image, 0, sizeof (mem->image));
+ mem->image.image_id = VA_INVALID_ID;
+ mem->image.buf = VA_INVALID_ID;
+
+ mem->is_derived = TRUE;
+ mem->is_dirty = FALSE;
+ mem->prev_mapflags = 0;
+ mem->mapped_data = NULL;
+}
+
+static void
+_reset_mem (GstVaMemory * mem, GstAllocator * allocator, gsize size)
+{
+ _clean_mem (mem);
+ g_atomic_int_set (&mem->map_count, 0);
+ g_mutex_init (&mem->lock);
+
+ gst_memory_init (GST_MEMORY_CAST (mem), GST_MEMORY_FLAG_NO_SHARE, allocator,
+ NULL, size, 0 /* align */ , 0 /* offset */ , size);
+}
+
+static inline gboolean
+_ensure_image (GstVaDisplay * display, VASurfaceID surface, GstVideoInfo * info,
+ VAImage * image, gboolean * derived)
+{
+ gint i;
+ gboolean try_derived;
+
+ if (image->image_id != VA_INVALID_ID)
+ return TRUE;
+
+ if (!_sync_surface (display, surface))
+ return FALSE;
+
+ try_derived = (derived) ? *derived : FALSE;
+
+ if (try_derived && _get_derive_image (display, surface, image))
+ goto bail;
+ if (!_create_image (display, GST_VIDEO_INFO_FORMAT (info),
+ GST_VIDEO_INFO_WIDTH (info), GST_VIDEO_INFO_HEIGHT (info), image))
+ return FALSE;
+
+ if (derived)
+ *derived = FALSE;
+
+bail:
+ for (i = 0; i < image->num_planes; i++) {
+ GST_VIDEO_INFO_PLANE_OFFSET (info, i) = image->offsets[i];
+ GST_VIDEO_INFO_PLANE_STRIDE (info, i) = image->pitches[i];
+ }
+
+ GST_VIDEO_INFO_SIZE (info) = image->data_size;
+
+ return TRUE;
+}
+
+static gpointer
+_va_map_unlocked (GstVaMemory * mem, GstMapFlags flags)
+{
+ GstAllocator *allocator = GST_MEMORY_CAST (mem)->allocator;
+ GstVaAllocator *va_allocator;
+ GstVaDisplay *display;
+
+ g_return_val_if_fail (mem->surface != VA_INVALID_ID, NULL);
+ g_return_val_if_fail (GST_IS_VA_ALLOCATOR (allocator), NULL);
+
+ if (g_atomic_int_get (&mem->map_count) > 0) {
+ if (mem->prev_mapflags != flags || !mem->mapped_data)
+ return NULL;
+ else
+ goto success;
+ }
+
+ va_allocator = GST_VA_ALLOCATOR (allocator);
+ display = va_allocator->display;
+
+ if (flags & GST_MAP_WRITE) {
+ mem->is_dirty = TRUE;
+ mem->is_derived = FALSE;
+ } else { /* GST_MAP_READ only */
+ mem->is_dirty = FALSE;
+ mem->is_derived = va_allocator->use_derived &&
+ (GST_VIDEO_INFO_FORMAT (&mem->info) == mem->surface_format);
+ }
+
+ if (!_ensure_image (display, mem->surface, &mem->info, &mem->image,
+ &mem->is_derived))
+ return NULL;
+
+ va_allocator->use_derived = mem->is_derived;
+
+ if (!mem->is_derived) {
+ if (!_get_image (display, mem->surface, &mem->image))
+ goto fail;
+ }
+
+ if (!_map_buffer (display, mem->image.buf, &mem->mapped_data))
+ goto fail;
+
+success:
+ {
+ mem->prev_mapflags = flags;
+ g_atomic_int_add (&mem->map_count, 1);
+ return mem->mapped_data;
+ }
+
+fail:
+ {
+ _destroy_image (display, mem->image.image_id);
+ _clean_mem (mem);
+ return NULL;
+ }
+}
+
+static gpointer
+_va_map (GstVaMemory * mem, gsize maxsize, GstMapFlags flags)
+{
+ gpointer data;
+
+ g_mutex_lock (&mem->lock);
+ data = _va_map_unlocked (mem, flags);
+ g_mutex_unlock (&mem->lock);
+
+ return data;
+}
+
+static gboolean
+_va_unmap_unlocked (GstVaMemory * mem)
+{
+ GstAllocator *allocator = GST_MEMORY_CAST (mem)->allocator;
+ GstVaDisplay *display;
+ gboolean ret = TRUE;
+
+ if (!g_atomic_int_dec_and_test (&mem->map_count))
+ return TRUE;
+
+ display = GST_VA_ALLOCATOR (allocator)->display;
+
+ if (mem->image.image_id != VA_INVALID_ID) {
+ if (mem->is_dirty && !mem->is_derived) {
+ ret = _put_image (display, mem->surface, &mem->image);
+ mem->is_dirty = FALSE;
+ }
+ /* XXX(victor): if is derived and is dirty, create another surface
+ * an replace it in mem */
+ }
+
+ ret &= _unmap_buffer (display, mem->image.buf);
+ ret &= _destroy_image (display, mem->image.image_id);
+
+ _clean_mem (mem);
+
+ return ret;
+}
+
+static gboolean
+_va_unmap (GstVaMemory * mem)
+{
+ gboolean ret;
+
+ g_mutex_lock (&mem->lock);
+ ret = _va_unmap_unlocked (mem);
+ g_mutex_unlock (&mem->lock);
+
+ return ret;
+}
+
+/* XXX(victor): shallow copy -- only the surface */
+static GstMemory *
+_va_copy_unlocked (GstVaMemory * mem)
+{
+ GstVaMemory *ret;
+ gsize size;
+
+ ret = g_slice_new (GstVaMemory);
+
+ size = GST_VIDEO_INFO_SIZE (&mem->info);
+
+ ret->info = mem->info;
+ ret->surface = mem->surface;
+
+ _reset_mem (ret, GST_MEMORY_CAST (mem)->allocator, size);
+
+ return GST_MEMORY_CAST (ret);
+}
+
+static GstMemory *
+_va_copy (GstVaMemory * mem, gssize offset, gssize size)
+{
+ GstMemory *ret;
+
+ g_mutex_lock (&mem->lock);
+ ret = _va_copy_unlocked (mem);
+ g_mutex_unlock (&mem->lock);
+
+ return ret;
+}
+
+static GstMemory *
+_va_share (GstMemory * mem, gssize offset, gssize size)
+{
+ /* VA surfaces are opaque structures, which cannot be shared */
+ return NULL;
+}
+
+static gboolean
+_va_is_span (GstMemory * mem1, GstMemory * mem2, gsize * offset)
+{
+ /* VA surfaces are opaque structures, which might live in other
+ * memory. It is impossible to know, so far, if they can mergable. */
+ return FALSE;
+}
+
+static void
+gst_va_allocator_init (GstVaAllocator * self)
+{
+ GstAllocator *allocator = GST_ALLOCATOR (self);
+
+ allocator->mem_type = GST_ALLOCATOR_VASURFACE;
+ allocator->mem_map = (GstMemoryMapFunction) _va_map;
+ allocator->mem_unmap = (GstMemoryUnmapFunction) _va_unmap;
+ allocator->mem_copy = (GstMemoryCopyFunction) _va_copy;
+ allocator->mem_share = _va_share;
+ allocator->mem_is_span = _va_is_span;
+
+ self->use_derived = TRUE;
+
+ GST_OBJECT_FLAG_SET (self, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC);
+}
+
+GstMemory *
+gst_va_allocator_alloc (GstAllocator * allocator,
+ GstVaAllocationParams * params)
+{
+ GstVaAllocator *self;
+ GstVaMemory *mem;
+ GstVideoFormat format;
+ VAImage image = { 0, };
+ VASurfaceID surface;
+ guint32 fourcc, rt_format;
+
+ g_return_val_if_fail (GST_IS_VA_ALLOCATOR (allocator), NULL);
+
+ self = GST_VA_ALLOCATOR (allocator);
+
+ format = GST_VIDEO_INFO_FORMAT (¶ms->info);
+ if (gst_va_video_format_is_extra (format))
+ format = GST_VIDEO_FORMAT_NV12;
+
+ fourcc = gst_va_fourcc_from_video_format (format);
+ rt_format = gst_va_chroma_from_video_format (format);
+ if (fourcc == 0 || rt_format == 0) {
+ GST_ERROR_OBJECT (allocator, "Unsupported format: %s",
+ gst_video_format_to_string (format));
+ return NULL;
+ }
+
+ if (!_create_surfaces (self->display, rt_format, fourcc,
+ GST_VIDEO_INFO_WIDTH (¶ms->info),
+ GST_VIDEO_INFO_HEIGHT (¶ms->info), params->usage_hint, &surface,
+ 1))
+ return NULL;
+
+ image.image_id = VA_INVALID_ID;
+ if (!_ensure_image (self->display, surface, ¶ms->info, &image, NULL))
+ return NULL;
+ _destroy_image (self->display, image.image_id);
+
+ mem = g_slice_new (GstVaMemory);
+
+ mem->surface = surface;
+ mem->surface_format = format;
+ mem->info = params->info;
+
+ _reset_mem (mem, allocator, GST_VIDEO_INFO_SIZE (¶ms->info));
+
+ GST_LOG_OBJECT (self, "Created surface %#x [%dx%d]", mem->surface,
+ GST_VIDEO_INFO_WIDTH (&mem->info), GST_VIDEO_INFO_HEIGHT (&mem->info));
+
+ return GST_MEMORY_CAST (mem);
+}
+
+GstAllocator *
+gst_va_allocator_new (GstVaDisplay * display)
+{
+ GstVaAllocator *self;
+
+ g_return_val_if_fail (GST_IS_VA_DISPLAY (display), NULL);
+
+ self = g_object_new (GST_TYPE_VA_ALLOCATOR, NULL);
+ self->display = gst_object_ref (display);
+ gst_object_ref_sink (self);
+
+ return GST_ALLOCATOR (self);
+}
+
+gboolean
+gst_va_allocator_try (GstAllocator * allocator, GstVaAllocationParams * params)
+{
+ GstMemory *mem;
+
+ mem = gst_va_allocator_alloc (allocator, params);
+ if (!mem)
+ return FALSE;
+ gst_memory_unref (mem);
+ return TRUE;
+}
+
+/*============ Utilities =====================================================*/
+
+VASurfaceID
+gst_va_memory_get_surface (GstMemory * mem, GstVideoInfo * info)
+{
+ VASurfaceID surface = VA_INVALID_ID;
+
+ if (!mem->allocator)
+ return VA_INVALID_ID;
+
+ if (GST_IS_VA_DMABUF_ALLOCATOR (mem->allocator)) {
+ GstVaBufferSurface *buf;
+
+ buf = gst_mini_object_get_qdata (GST_MINI_OBJECT (mem),
+ gst_va_buffer_surface_quark ());
+ if (buf) {
+ if (info)
+ *info = buf->info;
+ surface = buf->surface;
+ }
+ } else if (GST_IS_VA_ALLOCATOR (mem->allocator)) {
+ GstVaMemory *va_mem = (GstVaMemory *) mem;
+ surface = va_mem->surface;
+ if (info)
+ *info = va_mem->info;
+ }
+
+ return surface;
+}
+
+VASurfaceID
+gst_va_buffer_get_surface (GstBuffer * buffer, GstVideoInfo * info)
+{
+ GstMemory *mem;
+
+ mem = gst_buffer_peek_memory (buffer, 0);
+ if (!mem)
+ return VA_INVALID_ID;
+
+ return gst_va_memory_get_surface (mem, info);
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2020 Igalia, S.L.
+ * Author: Víctor Jáquez <vjaquez@igalia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include <gst/allocators/allocators.h>
+#include <gst/video/video.h>
+
+#include "gstvadisplay.h"
+
+G_BEGIN_DECLS
+
+typedef struct _GstVaAllocationParams GstVaAllocationParams;
+struct _GstVaAllocationParams
+{
+ GstVideoInfo info;
+ guint32 usage_hint;
+};
+
+#define GST_TYPE_VA_DMABUF_ALLOCATOR (gst_va_dmabuf_allocator_get_type())
+G_DECLARE_FINAL_TYPE (GstVaDmabufAllocator, gst_va_dmabuf_allocator, GST,
+ VA_DMABUF_ALLOCATOR, GstDmaBufAllocator);
+
+GstAllocator * gst_va_dmabuf_allocator_new (GstVaDisplay * display);
+gboolean gst_va_dmabuf_setup_buffer (GstAllocator * allocator,
+ GstBuffer * buffer,
+ GstVaAllocationParams * params);
+gboolean gst_va_dmabuf_try (GstAllocator * allocator,
+ GstVaAllocationParams * params);
+
+#define GST_TYPE_VA_ALLOCATOR (gst_va_allocator_get_type())
+G_DECLARE_FINAL_TYPE (GstVaAllocator, gst_va_allocator, GST, VA_ALLOCATOR, GstAllocator);
+
+#define GST_ALLOCATOR_VASURFACE "VAMemory"
+
+GstAllocator * gst_va_allocator_new (GstVaDisplay * display);
+GstMemory * gst_va_allocator_alloc (GstAllocator * allocator,
+ GstVaAllocationParams * params);
+gboolean gst_va_allocator_try (GstAllocator * allocator,
+ GstVaAllocationParams * params);
+
+VASurfaceID gst_va_memory_get_surface (GstMemory * mem,
+ GstVideoInfo * info);
+VASurfaceID gst_va_buffer_get_surface (GstBuffer * buffer,
+ GstVideoInfo * info);
+
+G_END_DECLS
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2020 Igalia, S.L.
+ * Author: Víctor Jáquez <vjaquez@igalia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstvacaps.h"
+
+#include <va/va_drmcommon.h>
+
+#include "gstvadisplay.h"
+#include "gstvaprofile.h"
+#include "gstvavideoformat.h"
+
+GST_DEBUG_CATEGORY_EXTERN (gst_va_display_debug);
+#define GST_CAT_DEFAULT gst_va_display_debug
+
+static const guint va_rt_format_list[] = {
+#define R(name) G_PASTE (VA_RT_FORMAT_, name)
+ R (YUV420),
+ R (YUV422),
+ R (YUV444),
+ R (YUV411),
+ R (YUV400),
+ R (YUV420_10),
+ R (YUV422_10),
+ R (YUV444_10),
+ R (YUV420_12),
+ R (YUV422_12),
+ R (YUV444_12),
+ R (YUV420_10BPP),
+ R (RGB16),
+ R (RGB32),
+ R (RGBP),
+ R (RGB32_10),
+ R (RGB32_10BPP),
+ R (PROTECTED),
+#undef R
+};
+
+VASurfaceAttrib *
+gst_va_get_surface_attribs (GstVaDisplay * display, VAConfigID config,
+ guint * attrib_count)
+{
+ VADisplay dpy;
+ VASurfaceAttrib *attribs;
+ VAStatus status;
+
+ dpy = gst_va_display_get_va_dpy (display);
+
+ gst_va_display_lock (display);
+ status = vaQuerySurfaceAttributes (dpy, config, NULL, attrib_count);
+ gst_va_display_unlock (display);
+ if (status != VA_STATUS_SUCCESS) {
+ GST_ERROR_OBJECT (display, "vaQuerySurfaceAttributes: %s",
+ vaErrorStr (status));
+ return NULL;
+ }
+
+ attribs = g_new (VASurfaceAttrib, *attrib_count);
+
+ gst_va_display_lock (display);
+ status = vaQuerySurfaceAttributes (dpy, config, attribs, attrib_count);
+ gst_va_display_unlock (display);
+ if (status != VA_STATUS_SUCCESS) {
+ GST_ERROR_OBJECT (display, "vaQuerySurfaceAttributes: %s",
+ vaErrorStr (status));
+ goto bail;
+ }
+
+ return attribs;
+
+bail:
+ g_free (attribs);
+ return NULL;
+}
+
+static gboolean
+_array_has_format (GArray * formats, GstVideoFormat format)
+{
+ GstVideoFormat fmt;
+ guint i;
+
+ for (i = 0; i < formats->len; i++) {
+ fmt = g_array_index (formats, GstVideoFormat, i);
+ if (fmt == format)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static gboolean
+_gst_caps_set_format_array (GstCaps * caps, GArray * formats)
+{
+ GstVideoFormat fmt;
+ GValue v_formats = G_VALUE_INIT;
+ const gchar *format;
+ guint i;
+
+ if (formats->len == 1) {
+ fmt = g_array_index (formats, GstVideoFormat, 0);
+ if (fmt == GST_VIDEO_FORMAT_UNKNOWN)
+ return FALSE;
+ format = gst_video_format_to_string (fmt);
+ if (!format)
+ return FALSE;
+
+ g_value_init (&v_formats, G_TYPE_STRING);
+ g_value_set_string (&v_formats, format);
+ } else if (formats->len > 1) {
+
+ gst_value_list_init (&v_formats, formats->len);
+
+ for (i = 0; i < formats->len; i++) {
+ GValue item = G_VALUE_INIT;
+
+ fmt = g_array_index (formats, GstVideoFormat, i);
+ if (fmt == GST_VIDEO_FORMAT_UNKNOWN)
+ continue;
+ format = gst_video_format_to_string (fmt);
+ if (!format)
+ continue;
+
+ g_value_init (&item, G_TYPE_STRING);
+ g_value_set_string (&item, format);
+ gst_value_list_append_value (&v_formats, &item);
+ g_value_unset (&item);
+ }
+ } else {
+ return FALSE;
+ }
+
+ gst_caps_set_value (caps, "format", &v_formats);
+ g_value_unset (&v_formats);
+
+ return TRUE;
+}
+
+/* extra formats to add to raw caps bacause *in theory* all drivers
+ * could create images from surface's native format (NV12) to
+ * these. */
+static const GstVideoFormat extra_formats[] = {
+ GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_YV12
+};
+
+gboolean
+gst_va_video_format_is_extra (guint format)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (extra_formats); i++) {
+ if (extra_formats[i] == format)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+GstCaps *
+gst_va_create_raw_caps_from_config (GstVaDisplay * display, VAConfigID config)
+{
+ GArray *formats;
+ GstCaps *caps, *base_caps, *feature_caps;
+ GstCapsFeatures *features;
+ GstVideoFormat format;
+ VASurfaceAttrib *attribs;
+ guint i, attrib_count, mem_type = 0;
+ gint min_width = 1, max_width = G_MAXINT;
+ gint min_height = 1, max_height = G_MAXINT;
+
+ attribs = gst_va_get_surface_attribs (display, config, &attrib_count);
+ if (!attribs)
+ return NULL;
+ formats = g_array_new (FALSE, FALSE, sizeof (GstVideoFormat));
+
+ for (i = 0; i < attrib_count; i++) {
+ if (attribs[i].value.type != VAGenericValueTypeInteger)
+ continue;
+ switch (attribs[i].type) {
+ case VASurfaceAttribPixelFormat:
+ format = gst_va_video_format_from_va_fourcc (attribs[i].value.value.i);
+ if (format != GST_VIDEO_FORMAT_UNKNOWN)
+ g_array_append_val (formats, format);
+ break;
+ case VASurfaceAttribMinWidth:
+ min_width = MAX (min_width, attribs[i].value.value.i);
+ break;
+ case VASurfaceAttribMaxWidth:
+ max_width = attribs[i].value.value.i;
+ break;
+ case VASurfaceAttribMinHeight:
+ min_height = MAX (min_height, attribs[i].value.value.i);
+ break;
+ case VASurfaceAttribMaxHeight:
+ max_height = attribs[i].value.value.i;
+ break;
+ case VASurfaceAttribMemoryType:
+ mem_type = attribs[i].value.value.i;
+ break;
+ default:
+ break;
+ }
+ }
+
+ base_caps = gst_caps_new_simple ("video/x-raw", "width", GST_TYPE_INT_RANGE,
+ min_width, max_width, "height", GST_TYPE_INT_RANGE, min_height,
+ max_height, NULL);
+
+ _gst_caps_set_format_array (base_caps, formats);
+
+ caps = gst_caps_new_empty ();
+
+ if (mem_type & VA_SURFACE_ATTRIB_MEM_TYPE_VA) {
+ feature_caps = gst_caps_copy (base_caps);
+ features = gst_caps_features_from_string ("memory:VAMemory");
+ gst_caps_set_features_simple (feature_caps, features);
+ caps = gst_caps_merge (caps, feature_caps);
+ }
+ if (mem_type & VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME
+ || mem_type & VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2) {
+ feature_caps = gst_caps_copy (base_caps);
+ features = gst_caps_features_from_string ("memory:DMABuf");
+ gst_caps_set_features_simple (feature_caps, features);
+ caps = gst_caps_merge (caps, feature_caps);
+ }
+ /* raw caps */
+ {
+ feature_caps =
+ gst_caps_new_simple ("video/x-raw", "width", GST_TYPE_INT_RANGE,
+ min_width, max_width, "height", GST_TYPE_INT_RANGE, min_height,
+ max_height, NULL);
+
+ if (_array_has_format (formats, GST_VIDEO_FORMAT_NV12)) {
+ for (i = 0; i < G_N_ELEMENTS (extra_formats); i++)
+ g_array_append_val (formats, extra_formats[i]);
+ }
+
+ if (_gst_caps_set_format_array (feature_caps, formats))
+ caps = gst_caps_merge (caps, feature_caps);
+ else
+ gst_clear_caps (&feature_caps);
+ }
+
+ gst_caps_unref (base_caps);
+ g_array_unref (formats);
+ g_free (attribs);
+
+ return caps;
+}
+
+static GstCaps *
+gst_va_create_raw_caps (GstVaDisplay * display, VAProfile profile,
+ VAEntrypoint entrypoint, guint rt_format)
+{
+ GstCaps *caps;
+ VAConfigAttrib attrib = {
+ .type = VAConfigAttribRTFormat,
+ .value = rt_format,
+ };
+ VAConfigID config;
+ VADisplay dpy;
+ VAStatus status;
+
+ dpy = gst_va_display_get_va_dpy (display);
+
+ gst_va_display_lock (display);
+ status = vaCreateConfig (dpy, profile, entrypoint, &attrib, 1, &config);
+ gst_va_display_unlock (display);
+ if (status != VA_STATUS_SUCCESS) {
+ GST_ERROR_OBJECT (display, "vaCreateConfig: %s", vaErrorStr (status));
+ return NULL;
+ }
+
+ caps = gst_va_create_raw_caps_from_config (display, config);
+
+ gst_va_display_lock (display);
+ status = vaDestroyConfig (dpy, config);
+ gst_va_display_unlock (display);
+ if (status != VA_STATUS_SUCCESS) {
+ GST_ERROR_OBJECT (display, "vaDestroyConfig: %s", vaErrorStr (status));
+ return NULL;
+ }
+
+ return caps;
+}
+
+static GstCaps *
+gst_va_create_coded_caps (GstVaDisplay * display, VAProfile profile,
+ VAEntrypoint entrypoint, guint32 * rt_formats_ptr)
+{
+ GstCaps *caps;
+ VAConfigAttrib attribs[] = {
+ {.type = VAConfigAttribMaxPictureWidth,},
+ {.type = VAConfigAttribMaxPictureHeight,},
+ {.type = VAConfigAttribRTFormat,},
+ };
+ VADisplay dpy;
+ VAStatus status;
+ guint32 value, rt_formats = 0;
+ gint i, max_width = -1, max_height = -1;
+
+ dpy = gst_va_display_get_va_dpy (display);
+
+ gst_va_display_lock (display);
+ status = vaGetConfigAttributes (dpy, profile, entrypoint, attribs,
+ G_N_ELEMENTS (attribs));
+ gst_va_display_unlock (display);
+ if (status != VA_STATUS_SUCCESS) {
+ GST_ERROR_OBJECT (display, "vaGetConfigAttributes: %s",
+ vaErrorStr (status));
+ return NULL;
+ }
+
+ for (i = 0; i < G_N_ELEMENTS (attribs); i++) {
+ value = attribs[i].value;
+ if (value == VA_ATTRIB_NOT_SUPPORTED)
+ continue;
+ switch (attribs[i].type) {
+ case VAConfigAttribMaxPictureHeight:
+ if (value <= G_MAXINT)
+ max_height = value;
+ break;
+ case VAConfigAttribMaxPictureWidth:
+ if (value <= G_MAXINT)
+ max_width = value;
+ break;
+ case VAConfigAttribRTFormat:
+ rt_formats = value;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (rt_formats_ptr)
+ *rt_formats_ptr = rt_formats;
+
+ caps = gst_va_profile_caps (profile);
+ if (!caps)
+ return NULL;
+
+ if (max_width == -1 || max_height == -1)
+ return caps;
+
+ gst_caps_set_simple (caps, "width", GST_TYPE_INT_RANGE, 1, max_width,
+ "height", GST_TYPE_INT_RANGE, 1, max_height, NULL);
+
+ return caps;
+}
+
+gboolean
+gst_va_caps_from_profiles (GstVaDisplay * display, GArray * profiles,
+ VAEntrypoint entrypoint, GstCaps ** codedcaps_ptr, GstCaps ** rawcaps_ptr)
+{
+ GstCaps *codedcaps, *rawcaps;
+ VAProfile profile;
+ gboolean ret;
+ gint i, j, k;
+ guint32 rt_formats;
+ gint min_width = 1, max_width = G_MAXINT;
+ gint min_height = 1, max_height = G_MAXINT;
+
+ g_return_val_if_fail (GST_IS_VA_DISPLAY (display), FALSE);
+ g_return_val_if_fail (profiles, FALSE);
+
+ codedcaps = gst_caps_new_empty ();
+ rawcaps = gst_caps_new_empty ();
+
+ for (i = 0; i < profiles->len; i++) {
+ GstCaps *profile_codedcaps;
+
+ profile = g_array_index (profiles, VAProfile, i);
+ profile_codedcaps = gst_va_create_coded_caps (display, profile, entrypoint,
+ &rt_formats);
+ if (!profile_codedcaps)
+ continue;
+
+ for (j = 0; rt_formats && j < G_N_ELEMENTS (va_rt_format_list); j++) {
+ if (rt_formats & va_rt_format_list[j]) {
+ GstCaps *profile_rawcaps = gst_va_create_raw_caps (display, profile,
+ entrypoint, va_rt_format_list[j]);
+
+ if (!profile_rawcaps)
+ continue;
+
+ /* fetch width and height ranges */
+ {
+ guint num_structures = gst_caps_get_size (profile_rawcaps);
+
+ for (k = 0; k < num_structures; k++) {
+ GstStructure *st = gst_caps_get_structure (profile_rawcaps, k);
+ if (!st)
+ continue;
+ if (gst_structure_has_field (st, "width")
+ && gst_structure_has_field (st, "height")) {
+ const GValue *w = gst_structure_get_value (st, "width");
+ const GValue *h = gst_structure_get_value (st, "height");
+
+ min_width = MAX (min_width, gst_value_get_int_range_min (w));
+ max_width = MIN (max_width, gst_value_get_int_range_max (w));
+ min_height = MAX (min_height, gst_value_get_int_range_min (h));
+ max_height = MIN (max_height, gst_value_get_int_range_max (h));
+ }
+ }
+ }
+
+ rawcaps = gst_caps_merge (rawcaps, profile_rawcaps);
+ }
+ }
+
+ /* check frame size range was specified otherwise use the one used
+ * by the rawcaps */
+ {
+ guint num_structures = gst_caps_get_size (profile_codedcaps);
+
+ for (k = 0; k < num_structures; k++) {
+ GstStructure *st = gst_caps_get_structure (profile_codedcaps, k);
+ if (!st)
+ continue;
+ if (!gst_structure_has_field (st, "width"))
+ gst_structure_set (st, "width", GST_TYPE_INT_RANGE, min_width,
+ max_width, NULL);
+ if (!gst_structure_has_field (st, "height"))
+ gst_structure_set (st, "height", GST_TYPE_INT_RANGE, min_height,
+ max_height, NULL);
+ }
+ }
+
+ codedcaps = gst_caps_merge (codedcaps, profile_codedcaps);
+ }
+
+ if (gst_caps_is_empty (rawcaps))
+ gst_caps_replace (&rawcaps, NULL);
+ if (gst_caps_is_empty (codedcaps))
+ gst_caps_replace (&codedcaps, NULL);
+
+ if ((ret = codedcaps && rawcaps)) {
+ rawcaps = gst_caps_simplify (rawcaps);
+ codedcaps = gst_caps_simplify (codedcaps);
+
+ if (rawcaps_ptr)
+ *rawcaps_ptr = gst_caps_ref (rawcaps);
+ if (codedcaps_ptr)
+ *codedcaps_ptr = gst_caps_ref (codedcaps);
+ }
+
+ if (codedcaps)
+ gst_caps_unref (codedcaps);
+ if (rawcaps)
+ gst_caps_unref (rawcaps);
+
+ return ret;
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2020 Igalia, S.L.
+ * Author: Víctor Jáquez <vjaquez@igalia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include "gstvadisplay.h"
+
+G_BEGIN_DECLS
+
+gboolean gst_va_caps_from_profiles (GstVaDisplay * display,
+ GArray * profiles,
+ VAEntrypoint entrypoint,
+ GstCaps ** codedcaps,
+ GstCaps ** rawcaps);
+
+VASurfaceAttrib * gst_va_get_surface_attribs (GstVaDisplay * display,
+ VAConfigID config,
+ guint * attrib_count);
+
+GstCaps * gst_va_create_raw_caps_from_config (GstVaDisplay * display,
+ VAConfigID config);
+
+gboolean gst_va_video_format_is_extra (guint format);
+
+G_END_DECLS
+
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2020 Igalia, S.L.
+ * Author: Víctor Jáquez <vjaquez@igalia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstvadecoder.h"
+
+#include "gstvacaps.h"
+#include "gstvadisplay_wrapped.h"
+
+struct _GstVaDecoder
+{
+ GstObject parent;
+
+ GArray *available_profiles;
+ GstCaps *srcpad_caps;
+ GstCaps *sinkpad_caps;
+ GstVaDisplay *display;
+ VAConfigID config;
+ VAContextID context;
+ VAProfile profile;
+ guint rt_format;
+ gint coded_width;
+ gint coded_height;
+};
+
+GST_DEBUG_CATEGORY_STATIC (gst_va_decoder_debug);
+#define GST_CAT_DEFAULT gst_va_decoder_debug
+
+#define gst_va_decoder_parent_class parent_class
+G_DEFINE_TYPE_WITH_CODE (GstVaDecoder, gst_va_decoder, GST_TYPE_OBJECT,
+ GST_DEBUG_CATEGORY_INIT (gst_va_decoder_debug, "vadecoder", 0,
+ "VA Decoder"));
+
+enum
+{
+ PROP_DISPLAY = 1,
+ PROP_PROFILE,
+ PROP_WIDTH,
+ PROP_HEIGHT,
+ PROP_CHROMA,
+ N_PROPERTIES
+};
+
+static GParamSpec *g_properties[N_PROPERTIES];
+
+static void
+gst_va_decoder_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstVaDecoder *self = GST_VA_DECODER (object);
+
+ switch (prop_id) {
+ case PROP_DISPLAY:{
+ g_assert (!self->display);
+ self->display = g_value_dup_object (value);
+ break;
+ }
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_va_decoder_get_property (GObject * object, guint prop_id, GValue * value,
+ GParamSpec * pspec)
+{
+ GstVaDecoder *self = GST_VA_DECODER (object);
+
+ switch (prop_id) {
+ case PROP_DISPLAY:
+ g_value_set_object (value, self->display);
+ break;
+ case PROP_PROFILE:
+ g_value_set_int (value, self->profile);
+ break;
+ case PROP_CHROMA:
+ g_value_set_uint (value, self->rt_format);
+ break;
+ case PROP_WIDTH:
+ g_value_set_int (value, self->coded_width);
+ break;
+ case PROP_HEIGHT:
+ g_value_set_int (value, self->coded_height);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_va_decoder_dispose (GObject * object)
+{
+ GstVaDecoder *self = GST_VA_DECODER (object);
+
+ gst_va_decoder_close (self);
+
+ g_clear_pointer (&self->available_profiles, g_array_unref);
+ gst_clear_object (&self->display);
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gst_va_decoder_class_init (GstVaDecoderClass * klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->set_property = gst_va_decoder_set_property;
+ gobject_class->get_property = gst_va_decoder_get_property;
+ gobject_class->dispose = gst_va_decoder_dispose;
+
+ g_properties[PROP_DISPLAY] =
+ g_param_spec_object ("display", "GstVaDisplay", "GstVADisplay object",
+ GST_TYPE_VA_DISPLAY,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+ g_properties[PROP_PROFILE] =
+ g_param_spec_int ("va-profile", "VAProfile", "VA Profile",
+ VAProfileNone, 50, VAProfileNone,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ g_properties[PROP_CHROMA] =
+ g_param_spec_uint ("va-rt-format", "VARTFormat", "VA RT Fromat or chroma",
+ VA_RT_FORMAT_YUV420, VA_RT_FORMAT_PROTECTED, VA_RT_FORMAT_YUV420,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ g_properties[PROP_WIDTH] =
+ g_param_spec_int ("coded-width", "coded-picture-width",
+ "coded picture width", 0, G_MAXINT, 0,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ g_properties[PROP_HEIGHT] =
+ g_param_spec_int ("coded-height", "coded-picture-height",
+ "coded picture height", 0, G_MAXINT, 0,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (gobject_class, N_PROPERTIES, g_properties);
+}
+
+static void
+gst_va_decoder_init (GstVaDecoder * self)
+{
+ self->profile = VAProfileNone;
+ self->config = VA_INVALID_ID;
+ self->context = VA_INVALID_ID;
+ self->rt_format = VA_RT_FORMAT_YUV420;
+ self->coded_width = 0;
+ self->coded_height = 0;
+}
+
+static gboolean
+gst_va_decoder_initialize (GstVaDecoder * self, guint32 codec)
+{
+ if (self->available_profiles)
+ return FALSE;
+
+ self->available_profiles = gst_va_display_get_profiles (self->display, codec,
+ VAEntrypointVLD);
+
+ return (self->available_profiles != NULL);
+}
+
+GstVaDecoder *
+gst_va_decoder_new (GstVaDisplay * display, guint32 codec)
+{
+ GstVaDecoder *self;
+
+ g_return_val_if_fail (GST_IS_VA_DISPLAY (display), NULL);
+
+ self = g_object_new (GST_TYPE_VA_DECODER, "display", display, NULL);
+ if (!gst_va_decoder_initialize (self, codec))
+ gst_clear_object (&self);
+
+ return self;
+}
+
+gboolean
+gst_va_decoder_is_open (GstVaDecoder * self)
+{
+ gboolean ret;
+
+ g_return_val_if_fail (GST_IS_VA_DECODER (self), FALSE);
+
+ GST_OBJECT_LOCK (self);
+ ret = (self->config != VA_INVALID_ID && self->profile != VAProfileNone);
+ GST_OBJECT_UNLOCK (self);
+ return ret;
+}
+
+gboolean
+gst_va_decoder_open (GstVaDecoder * self, VAProfile profile, guint rt_format)
+{
+ VAConfigAttrib attrib = {
+ .type = VAConfigAttribRTFormat,
+ .value = rt_format,
+ };
+ VADisplay dpy;
+ VAStatus status;
+
+ g_return_val_if_fail (GST_IS_VA_DECODER (self), FALSE);
+
+ if (self->config != VA_INVALID_ID)
+ return TRUE;
+
+ if (!gst_va_decoder_has_profile (self, profile)) {
+ GST_ERROR_OBJECT (self, "Unsupported profile: %d", profile);
+ return FALSE;
+ }
+
+ dpy = gst_va_display_get_va_dpy (self->display);
+ gst_va_display_lock (self->display);
+ status = vaCreateConfig (dpy, profile, VAEntrypointVLD, &attrib, 1,
+ &self->config);
+ gst_va_display_unlock (self->display);
+ if (status != VA_STATUS_SUCCESS) {
+ GST_ERROR_OBJECT (self, "vaCreateConfig: %s", vaErrorStr (status));
+ return FALSE;
+ }
+
+ GST_OBJECT_LOCK (self);
+ self->profile = profile;
+ self->rt_format = rt_format;
+ GST_OBJECT_UNLOCK (self);
+
+ /* now we should return now only this profile's caps */
+ gst_caps_replace (&self->srcpad_caps, NULL);
+
+ return TRUE;
+}
+
+gboolean
+gst_va_decoder_close (GstVaDecoder * self)
+{
+ VADisplay dpy;
+ VAStatus status;
+
+ g_return_val_if_fail (GST_IS_VA_DECODER (self), FALSE);
+
+ if (!gst_va_decoder_is_open (self))
+ return TRUE;
+
+ dpy = gst_va_display_get_va_dpy (self->display);
+
+ if (self->context != VA_INVALID_ID) {
+ gst_va_display_lock (self->display);
+ status = vaDestroyContext (dpy, self->context);
+ gst_va_display_unlock (self->display);
+ if (status != VA_STATUS_SUCCESS)
+ GST_ERROR_OBJECT (self, "vaDestroyContext: %s", vaErrorStr (status));
+ }
+
+ gst_va_display_lock (self->display);
+ status = vaDestroyConfig (dpy, self->config);
+ gst_va_display_unlock (self->display);
+ if (status != VA_STATUS_SUCCESS) {
+ GST_ERROR_OBJECT (self, "vaDestroyConfig: %s", vaErrorStr (status));
+ return FALSE;
+ }
+
+ GST_OBJECT_LOCK (self);
+ gst_va_decoder_init (self);
+ GST_OBJECT_UNLOCK (self);
+
+ gst_caps_replace (&self->srcpad_caps, NULL);
+ gst_caps_replace (&self->sinkpad_caps, NULL);
+
+ return TRUE;
+}
+
+gboolean
+gst_va_decoder_set_format (GstVaDecoder * self, gint coded_width,
+ gint coded_height, GArray * surfaces)
+{
+ VADisplay dpy;
+ VAStatus status;
+ VASurfaceID *render_targets = NULL;
+ gint num_render_targets = 0;
+
+ g_return_val_if_fail (GST_IS_VA_DECODER (self), FALSE);
+
+ if (self->context != VA_INVALID_ID) {
+ GST_WARNING_OBJECT (self, "decoder already has a format");
+ return TRUE;
+ }
+
+ if (!gst_va_decoder_is_open (self)) {
+ GST_ERROR_OBJECT (self, "decoder has not been opened yet");
+ return FALSE;
+ }
+
+ if (surfaces) {
+ num_render_targets = surfaces->len;
+ render_targets = (VASurfaceID *) surfaces->data;
+ }
+
+ dpy = gst_va_display_get_va_dpy (self->display);
+
+ gst_va_display_lock (self->display);
+ status = vaCreateContext (dpy, self->config, coded_width, coded_height,
+ VA_PROGRESSIVE, render_targets, num_render_targets, &self->context);
+ gst_va_display_unlock (self->display);
+
+ if (status != VA_STATUS_SUCCESS) {
+ GST_ERROR_OBJECT (self, "vaDestroyConfig: %s", vaErrorStr (status));
+ return FALSE;
+ }
+
+ GST_OBJECT_LOCK (self);
+ self->coded_width = coded_width;
+ self->coded_height = coded_height;
+ GST_OBJECT_UNLOCK (self);
+
+ return TRUE;
+}
+
+static gboolean
+_get_codec_caps (GstVaDecoder * self)
+{
+ GstCaps *sinkpad_caps = NULL, *srcpad_caps = NULL;
+
+ if (!gst_va_decoder_is_open (self)
+ && GST_IS_VA_DISPLAY_WRAPPED (self->display)) {
+ if (gst_va_caps_from_profiles (self->display, self->available_profiles,
+ VAEntrypointVLD, &sinkpad_caps, &srcpad_caps)) {
+ gst_caps_replace (&self->sinkpad_caps, sinkpad_caps);
+ gst_caps_replace (&self->srcpad_caps, srcpad_caps);
+ gst_caps_unref (srcpad_caps);
+ gst_caps_unref (sinkpad_caps);
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+GstCaps *
+gst_va_decoder_get_srcpad_caps (GstVaDecoder * self)
+{
+ GstCaps *srcpad_caps = NULL;
+
+ g_return_val_if_fail (GST_IS_VA_DECODER (self), FALSE);
+
+ if (self->srcpad_caps)
+ return gst_caps_ref (self->srcpad_caps);
+
+ if (_get_codec_caps (self))
+ return gst_caps_ref (self->srcpad_caps);
+
+ if (gst_va_decoder_is_open (self)) {
+ srcpad_caps = gst_va_create_raw_caps_from_config (self->display,
+ self->config);
+ gst_caps_replace (&self->srcpad_caps, srcpad_caps);
+ gst_caps_unref (srcpad_caps);
+
+ return gst_caps_ref (self->srcpad_caps);
+ }
+
+ return NULL;
+}
+
+GstCaps *
+gst_va_decoder_get_sinkpad_caps (GstVaDecoder * self)
+{
+ g_return_val_if_fail (GST_IS_VA_DECODER (self), FALSE);
+
+ if (self->sinkpad_caps)
+ return gst_caps_ref (self->sinkpad_caps);
+
+ if (_get_codec_caps (self))
+ return gst_caps_ref (self->sinkpad_caps);
+
+ return NULL;
+}
+
+gboolean
+gst_va_decoder_has_profile (GstVaDecoder * self, VAProfile profile)
+{
+ gint i;
+
+ g_return_val_if_fail (GST_IS_VA_DECODER (self), FALSE);
+
+ if (profile == VAProfileNone)
+ return FALSE;
+
+ for (i = 0; i < self->available_profiles->len; i++) {
+ if (g_array_index (self->available_profiles, VAProfile, i) == profile)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+gint
+gst_va_decoder_get_mem_types (GstVaDecoder * self)
+{
+ VASurfaceAttrib *attribs;
+ guint i, attrib_count;
+ gint ret = 0;
+
+ g_return_val_if_fail (GST_IS_VA_DECODER (self), 0);
+
+ if (!gst_va_decoder_is_open (self))
+ return 0;
+
+ attribs = gst_va_get_surface_attribs (self->display, self->config,
+ &attrib_count);
+ if (!attribs)
+ return 0;
+
+ for (i = 0; i < attrib_count; i++) {
+ if (attribs[i].value.type != VAGenericValueTypeInteger)
+ continue;
+ switch (attribs[i].type) {
+ case VASurfaceAttribMemoryType:
+ ret = attribs[i].value.value.i;
+ break;
+ default:
+ break;
+ }
+ }
+
+ g_free (attribs);
+
+ return ret;
+}
+
+gboolean
+gst_va_decoder_add_param_buffer (GstVaDecoder * self, GstVaDecodePicture * pic,
+ gint type, gpointer data, gsize size)
+{
+ VABufferID buffer;
+ VADisplay dpy;
+ VAStatus status;
+
+ g_return_val_if_fail (GST_IS_VA_DECODER (self), FALSE);
+ g_return_val_if_fail (self->context != VA_INVALID_ID, FALSE);
+ g_return_val_if_fail (pic && data && size > 0, FALSE);
+ g_return_val_if_fail (pic->buffers->len + 1 <= 16, FALSE);
+
+ dpy = gst_va_display_get_va_dpy (self->display);
+ gst_va_display_lock (self->display);
+ status = vaCreateBuffer (dpy, self->context, type, size, 1, data, &buffer);
+ gst_va_display_unlock (self->display);
+ if (status != VA_STATUS_SUCCESS) {
+ GST_ERROR_OBJECT (self, "vaCreateBuffer: %s", vaErrorStr (status));
+ return FALSE;
+ }
+
+ g_array_append_val (pic->buffers, buffer);
+ return TRUE;
+}
+
+gboolean
+gst_va_decoder_add_slice_buffer (GstVaDecoder * self, GstVaDecodePicture * pic,
+ gpointer params_data, gsize params_size, gpointer slice_data,
+ gsize slice_size)
+{
+ VABufferID params_buffer, slice_buffer;
+ VADisplay dpy;
+ VAStatus status;
+
+ g_return_val_if_fail (GST_IS_VA_DECODER (self), FALSE);
+ g_return_val_if_fail (self->context != VA_INVALID_ID, FALSE);
+ g_return_val_if_fail (pic && slice_data && slice_size > 0
+ && params_data && params_size > 0, FALSE);
+
+ dpy = gst_va_display_get_va_dpy (self->display);
+ gst_va_display_lock (self->display);
+ status = vaCreateBuffer (dpy, self->context, VASliceParameterBufferType,
+ params_size, 1, params_data, ¶ms_buffer);
+ gst_va_display_unlock (self->display);
+ if (status != VA_STATUS_SUCCESS) {
+ GST_ERROR_OBJECT (self, "vaCreateBuffer: %s", vaErrorStr (status));
+ return FALSE;
+ }
+
+ gst_va_display_lock (self->display);
+ status = vaCreateBuffer (dpy, self->context, VASliceDataBufferType,
+ slice_size, 1, slice_data, &slice_buffer);
+ gst_va_display_unlock (self->display);
+ if (status != VA_STATUS_SUCCESS) {
+ GST_ERROR_OBJECT (self, "vaCreateBuffer: %s", vaErrorStr (status));
+ return FALSE;
+ }
+
+ g_array_append_val (pic->slices, params_buffer);
+ g_array_append_val (pic->slices, slice_buffer);
+
+ return TRUE;
+}
+
+gboolean
+gst_va_decoder_decode (GstVaDecoder * self, GstVaDecodePicture * pic)
+{
+ VADisplay dpy;
+ VAStatus status;
+ gboolean ret = FALSE;
+
+ g_return_val_if_fail (GST_IS_VA_DECODER (self), FALSE);
+ g_return_val_if_fail (self->context != VA_INVALID_ID, FALSE);
+ g_return_val_if_fail (pic && pic->surface != VA_INVALID_ID, FALSE);
+
+ GST_TRACE_OBJECT (self, "Decode to surface %#x", pic->surface);
+
+ dpy = gst_va_display_get_va_dpy (self->display);
+
+ gst_va_display_lock (self->display);
+ status = vaBeginPicture (dpy, self->context, pic->surface);
+ gst_va_display_unlock (self->display);
+ if (status != VA_STATUS_SUCCESS) {
+ GST_ERROR_OBJECT (self, "vaBeginPicture: %s", vaErrorStr (status));
+ goto fail_end_pic;
+ }
+
+ gst_va_display_lock (self->display);
+ status = vaRenderPicture (dpy, self->context,
+ (VABufferID *) pic->buffers->data, pic->buffers->len);
+ gst_va_display_unlock (self->display);
+ if (status != VA_STATUS_SUCCESS) {
+ GST_ERROR_OBJECT (self, "vaRenderPicture: %s", vaErrorStr (status));
+ goto fail_end_pic;
+ }
+
+ gst_va_display_lock (self->display);
+ status = vaRenderPicture (dpy, self->context,
+ (VABufferID *) pic->slices->data, pic->slices->len);
+ gst_va_display_unlock (self->display);
+ if (status != VA_STATUS_SUCCESS) {
+ GST_ERROR_OBJECT (self, "vaRenderPicture: %s", vaErrorStr (status));
+ goto fail_end_pic;
+ }
+
+ gst_va_display_lock (self->display);
+ status = vaEndPicture (dpy, self->context);
+ gst_va_display_unlock (self->display);
+ if (status != VA_STATUS_SUCCESS) {
+ GST_ERROR_OBJECT (self, "vaEndPicture: %s", vaErrorStr (status));
+ goto bail;
+ }
+
+ ret = TRUE;
+
+bail:
+ gst_va_decoder_destroy_buffers (self, pic);
+
+ return ret;
+
+fail_end_pic:
+ {
+ gst_va_display_lock (self->display);
+ status = vaEndPicture (dpy, self->context);
+ gst_va_display_unlock (self->display);
+ if (status != VA_STATUS_SUCCESS)
+ GST_ERROR_OBJECT (self, "vaEndPicture: %s", vaErrorStr (status));
+ goto bail;
+ }
+}
+
+gboolean
+gst_va_decoder_destroy_buffers (GstVaDecoder * self, GstVaDecodePicture * pic)
+{
+ VABufferID buffer;
+ VADisplay dpy;
+ VAStatus status;
+ guint i;
+ gboolean ret = TRUE;
+
+ g_return_val_if_fail (GST_IS_VA_DECODER (self), FALSE);
+ g_return_val_if_fail (pic && pic->surface != VA_INVALID_ID, FALSE);
+
+ GST_TRACE_OBJECT (self, "Destroy buffers of surface %#x", pic->surface);
+
+ dpy = gst_va_display_get_va_dpy (self->display);
+
+ for (i = 0; i < pic->buffers->len; i++) {
+ buffer = g_array_index (pic->buffers, VABufferID, i);
+ gst_va_display_lock (self->display);
+ status = vaDestroyBuffer (dpy, buffer);
+ gst_va_display_unlock (self->display);
+ if (status != VA_STATUS_SUCCESS) {
+ ret = FALSE;
+ GST_WARNING ("Failed to destroy parameter buffer: %s",
+ vaErrorStr (status));
+ }
+ }
+
+ for (i = 0; i < pic->slices->len; i++) {
+ buffer = g_array_index (pic->slices, VABufferID, i);
+ gst_va_display_lock (self->display);
+ status = vaDestroyBuffer (dpy, buffer);
+ gst_va_display_unlock (self->display);
+ if (status != VA_STATUS_SUCCESS) {
+ ret = FALSE;
+ GST_WARNING ("Failed to destroy slice buffer: %s", vaErrorStr (status));
+ }
+ }
+
+ pic->buffers = g_array_set_size (pic->buffers, 0);
+ pic->slices = g_array_set_size (pic->slices, 0);
+
+ return ret;
+}
+
+
+GstVaDecodePicture *
+gst_va_decoder_new_decode_picture (GstVaDecoder * self, VASurfaceID surface)
+{
+ GstVaDecodePicture *pic;
+
+ g_return_val_if_fail (GST_IS_VA_DECODER (self), NULL);
+ g_return_val_if_fail (surface != VA_INVALID_ID, NULL);
+
+ pic = g_slice_new (GstVaDecodePicture);
+ pic->surface = surface;
+ pic->buffers = g_array_sized_new (FALSE, FALSE, sizeof (VABufferID), 16);
+ pic->slices = g_array_sized_new (FALSE, FALSE, sizeof (VABufferID), 64);
+
+ return pic;
+}
+
+void
+gst_va_decode_picture_free (GstVaDecodePicture * pic)
+{
+ g_return_if_fail (pic);
+
+ if (pic->buffers->len > 0 || pic->slices->len > 0)
+ GST_WARNING ("VABufferID are leaked");
+
+ g_clear_pointer (&pic->buffers, g_array_unref);
+ g_clear_pointer (&pic->slices, g_array_unref);
+
+ g_slice_free (GstVaDecodePicture, pic);
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2020 Igalia, S.L.
+ * Author: Víctor Jáquez <vjaquez@igalia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include "gstvadisplay.h"
+
+G_BEGIN_DECLS
+
+typedef struct _GstVaDecodePicture GstVaDecodePicture;
+struct _GstVaDecodePicture
+{
+ GArray *buffers;
+ GArray *slices;
+ VASurfaceID surface;
+};
+
+#define GST_TYPE_VA_DECODER (gst_va_decoder_get_type())
+G_DECLARE_FINAL_TYPE (GstVaDecoder, gst_va_decoder, GST, VA_DECODER, GstObject)
+
+GstVaDecoder * gst_va_decoder_new (GstVaDisplay * display,
+ guint32 codec);
+gboolean gst_va_decoder_open (GstVaDecoder * self,
+ VAProfile profile,
+ guint rt_format);
+gboolean gst_va_decoder_close (GstVaDecoder * self);
+gboolean gst_va_decoder_is_open (GstVaDecoder * self);
+gboolean gst_va_decoder_set_format (GstVaDecoder * self,
+ gint coded_width,
+ gint coded_height,
+ GArray * surfaces);
+GstCaps * gst_va_decoder_get_srcpad_caps (GstVaDecoder * self);
+GstCaps * gst_va_decoder_get_sinkpad_caps (GstVaDecoder * self);
+gboolean gst_va_decoder_has_profile (GstVaDecoder * self,
+ VAProfile profile);
+gint gst_va_decoder_get_mem_types (GstVaDecoder * self);
+
+gboolean gst_va_decoder_add_param_buffer (GstVaDecoder * self,
+ GstVaDecodePicture * pic,
+ gint type,
+ gpointer data,
+ gsize size);
+gboolean gst_va_decoder_add_slice_buffer (GstVaDecoder * self,
+ GstVaDecodePicture * pic,
+ gpointer params_data,
+ gsize params_size,
+ gpointer slice_data,
+ gsize slice_size);
+gboolean gst_va_decoder_decode (GstVaDecoder * self,
+ GstVaDecodePicture * pic);
+gboolean gst_va_decoder_destroy_buffers (GstVaDecoder * self,
+ GstVaDecodePicture * pic);
+
+GstVaDecodePicture * gst_va_decoder_new_decode_picture (GstVaDecoder * self,
+ VASurfaceID surface);
+void gst_va_decode_picture_free (GstVaDecodePicture * pic);
+
+G_END_DECLS
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2020 Igalia, S.L.
+ * Author: Víctor Jáquez <vjaquez@igalia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstvadevice.h"
+
+#include <gudev/gudev.h>
+#include "gstvadisplay_drm.h"
+
+#define GST_CAT_DEFAULT gstva_debug
+GST_DEBUG_CATEGORY_EXTERN (gstva_debug);
+
+GST_DEFINE_MINI_OBJECT_TYPE (GstVaDevice, gst_va_device);
+
+static void
+gst_va_device_free (GstVaDevice * device)
+{
+ if (device->display)
+ gst_object_unref (device->display);
+ g_free (device->render_device_path);
+ g_free (device);
+}
+
+static GstVaDevice *
+gst_va_device_new (GstVaDisplay * display, const gchar * render_device_path)
+{
+ GstVaDevice *device = g_new0 (GstVaDevice, 1);
+
+ gst_mini_object_init (GST_MINI_OBJECT_CAST (device), 0, GST_TYPE_VA_DEVICE,
+ NULL, NULL, (GstMiniObjectFreeFunction) gst_va_device_free);
+
+ /* take ownership */
+ device->display = display;
+ device->render_device_path = g_strdup (render_device_path);
+
+ return device;
+}
+
+GList *
+gst_va_device_find_devices (void)
+{
+ GUdevClient *client;
+ GList *udev_devices, *dev;
+ GQueue devices = G_QUEUE_INIT;
+
+ client = g_udev_client_new (NULL);
+ udev_devices = g_udev_client_query_by_subsystem (client, "drm");
+
+ for (dev = udev_devices; dev; dev = g_list_next (dev)) {
+ GstVaDisplay *dpy;
+ GUdevDevice *udev = (GUdevDevice *) dev->data;
+ const gchar *path = g_udev_device_get_device_file (udev);
+ const gchar *name = g_udev_device_get_name (udev);
+
+ if (!path || !g_str_has_prefix (name, "renderD")) {
+ GST_LOG ("Ignoring %s in %s", name, path);
+ continue;
+ }
+
+ if (!(dpy = gst_va_display_drm_new_from_path (path)))
+ continue;
+
+ GST_INFO ("Found VA-API device: %s", path);
+ g_queue_push_tail (&devices, gst_va_device_new (dpy, path));
+ }
+
+ g_list_free_full (udev_devices, g_object_unref);
+ g_object_unref (client);
+
+ return devices.head;
+}
+
+void
+gst_va_device_list_free (GList * devices)
+{
+ g_list_free_full (devices, (GDestroyNotify) gst_mini_object_unref);
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2020 Igalia, S.L.
+ * Author: Víctor Jáquez <vjaquez@igalia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+#include "gstvadisplay.h"
+
+#define GST_TYPE_VA_DEVICE (gst_va_device_get_type())
+#define GST_IS_VA_DEVICE(obj) (GST_IS_MINI_OBJECT_TYPE((obj), GST_TYPE_VA_DEVICE))
+#define GST_VA_DEVICE(obj) ((GstVaDevice *)(obj))
+
+typedef struct
+{
+ GstMiniObject mini_object;
+
+ GstVaDisplay *display;
+ gchar *render_device_path;
+} GstVaDevice;
+
+GType gst_va_device_get_type (void);
+GList * gst_va_device_find_devices (void);
+void gst_va_device_list_free (GList * devices);
+
+G_END_DECLS
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2020 Igalia, S.L.
+ * Author: Víctor Jáquez <vjaquez@igalia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstvadisplay.h"
+#include "gstvaprofile.h"
+
+GST_DEBUG_CATEGORY (gst_va_display_debug);
+#define GST_CAT_DEFAULT gst_va_display_debug
+
+typedef struct _GstVaDisplayPrivate GstVaDisplayPrivate;
+struct _GstVaDisplayPrivate
+{
+ GRecMutex lock;
+ VADisplay display;
+
+ gboolean foreign;
+ gboolean init;
+};
+
+#define gst_va_display_parent_class parent_class
+G_DEFINE_TYPE_WITH_CODE (GstVaDisplay, gst_va_display, GST_TYPE_OBJECT,
+ G_ADD_PRIVATE (GstVaDisplay);
+ GST_DEBUG_CATEGORY_INIT (gst_va_display_debug, "vadisplay", 0,
+ "VA Display"));
+enum
+{
+ PROP_VA_DISPLAY = 1,
+ N_PROPERTIES
+};
+
+static GParamSpec *g_properties[N_PROPERTIES];
+
+#define GET_PRIV(obj) gst_va_display_get_instance_private (GST_VA_DISPLAY (obj))
+
+static gboolean
+_gst_va_display_driver_filter (VADisplay display)
+{
+ const char *vendor = vaQueryVendorString (display);
+
+ GST_INFO ("VA-API driver vendor: %s", vendor);
+
+ /* XXX(victor): driver quirks & driver whitelist */
+
+ return TRUE;
+}
+
+static void
+gst_va_display_set_display (GstVaDisplay * self, gpointer display)
+{
+ GstVaDisplayPrivate *priv = GET_PRIV (self);
+
+ if (!display)
+ return;
+
+ if (vaDisplayIsValid (display) == 0) {
+ GST_WARNING_OBJECT (self,
+ "User's VA display is invalid. An internal one will be tried.");
+ return;
+ }
+
+ if (!_gst_va_display_driver_filter (display))
+ return;
+
+ priv->display = display;
+ priv->foreign = TRUE;
+
+ /* assume driver is already initialized */
+ priv->init = TRUE;
+}
+
+static void
+gst_va_display_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstVaDisplay *self = GST_VA_DISPLAY (object);
+
+ switch (prop_id) {
+ case PROP_VA_DISPLAY:{
+ gpointer display = g_value_get_pointer (value);
+ gst_va_display_set_display (self, display);
+ break;
+ }
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_va_display_get_property (GObject * object, guint prop_id, GValue * value,
+ GParamSpec * pspec)
+{
+ GstVaDisplayPrivate *priv = GET_PRIV (object);
+
+ switch (prop_id) {
+ case PROP_VA_DISPLAY:
+ g_value_set_pointer (value, priv->display);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_va_display_constructed (GObject * object)
+{
+ GstVaDisplay *self = GST_VA_DISPLAY (object);
+ GstVaDisplayPrivate *priv = GET_PRIV (object);
+ GstVaDisplayClass *klass = GST_VA_DISPLAY_GET_CLASS (object);
+
+ if (!priv->display && klass->create_va_display)
+ priv->display = klass->create_va_display (self);
+
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+}
+
+static void
+gst_va_display_dispose (GObject * object)
+{
+ GstVaDisplayPrivate *priv = GET_PRIV (object);
+
+ if (priv->display && !priv->foreign)
+ vaTerminate (priv->display);
+ priv->display = NULL;
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gst_va_display_finalize (GObject * object)
+{
+ GstVaDisplayPrivate *priv = GET_PRIV (object);
+
+ g_rec_mutex_clear (&priv->lock);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gst_va_display_class_init (GstVaDisplayClass * klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->set_property = gst_va_display_set_property;
+ gobject_class->get_property = gst_va_display_get_property;
+ gobject_class->constructed = gst_va_display_constructed;
+ gobject_class->dispose = gst_va_display_dispose;
+ gobject_class->finalize = gst_va_display_finalize;
+
+ g_properties[PROP_VA_DISPLAY] =
+ g_param_spec_pointer ("va-display", "VADisplay", "VA Display handler",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (gobject_class, N_PROPERTIES, g_properties);
+}
+
+static void
+gst_va_display_init (GstVaDisplay * self)
+{
+ GstVaDisplayPrivate *priv = GET_PRIV (self);
+
+ g_rec_mutex_init (&priv->lock);
+}
+
+void
+gst_va_display_lock (GstVaDisplay * self)
+{
+ GstVaDisplayPrivate *priv;
+
+ g_return_if_fail (GST_IS_VA_DISPLAY (self));
+
+ priv = GET_PRIV (self);
+
+ g_rec_mutex_lock (&priv->lock);
+}
+
+void
+gst_va_display_unlock (GstVaDisplay * self)
+{
+ GstVaDisplayPrivate *priv;
+
+ g_return_if_fail (GST_IS_VA_DISPLAY (self));
+
+ priv = GET_PRIV (self);
+
+ g_rec_mutex_unlock (&priv->lock);
+}
+
+#ifndef GST_DISABLE_GST_DEBUG
+static gchar *
+_strip_msg (const char *message)
+{
+ gchar *msg = g_strdup (message);
+ if (!msg)
+ return NULL;
+ return g_strstrip (msg);
+}
+
+static void
+_va_warning (gpointer object, const char *message)
+{
+ GstVaDisplay *self = GST_VA_DISPLAY (object);
+ gchar *msg;
+
+ if ((msg = _strip_msg (message))) {
+ GST_WARNING_OBJECT (self, "VA error: %s", msg);
+ g_free (msg);
+ }
+}
+
+static void
+_va_info (gpointer object, const char *message)
+{
+ GstVaDisplay *self = GST_VA_DISPLAY (object);
+ gchar *msg;
+
+ if ((msg = _strip_msg (message))) {
+ GST_INFO_OBJECT (self, "VA info: %s", msg);
+ g_free (msg);
+ }
+}
+#endif
+
+/**
+ * gst_va_display_initialize:
+ * @self: a #GstVaDisplay
+ *
+ * If the display is set by the user (foreign) it is assumed that the
+ * driver is already initialized, thus this function is noop.
+ *
+ * If the display is opened internally, this function will initialize
+ * the driver and it will set driver's message callbacks.
+ *
+ * NOTE: this function is supposed to be private, only used by
+ * GstVaDisplay descendants.
+ *
+ * Returns: %TRUE if the VA driver can be initialized; %FALSE
+ * otherwise
+ **/
+gboolean
+gst_va_display_initialize (GstVaDisplay * self)
+{
+ GstVaDisplayPrivate *priv;
+ VAStatus status;
+ int major_version = -1, minor_version = -1;
+
+ g_return_val_if_fail (GST_IS_VA_DISPLAY (self), FALSE);
+
+ priv = GET_PRIV (self);
+
+ if (priv->init)
+ return TRUE;
+
+ if (!priv->display)
+ return FALSE;
+
+#ifndef GST_DISABLE_GST_DEBUG
+ vaSetErrorCallback (priv->display, _va_warning, self);
+ vaSetInfoCallback (priv->display, _va_info, self);
+#endif
+
+ status = vaInitialize (priv->display, &major_version, &minor_version);
+ if (status != VA_STATUS_SUCCESS) {
+ GST_ERROR_OBJECT (self, "vaInitialize: %s", vaErrorStr (status));
+ return FALSE;
+ }
+
+ GST_INFO_OBJECT (self, "VA-API version %d.%d", major_version, minor_version);
+
+ priv->init = TRUE;
+
+ if (!_gst_va_display_driver_filter (priv->display))
+ return FALSE;
+
+ return TRUE;
+}
+
+VADisplay
+gst_va_display_get_va_dpy (GstVaDisplay * self)
+{
+ VADisplay dpy = 0;
+
+ g_return_val_if_fail (GST_IS_VA_DISPLAY (self), 0);
+
+ g_object_get (self, "va-display", &dpy, NULL);
+ return dpy;
+}
+
+GArray *
+gst_va_display_get_profiles (GstVaDisplay * self, guint32 codec,
+ VAEntrypoint entrypoint)
+{
+ GArray *ret = NULL;
+ VADisplay dpy;
+ VAEntrypoint *entrypoints;
+ VAProfile *profiles;
+ VAStatus status;
+ gint i, j, num_entrypoints = 0, num_profiles = 0;
+
+ g_return_val_if_fail (GST_IS_VA_DISPLAY (self), NULL);
+
+ dpy = gst_va_display_get_va_dpy (self);
+
+ gst_va_display_lock (self);
+ num_profiles = vaMaxNumProfiles (dpy);
+ num_entrypoints = vaMaxNumEntrypoints (dpy);
+ gst_va_display_unlock (self);
+
+ profiles = g_new (VAProfile, num_profiles);
+ entrypoints = g_new (VAEntrypoint, num_entrypoints);
+
+ gst_va_display_lock (self);
+ status = vaQueryConfigProfiles (dpy, profiles, &num_profiles);
+ gst_va_display_unlock (self);
+ if (status != VA_STATUS_SUCCESS) {
+ GST_ERROR ("vaQueryConfigProfile: %s", vaErrorStr (status));
+ goto bail;
+ }
+
+ for (i = 0; i < num_profiles; i++) {
+ if (codec != gst_va_profile_codec (profiles[i]))
+ continue;
+
+ gst_va_display_lock (self);
+ status = vaQueryConfigEntrypoints (dpy, profiles[i], entrypoints,
+ &num_entrypoints);
+ gst_va_display_unlock (self);
+ if (status != VA_STATUS_SUCCESS) {
+ GST_ERROR ("vaQueryConfigEntrypoints: %s", vaErrorStr (status));
+ goto bail;
+ }
+
+ for (j = 0; j < num_entrypoints; j++) {
+ if (entrypoints[j] == entrypoint) {
+ if (!ret)
+ ret = g_array_new (FALSE, FALSE, sizeof (VAProfile));
+ g_array_append_val (ret, profiles[i]);
+ break;
+ }
+ }
+ }
+
+bail:
+ g_free (entrypoints);
+ g_free (profiles);
+ return ret;
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2020 Igalia, S.L.
+ * Author: Víctor Jáquez <vjaquez@igalia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include <gst/gst.h>
+#include <va/va.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_VA_DISPLAY (gst_va_display_get_type())
+G_DECLARE_DERIVABLE_TYPE (GstVaDisplay, gst_va_display, GST, VA_DISPLAY, GstObject)
+
+struct _GstVaDisplayClass
+{
+ /*< private > */
+ GstObjectClass parent_class;
+
+ /*< public > */
+ gpointer (*create_va_display) (GstVaDisplay * self);
+};
+
+void gst_va_display_lock (GstVaDisplay * self);
+void gst_va_display_unlock (GstVaDisplay * self);
+gboolean gst_va_display_initialize (GstVaDisplay * self);
+VADisplay gst_va_display_get_va_dpy (GstVaDisplay * self);
+GArray * gst_va_display_get_profiles (GstVaDisplay * self,
+ guint32 codec,
+ VAEntrypoint entrypoint);
+
+G_END_DECLS
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2020 Igalia, S.L.
+ * Author: Víctor Jáquez <vjaquez@igalia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstvadisplay_drm.h"
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <va/va_drm.h>
+
+#if HAVE_LIBDRM
+#include <xf86drm.h>
+#endif
+
+struct _GstVaDisplayDrm
+{
+ GstVaDisplay parent;
+
+ gchar *path;
+ gint fd;
+};
+
+GST_DEBUG_CATEGORY_EXTERN (gst_va_display_debug);
+#define GST_CAT_DEFAULT gst_va_display_debug
+
+#define gst_va_display_drm_parent_class parent_class
+G_DEFINE_TYPE (GstVaDisplayDrm, gst_va_display_drm, GST_TYPE_VA_DISPLAY);
+
+enum
+{
+ PROP_PATH = 1,
+ N_PROPERTIES
+};
+
+static GParamSpec *g_properties[N_PROPERTIES];
+
+#define MAX_DEVICES 8
+
+static void
+gst_va_display_drm_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstVaDisplayDrm *self = GST_VA_DISPLAY_DRM (object);
+
+ switch (prop_id) {
+ case PROP_PATH:
+ self->path = g_value_dup_string (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_va_display_drm_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstVaDisplayDrm *self = GST_VA_DISPLAY_DRM (object);
+
+ switch (prop_id) {
+ case PROP_PATH:
+ g_value_set_string (value, self->path);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_va_display_drm_finalize (GObject * object)
+{
+ GstVaDisplayDrm *self = GST_VA_DISPLAY_DRM (object);
+
+ g_free (self->path);
+ if (self->fd > -1)
+ close (self->fd);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static gpointer
+gst_va_display_drm_create_va_display (GstVaDisplay * display)
+{
+ int fd, saved_errno = 0;
+ GstVaDisplayDrm *self = GST_VA_DISPLAY_DRM (display);
+
+ fd = open (self->path, O_RDWR);
+ saved_errno = errno;
+ if (fd < 0) {
+ GST_WARNING_OBJECT (self, "Failed to open %s: %s", self->path,
+ g_strerror (saved_errno));
+ close (fd);
+ return 0;
+ }
+#if HAVE_LIBDRM
+ {
+ drmVersion *version;
+
+ version = drmGetVersion (fd);
+ if (!version) {
+ GST_ERROR_OBJECT (self, "Device %s is not a DRM render node", self->path);
+ return 0;
+ }
+ GST_INFO_OBJECT (self, "DRM render node with kernel driver %s",
+ version->name);
+ drmFreeVersion (version);
+ }
+#endif
+
+ self->fd = fd;
+ return vaGetDisplayDRM (self->fd);
+}
+
+static void
+gst_va_display_drm_class_init (GstVaDisplayDrmClass * klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GstVaDisplayClass *vadisplay_class = GST_VA_DISPLAY_CLASS (klass);
+
+ gobject_class->set_property = gst_va_display_drm_set_property;
+ gobject_class->get_property = gst_va_display_drm_get_property;
+ gobject_class->finalize = gst_va_display_drm_finalize;
+
+ vadisplay_class->create_va_display = gst_va_display_drm_create_va_display;
+
+ g_properties[PROP_PATH] =
+ g_param_spec_string ("path", "render-path", "The path of DRM device",
+ "/dev/dri/renderD128",
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
+
+ g_object_class_install_properties (gobject_class, N_PROPERTIES, g_properties);
+}
+
+static void
+gst_va_display_drm_init (GstVaDisplayDrm * self)
+{
+ self->fd = -1;
+}
+
+/**
+ * gst_va_display_drm_new_from_path:
+ * @path: the path to the DRM device
+ *
+ * Creates a new #GstVaDisplay from a DRM device . It will try to open
+ * and operate the device in @path.
+ *
+ * Returns: a newly allocated #GstVaDisplay if the specified DRM
+ * render device could be opened and initialized; otherwise %NULL
+ * is returned.
+ **/
+GstVaDisplay *
+gst_va_display_drm_new_from_path (const gchar * path)
+{
+ GstVaDisplay *dpy;
+
+ g_return_val_if_fail (path, NULL);
+
+ dpy = g_object_new (GST_TYPE_VA_DISPLAY_DRM, "path", path, NULL);
+ if (!gst_va_display_initialize (dpy)) {
+ gst_object_unref (dpy);
+ return NULL;
+ }
+
+ return gst_object_ref_sink (dpy);
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2020 Igalia, S.L.
+ * Author: Víctor Jáquez <vjaquez@igalia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include "gstvadisplay.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_VA_DISPLAY_DRM (gst_va_display_drm_get_type())
+G_DECLARE_FINAL_TYPE (GstVaDisplayDrm, gst_va_display_drm, GST, VA_DISPLAY_DRM,
+ GstVaDisplay)
+
+GstVaDisplay * gst_va_display_drm_new_from_path (const gchar * path);
+
+G_END_DECLS
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2020 Igalia, S.L.
+ * Author: Víctor Jáquez <vjaquez@igalia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstvadisplay_wrapped.h"
+
+struct _GstVaDisplayWrapped
+{
+ GstVaDisplay parent;
+};
+
+#define gst_va_display_wrapped_parent_class parent_class
+G_DEFINE_TYPE (GstVaDisplayWrapped, gst_va_display_wrapped,
+ GST_TYPE_VA_DISPLAY);
+
+static void
+gst_va_display_wrapped_class_init (GstVaDisplayWrappedClass * klass)
+{
+}
+
+static void
+gst_va_display_wrapped_init (GstVaDisplayWrapped * self)
+{
+}
+
+/**
+ * gst_va_display_wrapped_new:
+ * @handle: a #VADisplay to wrap
+ *
+ * Creates a #GstVaDisplay wrapping an already created and initialized
+ * VADisplay.
+ *
+ * Returns: a new #GstVaDisplay if @handle is valid, Otherwise %NULL.
+ **/
+GstVaDisplay *
+gst_va_display_wrapped_new (guintptr handle)
+{
+ GstVaDisplay *dpy;
+
+ g_return_val_if_fail (handle, NULL);
+
+ dpy = g_object_new (GST_TYPE_VA_DISPLAY_WRAPPED, "display", handle, NULL);
+ if (!gst_va_display_initialize (dpy)) {
+ gst_object_unref (dpy);
+ return NULL;
+ }
+
+ return gst_object_ref_sink (dpy);
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2020 Igalia, S.L.
+ * Author: Víctor Jáquez <vjaquez@igalia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include "gstvadisplay.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_VA_DISPLAY_WRAPPED gst_va_display_wrapped_get_type()
+G_DECLARE_FINAL_TYPE (GstVaDisplayWrapped, gst_va_display_wrapped, GST,
+ VA_DISPLAY_WRAPPED, GstVaDisplay)
+
+GstVaDisplay * gst_va_display_wrapped_new (guintptr handle);
+
+G_END_DECLS
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2020 Igalia, S.L.
+ * Author: Víctor Jáquez <vjaquez@igalia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the0
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstvah264dec.h"
+
+#include <va/va_drmcommon.h>
+
+#include "gstvaallocator.h"
+#include "gstvacaps.h"
+#include "gstvadecoder.h"
+#include "gstvadevice.h"
+#include "gstvadisplay_drm.h"
+#include "gstvapool.h"
+#include "gstvaprofile.h"
+#include "gstvavideoformat.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_va_h264dec_debug);
+#define GST_CAT_DEFAULT gst_va_h264dec_debug
+
+#define GST_VA_H264_DEC(obj) ((GstVaH264Dec *) obj)
+#define GST_VA_H264_DEC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_FROM_INSTANCE (obj), GstVaH264DecClass))
+#define GST_VA_H264_DEC_CLASS(klass) ((GstVaH264DecClass *) klass)
+
+typedef struct _GstVaH264Dec GstVaH264Dec;
+typedef struct _GstVaH264DecClass GstVaH264DecClass;
+
+struct _GstVaH264DecClass
+{
+ GstH264DecoderClass parent_class;
+
+ gchar *render_device_path;
+};
+
+struct _GstVaH264Dec
+{
+ GstH264Decoder parent;
+
+ GstVaDecoder *decoder;
+
+ GstBufferPool *other_pool;
+
+ GstFlowReturn last_ret;
+ GstVideoCodecState *output_state;
+
+ VAProfile profile;
+ gint display_width;
+ gint display_height;
+ gint coded_width;
+ gint coded_height;
+ guint rt_format;
+ gint dpb_size;
+
+ gboolean need_negotiation;
+ gboolean need_cropping;
+ gboolean has_videometa;
+ gboolean copy_frames;
+};
+
+static GstElementClass *parent_class = NULL;
+
+struct CData
+{
+ gchar *render_device_path;
+ gchar *description;
+ GstCaps *sink_caps;
+ GstCaps *src_caps;
+};
+
+static gboolean
+gst_va_h264_dec_end_picture (GstH264Decoder * decoder, GstH264Picture * picture)
+{
+ GstVaH264Dec *self = GST_VA_H264_DEC (decoder);
+ GstVaDecodePicture *va_pic;
+
+ GST_LOG_OBJECT (self, "end picture %p, (poc %d)",
+ picture, picture->pic_order_cnt);
+
+ va_pic = gst_h264_picture_get_user_data (picture);
+
+ return gst_va_decoder_decode (self->decoder, va_pic);
+}
+
+static gboolean
+_copy_output_buffer (GstVaH264Dec * self, GstVideoCodecFrame * codec_frame)
+{
+ GstVideoFrame src_frame;
+ GstVideoFrame dest_frame;
+ GstVideoInfo dest_vinfo;
+ GstBuffer *buffer;
+ GstFlowReturn ret;
+
+ if (!self->other_pool)
+ return FALSE;
+
+ if (!gst_buffer_pool_set_active (self->other_pool, TRUE))
+ return FALSE;
+
+ gst_video_info_set_format (&dest_vinfo,
+ GST_VIDEO_INFO_FORMAT (&self->output_state->info), self->display_width,
+ self->display_height);
+
+ ret = gst_buffer_pool_acquire_buffer (self->other_pool, &buffer, NULL);
+ if (ret != GST_FLOW_OK)
+ goto fail;
+
+ if (!gst_video_frame_map (&src_frame, &self->output_state->info,
+ codec_frame->output_buffer, GST_MAP_READ))
+ goto fail;
+
+ if (!gst_video_frame_map (&dest_frame, &dest_vinfo, buffer, GST_MAP_WRITE)) {
+ gst_video_frame_unmap (&dest_frame);
+ goto fail;
+ }
+
+ /* gst_video_frame_copy can crop this, but does not know, so let
+ * make it think it's all right */
+ GST_VIDEO_INFO_WIDTH (&src_frame.info) = self->display_width;
+ GST_VIDEO_INFO_HEIGHT (&src_frame.info) = self->display_height;
+
+ if (!gst_video_frame_copy (&dest_frame, &src_frame)) {
+ gst_video_frame_unmap (&src_frame);
+ gst_video_frame_unmap (&dest_frame);
+ goto fail;
+ }
+
+ gst_video_frame_unmap (&src_frame);
+ gst_video_frame_unmap (&dest_frame);
+ gst_buffer_replace (&codec_frame->output_buffer, buffer);
+ gst_buffer_unref (buffer);
+
+ return TRUE;
+
+fail:
+ GST_ERROR_OBJECT (self, "Failed copy output buffer.");
+ return FALSE;
+}
+
+
+static GstFlowReturn
+gst_va_h264_dec_output_picture (GstH264Decoder * decoder,
+ GstH264Picture * picture)
+{
+ GstVaH264Dec *self = GST_VA_H264_DEC (decoder);
+ GstVideoCodecFrame *frame = NULL;
+
+ GST_LOG_OBJECT (self,
+ "Outputting picture %p (poc %d)", picture, picture->pic_order_cnt);
+
+ frame = gst_video_decoder_get_frame (GST_VIDEO_DECODER (self),
+ picture->system_frame_number);
+
+ if (self->last_ret != GST_FLOW_OK) {
+ gst_video_decoder_drop_frame (GST_VIDEO_DECODER (self), frame);
+ return self->last_ret;
+ }
+
+ if (self->copy_frames)
+ _copy_output_buffer (self, frame);
+
+ GST_BUFFER_PTS (frame->output_buffer) = GST_BUFFER_PTS (frame->input_buffer);
+ GST_BUFFER_DTS (frame->output_buffer) = GST_CLOCK_TIME_NONE;
+ GST_BUFFER_DURATION (frame->output_buffer) =
+ GST_BUFFER_DURATION (frame->input_buffer);
+
+ GST_LOG_OBJECT (self, "Finish frame %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (GST_BUFFER_PTS (frame->output_buffer)));
+
+ return gst_video_decoder_finish_frame (GST_VIDEO_DECODER (self), frame);
+}
+
+static void
+_init_vaapi_pic (VAPictureH264 * va_picture)
+{
+ va_picture->picture_id = VA_INVALID_ID;
+ va_picture->frame_idx = 0;
+ va_picture->flags = VA_PICTURE_H264_INVALID;
+ va_picture->TopFieldOrderCnt = 0;
+ va_picture->BottomFieldOrderCnt = 0;
+}
+
+static void
+_fill_vaapi_pic (VAPictureH264 * va_picture, GstH264Picture * picture)
+{
+ GstVaDecodePicture *va_pic;
+
+ va_pic = gst_h264_picture_get_user_data (picture);
+
+ if (!va_pic) {
+ _init_vaapi_pic (va_picture);
+ return;
+ }
+
+ va_picture->picture_id = va_pic->surface;
+ va_picture->flags = 0;
+
+ if (picture->ref && picture->long_term) {
+ va_picture->flags |= VA_PICTURE_H264_LONG_TERM_REFERENCE;
+ va_picture->frame_idx = picture->long_term_frame_idx;
+ } else {
+ if (picture->ref)
+ va_picture->flags |= VA_PICTURE_H264_SHORT_TERM_REFERENCE;
+ va_picture->frame_idx = picture->frame_num;
+ }
+
+ switch (picture->field) {
+ case GST_H264_PICTURE_FIELD_FRAME:
+ va_picture->TopFieldOrderCnt = picture->top_field_order_cnt;
+ va_picture->BottomFieldOrderCnt = picture->bottom_field_order_cnt;
+ break;
+ case GST_H264_PICTURE_FIELD_TOP_FIELD:
+ va_picture->flags |= VA_PICTURE_H264_TOP_FIELD;
+ va_picture->TopFieldOrderCnt = picture->top_field_order_cnt;
+ va_picture->BottomFieldOrderCnt = 0;
+ break;
+ case GST_H264_PICTURE_FIELD_BOTTOM_FIELD:
+ va_picture->flags |= VA_PICTURE_H264_BOTTOM_FIELD;
+ va_picture->TopFieldOrderCnt = 0;
+ va_picture->BottomFieldOrderCnt = picture->bottom_field_order_cnt;
+ break;
+ default:
+ va_picture->TopFieldOrderCnt = 0;
+ va_picture->BottomFieldOrderCnt = 0;
+ break;
+ }
+}
+
+/* fill the VA API reference picture lists from the GstCodec reference
+ * picture list */
+static void
+_fill_ref_pic_list (VAPictureH264 va_reflist[32], GArray * reflist)
+{
+ guint i;
+
+ for (i = 0; i < reflist->len; i++) {
+ GstH264Picture *picture = g_array_index (reflist, GstH264Picture *, i);
+ _fill_vaapi_pic (&va_reflist[i], picture);
+ }
+
+ for (; i < 32; i++)
+ _init_vaapi_pic (&va_reflist[i]);
+}
+
+static void
+_fill_pred_weight_table (GstH264SliceHdr * header,
+ VASliceParameterBufferH264 * slice_param)
+{
+ GstH264PPS *pps;
+ GstH264SPS *sps;
+ guint num_weight_tables = 0;
+ gint i, j;
+
+ pps = header->pps;
+ sps = pps->sequence;
+
+ if (pps->weighted_pred_flag
+ && (GST_H264_IS_P_SLICE (header) || GST_H264_IS_SP_SLICE (header)))
+ num_weight_tables = 1;
+ else if (pps->weighted_bipred_idc == 1 && GST_H264_IS_B_SLICE (header))
+ num_weight_tables = 2;
+
+ if (num_weight_tables == 0)
+ return;
+
+ slice_param->luma_log2_weight_denom =
+ header->pred_weight_table.luma_log2_weight_denom;
+ slice_param->chroma_log2_weight_denom =
+ header->pred_weight_table.chroma_log2_weight_denom;
+
+ /* VA API also wants the inferred (default) values, not only what is
+ * available in the bitstream (7.4.3.2). */
+
+ slice_param->luma_weight_l0_flag = 1;
+ for (i = 0; i <= slice_param->num_ref_idx_l0_active_minus1; i++) {
+ slice_param->luma_weight_l0[i] =
+ header->pred_weight_table.luma_weight_l0[i];
+ slice_param->luma_offset_l0[i] =
+ header->pred_weight_table.luma_offset_l0[i];
+ }
+
+ slice_param->chroma_weight_l0_flag = sps->chroma_array_type != 0;
+ if (slice_param->chroma_weight_l0_flag) {
+ for (i = 0; i <= slice_param->num_ref_idx_l0_active_minus1; i++) {
+ for (j = 0; j < 2; j++) {
+ slice_param->chroma_weight_l0[i][j] =
+ header->pred_weight_table.chroma_weight_l0[i][j];
+ slice_param->chroma_offset_l0[i][j] =
+ header->pred_weight_table.chroma_offset_l0[i][j];
+ }
+ }
+ }
+
+ if (num_weight_tables == 1)
+ return;
+
+ slice_param->luma_weight_l1_flag = 1;
+ for (i = 0; i <= slice_param->num_ref_idx_l1_active_minus1; i++) {
+ slice_param->luma_weight_l1[i] =
+ header->pred_weight_table.luma_weight_l1[i];
+ slice_param->luma_offset_l1[i] =
+ header->pred_weight_table.luma_offset_l1[i];
+ }
+
+ slice_param->chroma_weight_l1_flag = sps->chroma_array_type != 0;
+ if (slice_param->chroma_weight_l1_flag) {
+ for (i = 0; i <= slice_param->num_ref_idx_l1_active_minus1; i++) {
+ for (j = 0; j < 2; j++) {
+ slice_param->chroma_weight_l1[i][j] =
+ header->pred_weight_table.chroma_weight_l1[i][j];
+ slice_param->chroma_offset_l1[i][j] =
+ header->pred_weight_table.chroma_offset_l1[i][j];
+ }
+ }
+ }
+}
+
+static inline guint
+_get_slice_data_bit_offset (GstH264SliceHdr * header, guint nal_header_bytes)
+{
+ guint epb_count;
+
+ epb_count = header->n_emulation_prevention_bytes;
+ return 8 * nal_header_bytes + header->header_size - epb_count * 8;
+}
+
+static gboolean
+gst_va_h264_dec_decode_slice (GstH264Decoder * decoder,
+ GstH264Picture * picture, GstH264Slice * slice, GArray * ref_pic_list0,
+ GArray * ref_pic_list1)
+{
+ GstH264SliceHdr *header = &slice->header;
+ GstH264NalUnit *nalu = &slice->nalu;
+ GstVaDecodePicture *va_pic;
+ GstVaH264Dec *self = GST_VA_H264_DEC (decoder);
+ VASliceParameterBufferH264 slice_param;
+ gboolean ret;
+
+ GST_TRACE_OBJECT (self, "-");
+
+ /* *INDENT-OFF* */
+ slice_param = (VASliceParameterBufferH264) {
+ .slice_data_size = nalu->size,
+ .slice_data_offset = 0,
+ .slice_data_flag = VA_SLICE_DATA_FLAG_ALL,
+ .slice_data_bit_offset =
+ _get_slice_data_bit_offset (header, nalu->header_bytes),
+ .first_mb_in_slice = header->first_mb_in_slice,
+ .slice_type = header->type % 5,
+ .direct_spatial_mv_pred_flag = header->direct_spatial_mv_pred_flag,
+ .cabac_init_idc = header->cabac_init_idc,
+ .slice_qp_delta = header->slice_qp_delta,
+ .disable_deblocking_filter_idc = header->disable_deblocking_filter_idc,
+ .slice_alpha_c0_offset_div2 = header->slice_alpha_c0_offset_div2,
+ .slice_beta_offset_div2 = header->slice_beta_offset_div2,
+ .num_ref_idx_l0_active_minus1 = header->num_ref_idx_l0_active_minus1,
+ .num_ref_idx_l1_active_minus1 = header->num_ref_idx_l1_active_minus1,
+ };
+ /* *INDENT-ON* */
+
+ _fill_ref_pic_list (slice_param.RefPicList0, ref_pic_list0);
+ _fill_ref_pic_list (slice_param.RefPicList1, ref_pic_list1);
+
+ _fill_pred_weight_table (header, &slice_param);
+
+ va_pic = gst_h264_picture_get_user_data (picture);
+
+ ret = gst_va_decoder_add_slice_buffer (self->decoder, va_pic, &slice_param,
+ sizeof (slice_param), slice->nalu.data + slice->nalu.offset,
+ slice->nalu.size);
+ if (!ret) {
+ gst_va_decoder_destroy_buffers (self->decoder, va_pic);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gst_va_h264_dec_start_picture (GstH264Decoder * decoder,
+ GstH264Picture * picture, GstH264Slice * slice, GstH264Dpb * dpb)
+{
+ GstH264PPS *pps;
+ GstH264SPS *sps;
+ GstVaH264Dec *self = GST_VA_H264_DEC (decoder);
+ GstVaDecodePicture *va_pic;
+ VAIQMatrixBufferH264 iq_matrix = { 0, };
+ VAPictureParameterBufferH264 pic_param;
+ guint i, n;
+
+ GST_TRACE_OBJECT (self, "-");
+
+ va_pic = gst_h264_picture_get_user_data (picture);
+
+ pps = slice->header.pps;
+ sps = pps->sequence;
+
+ /* *INDENT-OFF* */
+ pic_param = (VAPictureParameterBufferH264) {
+ /* .CurrPic */
+ /* .ReferenceFrames */
+ .picture_width_in_mbs_minus1 = sps->pic_width_in_mbs_minus1,
+ .picture_height_in_mbs_minus1 =
+ sps->pic_height_in_map_units_minus1 << !sps->frame_mbs_only_flag,
+ .bit_depth_luma_minus8 = sps->bit_depth_luma_minus8,
+ .bit_depth_chroma_minus8 = sps->bit_depth_chroma_minus8,
+ .num_ref_frames = sps->num_ref_frames,
+ .seq_fields.bits = {
+ .chroma_format_idc = sps->chroma_format_idc,
+ .residual_colour_transform_flag = sps->separate_colour_plane_flag,
+ .gaps_in_frame_num_value_allowed_flag =
+ sps->gaps_in_frame_num_value_allowed_flag,
+ .frame_mbs_only_flag = sps->frame_mbs_only_flag,
+ .mb_adaptive_frame_field_flag = sps->mb_adaptive_frame_field_flag,
+ .direct_8x8_inference_flag = sps->direct_8x8_inference_flag,
+ .MinLumaBiPredSize8x8 = sps->level_idc >= 31, /* A.3.3.2 */
+ .log2_max_frame_num_minus4 = sps->log2_max_frame_num_minus4,
+ .pic_order_cnt_type = sps->pic_order_cnt_type,
+ .log2_max_pic_order_cnt_lsb_minus4 = sps->log2_max_pic_order_cnt_lsb_minus4,
+ .delta_pic_order_always_zero_flag = sps->delta_pic_order_always_zero_flag,
+ },
+ .pic_init_qp_minus26 = pps->pic_init_qp_minus26,
+ .pic_init_qs_minus26 = pps->pic_init_qs_minus26,
+ .chroma_qp_index_offset = pps->chroma_qp_index_offset,
+ .second_chroma_qp_index_offset = pps->second_chroma_qp_index_offset,
+ .pic_fields.bits = {
+ .entropy_coding_mode_flag = pps->entropy_coding_mode_flag,
+ .weighted_pred_flag = pps->weighted_pred_flag,
+ .weighted_bipred_idc = pps->weighted_bipred_idc,
+ .transform_8x8_mode_flag = pps->transform_8x8_mode_flag,
+ .field_pic_flag = slice->header.field_pic_flag,
+ .constrained_intra_pred_flag = pps->constrained_intra_pred_flag,
+ .pic_order_present_flag = pps->pic_order_present_flag,
+ .deblocking_filter_control_present_flag =
+ pps->deblocking_filter_control_present_flag,
+ .redundant_pic_cnt_present_flag = pps->redundant_pic_cnt_present_flag,
+ .reference_pic_flag = picture->nal_ref_idc != 0,
+ },
+ .frame_num = slice->header.frame_num,
+ };
+ /* *INDENT-ON* */
+
+ _fill_vaapi_pic (&pic_param.CurrPic, picture);
+
+ /* reference frames */
+ {
+ GArray *ref_list = g_array_sized_new (FALSE, FALSE,
+ sizeof (GstH264Picture *), 16);
+ g_array_set_clear_func (ref_list, (GDestroyNotify) gst_h264_picture_clear);
+
+ gst_h264_dpb_get_pictures_short_term_ref (dpb, ref_list);
+ for (i = 0; i < 16 && i < ref_list->len; i++) {
+ GstH264Picture *pic = g_array_index (ref_list, GstH264Picture *, i);
+ _fill_vaapi_pic (&pic_param.ReferenceFrames[i], pic);
+ }
+ g_array_set_size (ref_list, 0);
+
+ gst_h264_dpb_get_pictures_long_term_ref (dpb, ref_list);
+ for (; i < 16 && i < ref_list->len; i++) {
+ GstH264Picture *pic = g_array_index (ref_list, GstH264Picture *, i);
+ _fill_vaapi_pic (&pic_param.ReferenceFrames[i], pic);
+ }
+ g_array_unref (ref_list);
+
+ for (; i < 16; i++)
+ _init_vaapi_pic (&pic_param.ReferenceFrames[i]);
+ }
+
+ if (!gst_va_decoder_add_param_buffer (self->decoder, va_pic,
+ VAPictureParameterBufferType, &pic_param, sizeof (pic_param)))
+ goto fail;
+
+ /* there are always 6 4x4 scaling lists */
+ for (i = 0; i < 6; i++) {
+ gst_h264_quant_matrix_4x4_get_raster_from_zigzag (iq_matrix.ScalingList4x4
+ [i], pps->scaling_lists_4x4[i]);
+ }
+
+ /* We need the first 2 entries (Y intra and Y inter for YCbCr 4:2:2 and
+ * less, and the full 6 entries for 4:4:4, see Table 7-2 of the spec for
+ * more details */
+ n = (pps->sequence->chroma_format_idc == 3) ? 6 : 2;
+ for (i = 0; i < n; i++) {
+ gst_h264_quant_matrix_8x8_get_raster_from_zigzag (iq_matrix.ScalingList8x8
+ [i], pps->scaling_lists_8x8[i]);
+ }
+
+ if (!gst_va_decoder_add_param_buffer (self->decoder, va_pic,
+ VAIQMatrixBufferType, &iq_matrix, sizeof (iq_matrix)))
+ goto fail;
+
+ return TRUE;
+
+fail:
+ {
+ gst_va_decoder_destroy_buffers (self->decoder, va_pic);
+ return FALSE;
+ }
+}
+
+static gboolean
+gst_va_h264_dec_new_picture (GstH264Decoder * decoder, GstH264Picture * picture)
+{
+ GstVaH264Dec *self = GST_VA_H264_DEC (decoder);
+ GstVaDecodePicture *pic;
+ GstVideoCodecFrame *frame;
+ GstVideoDecoder *vdec = GST_VIDEO_DECODER (decoder);
+ VASurfaceID surface;
+
+ frame = gst_video_decoder_get_frame (vdec, picture->system_frame_number);
+ if (!frame)
+ return FALSE; /* something failed before */
+ self->last_ret = gst_video_decoder_allocate_output_frame (vdec, frame);
+ if (self->last_ret != GST_FLOW_OK)
+ goto error;
+
+ surface = gst_va_buffer_get_surface (frame->output_buffer, NULL);
+
+ gst_video_codec_frame_unref (frame);
+
+ pic = gst_va_decoder_new_decode_picture (self->decoder, surface);
+ gst_h264_picture_set_user_data (picture, pic,
+ (GDestroyNotify) gst_va_decode_picture_free);
+
+ GST_LOG_OBJECT (self, "New va decode picture %p - %#x", pic, pic->surface);
+
+ return TRUE;
+
+error:
+ {
+ gst_video_codec_frame_unref (frame);
+ return FALSE;
+ }
+}
+
+static inline guint
+_get_num_views (const GstH264SPS * sps)
+{
+ return 1 + (sps->extension_type == GST_H264_NAL_EXTENSION_MVC ?
+ sps->extension.mvc.num_views_minus1 : 0);
+}
+
+static guint
+_get_rtformat (GstVaH264Dec * self, guint8 bit_depth_luma,
+ guint8 chroma_format_idc)
+{
+ switch (bit_depth_luma) {
+ case 10:
+ if (chroma_format_idc == 3)
+ return VA_RT_FORMAT_YUV444_10;
+ if (chroma_format_idc == 2)
+ return VA_RT_FORMAT_YUV422_10;
+ else
+ return VA_RT_FORMAT_YUV420_10;
+ break;
+ case 8:
+ if (chroma_format_idc == 3)
+ return VA_RT_FORMAT_YUV444;
+ if (chroma_format_idc == 2)
+ return VA_RT_FORMAT_YUV422;
+ else
+ return VA_RT_FORMAT_YUV420;
+ break;
+ default:
+ GST_ERROR_OBJECT (self, "Unsupported chroma format: %d "
+ "(with depth luma: %d)", chroma_format_idc, bit_depth_luma);
+ return 0;
+ }
+}
+
+/* *INDENT-OFF* */
+static const struct
+{
+ GstH264Profile profile_idc;
+ VAProfile va_profile;
+} profile_map[] = {
+#define P(idc, va) { G_PASTE (GST_H264_PROFILE_, idc), G_PASTE (VAProfileH264, va) }
+ /* P (BASELINE, ), */
+ P (MAIN, Main),
+ /* P (EXTENDED, ), */
+ P (HIGH, High),
+ /* P (HIGH10, ), */
+ /* P (HIGH_422, ), */
+ /* P (HIGH_444, ), */
+ P (MULTIVIEW_HIGH, MultiviewHigh),
+ P (STEREO_HIGH, StereoHigh),
+ /* P (SCALABLE_BASELINE, ), */
+ /* P (SCALABLE_HIGH, ), */
+#undef P
+};
+/* *INDENT-ON* */
+
+static VAProfile
+_get_profile (GstVaH264Dec * self, const GstH264SPS * sps, gint max_dpb_size)
+{
+ VAProfile profiles[4];
+ gint i = 0, j;
+
+ for (j = 0; j < G_N_ELEMENTS (profile_map); j++) {
+ if (profile_map[j].profile_idc == sps->profile_idc) {
+ profiles[i++] = profile_map[j].va_profile;
+ break;
+ }
+ }
+
+ switch (sps->profile_idc) {
+ case GST_H264_PROFILE_BASELINE:
+ if (sps->constraint_set1_flag) { /* A.2.2 (main profile) */
+ profiles[i++] = VAProfileH264ConstrainedBaseline;
+ profiles[i++] = VAProfileH264Main;
+ }
+ break;
+ case GST_H264_PROFILE_EXTENDED:
+ if (sps->constraint_set1_flag) { /* A.2.2 (main profile) */
+ profiles[i++] = VAProfileH264Main;
+ }
+ break;
+ case GST_H264_PROFILE_MULTIVIEW_HIGH:
+ if (_get_num_views (sps) == 2) {
+ profiles[i++] = VAProfileH264StereoHigh;
+ }
+ if (max_dpb_size <= 16 /* && i965 driver */ ) {
+ profiles[i++] = VAProfileH264MultiviewHigh;
+ }
+ default:
+ break;
+ }
+
+ for (j = 0; j < i && j < G_N_ELEMENTS (profiles); j++) {
+ if (gst_va_decoder_has_profile (self->decoder, profiles[j]))
+ return profiles[j];
+ }
+
+ GST_ERROR_OBJECT (self, "Unsupported profile: %d", sps->profile_idc);
+
+ return VAProfileNone;
+}
+
+static gboolean
+_format_changed (GstVaH264Dec * self, VAProfile new_profile, guint new_rtformat,
+ gint new_width, gint new_height)
+{
+ VAProfile profile = VAProfileNone;
+ guint rt_format = VA_RT_FORMAT_YUV420;
+ gint width = 0, height = 0;
+
+ g_object_get (self->decoder, "va-profile", &profile, "va-rt-format",
+ &rt_format, "coded-width", &width, "coded-height", &height, NULL);
+
+ /* @TODO: Check if current buffers are large enough, and reuse
+ * them */
+ return !(profile == new_profile && rt_format == new_rtformat
+ && width == new_width && height == new_height);
+}
+
+static gboolean
+gst_va_h264_dec_new_sequence (GstH264Decoder * decoder, const GstH264SPS * sps,
+ gint max_dpb_size)
+{
+ GstVaH264Dec *self = GST_VA_H264_DEC (decoder);
+ VAProfile profile;
+ gint display_width;
+ gint display_height;
+ guint rt_format;
+ gboolean negotiation_needed = FALSE;
+
+ if (self->dpb_size < max_dpb_size)
+ self->dpb_size = max_dpb_size;
+
+ if (sps->frame_cropping_flag) {
+ display_width = sps->crop_rect_width;
+ display_height = sps->crop_rect_height;
+ } else {
+ display_width = sps->width;
+ display_height = sps->height;
+ }
+
+ profile = _get_profile (self, sps, max_dpb_size);
+ if (profile == VAProfileNone)
+ return FALSE;
+
+ rt_format = _get_rtformat (self, sps->bit_depth_luma_minus8 + 8,
+ sps->chroma_format_idc);
+ if (rt_format == 0)
+ return FALSE;
+
+ if (_format_changed (self, profile, rt_format, sps->width, sps->height)) {
+ self->profile = profile;
+ self->rt_format = rt_format;
+ self->coded_width = sps->width;
+ self->coded_height = sps->height;
+
+ negotiation_needed = TRUE;
+ GST_INFO_OBJECT (self, "Format changed to %s [%x] (%dx%d)",
+ gst_va_profile_name (profile), rt_format, self->coded_width,
+ self->coded_height);
+ }
+
+ if (self->display_width != display_width
+ || self->display_height != display_height) {
+ self->display_width = display_width;
+ self->display_height = display_height;
+
+ negotiation_needed = TRUE;
+ GST_INFO_OBJECT (self, "Resolution changed to %dx%d", self->display_width,
+ self->display_height);
+ }
+
+ self->need_cropping = self->display_width < self->coded_width
+ || self->display_height < self->coded_height;
+
+ if (negotiation_needed) {
+ self->need_negotiation = TRUE;
+ if (!gst_video_decoder_negotiate (GST_VIDEO_DECODER (self))) {
+ GST_ERROR_OBJECT (self, "Failed to negotiate with downstream");
+ return FALSE;
+ }
+ }
+
+ /* @TODO: check if it is required to copy surfaces to downstream */
+
+ return TRUE;
+}
+
+static gboolean
+gst_va_h264_dec_open (GstVideoDecoder * decoder)
+{
+ GstVaDisplay *display;
+ GstVaH264Dec *self = GST_VA_H264_DEC (decoder);
+ GstVaH264DecClass *klass = GST_VA_H264_DEC_GET_CLASS (decoder);
+
+ /* @XXX(victor): query pipeline's context */
+ display = gst_va_display_drm_new_from_path (klass->render_device_path);
+ if (!display)
+ return FALSE;
+
+ if (!self->decoder)
+ self->decoder = gst_va_decoder_new (display, H264);
+
+ gst_object_unref (display);
+
+ return (self->decoder != NULL);
+}
+
+static gboolean
+gst_va_h264_dec_close (GstVideoDecoder * decoder)
+{
+ GstVaH264Dec *self = GST_VA_H264_DEC (decoder);
+
+ gst_clear_object (&self->decoder);
+
+ return TRUE;
+}
+
+static GstCaps *
+_complete_sink_caps (GstCaps * sinkcaps)
+{
+ GstCaps *caps = gst_caps_copy (sinkcaps);
+ GValue val = G_VALUE_INIT;
+ const gchar *streamformat[] = { "avc", "avc3", "byte-stream" };
+ gint i;
+
+ g_value_init (&val, G_TYPE_STRING);
+ g_value_set_string (&val, "au");
+ gst_caps_set_value (caps, "alignment", &val);
+ g_value_unset (&val);
+
+ gst_value_list_init (&val, G_N_ELEMENTS (streamformat));
+ for (i = 0; i < G_N_ELEMENTS (streamformat); i++) {
+ GValue v = G_VALUE_INIT;
+
+ g_value_init (&v, G_TYPE_STRING);
+ g_value_set_string (&v, streamformat[i]);
+ gst_value_list_append_value (&val, &v);
+ g_value_unset (&v);
+ }
+ gst_caps_set_value (caps, "stream-format", &val);
+ g_value_unset (&val);
+
+ return caps;
+}
+
+static GstCaps *
+gst_va_h264_dec_sink_getcaps (GstVideoDecoder * decoder, GstCaps * filter)
+{
+ GstCaps *sinkcaps, *caps = NULL, *tmp;
+ GstVaH264Dec *self = GST_VA_H264_DEC (decoder);
+
+ if (self->decoder)
+ caps = gst_va_decoder_get_sinkpad_caps (self->decoder);
+
+ if (caps) {
+ sinkcaps = _complete_sink_caps (caps);
+ gst_caps_unref (caps);
+ if (filter) {
+ tmp = gst_caps_intersect_full (filter, sinkcaps,
+ GST_CAPS_INTERSECT_FIRST);
+ gst_caps_unref (sinkcaps);
+ caps = tmp;
+ } else {
+ caps = sinkcaps;
+ }
+ GST_LOG_OBJECT (self, "Returning caps %" GST_PTR_FORMAT, caps);
+ } else if (!caps) {
+ caps = gst_video_decoder_proxy_getcaps (decoder, NULL, filter);
+ }
+
+ return caps;
+}
+
+static gboolean
+gst_va_h264_dec_src_query (GstVideoDecoder * decoder, GstQuery * query)
+{
+ GstVaH264Dec *self = GST_VA_H264_DEC (decoder);
+ gboolean ret = FALSE;
+
+ switch (GST_QUERY_TYPE (query)) {
+ case GST_QUERY_CAPS:{
+ GstCaps *caps = NULL, *tmp, *filter = NULL;
+
+ gst_query_parse_caps (query, &filter);
+ if (self->decoder)
+ caps = gst_va_decoder_get_srcpad_caps (self->decoder);
+ if (caps) {
+ if (filter) {
+ tmp =
+ gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
+ gst_caps_unref (caps);
+ caps = tmp;
+ }
+
+ GST_LOG_OBJECT (self, "Returning caps %" GST_PTR_FORMAT, caps);
+ gst_query_set_caps_result (query, caps);
+ gst_caps_unref (caps);
+ ret = TRUE;
+ break;
+ }
+ /* else jump to default */
+ }
+ default:
+ ret = GST_VIDEO_DECODER_CLASS (parent_class)->src_query (decoder, query);
+ break;
+ }
+
+ return ret;
+}
+
+static gboolean
+gst_va_h264_dec_stop (GstVideoDecoder * decoder)
+{
+ GstVaH264Dec *self = GST_VA_H264_DEC (decoder);
+
+ if (!gst_va_decoder_close (self->decoder))
+ return FALSE;
+
+ if (self->output_state)
+ gst_video_codec_state_unref (self->output_state);
+ self->output_state = NULL;
+
+ if (self->other_pool)
+ gst_buffer_pool_set_active (self->other_pool, FALSE);
+ gst_clear_object (&self->other_pool);
+
+ return GST_VIDEO_DECODER_CLASS (parent_class)->stop (decoder);
+}
+
+static GstVideoFormat
+_default_video_format_from_chroma (guint chroma_type)
+{
+ switch (chroma_type) {
+ case VA_RT_FORMAT_YUV420:
+ case VA_RT_FORMAT_YUV422:
+ case VA_RT_FORMAT_YUV444:
+ return GST_VIDEO_FORMAT_NV12;
+ case VA_RT_FORMAT_YUV420_10:
+ case VA_RT_FORMAT_YUV422_10:
+ case VA_RT_FORMAT_YUV444_10:
+ return GST_VIDEO_FORMAT_P010_10LE;
+ default:
+ return GST_VIDEO_FORMAT_UNKNOWN;
+ }
+}
+
+static void
+_get_preferred_format_and_caps_features (GstVaH264Dec * self,
+ GstVideoFormat * format, GstCapsFeatures ** capsfeatures)
+{
+ GstCaps *peer_caps, *preferred_caps = NULL;
+ GstCapsFeatures *features;
+ GstStructure *structure;
+ const GValue *v_format;
+ guint num_structures, i;
+
+ peer_caps = gst_pad_get_allowed_caps (GST_VIDEO_DECODER_SRC_PAD (self));
+ GST_DEBUG_OBJECT (self, "Allowed caps %" GST_PTR_FORMAT, peer_caps);
+
+ /* prefer memory:VASurface over other caps features */
+ num_structures = gst_caps_get_size (peer_caps);
+ for (i = 0; i < num_structures; i++) {
+ features = gst_caps_get_features (peer_caps, i);
+ structure = gst_caps_get_structure (peer_caps, i);
+
+ if (gst_caps_features_is_any (features))
+ continue;
+
+ if (gst_caps_features_contains (features, "memory:VAMemory")) {
+ preferred_caps = gst_caps_new_full (gst_structure_copy (structure), NULL);
+ gst_caps_set_features_simple (preferred_caps,
+ gst_caps_features_copy (features));
+ break;
+ }
+ }
+
+ if (!preferred_caps)
+ preferred_caps = peer_caps;
+ else
+ gst_clear_caps (&peer_caps);
+
+ if (gst_caps_is_empty (preferred_caps)
+ || gst_caps_is_any (preferred_caps)) {
+ /* if any or not linked yet then system memory and nv12 */
+ if (capsfeatures)
+ *capsfeatures = NULL;
+ if (format)
+ *format = _default_video_format_from_chroma (self->rt_format);
+ goto bail;
+ }
+
+ features = gst_caps_get_features (preferred_caps, 0);
+ if (features && capsfeatures)
+ *capsfeatures = gst_caps_features_copy (features);
+
+ if (!format)
+ goto bail;
+
+ structure = gst_caps_get_structure (preferred_caps, 0);
+ v_format = gst_structure_get_value (structure, "format");
+ if (!v_format)
+ *format = _default_video_format_from_chroma (self->rt_format);
+ else if (G_VALUE_HOLDS_STRING (v_format))
+ *format = gst_video_format_from_string (g_value_get_string (v_format));
+ else if (GST_VALUE_HOLDS_LIST (v_format)) {
+ guint num_values = gst_value_list_get_size (v_format);
+ for (i = 0; i < num_values; i++) {
+ GstVideoFormat fmt;
+ const GValue *v_fmt = gst_value_list_get_value (v_format, i);
+ if (!v_fmt)
+ continue;
+ fmt = gst_video_format_from_string (g_value_get_string (v_fmt));
+ if (gst_va_chroma_from_video_format (fmt) == self->rt_format) {
+ *format = fmt;
+ break;
+ }
+ }
+ if (i == num_values)
+ *format = _default_video_format_from_chroma (self->rt_format);
+ }
+
+bail:
+ gst_clear_caps (&preferred_caps);
+}
+
+static gboolean
+gst_va_h264_dec_negotiate (GstVideoDecoder * decoder)
+{
+ GstVaH264Dec *self = GST_VA_H264_DEC (decoder);
+ GstVideoFormat format = GST_VIDEO_FORMAT_UNKNOWN;
+ GstCapsFeatures *capsfeatures = NULL;
+ GstH264Decoder *h264dec = GST_H264_DECODER (decoder);
+
+ /* Ignore downstream renegotiation request. */
+ if (!self->need_negotiation)
+ return TRUE;
+
+ self->need_negotiation = FALSE;
+
+ if (gst_va_decoder_is_open (self->decoder)
+ && !gst_va_decoder_close (self->decoder))
+ return FALSE;
+
+ if (!gst_va_decoder_open (self->decoder, self->profile, self->rt_format))
+ return FALSE;
+
+ if (!gst_va_decoder_set_format (self->decoder, self->coded_width,
+ self->coded_height, NULL))
+ return FALSE;
+
+ if (self->output_state)
+ gst_video_codec_state_unref (self->output_state);
+
+ _get_preferred_format_and_caps_features (self, &format, &capsfeatures);
+
+ self->output_state =
+ gst_video_decoder_set_output_state (decoder, format,
+ self->display_width, self->display_height, h264dec->input_state);
+
+ self->output_state->caps = gst_video_info_to_caps (&self->output_state->info);
+ if (capsfeatures)
+ gst_caps_set_features_simple (self->output_state->caps, capsfeatures);
+
+ GST_INFO_OBJECT (self, "Negotiated caps %" GST_PTR_FORMAT,
+ self->output_state->caps);
+
+ return GST_VIDEO_DECODER_CLASS (parent_class)->negotiate (decoder);
+}
+
+static inline gboolean
+_caps_is_dmabuf (GstVaH264Dec * self, GstCaps * caps)
+{
+ GstCapsFeatures *features;
+
+ features = gst_caps_get_features (caps, 0);
+ return gst_caps_features_contains (features, GST_CAPS_FEATURE_MEMORY_DMABUF)
+ && (gst_va_decoder_get_mem_types (self->decoder)
+ & VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME);
+}
+
+static inline void
+_shall_copy_frames (GstVaH264Dec * self, GstVideoInfo * info)
+{
+ GstVideoInfo ref_info;
+ guint i;
+
+ self->copy_frames = FALSE;
+
+ if (self->has_videometa)
+ return;
+
+ gst_video_info_set_format (&ref_info, GST_VIDEO_INFO_FORMAT (info),
+ self->display_width, self->display_height);
+
+ for (i = 0; i < GST_VIDEO_INFO_N_PLANES (info); i++) {
+ if (info->stride[i] != ref_info.stride[i] ||
+ info->offset[i] != ref_info.offset[i]) {
+ GST_WARNING_OBJECT (self,
+ "GstVideoMeta support required, copying frames.");
+ self->copy_frames = TRUE;
+ break;
+ }
+ }
+}
+
+static gboolean
+_try_allocator (GstVaH264Dec * self, GstAllocator * allocator, GstCaps * caps,
+ guint * size)
+{
+ GstVaAllocationParams params = {
+ .usage_hint = VA_SURFACE_ATTRIB_USAGE_HINT_DECODER,
+ };
+
+ if (!gst_video_info_from_caps (¶ms.info, caps))
+ return FALSE;
+ if (self->need_cropping) {
+ GST_VIDEO_INFO_WIDTH (¶ms.info) = self->coded_width;
+ GST_VIDEO_INFO_HEIGHT (¶ms.info) = self->coded_height;
+ }
+
+ if (GST_IS_VA_DMABUF_ALLOCATOR (allocator)) {
+ if (!gst_va_dmabuf_try (allocator, ¶ms))
+ return FALSE;
+ } else if (GST_IS_VA_ALLOCATOR (allocator)) {
+ if (!gst_va_allocator_try (allocator, ¶ms))
+ return FALSE;
+ _shall_copy_frames (self, ¶ms.info);
+ } else {
+ return FALSE;
+ }
+
+ if (size)
+ *size = GST_VIDEO_INFO_SIZE (¶ms.info);
+
+ return TRUE;
+}
+
+static GstAllocator *
+_create_allocator (GstVaH264Dec * self, GstCaps * caps, guint * size)
+{
+ GstAllocator *allocator = NULL;
+ GstVaDisplay *display = NULL;
+
+ g_object_get (self->decoder, "display", &display, NULL);
+
+ if (_caps_is_dmabuf (self, caps))
+ allocator = gst_va_dmabuf_allocator_new (display);
+ else
+ allocator = gst_va_allocator_new (display);
+
+ gst_object_unref (display);
+
+ if (!_try_allocator (self, allocator, caps, size))
+ gst_clear_object (&allocator);
+
+ return allocator;
+}
+
+/* 1. get allocator in query
+ * 1.1 if allocator is not ours and downstream doesn't handle
+ * videometa, keep it for other_pool
+ * 2. get pool in query
+ * 2.1 if pool is not va, keep it as other_pool if downstream
+ * doesn't handle videometa or (it doesn't handle alignment and
+ * the stream needs cropping)
+ * 2.2 if there's no pool in query and downstream doesn't handle
+ * videometa, create other_pool as GstVideoPool with the non-va
+ * from query and query's params
+ * 3. create our allocator and pool if they aren't in query
+ * 4. add or update pool and allocator in query
+ * 5. set our custom pool configuration
+ */
+static gboolean
+gst_va_h264_dec_decide_allocation (GstVideoDecoder * decoder, GstQuery * query)
+{
+ GstAllocator *allocator = NULL, *other_allocator = NULL;
+ GstAllocationParams other_params, params;
+ GstBufferPool *pool = NULL;
+ GstCaps *caps = NULL;
+ GstStructure *config;
+ GstVideoInfo info;
+ GstVaH264Dec *self = GST_VA_H264_DEC (decoder);
+ guint size, min, max;
+ gboolean update_pool = FALSE, update_allocator = FALSE, has_videoalignment;
+
+ gst_query_parse_allocation (query, &caps, NULL);
+
+ if (!(caps && gst_video_info_from_caps (&info, caps)))
+ goto wrong_caps;
+
+ self->has_videometa = gst_query_find_allocation_meta (query,
+ GST_VIDEO_META_API_TYPE, NULL);
+
+ if (gst_query_get_n_allocation_params (query) > 0) {
+ gst_query_parse_nth_allocation_param (query, 0, &allocator, &other_params);
+ if (allocator && !self->has_videometa
+ && !GST_IS_VA_DMABUF_ALLOCATOR (allocator)) {
+ /* save the allocator for the other pool */
+ if ((other_allocator = allocator))
+ allocator = NULL;
+ }
+ update_allocator = TRUE;
+ } else {
+ gst_allocation_params_init (&other_params);
+ }
+
+ gst_allocation_params_init (¶ms);
+
+ if (gst_query_get_n_allocation_pools (query) > 0) {
+ gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
+ if (pool) {
+ if (!GST_IS_VA_POOL (pool)) {
+ has_videoalignment = gst_buffer_pool_has_option (pool,
+ GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT);
+ if (!self->has_videometa || (!has_videoalignment
+ && self->need_cropping)) {
+ gst_object_replace ((GstObject **) & self->other_pool,
+ (GstObject *) pool);
+ gst_object_unref (pool); /* decrease previous increase */
+ }
+ gst_clear_object (&pool);
+ }
+ }
+
+ min = MAX (16 + 4, min); /* max num pic references + scratch surfaces */
+ size = MAX (size, GST_VIDEO_INFO_SIZE (&info));
+
+ update_pool = TRUE;
+ } else {
+ size = GST_VIDEO_INFO_SIZE (&info);
+
+ if (!self->has_videometa) {
+ GST_DEBUG_OBJECT (self, "making new other pool for copy");
+ self->other_pool = gst_video_buffer_pool_new ();
+ config = gst_buffer_pool_get_config (self->other_pool);
+ gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
+ gst_buffer_pool_config_set_allocator (config, other_allocator,
+ &other_params);
+ if (!gst_buffer_pool_set_config (self->other_pool, config)) {
+ GST_ERROR_OBJECT (self, "couldn't configure other pool for copy");
+ gst_clear_object (&self->other_pool);
+ }
+ } else {
+ gst_clear_object (&other_allocator);
+ }
+
+ min = 16 + 4; /* max num pic references + scratch surfaces */
+ max = 0;
+ }
+
+ if (!allocator) {
+ if (!(allocator = _create_allocator (self, caps, &size)))
+ return FALSE;
+ }
+
+ if (!pool)
+ pool = gst_va_pool_new ();
+
+ {
+ GstStructure *config = gst_buffer_pool_get_config (pool);
+
+ gst_buffer_pool_config_set_params (config, caps, size, min, max);
+ gst_buffer_pool_config_set_allocator (config, allocator, ¶ms);
+ gst_buffer_pool_config_add_option (config,
+ GST_BUFFER_POOL_OPTION_VIDEO_META);
+
+ if (self->need_cropping) {
+ GstVideoAlignment video_align = {
+ .padding_bottom = self->coded_height - self->display_height,
+ .padding_left = self->coded_width - self->display_width,
+ };
+ gst_buffer_pool_config_add_option (config,
+ GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT);
+ gst_buffer_pool_config_set_video_alignment (config, &video_align);
+ }
+
+ gst_buffer_pool_config_set_va_allocation_params (config,
+ VA_SURFACE_ATTRIB_USAGE_HINT_DECODER);
+
+ if (!gst_buffer_pool_set_config (pool, config))
+ return FALSE;
+ }
+
+ if (update_allocator)
+ gst_query_set_nth_allocation_param (query, 0, allocator, ¶ms);
+ else
+ gst_query_add_allocation_param (query, allocator, ¶ms);
+
+ if (update_pool)
+ gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
+ else
+ gst_query_add_allocation_pool (query, pool, size, min, max);
+
+ gst_object_unref (allocator);
+ gst_object_unref (pool);
+
+ return GST_VIDEO_DECODER_CLASS (parent_class)->decide_allocation (decoder,
+ query);
+
+wrong_caps:
+ {
+ GST_WARNING_OBJECT (self, "No valid caps");
+ return FALSE;
+ }
+}
+
+static void
+gst_va_h264_dec_dispose (GObject * object)
+{
+ gst_va_h264_dec_close (GST_VIDEO_DECODER (object));
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gst_va_h264_dec_class_init (gpointer g_class, gpointer class_data)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (g_class);
+ GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+ GstH264DecoderClass *h264decoder_class = GST_H264_DECODER_CLASS (g_class);
+ GstVaH264DecClass *klass = GST_VA_H264_DEC_CLASS (g_class);
+ GstVideoDecoderClass *decoder_class = GST_VIDEO_DECODER_CLASS (g_class);
+ struct CData *cdata = class_data;
+ gchar *long_name;
+
+ parent_class = g_type_class_peek_parent (g_class);
+
+ klass->render_device_path = cdata->render_device_path;
+
+ if (cdata->description) {
+ long_name = g_strdup_printf ("VA-API H.264 Decoder in %s",
+ cdata->description);
+ } else {
+ long_name = g_strdup ("VA-API H.264 Decoder");
+ }
+
+ gst_element_class_set_metadata (element_class, long_name,
+ "Codec/Decoder/Video/Hardware",
+ "VA-API based H.264 video decoder",
+ "Víctor Jáquez <vjaquez@igalia.com>");
+
+ gst_element_class_add_pad_template (element_class,
+ gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
+ cdata->sink_caps));
+ gst_element_class_add_pad_template (element_class,
+ gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
+ cdata->src_caps));
+
+ gobject_class->dispose = gst_va_h264_dec_dispose;
+
+ decoder_class->open = GST_DEBUG_FUNCPTR (gst_va_h264_dec_open);
+ decoder_class->close = GST_DEBUG_FUNCPTR (gst_va_h264_dec_close);
+ decoder_class->stop = GST_DEBUG_FUNCPTR (gst_va_h264_dec_stop);
+ decoder_class->getcaps = GST_DEBUG_FUNCPTR (gst_va_h264_dec_sink_getcaps);
+ decoder_class->src_query = GST_DEBUG_FUNCPTR (gst_va_h264_dec_src_query);
+ decoder_class->negotiate = GST_DEBUG_FUNCPTR (gst_va_h264_dec_negotiate);
+ decoder_class->decide_allocation =
+ GST_DEBUG_FUNCPTR (gst_va_h264_dec_decide_allocation);
+
+ h264decoder_class->new_sequence =
+ GST_DEBUG_FUNCPTR (gst_va_h264_dec_new_sequence);
+ h264decoder_class->decode_slice =
+ GST_DEBUG_FUNCPTR (gst_va_h264_dec_decode_slice);
+
+ h264decoder_class->new_picture =
+ GST_DEBUG_FUNCPTR (gst_va_h264_dec_new_picture);
+ h264decoder_class->output_picture =
+ GST_DEBUG_FUNCPTR (gst_va_h264_dec_output_picture);
+ h264decoder_class->start_picture =
+ GST_DEBUG_FUNCPTR (gst_va_h264_dec_start_picture);
+ h264decoder_class->end_picture =
+ GST_DEBUG_FUNCPTR (gst_va_h264_dec_end_picture);
+
+ g_free (long_name);
+ g_free (cdata->description);
+ gst_caps_unref (cdata->src_caps);
+ gst_caps_unref (cdata->sink_caps);
+ g_free (cdata);
+}
+
+static void
+gst_va_h264_dec_init (GTypeInstance * instance, gpointer g_class)
+{
+ gst_h264_decoder_set_process_ref_pic_lists (GST_H264_DECODER (instance),
+ TRUE);
+}
+
+static gpointer
+_register_debug_category (gpointer data)
+{
+ GST_DEBUG_CATEGORY_INIT (gst_va_h264dec_debug, "vah264dec", 0,
+ "VA h264 decoder");
+
+ return NULL;
+}
+
+gboolean
+gst_va_h264_dec_register (GstPlugin * plugin, GstVaDevice * device,
+ GstCaps * sink_caps, GstCaps * src_caps, guint rank)
+{
+ static GOnce debug_once = G_ONCE_INIT;
+ GType type;
+ GTypeInfo type_info = {
+ .class_size = sizeof (GstVaH264DecClass),
+ .class_init = gst_va_h264_dec_class_init,
+ .instance_size = sizeof (GstVaH264Dec),
+ .instance_init = gst_va_h264_dec_init,
+ };
+ struct CData *cdata;
+ gboolean ret;
+ gchar *type_name, *feature_name;
+
+ g_return_val_if_fail (GST_IS_PLUGIN (plugin), FALSE);
+ g_return_val_if_fail (GST_IS_VA_DEVICE (device), FALSE);
+ g_return_val_if_fail (GST_IS_CAPS (sink_caps), FALSE);
+ g_return_val_if_fail (GST_IS_CAPS (src_caps), FALSE);
+
+ cdata = g_new (struct CData, 1);
+ cdata->description = NULL;
+ cdata->render_device_path = g_strdup (device->render_device_path);
+ cdata->sink_caps = _complete_sink_caps (sink_caps);
+ cdata->src_caps = gst_caps_ref (src_caps);
+
+ /* class data will be leaked if the element never gets instantiated */
+ GST_MINI_OBJECT_FLAG_SET (cdata->sink_caps,
+ GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED);
+ GST_MINI_OBJECT_FLAG_SET (src_caps, GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED);
+
+ type_info.class_data = cdata;
+
+ type_name = g_strdup ("GstVaH264Dec");
+ feature_name = g_strdup ("vah264dec");
+
+ /* The first decoder to be registered should use a constant name,
+ * like vah264dec, for any additional decoders, we create unique
+ * names, using inserting the render device name. */
+ if (g_type_from_name (type_name)) {
+ gchar *basename = g_path_get_basename (device->render_device_path);
+ g_free (type_name);
+ g_free (feature_name);
+ type_name = g_strdup_printf ("GstVa%sH264Dec", basename);
+ feature_name = g_strdup_printf ("va%sh264dec", basename);
+ cdata->description = basename;
+
+ /* lower rank for non-first device */
+ if (rank > 0)
+ rank--;
+ }
+
+ g_once (&debug_once, _register_debug_category, NULL);
+
+ type = g_type_register_static (GST_TYPE_H264_DECODER,
+ type_name, &type_info, 0);
+
+ ret = gst_element_register (plugin, feature_name, rank, type);
+
+ g_free (type_name);
+ g_free (feature_name);
+
+ return ret;
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2020 Igalia, S.L.
+ * Author: Víctor Jáquez <vjaquez@igalia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include <gst/codecs/gsth264decoder.h>
+
+#include "gstvadevice.h"
+
+G_BEGIN_DECLS
+
+gboolean gst_va_h264_dec_register (GstPlugin * plugin,
+ GstVaDevice * device,
+ GstCaps * sink_caps,
+ GstCaps * src_caps,
+ guint rank);
+
+G_END_DECLS
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2020 Igalia, S.L.
+ * Author: Víctor Jáquez <vjaquez@igalia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstvapool.h"
+
+#include "gstvaallocator.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_va_pool_debug);
+#define GST_CAT_DEFAULT gst_va_pool_debug
+
+struct _GstVaPool
+{
+ GstBufferPool parent;
+
+ GstVideoInfo alloc_info;
+ GstVideoInfo caps_info;
+ GstAllocator *allocator;
+ guint32 usage_hint;
+ gboolean add_videometa;
+ gboolean need_alignment;
+ GstVideoAlignment video_align;
+};
+
+#define gst_va_pool_parent_class parent_class
+G_DEFINE_TYPE_WITH_CODE (GstVaPool, gst_va_pool, GST_TYPE_BUFFER_POOL,
+ GST_DEBUG_CATEGORY_INIT (gst_va_pool_debug, "vapool", 0, "VA Pool"));
+
+static const gchar **
+gst_va_pool_get_options (GstBufferPool * pool)
+{
+ static const gchar *options[] = { GST_BUFFER_POOL_OPTION_VIDEO_META,
+ GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT, NULL
+ };
+ return options;
+}
+
+static inline gboolean
+gst_buffer_pool_config_get_va_allocation_params (GstStructure * config,
+ guint32 * usage_hint)
+{
+ if (!gst_structure_get (config, "usage-hint", G_TYPE_UINT, usage_hint, NULL))
+ *usage_hint = VA_SURFACE_ATTRIB_USAGE_HINT_GENERIC;
+
+ return TRUE;
+}
+
+static gboolean
+gst_va_pool_set_config (GstBufferPool * pool, GstStructure * config)
+{
+ GstAllocator *allocator;
+ GstCaps *caps;
+ GstVaPool *vpool = GST_VA_POOL (pool);
+ GstVideoAlignment video_align = { 0, };
+ GstVideoInfo caps_info, alloc_info;
+ gint width, height;
+ guint size, min_buffers, max_buffers;
+ guint32 usage_hint;
+
+ if (!gst_buffer_pool_config_get_params (config, &caps, &size, &min_buffers,
+ &max_buffers))
+ goto wrong_config;
+
+ if (!caps)
+ goto no_caps;
+
+ if (!gst_video_info_from_caps (&caps_info, caps))
+ goto wrong_caps;
+
+ if (size < GST_VIDEO_INFO_SIZE (&caps_info))
+ goto wrong_size;
+
+ if (!gst_buffer_pool_config_get_allocator (config, &allocator, NULL))
+ goto wrong_config;
+
+ if (!(allocator && (GST_IS_VA_DMABUF_ALLOCATOR (allocator)
+ || GST_IS_VA_ALLOCATOR (allocator))))
+ goto wrong_config;
+
+ if (!gst_buffer_pool_config_get_va_allocation_params (config, &usage_hint))
+ goto wrong_config;
+
+ width = GST_VIDEO_INFO_WIDTH (&caps_info);
+ height = GST_VIDEO_INFO_HEIGHT (&caps_info);
+
+ GST_LOG_OBJECT (vpool, "%dx%d - %u | caps %" GST_PTR_FORMAT, width, height,
+ size, caps);
+
+ if (vpool->allocator)
+ gst_object_unref (vpool->allocator);
+ if ((vpool->allocator = allocator))
+ gst_object_ref (allocator);
+
+ /* enable metadata based on config of the pool */
+ vpool->add_videometa = gst_buffer_pool_config_has_option (config,
+ GST_BUFFER_POOL_OPTION_VIDEO_META);
+
+ /* parse extra alignment info */
+ vpool->need_alignment = gst_buffer_pool_config_has_option (config,
+ GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT);
+
+ if (vpool->need_alignment && vpool->add_videometa) {
+ gst_buffer_pool_config_get_video_alignment (config, &video_align);
+
+ width += video_align.padding_left + video_align.padding_right;
+ height += video_align.padding_bottom + video_align.padding_top;
+
+ /* apply the alignment to the info */
+ if (!gst_video_info_align (&caps_info, &video_align))
+ goto failed_to_align;
+
+ gst_buffer_pool_config_set_video_alignment (config, &video_align);
+ }
+
+ GST_VIDEO_INFO_SIZE (&caps_info) =
+ MAX (size, GST_VIDEO_INFO_SIZE (&caps_info));
+
+ alloc_info = caps_info;
+ GST_VIDEO_INFO_WIDTH (&alloc_info) = width;
+ GST_VIDEO_INFO_HEIGHT (&alloc_info) = height;
+
+ vpool->caps_info = caps_info;
+ vpool->alloc_info = alloc_info;
+ vpool->usage_hint = usage_hint;
+ vpool->video_align = video_align;
+
+ gst_buffer_pool_config_set_params (config, caps,
+ GST_VIDEO_INFO_SIZE (&caps_info), min_buffers, max_buffers);
+
+ return GST_BUFFER_POOL_CLASS (parent_class)->set_config (pool, config);
+
+ /* ERRORS */
+wrong_config:
+ {
+ GST_WARNING_OBJECT (vpool, "invalid config");
+ return FALSE;
+ }
+no_caps:
+ {
+ GST_WARNING_OBJECT (vpool, "no caps in config");
+ return FALSE;
+ }
+wrong_caps:
+ {
+ GST_WARNING_OBJECT (vpool,
+ "failed getting geometry from caps %" GST_PTR_FORMAT, caps);
+ return FALSE;
+ }
+wrong_size:
+ {
+ GST_WARNING_OBJECT (vpool,
+ "Provided size is to small for the caps: %u < %" G_GSIZE_FORMAT, size,
+ GST_VIDEO_INFO_SIZE (&caps_info));
+ return FALSE;
+ }
+failed_to_align:
+ {
+ GST_WARNING_OBJECT (pool, "Failed to align");
+ return FALSE;
+ }
+}
+
+static GstFlowReturn
+gst_va_pool_alloc (GstBufferPool * pool, GstBuffer ** buffer,
+ GstBufferPoolAcquireParams * params)
+{
+ GstBuffer *buf;
+ GstVideoMeta *vmeta;
+ GstVaPool *vpool = GST_VA_POOL (pool);
+ GstVaAllocationParams alloc_params = {
+ .info = vpool->alloc_info,
+ .usage_hint = vpool->usage_hint,
+ };
+
+ buf = gst_buffer_new ();
+
+ if (GST_IS_VA_DMABUF_ALLOCATOR (vpool->allocator)) {
+ if (!gst_va_dmabuf_setup_buffer (vpool->allocator, buf, &alloc_params))
+ goto no_memory;
+ } else if (GST_IS_VA_ALLOCATOR (vpool->allocator)) {
+ GstMemory *mem = gst_va_allocator_alloc (vpool->allocator, &alloc_params);
+ if (!mem)
+ goto no_memory;
+ gst_buffer_append_memory (buf, mem);
+ } else
+ goto no_memory;
+
+ if (vpool->add_videometa) {
+ /* GstVaAllocator may update offset/stride given the physical
+ * memory */
+ vmeta = gst_buffer_add_video_meta_full (buf, GST_VIDEO_FRAME_FLAG_NONE,
+ GST_VIDEO_INFO_FORMAT (&vpool->caps_info),
+ GST_VIDEO_INFO_WIDTH (&vpool->caps_info),
+ GST_VIDEO_INFO_HEIGHT (&vpool->caps_info),
+ GST_VIDEO_INFO_N_PLANES (&vpool->caps_info),
+ alloc_params.info.offset, alloc_params.info.stride);
+
+ if (vpool->need_alignment)
+ gst_video_meta_set_alignment (vmeta, vpool->video_align);
+ }
+
+ *buffer = buf;
+
+ return GST_FLOW_OK;
+
+ /* ERROR */
+no_memory:
+ {
+ gst_buffer_unref (buf);
+ GST_WARNING_OBJECT (vpool, "can't create memory");
+ return GST_FLOW_ERROR;
+ }
+}
+
+static void
+gst_va_pool_dispose (GObject * object)
+{
+ GstVaPool *pool = GST_VA_POOL (object);
+
+ GST_LOG_OBJECT (pool, "finalize video buffer pool %p", pool);
+
+ gst_clear_object (&pool->allocator);
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gst_va_pool_class_init (GstVaPoolClass * klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GstBufferPoolClass *gstbufferpool_class = GST_BUFFER_POOL_CLASS (klass);
+
+ gobject_class->dispose = gst_va_pool_dispose;
+
+ gstbufferpool_class->get_options = gst_va_pool_get_options;
+ gstbufferpool_class->set_config = gst_va_pool_set_config;
+ gstbufferpool_class->alloc_buffer = gst_va_pool_alloc;
+}
+
+static void
+gst_va_pool_init (GstVaPool * self)
+{
+}
+
+GstBufferPool *
+gst_va_pool_new (void)
+{
+ GstVaPool *pool;
+
+ pool = g_object_new (GST_TYPE_VA_POOL, NULL);
+ gst_object_ref_sink (pool);
+
+ GST_LOG_OBJECT (pool, "new va video buffer pool %p", pool);
+
+ return GST_BUFFER_POOL_CAST (pool);
+}
+
+void
+gst_buffer_pool_config_set_va_allocation_params (GstStructure * config,
+ guint usage_hint)
+{
+ gst_structure_set (config, "usage-hint", G_TYPE_UINT, usage_hint, NULL);
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2020 Igalia, S.L.
+ * Author: Víctor Jáquez <vjaquez@igalia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_VA_POOL (gst_va_pool_get_type())
+G_DECLARE_FINAL_TYPE (GstVaPool, gst_va_pool, GST, VA_POOL, GstBufferPool)
+
+GstBufferPool * gst_va_pool_new (void);
+void gst_buffer_pool_config_set_va_allocation_params (GstStructure * config,
+ guint usage_hint);
+G_END_DECLS
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2020 Igalia, S.L.
+ * Author: Víctor Jáquez <vjaquez@igalia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstvaprofile.h"
+
+/* *INDENT-OFF* */
+static const struct ProfileMap
+{
+ VAProfile profile;
+ GstVaCodecs codec;
+ const gchar *name;
+ const gchar *media_type;
+ const gchar *caps_str;
+} profile_map[] = {
+#define P(codec, name, media_type, caps_str) { \
+ G_PASTE (G_PASTE (VAProfile, codec), name), codec, \
+ G_STRINGIFY (G_PASTE (G_PASTE (VAProfile, codec), name)), \
+ media_type, caps_str }
+ P (MPEG2, Simple, "video/mpeg",
+ "mpegversion = (int) 2, profile = (string) simple"),
+ P (MPEG2, Main, "video/mpeg",
+ "mpegversion = (int) 2, profile = (string) main"),
+ P (MPEG4, Simple, "video/mpeg",
+ "mpegversion = (int) 4, profile = (string) simple"),
+ P (MPEG4, AdvancedSimple, "video/mpeg",
+ "mpegversion = (int) 4, profile = (string) advanced-simple"),
+ P (MPEG4, Main, "video/mpeg",
+ "mpegversion = (int) 4, profile = (string) main, "),
+ P (H264, Main, "video/x-h264", "profile = (string) { main, baseline }"),
+ P (H264, High, "video/x-h264",
+ "profile = (string) { progressive-high, constrained-high, high }"),
+ P (VC1, Simple, "video/x-wmv",
+ "wmvversion = (int) 3, profile = (string) simple"),
+ P (VC1, Main, "video/x-wmv",
+ "wmvversion = (int) 3, profile = (string) main"),
+ P (VC1, Advanced, "video/x-wmv",
+ "wmvversion = (int) 3, format = (string) WVC1, "
+ "profile = (string) advanced"),
+ P (H263, Baseline, "video/x-h263",
+ "variant = (string) itu, h263version = (string) h263, "
+ "profile = (string) baseline"),
+ P (JPEG, Baseline, "image/jpeg", NULL),
+ P (H264, ConstrainedBaseline, "video/x-h264",
+ "profile = (string) { baseline, constrained-baseline }"),
+ P (VP8, Version0_3, "video/x-vp8", NULL),
+ /* Unsupported profiles by current GstH264Decoder */
+ /* P (H264, MultiviewHigh, "video/x-h264", */
+ /* "profile = (string) { multiview-high, stereo-high }"), */
+ /* P (H264, StereoHigh, "video/x-h264", */
+ /* "profile = (string) { multiview-high, stereo-high }"), */
+ P (HEVC, Main, "video/x-h265", "profile = (string) main"),
+ P (HEVC, Main10, "video/x-h265", "profile = (string) main-10"),
+ P (VP9, Profile0, "video/x-vp9", "profile = (string) profile0"),
+ P (VP9, Profile1, "video/x-vp9", "profile = (string) profile1"),
+ P (VP9, Profile2, "video/x-vp9", "profile = (string) profile2"),
+ P (VP9, Profile3, "video/x-vp9", "profile = (string) profile3"),
+ P (HEVC, Main12, "video/x-h265", "profile = (string) main-12"),
+ P (HEVC, Main422_10, "video/x-h265", "profile = (string) main-422-10"),
+ P (HEVC, Main422_12, "video/x-h265", "profile = (string) main-422-12"),
+ P (HEVC, Main444, "video/x-h265", "profile = (string) main-444"),
+ P (HEVC, Main444_10, "video/x-h265", "profile = (string) main-444-10"),
+ P (HEVC, Main444_12, "video/x-h265", "profile = (string) main-444-12"),
+ P (HEVC, SccMain, "video/x-h265", "profile = (string) screen-extended-main"),
+ P (HEVC, SccMain10, "video/x-h265",
+ "profile = (string) screen-extended-main-10"),
+ P (HEVC, SccMain444, "video/x-h265",
+ "profile = (string) screen-extended-main-444"),
+#if VA_CHECK_VERSION(1,7,0)
+ P (AV1, Profile0, "video/x-av1", NULL),
+ P (AV1, Profile1, "video/x-av1", NULL),
+#endif
+#if VA_CHECK_VERSION(1, 8, 0)
+ P (HEVC, SccMain444_10, "video/x-h265",
+ "profile = (string) screen-extended-main-444-10"),
+#endif
+#undef P
+};
+/* *INDENT-ON* */
+
+static const struct ProfileMap *
+get_profile_map (VAProfile profile)
+{
+ int i;
+
+ for (i = 0; i < G_N_ELEMENTS (profile_map); i++) {
+ if (profile_map[i].profile == profile)
+ return &profile_map[i];
+ }
+
+ return NULL;
+}
+
+guint32
+gst_va_profile_codec (VAProfile profile)
+{
+ const struct ProfileMap *map = get_profile_map (profile);
+ return map ? map->codec : GST_MAKE_FOURCC ('N', 'O', 'N', 'E');
+}
+
+const gchar *
+gst_va_profile_name (VAProfile profile)
+{
+ const struct ProfileMap *map = get_profile_map (profile);
+ return map ? map->name : NULL;
+}
+
+GstCaps *
+gst_va_profile_caps (VAProfile profile)
+{
+ const struct ProfileMap *map = get_profile_map (profile);
+ GstCaps *caps;
+ gchar *caps_str;
+
+ if (!map)
+ return NULL;
+
+ if (map->caps_str)
+ caps_str = g_strdup_printf ("%s, %s", map->media_type, map->caps_str);
+ else
+ caps_str = g_strdup (map->media_type);
+
+ caps = gst_caps_from_string (caps_str);
+ g_free (caps_str);
+
+ return caps;
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2020 Igalia, S.L.
+ * Author: Víctor Jáquez <vjaquez@igalia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include <gst/gst.h>
+#include <va/va.h>
+
+G_BEGIN_DECLS
+
+typedef enum
+{
+ AV1 = GST_MAKE_FOURCC ('A', 'V', '0', '1'),
+ H263 = GST_MAKE_FOURCC ('H', '2', '6', '3'),
+ H264 = GST_MAKE_FOURCC ('H', '2', '6', '4'),
+ HEVC = GST_MAKE_FOURCC ('H', '2', '6', '5'),
+ JPEG = GST_MAKE_FOURCC ('J', 'P', 'E', 'G'),
+ MPEG2 = GST_MAKE_FOURCC ('M', 'P', 'E', 'G'),
+ MPEG4 = GST_MAKE_FOURCC ('M', 'P', 'G', '4'),
+ VC1 = GST_MAKE_FOURCC ('W', 'M', 'V', '3'),
+ VP8 = GST_MAKE_FOURCC ('V', 'P', '8', '0'),
+ VP9 = GST_MAKE_FOURCC ('V', 'P', '9', '0'),
+} GstVaCodecs;
+
+guint32 gst_va_profile_codec (VAProfile profile);
+GstCaps * gst_va_profile_caps (VAProfile profile);
+const gchar * gst_va_profile_name (VAProfile profile);
+
+G_END_DECLS
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2020 Igalia, S.L.
+ * Author: Víctor Jáquez <vjaquez@igalia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstvavideoformat.h"
+
+#define VA_NSB_FIRST 0 /* No Significant Bit */
+
+/* XXX(victor): add RGB fourcc runtime checkups for screwed drivers */
+/* *INDENT-OFF* */
+static const struct FormatMap
+{
+ GstVideoFormat format;
+ guint va_rtformat;
+ VAImageFormat va_format;
+} format_map[] = {
+#define F(format, fourcc, rtformat, order, bpp, depth, r, g, b, a) { \
+ G_PASTE (GST_VIDEO_FORMAT_, format), \
+ G_PASTE (VA_RT_FORMAT_, rtformat), \
+ { VA_FOURCC fourcc, G_PASTE (G_PASTE (VA_, order), _FIRST), \
+ bpp, depth, r, g, b, a } }
+#define G(format, fourcc, rtformat, order, bpp) \
+ F (format, fourcc, rtformat, order, bpp, 0, 0, 0 ,0, 0)
+ G (NV12, ('N', 'V', '1', '2'), YUV420, NSB, 12),
+ G (NV21, ('N', 'V', '2', '1'), YUV420, NSB, 21),
+ G (VUYA, ('A', 'Y', 'U', 'V'), YUV444, LSB, 32),
+ F (RGBA, ('R', 'G', 'B', 'A'), RGB32, LSB, 32, 32, 0x000000ff,
+ 0x0000ff00, 0x00ff0000, 0xff000000),
+ /* F (????, RGBX), */
+ F (BGRA, ('B', 'G', 'R', 'A'), RGB32, LSB, 32, 32, 0x00ff0000,
+ 0x0000ff00, 0x000000ff, 0xff000000),
+ F (ARGB, ('A', 'R', 'G', 'B'), RGB32, LSB, 32, 32, 0x0000ff00,
+ 0x00ff0000, 0xff000000, 0x000000ff),
+ /* F (????, XRGB), */
+ F (ABGR, ('A', 'B', 'G', 'R'), RGB32, LSB, 32, 32, 0xff000000,
+ 0x00ff0000, 0x0000ff00, 0x000000ff),
+ /* F (????, XBGR), */
+ G (UYVY, ('U', 'Y', 'V', 'Y'), YUV422, NSB, 16),
+ G (YUY2, ('Y', 'U', 'Y', '2'), YUV422, NSB, 16),
+ G (AYUV, ('A', 'Y', 'U', 'V'), YUV444, LSB, 32),
+ /* F (????, NV11), */
+ G (YV12, ('Y', 'V', '1', '2'), YUV420, NSB, 12),
+ /* F (????, P208), */
+ G (I420, ('I', '4', '2', '0'), YUV420, NSB, 12),
+ /* F (????, YV24), */
+ /* F (????, YV32), */
+ /* F (????, Y800), */
+ /* F (????, IMC3), */
+ /* F (????, 411P), */
+ /* F (????, 411R), */
+ /* F (????, 422H), */
+ /* F (????, 422V), */
+ /* F (????, 444P), */
+ /* F (????, RGBP), */
+ /* F (????, BGRP), */
+ /* F (????, RGB565), */
+ /* F (????, BGR565), */
+ G (Y210, ('Y', '2', '1', '0'), YUV422_10, NSB, 32),
+ /* F (????, Y216), */
+ G (Y410, ('Y', '4', '1', '0'), YUV444_10, NSB, 32),
+ /* F (????, Y416), */
+ /* F (????, YV16), */
+ G (P010_10LE, ('P', '0', '1', '0'), YUV420_10, NSB, 24),
+ /* F (P016_LE, P016, ????), */
+ /* F (????, I010), */
+ /* F (????, IYUV), */
+ /* F (????, A2R10G10B10), */
+ /* F (????, A2B10G10R10), */
+ /* F (????, X2R10G10B10), */
+ /* F (????, X2B10G10R10), */
+ G (GRAY8, ('Y', '8', '0', '0'), YUV400, NSB, 8),
+ /* F (????, Y16), */
+ /* G (VYUY, VYUY, YUV422), */
+ /* G (YVYU, YVYU, YUV422), */
+ /* F (ARGB64, ARGB64, ????), */
+ /* F (????, ABGR64), */
+#undef F
+#undef G
+};
+/* *INDENT-ON* */
+
+static const struct FormatMap *
+get_format_map_from_va_fourcc (guint va_fourcc)
+{
+ int i;
+
+ for (i = 0; i < G_N_ELEMENTS (format_map); i++) {
+ if (format_map[i].va_format.fourcc == va_fourcc)
+ return &format_map[i];
+ }
+
+ return NULL;
+}
+
+static const struct FormatMap *
+get_format_map_from_video_format (GstVideoFormat format)
+{
+ int i;
+
+ for (i = 0; i < G_N_ELEMENTS (format_map); i++) {
+ if (format_map[i].format == format)
+ return &format_map[i];
+ }
+
+ return NULL;
+}
+
+GstVideoFormat
+gst_va_video_format_from_va_fourcc (guint va_fourcc)
+{
+ const struct FormatMap *map = get_format_map_from_va_fourcc (va_fourcc);
+
+ return map ? map->format : GST_VIDEO_FORMAT_UNKNOWN;
+}
+
+guint
+gst_va_fourcc_from_video_format (GstVideoFormat format)
+{
+ const struct FormatMap *map = get_format_map_from_video_format (format);
+
+ return map ? map->va_format.fourcc : 0;
+}
+
+guint
+gst_va_chroma_from_video_format (GstVideoFormat format)
+{
+ const struct FormatMap *map = get_format_map_from_video_format (format);
+
+ return map ? map->va_rtformat : 0;
+}
+
+const VAImageFormat *
+gst_va_image_format_from_video_format (GstVideoFormat format)
+{
+ const struct FormatMap *map = get_format_map_from_video_format (format);
+
+ return map ? &map->va_format : NULL;
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2020 Igalia, S.L.
+ * Author: Víctor Jáquez <vjaquez@igalia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include <gst/video/video.h>
+#include <va/va.h>
+
+G_BEGIN_DECLS
+
+GstVideoFormat gst_va_video_format_from_va_fourcc (guint fourcc);
+guint gst_va_fourcc_from_video_format (GstVideoFormat format);
+guint gst_va_chroma_from_video_format (GstVideoFormat format);
+const VAImageFormat * gst_va_image_format_from_video_format (GstVideoFormat format);
+
+G_END_DECLS
--- /dev/null
+va_sources = [
+ 'plugin.c',
+ 'gstvaallocator.c',
+ 'gstvacaps.c',
+ 'gstvadecoder.c',
+ 'gstvadisplay.c',
+ 'gstvadisplay_drm.c',
+ 'gstvadisplay_wrapped.c',
+ 'gstvadevice.c',
+ 'gstvah264dec.c',
+ 'gstvapool.c',
+ 'gstvaprofile.c',
+ 'gstvavideoformat.c',
+]
+
+va_option = get_option('va')
+if va_option.disabled()
+ subdir_done()
+endif
+
+libva_req = ['>= 1.6']
+
+libva_dep = dependency('libva', version: libva_req, required: va_option)
+libva_drm_dep = dependency('libva-drm', version: libva_req, required: va_option)
+libgudev_dep = dependency('gudev-1.0', required: va_option)
+libdrm_dep = dependency('libdrm', required: false,
+ fallback: ['libdrm', 'ext_libdrm'])
+
+have_va = libva_dep.found() and libva_drm_dep.found()
+if not (have_va and libgudev_dep.found())
+ if va_option.enabled()
+ error('The va plugin was enabled explicity, but required dependencies were not found.')
+ endif
+ subdir_done()
+endif
+
+cdata.set10('HAVE_LIBDRM', libdrm_dep.found())
+
+driverdir = libva_dep.get_pkgconfig_variable('driverdir')
+if driverdir == ''
+ driverdir = join_paths(get_option('prefix'), get_option('libdir'), 'dri')
+endif
+gstva_cargs = ['-DLIBVA_DRIVERS_PATH="' + driverdir + '"']
+
+gstva = library('gstva',
+ va_sources,
+ c_args : gst_plugins_bad_args + extra_c_args + gstva_cargs + ['-std=c99'],
+ include_directories : [configinc],
+ dependencies : [gstbase_dep, gstvideo_dep, gstcodecs_dep, libva_dep, gstallocators_dep, libva_drm_dep, libgudev_dep, libdrm_dep] + extra_dep,
+ install : true,
+ install_dir : plugins_install_dir,
+)
+pkgconfig.generate(gstva, install_dir : plugins_pkgconfig_install_dir)
+plugins += [gstva]
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2020 Igalia, S.L.
+ * Author: Víctor Jáquez <vjaquez@igalia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstvacaps.h"
+#include "gstvadevice.h"
+#include "gstvah264dec.h"
+#include "gstvaprofile.h"
+
+#define GST_CAT_DEFAULT gstva_debug
+GST_DEBUG_CATEGORY (gstva_debug);
+
+static void
+plugin_add_dependencies (GstPlugin * plugin)
+{
+ const gchar *env_vars[] = { "LIBVA_DRIVER_NAME", NULL };
+ const gchar *kernel_paths[] = { "/dev/dri", NULL };
+ const gchar *kernel_names[] = { "renderD", NULL };
+
+ /* features get updated upon changes in /dev/dri/renderD* */
+ gst_plugin_add_dependency (plugin, NULL, kernel_paths, kernel_names,
+ GST_PLUGIN_DEPENDENCY_FLAG_FILE_NAME_IS_PREFIX);
+
+ /* features get updated upon changes on LIBVA_DRIVER_NAME envvar */
+ gst_plugin_add_dependency (plugin, env_vars, NULL, NULL,
+ GST_PLUGIN_DEPENDENCY_FLAG_NONE);
+
+ /* features get updated upon changes in default VA drivers
+ * directory */
+ gst_plugin_add_dependency_simple (plugin, "LIBVA_DRIVERS_PATH",
+ LIBVA_DRIVERS_PATH, "_drv_video.so",
+ GST_PLUGIN_DEPENDENCY_FLAG_FILE_NAME_IS_SUFFIX |
+ GST_PLUGIN_DEPENDENCY_FLAG_PATHS_ARE_DEFAULT_ONLY);
+}
+
+static void
+plugin_register_decoders (GstPlugin * plugin, GstVaDevice * device,
+ GHashTable * decoders)
+{
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_hash_table_iter_init (&iter, decoders);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ guint32 codec = *((gint64 *) key);
+ GArray *profiles = (GArray *) value;
+ GstCaps *sinkcaps = NULL, *srccaps = NULL;
+
+ if (!profiles || profiles->len == 0)
+ continue;
+
+ if (!gst_va_caps_from_profiles (device->display, profiles, VAEntrypointVLD,
+ &sinkcaps, &srccaps))
+ continue;
+
+ GST_LOG ("%d decoder codec: %" GST_FOURCC_FORMAT, profiles->len,
+ GST_FOURCC_ARGS (codec));
+ GST_LOG ("sink caps: %" GST_PTR_FORMAT, sinkcaps);
+ GST_LOG ("src caps: %" GST_PTR_FORMAT, srccaps);
+
+ switch (codec) {
+ case H264:
+ if (!gst_va_h264_dec_register (plugin, device, sinkcaps, srccaps,
+ GST_RANK_NONE)) {
+ GST_WARNING ("Failed to register H264 decoder: %s",
+ device->render_device_path);
+ }
+ break;
+ default:
+ GST_DEBUG ("No decoder implementation for %" GST_FOURCC_FORMAT,
+ GST_FOURCC_ARGS (codec));
+ break;
+ }
+
+ gst_caps_unref (srccaps);
+ gst_caps_unref (sinkcaps);
+ }
+}
+
+static void
+plugin_register_encoders (GstPlugin * plugin, GstVaDevice * device,
+ GHashTable * encoders, VAEntrypoint entrypoint)
+{
+ GHashTableIter iter;
+ gpointer key, value;
+ const gchar *str;
+
+ if (entrypoint == VAEntrypointEncSliceLP)
+ str = "low power ";
+ else
+ str = "";
+
+ g_hash_table_iter_init (&iter, encoders);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ guint32 codec = *((gint64 *) key);
+ GArray *profiles = (GArray *) value;
+ GstCaps *sinkcaps = NULL, *srccaps = NULL;
+
+ if (!profiles || profiles->len == 0)
+ continue;
+
+ if (!gst_va_caps_from_profiles (device->display, profiles, entrypoint,
+ &srccaps, &sinkcaps))
+ continue;
+
+ GST_LOG ("%d encoder %scodec: %" GST_FOURCC_FORMAT, profiles->len, str,
+ GST_FOURCC_ARGS (codec));
+ GST_LOG ("sink caps: %" GST_PTR_FORMAT, sinkcaps);
+ GST_LOG ("src caps: %" GST_PTR_FORMAT, srccaps);
+
+ gst_caps_unref (srccaps);
+ gst_caps_unref (sinkcaps);
+ }
+}
+
+static inline void
+_insert_profile_in_table (GHashTable * table, VAProfile profile)
+{
+ gint64 codec = gst_va_profile_codec (profile);
+ GArray *profiles;
+
+ if (codec == GST_MAKE_FOURCC ('N', 'O', 'N', 'E'))
+ return;
+
+ profiles = g_hash_table_lookup (table, &codec);
+ if (!profiles) {
+ gint64 *codec_ptr = g_new (gint64, 1);
+
+ *codec_ptr = codec;
+ profiles = g_array_new (FALSE, FALSE, sizeof (VAProfile));
+ g_hash_table_insert (table, codec_ptr, profiles);
+ }
+ g_array_append_val (profiles, profile);
+}
+
+static gboolean
+plugin_register_elements (GstPlugin * plugin, GstVaDevice * device)
+{
+ VADisplay dpy = gst_va_display_get_va_dpy (device->display);
+ VAEntrypoint *entrypoints = g_new (VAEntrypoint, vaMaxNumEntrypoints (dpy));
+ VAProfile *profiles = g_new (VAProfile, vaMaxNumProfiles (dpy));
+ VAStatus status;
+ GHashTable *decoders, *encoders, *encoderslp, *encodersimg;
+ gint i, j, num_entrypoints = 0, num_profiles = 0;
+ gboolean ret = FALSE;
+
+ decoders = g_hash_table_new_full (g_int64_hash, g_int64_equal,
+ (GDestroyNotify) g_free, (GDestroyNotify) g_array_unref);
+ encoders = g_hash_table_new_full (g_int64_hash, g_int64_equal,
+ (GDestroyNotify) g_free, (GDestroyNotify) g_array_unref);
+ encoderslp = g_hash_table_new_full (g_int64_hash, g_int64_equal,
+ (GDestroyNotify) g_free, (GDestroyNotify) g_array_unref);
+ encodersimg = g_hash_table_new_full (g_int64_hash, g_int64_equal,
+ (GDestroyNotify) g_free, (GDestroyNotify) g_array_unref);
+
+ status = vaQueryConfigProfiles (dpy, profiles, &num_profiles);
+ if (status != VA_STATUS_SUCCESS) {
+ GST_ERROR ("vaQueryConfigProfile: %s", vaErrorStr (status));
+ goto bail;
+ }
+
+ for (i = 0; i < num_profiles; i++) {
+ status = vaQueryConfigEntrypoints (dpy, profiles[i], entrypoints,
+ &num_entrypoints);
+ if (status != VA_STATUS_SUCCESS) {
+ GST_ERROR ("vaQueryConfigEntrypoints: %s", vaErrorStr (status));
+ goto bail;
+ }
+
+ for (j = 0; j < num_entrypoints; j++) {
+ if (entrypoints[j] == VAEntrypointVLD)
+ _insert_profile_in_table (decoders, profiles[i]);
+ else if (entrypoints[j] == VAEntrypointEncSlice)
+ _insert_profile_in_table (encoders, profiles[i]);
+ else if (entrypoints[j] == VAEntrypointEncSliceLP)
+ _insert_profile_in_table (encoderslp, profiles[i]);
+ else if (entrypoints[j] == VAEntrypointEncPicture)
+ _insert_profile_in_table (encodersimg, profiles[i]);
+ }
+ }
+
+ plugin_register_decoders (plugin, device, decoders);
+ plugin_register_encoders (plugin, device, encoders, VAEntrypointEncSlice);
+ plugin_register_encoders (plugin, device, encoderslp, VAEntrypointEncSliceLP);
+ plugin_register_encoders (plugin, device, encodersimg,
+ VAEntrypointEncPicture);
+
+ ret = TRUE;
+
+bail:
+ g_hash_table_unref (encodersimg);
+ g_hash_table_unref (encoderslp);
+ g_hash_table_unref (encoders);
+ g_hash_table_unref (decoders);
+ g_free (entrypoints);
+ g_free (profiles);
+
+ return ret;
+}
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+ GList *devices, *dev;
+ gboolean ret = TRUE;
+
+ GST_DEBUG_CATEGORY_INIT (gstva_debug, "va", 0, "VA general debug");
+
+ plugin_add_dependencies (plugin);
+
+ devices = gst_va_device_find_devices ();
+ for (dev = devices; dev; dev = g_list_next (dev)) {
+ if (!plugin_register_elements (plugin, dev->data)) {
+ ret = FALSE;
+ break;
+ }
+ }
+ gst_va_device_list_free (devices);
+
+ return ret;
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ va, "VA-API codecs plugin",
+ plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)