/*
 *  gstvaapivideometa.c - Gst VA video meta
 *
 *  Copyright (C) 2010-2011 Splitted-Desktop Systems
 *    Author: Gwenole Beauchesne <gwenole.beauchesne@splitted-desktop.com>
 *  Copyright (C) 2011-2013 Intel Corporation
 *    Author: Gwenole Beauchesne <gwenole.beauchesne@intel.com>
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public License
 *  as published by the Free Software Foundation; either version 2.1
 *  of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free
 *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 *  Boston, MA 02110-1301 USA
 */

/**
 * SECTION:gstvaapivideometa
 * @short_description: VA video meta for GStreamer
 *
 * Describes a #GstMeta for VA-base video buffers.
 */

#include "gstcompat.h"
#include <gst/vaapi/gstvaapiimagepool.h>
#include <gst/vaapi/gstvaapisurfacepool.h>
#include "gstvaapivideometa.h"
#include "gstvaapivideomemory.h"

#define GST_VAAPI_VIDEO_META(obj) \
  ((GstVaapiVideoMeta *) (obj))
#define GST_VAAPI_IS_VIDEO_META(obj) \
  (GST_VAAPI_VIDEO_META (obj) != NULL)

struct _GstVaapiVideoMeta
{
  GstBuffer *buffer;
  gint ref_count;
  GstVaapiDisplay *display;
  GstVaapiVideoPool *image_pool;
  GstVaapiImage *image;
  GstVaapiSurfaceProxy *proxy;
  GFunc converter;
  guint render_flags;
  GstVaapiRectangle render_rect;
  guint has_render_rect:1;
};

static gboolean
ensure_surface_proxy (GstVaapiVideoMeta * meta)
{
  if (!meta->proxy)
    return FALSE;

  if (meta->buffer) {
    GstMemory *const mem = gst_buffer_peek_memory (meta->buffer, 0);

    if (GST_VAAPI_IS_VIDEO_MEMORY (mem))
      return gst_vaapi_video_memory_sync (GST_VAAPI_VIDEO_MEMORY_CAST (mem));
  }
  return TRUE;
}

static inline void
set_display (GstVaapiVideoMeta * meta, GstVaapiDisplay * display)
{
  gst_vaapi_display_replace (&meta->display, display);
}

static inline void
set_image (GstVaapiVideoMeta * meta, GstVaapiImage * image)
{
  meta->image = gst_vaapi_object_ref (image);
  set_display (meta, gst_vaapi_object_get_display (GST_VAAPI_OBJECT (image)));
}

static gboolean
set_image_from_pool (GstVaapiVideoMeta * meta, GstVaapiVideoPool * pool)
{
  GstVaapiImage *image;

  image = gst_vaapi_video_pool_get_object (pool);
  if (!image)
    return FALSE;

  set_image (meta, image);
  meta->image_pool = gst_vaapi_video_pool_ref (pool);
  return TRUE;
}

static gboolean
set_surface_proxy (GstVaapiVideoMeta * meta, GstVaapiSurfaceProxy * proxy)
{
  GstVaapiSurface *surface;

  surface = GST_VAAPI_SURFACE_PROXY_SURFACE (proxy);
  if (!surface)
    return FALSE;

  meta->proxy = gst_vaapi_surface_proxy_ref (proxy);
  set_display (meta, gst_vaapi_object_get_display (GST_VAAPI_OBJECT (surface)));
  return TRUE;
}

static gboolean
set_surface_proxy_from_pool (GstVaapiVideoMeta * meta, GstVaapiVideoPool * pool)
{
  GstVaapiSurfaceProxy *proxy;
  gboolean success;

  proxy = gst_vaapi_surface_proxy_new_from_pool (GST_VAAPI_SURFACE_POOL (pool));
  if (!proxy)
    return FALSE;

  success = set_surface_proxy (meta, proxy);
  gst_vaapi_surface_proxy_unref (proxy);
  return success;
}

static void
gst_vaapi_video_meta_destroy_image (GstVaapiVideoMeta * meta)
{
  if (meta->image) {
    if (meta->image_pool)
      gst_vaapi_video_pool_put_object (meta->image_pool, meta->image);
    gst_vaapi_object_unref (meta->image);
    meta->image = NULL;
  }
  gst_vaapi_video_pool_replace (&meta->image_pool, NULL);
}

static inline void
gst_vaapi_video_meta_destroy_proxy (GstVaapiVideoMeta * meta)
{
  gst_vaapi_surface_proxy_replace (&meta->proxy, NULL);
}

