static gboolean
activate_vpp (GstVaapiDecodeBin * vaapidecbin)
{
  GstElement *src;

  if (vaapidecbin->ghost_pad_src || vaapidecbin->postproc)
    return TRUE;

  if (!vaapidecbin->has_vpp || vaapidecbin->disable_vpp) {
    src = vaapidecbin->queue;
    goto connect_src_ghost_pad;
  }

  /* create the postproc */
  vaapidecbin->postproc =
      gst_element_factory_make ("vaapipostproc", "vaapipostproc");
  if (!vaapidecbin->postproc)
    goto error_element_missing;

  g_object_set (G_OBJECT (vaapidecbin->postproc), "deinterlace-method",
      vaapidecbin->deinterlace_method, NULL);

  gst_bin_add (GST_BIN (vaapidecbin), vaapidecbin->postproc);
  if (!gst_element_link_pads_full (vaapidecbin->queue, "src",
          vaapidecbin->postproc, "sink", GST_PAD_LINK_CHECK_NOTHING))
    goto error_link_pad;

  GST_DEBUG_OBJECT (vaapidecbin, "Enabling VPP");
  src = vaapidecbin->postproc;

  goto connect_src_ghost_pad;

error_element_missing:
  {
    post_missing_element_message (vaapidecbin, "vaapipostproc");
    return FALSE;
  }
error_link_pad:
  {
    GST_ERROR_OBJECT (vaapidecbin, "Failed to link the child elements");
    return FALSE;
  }
connect_src_ghost_pad:
  {
    GstPad *srcpad, *ghostpad;

    srcpad = gst_element_get_static_pad (src, "src");
    ghostpad = gst_ghost_pad_new ("src", srcpad);
    vaapidecbin->ghost_pad_src = ghostpad;
    gst_object_unref (srcpad);
    gst_element_add_pad (GST_ELEMENT (vaapidecbin), ghostpad);
    return TRUE;
  }
}
static void
gst_vaapi_decode_bin_init (GstVaapiDecodeBin * vaapidecbin)
{
  GstPad *pad, *ghostpad;

  vaapidecbin->deinterlace_method = DEFAULT_DEINTERLACE_METHOD;
  vaapidecbin->disable_vpp = (g_getenv ("GST_VAAPI_DISABLE_VPP") != NULL);

  /* create the decoder */
  vaapidecbin->decoder =
      g_object_new (g_type_from_name ("GstVaapiDecode"), NULL);
  g_assert (vaapidecbin->decoder);

  /* create the queue */
  vaapidecbin->queue = gst_element_factory_make ("queue", "vaapi-queue");
  if (!vaapidecbin->queue) {
    g_clear_object (&vaapidecbin->decoder);
    post_missing_element_message (vaapidecbin, "queue");
    return;
  }

  gst_bin_add_many (GST_BIN (vaapidecbin), vaapidecbin->decoder,
      vaapidecbin->queue, NULL);

  if (!gst_element_link (vaapidecbin->decoder, vaapidecbin->queue)) {
    g_clear_object (&vaapidecbin->decoder);
    g_clear_object (&vaapidecbin->queue);
    g_critical ("failed to link decoder and queue");
    return;
  }

  /* create ghost pad sink */
  pad = gst_element_get_static_pad (vaapidecbin->decoder, "sink");
  ghostpad = gst_ghost_pad_new_from_template ("sink", pad,
      GST_PAD_PAD_TEMPLATE (pad));
  gst_object_unref (pad);
  if (!gst_element_add_pad (GST_ELEMENT (vaapidecbin), ghostpad))
    g_critical ("failed to add decoder sink pad to bin");

  /* create ghost pad src */
  pad = gst_element_get_static_pad (GST_ELEMENT (vaapidecbin->queue), "src");
  ghostpad = gst_ghost_pad_new_from_template ("src", pad,
      GST_PAD_PAD_TEMPLATE (pad));
  gst_object_unref (pad);
  if (!gst_element_add_pad (GST_ELEMENT (vaapidecbin), ghostpad))
    g_critical ("failed to add queue source pad to bin");
}
static gboolean
gst_vaapi_decode_bin_configure (GstVaapiDecodeBin * vaapidecbin)
{
  gchar *missing_factory = NULL;

  /* create the decoder */
  vaapidecbin->decoder =
      gst_element_factory_make ("vaapidecode", "vaapidecode");
  if (!vaapidecbin->decoder) {
    missing_factory = "vaapidecode";
    goto error_element_missing;
  }
  /* create the queue */
  vaapidecbin->queue = gst_element_factory_make ("queue", "queue");
  if (!vaapidecbin->queue) {
    missing_factory = "queue";
    goto error_element_missing;
  }

  g_object_set (G_OBJECT (vaapidecbin->queue),
      "max-size-bytes", vaapidecbin->max_size_bytes,
      "max-size-buffers", vaapidecbin->max_size_buffers,
      "max-size-time", vaapidecbin->max_size_time, NULL);

  gst_bin_add_many (GST_BIN (vaapidecbin),
      vaapidecbin->decoder, vaapidecbin->queue, NULL);

  if (!gst_element_link_pads_full (vaapidecbin->decoder, "src",
          vaapidecbin->queue, "sink", GST_PAD_LINK_CHECK_NOTHING))
    goto error_link_pad;

  return TRUE;

error_element_missing:
  {
    post_missing_element_message (vaapidecbin, missing_factory);
    return FALSE;
  }
error_link_pad:
  {
    GST_ERROR_OBJECT (vaapidecbin, "Failed to link the child elements");
    return FALSE;
  }
}
static void
pad_blocked_cb (GstPad * pad, gboolean blocked, GstPlaySinkVideoConvert * self)
{
    GstPad *peer;
    GstCaps *caps;
    gboolean raw;

    GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self);
    self->sink_proxypad_blocked = blocked;
    GST_DEBUG_OBJECT (self, "Pad blocked: %d", blocked);
    if (!blocked)
        goto done;

    /* There must be a peer at this point */
    peer = gst_pad_get_peer (self->sinkpad);
    caps = gst_pad_get_negotiated_caps (peer);
    if (!caps)
        caps = gst_pad_get_caps_reffed (peer);
    gst_object_unref (peer);

    raw = is_raw_caps (caps);
    GST_DEBUG_OBJECT (self, "Caps %" GST_PTR_FORMAT " are raw: %d", caps, raw);
    gst_caps_unref (caps);

    if (raw == self->raw)
        goto unblock;
    self->raw = raw;

    if (raw) {
        GstBin *bin = GST_BIN_CAST (self);
        GstElement *head = NULL, *prev = NULL;
        GstPad *pad;

        GST_DEBUG_OBJECT (self, "Creating raw conversion pipeline");

        gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), NULL);
        gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), NULL);

        self->conv = gst_element_factory_make ("ffmpegcolorspace", "conv");
        if (self->conv == NULL) {
            post_missing_element_message (self, "ffmpegcolorspace");
            GST_ELEMENT_WARNING (self, CORE, MISSING_PLUGIN,
                                 (_("Missing element '%s' - check your GStreamer installation."),
                                  "ffmpegcolorspace"), ("video rendering might fail"));
        } else {
            gst_bin_add (bin, self->conv);
            gst_element_sync_state_with_parent (self->conv);
            distribute_running_time (self->conv, &self->segment);
            prev = head = self->conv;
        }

        self->scale = gst_element_factory_make ("videoscale", "scale");
        if (self->scale == NULL) {
            post_missing_element_message (self, "videoscale");
            GST_ELEMENT_WARNING (self, CORE, MISSING_PLUGIN,
                                 (_("Missing element '%s' - check your GStreamer installation."),
                                  "videoscale"), ("possibly a liboil version mismatch?"));
        } else {
            /* Add black borders if necessary to keep the DAR */
            g_object_set (self->scale, "add-borders", TRUE, NULL);
            gst_bin_add (bin, self->scale);
            gst_element_sync_state_with_parent (self->scale);
            distribute_running_time (self->scale, &self->segment);
            if (prev) {
                if (!gst_element_link_pads_full (prev, "src", self->scale, "sink",
                                                 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
                    goto link_failed;
            } else {
                head = self->scale;
            }
            prev = self->scale;
        }

        if (head) {
            pad = gst_element_get_static_pad (head, "sink");
            gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), pad);
            gst_object_unref (pad);
        }

        if (prev) {
            pad = gst_element_get_static_pad (prev, "src");
            gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), pad);
            gst_object_unref (pad);
        }

        if (!head && !prev) {
            gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad),
                                      self->sink_proxypad);
        }

        GST_DEBUG_OBJECT (self, "Raw conversion pipeline created");
    } else {
        GstBin *bin = GST_BIN_CAST (self);

        GST_DEBUG_OBJECT (self, "Removing raw conversion pipeline");

        gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), NULL);
        gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), NULL);

        if (self->conv) {
            gst_element_set_state (self->conv, GST_STATE_NULL);
            gst_bin_remove (bin, self->conv);
            self->conv = NULL;
        }
        if (self->scale) {
            gst_element_set_state (self->scale, GST_STATE_NULL);
            gst_bin_remove (bin, self->scale);
            self->scale = NULL;
        }

        gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad),
                                  self->sink_proxypad);

        GST_DEBUG_OBJECT (self, "Raw conversion pipeline removed");
    }

