static void
gst_vaapi_video_allocator_finalize (GObject * object)
{
  GstVaapiVideoAllocator *const allocator =
      GST_VAAPI_VIDEO_ALLOCATOR_CAST (object);

  gst_vaapi_video_pool_replace (&allocator->surface_pool, NULL);
  gst_vaapi_video_pool_replace (&allocator->image_pool, NULL);

  G_OBJECT_CLASS (gst_vaapi_video_allocator_parent_class)->finalize (object);
}
static void
gst_vaapi_window_wayland_destroy (GstVaapiWindow * window)
{
    GstVaapiWindowWaylandPrivate *const priv =
        GST_VAAPI_WINDOW_WAYLAND_GET_PRIVATE (window);

    /* Wait for the last frame to complete redraw */
    gst_vaapi_window_wayland_sync (window);

    if (priv->last_frame) {
        frame_state_free (priv->last_frame);
        priv->last_frame = NULL;
    }

    if (priv->shell_surface) {
        wl_shell_surface_destroy (priv->shell_surface);
        priv->shell_surface = NULL;
    }

    if (priv->surface) {
        wl_surface_destroy (priv->surface);
        priv->surface = NULL;
    }

    if (priv->event_queue) {
        wl_event_queue_destroy (priv->event_queue);
        priv->event_queue = NULL;
    }

    gst_vaapi_filter_replace (&priv->filter, NULL);
    gst_vaapi_video_pool_replace (&priv->surface_pool, NULL);

    gst_poll_free (priv->poll);
}
/* Base encoder cleanup (internal) */
void
gst_vaapi_encoder_finalize (GstVaapiEncoder * encoder)
{
  GstVaapiEncoderClass *const klass = GST_VAAPI_ENCODER_GET_CLASS (encoder);

  klass->finalize (encoder);

  gst_vaapi_object_replace (&encoder->context, NULL);
  gst_vaapi_display_replace (&encoder->display, NULL);
  encoder->va_display = NULL;

  if (encoder->properties) {
    g_ptr_array_unref (encoder->properties);
    encoder->properties = NULL;
  }

  gst_vaapi_video_pool_replace (&encoder->codedbuf_pool, NULL);
  if (encoder->codedbuf_queue) {
    g_async_queue_unref (encoder->codedbuf_queue);
    encoder->codedbuf_queue = NULL;
  }
  g_cond_clear (&encoder->surface_free);
  g_cond_clear (&encoder->codedbuf_free);
  g_mutex_clear (&encoder->mutex);
}
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 void
coded_buffer_proxy_finalize (GstVaapiCodedBufferProxy * proxy)
{
  if (proxy->buffer) {
    if (proxy->pool)
      gst_vaapi_video_pool_put_object (proxy->pool, proxy->buffer);
    gst_vaapi_object_unref (proxy->buffer);
    proxy->buffer = NULL;
  }
  gst_vaapi_video_pool_replace (&proxy->pool, NULL);
  coded_buffer_proxy_set_user_data (proxy, NULL, NULL);

  /* Notify the user function that the object is now destroyed */
  if (proxy->destroy_func)
    proxy->destroy_func (proxy->destroy_data);
}
/* Reconfigures the encoder with the new properties */
static GstVaapiEncoderStatus
gst_vaapi_encoder_reconfigure_internal (GstVaapiEncoder * encoder)
{
  GstVaapiEncoderClass *const klass = GST_VAAPI_ENCODER_GET_CLASS (encoder);
  GstVideoInfo *const vip = GST_VAAPI_ENCODER_VIDEO_INFO (encoder);
  GstVaapiEncoderStatus status;
  GstVaapiVideoPool *pool;
  guint codedbuf_size;

  /* Generate a keyframe every second */
  if (!encoder->keyframe_period)
    encoder->keyframe_period = (vip->fps_n + vip->fps_d - 1) / vip->fps_d;

  status = klass->reconfigure (encoder);
  if (status != GST_VAAPI_ENCODER_STATUS_SUCCESS)
    return status;

  if (!gst_vaapi_encoder_ensure_context (encoder))
    goto error_reset_context;

  codedbuf_size = encoder->codedbuf_pool ?
      gst_vaapi_coded_buffer_pool_get_buffer_size (GST_VAAPI_CODED_BUFFER_POOL
      (encoder)) : 0;
  if (codedbuf_size != encoder->codedbuf_size) {
    pool = gst_vaapi_coded_buffer_pool_new (encoder, encoder->codedbuf_size);
    if (!pool)
      goto error_alloc_codedbuf_pool;
    gst_vaapi_video_pool_set_capacity (pool, 5);
    gst_vaapi_video_pool_replace (&encoder->codedbuf_pool, pool);
    gst_vaapi_video_pool_unref (pool);
  }
  return GST_VAAPI_ENCODER_STATUS_SUCCESS;

  /* ERRORS */
error_alloc_codedbuf_pool:
  {
    GST_ERROR ("failed to initialize coded buffer pool");
    return GST_VAAPI_ENCODER_STATUS_ERROR_ALLOCATION_FAILED;
  }
error_reset_context:
  {
    GST_ERROR ("failed to update VA context");
    return GST_VAAPI_ENCODER_STATUS_ERROR_OPERATION_FAILED;
  }
}
static void
frame_state_free (FrameState * frame)
{
    if (!frame)
        return;

    if (frame->surface) {
        if (frame->surface_pool)
            gst_vaapi_video_pool_put_object (frame->surface_pool, frame->surface);
        frame->surface = NULL;
    }
    gst_vaapi_video_pool_replace (&frame->surface_pool, NULL);

    if (frame->callback) {
        wl_callback_destroy (frame->callback);
        frame->callback = NULL;
    }
    g_slice_free (FrameState, frame);
}
static gboolean
gst_vaapi_window_wayland_resize (GstVaapiWindow * window,
                                 guint width, guint height)
{
    GstVaapiWindowWaylandPrivate *const priv =
        GST_VAAPI_WINDOW_WAYLAND_GET_PRIVATE (window);
    GstVaapiDisplayWaylandPrivate *const priv_display =
        GST_VAAPI_DISPLAY_WAYLAND_GET_PRIVATE (GST_VAAPI_OBJECT_DISPLAY (window));

    GST_DEBUG ("resize window, new size %ux%u", width, height);

    gst_vaapi_video_pool_replace (&priv->surface_pool, NULL);
    if (priv->opaque_region)
        wl_region_destroy (priv->opaque_region);
    GST_VAAPI_OBJECT_LOCK_DISPLAY (window);
    priv->opaque_region = wl_compositor_create_region (priv_display->compositor);
    GST_VAAPI_OBJECT_UNLOCK_DISPLAY (window);
    wl_region_add (priv->opaque_region, 0, 0, width, height);

    return TRUE;
}
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 int
app_run (App * app)
{
  GstVaapiImage *image;
  GstVaapiVideoPool *pool;
  GThread *buffer_thread;
  gsize id;
  int ret = EXIT_FAILURE;

  image = gst_vaapi_image_new (app->display, GST_VIDEO_FORMAT_I420,
      app->parser->width, app->parser->height);

  {
    GstVideoInfo vi;
    gst_video_info_set_format (&vi, GST_VIDEO_FORMAT_ENCODED,
        app->parser->width, app->parser->height);
    pool = gst_vaapi_surface_pool_new_full (app->display, &vi, 0);
  }

  buffer_thread = g_thread_new ("get buffer thread", get_buffer_thread, app);

  while (1) {
    GstVaapiSurfaceProxy *proxy;
    GstVaapiSurface *surface;

    if (!load_frame (app, image))
      break;

    if (!gst_vaapi_image_unmap (image))
      break;

    proxy =
        gst_vaapi_surface_proxy_new_from_pool (GST_VAAPI_SURFACE_POOL (pool));
    if (!proxy) {
      g_warning ("Could not get surface proxy from pool.");
      break;
    }
    surface = gst_vaapi_surface_proxy_get_surface (proxy);
    if (!surface) {
      g_warning ("Could not get surface from proxy.");
      break;
    }

    if (!gst_vaapi_surface_put_image (surface, image)) {
      g_warning ("Could not update surface");
      break;
    }

    if (!upload_frame (app->encoder, proxy)) {
      g_warning ("put frame failed");
      break;
    }

    app->read_frames++;
    id = gst_vaapi_surface_get_id (surface);
    g_debug ("input frame %d, surface id = %" G_GSIZE_FORMAT, app->read_frames,
        id);

    gst_vaapi_surface_proxy_unref (proxy);
  }

  app->input_stopped = TRUE;

  g_thread_join (buffer_thread);

  if (!app->encode_failed && feof (app->parser->fp))
    ret = EXIT_SUCCESS;

  gst_vaapi_video_pool_replace (&pool, NULL);
  gst_vaapi_object_unref (image);
  return ret;
}