static void
gst_vaapi_video_meta_finalize (GstVaapiVideoMeta * meta)
{
  gst_vaapi_video_meta_destroy_image (meta);
  gst_vaapi_video_meta_destroy_proxy (meta);
  gst_vaapi_display_replace (&meta->display, NULL);
}

static void
gst_vaapi_video_meta_init (GstVaapiVideoMeta * meta)
{
  meta->buffer = NULL;
  meta->ref_count = 1;
  meta->display = NULL;
  meta->image_pool = NULL;
  meta->image = NULL;
  meta->proxy = NULL;
  meta->converter = NULL;
  meta->render_flags = 0;
  meta->has_render_rect = FALSE;
}

static inline GstVaapiVideoMeta *
_gst_vaapi_video_meta_create (void)
{
  return g_slice_new (GstVaapiVideoMeta);
}

static inline void
_gst_vaapi_video_meta_destroy (GstVaapiVideoMeta * meta)
{
  g_slice_free1 (sizeof (*meta), meta);
}

static inline GstVaapiVideoMeta *
_gst_vaapi_video_meta_new (void)
{
  GstVaapiVideoMeta *meta;

  meta = _gst_vaapi_video_meta_create ();
  if (!meta)
    return NULL;
  gst_vaapi_video_meta_init (meta);
  return meta;
}

static inline void
_gst_vaapi_video_meta_free (GstVaapiVideoMeta * meta)
{
  g_atomic_int_inc (&meta->ref_count);

  gst_vaapi_video_meta_finalize (meta);

  if (G_LIKELY (g_atomic_int_dec_and_test (&meta->ref_count)))
    _gst_vaapi_video_meta_destroy (meta);
}

/**
 * gst_vaapi_video_meta_copy:
 * @meta: a #GstVaapiVideoMeta
 *
 * Creates a copy of #GstVaapiVideoMeta object @meta. The original
 * @meta object shall not contain any VA objects created from a
 * #GstVaapiVideoPool.
 *
 * Return value: the newly allocated #GstVaapiVideoMeta, or %NULL on error
 */
GstVaapiVideoMeta *
gst_vaapi_video_meta_copy (GstVaapiVideoMeta * meta)
{
  GstVaapiVideoMeta *copy;

  g_return_val_if_fail (GST_VAAPI_IS_VIDEO_META (meta), NULL);

  if (meta->image_pool)
    return NULL;

  copy = _gst_vaapi_video_meta_create ();
  if (!copy)
    return NULL;

  copy->buffer = NULL;
  copy->ref_count = 1;
  copy->display = gst_vaapi_display_ref (meta->display);
  copy->image_pool = NULL;
  copy->image = meta->image ? gst_vaapi_object_ref (meta->image) : NULL;
  copy->proxy = meta->proxy ? gst_vaapi_surface_proxy_copy (meta->proxy) : NULL;
  copy->converter = meta->converter;
  copy->render_flags = meta->render_flags;

  copy->has_render_rect = meta->has_render_rect;
  if (copy->has_render_rect)
    copy->render_rect = meta->render_rect;
  return copy;
}

/**
 * gst_vaapi_video_meta_new:
 * @display: a #GstVaapiDisplay
 *
 * Creates an empty #GstVaapiVideoMeta. The caller is responsible for completing
 * the initialization of the meta with the gst_vaapi_video_meta_set_*()
 * functions.
 *
 * This function shall only be called from within gstreamer-vaapi
 * plugin elements.
 *
 * Return value: the newly allocated #GstVaapiVideoMeta, or %NULL or error
 */
GstVaapiVideoMeta *
gst_vaapi_video_meta_new (GstVaapiDisplay * display)
{
  GstVaapiVideoMeta *meta;

  g_return_val_if_fail (display != NULL, NULL);

  meta = _gst_vaapi_video_meta_new ();
  if (G_UNLIKELY (!meta))
    return NULL;

  set_display (meta, display);
  return meta;
}

/**
 * gst_vaapi_video_meta_new_from_pool:
 * @pool: a #GstVaapiVideoPool
 *
 * Creates a #GstVaapiVideoMeta with a video object allocated from a @pool.
 * Only #GstVaapiSurfacePool and #GstVaapiImagePool pools are supported.
 *
 * The meta object is destroyed through the last call to
 * gst_vaapi_video_meta_unref() and the video objects are pushed back
 * to their respective pools.
 *
 * Return value: the newly allocated #GstVaapiVideoMeta, or %NULL on error
 */
