diff --git a/docs/manual/advanced-buffering.xml b/docs/manual/advanced-buffering.xml
index 91fcb0a9dd..6d1660fafe 100644
--- a/docs/manual/advanced-buffering.xml
+++ b/docs/manual/advanced-buffering.xml
@@ -58,12 +58,12 @@
case GST_MESSAGE_BUFFERING:{
gint percent;
- gst_message_parse_buffering (message, &percent);
-
/* no state management needed for live pipelines */
if (is_live)
break;
+ gst_message_parse_buffering (message, &percent);
+
if (percent == 100) {
/* a 100% message means buffering is done */
buffering = FALSE;
@@ -256,14 +256,195 @@
Buffering strategies
- WRITEME
+ What follows are some ideas for implementing different buffering
+ strategies based on the buffering messages and buffering query.
-
- The application can use the BUFFERING query to get the estimated
- download time and match this time to the current/remaining playback time
- to control when playback should start to have a non-interrupted playback
- experience.
-
-
+
+ No-rebuffer strategy
+
+ We would like to buffer enough data in the pipeline so that playback
+ continues without interruptions. What we need to know to implement
+ this is know the total remaining playback time in the file and the
+ total remaining download time. If the buffering time is less than the
+ playback time, we can start playback without interruptions.
+
+
+ We have all this information available with the DURATION, POSITION and
+ BUFFERING queries. We need to periodically execute the buffering query
+ to get the current buffering status. We also need to have a large
+ enough buffer to hold the complete file, worst case. It is best to
+ use this buffering strategy with download buffering (see
+ ).
+
+
+ This is what the code would look like:
+
+
+
+
+
+GstState target_state;
+static gboolean is_live;
+static gboolean is_buffering;
+
+static gboolean
+buffer_timeout (gpointer data)
+{
+ GstElement *pipeline = data;
+ GstQuery *query;
+ gboolean busy;
+ gint percent;
+ gint64 estimated_total;
+ gint64 position, duration;
+ 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);
+ gst_query_parse_buffering_range (query, NULL, NULL, NULL, &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
+ ", percent %d", play_left, estimated_total, 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 = 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 = 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);
+
+ /* make sure we have a URI */
+ if (argc != 2) {
+ g_print ("Usage: %s <URI>\n", argv[0]);
+ return -1;
+ }
+
+ /* set up */
+ pipeline = gst_element_factory_make ("playbin", "pipeline");
+ g_object_set (G_OBJECT (pipeline), "uri", argv[1], 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;
+}
+]]>
+
+
+
+ See how we set the pipeline to the PAUSED state first. We will receive
+ buffering messages during the preroll state when buffering is needed.
+ When we are prerolled (on_message_async_done) we see if buffering is
+ going on, if not, we start playback. If buffering was going on, we start
+ a timeout to poll the buffering state. If the estimated time to download
+ is less than the remaining playback time, we start playback.
+
+
+
diff --git a/tests/examples/manual/.gitignore b/tests/examples/manual/.gitignore
index 4cf9593d4f..1f16c7d065 100644
--- a/tests/examples/manual/.gitignore
+++ b/tests/examples/manual/.gitignore
@@ -22,12 +22,15 @@ queue
threads
bin
decodebin
+dynamic
elementcreate
elementfactory
elementlink
ghostpad
pad
playbin
+playsink
+norebuffer
probe
query
fakesrc
diff --git a/tests/examples/manual/Makefile.am b/tests/examples/manual/Makefile.am
index 396c100368..dec8a3fc1c 100644
--- a/tests/examples/manual/Makefile.am
+++ b/tests/examples/manual/Makefile.am
@@ -41,6 +41,7 @@ EXAMPLES = \
dynformat \
effectswitch \
testrtpool \
+ norebuffer \
playbin \
decodebin \
playsink
@@ -61,6 +62,7 @@ BUILT_SOURCES = \
dynformat.c \
effectswitch.c \
testrtpool.c \
+ norebuffer.c \
playbin.c decodebin.c \
playsink.c
@@ -112,6 +114,9 @@ dynformat.c: $(top_srcdir)/docs/manual/advanced-dataaccess.xml
effectswitch.c: $(top_srcdir)/docs/manual/advanced-dataaccess.xml
$(PERL_PATH) $(srcdir)/extract.pl $@ $<
+norebuffer.c: $(top_srcdir)/docs/manual/advanced-buffering.xml
+ $(PERL_PATH) $(srcdir)/extract.pl $@ $<
+
playbin.c decodebin.c playsink.c: $(top_srcdir)/docs/manual/highlevel-playback.xml
$(PERL_PATH) $(srcdir)/extract.pl $@ $<