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.
This commit is contained in:
Thiago Santos 2013-01-08 18:01:17 -03:00
parent 4b6900999a
commit b9aec0ad0d
4 changed files with 159 additions and 2 deletions

View file

@ -42,6 +42,16 @@
GST_DEBUG_CATEGORY (mssdemux_debug); 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 = static GstStaticPadTemplate gst_mss_demux_sink_template =
GST_STATIC_PAD_TEMPLATE ("sink", GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK, GST_PAD_SINK,
@ -64,8 +74,12 @@ GST_STATIC_PAD_TEMPLATE ("audio_%02u",
GST_BOILERPLATE (GstMssDemux, gst_mss_demux, GstMssDemux, GST_TYPE_ELEMENT); GST_BOILERPLATE (GstMssDemux, gst_mss_demux, GstMssDemux, GST_TYPE_ELEMENT);
static void gst_mss_demux_dispose (GObject * object); static void gst_mss_demux_dispose (GObject * object);
static GstStateChangeReturn static void gst_mss_demux_set_property (GObject * object, guint prop_id,
gst_mss_demux_change_state (GstElement * element, GstStateChange transition); 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_chain (GstPad * pad, GstBuffer * buffer);
static GstFlowReturn gst_mss_demux_event (GstPad * pad, GstEvent * event); 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); parent_class = g_type_class_peek_parent (klass);
gobject_class->dispose = gst_mss_demux_dispose; 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 = gstelement_class->change_state =
GST_DEBUG_FUNCPTR (gst_mss_demux_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); 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 static GstStateChangeReturn
gst_mss_demux_change_state (GstElement * element, GstStateChange transition) 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); gst_mss_stream_set_active (manifeststream, TRUE);
mssdemux->streams = g_slist_append (mssdemux->streams, stream); mssdemux->streams = g_slist_append (mssdemux->streams, stream);
} }
/* select initial bitrates */
gst_mss_manifest_change_bitrate (mssdemux->manifest,
mssdemux->connection_speed);
} }
static gboolean static gboolean

View file

@ -81,6 +81,8 @@ struct _GstMssDemux {
guint n_videos; guint n_videos;
guint n_audios; guint n_audios;
/* properties */
guint64 connection_speed; /* in bps */
}; };
struct _GstMssDemuxClass { struct _GstMssDemuxClass {

View file

@ -754,3 +754,98 @@ gst_mss_stream_seek (GstMssStream * stream, guint64 time)
return TRUE; 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;
}

View file

@ -45,6 +45,8 @@ guint64 gst_mss_manifest_get_timescale (GstMssManifest * manifest);
guint64 gst_mss_manifest_get_duration (GstMssManifest * manifest); guint64 gst_mss_manifest_get_duration (GstMssManifest * manifest);
GstClockTime gst_mss_manifest_get_gst_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_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); GstMssStreamType gst_mss_stream_get_type (GstMssStream *stream);
GstCaps * gst_mss_stream_get_caps (GstMssStream * stream); GstCaps * gst_mss_stream_get_caps (GstMssStream * stream);