static GstPadProbeReturn
switch_to_smpte_cb (GstPad * sink, gboolean blocked,
    GESTrackVideoTransition * transition)
{
  GstElement *smptealpha = gst_element_factory_make ("smptealpha", NULL);
  GstElement *smptealphab = gst_element_factory_make ("smptealpha", NULL);
  GESTrackVideoTransitionPrivate *priv = transition->priv;

  if (priv->pending_type == GES_VIDEO_STANDARD_TRANSITION_TYPE_CROSSFADE)
    goto beach;

  GST_INFO ("Bin %p switching from crossfade to smpte", priv->topbin);

  add_smpte_to_bin (priv->sinka, smptealpha, priv);
  add_smpte_to_bin (priv->sinkb, smptealphab, priv);

  if (priv->pending_border_value != -1) {
    g_object_set (smptealphab, "border", priv->pending_border_value, NULL);
    priv->pending_border_value = -1;
  }

  if (priv->pending_inverted) {
    g_object_set (smptealphab, "invert", priv->pending_inverted, NULL);
    priv->pending_inverted = FALSE;
  }

  replace_mixer (priv);

  priv->start_value = 1.0;
  priv->end_value = 0.0;

  set_interpolation (GST_OBJECT (smptealphab), priv, (gchar *) "position");
  ges_track_video_transition_duration_changed (GES_TRACK_OBJECT (transition),
      priv->dur);


  priv->sinka = (GstPad *) link_element_to_mixer (smptealpha, priv->mixer);
  priv->sinkb = (GstPad *) link_element_to_mixer (smptealphab, priv->mixer);

  priv->smpte = smptealphab;

  priv->type = priv->pending_type;

  GST_INFO ("Bin %p switched from crossfade to smpte", priv->topbin);

beach:
  priv->pending_type = GES_VIDEO_STANDARD_TRANSITION_TYPE_NONE;

  return GST_PAD_PROBE_REMOVE;
}
static GstPadProbeReturn
switch_to_crossfade_cb (GstPad * sink, gboolean blocked,
    GESTrackVideoTransition * transition)
{
  GstElement *peera;
  GstElement *peerb;
  GESTrackVideoTransitionPrivate *priv = transition->priv;

  GST_INFO ("Bin %p switching from smpte to crossfade", priv->topbin);

  if (priv->pending_type != GES_VIDEO_STANDARD_TRANSITION_TYPE_CROSSFADE)
    goto beach;

  peera = remove_smpte_from_bin (priv, priv->sinka);
  peerb = remove_smpte_from_bin (priv, priv->sinkb);
  if (!peera || !peerb)
    goto beach;

  replace_mixer (priv);

  priv->sinka = (GstPad *) link_element_to_mixer (peera, priv->mixer);
  priv->sinkb = (GstPad *) link_element_to_mixer (peerb, priv->mixer);

  priv->start_value = 0.0;
  priv->end_value = 1.0;
  set_interpolation (GST_OBJECT (priv->sinkb), priv, (gchar *) "alpha");
  ges_track_video_transition_duration_changed (GES_TRACK_OBJECT (transition),
      priv->dur);

  priv->smpte = NULL;

  gst_object_unref (peera);
  gst_object_unref (peerb);

  priv->type = priv->pending_type;

  GST_INFO ("Bin %p switched from smpte to crossfade", priv->topbin);

beach:
  priv->pending_type = GES_VIDEO_STANDARD_TRANSITION_TYPE_NONE;

  return GST_PAD_PROBE_REMOVE;
}
static GstElement *
ges_track_video_transition_create_element (GESTrackObject * object)
{
  GstElement *topbin, *iconva, *iconvb, *scalea, *scaleb, *capsfilt, *oconv;
  GstObject *target = NULL;
  const gchar *propname = NULL;
  GstElement *mixer = NULL;
  GstPad *sinka_target, *sinkb_target, *src_target, *sinka, *sinkb, *src,
      *srca_pad;
  GESTrackVideoTransition *self;
  GESTrackVideoTransitionPrivate *priv;

  self = GES_TRACK_VIDEO_TRANSITION (object);
  priv = self->priv;

  GST_LOG ("creating a video bin");

  topbin = gst_bin_new ("transition-bin");
  iconva = gst_element_factory_make ("videoconvert", "tr-csp-a");
  iconvb = gst_element_factory_make ("videoconvert", "tr-csp-b");
  scalea = gst_element_factory_make ("videoscale", "vs-a");
  scaleb = gst_element_factory_make ("videoscale", "vs-b");
  capsfilt = gst_element_factory_make ("capsfilter", "capsfilt");
  oconv = gst_element_factory_make ("videoconvert", "tr-csp-output");

  gst_bin_add_many (GST_BIN (topbin), iconva, iconvb, scalea, scaleb, capsfilt,
      oconv, NULL);

  mixer = gst_element_factory_make ("videomixer", NULL);
  g_assert (mixer);
  g_object_set (G_OBJECT (mixer), "background", 1, NULL);
  gst_bin_add (GST_BIN (topbin), mixer);

  if (priv->pending_type != GES_VIDEO_STANDARD_TRANSITION_TYPE_CROSSFADE) {
    priv->sinka =
        (GstPad *) link_element_to_mixer_with_smpte (GST_BIN (topbin), iconva,
        mixer, priv->pending_type, NULL);
    priv->sinkb =
        (GstPad *) link_element_to_mixer_with_smpte (GST_BIN (topbin), iconvb,
        mixer, priv->pending_type, &priv->smpte);
    target = GST_OBJECT (priv->smpte);
    propname = "position";
    priv->start_value = 1.0;
    priv->end_value = 0.0;
  } else {
    gst_element_link_pads_full (iconva, "src", scalea, "sink",
        GST_PAD_LINK_CHECK_NOTHING);
    gst_element_link_pads_full (iconvb, "src", scaleb, "sink",
        GST_PAD_LINK_CHECK_NOTHING);
    gst_element_link_pads_full (scaleb, "src", capsfilt, "sink",
        GST_PAD_LINK_CHECK_NOTHING);

    priv->sinka = (GstPad *) link_element_to_mixer (scalea, mixer);
    priv->sinkb = (GstPad *) link_element_to_mixer (capsfilt, mixer);
    target = GST_OBJECT (priv->sinkb);
    propname = "alpha";
    priv->start_value = 0.0;
    priv->end_value = 1.0;
  }

  priv->mixer = gst_object_ref (mixer);

  fast_element_link (mixer, oconv);

  sinka_target = gst_element_get_static_pad (iconva, "sink");
  sinkb_target = gst_element_get_static_pad (iconvb, "sink");
  src_target = gst_element_get_static_pad (oconv, "src");

  sinka = gst_ghost_pad_new ("sinka", sinka_target);
  sinkb = gst_ghost_pad_new ("sinkb", sinkb_target);
  src = gst_ghost_pad_new ("src", src_target);

  gst_element_add_pad (topbin, src);
  gst_element_add_pad (topbin, sinka);
  gst_element_add_pad (topbin, sinkb);

  srca_pad = gst_element_get_static_pad (scalea, "src");
  g_signal_connect (srca_pad, "notify::caps", G_CALLBACK (on_caps_set),
      (GstElement *) capsfilt);

  gst_object_unref (sinka_target);
  gst_object_unref (sinkb_target);
  gst_object_unref (src_target);
  gst_object_unref (srca_pad);

  /* set up interpolation */

  set_interpolation (target, priv, propname);

  priv->topbin = topbin;
  priv->type = priv->pending_type;

  return topbin;
}
static GstElement *
ges_track_video_transition_create_element (GESTrackObject * object)
{
  GstElement *topbin, *iconva, *iconvb, *oconv;
  GObject *target = NULL;
  const gchar *propname = NULL;
  GstElement *mixer = NULL;
  GstPad *sinka_target, *sinkb_target, *src_target, *sinka, *sinkb, *src;
  GstController *controller;
  GstInterpolationControlSource *control_source;
  GESTrackVideoTransition *self;
  GESTrackVideoTransitionPrivate *priv;

  self = GES_TRACK_VIDEO_TRANSITION (object);
  priv = self->priv;

  GST_LOG ("creating a video bin");

  topbin = gst_bin_new ("transition-bin");
  iconva = gst_element_factory_make ("ffmpegcolorspace", "tr-csp-a");
  iconvb = gst_element_factory_make ("ffmpegcolorspace", "tr-csp-b");
  oconv = gst_element_factory_make ("ffmpegcolorspace", "tr-csp-output");

  gst_bin_add_many (GST_BIN (topbin), iconva, iconvb, oconv, NULL);
  /* Prefer videomixer2 to videomixer */
  mixer = gst_element_factory_make ("videomixer2", NULL);
  if (mixer == NULL)
    mixer = gst_element_factory_make ("videomixer", NULL);
  g_object_set (G_OBJECT (mixer), "background", 1, NULL);
  gst_bin_add (GST_BIN (topbin), mixer);

  if (priv->type != GES_VIDEO_STANDARD_TRANSITION_TYPE_CROSSFADE) {
    priv->sinka =
        (GstPad *) link_element_to_mixer_with_smpte (GST_BIN (topbin), iconva,
        mixer, priv->type, NULL);
    priv->sinkb =
        (GstPad *) link_element_to_mixer_with_smpte (GST_BIN (topbin), iconvb,
        mixer, priv->type, &priv->smpte);
    target = (GObject *) priv->smpte;
    propname = "position";
    priv->start_value = 1.0;
    priv->end_value = 0.0;
  } else {
    priv->sinka = (GstPad *) link_element_to_mixer (iconva, mixer);
    priv->sinkb = (GstPad *) link_element_to_mixer (iconvb, mixer);
    target = (GObject *) priv->sinkb;
    propname = "alpha";
    priv->start_value = 0.0;
    priv->end_value = 1.0;
  }

  priv->mixer = gst_object_ref (mixer);

  fast_element_link (mixer, oconv);

  sinka_target = gst_element_get_static_pad (iconva, "sink");
  sinkb_target = gst_element_get_static_pad (iconvb, "sink");
  src_target = gst_element_get_static_pad (oconv, "src");

  sinka = gst_ghost_pad_new ("sinka", sinka_target);
  sinkb = gst_ghost_pad_new ("sinkb", sinkb_target);
  src = gst_ghost_pad_new ("src", src_target);

  gst_element_add_pad (topbin, src);
  gst_element_add_pad (topbin, sinka);
  gst_element_add_pad (topbin, sinkb);

  gst_object_unref (sinka_target);
  gst_object_unref (sinkb_target);
  gst_object_unref (src_target);

  /* set up interpolation */

  g_object_set (target, propname, (gfloat) 0.0, NULL);

  controller = gst_object_control_properties (target, propname, NULL);

  control_source = gst_interpolation_control_source_new ();
  gst_controller_set_control_source (controller,
      propname, GST_CONTROL_SOURCE (control_source));
  gst_interpolation_control_source_set_interpolation_mode (control_source,
      GST_INTERPOLATE_LINEAR);

  priv->controller = controller;
  priv->control_source = control_source;

  return topbin;
}