mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 04:01:08 +00:00
manual: add example of no-rebuffer buffering strategy
This commit is contained in:
parent
0447c72eb0
commit
b6ccd95b22
3 changed files with 199 additions and 10 deletions
|
@ -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 (&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;
|
||||
}
|
||||
]]>
|
||||
<!-- 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>
|
||||
|
|
3
tests/examples/manual/.gitignore
vendored
3
tests/examples/manual/.gitignore
vendored
|
@ -22,12 +22,15 @@ queue
|
|||
threads
|
||||
bin
|
||||
decodebin
|
||||
dynamic
|
||||
elementcreate
|
||||
elementfactory
|
||||
elementlink
|
||||
ghostpad
|
||||
pad
|
||||
playbin
|
||||
playsink
|
||||
norebuffer
|
||||
probe
|
||||
query
|
||||
fakesrc
|
||||
|
|
|
@ -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 $@ $<
|
||||
|
||||
|
|
Loading…
Reference in a new issue