manual: add example of no-rebuffer buffering strategy

This commit is contained in:
Wim Taymans 2012-10-11 17:10:17 +02:00
parent 0447c72eb0
commit b6ccd95b22
3 changed files with 199 additions and 10 deletions

View file

@ -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 @@
<sect1 id="section-buffering-strategies">
<title>Buffering strategies </title>
<para>
WRITEME
What follows are some ideas for implementing different buffering
strategies based on the buffering messages and buffering query.
</para>
<para>
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.
</para>
</sect1>
<sect2 id="section-buffering-norebuffer">
<title>No-rebuffer strategy </title>
<para>
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.
</para>
<para>
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
<xref linkend="section-buffering-download"/>).
</para>
<para>
This is what the code would look like:
</para>
<programlisting>
<!-- example-begin norebuffer.c a -->
<![CDATA[
#include <gst/gst.h>
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 (&amp;argc, &amp;argv);
loop = g_main_loop_new (NULL, FALSE);
/* make sure we have a URI */
if (argc != 2) {
g_print ("Usage: %s &lt;URI&gt;\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;
}
]]>
<!-- example-end norebuffer.c a -->
</programlisting>
<para>
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.
</para>
</sect2>
</sect1>
</chapter>

View file

@ -22,12 +22,15 @@ queue
threads
bin
decodebin
dynamic
elementcreate
elementfactory
elementlink
ghostpad
pad
playbin
playsink
norebuffer
probe
query
fakesrc

View file

@ -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 $@ $<