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
This commit is contained in:
Mathieu Duponchelle 2015-01-27 13:48:42 +01:00
parent 767fefafb3
commit c98348c141
2 changed files with 110 additions and 17 deletions

View file

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

View file

@ -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;
};