/* gcc -o do_seek do_seek.c `pkg-config --cflags --libs gstreamer-1.0` -Wall -g -O2
 */
#include <gst/gst.h>

static void
wait_preroll (GstElement * playbin)
{
  GstMessage *msg;
  GstBus *bus;

  bus = gst_element_get_bus (playbin);

  msg = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE,
      GST_MESSAGE_ERROR | GST_MESSAGE_ASYNC_DONE);

  if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR)
    g_error ("Error!: %s", gst_structure_to_string (gst_message_get_structure (msg)));

  gst_message_unref (msg);
  gst_object_unref (bus);
}

static void
seek (GstElement * playbin, gdouble seconds)
{
  GstStateChangeReturn sret;
  GstClockTime pos = (gdouble) GST_SECOND * seconds;
  GstSeekFlags flags;
  gdouble wait_time = 10.0;

  flags = GST_SEEK_FLAG_KEY_UNIT | GST_SEEK_FLAG_FLUSH;
  //flags = GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_FLUSH;

  g_print ("Seeking to %.1f seconds..\n", seconds);
  gst_element_seek (playbin, 1.0, GST_FORMAT_TIME, flags,
      GST_SEEK_TYPE_SET, pos, GST_SEEK_TYPE_NONE, -1);

  sret = gst_element_get_state (playbin, NULL, NULL, wait_time * GST_SECOND);
  if (sret == GST_STATE_CHANGE_ASYNC)
    g_warning ("still not prerolled after 2 seconds of waiting after seek!");
}

int
main (int argc, char ** argv)
{
  GTimer *t = g_timer_new ();
  GstElement *playbin;
  gdouble min, max, avg, duration;
  gint64 dur = -1;
  gchar *uri;
  int i;

  gst_init (&argc, &argv);

  if (argc < 2)
    g_error ("Usage: %s (FILE|URI)\n", argv[0]);

  if (gst_uri_is_valid (argv[1])) {
    uri = g_strdup (argv[1]);
  } else {
    uri = gst_filename_to_uri (argv[1], NULL);
    if (uri == NULL)
      g_error ("Could not convert filename '%s' to URI\n", argv[1]);
  }

  playbin = gst_element_factory_make ("playbin", NULL);
  g_print ("URI: %s\n", uri);
  g_object_set (playbin, "uri", uri, NULL);
  g_free (uri);

  g_timer_reset (t);
  g_print ("Prerolling...\n");
  gst_element_set_state (playbin, GST_STATE_PAUSED);
  wait_preroll (playbin);
  g_print ("Prerolled. (%.1fs)\n", g_timer_elapsed (t, NULL));

  if (!gst_element_query_duration (playbin, GST_FORMAT_TIME, &dur))
    g_error ("Couldn't query duration.");

  duration = dur * 1.0 / GST_SECOND;
  g_print ("Duration: %.1fs\n", duration);

#define LOOPS 100
  min = max = avg = 0.0;
  for (i = 0; i < LOOPS; ++i) {
    gdouble seek_pos, elapsed;

    seek_pos = g_random_double_range (0, duration);
    g_timer_reset (t);
    seek (playbin, seek_pos);
    elapsed = g_timer_elapsed (t, NULL);
    avg += elapsed;
    if (min == 0.0 || elapsed < min)
      min = elapsed;
    if (elapsed > max)
      max = elapsed;
    g_print ("Seek #%3u took %.0fms\n", i, elapsed * 1000.0);
  }
  avg /= LOOPS;
  g_print ("%u seeks, min: %.0fms, max %.0fms, avg %.0fms\n", LOOPS,
      min * 1000.0, max * 1000.0, avg * 1000.0);

  gst_element_set_state (playbin, GST_STATE_NULL);
  gst_object_unref (playbin);

  return 0;
}
