//based on http://gstreamer.freedesktop.org/data/doc/gstreamer/head/manual/html/section-buffering-strategies.html

#include <gst/gst.h>

GstState target_state;
static gboolean is_live;
static gboolean is_buffering;

static gboolean
buffer_timeout(gpointer data)
{
	GstElement *pipeline = (GstElement*)data;
	GstQuery *query;
	gboolean busy;
	gint percent;
	gint64 estimated_total;
	gint64 position, duration;
	gint64 start, stop;
	guint64 play_left;

	query = gst_query_new_buffering(GST_FORMAT_TIME);

	if (!gst_element_query(pipeline, query))
		return TRUE;

	gst_query_parse_buffering_percent(query, &busy, &percent);
	GstFormat format;
	gst_query_parse_buffering_range(query, &format, &start, &stop, &estimated_total);

	if (estimated_total == -1)
		estimated_total = 0;

	/* calculate the remaining playback time */
	if (!gst_element_query_position(pipeline, GST_FORMAT_TIME, &position))
		position = -1;
	if (!gst_element_query_duration(pipeline, GST_FORMAT_TIME, &duration))
		duration = -1;

	if (duration != -1 && position != -1)
		play_left = GST_TIME_AS_MSECONDS(duration - position);
	else
		play_left = 0;

	g_message("play_left %" G_GUINT64_FORMAT", estimated_total %" G_GUINT64_FORMAT ", start %" G_GUINT64_FORMAT ", stop %" G_GUINT64_FORMAT
		", percent %d", play_left, estimated_total, start, stop, percent);

	/* we are buffering or the estimated download time is bigger than the
	* remaining playback time. We keep buffering. */
	is_buffering = (busy || estimated_total * 1.1 > play_left);

	if (!is_buffering)
		gst_element_set_state(pipeline, target_state);

	return is_buffering;
}

static void
on_message_buffering(GstBus *bus, GstMessage *message, gpointer user_data)
{
	GstElement *pipeline = (GstElement*)user_data;
	gint percent;

	/* no state management needed for live pipelines */
	if (is_live)
		return;

	gst_message_parse_buffering(message, &percent);

	if (percent < 100) {
		/* buffering busy */
		if (is_buffering == FALSE) {
			is_buffering = TRUE;
			if (target_state == GST_STATE_PLAYING) {
				/* we were not buffering but PLAYING, PAUSE  the pipeline. */
				gst_element_set_state(pipeline, GST_STATE_PAUSED);
			}
		}
	}
}

static void
on_message_async_done(GstBus *bus, GstMessage *message, gpointer user_data)
{
	GstElement *pipeline = (GstElement*)user_data;

	if (is_buffering == FALSE)
		gst_element_set_state(pipeline, target_state);
	else
		g_timeout_add(500, buffer_timeout, pipeline);
}

gint
main(gint   argc,
gchar *argv[])
{
	GstElement *pipeline;
	GMainLoop *loop;
	GstBus *bus;
	GstStateChangeReturn ret;

	/* init GStreamer */
	gst_init(&argc, &argv);
	loop = g_main_loop_new(NULL, FALSE);

	/* set up */
	pipeline = gst_element_factory_make("playbin", "play");
	g_object_set(G_OBJECT(pipeline), "uri", "http://media.w3.org/2010/05/sintel/trailer.mp4", NULL);
	g_object_set(G_OBJECT(pipeline), "flags", 0x697, NULL);

	bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));
	gst_bus_add_signal_watch(bus);

	g_signal_connect(bus, "message::buffering",
		(GCallback)on_message_buffering, pipeline);
	g_signal_connect(bus, "message::async-done",
		(GCallback)on_message_async_done, pipeline);
	gst_object_unref(bus);

	is_buffering = FALSE;
	target_state = GST_STATE_PLAYING;
	ret = gst_element_set_state(pipeline, GST_STATE_PAUSED);

	switch (ret) {
	case GST_STATE_CHANGE_SUCCESS:
		is_live = FALSE;
		break;

	case GST_STATE_CHANGE_FAILURE:
		g_warning("failed to PAUSE");
		return -1;

	case GST_STATE_CHANGE_NO_PREROLL:
		is_live = TRUE;
		break;

	default:
		break;
	}

	/* now run */
	g_main_loop_run(loop);

	/* also clean up */
	gst_element_set_state(pipeline, GST_STATE_NULL);
	gst_object_unref(GST_OBJECT(pipeline));
	g_main_loop_unref(loop);

	return 0;
}