/**
 * 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
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;
}
/**
 * gst_vaapi_surface_proxy_copy:
 * @proxy: the parent #GstVaapiSurfaceProxy
 *
 * Creates are new VA surface proxy object from the supplied parent
 * @proxy object with the same initial information, e.g. timestamp,
 * duration.
 *
 * Note: the destroy notify function is not copied into the new
 * surface proxy object.
 *
 * Returns: The same newly allocated @proxy object, or %NULL on error
 */
GstVaapiSurfaceProxy *
gst_vaapi_surface_proxy_copy (GstVaapiSurfaceProxy * proxy)
{
  GstVaapiSurfaceProxy *copy;

  g_return_val_if_fail (proxy != NULL, NULL);

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

  GST_VAAPI_SURFACE_PROXY_FLAGS (copy) = GST_VAAPI_SURFACE_PROXY_FLAGS (proxy);

  copy->parent = gst_vaapi_surface_proxy_ref (proxy->parent ?
      proxy->parent : proxy);
  copy->pool = proxy->pool ? gst_vaapi_video_pool_ref (proxy->pool) : NULL;
  copy->surface = gst_vaapi_object_ref (proxy->surface);
  copy->view_id = proxy->view_id;
  copy->timestamp = proxy->timestamp;
  copy->duration = proxy->duration;
  copy->destroy_func = NULL;
  copy->has_crop_rect = proxy->has_crop_rect;
  if (copy->has_crop_rect)
    copy->crop_rect = proxy->crop_rect;

#if USE_H264_FEI_ENCODER

  if (proxy->mv)
    copy->mv = (GstVaapiEncFeiMv *)
        gst_vaapi_fei_codec_object_ref (GST_VAAPI_FEI_CODEC_OBJECT (proxy->mv));
  else
    copy->mv = NULL;

  if (proxy->mbcode)
    copy->mbcode = (GstVaapiEncFeiMbCode *)
        gst_vaapi_fei_codec_object_ref (GST_VAAPI_FEI_CODEC_OBJECT
        (proxy->mbcode));
  else
    copy->mbcode = NULL;

  if (proxy->mvpred)
    copy->mvpred = (GstVaapiEncFeiMvPredictor *)
        gst_vaapi_fei_codec_object_ref (GST_VAAPI_FEI_CODEC_OBJECT
        (proxy->mvpred));
  else
    copy->mvpred = NULL;

  if (proxy->qp)
    copy->qp = (GstVaapiEncFeiQp *)
        gst_vaapi_fei_codec_object_ref (GST_VAAPI_FEI_CODEC_OBJECT (proxy->qp));
  else
    copy->qp = NULL;

  if (proxy->mbcntrl)
    copy->mbcntrl = (GstVaapiEncFeiMbControl *)
        gst_vaapi_fei_codec_object_ref (GST_VAAPI_FEI_CODEC_OBJECT
        (proxy->mbcntrl));
  else
    copy->mbcntrl = NULL;

  if (proxy->dist)
    copy->dist = (GstVaapiEncFeiDistortion *)
        gst_vaapi_fei_codec_object_ref (GST_VAAPI_FEI_CODEC_OBJECT
        (proxy->dist));
  else
    copy->dist = NULL;


#endif

  return copy;
}
static gboolean
gst_vaapi_window_wayland_render (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);
    struct wl_display *const wl_display =
        GST_VAAPI_OBJECT_NATIVE_DISPLAY (window);
    struct wl_buffer *buffer;
    FrameState *frame;
    guint width, height, va_flags;
    VAStatus status;
    gboolean need_vpp = FALSE;

    /* Check that we don't need to crop source VA surface */
    gst_vaapi_surface_get_size (surface, &width, &height);
    if (src_rect->x != 0 || src_rect->y != 0)
        need_vpp = TRUE;
    if (src_rect->width != width || src_rect->height != height)
        need_vpp = TRUE;

    /* Check that we don't render to a subregion of this window */
    if (dst_rect->x != 0 || dst_rect->y != 0)
        need_vpp = TRUE;
    if (dst_rect->width != window->width || dst_rect->height != window->height)
        need_vpp = TRUE;

    /* Try to construct a Wayland buffer from VA surface as is (without VPP) */
    if (!need_vpp) {
        GST_VAAPI_OBJECT_LOCK_DISPLAY (window);
        va_flags = from_GstVaapiSurfaceRenderFlags (flags);
        status = vaGetSurfaceBufferWl (GST_VAAPI_DISPLAY_VADISPLAY (display),
                                       GST_VAAPI_OBJECT_ID (surface),
                                       va_flags & (VA_TOP_FIELD | VA_BOTTOM_FIELD), &buffer);
        GST_VAAPI_OBJECT_UNLOCK_DISPLAY (window);
        if (status == VA_STATUS_ERROR_FLAG_NOT_SUPPORTED)
            need_vpp = TRUE;
        else if (!vaapi_check_status (status, "vaGetSurfaceBufferWl()"))
            return FALSE;
    }

    /* Try to construct a Wayland buffer with VPP */
    if (need_vpp) {
        if (priv->use_vpp) {
            GstVaapiSurface *const vpp_surface =
                vpp_convert (window, surface, src_rect, dst_rect, flags);
            if (G_UNLIKELY (!vpp_surface))
                need_vpp = FALSE;
            else {
                surface = vpp_surface;
                width = window->width;
                height = window->height;
            }
        }

        GST_VAAPI_OBJECT_LOCK_DISPLAY (window);
        status = vaGetSurfaceBufferWl (GST_VAAPI_DISPLAY_VADISPLAY (display),
                                       GST_VAAPI_OBJECT_ID (surface), VA_FRAME_PICTURE, &buffer);
        GST_VAAPI_OBJECT_UNLOCK_DISPLAY (window);
        if (!vaapi_check_status (status, "vaGetSurfaceBufferWl()"))
            return FALSE;
    }

    /* Wait for the previous frame to complete redraw */
    if (!gst_vaapi_window_wayland_sync (window)) {
        wl_buffer_destroy (buffer);
        return !priv->sync_failed;
    }

    frame = frame_state_new (window);
    if (!frame)
        return FALSE;
    g_atomic_pointer_set (&priv->last_frame, frame);
    g_atomic_int_inc (&priv->num_frames_pending);

    if (need_vpp && priv->use_vpp) {
        frame->surface = surface;
        frame->surface_pool = gst_vaapi_video_pool_ref (priv->surface_pool);
    }

    /* XXX: attach to the specified target rectangle */
    GST_VAAPI_OBJECT_LOCK_DISPLAY (window);
    wl_surface_attach (priv->surface, buffer, 0, 0);
    wl_surface_damage (priv->surface, 0, 0, width, height);

    if (priv->opaque_region) {
        wl_surface_set_opaque_region (priv->surface, priv->opaque_region);
        wl_region_destroy (priv->opaque_region);
        priv->opaque_region = NULL;
    }

    wl_proxy_set_queue ((struct wl_proxy *) buffer, priv->event_queue);
    wl_buffer_add_listener (buffer, &frame_buffer_listener, frame);

    frame->callback = wl_surface_frame (priv->surface);
    wl_callback_add_listener (frame->callback, &frame_callback_listener, frame);

    wl_surface_commit (priv->surface);
    wl_display_flush (wl_display);
    GST_VAAPI_OBJECT_UNLOCK_DISPLAY (window);
    return TRUE;
}