void
gst_vaapi_decoder_finalize (GstVaapiDecoder * decoder)
{
  const GstVaapiDecoderClass *const klass =
      GST_VAAPI_DECODER_GET_CLASS (decoder);

  if (klass->destroy)
    klass->destroy (decoder);

  gst_video_codec_state_unref (decoder->codec_state);
  decoder->codec_state = NULL;

  parser_state_finalize (&decoder->parser_state);

  if (decoder->buffers) {
    g_async_queue_unref (decoder->buffers);
    decoder->buffers = NULL;
  }

  if (decoder->frames) {
    g_async_queue_unref (decoder->frames);
    decoder->frames = NULL;
  }

  gst_vaapi_object_replace (&decoder->context, NULL);
  decoder->va_context = VA_INVALID_ID;

  gst_vaapi_display_replace (&decoder->display, NULL);
  decoder->va_display = NULL;
}
static void
gst_vaapi_video_meta_finalize (GstVaapiVideoMeta * meta)
{
  gst_vaapi_video_meta_destroy_image (meta);
  gst_vaapi_video_meta_destroy_proxy (meta);
  gst_vaapi_display_replace (&meta->display, NULL);
}
/**
 * gst_vaapi_plugin_base_close:
 * @plugin: a #GstVaapiPluginBase
 *
 * Deallocates all internal resources that were allocated so
 * far. i.e. put the base plugin object into a clean state.
 */
void
gst_vaapi_plugin_base_close (GstVaapiPluginBase * plugin)
{
  /* Release vaapi textures first if exist, which refs display object */
  plugin_reset_texture_map (plugin);

  gst_vaapi_display_replace (&plugin->display, NULL);
  gst_object_replace (&plugin->gl_context, NULL);
  gst_object_replace (&plugin->gl_display, NULL);
  gst_object_replace (&plugin->gl_other_context, NULL);

  gst_caps_replace (&plugin->sinkpad_caps, NULL);
  gst_video_info_init (&plugin->sinkpad_info);
  if (plugin->sinkpad_buffer_pool) {
    gst_object_unref (plugin->sinkpad_buffer_pool);
    plugin->sinkpad_buffer_pool = NULL;
  }
  g_clear_object (&plugin->srcpad_buffer_pool);

  g_clear_object (&plugin->sinkpad_allocator);
  g_clear_object (&plugin->srcpad_allocator);

  gst_caps_replace (&plugin->srcpad_caps, NULL);
  gst_video_info_init (&plugin->srcpad_info);
  gst_caps_replace (&plugin->allowed_raw_caps, NULL);
}
/* 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 gboolean
gst_vaapi_display_egl_bind_display (GstVaapiDisplay * base_display,
    gpointer native_params)
{
  GstVaapiDisplay *native_display = NULL;
  GstVaapiDisplayEGL *display = GST_VAAPI_DISPLAY_EGL (base_display);
  EglDisplay *egl_display;
  const InitParams *params = (InitParams *) native_params;

  if (params->display) {
    native_display = params->display;
  } else {
#if USE_X11
    native_display = gst_vaapi_display_x11_new (NULL);
#endif
#if USE_WAYLAND
    if (!native_display)
      native_display = gst_vaapi_display_wayland_new (NULL);
#endif
  }
  if (!native_display)
    return FALSE;

  gst_vaapi_display_replace (&display->display, native_display);

  egl_display = egl_display_new (GST_VAAPI_DISPLAY_NATIVE (display->display));
  if (!egl_display)
    return FALSE;

  egl_object_replace (&display->egl_display, egl_display);
  egl_object_unref (egl_display);
  display->gles_version = params->gles_version;
  return TRUE;
}
static void
gst_vaapi_object_finalize (GstVaapiObject * object)
{
  const GstVaapiObjectClass *const klass = GST_VAAPI_OBJECT_GET_CLASS (object);

  if (klass->finalize)
    klass->finalize (object);
  gst_vaapi_display_replace (&object->display, NULL);
}
/* GstVideoContext interface */
static void
plugin_set_display (GstVaapiPluginBase * plugin, GstVaapiDisplay * display)
{
  const gchar *const display_name =
      gst_vaapi_display_get_display_name (display);

  if (plugin->display_name && g_strcmp0 (plugin->display_name, display_name)) {
    GST_DEBUG_OBJECT (plugin, "incompatible display name '%s', requested '%s'",
        display_name, plugin->display_name);
    gst_vaapi_display_replace (&plugin->display, NULL);
  } else {
    GST_INFO_OBJECT (plugin, "set display %p", display);
    gst_vaapi_display_replace (&plugin->display, display);
    plugin->display_type = gst_vaapi_display_get_display_type (display);
    gst_vaapi_plugin_base_set_display_name (plugin, display_name);
  }
  gst_vaapi_display_unref (display);
}
void
gst_vaapi_video_pool_finalize (GstVaapiVideoPool * pool)
{
  g_list_free_full (pool->used_objects, gst_vaapi_object_unref);
  g_queue_foreach (&pool->free_objects, (GFunc) gst_vaapi_object_unref, NULL);
  g_queue_clear (&pool->free_objects);
  gst_vaapi_display_replace (&pool->display, NULL);
  g_mutex_clear (&pool->mutex);
}
static void
gst_vaapi_video_buffer_pool_finalize (GObject * object)
{
  GstVaapiVideoBufferPoolPrivate *const priv =
      GST_VAAPI_VIDEO_BUFFER_POOL (object)->priv;

  gst_vaapi_display_replace (&priv->display, NULL);
  g_clear_object (&priv->allocator);

  G_OBJECT_CLASS (gst_vaapi_video_buffer_pool_parent_class)->finalize (object);
}
/**
 * gst_vaapi_plugin_base_ensure_display:
 * @plugin: a #GstVaapiPluginBase
 *
 * Ensures the display stored in @plugin complies with the requested
 * display type constraints.
 *
 * Returns: %TRUE if the display was created to match the requested
 *   type, %FALSE otherwise.
 */
