/**
 * gst_vaapi_surface_proxy_new_from_pool:
 * @pool: a #GstVaapiSurfacePool
 *
 * Allocates a new surface from the supplied surface @pool and creates
 * the wrapped surface proxy object from it. When the last reference
 * to the proxy object is released, then the underlying VA surface is
 * pushed back to its parent pool.
 *
 * Returns: The same newly allocated @proxy object, or %NULL on error
 */
GstVaapiSurfaceProxy *
gst_vaapi_surface_proxy_new_from_pool (GstVaapiSurfacePool * pool)
{
  GstVaapiSurfaceProxy *proxy;

  g_return_val_if_fail (pool != NULL, NULL);

  proxy = (GstVaapiSurfaceProxy *)
      gst_vaapi_mini_object_new (gst_vaapi_surface_proxy_class ());
  if (!proxy)
    return NULL;

  proxy->parent = NULL;
  proxy->destroy_func = NULL;
  proxy->pool = gst_vaapi_video_pool_ref (GST_VAAPI_VIDEO_POOL (pool));
  proxy->surface = gst_vaapi_video_pool_get_object (proxy->pool);
  if (!proxy->surface)
    goto error;
  gst_vaapi_object_ref (proxy->surface);
  gst_vaapi_surface_proxy_init_properties (proxy);
  return proxy;

  /* ERRORS */
error:
  {
    gst_vaapi_surface_proxy_unref (proxy);
    return NULL;
  }
}
/**
 * gst_vaapi_coded_buffer_proxy_new_from_pool:
 * @pool: a #GstVaapiCodedBufferPool
 *
 * Allocates a new coded buffer from the supplied @pool and creates
 * the wrapped coded buffer proxy object from it. When the last
 * reference to the proxy object is released, then the underlying VA
 * coded buffer is pushed back to its parent pool.
 *
 * Returns: The same newly allocated @proxy object, or %NULL on error
 */
