From c98348c1417fbe6cabe0fd4dfcb3d65acd8d5c81 Mon Sep 17 00:00:00 2001 From: Mathieu Duponchelle Date: Tue, 27 Jan 2015 13:48:42 +0100 Subject: [PATCH] adaptivedemux: track per-fragment bitrates. And use the average to go up in resolution, and the last fragment bitrate to go down. This allows the demuxer to react rapidly to bitrate loss, and be conservative for bitrate improvements. + Add a construct only property to define the number of fragments to consider when calculating the average moving bitrate. https://bugzilla.gnome.org/show_bug.cgi?id=742979 --- gst-libs/gst/adaptivedemux/gstadaptivedemux.c | 116 +++++++++++++++--- gst-libs/gst/adaptivedemux/gstadaptivedemux.h | 11 ++ 2 files changed, 110 insertions(+), 17 deletions(-) diff --git a/gst-libs/gst/adaptivedemux/gstadaptivedemux.c b/gst-libs/gst/adaptivedemux/gstadaptivedemux.c index dcf16aaf32..6568110c3e 100644 --- a/gst-libs/gst/adaptivedemux/gstadaptivedemux.c +++ b/gst-libs/gst/adaptivedemux/gstadaptivedemux.c @@ -86,6 +86,14 @@ GST_DEBUG_CATEGORY (adaptivedemux_debug); #define MAX_DOWNLOAD_ERROR_COUNT 3 #define DEFAULT_FAILED_COUNT 3 +#define DEFAULT_LOOKBACK_FRAGMENTS 3 + +enum +{ + PROP_0, + PROP_LOOKBACK_FRAGMENTS, + PROP_LAST +}; enum GstAdaptiveDemuxFlowReturn { @@ -209,6 +217,38 @@ gst_adaptive_demux_get_type (void) return type; } +static void +gst_adaptive_demux_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstAdaptiveDemux *demux = GST_ADAPTIVE_DEMUX (object); + + switch (prop_id) { + case PROP_LOOKBACK_FRAGMENTS: + demux->num_lookback_fragments = g_value_get_uint (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_adaptive_demux_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstAdaptiveDemux *demux = GST_ADAPTIVE_DEMUX (object); + + switch (prop_id) { + case PROP_LOOKBACK_FRAGMENTS: + g_value_set_uint (value, demux->num_lookback_fragments); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + static void gst_adaptive_demux_class_init (GstAdaptiveDemuxClass * klass) { @@ -226,8 +266,17 @@ gst_adaptive_demux_class_init (GstAdaptiveDemuxClass * klass) parent_class = g_type_class_peek_parent (klass); g_type_class_add_private (klass, sizeof (GstAdaptiveDemuxPrivate)); + gobject_class->set_property = gst_adaptive_demux_set_property; + gobject_class->get_property = gst_adaptive_demux_get_property; gobject_class->finalize = gst_adaptive_demux_finalize; + g_object_class_install_property (gobject_class, PROP_LOOKBACK_FRAGMENTS, + g_param_spec_uint ("num-lookback-fragments", + "Number of fragments to look back", + "The number of fragments the demuxer will look back to calculate an average bitrate", + 1, G_MAXUINT, DEFAULT_LOOKBACK_FRAGMENTS, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY)); + gstelement_class->change_state = gst_adaptive_demux_change_state; gstbin_class->handle_message = gst_adaptive_demux_handle_message; @@ -274,6 +323,8 @@ gst_adaptive_demux_init (GstAdaptiveDemux * demux, gst_pad_set_chain_function (demux->sinkpad, GST_DEBUG_FUNCPTR (gst_adaptive_demux_sink_chain)); + demux->num_lookback_fragments = DEFAULT_LOOKBACK_FRAGMENTS; + gst_element_add_pad (GST_ELEMENT (demux), demux->sinkpad); } @@ -702,6 +753,8 @@ gst_adaptive_demux_stream_new (GstAdaptiveDemux * demux, GstPad * pad) stream->pad = pad; stream->demux = demux; + stream->fragment_bitrates = + g_malloc0 (sizeof (guint64) * demux->num_lookback_fragments); gst_pad_set_element_private (pad, stream); gst_pad_set_query_function (pad, @@ -766,6 +819,8 @@ gst_adaptive_demux_stream_free (GstAdaptiveDemuxStream * stream) g_cond_clear (&stream->fragment_download_cond); g_mutex_clear (&stream->fragment_download_lock); + g_free (stream->fragment_bitrates); + if (stream->pad) { gst_object_unref (stream->pad); stream->pad = NULL; @@ -1165,26 +1220,47 @@ gst_adaptive_demux_stream_set_tags (GstAdaptiveDemuxStream * stream, } static guint64 -gst_adaptive_demux_stream_update_current_bitrate (GstAdaptiveDemuxStream * - stream) +_update_average_bitrate (GstAdaptiveDemux * demux, + GstAdaptiveDemuxStream * stream, guint64 new_bitrate) { - guint64 bitrate = 0; + gint index = stream->moving_index % demux->num_lookback_fragments; - if (stream->download_total_time) - bitrate = - (stream->download_total_bytes * 8) / - ((double) stream->download_total_time / G_GUINT64_CONSTANT (1000000)); + stream->moving_bitrate -= stream->fragment_bitrates[index]; + stream->fragment_bitrates[index] = new_bitrate; + stream->moving_bitrate += new_bitrate; - if (stream->current_download_rate != -1) - bitrate = (stream->current_download_rate + bitrate * 3) / 4; - if (bitrate > G_MAXINT) - bitrate = G_MAXINT; - stream->current_download_rate = bitrate; - GST_DEBUG_OBJECT (stream->pad, "Bitrate: %" G_GUINT64_FORMAT - " (bytes: %" G_GINT64_FORMAT " / %" G_GINT64_FORMAT " microsecs", - bitrate, stream->download_total_bytes, stream->download_total_time); + stream->moving_index += 1; - return bitrate; + if (stream->moving_index > demux->num_lookback_fragments) + return stream->moving_bitrate / demux->num_lookback_fragments; + return stream->moving_bitrate / stream->moving_index; +} + +static guint64 +gst_adaptive_demux_stream_update_current_bitrate (GstAdaptiveDemux * demux, + GstAdaptiveDemuxStream * stream) +{ + guint64 average_bitrate; + guint64 fragment_bitrate; + + fragment_bitrate = + (stream->fragment_total_size * 8) / + ((double) stream->fragment_total_time / G_GUINT64_CONSTANT (1000000)); + stream->fragment_total_size = 0; + stream->fragment_total_time = 0; + + average_bitrate = _update_average_bitrate (demux, stream, fragment_bitrate); + + GST_INFO_OBJECT (stream, "last fragment bitrate was %" G_GUINT64_FORMAT, + fragment_bitrate); + GST_INFO_OBJECT (stream, + "Last %u fragments average bitrate is %" G_GUINT64_FORMAT, + demux->num_lookback_fragments, average_bitrate); + + /* Conservative approach, make sure we don't upgrade too fast */ + stream->current_download_rate = MIN (average_bitrate, fragment_bitrate); + + return stream->current_download_rate; } static GstFlowReturn @@ -1337,6 +1413,10 @@ _src_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) g_get_monotonic_time () - stream->download_chunk_start_time; stream->download_total_bytes += gst_buffer_get_size (buffer); + stream->fragment_total_size += gst_buffer_get_size (buffer); + stream->fragment_total_time += + g_get_monotonic_time () - stream->download_chunk_start_time; + gst_adapter_push (stream->adapter, buffer); GST_DEBUG_OBJECT (stream->pad, "Received buffer of size %" G_GSIZE_FORMAT ". Now %" G_GSIZE_FORMAT " on adapter", gst_buffer_get_size (buffer), @@ -2229,6 +2309,8 @@ gst_adaptive_demux_stream_advance_fragment_unlocked (GstAdaptiveDemux * demux, g_clear_error (&stream->last_error); stream->download_total_time += g_get_monotonic_time () - stream->download_chunk_start_time; + stream->fragment_total_time += + g_get_monotonic_time () - stream->download_chunk_start_time; /* FIXME - url has no indication of byte ranges for subsegments */ gst_element_post_message (GST_ELEMENT_CAST (demux), @@ -2253,7 +2335,7 @@ gst_adaptive_demux_stream_advance_fragment_unlocked (GstAdaptiveDemux * demux, if (ret == GST_FLOW_OK) { if (gst_adaptive_demux_stream_select_bitrate (demux, stream, - gst_adaptive_demux_stream_update_current_bitrate (stream))) { + gst_adaptive_demux_stream_update_current_bitrate (demux, stream))) { stream->need_header = TRUE; gst_adapter_clear (stream->adapter); ret = (GstFlowReturn) GST_ADAPTIVE_DEMUX_FLOW_SWITCH; diff --git a/gst-libs/gst/adaptivedemux/gstadaptivedemux.h b/gst-libs/gst/adaptivedemux/gstadaptivedemux.h index 456d093dc0..635aa1b825 100644 --- a/gst-libs/gst/adaptivedemux/gstadaptivedemux.h +++ b/gst-libs/gst/adaptivedemux/gstadaptivedemux.h @@ -144,6 +144,15 @@ struct _GstAdaptiveDemuxStream gint64 download_total_bytes; gint current_download_rate; + /* Per fragment download information */ + guint64 fragment_total_time; + guint64 fragment_total_size; + + /* Average for the last fragments */ + guint64 moving_bitrate; + guint moving_index; + guint64 *fragment_bitrates; + GstAdaptiveDemuxStreamFragment fragment; guint download_error_count; @@ -182,6 +191,8 @@ struct _GstAdaptiveDemux gchar *manifest_uri; gchar *manifest_base_uri; + guint num_lookback_fragments; + gboolean have_group_id; guint group_id; };