From c8ef39cac7337b55901bfb38ca5a8ecda07376a8 Mon Sep 17 00:00:00 2001 From: Alex Ashley Date: Tue, 7 Jul 2015 15:38:08 +0100 Subject: [PATCH] dashdemux: provide a default suggestedPresentationDelay If MPD@suggestedPresentationDelay is not present in the manifest, dashdemux selects the fragment closest to the most recently generated fragment. This causes a playback issue because this choice does not allow the DASH client to build up any buffer of downloaded fragments without pausing playback. This is because by definition new fragments appear on the server in real-time (e.g. if segment duration is 4 seconds, a new fragment will appear on the server every 4 seconds). If the starting playback position was n*segmentDuration seconds behind "now", the DASH client could download up to 'n' fragments faster than realtime before it reached the point where it needed to wait for fragments to appear on the server. The MPD@suggestedPresentationDelay attribute allows a content publisher to provide a suggested starting position that is behind the current "live" position. If the MPD@suggestedPresentationDelay attribute is not present, provide a suitable default value as a property of the dashdemux element. To allow the default presentation delay to be specified either using fragments or seconds, the property is a string that contains a number and a unit (e.g. "10 seconds", "4 fragments", "2500ms"). --- ext/dash/gstdashdemux.c | 28 ++++++++++++++++++++++++++ ext/dash/gstdashdemux.h | 1 + ext/dash/gstmpdparser.c | 40 +++++++++++++++++++++++++++++++++++++ ext/dash/gstmpdparser.h | 1 + tests/check/elements/dash_mpd.c | 44 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 114 insertions(+) diff --git a/ext/dash/gstdashdemux.c b/ext/dash/gstdashdemux.c index 3d909da..4156bd5 100644 --- a/ext/dash/gstdashdemux.c +++ b/ext/dash/gstdashdemux.c @@ -146,6 +146,7 @@ #include #include +#include #include #include #include @@ -188,6 +189,7 @@ enum PROP_MAX_BUFFERING_TIME, PROP_BANDWIDTH_USAGE, PROP_MAX_BITRATE, + PROP_PRESENTATION_DELAY, PROP_LAST }; @@ -195,6 +197,7 @@ enum #define DEFAULT_MAX_BUFFERING_TIME 30 /* in seconds */ #define DEFAULT_BANDWIDTH_USAGE 0.8 /* 0 to 1 */ #define DEFAULT_MAX_BITRATE 24000000 /* in bit/s */ +#define DEFAULT_PRESENTATION_DELAY NULL /* zero */ /* Clock drift compensation for live streams */ #define SLOW_CLOCK_UPDATE_INTERVAL (1000000 * 30 * 60) /* 30 minutes */ @@ -299,6 +302,7 @@ gst_dash_demux_dispose (GObject * obj) gst_dash_demux_clock_drift_free (demux->clock_drift); demux->clock_drift = NULL; + g_free (demux->default_presentation_delay); G_OBJECT_CLASS (parent_class)->dispose (obj); } @@ -379,6 +383,12 @@ gst_dash_demux_class_init (GstDashDemuxClass * klass) 1000, G_MAXUINT, DEFAULT_MAX_BITRATE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_PRESENTATION_DELAY, + g_param_spec_string ("presentation-delay", "Presentation delay", + "Default presentation delay (in seconds, milliseconds or fragments) (e.g. 12s, 2500ms, 3f)", + DEFAULT_PRESENTATION_DELAY, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + gst_element_class_add_pad_template (gstelement_class, gst_static_pad_template_get (&gst_dash_demux_audiosrc_template)); gst_element_class_add_pad_template (gstelement_class, @@ -441,6 +451,7 @@ gst_dash_demux_init (GstDashDemux * demux) /* Properties */ demux->max_buffering_time = DEFAULT_MAX_BUFFERING_TIME * GST_SECOND; demux->max_bitrate = DEFAULT_MAX_BITRATE; + demux->default_presentation_delay = DEFAULT_PRESENTATION_DELAY; g_mutex_init (&demux->client_lock); @@ -465,6 +476,10 @@ gst_dash_demux_set_property (GObject * object, guint prop_id, case PROP_MAX_BITRATE: demux->max_bitrate = g_value_get_uint (value); break; + case PROP_PRESENTATION_DELAY: + g_free (demux->default_presentation_delay); + demux->default_presentation_delay = g_value_dup_string (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -488,6 +503,12 @@ gst_dash_demux_get_property (GObject * object, guint prop_id, GValue * value, case PROP_MAX_BITRATE: g_value_set_uint (value, demux->max_bitrate); break; + case PROP_PRESENTATION_DELAY: + if (demux->default_presentation_delay == NULL) + g_value_set_static_string (value, ""); + else + g_value_set_string (value, demux->default_presentation_delay); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -684,6 +705,13 @@ gst_dash_demux_setup_streams (GstAdaptiveDemux * demux) dashdemux->client->mpd_node->suggestedPresentationDelay * -1000); gst_date_time_unref (now); now = target; + } else if (dashdemux->default_presentation_delay) { + gint64 dfp = + gst_mpd_client_parse_default_presentation_delay (dashdemux->client, + dashdemux->default_presentation_delay); + GstDateTime *target = gst_mpd_client_add_time_difference (now, dfp); + gst_date_time_unref (now); + now = target; } period_idx = gst_mpd_client_get_period_index_at_time (dashdemux->client, now); diff --git a/ext/dash/gstdashdemux.h b/ext/dash/gstdashdemux.h index 3d01a0b..5ed9620 100644 --- a/ext/dash/gstdashdemux.h +++ b/ext/dash/gstdashdemux.h @@ -96,6 +96,7 @@ struct _GstDashDemux /* Properties */ GstClockTime max_buffering_time; /* Maximum buffering time accumulated during playback */ guint64 max_bitrate; /* max of bitrate supported by target decoder */ + gchar* default_presentation_delay; /* presentation time delay if MPD@suggestedPresentationDelay is not present */ gint n_audio_streams; gint n_video_streams; diff --git a/ext/dash/gstmpdparser.c b/ext/dash/gstmpdparser.c index 953e207..3a1cb82 100644 --- a/ext/dash/gstmpdparser.c +++ b/ext/dash/gstmpdparser.c @@ -5807,3 +5807,43 @@ gst_mpd_client_has_isoff_ondemand_profile (GstMpdClient * client) { return client->profile_isoff_ondemand; } + +/** + * gst_mpd_client_parse_default_presentation_delay: + * @client: #GstMpdClient that has a parsed manifest + * @default_presentation_delay: A string that specifies a time period + * in fragments (e.g. "5 f"), seconds ("12 s") or milliseconds + * ("12000 ms") + * Returns: the parsed string in milliseconds + * + * Since: 1.6 + */ +gint64 +gst_mpd_client_parse_default_presentation_delay (GstMpdClient * client, + const gchar * default_presentation_delay) +{ + gint64 value; + char *endptr = NULL; + + g_return_val_if_fail (client != NULL, 0); + g_return_val_if_fail (default_presentation_delay != NULL, 0); + value = strtol (default_presentation_delay, &endptr, 10); + if (endptr == default_presentation_delay || value == 0) { + return 0; + } + while (*endptr == ' ') + endptr++; + if (*endptr == 's' || *endptr == 'S') { + value *= 1000; /* convert to ms */ + } else if (*endptr == 'f' || *endptr == 'F') { + gint64 segment_duration; + g_assert (client->mpd_node != NULL); + segment_duration = client->mpd_node->maxSegmentDuration; + value *= segment_duration; + } else if (*endptr != 'm' && *endptr != 'M') { + GST_ERROR ("Unable to parse default presentation delay: %s", + default_presentation_delay); + value = 0; + } + return value; +} diff --git a/ext/dash/gstmpdparser.h b/ext/dash/gstmpdparser.h index 6e29d22..f76b379 100644 --- a/ext/dash/gstmpdparser.h +++ b/ext/dash/gstmpdparser.h @@ -594,6 +594,7 @@ guint gst_mpdparser_get_list_and_nb_of_audio_language (GstMpdClient *client, GLi gint64 gst_mpd_client_calculate_time_difference (const GstDateTime * t1, const GstDateTime * t2); GstDateTime *gst_mpd_client_add_time_difference (GstDateTime * t1, gint64 usecs); +gint64 gst_mpd_client_parse_default_presentation_delay(GstMpdClient * client, const gchar * default_presentation_delay); /* profiles */ gboolean gst_mpd_client_has_isoff_ondemand_profile (GstMpdClient *client); diff --git a/tests/check/elements/dash_mpd.c b/tests/check/elements/dash_mpd.c index 633beb2..00799fa 100644 --- a/tests/check/elements/dash_mpd.c +++ b/tests/check/elements/dash_mpd.c @@ -4751,6 +4751,49 @@ GST_START_TEST (dash_mpdparser_unmatched_segmentTimeline_segmentURL) GST_END_TEST; /* + * Test parsing of the default presentation delay property + */ +GST_START_TEST (dash_mpdparser_default_presentation_delay) +{ + const gchar *xml = + "" + "" + " "; + + gboolean ret; + GstMpdClient *mpdclient = gst_mpd_client_new (); + gint64 value; + + ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml)); + assert_equals_int (ret, TRUE); + value = gst_mpd_client_parse_default_presentation_delay (mpdclient, "5s"); + assert_equals_int64 (value, 5000); + value = gst_mpd_client_parse_default_presentation_delay (mpdclient, "5S"); + assert_equals_int64 (value, 5000); + value = + gst_mpd_client_parse_default_presentation_delay (mpdclient, "5 seconds"); + assert_equals_int64 (value, 5000); + value = gst_mpd_client_parse_default_presentation_delay (mpdclient, "2500ms"); + assert_equals_int64 (value, 2500); + value = gst_mpd_client_parse_default_presentation_delay (mpdclient, "3f"); + assert_equals_int64 (value, 6000); + value = gst_mpd_client_parse_default_presentation_delay (mpdclient, "3F"); + assert_equals_int64 (value, 6000); + value = gst_mpd_client_parse_default_presentation_delay (mpdclient, ""); + assert_equals_int64 (value, 0); + value = gst_mpd_client_parse_default_presentation_delay (mpdclient, "10"); + assert_equals_int64 (value, 0); + value = + gst_mpd_client_parse_default_presentation_delay (mpdclient, + "not a number"); + assert_equals_int64 (value, 0); +} + +GST_END_TEST; + +/* * create a test suite containing all dash testcases */ static Suite * @@ -4871,6 +4914,7 @@ dash_suite (void) tcase_add_test (tc_simpleMPD, dash_mpdparser_isoff_ondemand_profile); tcase_add_test (tc_simpleMPD, dash_mpdparser_GstDateTime); tcase_add_test (tc_simpleMPD, dash_mpdparser_various_duration_formats); + tcase_add_test (tc_simpleMPD, dash_mpdparser_default_presentation_delay); /* tests checking the MPD management * (eg. setting active streams, obtaining attributes values) -- 2.7.4