gboolean
gst_vaapi_plugin_base_ensure_display (GstVaapiPluginBase * plugin)
{
  if (gst_vaapi_plugin_base_has_display_type (plugin, plugin->display_type_req))
    return TRUE;
  gst_vaapi_display_replace (&plugin->display, NULL);

  if (!gst_vaapi_ensure_display (GST_ELEMENT (plugin),
          plugin->display_type_req))
    return FALSE;
  plugin->display_type = gst_vaapi_display_get_display_type (plugin->display);

  GST_VAAPI_PLUGIN_BASE_GET_CLASS (plugin)->display_changed (plugin);
  return TRUE;
}
static void
gst_vaapi_display_egl_finalize (GObject * object)
{
  GstVaapiDisplayEGL *dpy = GST_VAAPI_DISPLAY_EGL (object);

  if (dpy->texture_map)
    gst_object_unref (dpy->texture_map);

  /* HACK to avoid to call twice vaTerminate() since this and the
   * proxied display share the same vaDisplay */
  GST_VAAPI_DISPLAY_VADISPLAY (object) = NULL;

  egl_object_replace (&dpy->egl_display, NULL);
  egl_object_replace (&dpy->egl_context, NULL);

  gst_vaapi_display_replace (&dpy->display, NULL);

  G_OBJECT_CLASS (gst_vaapi_display_egl_parent_class)->finalize (object);
}
/**
 * gst_vaapi_plugin_base_close:
 * @plugin: a #GstVaapiPluginBase
 *
 * Deallocates all internal resources that were allocated so
 * far. i.e. put the base plugin object into a clean state.
 */
