static void
gst_vaapi_video_meta_holder_free (GstVaapiVideoMetaHolder * meta,
    GstBuffer * buffer)
{
  if (meta->meta)
    gst_vaapi_video_meta_unref (meta->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;

error:
  gst_vaapi_video_meta_unref (meta);
  return NULL;
}
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;
}
static GstBuffer *
new_vbuffer (GstVaapiVideoMeta * meta)
{
  GstBuffer *buffer;

  g_return_val_if_fail (meta != NULL, NULL);

  gst_vaapi_video_meta_set_surface_converter (meta,
      get_surface_converter (gst_vaapi_video_meta_get_display (meta)));

  buffer = gst_surface_buffer_new ();
  if (buffer)
    gst_buffer_set_vaapi_video_meta (buffer, meta);
  gst_vaapi_video_meta_unref (meta);
  return buffer;
}
static GstVaapiVideoMemory *
gst_vaapi_video_memory_copy (GstVaapiVideoMemory * mem,
    gssize offset, gssize size)
{
  GstVaapiVideoMeta *meta;
  GstMemory *out_mem;
  gsize maxsize;

  g_return_val_if_fail (mem, NULL);
  g_return_val_if_fail (mem->meta, NULL);

  /* XXX: this implements a soft-copy, i.e. underlying VA surfaces
     are not copied */
  (void) gst_memory_get_sizes (GST_MEMORY_CAST (mem), NULL, &maxsize);
  if (offset != 0 || (size != -1 && (gsize) size != maxsize))
    goto error_unsupported;

  if (!ensure_surface_is_current (mem))
    goto error_no_current_surface;

  meta = gst_vaapi_video_meta_copy (mem->meta);
  if (!meta)
    goto error_allocate_memory;

  out_mem = gst_vaapi_video_memory_new (GST_MEMORY_CAST (mem)->allocator, meta);
  gst_vaapi_video_meta_unref (meta);
  if (!out_mem)
    goto error_allocate_memory;
  return GST_VAAPI_VIDEO_MEMORY_CAST (out_mem);

  /* ERRORS */
error_no_current_surface:
  GST_ERROR ("failed to make surface current");
  return NULL;
error_unsupported:
  GST_ERROR ("failed to copy partial memory (unsupported operation)");
  return NULL;
error_allocate_memory:
  GST_ERROR ("failed to allocate GstVaapiVideoMemory copy");
  return NULL;
}
/**
 * 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);
}
static GstFlowReturn
gst_vaapi_video_buffer_pool_alloc_buffer (GstBufferPool * pool,
    GstBuffer ** out_buffer_ptr, GstBufferPoolAcquireParams * params)
{
  GstVaapiVideoBufferPoolPrivate *const priv =
      GST_VAAPI_VIDEO_BUFFER_POOL (pool)->priv;
  GstVaapiVideoBufferPoolAcquireParams *const priv_params =
      (GstVaapiVideoBufferPoolAcquireParams *) params;
  GstVaapiVideoMeta *meta;
  GstMemory *mem;
  GstBuffer *buffer;

  const gboolean alloc_vaapi_video_meta = !params ||
      !(params->flags & GST_VAAPI_VIDEO_BUFFER_POOL_ACQUIRE_FLAG_NO_ALLOC);

  if (!priv->allocator)
    goto error_no_allocator;

  if (alloc_vaapi_video_meta) {
    meta = gst_vaapi_video_meta_new (priv->display);
    if (!meta)
      goto error_create_meta;

    buffer = gst_vaapi_video_buffer_new (meta);
  } else {
    meta = NULL;
    buffer = gst_vaapi_video_buffer_new_empty ();
  }
  if (!buffer)
    goto error_create_buffer;

  if (priv_params && priv_params->proxy)
    gst_vaapi_video_meta_set_surface_proxy (meta, priv_params->proxy);

  if (priv->use_dmabuf_memory)
    mem = gst_vaapi_dmabuf_memory_new (priv->allocator, meta);
  else
    mem = gst_vaapi_video_memory_new (priv->allocator, meta);
  if (!mem)
    goto error_create_memory;
  gst_vaapi_video_meta_replace (&meta, NULL);
  gst_buffer_append_memory (buffer, mem);

  if (priv->options & GST_VAAPI_VIDEO_BUFFER_POOL_OPTION_VIDEO_META) {
    GstVideoInfo *const vip = &priv->vmeta_vinfo;
    GstVideoMeta *vmeta;

    vmeta = gst_buffer_add_video_meta_full (buffer, 0,
        GST_VIDEO_INFO_FORMAT (vip), GST_VIDEO_INFO_WIDTH (vip),
        GST_VIDEO_INFO_HEIGHT (vip), GST_VIDEO_INFO_N_PLANES (vip),
        &GST_VIDEO_INFO_PLANE_OFFSET (vip, 0),
        &GST_VIDEO_INFO_PLANE_STRIDE (vip, 0));

    if (GST_VAAPI_IS_VIDEO_MEMORY (mem)) {
      vmeta->map = gst_video_meta_map_vaapi_memory;
      vmeta->unmap = gst_video_meta_unmap_vaapi_memory;
    }
  }
#if (USE_GLX || USE_EGL)
  if (priv->options & GST_VAAPI_VIDEO_BUFFER_POOL_OPTION_GL_TEXTURE_UPLOAD)
    gst_buffer_add_texture_upload_meta (buffer);
#endif

  *out_buffer_ptr = buffer;
  return GST_FLOW_OK;

  /* ERRORS */
error_no_allocator:
  {
    GST_ERROR_OBJECT (pool, "no GstAllocator in buffer pool");
    return GST_FLOW_ERROR;
  }
error_create_meta:
  {
    GST_ERROR_OBJECT (pool, "failed to allocate vaapi video meta");
    return GST_FLOW_ERROR;
  }
error_create_buffer:
  {
    GST_ERROR_OBJECT (pool, "failed to create video buffer");
    gst_vaapi_video_meta_unref (meta);
    return GST_FLOW_ERROR;
  }
error_create_memory:
  {
    GST_ERROR_OBJECT (pool, "failed to create video memory");
    gst_buffer_unref (buffer);
    gst_vaapi_video_meta_unref (meta);
    return GST_FLOW_ERROR;
  }
}