unblock:
    gst_pad_set_blocked_async_full (self->sink_proxypad, FALSE,
                                    (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self),
                                    (GDestroyNotify) gst_object_unref);

done:
    GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self);
    return;

link_failed:
    {
        GST_ELEMENT_ERROR (self, CORE, PAD,
                           (NULL), ("Failed to configure the video converter."));
        gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad),
                                  self->sink_proxypad);
        gst_pad_set_blocked_async_full (self->sink_proxypad, FALSE,
                                        (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self),
                                        (GDestroyNotify) gst_object_unref);
        return;
    }
}
static gboolean
gst_vaapi_decode_bin_configure (GstVaapiDecodeBin * vaapidecbin)
{
  GstElement *capsfilter;
  GstCaps *caps;
  GstPad *queue_srcpad, *bin_srcpad, *capsfilter_sinkpad, *vpp_srcpad;
  gboolean res;
  gboolean has_vpp;

  g_object_set (G_OBJECT (vaapidecbin->queue),
      "max-size-bytes", vaapidecbin->max_size_bytes,
      "max-size-buffers", vaapidecbin->max_size_buffers,
      "max-size-time", vaapidecbin->max_size_time, NULL);

  if (vaapidecbin->disable_vpp || vaapidecbin->configured)
    return TRUE;

  has_vpp = _gst_vaapi_has_video_processing;

  if (!has_vpp && (vaapidecbin->deinterlace_method ==
          GST_VAAPI_DEINTERLACE_METHOD_MOTION_ADAPTIVE
          || vaapidecbin->deinterlace_method ==
          GST_VAAPI_DEINTERLACE_METHOD_MOTION_COMPENSATED)) {
    GST_ERROR_OBJECT (vaapidecbin,
        "Don't have VPP support but advanced deinterlacing selected");
    return FALSE;
  }

  GST_INFO_OBJECT (vaapidecbin, "enabling VPP");

  /* capsfilter to avoid negotiation with vaapidecode */
  caps = gst_caps_from_string
      ("video/x-raw(memory:VASurface), format=(string)NV12");
  if (!caps)
    goto error_cannot_set_caps;
  capsfilter = gst_element_factory_make ("capsfilter", NULL);
  g_object_set (capsfilter, "caps", caps, NULL);
  gst_caps_unref (caps);

  /* create the postproc */
  vaapidecbin->postproc = gst_element_factory_make ("vaapipostproc", NULL);
  if (!vaapidecbin->postproc)
    goto error_vpp_missing;
  g_object_set (G_OBJECT (vaapidecbin->postproc), "deinterlace-method",
      vaapidecbin->deinterlace_method, NULL);

  gst_bin_add_many (GST_BIN (vaapidecbin), capsfilter, vaapidecbin->postproc,
      NULL);

  if (!gst_element_link (capsfilter, vaapidecbin->postproc))
    goto error_sync_state;

  if (!gst_element_sync_state_with_parent (capsfilter))
    goto error_sync_state;
  if (!gst_element_sync_state_with_parent (vaapidecbin->postproc))
    goto error_sync_state;

  /* break source ghost pad target */
  bin_srcpad =
      gst_element_get_static_pad (GST_ELEMENT_CAST (vaapidecbin), "src");
  if (!gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (bin_srcpad), NULL))
    goto error_link_pad;

  /* link decoder and queue */
  queue_srcpad = gst_element_get_static_pad (vaapidecbin->queue, "src");
  capsfilter_sinkpad = gst_element_get_static_pad (capsfilter, "sink");
  res = (gst_pad_link (queue_srcpad, capsfilter_sinkpad) == GST_PAD_LINK_OK);
  gst_object_unref (capsfilter_sinkpad);
  gst_object_unref (queue_srcpad);
  if (!res)
    goto error_link_pad;

  /* set vpp source pad as source ghost pad target */
  vpp_srcpad = gst_element_get_static_pad (vaapidecbin->postproc, "src");
  res = gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (bin_srcpad), vpp_srcpad);
  gst_object_unref (vpp_srcpad);
  if (!res)
    goto error_link_pad;

  gst_object_unref (bin_srcpad);
  vaapidecbin->configured = TRUE;

  return TRUE;

  /* ERRORS */
error_cannot_set_caps:
  {
    GST_ELEMENT_ERROR (vaapidecbin, CORE, PAD,
        ("Failed to configure caps for VA Surfaces."), (NULL));
    return FALSE;
  }
error_vpp_missing:
  {
    post_missing_element_message (vaapidecbin, "vaapipostproc");
    return FALSE;
  }
error_sync_state:
  {
    GST_ELEMENT_ERROR (vaapidecbin, CORE, STATE_CHANGE,
        ("Failed to sync state of vaapipostproc"), (NULL));
    return FALSE;
  }
error_link_pad:
  {
    gst_object_unref (bin_srcpad);
    GST_ELEMENT_ERROR (vaapidecbin, CORE, PAD,
        ("Failed to configure the vaapidecodebin."), (NULL));
    return FALSE;
  }
}
static gboolean
gst_vaapi_decode_bin_configure (GstVaapiDecodeBin * vaapidecbin)
{
  gchar *missing_factory = NULL;
  GstPad *pad, *ghostpad;
  GstPadTemplate *tmpl;

  /* create the decoder */
  vaapidecbin->decoder =
      gst_element_factory_make ("vaapidecode", "vaapidecode");
  if (!vaapidecbin->decoder) {
    missing_factory = "vaapidecode";
    goto error_element_missing;
  }
  /* create the queue */
  vaapidecbin->queue = gst_element_factory_make ("queue", "queue");
  if (!vaapidecbin->queue) {
    missing_factory = "queue";
    goto error_element_missing;
  }

  g_object_set (G_OBJECT (vaapidecbin->queue),
      "max-size-bytes", vaapidecbin->max_size_bytes,
      "max-size-buffers", vaapidecbin->max_size_buffers,
      "max-size-time", vaapidecbin->max_size_time, NULL);

  gst_bin_add_many (GST_BIN (vaapidecbin), vaapidecbin->decoder,
      vaapidecbin->queue, NULL);

  if (!gst_element_link_many (vaapidecbin->decoder, vaapidecbin->queue, NULL))
    goto error_link_pad;

  /* create ghost pad sink */
  pad = gst_element_get_static_pad (GST_ELEMENT (vaapidecbin->decoder), "sink");
  ghostpad = gst_ghost_pad_new_from_template ("sink", pad,
      GST_PAD_PAD_TEMPLATE (pad));
  gst_object_unref (pad);
  if (!gst_element_add_pad (GST_ELEMENT (vaapidecbin), ghostpad))
    goto error_adding_pad;

  /* create ghost pad src */
  pad = gst_element_get_static_pad (GST_ELEMENT (vaapidecbin->queue), "src");
  tmpl = gst_static_pad_template_get (&gst_vaapi_decode_bin_src_factory);
  ghostpad = gst_ghost_pad_new_from_template ("src", pad, tmpl);
  gst_object_unref (pad);
  gst_object_unref (tmpl);
  if (!gst_element_add_pad (GST_ELEMENT (vaapidecbin), ghostpad))
    goto error_adding_pad;

  return TRUE;

error_element_missing:
  {
    post_missing_element_message (vaapidecbin, missing_factory);
    return FALSE;
  }
error_link_pad:
  {
    GST_ELEMENT_ERROR (vaapidecbin, CORE, PAD, (NULL),
        ("Failed to configure the vaapidecodebin."));
    return FALSE;
  }
error_adding_pad:
  {
    GST_ELEMENT_ERROR (vaapidecbin, CORE, PAD, (NULL),
        ("Failed to adding pads."));
    return FALSE;
  }
}
static gboolean
activate_vpp (GstVaapiDecodeBin * vaapidecbin)
{
  GstPad *queue_srcpad, *srcpad, *vpp_sinkpad, *vpp_srcpad;
  gboolean res;

  if (vaapidecbin->postproc)
    return TRUE;

  if (vaapidecbin->has_vpp != HAS_VPP_YES || vaapidecbin->disable_vpp)
    return TRUE;

  GST_DEBUG_OBJECT (vaapidecbin, "Enabling VPP");

  /* create the postproc */
  vaapidecbin->postproc =
      gst_element_factory_make ("vaapipostproc", "vaapipostproc");
  if (!vaapidecbin->postproc)
    goto error_element_missing;

  g_object_set (G_OBJECT (vaapidecbin->postproc), "deinterlace-method",
      vaapidecbin->deinterlace_method, NULL);

  gst_bin_add (GST_BIN (vaapidecbin), vaapidecbin->postproc);

  if (!gst_element_sync_state_with_parent (vaapidecbin->postproc))
    goto error_sync_state;

  srcpad = gst_element_get_static_pad (GST_ELEMENT_CAST (vaapidecbin), "src");
  if (!gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (srcpad), NULL))
    goto error_link_pad;

  queue_srcpad = gst_element_get_static_pad (vaapidecbin->queue, "src");
  vpp_sinkpad = gst_element_get_static_pad (vaapidecbin->postproc, "sink");
  res = (gst_pad_link (queue_srcpad, vpp_sinkpad) == GST_PAD_LINK_OK);
  gst_object_unref (vpp_sinkpad);
  gst_object_unref (queue_srcpad);
  if (!res)
    goto error_link_pad;

  vpp_srcpad = gst_element_get_static_pad (vaapidecbin->postproc, "src");
  res = gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (srcpad), vpp_srcpad);
  gst_object_unref (vpp_srcpad);
  if (!res)
    goto error_link_pad;

  gst_object_unref (srcpad);

  return TRUE;

error_element_missing:
  {
    post_missing_element_message (vaapidecbin, "vaapipostproc");
    return FALSE;
  }
error_sync_state:
  {
    GST_ERROR_OBJECT (vaapidecbin, "Failed to sync VPP state");
    return FALSE;
  }
error_link_pad:
  {
    gst_object_unref (srcpad);
    GST_ERROR_OBJECT (vaapidecbin, "Failed to link the child elements");
    return FALSE;
  }
}