mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-06-07 16:08:51 +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:{
|
case GST_MESSAGE_BUFFERING:{
|
||||||
gint percent;
|
gint percent;
|
||||||
|
|
||||||
gst_message_parse_buffering (message, &percent);
|
|
||||||
|
|
||||||
/* no state management needed for live pipelines */
|
/* no state management needed for live pipelines */
|
||||||
if (is_live)
|
if (is_live)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
gst_message_parse_buffering (message, &percent);
|
||||||
|
|
||||||
if (percent == 100) {
|
if (percent == 100) {
|
||||||
/* a 100% message means buffering is done */
|
/* a 100% message means buffering is done */
|
||||||
buffering = FALSE;
|
buffering = FALSE;
|
||||||
|
@ -256,14 +256,195 @@
|
||||||
<sect1 id="section-buffering-strategies">
|
<sect1 id="section-buffering-strategies">
|
||||||
<title>Buffering strategies </title>
|
<title>Buffering strategies </title>
|
||||||
<para>
|
<para>
|
||||||
WRITEME
|
What follows are some ideas for implementing different buffering
|
||||||
|
strategies based on the buffering messages and buffering query.
|
||||||
</para>
|
</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>
|
</chapter>
|
||||||
|
|
3
tests/examples/manual/.gitignore
vendored
3
tests/examples/manual/.gitignore
vendored
|
@ -22,12 +22,15 @@ queue
|
||||||
threads
|
threads
|
||||||
bin
|
bin
|
||||||
decodebin
|
decodebin
|
||||||
|
dynamic
|
||||||
elementcreate
|
elementcreate
|
||||||
elementfactory
|
elementfactory
|
||||||
elementlink
|
elementlink
|
||||||
ghostpad
|
ghostpad
|
||||||
pad
|
pad
|
||||||
playbin
|
playbin
|
||||||
|
playsink
|
||||||
|
norebuffer
|
||||||
probe
|
probe
|
||||||
query
|
query
|
||||||
fakesrc
|
fakesrc
|
||||||
|
|
|
@ -41,6 +41,7 @@ EXAMPLES = \
|
||||||
dynformat \
|
dynformat \
|
||||||
effectswitch \
|
effectswitch \
|
||||||
testrtpool \
|
testrtpool \
|
||||||
|
norebuffer \
|
||||||
playbin \
|
playbin \
|
||||||
decodebin \
|
decodebin \
|
||||||
playsink
|
playsink
|
||||||
|
@ -61,6 +62,7 @@ BUILT_SOURCES = \
|
||||||
dynformat.c \
|
dynformat.c \
|
||||||
effectswitch.c \
|
effectswitch.c \
|
||||||
testrtpool.c \
|
testrtpool.c \
|
||||||
|
norebuffer.c \
|
||||||
playbin.c decodebin.c \
|
playbin.c decodebin.c \
|
||||||
playsink.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
|
effectswitch.c: $(top_srcdir)/docs/manual/advanced-dataaccess.xml
|
||||||
$(PERL_PATH) $(srcdir)/extract.pl $@ $<
|
$(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
|
playbin.c decodebin.c playsink.c: $(top_srcdir)/docs/manual/highlevel-playback.xml
|
||||||
$(PERL_PATH) $(srcdir)/extract.pl $@ $<
|
$(PERL_PATH) $(srcdir)/extract.pl $@ $<
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue