From b9aec0ad0df44080a9a032353fabe92e3e4d6951 Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Tue, 8 Jan 2013 18:01:17 -0300 Subject: [PATCH] mssdemux: add connection-speed property This speed limits the maximum bitrate of streams. Currently it is only read when starting the pipeline, but it should be used for switching bitrates during playback to adapt to network changes. --- ext/smoothstreaming/gstmssdemux.c | 62 +++++++++++++++++- ext/smoothstreaming/gstmssdemux.h | 2 + ext/smoothstreaming/gstmssmanifest.c | 95 ++++++++++++++++++++++++++++ ext/smoothstreaming/gstmssmanifest.h | 2 + 4 files changed, 159 insertions(+), 2 deletions(-) diff --git a/ext/smoothstreaming/gstmssdemux.c b/ext/smoothstreaming/gstmssdemux.c index 22bfdc7aca..cb6cde3e23 100644 --- a/ext/smoothstreaming/gstmssdemux.c +++ b/ext/smoothstreaming/gstmssdemux.c @@ -42,6 +42,16 @@ GST_DEBUG_CATEGORY (mssdemux_debug); +#define DEFAULT_CONNECTION_SPEED 0 + +enum +{ + PROP_0, + + PROP_CONNECTION_SPEED, + PROP_LAST +}; + static GstStaticPadTemplate gst_mss_demux_sink_template = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, @@ -64,8 +74,12 @@ GST_STATIC_PAD_TEMPLATE ("audio_%02u", GST_BOILERPLATE (GstMssDemux, gst_mss_demux, GstMssDemux, GST_TYPE_ELEMENT); static void gst_mss_demux_dispose (GObject * object); -static GstStateChangeReturn -gst_mss_demux_change_state (GstElement * element, GstStateChange transition); +static void gst_mss_demux_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_mss_demux_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static GstStateChangeReturn gst_mss_demux_change_state (GstElement * element, + GstStateChange transition); static GstFlowReturn gst_mss_demux_chain (GstPad * pad, GstBuffer * buffer); static GstFlowReturn gst_mss_demux_event (GstPad * pad, GstEvent * event); @@ -106,6 +120,14 @@ gst_mss_demux_class_init (GstMssDemuxClass * klass) parent_class = g_type_class_peek_parent (klass); gobject_class->dispose = gst_mss_demux_dispose; + gobject_class->set_property = gst_mss_demux_set_property; + gobject_class->get_property = gst_mss_demux_get_property; + + g_object_class_install_property (gobject_class, PROP_CONNECTION_SPEED, + g_param_spec_uint64 ("connection-speed", "Connection Speed", + "Network connection speed in kbps (0 = unknown)", + 0, G_MAXUINT64 / 1000, DEFAULT_CONNECTION_SPEED, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_mss_demux_change_state); @@ -216,6 +238,38 @@ gst_mss_demux_dispose (GObject * object) G_OBJECT_CLASS (parent_class)->dispose (object); } +static void +gst_mss_demux_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstMssDemux *mssdemux = GST_MSS_DEMUX (object); + + switch (prop_id) { + case PROP_CONNECTION_SPEED: + mssdemux->connection_speed = g_value_get_uint (value) * 1000; + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_mss_demux_get_property (GObject * object, guint prop_id, GValue * value, + GParamSpec * pspec) +{ + GstMssDemux *mssdemux = GST_MSS_DEMUX (object); + + switch (prop_id) { + case PROP_CONNECTION_SPEED: + g_value_set_uint (value, mssdemux->connection_speed / 1000); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + static GstStateChangeReturn gst_mss_demux_change_state (GstElement * element, GstStateChange transition) { @@ -532,6 +586,10 @@ gst_mss_demux_create_streams (GstMssDemux * mssdemux) gst_mss_stream_set_active (manifeststream, TRUE); mssdemux->streams = g_slist_append (mssdemux->streams, stream); } + + /* select initial bitrates */ + gst_mss_manifest_change_bitrate (mssdemux->manifest, + mssdemux->connection_speed); } static gboolean diff --git a/ext/smoothstreaming/gstmssdemux.h b/ext/smoothstreaming/gstmssdemux.h index 9dee169e88..54d87f9f94 100644 --- a/ext/smoothstreaming/gstmssdemux.h +++ b/ext/smoothstreaming/gstmssdemux.h @@ -81,6 +81,8 @@ struct _GstMssDemux { guint n_videos; guint n_audios; + /* properties */ + guint64 connection_speed; /* in bps */ }; struct _GstMssDemuxClass { diff --git a/ext/smoothstreaming/gstmssmanifest.c b/ext/smoothstreaming/gstmssmanifest.c index 71fd576a26..0659164564 100644 --- a/ext/smoothstreaming/gstmssmanifest.c +++ b/ext/smoothstreaming/gstmssmanifest.c @@ -754,3 +754,98 @@ gst_mss_stream_seek (GstMssStream * stream, guint64 time) return TRUE; } + +guint64 +gst_mss_manifest_get_current_bitrate (GstMssManifest * manifest) +{ + guint64 bitrate = 0; + GSList *iter; + + for (iter = gst_mss_manifest_get_streams (manifest); iter; + iter = g_slist_next (iter)) { + GstMssStream *stream = iter->data; + if (stream->active && stream->current_quality) { + GstMssStreamQuality *q = stream->current_quality->data; + + bitrate += q->bitrate; + } + } + + return bitrate; +} + +static gboolean +gst_mss_stream_select_bitrate (GstMssStream * stream, guint64 bitrate) +{ + GList *iter = stream->current_quality; + GList *next; + GstMssStreamQuality *q = iter->data; + + while (q->bitrate > bitrate) { + next = g_list_previous (iter); + if (next) { + iter = next; + q = iter->data; + } else { + break; + } + } + + while (q->bitrate < bitrate) { + GstMssStreamQuality *next_q; + next = g_list_next (iter); + if (next) { + next_q = next->data; + if (next_q->bitrate < bitrate) { + iter = next; + q = iter->data; + } else { + break; + } + } else { + break; + } + } + + if (iter == stream->current_quality) + return FALSE; + stream->current_quality = iter; + return TRUE; +} + +/** + * gst_mss_manifest_change_bitrate: + * @manifest: the manifest + * @bitrate: the maximum bitrate to use (bps) + * + * Iterates over the active streams and changes their bitrates to the maximum + * value so that the bitrates of all streams are not larger than + * @bitrate. + * + * Return: %TRUE if any stream changed its bitrate + */ +gboolean +gst_mss_manifest_change_bitrate (GstMssManifest * manifest, guint64 bitrate) +{ + gboolean ret = FALSE; + GSList *iter; + + /* TODO This algorithm currently sets the same bitrate for all streams, + * it should actually use the sum of all streams bitrates to compare to + * the target value */ + + if (bitrate == 0) { + /* use maximum */ + bitrate = G_MAXUINT64; + } + + for (iter = gst_mss_manifest_get_streams (manifest); iter; + iter = g_slist_next (iter)) { + GstMssStream *stream = iter->data; + if (stream->active) { + ret = ret | gst_mss_stream_select_bitrate (stream, bitrate); + } + } + + return ret; +} diff --git a/ext/smoothstreaming/gstmssmanifest.h b/ext/smoothstreaming/gstmssmanifest.h index 6abb4e9fac..796130a746 100644 --- a/ext/smoothstreaming/gstmssmanifest.h +++ b/ext/smoothstreaming/gstmssmanifest.h @@ -45,6 +45,8 @@ guint64 gst_mss_manifest_get_timescale (GstMssManifest * manifest); guint64 gst_mss_manifest_get_duration (GstMssManifest * manifest); GstClockTime gst_mss_manifest_get_gst_duration (GstMssManifest * manifest); gboolean gst_mss_manifest_seek (GstMssManifest * manifest, guint64 time); +gboolean gst_mss_manifest_change_bitrate (GstMssManifest *manifest, guint64 bitrate); +guint64 gst_mss_manifest_get_current_bitrate (GstMssManifest * manifest); GstMssStreamType gst_mss_stream_get_type (GstMssStream *stream); GstCaps * gst_mss_stream_get_caps (GstMssStream * stream); -- 2.34.1