static void
gst_vaapiencode_init (GstVaapiEncode * encode)
{
  GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (encode);

  gst_vaapi_plugin_base_init (GST_VAAPI_PLUGIN_BASE (encode), GST_CAT_DEFAULT);
  gst_pad_use_fixed_caps (plugin->srcpad);
}
gboolean
gst_vaapi_ensure_display (gpointer element, GstVaapiDisplayType type)
{
  GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (element);
  GstVaapiDisplay *display;
  GstVideoContext *context;

  g_return_val_if_fail (GST_IS_VIDEO_CONTEXT (element), FALSE);

  context = GST_VIDEO_CONTEXT (element);
  g_return_val_if_fail (context != NULL, FALSE);

  gst_vaapi_video_context_prepare (context, display_types);

  /* Neighbour found and it updated the display */
  if (gst_vaapi_plugin_base_has_display_type (plugin, type))
    return TRUE;

  /* If no neighboor, or application not interested, use system default */
  if (plugin->gl_context)
    display = gst_vaapi_create_display_from_gl_context (plugin->gl_context);
  else
    display = gst_vaapi_create_display (type, plugin->display_name);
  if (!display)
    return FALSE;

  gst_vaapi_video_context_propagate (context, display);
  GST_VAAPI_PLUGIN_BASE_DISPLAY_REPLACE (plugin, display);
  gst_vaapi_display_unref (display);
  return TRUE;
}
static gboolean
gst_vaapidecode_decide_allocation (GstVideoDecoder * vdec, GstQuery * query)
{
  GstVaapiDecode *const decode = GST_VAAPIDECODE (vdec);
  GstCaps *caps = NULL;

  gst_query_parse_allocation (query, &caps, NULL);
  if (!caps)
    goto error_no_caps;

  decode->has_texture_upload_meta = FALSE;

#if (USE_GLX || USE_EGL)
  decode->has_texture_upload_meta =
      gst_query_find_allocation_meta (query,
      GST_VIDEO_GL_TEXTURE_UPLOAD_META_API_TYPE, NULL) &&
      gst_vaapi_caps_feature_contains (caps,
      GST_VAAPI_CAPS_FEATURE_GL_TEXTURE_UPLOAD_META);
#endif

  return gst_vaapi_plugin_base_decide_allocation (GST_VAAPI_PLUGIN_BASE (vdec),
      query);

  /* ERRORS */
error_no_caps:
  {
    GST_ERROR_OBJECT (decode, "no caps specified");
    return FALSE;
  }
}
static void
gst_vaapidecode_init (GstVaapiDecode * decode)
{
  GstVideoDecoder *const vdec = GST_VIDEO_DECODER (decode);

  gst_vaapi_plugin_base_init (GST_VAAPI_PLUGIN_BASE (decode), GST_CAT_DEFAULT);

  decode->decoder = NULL;
  decode->decoder_caps = NULL;
  decode->allowed_caps = NULL;

  g_mutex_init (&decode->surface_ready_mutex);
  g_cond_init (&decode->surface_ready);

  gst_video_decoder_set_packetized (vdec, FALSE);

#if !GST_CHECK_VERSION(1,4,0)
  /* Pad through which data comes in to the element */
  GstPad *pad = GST_VAAPI_PLUGIN_BASE_SINK_PAD (decode);
  gst_pad_set_query_function (pad, GST_DEBUG_FUNCPTR (gst_vaapidecode_query));

  /* Pad through which data goes out of the element */
  pad = GST_VAAPI_PLUGIN_BASE_SRC_PAD (decode);
  gst_pad_set_query_function (pad, GST_DEBUG_FUNCPTR (gst_vaapidecode_query));
#endif
}
gboolean
gst_vaapi_ensure_display (GstElement * element, GstVaapiDisplayType type)
{
  GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (element);
  GstVaapiDisplay *display;

  g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);

  if (gst_vaapi_video_context_prepare (element, &plugin->display)) {
    /* Neighbour found and it updated the display */
    if (gst_vaapi_plugin_base_has_display_type (plugin, type))
      return TRUE;
  }

  /* If no neighboor, or application not interested, use system default */
  if (plugin->gl_context)
    display = gst_vaapi_create_display_from_gl_context (plugin->gl_context);
  else
    display = gst_vaapi_create_display (type, plugin->display_name);
  if (!display)
    return FALSE;

  gst_vaapi_video_context_propagate (element, display);
  gst_vaapi_display_unref (display);
  return TRUE;
}
static void
plugin_set_context (GstElement * element, GstContext * context)
{
  GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (element);
  GstVaapiDisplay *display = NULL;

  if (gst_vaapi_video_context_get_display (context, &display))
    plugin_set_display (plugin, display);
}
static gboolean
gst_vaapiencode_propose_allocation (GstVideoEncoder * venc, GstQuery * query)
{
  GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (venc);

  if (!gst_vaapi_plugin_base_propose_allocation (plugin, query))
    return FALSE;
  return TRUE;
}
static gboolean
is_src_allocator_dmabuf (GstVaapiDecode * decode)
{
  GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (decode);

  if (!GST_VAAPI_PLUGIN_BASE_SRC_PAD_CAN_DMABUF (plugin))
    return FALSE;
  return gst_vaapi_is_dmabuf_allocator (plugin->srcpad_allocator);
}
static gboolean
ensure_uploader (GstVaapiEncode * encode)
{
  if (!ensure_display (encode))
    return FALSE;
  if (!gst_vaapi_plugin_base_ensure_uploader (GST_VAAPI_PLUGIN_BASE (encode)))
    return FALSE;
  return TRUE;
}
static gboolean
gst_vaapiencode_close (GstVideoEncoder * venc)
{
  GstVaapiEncode *const encode = GST_VAAPIENCODE_CAST (venc);

  gst_vaapiencode_destroy (encode);
  gst_vaapi_plugin_base_close (GST_VAAPI_PLUGIN_BASE (encode));
  return TRUE;
}
static gboolean
gst_vaapidecode_close (GstVideoDecoder * vdec)
{
  GstVaapiDecode *const decode = GST_VAAPIDECODE (vdec);

  gst_vaapi_decode_input_state_replace (decode, NULL);
  gst_vaapidecode_destroy (decode);
  gst_vaapi_plugin_base_close (GST_VAAPI_PLUGIN_BASE (decode));
  return TRUE;
}
static gboolean
gst_vaapidecode_close (GstVideoDecoder * vdec)
{
  GstVaapiDecode *const decode = GST_VAAPIDECODE (vdec);

  gst_vaapidecode_destroy (decode);
  gst_caps_replace (&decode->allowed_srcpad_caps, NULL);
  gst_caps_replace (&decode->allowed_sinkpad_caps, NULL);
  gst_vaapi_plugin_base_close (GST_VAAPI_PLUGIN_BASE (decode));
  return TRUE;
}
static gboolean
gst_vaapiencode_set_format (GstVideoEncoder * venc, GstVideoCodecState * state)
{
  GstVaapiEncode *const encode = GST_VAAPIENCODE_CAST (venc);
  gboolean ret;

  g_return_val_if_fail (state->caps != NULL, FALSE);

  if (!set_codec_state (encode, state))
    return FALSE;

  if (!gst_vaapi_plugin_base_set_caps (GST_VAAPI_PLUGIN_BASE (encode),
          state->caps, NULL))
    return FALSE;

  if (encode->input_state)
    gst_video_codec_state_unref (encode->input_state);
  encode->input_state = gst_video_codec_state_ref (state);
  encode->input_state_changed = TRUE;

  ret = gst_pad_start_task (GST_VAAPI_PLUGIN_BASE_SRC_PAD (encode),
      (GstTaskFunction) gst_vaapiencode_buffer_loop, encode, NULL);

  if (!ret)
    return FALSE;

  /* Store some tags */
  {
    GstTagList *tags = gst_tag_list_new_empty ();
    const gchar *encoder, *codec;
    guint bitrate = 0;

    g_object_get (encode, "bitrate", &bitrate, NULL);
    gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_NOMINAL_BITRATE,
        bitrate, NULL);

    if ((encoder =
            gst_element_class_get_metadata (GST_ELEMENT_GET_CLASS (encode),
                GST_ELEMENT_METADATA_LONGNAME)))
      gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_ENCODER, encoder,
          NULL);

    if ((codec =
            gst_vaapi_codec_get_name (gst_vaapi_profile_get_codec
                (gst_vaapi_profile_from_caps (state->caps)))))
      gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_CODEC, codec,
          NULL);

    gst_video_encoder_merge_tags (venc, tags, GST_TAG_MERGE_REPLACE);
    gst_tag_list_unref (tags);
  }

  return TRUE;
}
static void
gst_vaapidecode_finalize (GObject * object)
{
  GstVaapiDecode *const decode = GST_VAAPIDECODE (object);

  g_cond_clear (&decode->surface_ready);
  g_mutex_clear (&decode->surface_ready_mutex);

  gst_vaapi_plugin_base_finalize (GST_VAAPI_PLUGIN_BASE (object));
  G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gst_vaapidecode_init (GstVaapiDecode * decode)
{
  GstVideoDecoder *const vdec = GST_VIDEO_DECODER (decode);

  gst_vaapi_plugin_base_init (GST_VAAPI_PLUGIN_BASE (decode), GST_CAT_DEFAULT);

  g_mutex_init (&decode->surface_ready_mutex);
  g_cond_init (&decode->surface_ready);

  gst_video_decoder_set_packetized (vdec, FALSE);
}
static GstCaps *
gst_vaapiencode_get_caps_impl (GstVideoEncoder * venc)
{
  GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (venc);
  GstCaps *caps;

  if (plugin->sinkpad_caps)
    caps = gst_caps_ref (plugin->sinkpad_caps);
  else {
    caps = gst_pad_get_pad_template_caps (plugin->sinkpad);
  }
  return caps;
}
static gboolean
gst_vaapidecode_open (GstVideoDecoder * vdec)
{
  GstVaapiDecode *const decode = GST_VAAPIDECODE (vdec);

  if (!gst_vaapi_plugin_base_open (GST_VAAPI_PLUGIN_BASE (decode)))
    return FALSE;

  decode->display_width = 0;
  decode->display_height = 0;
  gst_video_info_init (&decode->decoded_info);

  return TRUE;
}
static void
gst_vaapiencode_finalize (GObject * object)
{
  GstVaapiEncode *const encode = GST_VAAPIENCODE_CAST (object);

  gst_vaapiencode_destroy (encode);

  if (encode->prop_values) {
    g_ptr_array_unref (encode->prop_values);
    encode->prop_values = NULL;
  }

  gst_vaapi_plugin_base_finalize (GST_VAAPI_PLUGIN_BASE (object));
  G_OBJECT_CLASS (gst_vaapiencode_parent_class)->finalize (object);
}
static gboolean
gst_vaapiencode_open (GstVideoEncoder * venc)
{
  GstVaapiEncode *const encode = GST_VAAPIENCODE_CAST (venc);
  GstVaapiDisplay *const old_display = GST_VAAPI_PLUGIN_BASE_DISPLAY (encode);
  gboolean success;

  if (!gst_vaapi_plugin_base_open (GST_VAAPI_PLUGIN_BASE (encode)))
    return FALSE;

  GST_VAAPI_PLUGIN_BASE_DISPLAY (encode) = NULL;
  success = ensure_display (encode);
  if (old_display)
    gst_vaapi_display_unref (old_display);
  return success;
}
static gboolean
gst_vaapidecode_set_format (GstVideoDecoder * vdec, GstVideoCodecState * state)
{
  GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (vdec);
  GstVaapiDecode *const decode = GST_VAAPIDECODE (vdec);

  if (!gst_vaapi_decode_input_state_replace (decode, state))
    return TRUE;
  if (!gst_vaapidecode_update_sink_caps (decode, state->caps))
    return FALSE;
  if (!gst_vaapi_plugin_base_set_caps (plugin, decode->sinkpad_caps, NULL))
    return FALSE;
  if (!gst_vaapidecode_reset (decode, decode->sinkpad_caps, FALSE))
    return FALSE;

  return TRUE;
}
static gboolean
gst_vaapidecode_ensure_allowed_srcpad_caps (GstVaapiDecode * decode)
{
  GstCaps *out_caps, *raw_caps;

  if (decode->allowed_srcpad_caps)
    return TRUE;

  if (!GST_VAAPI_PLUGIN_BASE_DISPLAY (decode))
    return FALSE;

  /* Create VA caps */
  out_caps = gst_caps_from_string (GST_VAAPI_MAKE_SURFACE_CAPS);
  if (!out_caps) {
    GST_WARNING_OBJECT (decode, "failed to create VA/GL source caps");
    return FALSE;
  }
#if (USE_GLX || USE_EGL)
  if (!GST_VAAPI_PLUGIN_BASE_SRC_PAD_CAN_DMABUF (decode) &&
      gst_vaapi_display_has_opengl (GST_VAAPI_PLUGIN_BASE_DISPLAY (decode))) {
    out_caps = gst_caps_make_writable (out_caps);
    gst_caps_append (out_caps,
        gst_caps_from_string (GST_VAAPI_MAKE_GLTEXUPLOAD_CAPS));
  }
#endif
  out_caps = gst_caps_make_writable (out_caps);
  gst_caps_append (out_caps, gst_caps_from_string (GST_VAAPI_MAKE_DMABUF_CAPS));

  raw_caps = gst_vaapi_plugin_base_get_allowed_srcpad_raw_caps
      (GST_VAAPI_PLUGIN_BASE (decode),
      GST_VIDEO_INFO_FORMAT (&decode->decoded_info));
  if (!raw_caps) {
    gst_caps_unref (out_caps);
    GST_WARNING_OBJECT (decode, "failed to create raw sink caps");
    return FALSE;
  }

  out_caps = gst_caps_make_writable (out_caps);
  gst_caps_append (out_caps, gst_caps_copy (raw_caps));
  decode->allowed_srcpad_caps = out_caps;

  GST_INFO_OBJECT (decode, "allowed srcpad caps: %" GST_PTR_FORMAT,
      decode->allowed_srcpad_caps);

  return TRUE;
}
static gboolean
gst_vaapidecode_src_query (GstVideoDecoder * vdec, GstQuery * query)
{
  gboolean ret = TRUE;
  GstVaapiDecode *const decode = GST_VAAPIDECODE (vdec);
  GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (decode);

  switch (GST_QUERY_TYPE (query)) {
    case GST_QUERY_CAPS:{
      GstCaps *caps, *filter = NULL;
      GstPad *pad = GST_VIDEO_DECODER_SRC_PAD (vdec);

      gst_query_parse_caps (query, &filter);
      caps = gst_pad_get_pad_template_caps (pad);

      if (filter) {
        GstCaps *tmp = caps;
        caps = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
        gst_caps_unref (tmp);
      }

      gst_query_set_caps_result (query, caps);
      gst_caps_unref (caps);
      break;
    }
    case GST_QUERY_CONTEXT:{
      ret = gst_vaapi_handle_context_query (query, plugin->display);
      break;
    }
    default:{
#if GST_CHECK_VERSION(1,4,0)
      ret = GST_VIDEO_DECODER_CLASS (gst_vaapidecode_parent_class)->src_query
          (vdec, query);
#else
      GstPad *pad = GST_VIDEO_DECODER_SRC_PAD (vdec);
      GstObject *parent = gst_pad_get_parent (pad);
      ret = plugin->srcpad_query (pad, parent, query);
      if (parent)
        gst_object_unref (parent);
#endif
      break;
    }
  }

  return ret;
}
static gboolean
gst_vaapiencode_query (GstPad * pad, GstObject * parent, GstQuery * query)
{
  GstVaapiPluginBase *const plugin =
      GST_VAAPI_PLUGIN_BASE (gst_pad_get_parent_element (pad));
  gboolean success;

  GST_INFO_OBJECT (plugin, "query type %s", GST_QUERY_TYPE_NAME (query));

  if (gst_vaapi_reply_to_query (query, plugin->display))
    success = TRUE;
  else if (GST_PAD_IS_SINK (pad))
    success = plugin->sinkpad_query (plugin->sinkpad, parent, query);
  else
    success = plugin->srcpad_query (plugin->srcpad, parent, query);

  gst_object_unref (plugin);
  return success;
}
static void
gst_vaapi_find_gl_context (GstElement * element)
{
#if USE_GST_GL_HELPERS
  GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (element);

  /* if the element is vaapisink or any vaapi encoder it doesn't need
   * to know a GstGLContext in order to create an appropriate
   * GstVaapiDisplay. Let's them to choose their own
   * GstVaapiDisplay */
  if (GST_IS_VIDEO_SINK (element) || GST_IS_VIDEO_ENCODER (element))
    return;

  if (!gst_gl_ensure_element_data (plugin,
          (GstGLDisplay **) & plugin->gl_display,
          (GstGLContext **) & plugin->gl_other_context))
    goto no_valid_gl_display;

  gst_vaapi_find_gl_local_context (element, &plugin->gl_context);

  if (plugin->gl_context) {
    gst_vaapi_plugin_base_set_srcpad_can_dmabuf (plugin, plugin->gl_context);
  } else {
    GstObject *gl_context;

    gl_context = gst_vaapi_plugin_base_create_gl_context (plugin);
    if (gl_context) {
      gst_vaapi_plugin_base_set_gl_context (plugin, gl_context);
      gst_object_unref (gl_context);
    }
  }

  /* ERRORS */
no_valid_gl_display:
  {
    GST_INFO_OBJECT (plugin, "No valid GL display found");
    gst_object_replace (&plugin->gl_display, NULL);
    gst_object_replace (&plugin->gl_other_context, NULL);
    return;
  }
#endif
}
static gboolean
gst_vaapidecode_start (GstVideoDecoder * vdec)
{
  GstVaapiDecode *const decode = GST_VAAPIDECODE (vdec);
  GstVaapiDisplay *const old_display = GST_VAAPI_PLUGIN_BASE_DISPLAY (decode);
  gboolean success;

  /* Let GstVideoContext ask for a proper display to its neighbours */
  /* Note: steal old display that may be allocated from get_caps()
     so that to retain a reference to it, thus avoiding extra
     initialization steps if we turn out to simply re-use the
     existing (cached) VA display */
  GST_VAAPI_PLUGIN_BASE_DISPLAY (decode) = NULL;
  success =
      gst_vaapi_plugin_base_ensure_display (GST_VAAPI_PLUGIN_BASE (decode));
  if (old_display)
    gst_vaapi_display_unref (old_display);

  return success;
}
gboolean
gst_vaapi_handle_context_query (GstElement * element, GstQuery * query)
{
  GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (element);
  const gchar *type = NULL;
  GstContext *context, *old_context;

  g_return_val_if_fail (query != NULL, FALSE);

#if USE_GST_GL_HELPERS
  if (plugin->gl_display && plugin->gl_context && plugin->gl_other_context) {
    if (gst_gl_handle_context_query (element, query,
            (GstGLDisplay *) plugin->gl_display,
            (GstGLContext *) plugin->gl_context,
            (GstGLContext *) plugin->gl_other_context))
      return TRUE;
  }
#endif

  if (!plugin->display)
    return FALSE;

  if (!gst_query_parse_context_type (query, &type))
    return FALSE;

  if (g_strcmp0 (type, GST_VAAPI_DISPLAY_CONTEXT_TYPE_NAME))
    return FALSE;

  gst_query_parse_context (query, &old_context);
  if (old_context) {
    context = gst_context_copy (old_context);
    gst_vaapi_video_context_set_display (context, plugin->display);
  } else {
    context = gst_vaapi_video_context_new_with_display (plugin->display, FALSE);
  }

  gst_query_set_context (query, context);
  gst_context_unref (context);

  return TRUE;
}
static gboolean
gst_vaapidecode_negotiate (GstVaapiDecode * decode)
{
  GstVideoDecoder *const vdec = GST_VIDEO_DECODER (decode);
  GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (vdec);

  GST_DEBUG_OBJECT (decode, "input codec state changed: renegotiating");

  GST_VIDEO_DECODER_STREAM_LOCK (vdec);
  if (!gst_vaapi_plugin_base_set_caps (plugin, decode->sinkpad_caps, NULL))
    return FALSE;
  if (!gst_vaapidecode_update_src_caps (decode))
    return FALSE;
  if (!gst_vaapi_plugin_base_set_caps (plugin, NULL, decode->srcpad_caps))
    return FALSE;
  GST_VIDEO_DECODER_STREAM_UNLOCK (vdec);

  if (!gst_video_decoder_negotiate (vdec))
    return FALSE;

  return TRUE;
}
gboolean
gst_vaapi_ensure_display (GstElement * element, GstVaapiDisplayType type)
{
  GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (element);
  GstVaapiDisplay *display = NULL;

  g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);

  if (gst_vaapi_video_context_prepare (element, &plugin->display)) {
    /* Neighbour found and it updated the display */
    if (gst_vaapi_plugin_base_has_display_type (plugin, type))
      return TRUE;
  }

  /* Query for a local GstGL context. If it's found, it will be used
   * to create the VA display */
  if (!plugin->gl_context)
    gst_vaapi_find_gl_context (element);

  /* If no neighboor, or application not interested, use system default */
  if (plugin->gl_context) {
    display = gst_vaapi_create_display_from_gl_context (plugin->gl_context);
    /* Cannot instantiate VA display based on GL context. Reset the
     *  requested display type to ANY to try again */
    if (!display)
      gst_vaapi_plugin_base_set_display_type (plugin,
          GST_VAAPI_DISPLAY_TYPE_ANY);
  }
  if (!display)
    display = gst_vaapi_create_display (type, plugin->display_name);
  if (!display)
    return FALSE;

  gst_vaapi_video_context_propagate (element, display);
  gst_object_unref (display);
  return TRUE;
}
static gboolean
gst_vaapiencode_set_format (GstVideoEncoder * venc, GstVideoCodecState * state)
{
  GstVaapiEncode *const encode = GST_VAAPIENCODE_CAST (venc);

  g_return_val_if_fail (state->caps != NULL, FALSE);

  if (!ensure_encoder (encode))
    return FALSE;
  if (!set_codec_state (encode, state))
    return FALSE;

  if (!gst_vaapi_plugin_base_set_caps (GST_VAAPI_PLUGIN_BASE (encode),
          state->caps, NULL))
    return FALSE;

  if (encode->input_state)
    gst_video_codec_state_unref (encode->input_state);
  encode->input_state = gst_video_codec_state_ref (state);
  encode->input_state_changed = TRUE;

  return gst_pad_start_task (GST_VAAPI_PLUGIN_BASE_SRC_PAD (encode),
      (GstTaskFunction) gst_vaapiencode_buffer_loop, encode, NULL);
}
static inline gboolean
gst_vaapidecode_ensure_display (GstVaapiDecode * decode)
{
  return gst_vaapi_plugin_base_ensure_display (GST_VAAPI_PLUGIN_BASE (decode));
}