static void
render_frame_free (RenderFrame * rfp)
{
  if (G_UNLIKELY (!rfp))
    return;
  gst_vaapi_surface_proxy_replace (&rfp->proxy, NULL);
  g_slice_free (RenderFrame, rfp);
}
static void
gst_vaapi_video_memory_free (GstVaapiVideoMemory * mem)
{
  mem->surface = NULL;
  gst_vaapi_video_memory_reset_image (mem);
  gst_vaapi_surface_proxy_replace (&mem->proxy, NULL);
  gst_vaapi_video_meta_replace (&mem->meta, NULL);
  gst_object_unref (GST_MEMORY_CAST (mem)->allocator);
  g_slice_free (GstVaapiVideoMemory, mem);
}
void
gst_vaapi_video_memory_reset_surface (GstVaapiVideoMemory * mem)
{
  mem->surface = NULL;
  gst_vaapi_video_memory_reset_image (mem);
  gst_vaapi_surface_proxy_replace (&mem->proxy, NULL);
  if (mem->meta)
    gst_vaapi_video_meta_set_surface_proxy (mem->meta, NULL);

  GST_VAAPI_VIDEO_MEMORY_FLAG_UNSET (mem,
      GST_VAAPI_VIDEO_MEMORY_FLAG_SURFACE_IS_CURRENT);
}
static gboolean
ensure_surface (GstVaapiVideoMemory * mem)
{
  if (!mem->proxy) {
    gst_vaapi_surface_proxy_replace (&mem->proxy,
        gst_vaapi_video_meta_get_surface_proxy (mem->meta));

    if (!mem->proxy) {
      mem->proxy = new_surface_proxy (mem);
      if (!mem->proxy)
        return FALSE;
      gst_vaapi_video_meta_set_surface_proxy (mem->meta, mem->proxy);
    }
  }
  mem->surface = GST_VAAPI_SURFACE_PROXY_SURFACE (mem->proxy);
  return mem->surface != NULL;
}
static void
gst_vaapi_surface_proxy_finalize (GstVaapiSurfaceProxy * proxy)
{
  if (proxy->surface) {
    if (proxy->pool && !proxy->parent)
      gst_vaapi_video_pool_put_object (proxy->pool, proxy->surface);
    gst_vaapi_object_unref (proxy->surface);
    proxy->surface = NULL;
  }
  gst_vaapi_video_pool_replace (&proxy->pool, NULL);
  gst_vaapi_surface_proxy_replace (&proxy->parent, NULL);

  /* Notify the user function that the object is now destroyed */
  if (proxy->destroy_func)
    proxy->destroy_func (proxy->destroy_data);

#if USE_H264_FEI_ENCODER
  if (proxy->mvpred)
    gst_vaapi_fei_codec_object_replace ((GstVaapiFeiCodecObject **) &
        proxy->mvpred, NULL);
  if (proxy->mbcntrl)
    gst_vaapi_fei_codec_object_replace ((GstVaapiFeiCodecObject **) &
        proxy->mbcntrl, NULL);
  if (proxy->qp)
    gst_vaapi_fei_codec_object_replace ((GstVaapiFeiCodecObject **) &
        proxy->qp, NULL);
  if (proxy->mbcode)
    gst_vaapi_fei_codec_object_replace ((GstVaapiFeiCodecObject **) &
        proxy->mbcode, NULL);
  if (proxy->mv)
    gst_vaapi_fei_codec_object_replace ((GstVaapiFeiCodecObject **) &
        proxy->mv, NULL);
  if (proxy->dist)
    gst_vaapi_fei_codec_object_replace ((GstVaapiFeiCodecObject **) &
        proxy->dist, NULL);
#endif
}
static void
gst_vaapi_video_memory_unmap (GstVaapiVideoMemory * mem)
{
  if (mem->map_count == 1) {
    switch (mem->map_type) {
      case GST_VAAPI_VIDEO_MEMORY_MAP_TYPE_SURFACE:
        gst_vaapi_surface_proxy_replace (&mem->proxy, NULL);
        break;
      case GST_VAAPI_VIDEO_MEMORY_MAP_TYPE_LINEAR:
        gst_vaapi_image_unmap (mem->image);
        break;
      default:
        goto error_incompatible_map;
    }
    mem->map_type = 0;
  }
  mem->map_count--;
  return;

  /* ERRORS */
error_incompatible_map:
  GST_ERROR ("incompatible map type (%d)", mem->map_type);
  return;
}
static inline void
gst_vaapi_video_meta_destroy_proxy (GstVaapiVideoMeta * meta)
{
  gst_vaapi_surface_proxy_replace (&meta->proxy, NULL);
}
static gpointer
gst_vaapi_video_memory_map (GstVaapiVideoMemory * mem, gsize maxsize,
    guint flags)
{
  gpointer data;

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

  if (mem->map_count == 0) {
    switch (flags & GST_MAP_READWRITE) {
      case 0:
        // No flags set: return a GstVaapiSurfaceProxy
        gst_vaapi_surface_proxy_replace (&mem->proxy,
            gst_vaapi_video_meta_get_surface_proxy (mem->meta));
        if (!mem->proxy)
          goto error_no_surface_proxy;
        if (!ensure_surface_is_current (mem))
          goto error_no_current_surface;
        mem->map_type = GST_VAAPI_VIDEO_MEMORY_MAP_TYPE_SURFACE;
        break;
      case GST_MAP_READ:
        // Only read flag set: return raw pixels
        if (!ensure_surface (mem))
          goto error_no_surface;
        if (!ensure_image (mem))
          goto error_no_image;
        if (!ensure_image_is_current (mem))
          goto error_no_current_image;
        if (!gst_vaapi_image_map (mem->image))
          goto error_map_image;
        mem->map_type = GST_VAAPI_VIDEO_MEMORY_MAP_TYPE_LINEAR;
        break;
      default:
        goto error_unsupported_map;
    }
  }

  switch (mem->map_type) {
    case GST_VAAPI_VIDEO_MEMORY_MAP_TYPE_SURFACE:
      if (!mem->proxy)
        goto error_no_surface_proxy;
      data = mem->proxy;
      break;
    case GST_VAAPI_VIDEO_MEMORY_MAP_TYPE_LINEAR:
      if (!mem->image)
        goto error_no_image;
      data = get_image_data (mem->image);
      break;
    default:
      goto error_unsupported_map_type;
  }
  mem->map_count++;
  return data;

  /* ERRORS */
error_unsupported_map:
  GST_ERROR ("unsupported map flags (0x%x)", flags);
  return NULL;
error_unsupported_map_type:
  GST_ERROR ("unsupported map type (%d)", mem->map_type);
  return NULL;
error_no_surface_proxy:
  GST_ERROR ("failed to extract GstVaapiSurfaceProxy from video meta");
  return NULL;
error_no_surface:
  GST_ERROR ("failed to extract VA surface from video buffer");
  return NULL;
error_no_current_surface:
  GST_ERROR ("failed to make surface current");
  return NULL;
error_no_image:
  GST_ERROR ("failed to extract VA image from video buffer");
  return NULL;
error_no_current_image:
  GST_ERROR ("failed to make image current");
  return NULL;
error_map_image:
  GST_ERROR ("failed to map VA image");
  return NULL;
}