GstVaapiVideoMeta *
gst_vaapi_video_meta_new_from_pool (GstVaapiVideoPool * pool)
{
  GstVaapiVideoMeta *meta;
  GstVaapiVideoPoolObjectType object_type;

  g_return_val_if_fail (pool != NULL, NULL);

  meta = _gst_vaapi_video_meta_new ();
  if (G_UNLIKELY (!meta))
    return NULL;

  object_type = gst_vaapi_video_pool_get_object_type (pool);
  switch (object_type) {
    case GST_VAAPI_VIDEO_POOL_OBJECT_TYPE_IMAGE:
      if (!set_image_from_pool (meta, pool))
        goto error;
      break;
    case GST_VAAPI_VIDEO_POOL_OBJECT_TYPE_SURFACE:
      if (!set_surface_proxy_from_pool (meta, pool))
        goto error;
      break;
    default:
      GST_ERROR ("unsupported video buffer pool of type %d", object_type);
      goto error;
  }
  set_display (meta, gst_vaapi_video_pool_get_display (pool));
  return meta;

  /* ERRORS */
error:
  {
    gst_vaapi_video_meta_unref (meta);
    return NULL;
  }
}

/**
 * gst_vaapi_video_meta_new_with_image:
 * @image: a #GstVaapiImage
 *
 * Creates a #GstVaapiVideoMeta with the specified @image. The resulting
 * meta holds an additional reference to the @image.
 *
 * This function shall only be called from within gstreamer-vaapi
 * plugin elements.
 *
 * Return value: the newly allocated #GstVaapiVideoMeta, or %NULL on error
 */
GstVaapiVideoMeta *
gst_vaapi_video_meta_new_with_image (GstVaapiImage * image)
{
  GstVaapiVideoMeta *meta;

  g_return_val_if_fail (image != NULL, NULL);

  meta = _gst_vaapi_video_meta_new ();
  if (G_UNLIKELY (!meta))
    return NULL;

  gst_vaapi_video_meta_set_image (meta, image);
  return meta;
}

/**
 * gst_vaapi_video_meta_new_with_surface_proxy:
 * @proxy: a #GstVaapiSurfaceProxy
 *
 * Creates a #GstVaapiVideoMeta with the specified surface @proxy. The
 * resulting meta holds an additional reference to the @proxy.
 *
 * This function shall only be called from within gstreamer-vaapi
 * plugin elements.
 *
 * Return value: the newly allocated #GstVaapiVideoMeta, or %NULL on error
 */
GstVaapiVideoMeta *
gst_vaapi_video_meta_new_with_surface_proxy (GstVaapiSurfaceProxy * proxy)
{
  GstVaapiVideoMeta *meta;

  g_return_val_if_fail (proxy != NULL, NULL);

  meta = _gst_vaapi_video_meta_new ();
  if (G_UNLIKELY (!meta))
    return NULL;

  gst_vaapi_video_meta_set_surface_proxy (meta, proxy);
  return meta;
}

/**
 * gst_vaapi_video_meta_ref:
 * @meta: a #GstVaapiVideoMeta
 *
 * Atomically increases the reference count of the given @meta by one.
 *
 * Returns: The same @meta argument
 */
GstVaapiVideoMeta *
gst_vaapi_video_meta_ref (GstVaapiVideoMeta * meta)
{
  g_return_val_if_fail (meta != NULL, NULL);

  g_atomic_int_inc (&meta->ref_count);
  return meta;
}

/**
 * gst_vaapi_video_meta_unref:
 * @meta: a #GstVaapiVideoMeta
 *
 * Atomically decreases the reference count of the @meta by one. If
 * the reference count reaches zero, the object will be free'd.
 */
void
gst_vaapi_video_meta_unref (GstVaapiVideoMeta * meta)
{
  g_return_if_fail (meta != NULL);
  g_return_if_fail (meta->ref_count > 0);

  if (g_atomic_int_dec_and_test (&meta->ref_count))
    _gst_vaapi_video_meta_free (meta);
}

/**
 * gst_vaapi_video_meta_replace:
 * @old_meta_ptr: a pointer to a #GstVaapiVideoMeta
 * @new_meta: a #GstVaapiVideoMeta
 *
 * @new_meta. This means that @old_meta_ptr shall reference a valid
 * Atomically replaces the meta object held in @old_meta_ptr with
 * object. However, @new_meta can be NULL.
 */
