static void
ges_track_video_transition_duration_changed (GESTrackObject * object,
    guint64 duration)
{
  GstElement *gnlobj = ges_track_object_get_gnlobject (object);
  GESTrackVideoTransition *self = GES_TRACK_VIDEO_TRANSITION (object);
  GESTrackVideoTransitionPrivate *priv = self->priv;
  GstTimedValueControlSource *ts;

  GST_LOG ("updating controller");

  if (G_UNLIKELY (!gnlobj || !priv->control_source))
    return;

  ts = GST_TIMED_VALUE_CONTROL_SOURCE (priv->control_source);

  GST_INFO ("duration: %" G_GUINT64_FORMAT, duration);
  GST_LOG ("setting values on controller");

  gst_timed_value_control_source_unset_all (ts);
  gst_timed_value_control_source_set (ts, 0, priv->start_value);
  gst_timed_value_control_source_set (ts, duration, priv->end_value);

  priv->dur = duration;
  GST_LOG ("done updating controller");
}
static void
set_program (GstObject * elem, GstStructure * prog)
{
  const GstStructure *s;
  GstControlSource *cs;
  GstClockTime ts, dur;
  gdouble v;
  const GValue *frame;
  GHashTable *css;
  gint i, j;
  const gchar *name;

  css = g_hash_table_new (g_str_hash, g_str_equal);

  ts = 0;
  dur = gst_util_uint64_scale_int (GST_SECOND, 1, 15);

  /* loop over each image in prog */
  for (i = 0; i < gst_structure_n_fields (prog); i++) {
    GST_DEBUG ("ctrl on %" GST_TIME_FORMAT, GST_TIME_ARGS (ts));

    frame =
        gst_structure_get_value (prog, gst_structure_nth_field_name (prog, i));
    s = gst_value_get_structure (frame);
    for (j = 0; j < gst_structure_n_fields (s); j++) {
      name = gst_structure_nth_field_name (s, j);
      cs = g_hash_table_lookup (css, name);
      if (!cs) {
        cs = gst_interpolation_control_source_new ();
        gst_object_add_control_binding (elem,
            gst_direct_control_binding_new (elem, name, cs));
        g_object_set (cs, "mode", GST_INTERPOLATION_MODE_NONE, NULL);
        g_hash_table_insert (css, (gpointer) name, cs);
        gst_object_unref (cs);
      }
      gst_structure_get_double (s, name, &v);
      gst_timed_value_control_source_set ((GstTimedValueControlSource *) cs, ts,
          v);
      GST_DEBUG ("  %s = %lf", name, v);
    }
    ts += dur;
  }

  g_hash_table_unref (css);
}
int
main (int argc, char **argv)
{
  GMainLoop *loop;
  gint i;

  GstElement *audiotestsrc;
  GstElement *audioconvert1, *audioconvert2;
  GstElement *pitch;
  GstElement *sink;
  GstElement *pipeline;
  GstControlSource *cs;
  GstTimedValueControlSource *tvcs;

  if (argc != 2) {
    g_printerr ("Usage: %s <audiosink>\n", argv[0]);
    return 1;
  }

  /* initialize GStreamer */
  gst_init (&argc, &argv);

  loop = g_main_loop_new (NULL, FALSE);

  pipeline = gst_pipeline_new ("audio-player");
  audiotestsrc = gst_element_factory_make ("audiotestsrc", "audiotestsrc");
  g_assert (audiotestsrc != NULL);
  audioconvert1 = gst_element_factory_make ("audioconvert", "audioconvert1");
  g_assert (audioconvert1 != NULL);
  audioconvert2 = gst_element_factory_make ("audioconvert", "audioconvert2");
  g_assert (audioconvert2 != NULL);
  pitch = gst_element_factory_make ("pitch", "pitch");
  g_assert (pitch != NULL);
  sink = gst_element_factory_make (argv[1], "sink");
  g_assert (sink != NULL);

  gst_bin_add_many (GST_BIN (pipeline),
      audiotestsrc, audioconvert1, pitch, audioconvert2, sink, NULL);
  gst_element_link_many (audiotestsrc, audioconvert1, pitch, audioconvert2,
      sink, NULL);

  /* set up a controller */
  cs = gst_interpolation_control_source_new ();
  g_object_set (cs, "mode", GST_INTERPOLATION_MODE_LINEAR, NULL);

  gst_object_add_control_binding (pitch,
      gst_direct_control_binding_new (pitch, "pitch", cs));
  tvcs = (GstTimedValueControlSource *) cs;

  for (i = 0; i < 100; ++i) {
    if (i % 2)
      gst_timed_value_control_source_set (tvcs, i * GST_SECOND, 0.5);
    else
      gst_timed_value_control_source_set (tvcs, i * GST_SECOND, 1.5);
  }

  gst_element_set_state (pipeline, GST_STATE_PLAYING);
  g_print ("Running\n");
  g_main_loop_run (loop);

  /* clean up nicely */
  gst_object_unref (cs);
  g_print ("Returned, stopping playback\n");
  gst_element_set_state (pipeline, GST_STATE_NULL);
  g_print ("Deleting pipeline\n");
  gst_object_unref (GST_OBJECT (pipeline));

  return 0;
}
static void
_update_control_bindings (GESTimelineElement * element, GstClockTime inpoint,
    GstClockTime duration)
{
  GParamSpec **specs;
  guint n, n_specs;
  GstControlBinding *binding;
  GstTimedValueControlSource *source;
  GESTrackElement *self = GES_TRACK_ELEMENT (element);

  specs = ges_track_element_list_children_properties (self, &n_specs);

  for (n = 0; n < n_specs; ++n) {
    GList *values, *tmp;
    GstTimedValue *last, *first, *prev = NULL, *next = NULL;
    gfloat value_at_pos;

    binding = ges_track_element_get_control_binding (self, specs[n]->name);

    if (!binding)
      continue;

    g_object_get (binding, "control_source", &source, NULL);

    if (duration == 0) {
      gst_timed_value_control_source_unset_all (GST_TIMED_VALUE_CONTROL_SOURCE
          (source));
      continue;
    }

    values =
        gst_timed_value_control_source_get_all (GST_TIMED_VALUE_CONTROL_SOURCE
        (source));

    if (g_list_length (values) == 0)
      continue;

    first = values->data;

    for (tmp = values->next; tmp; tmp = tmp->next) {
      next = tmp->data;

      if (next->timestamp > inpoint)
        break;
    }

    value_at_pos = interpolate_values_for_position (first, next, inpoint);
    gst_timed_value_control_source_unset (source, first->timestamp);
    gst_timed_value_control_source_set (source, inpoint, value_at_pos);

    values =
        gst_timed_value_control_source_get_all (GST_TIMED_VALUE_CONTROL_SOURCE
        (source));

    if (duration != GST_CLOCK_TIME_NONE) {
      last = g_list_last (values)->data;

      for (tmp = g_list_last (values)->prev; tmp; tmp = tmp->prev) {
        prev = tmp->data;

        if (prev->timestamp < duration + inpoint)
          break;
      }

      value_at_pos =
          interpolate_values_for_position (prev, last, duration + inpoint);

      gst_timed_value_control_source_unset (source, last->timestamp);
      gst_timed_value_control_source_set (source, duration + inpoint,
          value_at_pos);
      values =
          gst_timed_value_control_source_get_all (GST_TIMED_VALUE_CONTROL_SOURCE
          (source));
    }

    for (tmp = values; tmp; tmp = tmp->next) {
      GstTimedValue *value = tmp->data;
      if (value->timestamp < inpoint)
        gst_timed_value_control_source_unset (source, value->timestamp);
      else if (duration != GST_CLOCK_TIME_NONE
          && value->timestamp > duration + inpoint)
        gst_timed_value_control_source_unset (source, value->timestamp);
    }
  }

  g_free (specs);
}