void
gst_vaapi_plugin_base_close (GstVaapiPluginBase * plugin)
{
  gst_vaapi_display_replace (&plugin->display, NULL);
  gst_object_replace (&plugin->gl_context, NULL);

  gst_caps_replace (&plugin->sinkpad_caps, NULL);
  plugin->sinkpad_caps_changed = FALSE;
  gst_video_info_init (&plugin->sinkpad_info);
  if (plugin->sinkpad_buffer_pool) {
    gst_object_unref (plugin->sinkpad_buffer_pool);
    plugin->sinkpad_buffer_pool = NULL;
  }
  g_clear_object (&plugin->srcpad_buffer_pool);

  gst_caps_replace (&plugin->srcpad_caps, NULL);
  plugin->srcpad_caps_changed = FALSE;
  gst_video_info_init (&plugin->srcpad_info);
  gst_caps_replace (&plugin->allowed_raw_caps, NULL);
}
static void
app_free (App * app)
{
  guint i;

  if (!app)
    return;

  if (app->file) {
    g_mapped_file_unref (app->file);
    app->file = NULL;
  }
  g_free (app->file_name);

  for (i = 0; i < G_N_ELEMENTS (app->pixmaps); i++)
    gst_vaapi_pixmap_replace (&app->pixmaps[i], NULL);
  gst_vaapi_decoder_replace (&app->decoder, NULL);
  gst_vaapi_window_replace (&app->window, NULL);
  gst_vaapi_display_replace (&app->display, NULL);

  if (app->decoder_queue) {
    g_async_queue_unref (app->decoder_queue);
    app->decoder_queue = NULL;
  }
  g_cond_clear (&app->decoder_ready);

  if (app->timer) {
    g_timer_destroy (app->timer);
    app->timer = NULL;
  }

  g_cond_clear (&app->render_ready);
  g_cond_clear (&app->event_cond);
  g_mutex_clear (&app->mutex);
  g_slice_free (App, app);
}
static gboolean
gst_vaapi_display_egl_bind_display (GstVaapiDisplay * base_display,
    gpointer native_params)
{
  GstVaapiDisplay *native_vaapi_display;
  GstVaapiDisplayEGL *display = GST_VAAPI_DISPLAY_EGL (base_display);
  EglDisplay *egl_display;
  EGLDisplay *native_egl_display;
  guint gl_platform = EGL_PLATFORM_UNKNOWN;
  const InitParams *params = (InitParams *) native_params;
  GstVaapiDisplayPrivate *const priv = GST_VAAPI_DISPLAY_GET_PRIVATE (display);

  native_vaapi_display = params->display;
  native_egl_display = params->gl_display;

  if (!native_vaapi_display) {
#if USE_X11
    if (params->display_type == GST_VAAPI_DISPLAY_TYPE_ANY
        || params->display_type == GST_VAAPI_DISPLAY_TYPE_X11
        || params->display_type == GST_VAAPI_DISPLAY_TYPE_EGL)
      native_vaapi_display = gst_vaapi_display_x11_new (NULL);
#endif
#if USE_WAYLAND
    if (!native_vaapi_display)
      native_vaapi_display = gst_vaapi_display_wayland_new (NULL);
#endif
  } else {
    /* thus it could be assigned to parent */
    gst_object_ref (native_vaapi_display);
  }
  if (!native_vaapi_display)
    return FALSE;

  gst_vaapi_display_replace (&display->display, native_vaapi_display);
  priv->parent = native_vaapi_display;

  switch (GST_VAAPI_DISPLAY_GET_CLASS_TYPE (display->display)) {
    case GST_VAAPI_DISPLAY_TYPE_X11:
      gl_platform = EGL_PLATFORM_X11;
      break;
    case GST_VAAPI_DISPLAY_TYPE_WAYLAND:
      gl_platform = EGL_PLATFORM_WAYLAND;
      break;
    default:
      break;
  }

  if (native_egl_display) {
    egl_display = egl_display_new_wrapped (native_egl_display);
  } else {
    egl_display = egl_display_new (GST_VAAPI_DISPLAY_NATIVE (display->display),
        gl_platform);
  }
  if (!egl_display)
    return FALSE;

  egl_object_replace (&display->egl_display, egl_display);
  egl_object_unref (egl_display);
  display->gles_version = params->gles_version;
  return TRUE;
}
static void
gst_vaapi_display_egl_close_display (GstVaapiDisplay * base_display)
{
  GstVaapiDisplayEGL *display = GST_VAAPI_DISPLAY_EGL (base_display);
  gst_vaapi_display_replace (&display->display, NULL);
}
static inline void
set_display (GstVaapiVideoMeta * meta, GstVaapiDisplay * display)
{
  gst_vaapi_display_replace (&meta->display, display);
}