void
gst_vaapi_video_meta_replace (GstVaapiVideoMeta ** old_meta_ptr,
    GstVaapiVideoMeta * new_meta)
{
  GstVaapiVideoMeta *old_meta;

  g_return_if_fail (old_meta_ptr != NULL);

  old_meta = g_atomic_pointer_get ((gpointer *) old_meta_ptr);

  if (old_meta == new_meta)
    return;

  if (new_meta)
    gst_vaapi_video_meta_ref (new_meta);

  while (!g_atomic_pointer_compare_and_exchange ((gpointer *) old_meta_ptr,
          old_meta, new_meta))
    old_meta = g_atomic_pointer_get ((gpointer *) old_meta_ptr);

  if (old_meta)
    gst_vaapi_video_meta_unref (old_meta);
}

/**
 * gst_vaapi_video_meta_get_display:
 * @meta: a #GstVaapiVideoMeta
 *
 * Retrieves the #GstVaapiDisplay the @meta is bound to. The @meta
 * owns the returned #GstVaapiDisplay object so the caller is
 * responsible for calling g_object_ref() when needed.
 *
 * Return value: the #GstVaapiDisplay the @meta is bound to
 */
GstVaapiDisplay *
gst_vaapi_video_meta_get_display (GstVaapiVideoMeta * meta)
{
  g_return_val_if_fail (GST_VAAPI_IS_VIDEO_META (meta), NULL);

  return meta->display;
}

/**
 * gst_vaapi_video_meta_get_image:
 * @meta: a #GstVaapiVideoMeta
 *
 * Retrieves the #GstVaapiImage bound to the @meta. The @meta owns
 * the #GstVaapiImage so the caller is responsible for calling
 * gst_vaapi_object_ref() when needed.
 *
 * Return value: the #GstVaapiImage bound to the @meta, or %NULL if
 *   there is none
 */
GstVaapiImage *
gst_vaapi_video_meta_get_image (GstVaapiVideoMeta * meta)
{
  g_return_val_if_fail (GST_VAAPI_IS_VIDEO_META (meta), NULL);

  return meta->image;
}

/**
 * gst_vaapi_video_meta_set_image:
 * @meta: a #GstVaapiVideoMeta
 * @image: a #GstVaapiImage
 *
 * Binds @image to the @meta. If the @meta contains another image
 * previously allocated from a pool, it's pushed back to its parent
 * pool and the pool is also released.
 */
void
gst_vaapi_video_meta_set_image (GstVaapiVideoMeta * meta, GstVaapiImage * image)
{
  g_return_if_fail (GST_VAAPI_IS_VIDEO_META (meta));

  gst_vaapi_video_meta_destroy_image (meta);

  if (image)
    set_image (meta, image);
}

/**
 * gst_vaapi_video_meta_set_image_from_pool
 * @meta: a #GstVaapiVideoMeta
 * @pool: a #GstVaapiVideoPool
 *
 * Binds a newly allocated video object from the @pool. The @pool
 * shall be of type #GstVaapiImagePool. Previously allocated objects
 * are released and returned to their parent pools, if any.
 *
 * Return value: %TRUE on success
 */
gboolean
gst_vaapi_video_meta_set_image_from_pool (GstVaapiVideoMeta * meta,
    GstVaapiVideoPool * pool)
{
  g_return_val_if_fail (GST_VAAPI_IS_VIDEO_META (meta), FALSE);
  g_return_val_if_fail (pool != NULL, FALSE);
  g_return_val_if_fail (gst_vaapi_video_pool_get_object_type (pool) ==
      GST_VAAPI_VIDEO_POOL_OBJECT_TYPE_IMAGE, FALSE);

  gst_vaapi_video_meta_destroy_image (meta);

  return set_image_from_pool (meta, pool);
}

/**
 * gst_vaapi_video_meta_get_surface:
 * @meta: a #GstVaapiVideoMeta
 *
 * Retrieves the #GstVaapiSurface bound to the @meta. The @meta
 * owns the #GstVaapiSurface so the caller is responsible for calling
 * gst_vaapi_object_ref() when needed.
 *
 * Return value: the #GstVaapiSurface bound to the @meta, or %NULL if
 *   there is none
 */
GstVaapiSurface *
gst_vaapi_video_meta_get_surface (GstVaapiVideoMeta * meta)
{
  g_return_val_if_fail (GST_VAAPI_IS_VIDEO_META (meta), NULL);

  return ensure_surface_proxy (meta) ?
      GST_VAAPI_SURFACE_PROXY_SURFACE (meta->proxy) : NULL;
}

