| |
| /*** block a from ../../../docs/manual/advanced-buffering.xml ***/ |
| #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) { |
| 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) |
| 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; |
| } |