GstVaapiCodedBufferProxy *
gst_vaapi_coded_buffer_proxy_new_from_pool (GstVaapiCodedBufferPool * pool)
{
  GstVaapiCodedBufferProxy *proxy;

  g_return_val_if_fail (pool != NULL, NULL);
  g_return_val_if_fail (GST_VAAPI_VIDEO_POOL (pool)->object_type ==
      GST_VAAPI_VIDEO_POOL_OBJECT_TYPE_CODED_BUFFER, NULL);

  proxy = (GstVaapiCodedBufferProxy *)
      gst_vaapi_mini_object_new (gst_vaapi_coded_buffer_proxy_class ());
  if (!proxy)
    return NULL;

  proxy->destroy_func = NULL;
  proxy->user_data_destroy = NULL;
  proxy->pool = gst_vaapi_video_pool_ref (pool);
  proxy->buffer = gst_vaapi_video_pool_get_object (proxy->pool);
  if (!proxy->buffer)
    goto error;
  gst_vaapi_object_ref (proxy->buffer);
  return proxy;

  /* ERRORS */
error:
  {
    gst_vaapi_coded_buffer_proxy_unref (proxy);
    return NULL;
  }
}
static gboolean
ensure_image (GstVaapiVideoMemory * mem)
{
  if (!mem->image && mem->use_direct_rendering) {
    mem->image = gst_vaapi_surface_derive_image (mem->surface);
    if (!mem->image) {
      GST_WARNING ("failed to derive image, fallbacking to copy");
      mem->use_direct_rendering = FALSE;
    } else if (gst_vaapi_surface_get_format (mem->surface) !=
        GST_VIDEO_INFO_FORMAT (mem->image_info)) {
      gst_vaapi_object_replace (&mem->image, NULL);
      mem->use_direct_rendering = FALSE;
    }
  }

  if (!mem->image) {
    GstVaapiVideoAllocator *const allocator =
        GST_VAAPI_VIDEO_ALLOCATOR_CAST (GST_MEMORY_CAST (mem)->allocator);

    mem->image = gst_vaapi_video_pool_get_object (allocator->image_pool);
    if (!mem->image)
      return FALSE;
  }
  gst_vaapi_video_meta_set_image (mem->meta, mem->image);
  return TRUE;
}
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;
}
int
main (int argc, char *argv[])
{
  GstVaapiDisplay *display;
  GstVaapiSurface *surface;
  GstVaapiID surface_id;
  GstVaapiSurface *surfaces[MAX_SURFACES];
  GstVaapiVideoPool *pool;
  gint i;

  static const GstVaapiChromaType chroma_type = GST_VAAPI_CHROMA_TYPE_YUV420;
  static const guint width = 320;
  static const guint height = 240;

  if (!video_output_init (&argc, argv, NULL))
    g_error ("failed to initialize video output subsystem");

  display = video_output_create_display (NULL);
  if (!display)
    g_error ("could not create Gst/VA display");

  surface = gst_vaapi_surface_new (display, chroma_type, width, height);
  if (!surface)
    g_error ("could not create Gst/VA surface");

  surface_id = gst_vaapi_surface_get_id (surface);
  g_print ("created surface %" GST_VAAPI_ID_FORMAT "\n",
      GST_VAAPI_ID_ARGS (surface_id));

  gst_vaapi_object_unref (surface);

  pool = gst_vaapi_surface_pool_new (display, GST_VIDEO_FORMAT_ENCODED,
      width, height);
  if (!pool)
    g_error ("could not create Gst/VA surface pool");

  for (i = 0; i < MAX_SURFACES; i++) {
    surface = gst_vaapi_video_pool_get_object (pool);
    if (!surface)
      g_error ("could not allocate Gst/VA surface from pool");
    g_print ("created surface %" GST_VAAPI_ID_FORMAT " from pool\n",
        GST_VAAPI_ID_ARGS (gst_vaapi_surface_get_id (surface)));
    surfaces[i] = surface;
  }

  /* Check the pool doesn't return the last free'd surface */
  surface = gst_vaapi_object_ref (surfaces[1]);

  for (i = 0; i < 2; i++)
    gst_vaapi_video_pool_put_object (pool, surfaces[i]);

  for (i = 0; i < 2; i++) {
    surfaces[i] = gst_vaapi_video_pool_get_object (pool);
    if (!surfaces[i])
      g_error ("could not re-allocate Gst/VA surface%d from pool", i);
    g_print ("created surface %" GST_VAAPI_ID_FORMAT " from pool (realloc)\n",
        GST_VAAPI_ID_ARGS (gst_vaapi_surface_get_id (surfaces[i])));
  }

  if (surface == surfaces[0])
    g_error ("Gst/VA pool doesn't queue free surfaces");

  for (i = MAX_SURFACES - 1; i >= 0; i--) {
    if (!surfaces[i])
      continue;
    gst_vaapi_video_pool_put_object (pool, surfaces[i]);
    surfaces[i] = NULL;
  }

  /* Unref in random order to check objects are correctly refcounted */
  gst_vaapi_display_unref (display);
  gst_vaapi_video_pool_unref (pool);
  gst_vaapi_object_unref (surface);
  video_output_exit ();
  return 0;
}
static GstVaapiSurface *
vpp_convert (GstVaapiWindow * window,
             GstVaapiSurface * surface,
             const GstVaapiRectangle * src_rect,
             const GstVaapiRectangle * dst_rect, guint flags)
{
    GstVaapiWindowWaylandPrivate *const priv =
        GST_VAAPI_WINDOW_WAYLAND_GET_PRIVATE (window);
    GstVaapiDisplay *const display = GST_VAAPI_OBJECT_DISPLAY (window);
    GstVaapiSurface *vpp_surface = NULL;
    GstVaapiFilterStatus status;

    /* Ensure VA surface pool is created */
    /* XXX: optimize the surface format to use. e.g. YUY2 */
    if (!priv->surface_pool) {
        priv->surface_pool = gst_vaapi_surface_pool_new (display,
                             priv->surface_format, window->width, window->height);
        if (!priv->surface_pool)
            return NULL;
        gst_vaapi_filter_replace (&priv->filter, NULL);
    }

    /* Ensure VPP pipeline is built */
    if (!priv->filter) {
        priv->filter = gst_vaapi_filter_new (display);
        if (!priv->filter)
            goto error_create_filter;
        if (!gst_vaapi_filter_set_format (priv->filter, priv->surface_format))
            goto error_unsupported_format;
    }
    if (!gst_vaapi_filter_set_cropping_rectangle (priv->filter, src_rect))
        return NULL;
    if (!gst_vaapi_filter_set_target_rectangle (priv->filter, dst_rect))
        return NULL;

    /* Post-process the decoded source surface */
    vpp_surface = gst_vaapi_video_pool_get_object (priv->surface_pool);
    if (!vpp_surface)
        return NULL;

    status = gst_vaapi_filter_process (priv->filter, surface, vpp_surface, flags);
    if (status != GST_VAAPI_FILTER_STATUS_SUCCESS)
        goto error_process_filter;
    return vpp_surface;

    /* ERRORS */
error_create_filter:
    {
        GST_WARNING ("failed to create VPP filter. Disabling");
        priv->use_vpp = FALSE;
        return NULL;
    }
error_unsupported_format:
    {
        GST_ERROR ("unsupported render target format %s",
                   gst_vaapi_video_format_to_string (priv->surface_format));
        priv->use_vpp = FALSE;
        return NULL;
    }
error_process_filter:
    {
        GST_ERROR ("failed to process surface %" GST_VAAPI_ID_FORMAT " (error %d)",
                   GST_VAAPI_ID_ARGS (GST_VAAPI_OBJECT_ID (surface)), status);
        gst_vaapi_video_pool_put_object (priv->surface_pool, vpp_surface);
        return NULL;
    }
}