/**
 * gst_vaapi_video_meta_get_surface_proxy:
 * @meta: a #GstVaapiVideoMeta
 *
 * Retrieves the #GstVaapiSurfaceProxy bound to the @meta. The @meta
 * owns the #GstVaapiSurfaceProxy so the caller is responsible for calling
 * gst_surface_proxy_ref() when needed.
 *
 * Return value: the #GstVaapiSurfaceProxy bound to the @meta, or
 *   %NULL if there is none
 */
GstVaapiSurfaceProxy *
gst_vaapi_video_meta_get_surface_proxy (GstVaapiVideoMeta * meta)
{
  g_return_val_if_fail (GST_VAAPI_IS_VIDEO_META (meta), NULL);

  return ensure_surface_proxy (meta) ? meta->proxy : NULL;
}

/**
 * gst_vaapi_video_meta_set_surface_proxy:
 * @meta: a #GstVaapiVideoMeta
 * @proxy: a #GstVaapiSurfaceProxy
 *
 * Binds surface @proxy to the @meta. If the @meta contains another
 * surface previously allocated from a pool, it's pushed back to its
 * parent pool and the pool is also released.
 */
void
gst_vaapi_video_meta_set_surface_proxy (GstVaapiVideoMeta * meta,
    GstVaapiSurfaceProxy * proxy)
{
  const GstVaapiRectangle *crop_rect;

  g_return_if_fail (GST_VAAPI_IS_VIDEO_META (meta));

  gst_vaapi_video_meta_destroy_proxy (meta);

  if (proxy) {
    if (!set_surface_proxy (meta, proxy))
      return;

    crop_rect = gst_vaapi_surface_proxy_get_crop_rect (proxy);
    if (crop_rect)
      gst_vaapi_video_meta_set_render_rect (meta, crop_rect);
  }
}

/**
 * gst_vaapi_video_meta_get_surface_converter:
 * @meta: a #GstVaapiVideoMeta
 *
 * Retrieves the surface converter bound to the @meta.
 *
 * Return value: the surface converter associated with the video @meta
 */
GFunc
gst_vaapi_video_meta_get_surface_converter (GstVaapiVideoMeta * meta)
{
  g_return_val_if_fail (GST_VAAPI_IS_VIDEO_META (meta), NULL);

  return meta->converter;
}

/**
 * gst_vaapi_video_meta_set_surface_converter:
 * @meta: a #GstVaapiVideoMeta
 * @func: a pointer to the surface converter function
 *
 * Sets the @meta surface converter function to @func.
 */
void
gst_vaapi_video_meta_set_surface_converter (GstVaapiVideoMeta * meta,
    GFunc func)
{
  g_return_if_fail (GST_VAAPI_IS_VIDEO_META (meta));

  meta->converter = func;
}

/**
 * gst_vaapi_video_meta_get_render_flags:
 * @meta: a #GstVaapiVideoMeta
 *
 * Retrieves the surface render flags bound to the @meta.
 *
 * Return value: a combination for #GstVaapiSurfaceRenderFlags
 */
guint
gst_vaapi_video_meta_get_render_flags (GstVaapiVideoMeta * meta)
{
  g_return_val_if_fail (GST_VAAPI_IS_VIDEO_META (meta), 0);
  g_return_val_if_fail (meta->proxy != NULL, 0);

  return meta->render_flags;
}

/**
 * gst_vaapi_video_meta_set_render_flags:
 * @meta: a #GstVaapiVideoMeta
 * @flags: a set of surface render flags
 *
 * Sets #GstVaapiSurfaceRenderFlags to the @meta.
 */
void
gst_vaapi_video_meta_set_render_flags (GstVaapiVideoMeta * meta, guint flags)
{
  g_return_if_fail (GST_VAAPI_IS_VIDEO_META (meta));
  g_return_if_fail (meta->proxy != NULL);

  meta->render_flags = flags;
}

/**
 * gst_vaapi_video_meta_get_render_rect:
 * @meta: a #GstVaapiVideoMeta
 *
 * Retrieves the render rectangle bound to the @meta
 *
 * Return value: render rectangle associated with the video meta.
 */
const GstVaapiRectangle *
gst_vaapi_video_meta_get_render_rect (GstVaapiVideoMeta * meta)
{
  g_return_val_if_fail (GST_VAAPI_IS_VIDEO_META (meta), NULL);

  if (!meta->has_render_rect)
    return NULL;
  return &meta->render_rect;
}

/**
 * gst_vaapi_video_meta_set_render_rect:
 * @meta: a #GstVaapiVideoMeta
 * @rect: a #GstVaapiRectangle
 *
 * Sets the render rectangle @rect to the @meta.
 */
void
gst_vaapi_video_meta_set_render_rect (GstVaapiVideoMeta * meta,
    const GstVaapiRectangle * rect)
{
  g_return_if_fail (GST_VAAPI_IS_VIDEO_META (meta));

  meta->has_render_rect = rect != NULL;
  if (meta->has_render_rect)
    meta->render_rect = *rect;
}

#define GST_VAAPI_VIDEO_META_HOLDER(meta) \
  ((GstVaapiVideoMetaHolder *) (meta))

typedef struct _GstVaapiVideoMetaHolder GstVaapiVideoMetaHolder;
struct _GstVaapiVideoMetaHolder
{
  GstMeta base;
  GstVaapiVideoMeta *meta;
};

static gboolean
gst_vaapi_video_meta_holder_init (GstVaapiVideoMetaHolder * meta,
    gpointer params, GstBuffer * buffer)
{
  meta->meta = NULL;
  return TRUE;
}

static void
gst_vaapi_video_meta_holder_free (GstVaapiVideoMetaHolder * meta,
    GstBuffer * buffer)
{
  if (meta->meta)
    gst_vaapi_video_meta_unref (meta->meta);
}

static gboolean
gst_vaapi_video_meta_holder_transform (GstBuffer * dst_buffer, GstMeta * meta,
    GstBuffer * src_buffer, GQuark type, gpointer data)
{
  GstVaapiVideoMetaHolder *const src_meta = GST_VAAPI_VIDEO_META_HOLDER (meta);

  if (GST_META_TRANSFORM_IS_COPY (type)) {
    GstVaapiVideoMeta *const dst_meta =
        gst_vaapi_video_meta_copy (src_meta->meta);
    gst_buffer_set_vaapi_video_meta (dst_buffer, dst_meta);
    gst_vaapi_video_meta_unref (dst_meta);
    return TRUE;
  }
  return FALSE;
}

GType
gst_vaapi_video_meta_api_get_type (void)
{
  static gsize g_type;
  static const gchar *tags[] = { "memory", NULL };

  if (g_once_init_enter (&g_type)) {
    GType type = gst_meta_api_type_register ("GstVaapiVideoMetaAPI", tags);
    g_once_init_leave (&g_type, type);
  }
  return g_type;
}

#define GST_VAAPI_VIDEO_META_INFO gst_vaapi_video_meta_info_get ()
static const GstMetaInfo *
gst_vaapi_video_meta_info_get (void)
{
  static gsize g_meta_info;

  if (g_once_init_enter (&g_meta_info)) {
    gsize meta_info =
        GPOINTER_TO_SIZE (gst_meta_register (GST_VAAPI_VIDEO_META_API_TYPE,
            "GstVaapiVideoMeta", sizeof (GstVaapiVideoMetaHolder),
            (GstMetaInitFunction) gst_vaapi_video_meta_holder_init,
            (GstMetaFreeFunction) gst_vaapi_video_meta_holder_free,
            (GstMetaTransformFunction) gst_vaapi_video_meta_holder_transform));
    g_once_init_leave (&g_meta_info, meta_info);
  }
  return GSIZE_TO_POINTER (g_meta_info);
}

GstVaapiVideoMeta *
gst_buffer_get_vaapi_video_meta (GstBuffer * buffer)
{
  GstVaapiVideoMeta *meta;
  GstMeta *m;

  g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL);

  m = gst_buffer_get_meta (buffer, GST_VAAPI_VIDEO_META_API_TYPE);
  if (!m)
    return NULL;

  meta = GST_VAAPI_VIDEO_META_HOLDER (m)->meta;
  if (meta)
    meta->buffer = buffer;
  return meta;
}

void
gst_buffer_set_vaapi_video_meta (GstBuffer * buffer, GstVaapiVideoMeta * meta)
{
  GstMeta *m;

  g_return_if_fail (GST_IS_BUFFER (buffer));
  g_return_if_fail (GST_VAAPI_IS_VIDEO_META (meta));

  m = gst_buffer_add_meta (buffer, GST_VAAPI_VIDEO_META_INFO, NULL);
  if (m)
    GST_VAAPI_VIDEO_META_HOLDER (m)->meta = gst_vaapi_video_meta_ref (meta);
}
