dash: split mpdparser, mpdclient and xmlhelper
authorStéphane Cerveau <scerveau@collabora.com>
Mon, 20 May 2019 16:48:23 +0000 (18:48 +0200)
committerStéphane Cerveau <scerveau@collabora.com>
Thu, 5 Dec 2019 09:06:37 +0000 (09:06 +0000)
provide a separate namespace for mpd helper
for xml parsing and the real mpd parsing.

12 files changed:
ext/dash/gstdashdemux.c
ext/dash/gstdashdemux.h
ext/dash/gstmpdclient.c [new file with mode: 0644]
ext/dash/gstmpdclient.h [new file with mode: 0644]
ext/dash/gstmpdhelper.c [new file with mode: 0644]
ext/dash/gstmpdhelper.h [new file with mode: 0644]
ext/dash/gstmpdparser.c
ext/dash/gstmpdparser.h
ext/dash/gstxmlhelper.c [new file with mode: 0644]
ext/dash/gstxmlhelper.h [new file with mode: 0644]
ext/dash/meson.build
tests/check/elements/dash_mpd.c

index f550b30..9b0fa26 100644 (file)
@@ -520,7 +520,7 @@ gst_dash_demux_get_presentation_offset (GstAdaptiveDemux * demux,
   GstDashDemuxStream *dashstream = (GstDashDemuxStream *) stream;
   GstDashDemux *dashdemux = GST_DASH_DEMUX_CAST (demux);
 
-  return gst_mpd_parser_get_stream_presentation_offset (dashdemux->client,
+  return gst_mpd_client_get_stream_presentation_offset (dashdemux->client,
       dashstream->index);
 }
 
@@ -529,7 +529,7 @@ gst_dash_demux_get_period_start_time (GstAdaptiveDemux * demux)
 {
   GstDashDemux *dashdemux = GST_DASH_DEMUX_CAST (demux);
 
-  return gst_mpd_parser_get_period_start_time (dashdemux->client);
+  return gst_mpd_client_get_period_start_time (dashdemux->client);
 }
 
 static void
@@ -778,14 +778,14 @@ gst_dash_demux_setup_all_streams (GstDashDemux * demux)
       gst_mpd_client_get_period_index (demux->client));
 
   /* clean old active stream list, if any */
-  gst_active_streams_free (demux->client);
+  gst_mpd_client_active_streams_free (demux->client);
 
   if (!gst_dash_demux_setup_mpdparser_streams (demux, demux->client)) {
     return FALSE;
   }
 
   GST_DEBUG_OBJECT (demux, "Creating stream objects");
-  for (i = 0; i < gst_mpdparser_get_nb_active_stream (demux->client); i++) {
+  for (i = 0; i < gst_mpd_client_get_nb_active_stream (demux->client); i++) {
     GstDashDemuxStream *stream;
     GstActiveStream *active_stream;
     GstCaps *caps;
@@ -794,7 +794,8 @@ gst_dash_demux_setup_all_streams (GstDashDemux * demux)
     gchar *lang = NULL;
     GstTagList *tags = NULL;
 
-    active_stream = gst_mpdparser_get_active_stream_by_index (demux->client, i);
+    active_stream =
+        gst_mpd_client_get_active_stream_by_index (demux->client, i);
     if (active_stream == NULL)
       continue;
 
@@ -1036,7 +1037,7 @@ gst_dash_demux_process_manifest (GstAdaptiveDemux * demux, GstBuffer * buf)
 
   if (gst_buffer_map (buf, &mapinfo, GST_MAP_READ)) {
     manifest = (gchar *) mapinfo.data;
-    if (gst_mpd_parse (dashdemux->client, manifest, mapinfo.size)) {
+    if (gst_mpd_client_parse (dashdemux->client, manifest, mapinfo.size)) {
       if (gst_mpd_client_setup_media_presentation (dashdemux->client, 0, 0,
               NULL)) {
         ret = TRUE;
@@ -1232,7 +1233,7 @@ gst_dash_demux_stream_update_headers_info (GstAdaptiveDemuxStream * stream)
 
   if (path != NULL) {
     stream->fragment.header_uri =
-        gst_uri_join_strings (gst_mpdparser_get_baseURL (dashdemux->client,
+        gst_uri_join_strings (gst_mpd_client_get_baseURL (dashdemux->client,
             dashstream->index), path);
     g_free (path);
     path = NULL;
@@ -1244,7 +1245,7 @@ gst_dash_demux_stream_update_headers_info (GstAdaptiveDemuxStream * stream)
 
   if (path != NULL) {
     stream->fragment.index_uri =
-        gst_uri_join_strings (gst_mpdparser_get_baseURL (dashdemux->client,
+        gst_uri_join_strings (gst_mpd_client_get_baseURL (dashdemux->client,
             dashstream->index), path);
     g_free (path);
   }
@@ -1501,9 +1502,9 @@ gst_dash_demux_stream_seek (GstAdaptiveDemuxStream * stream, gboolean forward,
   if (is_isobmff) {
     GstClockTime period_start, offset;
 
-    period_start = gst_mpd_parser_get_period_start_time (dashdemux->client);
+    period_start = gst_mpd_client_get_period_start_time (dashdemux->client);
     offset =
-        gst_mpd_parser_get_stream_presentation_offset (dashdemux->client,
+        gst_mpd_client_get_stream_presentation_offset (dashdemux->client,
         dashstream->index);
 
     if (G_UNLIKELY (ts < period_start))
@@ -2198,12 +2199,12 @@ gst_dash_demux_stream_select_bitrate (GstAdaptiveDemuxStream * stream,
   if (GST_ADAPTIVE_DEMUX_IN_TRICKMODE_KEY_UNITS (base_demux) ||
       ABS (base_demux->segment.rate) <= 1.0) {
     new_index =
-        gst_mpdparser_get_rep_idx_with_max_bandwidth (rep_list, bitrate,
+        gst_mpd_client_get_rep_idx_with_max_bandwidth (rep_list, bitrate,
         demux->max_video_width, demux->max_video_height,
         demux->max_video_framerate_n, demux->max_video_framerate_d);
   } else {
     new_index =
-        gst_mpdparser_get_rep_idx_with_max_bandwidth (rep_list,
+        gst_mpd_client_get_rep_idx_with_max_bandwidth (rep_list,
         bitrate / ABS (base_demux->segment.rate), demux->max_video_width,
         demux->max_video_height, demux->max_video_framerate_n,
         demux->max_video_framerate_d);
@@ -2211,7 +2212,7 @@ gst_dash_demux_stream_select_bitrate (GstAdaptiveDemuxStream * stream,
 
   /* if no representation has the required bandwidth, take the lowest one */
   if (new_index == -1)
-    new_index = gst_mpdparser_get_rep_idx_with_min_bandwidth (rep_list);
+    new_index = gst_mpd_client_get_rep_idx_with_min_bandwidth (rep_list);
 
   if (new_index != active_stream->representation_idx) {
     GstRepresentationNode *rep = g_list_nth_data (rep_list, new_index);
@@ -2346,7 +2347,7 @@ gst_dash_demux_seek (GstAdaptiveDemux * demux, GstEvent * seek)
     GST_DEBUG_OBJECT (demux, "Seeking to Period %d", current_period);
 
     /* clean old active stream list, if any */
-    gst_active_streams_free (dashdemux->client);
+    gst_mpd_client_active_streams_free (dashdemux->client);
     dashdemux->trickmode_no_audio = trickmode_no_audio;
 
     /* setup video, audio and subtitle streams, starting from the new Period */
@@ -2356,7 +2357,7 @@ gst_dash_demux_seek (GstAdaptiveDemux * demux, GstEvent * seek)
     streams = demux->next_streams;
   } else if (dashdemux->trickmode_no_audio != trickmode_no_audio) {
     /* clean old active stream list, if any */
-    gst_active_streams_free (dashdemux->client);
+    gst_mpd_client_active_streams_free (dashdemux->client);
     dashdemux->trickmode_no_audio = trickmode_no_audio;
 
     /* setup video, audio and subtitle streams, starting from the new Period */
@@ -2404,7 +2405,7 @@ gst_dash_demux_update_manifest_data (GstAdaptiveDemux * demux,
   new_client->mpd_base_uri = g_strdup (demux->manifest_base_uri);
   gst_buffer_map (buffer, &mapinfo, GST_MAP_READ);
 
-  if (gst_mpd_parse (new_client, (gchar *) mapinfo.data, mapinfo.size)) {
+  if (gst_mpd_client_parse (new_client, (gchar *) mapinfo.data, mapinfo.size)) {
     const gchar *period_id;
     guint period_idx;
     GList *iter;
@@ -2490,7 +2491,7 @@ gst_dash_demux_update_manifest_data (GstAdaptiveDemux * demux,
         /* _get_next_fragment_timestamp() returned relative timestamp to
          * corresponding period start, but _client_stream_seek expects absolute
          * MPD time. */
-        ts += gst_mpd_parser_get_period_start_time (dashdemux->client);
+        ts += gst_mpd_client_get_period_start_time (dashdemux->client);
 
         GST_DEBUG_OBJECT (GST_ADAPTIVE_DEMUX_STREAM_PAD (demux_stream),
             "Current position: %" GST_TIME_FORMAT ", updating to %"
index fdbbf31..ae22a19 100644 (file)
@@ -34,7 +34,7 @@
 #include <gst/adaptivedemux/gstadaptivedemux.h>
 #include <gst/base/gstadapter.h>
 #include <gst/base/gstdataqueue.h>
-#include "gstmpdparser.h"
+#include "gstmpdclient.h"
 #include <gst/isoff/gstisoff.h>
 #include <gst/uridownloader/gsturidownloader.h>
 
diff --git a/ext/dash/gstmpdclient.c b/ext/dash/gstmpdclient.c
new file mode 100644 (file)
index 0000000..a4015d8
--- /dev/null
@@ -0,0 +1,2915 @@
+/* GStreamer
+ *
+ * Copyright (C) 2019 Collabora Ltd.
+ *   Author: Stéphane Cerveau <scerveau@collabora.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ */
+
+#include "gstmpdclient.h"
+#include "gstmpdparser.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_dash_mpd_client_debug);
+#undef GST_CAT_DEFAULT
+#define GST_CAT_DEFAULT gst_dash_mpd_client_debug
+
+
+static GstSegmentBaseType *gst_mpd_client_get_segment_base (GstPeriodNode *
+    Period, GstAdaptationSetNode * AdaptationSet,
+    GstRepresentationNode * Representation);
+static GstSegmentListNode *gst_mpd_client_get_segment_list (GstMpdClient *
+    client, GstPeriodNode * Period, GstAdaptationSetNode * AdaptationSet,
+    GstRepresentationNode * Representation);
+/* Segments */
+static guint gst_mpd_client_get_segments_counts (GstMpdClient * client,
+    GstActiveStream * stream);
+
+static GList *gst_mpd_client_fetch_external_periods (GstMpdClient * client,
+    GstPeriodNode * period_node);
+static GList *gst_mpd_client_fetch_external_adaptation_set (GstMpdClient *
+    client, GstPeriodNode * period, GstAdaptationSetNode * adapt_set);
+
+static GstRepresentationNode *gst_mpd_client_get_lowest_representation (GList *
+    Representations);
+static GstStreamPeriod *gst_mpd_client_get_stream_period (GstMpdClient *
+    client);
+
+
+static GstRepresentationNode *
+gst_mpd_client_get_lowest_representation (GList * Representations)
+{
+  GList *list = NULL;
+  GstRepresentationNode *rep = NULL;
+  GstRepresentationNode *lowest = NULL;
+
+  if (Representations == NULL)
+    return NULL;
+
+  for (list = g_list_first (Representations); list; list = g_list_next (list)) {
+    rep = (GstRepresentationNode *) list->data;
+    if (rep && (!lowest || rep->bandwidth < lowest->bandwidth)) {
+      lowest = rep;
+    }
+  }
+
+  return lowest;
+}
+
+#if 0
+static GstRepresentationNode *
+gst_mpdparser_get_highest_representation (GList * Representations)
+{
+  GList *list = NULL;
+
+  if (Representations == NULL)
+    return NULL;
+
+  list = g_list_last (Representations);
+
+  return list ? (GstRepresentationNode *) list->data : NULL;
+}
+
+static GstRepresentationNode *
+gst_mpdparser_get_representation_with_max_bandwidth (GList * Representations,
+    gint max_bandwidth)
+{
+  GList *list = NULL;
+  GstRepresentationNode *representation, *best_rep = NULL;
+
+  if (Representations == NULL)
+    return NULL;
+
+  if (max_bandwidth <= 0)       /* 0 => get highest representation available */
+    return gst_mpdparser_get_highest_representation (Representations);
+
+  for (list = g_list_first (Representations); list; list = g_list_next (list)) {
+    representation = (GstRepresentationNode *) list->data;
+    if (representation && representation->bandwidth <= max_bandwidth) {
+      best_rep = representation;
+    }
+  }
+
+  return best_rep;
+}
+#endif
+
+static GstSegmentListNode *
+gst_mpd_client_fetch_external_segment_list (GstMpdClient * client,
+    GstPeriodNode * Period,
+    GstAdaptationSetNode * AdaptationSet,
+    GstRepresentationNode * Representation,
+    GstSegmentListNode * parent, GstSegmentListNode * segment_list)
+{
+  GstFragment *download;
+  GstBuffer *segment_list_buffer = NULL;
+  GstMapInfo map;
+  GError *err = NULL;
+
+  GstUri *base_uri, *uri;
+  gchar *query = NULL;
+  gchar *uri_string;
+  GstSegmentListNode *new_segment_list = NULL;
+
+  /* ISO/IEC 23009-1:2014 5.5.3 4)
+   * Remove nodes that resolve to nothing when resolving
+   */
+  if (strcmp (segment_list->xlink_href,
+          "urn:mpeg:dash:resolve-to-zero:2013") == 0) {
+    return NULL;
+  }
+
+  if (!client->downloader) {
+    return NULL;
+  }
+
+  /* Build absolute URI */
+
+  /* Get base URI at the MPD level */
+  base_uri =
+      gst_uri_from_string (client->
+      mpd_base_uri ? client->mpd_base_uri : client->mpd_uri);
+
+  /* combine a BaseURL at the MPD level with the current base url */
+  base_uri = combine_urls (base_uri, client->mpd_node->BaseURLs, &query, 0);
+
+  /* combine a BaseURL at the Period level with the current base url */
+  base_uri = combine_urls (base_uri, Period->BaseURLs, &query, 0);
+
+  if (AdaptationSet) {
+    /* combine a BaseURL at the AdaptationSet level with the current base url */
+    base_uri = combine_urls (base_uri, AdaptationSet->BaseURLs, &query, 0);
+
+    if (Representation) {
+      /* combine a BaseURL at the Representation level with the current base url */
+      base_uri = combine_urls (base_uri, Representation->BaseURLs, &query, 0);
+    }
+  }
+
+  uri = gst_uri_from_string_with_base (base_uri, segment_list->xlink_href);
+  if (query)
+    gst_uri_set_query_string (uri, query);
+  g_free (query);
+  uri_string = gst_uri_to_string (uri);
+  gst_uri_unref (base_uri);
+  gst_uri_unref (uri);
+
+  download =
+      gst_uri_downloader_fetch_uri (client->downloader,
+      uri_string, client->mpd_uri, TRUE, FALSE, TRUE, &err);
+  g_free (uri_string);
+
+  if (!download) {
+    GST_ERROR ("Failed to download external SegmentList node at '%s': %s",
+        segment_list->xlink_href, err->message);
+    g_clear_error (&err);
+    return NULL;
+  }
+
+  segment_list_buffer = gst_fragment_get_buffer (download);
+  g_object_unref (download);
+
+  gst_buffer_map (segment_list_buffer, &map, GST_MAP_READ);
+
+  new_segment_list =
+      gst_mpdparser_get_external_segment_list ((const gchar *) map.data,
+      map.size, parent);
+
+  if (segment_list_buffer) {
+    gst_buffer_unmap (segment_list_buffer, &map);
+    gst_buffer_unref (segment_list_buffer);
+  }
+
+  return new_segment_list;
+}
+
+static GstSegmentBaseType *
+gst_mpd_client_get_segment_base (GstPeriodNode * Period,
+    GstAdaptationSetNode * AdaptationSet,
+    GstRepresentationNode * Representation)
+{
+  GstSegmentBaseType *SegmentBase = NULL;
+
+  if (Representation && Representation->SegmentBase) {
+    SegmentBase = Representation->SegmentBase;
+  } else if (AdaptationSet && AdaptationSet->SegmentBase) {
+    SegmentBase = AdaptationSet->SegmentBase;
+  } else if (Period && Period->SegmentBase) {
+    SegmentBase = Period->SegmentBase;
+  }
+  /* the SegmentBase element could be encoded also inside a SegmentList element */
+  if (SegmentBase == NULL) {
+    if (Representation && Representation->SegmentList
+        && Representation->SegmentList->MultSegBaseType
+        && Representation->SegmentList->MultSegBaseType->SegBaseType) {
+      SegmentBase = Representation->SegmentList->MultSegBaseType->SegBaseType;
+    } else if (AdaptationSet && AdaptationSet->SegmentList
+        && AdaptationSet->SegmentList->MultSegBaseType
+        && AdaptationSet->SegmentList->MultSegBaseType->SegBaseType) {
+      SegmentBase = AdaptationSet->SegmentList->MultSegBaseType->SegBaseType;
+    } else if (Period && Period->SegmentList
+        && Period->SegmentList->MultSegBaseType
+        && Period->SegmentList->MultSegBaseType->SegBaseType) {
+      SegmentBase = Period->SegmentList->MultSegBaseType->SegBaseType;
+    }
+  }
+
+  return SegmentBase;
+}
+
+static GstSegmentListNode *
+gst_mpd_client_get_segment_list (GstMpdClient * client, GstPeriodNode * Period,
+    GstAdaptationSetNode * AdaptationSet,
+    GstRepresentationNode * Representation)
+{
+  GstSegmentListNode **SegmentList;
+  GstSegmentListNode *ParentSegmentList = NULL;
+
+  if (Representation && Representation->SegmentList) {
+    SegmentList = &Representation->SegmentList;
+    ParentSegmentList = AdaptationSet->SegmentList;
+  } else if (AdaptationSet && AdaptationSet->SegmentList) {
+    SegmentList = &AdaptationSet->SegmentList;
+    ParentSegmentList = Period->SegmentList;
+    Representation = NULL;
+  } else {
+    Representation = NULL;
+    AdaptationSet = NULL;
+    SegmentList = &Period->SegmentList;
+  }
+
+  /* Resolve external segment list here. */
+  if (*SegmentList && (*SegmentList)->xlink_href) {
+    GstSegmentListNode *new_segment_list;
+
+    /* TODO: Use SegmentList of parent if
+     * - Parent has its own SegmentList
+     * - Fail to get SegmentList from external xml
+     */
+    new_segment_list =
+        gst_mpd_client_fetch_external_segment_list (client, Period,
+        AdaptationSet, Representation, ParentSegmentList, *SegmentList);
+
+    gst_mpdparser_free_segment_list_node (*SegmentList);
+    *SegmentList = new_segment_list;
+  }
+
+  return *SegmentList;
+}
+
+static GstClockTime
+gst_mpd_client_get_segment_duration (GstMpdClient * client,
+    GstActiveStream * stream, guint64 * scale_dur)
+{
+  GstStreamPeriod *stream_period;
+  GstMultSegmentBaseType *base = NULL;
+  GstClockTime duration = 0;
+
+  g_return_val_if_fail (stream != NULL, GST_CLOCK_TIME_NONE);
+  stream_period = gst_mpd_client_get_stream_period (client);
+  g_return_val_if_fail (stream_period != NULL, GST_CLOCK_TIME_NONE);
+
+  if (stream->cur_segment_list) {
+    base = stream->cur_segment_list->MultSegBaseType;
+  } else if (stream->cur_seg_template) {
+    base = stream->cur_seg_template->MultSegBaseType;
+  }
+
+  if (base == NULL || base->SegBaseType == NULL) {
+    /* this may happen when we have a single segment */
+    duration = stream_period->duration;
+    if (scale_dur)
+      *scale_dur = duration;
+  } else {
+    /* duration is guint so this cannot overflow */
+    duration = base->duration * GST_SECOND;
+    if (scale_dur)
+      *scale_dur = duration;
+    duration /= base->SegBaseType->timescale;
+  }
+
+  return duration;
+}
+
+
+GstMpdClient *
+gst_mpd_client_new (void)
+{
+  GstMpdClient *client;
+  GST_DEBUG_CATEGORY_INIT (gst_dash_mpd_client_debug, "dashmpdclient", 0,
+      "DashmMpdClient");
+  client = g_new0 (GstMpdClient, 1);
+
+  return client;
+}
+
+void
+gst_mpd_client_active_streams_free (GstMpdClient * client)
+{
+  if (client->active_streams) {
+    g_list_foreach (client->active_streams,
+        (GFunc) gst_mpdparser_free_active_stream, NULL);
+    g_list_free (client->active_streams);
+    client->active_streams = NULL;
+  }
+}
+
+void
+gst_mpd_client_free (GstMpdClient * client)
+{
+  g_return_if_fail (client != NULL);
+
+  if (client->mpd_node)
+    gst_mpdparser_free_mpd_node (client->mpd_node);
+
+  if (client->periods) {
+    g_list_free_full (client->periods,
+        (GDestroyNotify) gst_mpdparser_free_stream_period);
+  }
+
+  gst_mpd_client_active_streams_free (client);
+
+  g_free (client->mpd_uri);
+  client->mpd_uri = NULL;
+  g_free (client->mpd_base_uri);
+  client->mpd_base_uri = NULL;
+
+  if (client->downloader)
+    gst_object_unref (client->downloader);
+  client->downloader = NULL;
+
+  g_free (client);
+}
+
+gboolean
+gst_mpd_client_parse (GstMpdClient * client, const gchar * data, gint size)
+{
+  gboolean ret = FALSE;
+
+
+  ret = gst_mpdparser_get_mpd_node (&client->mpd_node, data, size);
+
+  if (ret) {
+    gst_mpd_client_check_profiles (client);
+    gst_mpd_client_fetch_on_load_external_resources (client);
+  }
+
+  return ret;
+}
+
+GstDateTime *
+gst_mpd_client_get_availability_start_time (GstMpdClient * client)
+{
+  GstDateTime *start_time;
+
+  if (client == NULL)
+    return (GstDateTime *) NULL;
+
+  start_time = client->mpd_node->availabilityStartTime;
+  if (start_time)
+    gst_date_time_ref (start_time);
+  return start_time;
+}
+
+void
+gst_mpd_client_set_uri_downloader (GstMpdClient * client,
+    GstUriDownloader * downloader)
+{
+  if (client->downloader)
+    gst_object_unref (client->downloader);
+  client->downloader = gst_object_ref (downloader);
+}
+
+void
+gst_mpd_client_check_profiles (GstMpdClient * client)
+{
+  GST_DEBUG ("Profiles: %s",
+      client->mpd_node->profiles ? client->mpd_node->profiles : "<none>");
+
+  if (!client->mpd_node->profiles)
+    return;
+
+  if (g_strstr_len (client->mpd_node->profiles, -1,
+          "urn:mpeg:dash:profile:isoff-on-demand:2011")) {
+    client->profile_isoff_ondemand = TRUE;
+    GST_DEBUG ("Found ISOFF on demand profile (2011)");
+  }
+}
+
+void
+gst_mpd_client_fetch_on_load_external_resources (GstMpdClient * client)
+{
+  GList *l;
+
+  for (l = client->mpd_node->Periods; l; /* explicitly advanced below */ ) {
+    GstPeriodNode *period = l->data;
+    GList *m;
+
+    if (period->xlink_href && period->actuate == GST_XLINK_ACTUATE_ON_LOAD) {
+      GList *new_periods, *prev, *next;
+
+      new_periods = gst_mpd_client_fetch_external_periods (client, period);
+
+      prev = l->prev;
+      client->mpd_node->Periods =
+          g_list_delete_link (client->mpd_node->Periods, l);
+      gst_mpdparser_free_period_node (period);
+      period = NULL;
+
+      /* Get new next node, we will insert before this */
+      if (prev)
+        next = prev->next;
+      else
+        next = client->mpd_node->Periods;
+
+      while (new_periods) {
+        client->mpd_node->Periods =
+            g_list_insert_before (client->mpd_node->Periods, next,
+            new_periods->data);
+        new_periods = g_list_delete_link (new_periods, new_periods);
+      }
+      next = NULL;
+
+      /* Update our iterator to the first new period if any, or the next */
+      if (prev)
+        l = prev->next;
+      else
+        l = client->mpd_node->Periods;
+
+      continue;
+    }
+
+    if (period->SegmentList && period->SegmentList->xlink_href
+        && period->SegmentList->actuate == GST_XLINK_ACTUATE_ON_LOAD) {
+      GstSegmentListNode *new_segment_list;
+
+      new_segment_list =
+          gst_mpd_client_fetch_external_segment_list (client, period, NULL,
+          NULL, NULL, period->SegmentList);
+
+      gst_mpdparser_free_segment_list_node (period->SegmentList);
+      period->SegmentList = new_segment_list;
+    }
+
+    for (m = period->AdaptationSets; m; /* explicitly advanced below */ ) {
+      GstAdaptationSetNode *adapt_set = m->data;
+      GList *n;
+
+      if (adapt_set->xlink_href
+          && adapt_set->actuate == GST_XLINK_ACTUATE_ON_LOAD) {
+        GList *new_adapt_sets, *prev, *next;
+
+        new_adapt_sets =
+            gst_mpd_client_fetch_external_adaptation_set (client, period,
+            adapt_set);
+
+        prev = m->prev;
+        period->AdaptationSets = g_list_delete_link (period->AdaptationSets, m);
+        gst_mpdparser_free_adaptation_set_node (adapt_set);
+        adapt_set = NULL;
+
+        /* Get new next node, we will insert before this */
+        if (prev)
+          next = prev->next;
+        else
+          next = period->AdaptationSets;
+
+        while (new_adapt_sets) {
+          period->AdaptationSets =
+              g_list_insert_before (period->AdaptationSets, next,
+              new_adapt_sets->data);
+          new_adapt_sets = g_list_delete_link (new_adapt_sets, new_adapt_sets);
+        }
+        next = NULL;
+
+        /* Update our iterator to the first new adapt_set if any, or the next */
+        if (prev)
+          m = prev->next;
+        else
+          m = period->AdaptationSets;
+
+        continue;
+      }
+
+      if (adapt_set->SegmentList && adapt_set->SegmentList->xlink_href
+          && adapt_set->SegmentList->actuate == GST_XLINK_ACTUATE_ON_LOAD) {
+        GstSegmentListNode *new_segment_list;
+
+        new_segment_list =
+            gst_mpd_client_fetch_external_segment_list (client, period,
+            adapt_set, NULL, period->SegmentList, adapt_set->SegmentList);
+
+        gst_mpdparser_free_segment_list_node (adapt_set->SegmentList);
+        adapt_set->SegmentList = new_segment_list;
+      }
+
+      for (n = adapt_set->Representations; n; n = n->next) {
+        GstRepresentationNode *representation = n->data;
+
+        if (representation->SegmentList
+            && representation->SegmentList->xlink_href
+            && representation->SegmentList->actuate ==
+            GST_XLINK_ACTUATE_ON_LOAD) {
+
+          GstSegmentListNode *new_segment_list;
+
+          new_segment_list =
+              gst_mpd_client_fetch_external_segment_list (client, period,
+              adapt_set, representation, adapt_set->SegmentList,
+              representation->SegmentList);
+
+          gst_mpdparser_free_segment_list_node (representation->SegmentList);
+          representation->SegmentList = new_segment_list;
+
+        }
+      }
+
+      m = m->next;
+    }
+
+    l = l->next;
+  }
+}
+
+
+static GstStreamPeriod *
+gst_mpd_client_get_stream_period (GstMpdClient * client)
+{
+  g_return_val_if_fail (client != NULL, NULL);
+  g_return_val_if_fail (client->periods != NULL, NULL);
+
+  return g_list_nth_data (client->periods, client->period_idx);
+}
+
+const gchar *
+gst_mpd_client_get_baseURL (GstMpdClient * client, guint indexStream)
+{
+  GstActiveStream *stream;
+
+  g_return_val_if_fail (client != NULL, NULL);
+  g_return_val_if_fail (client->active_streams != NULL, NULL);
+  stream = g_list_nth_data (client->active_streams, indexStream);
+  g_return_val_if_fail (stream != NULL, NULL);
+
+  return stream->baseURL;
+}
+
+/* select a stream and extract the baseURL (if present) */
+gchar *
+gst_mpd_client_parse_baseURL (GstMpdClient * client, GstActiveStream * stream,
+    gchar ** query)
+{
+  GstStreamPeriod *stream_period;
+  static const gchar empty[] = "";
+  gchar *ret = NULL;
+  GstUri *abs_url;
+
+  g_return_val_if_fail (stream != NULL, g_strdup (empty));
+  stream_period = gst_mpd_client_get_stream_period (client);
+  g_return_val_if_fail (stream_period != NULL, g_strdup (empty));
+  g_return_val_if_fail (stream_period->period != NULL, g_strdup (empty));
+
+  /* NULLify query return before we start */
+  if (query)
+    *query = NULL;
+
+  /* initialise base url */
+  abs_url =
+      gst_uri_from_string (client->
+      mpd_base_uri ? client->mpd_base_uri : client->mpd_uri);
+
+  /* combine a BaseURL at the MPD level with the current base url */
+  abs_url =
+      combine_urls (abs_url, client->mpd_node->BaseURLs, query,
+      stream->baseURL_idx);
+
+  /* combine a BaseURL at the Period level with the current base url */
+  abs_url =
+      combine_urls (abs_url, stream_period->period->BaseURLs, query,
+      stream->baseURL_idx);
+
+  GST_DEBUG ("Current adaptation set id %i (%s)", stream->cur_adapt_set->id,
+      stream->cur_adapt_set->contentType);
+  /* combine a BaseURL at the AdaptationSet level with the current base url */
+  abs_url =
+      combine_urls (abs_url, stream->cur_adapt_set->BaseURLs, query,
+      stream->baseURL_idx);
+
+  /* combine a BaseURL at the Representation level with the current base url */
+  abs_url =
+      combine_urls (abs_url, stream->cur_representation->BaseURLs, query,
+      stream->baseURL_idx);
+
+  ret = gst_uri_to_string (abs_url);
+  gst_uri_unref (abs_url);
+
+  return ret;
+}
+
+static GstClockTime
+gst_mpd_client_get_segment_end_time (GstMpdClient * client,
+    GPtrArray * segments, const GstMediaSegment * segment, gint index)
+{
+  const GstStreamPeriod *stream_period;
+  GstClockTime end;
+
+  if (segment->repeat >= 0)
+    return segment->start + (segment->repeat + 1) * segment->duration;
+
+  if (index < segments->len - 1) {
+    const GstMediaSegment *next_segment =
+        g_ptr_array_index (segments, index + 1);
+    end = next_segment->start;
+  } else {
+    stream_period = gst_mpd_client_get_stream_period (client);
+    end = stream_period->start + stream_period->duration;
+  }
+  return end;
+}
+
+static gboolean
+gst_mpd_client_add_media_segment (GstActiveStream * stream,
+    GstSegmentURLNode * url_node, guint number, gint repeat,
+    guint64 scale_start, guint64 scale_duration,
+    GstClockTime start, GstClockTime duration)
+{
+  GstMediaSegment *media_segment;
+
+  g_return_val_if_fail (stream->segments != NULL, FALSE);
+
+  media_segment = g_slice_new0 (GstMediaSegment);
+
+  media_segment->SegmentURL = url_node;
+  media_segment->number = number;
+  media_segment->scale_start = scale_start;
+  media_segment->scale_duration = scale_duration;
+  media_segment->start = start;
+  media_segment->duration = duration;
+  media_segment->repeat = repeat;
+
+  g_ptr_array_add (stream->segments, media_segment);
+  GST_LOG ("Added new segment: number %d, repeat %d, "
+      "ts: %" GST_TIME_FORMAT ", dur: %"
+      GST_TIME_FORMAT, number, repeat,
+      GST_TIME_ARGS (start), GST_TIME_ARGS (duration));
+
+  return TRUE;
+}
+
+static void
+gst_mpd_client_stream_update_presentation_time_offset (GstMpdClient * client,
+    GstActiveStream * stream)
+{
+  GstSegmentBaseType *segbase = NULL;
+
+  /* Find the used segbase */
+  if (stream->cur_segment_list) {
+    segbase = stream->cur_segment_list->MultSegBaseType->SegBaseType;
+  } else if (stream->cur_seg_template) {
+    segbase = stream->cur_seg_template->MultSegBaseType->SegBaseType;
+  } else if (stream->cur_segment_base) {
+    segbase = stream->cur_segment_base;
+  }
+
+  if (segbase) {
+    /* Avoid overflows */
+    stream->presentationTimeOffset =
+        gst_util_uint64_scale (segbase->presentationTimeOffset, GST_SECOND,
+        segbase->timescale);
+  } else {
+    stream->presentationTimeOffset = 0;
+  }
+
+  GST_LOG ("Setting stream's presentation time offset to %" GST_TIME_FORMAT,
+      GST_TIME_ARGS (stream->presentationTimeOffset));
+}
+
+gboolean
+gst_mpd_client_setup_representation (GstMpdClient * client,
+    GstActiveStream * stream, GstRepresentationNode * representation)
+{
+  GstStreamPeriod *stream_period;
+  GList *rep_list;
+  GstClockTime PeriodStart, PeriodEnd, start_time, duration;
+  guint i;
+  guint64 start;
+
+  if (stream->cur_adapt_set == NULL) {
+    GST_WARNING ("No valid AdaptationSet node in the MPD file, aborting...");
+    return FALSE;
+  }
+
+  rep_list = stream->cur_adapt_set->Representations;
+  stream->cur_representation = representation;
+  stream->representation_idx = g_list_index (rep_list, representation);
+
+  /* clean the old segment list, if any */
+  if (stream->segments) {
+    g_ptr_array_unref (stream->segments);
+    stream->segments = NULL;
+  }
+
+  stream_period = gst_mpd_client_get_stream_period (client);
+  g_return_val_if_fail (stream_period != NULL, FALSE);
+  g_return_val_if_fail (stream_period->period != NULL, FALSE);
+
+  PeriodStart = stream_period->start;
+  if (GST_CLOCK_TIME_IS_VALID (stream_period->duration))
+    PeriodEnd = stream_period->start + stream_period->duration;
+  else
+    PeriodEnd = GST_CLOCK_TIME_NONE;
+
+  GST_LOG ("Building segment list for Period from %" GST_TIME_FORMAT " to %"
+      GST_TIME_FORMAT, GST_TIME_ARGS (PeriodStart), GST_TIME_ARGS (PeriodEnd));
+
+  if (representation->SegmentBase != NULL
+      || representation->SegmentList != NULL) {
+    GList *SegmentURL;
+
+    /* We have a fixed list of segments for any of the cases here,
+     * init the segments list */
+    gst_mpdparser_init_active_stream_segments (stream);
+
+    /* get the first segment_base of the selected representation */
+    if ((stream->cur_segment_base =
+            gst_mpd_client_get_segment_base (stream_period->period,
+                stream->cur_adapt_set, representation)) == NULL) {
+      GST_DEBUG ("No useful SegmentBase node for the current Representation");
+    }
+
+    /* get the first segment_list of the selected representation */
+    if ((stream->cur_segment_list =
+            gst_mpd_client_get_segment_list (client, stream_period->period,
+                stream->cur_adapt_set, representation)) == NULL) {
+      GST_DEBUG ("No useful SegmentList node for the current Representation");
+      /* here we should have a single segment for each representation, whose URL is encoded in the baseURL element */
+      if (!gst_mpd_client_add_media_segment (stream, NULL, 1, 0, 0,
+              PeriodEnd - PeriodStart, PeriodStart, PeriodEnd - PeriodStart)) {
+        return FALSE;
+      }
+    } else {
+      /* build the list of GstMediaSegment nodes from the SegmentList node */
+      SegmentURL = stream->cur_segment_list->SegmentURL;
+      if (SegmentURL == NULL) {
+        GST_WARNING
+            ("No valid list of SegmentURL nodes in the MPD file, aborting...");
+        return FALSE;
+      }
+
+      /* build segment list */
+      i = stream->cur_segment_list->MultSegBaseType->startNumber;
+      start = 0;
+      start_time = PeriodStart;
+
+      GST_LOG ("Building media segment list using a SegmentList node");
+      if (stream->cur_segment_list->MultSegBaseType->SegmentTimeline) {
+        GstSegmentTimelineNode *timeline;
+        GstSNode *S;
+        GList *list;
+        GstClockTime presentationTimeOffset;
+        GstSegmentBaseType *segbase;
+
+        segbase = stream->cur_segment_list->MultSegBaseType->SegBaseType;
+        presentationTimeOffset =
+            gst_util_uint64_scale (segbase->presentationTimeOffset, GST_SECOND,
+            segbase->timescale);
+        GST_LOG ("presentationTimeOffset = %" G_GUINT64_FORMAT,
+            presentationTimeOffset);
+
+        timeline = stream->cur_segment_list->MultSegBaseType->SegmentTimeline;
+        for (list = g_queue_peek_head_link (&timeline->S); list;
+            list = g_list_next (list)) {
+          guint timescale;
+
+          S = (GstSNode *) list->data;
+          GST_LOG ("Processing S node: d=%" G_GUINT64_FORMAT " r=%d t=%"
+              G_GUINT64_FORMAT, S->d, S->r, S->t);
+          timescale =
+              stream->cur_segment_list->MultSegBaseType->SegBaseType->timescale;
+          duration = gst_util_uint64_scale (S->d, GST_SECOND, timescale);
+
+          if (S->t > 0) {
+            start = S->t;
+            start_time = gst_util_uint64_scale (S->t, GST_SECOND, timescale)
+                + PeriodStart - presentationTimeOffset;
+          }
+
+          if (!SegmentURL) {
+            GST_WARNING
+                ("SegmentTimeline does not have a matching SegmentURL, aborting...");
+            return FALSE;
+          }
+
+          if (!gst_mpd_client_add_media_segment (stream, SegmentURL->data, i,
+                  S->r, start, S->d, start_time, duration)) {
+            return FALSE;
+          }
+          i += S->r + 1;
+          start_time += duration * (S->r + 1);
+          start += S->d * (S->r + 1);
+          SegmentURL = g_list_next (SegmentURL);
+        }
+      } else {
+        guint64 scale_dur;
+
+        duration =
+            gst_mpd_client_get_segment_duration (client, stream, &scale_dur);
+        if (!GST_CLOCK_TIME_IS_VALID (duration))
+          return FALSE;
+
+        while (SegmentURL) {
+          if (!gst_mpd_client_add_media_segment (stream, SegmentURL->data, i,
+                  0, start, scale_dur, start_time, duration)) {
+            return FALSE;
+          }
+          i++;
+          start += scale_dur;
+          start_time += duration;
+          SegmentURL = g_list_next (SegmentURL);
+        }
+      }
+    }
+  } else {
+    if (representation->SegmentTemplate != NULL) {
+      stream->cur_seg_template = representation->SegmentTemplate;
+    } else if (stream->cur_adapt_set->SegmentTemplate != NULL) {
+      stream->cur_seg_template = stream->cur_adapt_set->SegmentTemplate;
+    } else if (stream_period->period->SegmentTemplate != NULL) {
+      stream->cur_seg_template = stream_period->period->SegmentTemplate;
+    }
+
+    if (stream->cur_seg_template == NULL
+        || stream->cur_seg_template->MultSegBaseType == NULL) {
+
+      gst_mpdparser_init_active_stream_segments (stream);
+      /* here we should have a single segment for each representation, whose URL is encoded in the baseURL element */
+      if (!gst_mpd_client_add_media_segment (stream, NULL, 1, 0, 0,
+              PeriodEnd - PeriodStart, 0, PeriodEnd - PeriodStart)) {
+        return FALSE;
+      }
+    } else {
+      GstClockTime presentationTimeOffset;
+      GstMultSegmentBaseType *mult_seg =
+          stream->cur_seg_template->MultSegBaseType;
+      presentationTimeOffset =
+          gst_util_uint64_scale (mult_seg->SegBaseType->presentationTimeOffset,
+          GST_SECOND, mult_seg->SegBaseType->timescale);
+      GST_LOG ("presentationTimeOffset = %" GST_TIME_FORMAT,
+          GST_TIME_ARGS (presentationTimeOffset));
+      /* build segment list */
+      i = mult_seg->startNumber;
+      start = 0;
+      start_time = 0;
+
+      GST_LOG ("Building media segment list using this template: %s",
+          stream->cur_seg_template->media);
+
+      if (mult_seg->SegmentTimeline) {
+        GstSegmentTimelineNode *timeline;
+        GstSNode *S;
+        GList *list;
+
+        timeline = mult_seg->SegmentTimeline;
+        gst_mpdparser_init_active_stream_segments (stream);
+        for (list = g_queue_peek_head_link (&timeline->S); list;
+            list = g_list_next (list)) {
+          guint timescale;
+
+          S = (GstSNode *) list->data;
+          GST_LOG ("Processing S node: d=%" G_GUINT64_FORMAT " r=%u t=%"
+              G_GUINT64_FORMAT, S->d, S->r, S->t);
+          timescale = mult_seg->SegBaseType->timescale;
+          duration = gst_util_uint64_scale (S->d, GST_SECOND, timescale);
+          if (S->t > 0) {
+            start = S->t;
+            start_time = gst_util_uint64_scale (S->t, GST_SECOND, timescale)
+                + PeriodStart - presentationTimeOffset;
+          }
+
+          if (!gst_mpd_client_add_media_segment (stream, NULL, i, S->r, start,
+                  S->d, start_time, duration)) {
+            return FALSE;
+          }
+          i += S->r + 1;
+          start += S->d * (S->r + 1);
+          start_time += duration * (S->r + 1);
+        }
+      } else {
+        /* NOP - The segment is created on demand with the template, no need
+         * to build a list */
+      }
+    }
+  }
+
+  /* clip duration of segments to stop at period end */
+  if (stream->segments && stream->segments->len) {
+    if (GST_CLOCK_TIME_IS_VALID (PeriodEnd)) {
+      guint n;
+
+      for (n = 0; n < stream->segments->len; ++n) {
+        GstMediaSegment *media_segment =
+            g_ptr_array_index (stream->segments, n);
+        if (media_segment) {
+          if (media_segment->start + media_segment->duration > PeriodEnd) {
+            GstClockTime stop = PeriodEnd;
+            if (n < stream->segments->len - 1) {
+              GstMediaSegment *next_segment =
+                  g_ptr_array_index (stream->segments, n + 1);
+              if (next_segment && next_segment->start < PeriodEnd)
+                stop = next_segment->start;
+            }
+            media_segment->duration =
+                media_segment->start > stop ? 0 : stop - media_segment->start;
+            GST_LOG ("Fixed duration of segment %u: %" GST_TIME_FORMAT, n,
+                GST_TIME_ARGS (media_segment->duration));
+
+            /* If the segment was clipped entirely, we discard it and all
+             * subsequent ones */
+            if (media_segment->duration == 0) {
+              GST_WARNING ("Discarding %u segments outside period",
+                  stream->segments->len - n);
+              /* _set_size should properly unref elements */
+              g_ptr_array_set_size (stream->segments, n);
+              break;
+            }
+          }
+        }
+      }
+    }
+#ifndef GST_DISABLE_GST_DEBUG
+    if (stream->segments->len > 0) {
+      GstMediaSegment *last_media_segment =
+          g_ptr_array_index (stream->segments, stream->segments->len - 1);
+      GST_LOG ("Built a list of %d segments", last_media_segment->number);
+    } else {
+      GST_LOG ("All media segments were clipped");
+    }
+#endif
+  }
+
+  g_free (stream->baseURL);
+  g_free (stream->queryURL);
+  stream->baseURL =
+      gst_mpd_client_parse_baseURL (client, stream, &stream->queryURL);
+
+  gst_mpd_client_stream_update_presentation_time_offset (client, stream);
+
+  return TRUE;
+}
+
+#define CUSTOM_WRAPPER_START "<custom_wrapper>"
+#define CUSTOM_WRAPPER_END "</custom_wrapper>"
+
+static GList *
+gst_mpd_client_fetch_external_periods (GstMpdClient * client,
+    GstPeriodNode * period_node)
+{
+  GstFragment *download;
+  GstAdapter *adapter;
+  GstBuffer *period_buffer;
+  GError *err = NULL;
+
+  GstUri *base_uri, *uri;
+  gchar *query = NULL;
+  gchar *uri_string, *wrapper;
+  GList *new_periods = NULL;
+  const gchar *data;
+
+  /* ISO/IEC 23009-1:2014 5.5.3 4)
+   * Remove nodes that resolve to nothing when resolving
+   */
+  if (strcmp (period_node->xlink_href,
+          "urn:mpeg:dash:resolve-to-zero:2013") == 0) {
+    return NULL;
+  }
+
+  if (!client->downloader) {
+    return NULL;
+  }
+
+  /* Build absolute URI */
+
+  /* Get base URI at the MPD level */
+  base_uri =
+      gst_uri_from_string (client->
+      mpd_base_uri ? client->mpd_base_uri : client->mpd_uri);
+
+  /* combine a BaseURL at the MPD level with the current base url */
+  base_uri = combine_urls (base_uri, client->mpd_node->BaseURLs, &query, 0);
+  uri = gst_uri_from_string_with_base (base_uri, period_node->xlink_href);
+  if (query)
+    gst_uri_set_query_string (uri, query);
+  g_free (query);
+  uri_string = gst_uri_to_string (uri);
+  gst_uri_unref (base_uri);
+  gst_uri_unref (uri);
+
+  download =
+      gst_uri_downloader_fetch_uri (client->downloader,
+      uri_string, client->mpd_uri, TRUE, FALSE, TRUE, &err);
+  g_free (uri_string);
+
+  if (!download) {
+    GST_ERROR ("Failed to download external Period node at '%s': %s",
+        period_node->xlink_href, err->message);
+    g_clear_error (&err);
+    return NULL;
+  }
+
+  period_buffer = gst_fragment_get_buffer (download);
+  g_object_unref (download);
+
+  /* external xml could have multiple period without root xmlNode.
+   * To avoid xml parsing error caused by no root node, wrapping it with
+   * custom root node */
+  adapter = gst_adapter_new ();
+
+  wrapper = g_new (gchar, strlen (CUSTOM_WRAPPER_START));
+  memcpy (wrapper, CUSTOM_WRAPPER_START, strlen (CUSTOM_WRAPPER_START));
+  gst_adapter_push (adapter,
+      gst_buffer_new_wrapped (wrapper, strlen (CUSTOM_WRAPPER_START)));
+
+  gst_adapter_push (adapter, period_buffer);
+
+  wrapper = g_strdup (CUSTOM_WRAPPER_END);
+  gst_adapter_push (adapter,
+      gst_buffer_new_wrapped (wrapper, strlen (CUSTOM_WRAPPER_END) + 1));
+
+  data = gst_adapter_map (adapter, gst_adapter_available (adapter));
+
+  new_periods =
+      gst_mpdparser_get_external_periods (data,
+      gst_adapter_available (adapter));
+
+  gst_adapter_unmap (adapter);
+  gst_adapter_clear (adapter);
+  gst_object_unref (adapter);
+
+  return new_periods;
+}
+
+gboolean
+gst_mpd_client_setup_media_presentation (GstMpdClient * client,
+    GstClockTime time, gint period_idx, const gchar * period_id)
+{
+  GstStreamPeriod *stream_period;
+  GstClockTime start, duration;
+  GList *list, *next;
+  guint idx;
+  gboolean ret = FALSE;
+
+  g_return_val_if_fail (client != NULL, FALSE);
+  g_return_val_if_fail (client->mpd_node != NULL, FALSE);
+
+  /* Check if we set up the media presentation far enough already */
+  for (list = client->periods; list; list = list->next) {
+    GstStreamPeriod *stream_period = list->data;
+
+    if ((time != GST_CLOCK_TIME_NONE
+            && stream_period->duration != GST_CLOCK_TIME_NONE
+            && stream_period->start + stream_period->duration >= time)
+        || (time != GST_CLOCK_TIME_NONE && stream_period->start >= time))
+      return TRUE;
+
+    if (period_idx != -1 && stream_period->number >= period_idx)
+      return TRUE;
+
+    if (period_id != NULL && stream_period->period->id != NULL
+        && strcmp (stream_period->period->id, period_id) == 0)
+      return TRUE;
+
+  }
+
+  GST_DEBUG ("Building the list of Periods in the Media Presentation");
+  /* clean the old period list, if any */
+  /* TODO: In theory we could reuse the ones we have so far but that
+   * seems more complicated than the overhead caused here
+   */
+  if (client->periods) {
+    g_list_foreach (client->periods,
+        (GFunc) gst_mpdparser_free_stream_period, NULL);
+    g_list_free (client->periods);
+    client->periods = NULL;
+  }
+
+  idx = 0;
+  start = 0;
+  duration = GST_CLOCK_TIME_NONE;
+
+  if (client->mpd_node->mediaPresentationDuration <= 0 &&
+      client->mpd_node->mediaPresentationDuration != -1) {
+    /* Invalid MPD file: MPD duration is negative or zero */
+    goto syntax_error;
+  }
+
+  for (list = client->mpd_node->Periods; list; /* explicitly advanced below */ ) {
+    GstPeriodNode *period_node = list->data;
+    GstPeriodNode *next_period_node = NULL;
+
+    /* Download external period */
+    if (period_node->xlink_href) {
+      GList *new_periods;
+      GList *prev;
+
+      new_periods = gst_mpd_client_fetch_external_periods (client, period_node);
+
+      prev = list->prev;
+      client->mpd_node->Periods =
+          g_list_delete_link (client->mpd_node->Periods, list);
+      gst_mpdparser_free_period_node (period_node);
+      period_node = NULL;
+
+      /* Get new next node, we will insert before this */
+      if (prev)
+        next = prev->next;
+      else
+        next = client->mpd_node->Periods;
+
+      while (new_periods) {
+        client->mpd_node->Periods =
+            g_list_insert_before (client->mpd_node->Periods, next,
+            new_periods->data);
+        new_periods = g_list_delete_link (new_periods, new_periods);
+      }
+      next = NULL;
+
+      /* Update our iterator to the first new period if any, or the next */
+      if (prev)
+        list = prev->next;
+      else
+        list = client->mpd_node->Periods;
+
+      /* And try again */
+      continue;
+    }
+
+    if (period_node->start != -1) {
+      /* we have a regular period */
+      /* start cannot be smaller than previous start */
+      if (list != g_list_first (client->mpd_node->Periods)
+          && start >= period_node->start * GST_MSECOND) {
+        /* Invalid MPD file: duration would be negative or zero */
+        goto syntax_error;
+      }
+      start = period_node->start * GST_MSECOND;
+    } else if (duration != GST_CLOCK_TIME_NONE) {
+      /* start time inferred from previous period, this is still a regular period */
+      start += duration;
+    } else if (idx == 0 && client->mpd_node->type == GST_MPD_FILE_TYPE_STATIC) {
+      /* first period of a static MPD file, start time is 0 */
+      start = 0;
+    } else if (client->mpd_node->type == GST_MPD_FILE_TYPE_DYNAMIC) {
+      /* this should be a live stream, let this pass */
+    } else {
+      /* this is an 'Early Available Period' */
+      goto early;
+    }
+
+    /* compute duration.
+       If there is a start time for the next period, or this is the last period
+       and mediaPresentationDuration was set, those values will take precedence
+       over a configured period duration in computing this period's duration
+
+       ISO/IEC 23009-1:2014(E), chapter 5.3.2.1
+       "The Period extends until the PeriodStart of the next Period, or until
+       the end of the Media Presentation in the case of the last Period."
+     */
+
+    while ((next = g_list_next (list)) != NULL) {
+      /* try to infer this period duration from the start time of the next period */
+      next_period_node = next->data;
+
+      if (next_period_node->xlink_href) {
+        GList *new_periods;
+
+        new_periods =
+            gst_mpd_client_fetch_external_periods (client, next_period_node);
+
+        client->mpd_node->Periods =
+            g_list_delete_link (client->mpd_node->Periods, next);
+        gst_mpdparser_free_period_node (next_period_node);
+        next_period_node = NULL;
+        /* Get new next node, we will insert before this */
+        next = g_list_next (list);
+        while (new_periods) {
+          client->mpd_node->Periods =
+              g_list_insert_before (client->mpd_node->Periods, next,
+              new_periods->data);
+          new_periods = g_list_delete_link (new_periods, new_periods);
+        }
+
+        /* And try again, getting the next list element which is now our newly
+         * inserted nodes. If any */
+      } else {
+        /* Got the next period and it doesn't have to be downloaded first */
+        break;
+      }
+    }
+
+    if (next_period_node) {
+      if (next_period_node->start != -1) {
+        if (start >= next_period_node->start * GST_MSECOND) {
+          /* Invalid MPD file: duration would be negative or zero */
+          goto syntax_error;
+        }
+        duration = next_period_node->start * GST_MSECOND - start;
+      } else if (period_node->duration != -1) {
+        if (period_node->duration <= 0) {
+          /* Invalid MPD file: duration would be negative or zero */
+          goto syntax_error;
+        }
+        duration = period_node->duration * GST_MSECOND;
+      } else if (client->mpd_node->type == GST_MPD_FILE_TYPE_DYNAMIC) {
+        /* might be a live file, ignore unspecified duration */
+      } else {
+        /* Invalid MPD file! */
+        goto syntax_error;
+      }
+    } else if (client->mpd_node->mediaPresentationDuration != -1) {
+      /* last Period of the Media Presentation */
+      if (client->mpd_node->mediaPresentationDuration * GST_MSECOND <= start) {
+        /* Invalid MPD file: duration would be negative or zero */
+        goto syntax_error;
+      }
+      duration =
+          client->mpd_node->mediaPresentationDuration * GST_MSECOND - start;
+    } else if (period_node->duration != -1) {
+      duration = period_node->duration * GST_MSECOND;
+    } else if (client->mpd_node->type == GST_MPD_FILE_TYPE_DYNAMIC) {
+      /* might be a live file, ignore unspecified duration */
+    } else {
+      /* Invalid MPD file! */
+      goto syntax_error;
+    }
+
+    stream_period = g_slice_new0 (GstStreamPeriod);
+    client->periods = g_list_append (client->periods, stream_period);
+    stream_period->period = period_node;
+    stream_period->number = idx++;
+    stream_period->start = start;
+    stream_period->duration = duration;
+    ret = TRUE;
+    GST_LOG (" - added Period %d start=%" GST_TIME_FORMAT " duration=%"
+        GST_TIME_FORMAT, idx, GST_TIME_ARGS (start), GST_TIME_ARGS (duration));
+
+    if ((time != GST_CLOCK_TIME_NONE
+            && stream_period->duration != GST_CLOCK_TIME_NONE
+            && stream_period->start + stream_period->duration >= time)
+        || (time != GST_CLOCK_TIME_NONE && stream_period->start >= time))
+      break;
+
+    if (period_idx != -1 && stream_period->number >= period_idx)
+      break;
+
+    if (period_id != NULL && stream_period->period->id != NULL
+        && strcmp (stream_period->period->id, period_id) == 0)
+      break;
+
+    list = list->next;
+  }
+
+  GST_DEBUG
+      ("Found a total of %d valid Periods in the Media Presentation up to this point",
+      idx);
+  return ret;
+
+early:
+  GST_WARNING
+      ("Found an Early Available Period, skipping the rest of the Media Presentation");
+  return ret;
+
+syntax_error:
+  GST_WARNING
+      ("Cannot get the duration of the Period %d, skipping the rest of the Media Presentation",
+      idx);
+  return ret;
+}
+
+static GList *
+gst_mpd_client_fetch_external_adaptation_set (GstMpdClient * client,
+    GstPeriodNode * period, GstAdaptationSetNode * adapt_set)
+{
+  GstFragment *download;
+  GstBuffer *adapt_set_buffer;
+  GstMapInfo map;
+  GError *err = NULL;
+  GstUri *base_uri, *uri;
+  gchar *query = NULL;
+  gchar *uri_string;
+  GList *new_adapt_sets = NULL;
+
+  /* ISO/IEC 23009-1:2014 5.5.3 4)
+   * Remove nodes that resolve to nothing when resolving
+   */
+  if (strcmp (adapt_set->xlink_href, "urn:mpeg:dash:resolve-to-zero:2013") == 0) {
+    return NULL;
+  }
+
+  if (!client->downloader) {
+    return NULL;
+  }
+
+  /* Build absolute URI */
+
+  /* Get base URI at the MPD level */
+  base_uri =
+      gst_uri_from_string (client->
+      mpd_base_uri ? client->mpd_base_uri : client->mpd_uri);
+
+  /* combine a BaseURL at the MPD level with the current base url */
+  base_uri = combine_urls (base_uri, client->mpd_node->BaseURLs, &query, 0);
+
+  /* combine a BaseURL at the Period level with the current base url */
+  base_uri = combine_urls (base_uri, period->BaseURLs, &query, 0);
+
+  uri = gst_uri_from_string_with_base (base_uri, adapt_set->xlink_href);
+  if (query)
+    gst_uri_set_query_string (uri, query);
+  g_free (query);
+  uri_string = gst_uri_to_string (uri);
+  gst_uri_unref (base_uri);
+  gst_uri_unref (uri);
+
+  download =
+      gst_uri_downloader_fetch_uri (client->downloader,
+      uri_string, client->mpd_uri, TRUE, FALSE, TRUE, &err);
+  g_free (uri_string);
+
+  if (!download) {
+    GST_ERROR ("Failed to download external AdaptationSet node at '%s': %s",
+        adapt_set->xlink_href, err->message);
+    g_clear_error (&err);
+    return NULL;
+  }
+
+  adapt_set_buffer = gst_fragment_get_buffer (download);
+  g_object_unref (download);
+
+  gst_buffer_map (adapt_set_buffer, &map, GST_MAP_READ);
+
+  new_adapt_sets =
+      gst_mpdparser_get_external_adaptation_sets ((const gchar *) map.data,
+      map.size, period);
+
+  gst_buffer_unmap (adapt_set_buffer, &map);
+  gst_buffer_unref (adapt_set_buffer);
+
+  return new_adapt_sets;
+}
+
+static GList *
+gst_mpd_client_get_adaptation_sets_for_period (GstMpdClient * client,
+    GstStreamPeriod * period)
+{
+  GList *list;
+
+  g_return_val_if_fail (period != NULL, NULL);
+
+  /* Resolve all external adaptation sets of this period. Every user of
+   * the adaptation sets would need to know the content of all adaptation sets
+   * to decide which one to use, so we have to resolve them all here
+   */
+  for (list = period->period->AdaptationSets; list;
+      /* advanced explicitly below */ ) {
+    GstAdaptationSetNode *adapt_set = (GstAdaptationSetNode *) list->data;
+    GList *new_adapt_sets = NULL, *prev, *next;
+
+    if (!adapt_set->xlink_href) {
+      list = list->next;
+      continue;
+    }
+
+    new_adapt_sets =
+        gst_mpd_client_fetch_external_adaptation_set (client, period->period,
+        adapt_set);
+
+    prev = list->prev;
+    period->period->AdaptationSets =
+        g_list_delete_link (period->period->AdaptationSets, list);
+    gst_mpdparser_free_adaptation_set_node (adapt_set);
+    adapt_set = NULL;
+
+    /* Get new next node, we will insert before this */
+    if (prev)
+      next = prev->next;
+    else
+      next = period->period->AdaptationSets;
+
+    while (new_adapt_sets) {
+      period->period->AdaptationSets =
+          g_list_insert_before (period->period->AdaptationSets, next,
+          new_adapt_sets->data);
+      new_adapt_sets = g_list_delete_link (new_adapt_sets, new_adapt_sets);
+    }
+
+    /* Update our iterator to the first new adaptation set if any, or the next */
+    if (prev)
+      list = prev->next;
+    else
+      list = period->period->AdaptationSets;
+  }
+
+  return period->period->AdaptationSets;
+}
+
+GList *
+gst_mpd_client_get_adaptation_sets (GstMpdClient * client)
+{
+  GstStreamPeriod *stream_period;
+
+  stream_period = gst_mpd_client_get_stream_period (client);
+  if (stream_period == NULL || stream_period->period == NULL) {
+    GST_DEBUG ("No more Period nodes in the MPD file, terminating...");
+    return NULL;
+  }
+
+  return gst_mpd_client_get_adaptation_sets_for_period (client, stream_period);
+}
+
+gboolean
+gst_mpd_client_setup_streaming (GstMpdClient * client,
+    GstAdaptationSetNode * adapt_set)
+{
+  GstRepresentationNode *representation;
+  GList *rep_list = NULL;
+  GstActiveStream *stream;
+
+  rep_list = adapt_set->Representations;
+  if (!rep_list) {
+    GST_WARNING ("Can not retrieve any representation, aborting...");
+    return FALSE;
+  }
+
+  stream = g_slice_new0 (GstActiveStream);
+  gst_mpdparser_init_active_stream_segments (stream);
+
+  stream->baseURL_idx = 0;
+  stream->cur_adapt_set = adapt_set;
+
+  GST_DEBUG ("0. Current stream %p", stream);
+
+#if 0
+  /* fast start */
+  representation =
+      gst_mpdparser_get_representation_with_max_bandwidth (rep_list,
+      stream->max_bandwidth);
+
+  if (!representation) {
+    GST_WARNING
+        ("Can not retrieve a representation with the requested bandwidth");
+    representation = gst_mpd_client_get_lowest_representation (rep_list);
+  }
+#else
+  /* slow start */
+  representation = gst_mpd_client_get_lowest_representation (rep_list);
+#endif
+
+  if (!representation) {
+    GST_WARNING ("No valid representation in the MPD file, aborting...");
+    gst_mpdparser_free_active_stream (stream);
+    return FALSE;
+  }
+  stream->mimeType =
+      gst_mpdparser_representation_get_mimetype (adapt_set, representation);
+  if (stream->mimeType == GST_STREAM_UNKNOWN) {
+    GST_WARNING ("Unknown mime type in the representation, aborting...");
+    gst_mpdparser_free_active_stream (stream);
+    return FALSE;
+  }
+
+  client->active_streams = g_list_append (client->active_streams, stream);
+  if (!gst_mpd_client_setup_representation (client, stream, representation)) {
+    GST_WARNING ("Failed to setup the representation, aborting...");
+    return FALSE;
+  }
+
+  GST_INFO ("Successfully setup the download pipeline for mimeType %d",
+      stream->mimeType);
+
+  return TRUE;
+}
+
+gboolean
+gst_mpd_client_stream_seek (GstMpdClient * client, GstActiveStream * stream,
+    gboolean forward, GstSeekFlags flags, GstClockTime ts,
+    GstClockTime * final_ts)
+{
+  gint index = 0;
+  gint repeat_index = 0;
+  GstMediaSegment *selectedChunk = NULL;
+
+  g_return_val_if_fail (stream != NULL, 0);
+
+  if (stream->segments) {
+    for (index = 0; index < stream->segments->len; index++) {
+      gboolean in_segment = FALSE;
+      GstMediaSegment *segment = g_ptr_array_index (stream->segments, index);
+      GstClockTime end_time;
+
+      GST_DEBUG ("Looking at fragment sequence chunk %d / %d", index,
+          stream->segments->len);
+
+      end_time =
+          gst_mpd_client_get_segment_end_time (client, stream->segments,
+          segment, index);
+
+      /* avoid downloading another fragment just for 1ns in reverse mode */
+      if (forward)
+        in_segment = ts < end_time;
+      else
+        in_segment = ts <= end_time;
+
+      if (in_segment) {
+        GstClockTime chunk_time;
+
+        selectedChunk = segment;
+        repeat_index = (ts - segment->start) / segment->duration;
+
+        chunk_time = segment->start + segment->duration * repeat_index;
+
+        /* At the end of a segment in reverse mode, start from the previous fragment */
+        if (!forward && repeat_index > 0
+            && ((ts - segment->start) % segment->duration == 0))
+          repeat_index--;
+
+        if ((flags & GST_SEEK_FLAG_SNAP_NEAREST) == GST_SEEK_FLAG_SNAP_NEAREST) {
+          if (repeat_index + 1 < segment->repeat) {
+            if (ts - chunk_time > chunk_time + segment->duration - ts)
+              repeat_index++;
+          } else if (index + 1 < stream->segments->len) {
+            GstMediaSegment *next_segment =
+                g_ptr_array_index (stream->segments, index + 1);
+
+            if (ts - chunk_time > next_segment->start - ts) {
+              repeat_index = 0;
+              selectedChunk = next_segment;
+              index++;
+            }
+          }
+        } else if (((forward && flags & GST_SEEK_FLAG_SNAP_AFTER) ||
+                (!forward && flags & GST_SEEK_FLAG_SNAP_BEFORE)) &&
+            ts != chunk_time) {
+
+          if (repeat_index + 1 < segment->repeat) {
+            repeat_index++;
+          } else {
+            repeat_index = 0;
+            if (index + 1 >= stream->segments->len) {
+              selectedChunk = NULL;
+            } else {
+              selectedChunk = g_ptr_array_index (stream->segments, ++index);
+            }
+          }
+        }
+        break;
+      }
+    }
+
+    if (selectedChunk == NULL) {
+      stream->segment_index = stream->segments->len;
+      stream->segment_repeat_index = 0;
+      GST_DEBUG ("Seek to after last segment");
+      return FALSE;
+    }
+
+    if (final_ts)
+      *final_ts = selectedChunk->start + selectedChunk->duration * repeat_index;
+  } else {
+    GstClockTime duration =
+        gst_mpd_client_get_segment_duration (client, stream, NULL);
+    GstStreamPeriod *stream_period = gst_mpd_client_get_stream_period (client);
+    guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
+    GstClockTime index_time;
+
+    g_return_val_if_fail (stream->cur_seg_template->
+        MultSegBaseType->SegmentTimeline == NULL, FALSE);
+    if (!GST_CLOCK_TIME_IS_VALID (duration)) {
+      return FALSE;
+    }
+
+    if (ts > stream_period->start)
+      ts -= stream_period->start;
+    else
+      ts = 0;
+
+    index = ts / duration;
+
+    /* At the end of a segment in reverse mode, start from the previous fragment */
+    if (!forward && index > 0 && ts % duration == 0)
+      index--;
+
+    index_time = index * duration;
+
+    if ((flags & GST_SEEK_FLAG_SNAP_NEAREST) == GST_SEEK_FLAG_SNAP_NEAREST) {
+      if (ts - index_time > index_time + duration - ts)
+        index++;
+    } else if (((forward && flags & GST_SEEK_FLAG_SNAP_AFTER) ||
+            (!forward && flags & GST_SEEK_FLAG_SNAP_BEFORE))
+        && ts != index_time) {
+      index++;
+    }
+
+    if (segments_count > 0 && index >= segments_count) {
+      stream->segment_index = segments_count;
+      stream->segment_repeat_index = 0;
+      GST_DEBUG ("Seek to after last segment");
+      return FALSE;
+    }
+    if (final_ts)
+      *final_ts = index * duration;
+  }
+
+  stream->segment_repeat_index = repeat_index;
+  stream->segment_index = index;
+
+  return TRUE;
+}
+
+gint64
+gst_mpd_client_calculate_time_difference (const GstDateTime * t1,
+    const GstDateTime * t2)
+{
+  GDateTime *gdt1, *gdt2;
+  GTimeSpan diff;
+
+  g_assert (t1 != NULL && t2 != NULL);
+  gdt1 = gst_date_time_to_g_date_time ((GstDateTime *) t1);
+  gdt2 = gst_date_time_to_g_date_time ((GstDateTime *) t2);
+  diff = g_date_time_difference (gdt2, gdt1);
+  g_date_time_unref (gdt1);
+  g_date_time_unref (gdt2);
+  return diff * GST_USECOND;
+}
+
+GstDateTime *
+gst_mpd_client_add_time_difference (GstDateTime * t1, gint64 usecs)
+{
+  GDateTime *gdt;
+  GDateTime *gdt2;
+  GstDateTime *rv;
+
+  g_assert (t1 != NULL);
+  gdt = gst_date_time_to_g_date_time (t1);
+  g_assert (gdt != NULL);
+  gdt2 = g_date_time_add (gdt, usecs);
+  g_assert (gdt2 != NULL);
+  g_date_time_unref (gdt);
+  rv = gst_date_time_new_from_g_date_time (gdt2);
+
+  /* Don't g_date_time_unref(gdt2) because gst_date_time_new_from_g_date_time takes
+   * ownership of the GDateTime pointer.
+   */
+
+  return rv;
+}
+
+gboolean
+gst_mpd_client_get_last_fragment_timestamp_end (GstMpdClient * client,
+    guint stream_idx, GstClockTime * ts)
+{
+  GstActiveStream *stream;
+  gint segment_idx;
+  GstMediaSegment *currentChunk;
+  GstStreamPeriod *stream_period;
+
+  GST_DEBUG ("Stream index: %i", stream_idx);
+  stream = g_list_nth_data (client->active_streams, stream_idx);
+  g_return_val_if_fail (stream != NULL, 0);
+
+  if (!stream->segments) {
+    stream_period = gst_mpd_client_get_stream_period (client);
+    *ts = stream_period->start + stream_period->duration;
+  } else {
+    segment_idx = gst_mpd_client_get_segments_counts (client, stream) - 1;
+    if (segment_idx >= stream->segments->len) {
+      GST_WARNING ("Segment index %d is outside of segment list of length %d",
+          segment_idx, stream->segments->len);
+      return FALSE;
+    }
+    currentChunk = g_ptr_array_index (stream->segments, segment_idx);
+
+    if (currentChunk->repeat >= 0) {
+      *ts =
+          currentChunk->start + (currentChunk->duration * (1 +
+              currentChunk->repeat));
+    } else {
+      /* 5.3.9.6.1: negative repeat means repeat till the end of the
+       * period, or the next update of the MPD (which I think is
+       * implicit, as this will all get deleted/recreated), or the
+       * start of the next segment, if any. */
+      stream_period = gst_mpd_client_get_stream_period (client);
+      *ts = stream_period->start + stream_period->duration;
+    }
+  }
+
+  return TRUE;
+}
+
+gboolean
+gst_mpd_client_get_next_fragment_timestamp (GstMpdClient * client,
+    guint stream_idx, GstClockTime * ts)
+{
+  GstActiveStream *stream;
+  GstMediaSegment *currentChunk;
+
+  GST_DEBUG ("Stream index: %i", stream_idx);
+  stream = g_list_nth_data (client->active_streams, stream_idx);
+  g_return_val_if_fail (stream != NULL, 0);
+
+  if (stream->segments) {
+    GST_DEBUG ("Looking for fragment sequence chunk %d / %d",
+        stream->segment_index, stream->segments->len);
+    if (stream->segment_index >= stream->segments->len)
+      return FALSE;
+    currentChunk = g_ptr_array_index (stream->segments, stream->segment_index);
+
+    *ts =
+        currentChunk->start +
+        (currentChunk->duration * stream->segment_repeat_index);
+  } else {
+    GstClockTime duration =
+        gst_mpd_client_get_segment_duration (client, stream, NULL);
+    guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
+
+    g_return_val_if_fail (stream->cur_seg_template->
+        MultSegBaseType->SegmentTimeline == NULL, FALSE);
+    if (!GST_CLOCK_TIME_IS_VALID (duration) || (segments_count > 0
+            && stream->segment_index >= segments_count)) {
+      return FALSE;
+    }
+    *ts = stream->segment_index * duration;
+  }
+
+  return TRUE;
+}
+
+GstClockTime
+gst_mpd_client_get_stream_presentation_offset (GstMpdClient * client,
+    guint stream_idx)
+{
+  GstActiveStream *stream = NULL;
+
+  g_return_val_if_fail (client != NULL, 0);
+  g_return_val_if_fail (client->active_streams != NULL, 0);
+  stream = g_list_nth_data (client->active_streams, stream_idx);
+  g_return_val_if_fail (stream != NULL, 0);
+
+  return stream->presentationTimeOffset;
+}
+
+GstClockTime
+gst_mpd_client_get_period_start_time (GstMpdClient * client)
+{
+  GstStreamPeriod *stream_period = NULL;
+
+  g_return_val_if_fail (client != NULL, 0);
+  stream_period = gst_mpd_client_get_stream_period (client);
+  g_return_val_if_fail (stream_period != NULL, 0);
+
+  return stream_period->start;
+}
+
+/**
+ * gst_mpd_client_get_utc_timing_sources:
+ * @client: #GstMpdClient to check for UTCTiming elements
+ * @methods: A bit mask of #GstMPDUTCTimingType that specifies the methods
+ *     to search for.
+ * @selected_method: (nullable): The selected method
+ * Returns: (transfer none): A NULL terminated array of URLs of servers
+ *     that use @selected_method to provide a realtime clock.
+ *
+ * Searches the UTCTiming elements found in the manifest for an element
+ * that uses one of the UTC timing methods specified in @selected_method.
+ * If multiple UTCTiming elements are present that support one of the
+ * methods specified in @selected_method, the first one is returned.
+ *
+ * Since: 1.6
+ */
+gchar **
+gst_mpd_client_get_utc_timing_sources (GstMpdClient * client,
+    guint methods, GstMPDUTCTimingType * selected_method)
+{
+  GList *list;
+
+  g_return_val_if_fail (client != NULL, NULL);
+  g_return_val_if_fail (client->mpd_node != NULL, NULL);
+  for (list = g_list_first (client->mpd_node->UTCTiming); list;
+      list = g_list_next (list)) {
+    const GstUTCTimingNode *node = (const GstUTCTimingNode *) list->data;
+    if (node->method & methods) {
+      if (selected_method) {
+        *selected_method = node->method;
+      }
+      return node->urls;
+    }
+  }
+  return NULL;
+}
+
+
+gboolean
+gst_mpd_client_get_next_fragment (GstMpdClient * client,
+    guint indexStream, GstMediaFragmentInfo * fragment)
+{
+  GstActiveStream *stream = NULL;
+  GstMediaSegment *currentChunk;
+  gchar *mediaURL = NULL;
+  gchar *indexURL = NULL;
+  GstUri *base_url, *frag_url;
+
+  /* select stream */
+  g_return_val_if_fail (client != NULL, FALSE);
+  g_return_val_if_fail (client->active_streams != NULL, FALSE);
+  stream = g_list_nth_data (client->active_streams, indexStream);
+  g_return_val_if_fail (stream != NULL, FALSE);
+  g_return_val_if_fail (stream->cur_representation != NULL, FALSE);
+
+  if (stream->segments) {
+    GST_DEBUG ("Looking for fragment sequence chunk %d / %d",
+        stream->segment_index, stream->segments->len);
+    if (stream->segment_index >= stream->segments->len)
+      return FALSE;
+  } else {
+    GstClockTime duration = gst_mpd_client_get_segment_duration (client,
+        stream, NULL);
+    guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
+
+    g_return_val_if_fail (stream->cur_seg_template->
+        MultSegBaseType->SegmentTimeline == NULL, FALSE);
+    if (!GST_CLOCK_TIME_IS_VALID (duration) || (segments_count > 0
+            && stream->segment_index >= segments_count)) {
+      return FALSE;
+    }
+    fragment->duration = duration;
+  }
+
+  /* FIXME rework discont checking */
+  /* fragment->discontinuity = segment_idx != currentChunk.number; */
+  fragment->range_start = 0;
+  fragment->range_end = -1;
+  fragment->index_uri = NULL;
+  fragment->index_range_start = 0;
+  fragment->index_range_end = -1;
+
+  if (stream->segments) {
+    currentChunk = g_ptr_array_index (stream->segments, stream->segment_index);
+
+    GST_DEBUG ("currentChunk->SegmentURL = %p", currentChunk->SegmentURL);
+    if (currentChunk->SegmentURL != NULL) {
+      mediaURL =
+          g_strdup (gst_mpdparser_get_mediaURL (stream,
+              currentChunk->SegmentURL));
+      indexURL = g_strdup (currentChunk->SegmentURL->index);
+    } else if (stream->cur_seg_template != NULL) {
+      mediaURL =
+          gst_mpdparser_build_URL_from_template (stream->
+          cur_seg_template->media, stream->cur_representation->id,
+          currentChunk->number + stream->segment_repeat_index,
+          stream->cur_representation->bandwidth,
+          currentChunk->scale_start +
+          stream->segment_repeat_index * currentChunk->scale_duration);
+      if (stream->cur_seg_template->index) {
+        indexURL =
+            gst_mpdparser_build_URL_from_template (stream->
+            cur_seg_template->index, stream->cur_representation->id,
+            currentChunk->number + stream->segment_repeat_index,
+            stream->cur_representation->bandwidth,
+            currentChunk->scale_start +
+            stream->segment_repeat_index * currentChunk->scale_duration);
+      }
+    }
+    GST_DEBUG ("mediaURL = %s", mediaURL);
+    GST_DEBUG ("indexURL = %s", indexURL);
+
+    fragment->timestamp =
+        currentChunk->start +
+        stream->segment_repeat_index * currentChunk->duration;
+    fragment->duration = currentChunk->duration;
+    if (currentChunk->SegmentURL) {
+      if (currentChunk->SegmentURL->mediaRange) {
+        fragment->range_start =
+            currentChunk->SegmentURL->mediaRange->first_byte_pos;
+        fragment->range_end =
+            currentChunk->SegmentURL->mediaRange->last_byte_pos;
+      }
+      if (currentChunk->SegmentURL->indexRange) {
+        fragment->index_range_start =
+            currentChunk->SegmentURL->indexRange->first_byte_pos;
+        fragment->index_range_end =
+            currentChunk->SegmentURL->indexRange->last_byte_pos;
+      }
+    }
+  } else {
+    if (stream->cur_seg_template != NULL) {
+      mediaURL =
+          gst_mpdparser_build_URL_from_template (stream->
+          cur_seg_template->media, stream->cur_representation->id,
+          stream->segment_index +
+          stream->cur_seg_template->MultSegBaseType->startNumber,
+          stream->cur_representation->bandwidth,
+          stream->segment_index * fragment->duration);
+      if (stream->cur_seg_template->index) {
+        indexURL =
+            gst_mpdparser_build_URL_from_template (stream->
+            cur_seg_template->index, stream->cur_representation->id,
+            stream->segment_index +
+            stream->cur_seg_template->MultSegBaseType->startNumber,
+            stream->cur_representation->bandwidth,
+            stream->segment_index * fragment->duration);
+      }
+    } else {
+      return FALSE;
+    }
+
+    GST_DEBUG ("mediaURL = %s", mediaURL);
+    GST_DEBUG ("indexURL = %s", indexURL);
+
+    fragment->timestamp = stream->segment_index * fragment->duration;
+  }
+
+  base_url = gst_uri_from_string (stream->baseURL);
+  frag_url = gst_uri_from_string_with_base (base_url, mediaURL);
+  g_free (mediaURL);
+  if (stream->queryURL) {
+    frag_url = gst_uri_make_writable (frag_url);
+    gst_uri_set_query_string (frag_url, stream->queryURL);
+  }
+  fragment->uri = gst_uri_to_string (frag_url);
+  gst_uri_unref (frag_url);
+
+  if (indexURL != NULL) {
+    frag_url = gst_uri_make_writable (gst_uri_from_string_with_base (base_url,
+            indexURL));
+    gst_uri_set_query_string (frag_url, stream->queryURL);
+    fragment->index_uri = gst_uri_to_string (frag_url);
+    gst_uri_unref (frag_url);
+    g_free (indexURL);
+  } else if (indexURL == NULL && (fragment->index_range_start
+          || fragment->index_range_end != -1)) {
+    /* index has no specific URL but has a range, we should only use this if
+     * the media also has a range, otherwise we are serving some data twice
+     * (in the media fragment and again in the index) */
+    if (!(fragment->range_start || fragment->range_end != -1)) {
+      GST_WARNING ("Ignoring index ranges because there isn't a media range "
+          "and URIs would be the same");
+      /* removing index information */
+      fragment->index_range_start = 0;
+      fragment->index_range_end = -1;
+    }
+  }
+
+  gst_uri_unref (base_url);
+
+  GST_DEBUG ("Loading chunk with URL %s", fragment->uri);
+
+  return TRUE;
+}
+
+gboolean
+gst_mpd_client_has_next_segment (GstMpdClient * client,
+    GstActiveStream * stream, gboolean forward)
+{
+  if (forward) {
+    guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
+
+    if (segments_count > 0 && stream->segments
+        && stream->segment_index + 1 == segments_count) {
+      GstMediaSegment *segment;
+
+      segment = g_ptr_array_index (stream->segments, stream->segment_index);
+      if (segment->repeat >= 0
+          && stream->segment_repeat_index >= segment->repeat)
+        return FALSE;
+    } else if (segments_count > 0
+        && stream->segment_index + 1 >= segments_count) {
+      return FALSE;
+    }
+  } else {
+    if (stream->segment_index < 0)
+      return FALSE;
+  }
+
+  return TRUE;
+}
+
+GstFlowReturn
+gst_mpd_client_advance_segment (GstMpdClient * client, GstActiveStream * stream,
+    gboolean forward)
+{
+  GstMediaSegment *segment;
+  GstFlowReturn ret = GST_FLOW_OK;
+  guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
+
+  GST_DEBUG ("Advancing segment. Current: %d / %d r:%d", stream->segment_index,
+      segments_count, stream->segment_repeat_index);
+
+  /* handle special cases first */
+  if (forward) {
+    if (segments_count > 0 && stream->segment_index >= segments_count) {
+      ret = GST_FLOW_EOS;
+      goto done;
+    }
+
+    if (stream->segments == NULL) {
+      if (stream->segment_index < 0) {
+        stream->segment_index = 0;
+      } else {
+        stream->segment_index++;
+        if (segments_count > 0 && stream->segment_index >= segments_count) {
+          ret = GST_FLOW_EOS;
+        }
+      }
+      goto done;
+    }
+
+    /* special case for when playback direction is reverted right at *
+     * the end of the segment list */
+    if (stream->segment_index < 0) {
+      stream->segment_index = 0;
+      goto done;
+    }
+  } else {
+    if (stream->segments == NULL)
+      stream->segment_index--;
+    if (stream->segment_index < 0) {
+      stream->segment_index = -1;
+      ret = GST_FLOW_EOS;
+      goto done;
+    }
+    if (stream->segments == NULL)
+      goto done;
+
+    /* special case for when playback direction is reverted right at *
+     * the end of the segment list */
+    if (stream->segment_index >= segments_count) {
+      stream->segment_index = segments_count - 1;
+      segment = g_ptr_array_index (stream->segments, stream->segment_index);
+      if (segment->repeat >= 0) {
+        stream->segment_repeat_index = segment->repeat;
+      } else {
+        GstClockTime start = segment->start;
+        GstClockTime end =
+            gst_mpd_client_get_segment_end_time (client, stream->segments,
+            segment,
+            stream->segment_index);
+        stream->segment_repeat_index =
+            (guint) (end - start) / segment->duration;
+      }
+      goto done;
+    }
+  }
+
+  /* for the normal cases we can get the segment safely here */
+  segment = g_ptr_array_index (stream->segments, stream->segment_index);
+  if (forward) {
+    if (segment->repeat >= 0 && stream->segment_repeat_index >= segment->repeat) {
+      stream->segment_repeat_index = 0;
+      stream->segment_index++;
+      if (segments_count > 0 && stream->segment_index >= segments_count) {
+        ret = GST_FLOW_EOS;
+        goto done;
+      }
+    } else {
+      stream->segment_repeat_index++;
+    }
+  } else {
+    if (stream->segment_repeat_index == 0) {
+      stream->segment_index--;
+      if (stream->segment_index < 0) {
+        ret = GST_FLOW_EOS;
+        goto done;
+      }
+
+      segment = g_ptr_array_index (stream->segments, stream->segment_index);
+      /* negative repeats only seem to make sense at the end of a list,
+       * so this one will probably not be. Needs some sanity checking
+       * when loading the XML data. */
+      if (segment->repeat >= 0) {
+        stream->segment_repeat_index = segment->repeat;
+      } else {
+        GstClockTime start = segment->start;
+        GstClockTime end =
+            gst_mpd_client_get_segment_end_time (client, stream->segments,
+            segment,
+            stream->segment_index);
+        stream->segment_repeat_index =
+            (guint) (end - start) / segment->duration;
+      }
+    } else {
+      stream->segment_repeat_index--;
+    }
+  }
+
+done:
+  GST_DEBUG ("Advanced to segment: %d / %d r:%d (ret: %s)",
+      stream->segment_index, segments_count,
+      stream->segment_repeat_index, gst_flow_get_name (ret));
+  return ret;
+}
+
+gboolean
+gst_mpd_client_get_next_header (GstMpdClient * client, gchar ** uri,
+    guint stream_idx, gint64 * range_start, gint64 * range_end)
+{
+  GstActiveStream *stream;
+  GstStreamPeriod *stream_period;
+
+  stream = gst_mpd_client_get_active_stream_by_index (client, stream_idx);
+  g_return_val_if_fail (stream != NULL, FALSE);
+  g_return_val_if_fail (stream->cur_representation != NULL, FALSE);
+  stream_period = gst_mpd_client_get_stream_period (client);
+  g_return_val_if_fail (stream_period != NULL, FALSE);
+  g_return_val_if_fail (stream_period->period != NULL, FALSE);
+
+  *range_start = 0;
+  *range_end = -1;
+
+  GST_DEBUG ("Looking for current representation header");
+  *uri = NULL;
+  if (stream->cur_segment_base) {
+    if (stream->cur_segment_base->Initialization) {
+      *uri =
+          g_strdup (gst_mpdparser_get_initializationURL (stream,
+              stream->cur_segment_base->Initialization));
+      if (stream->cur_segment_base->Initialization->range) {
+        *range_start =
+            stream->cur_segment_base->Initialization->range->first_byte_pos;
+        *range_end =
+            stream->cur_segment_base->Initialization->range->last_byte_pos;
+      }
+    } else if (stream->cur_segment_base->indexRange) {
+      *uri =
+          g_strdup (gst_mpdparser_get_initializationURL (stream,
+              stream->cur_segment_base->Initialization));
+      *range_start = 0;
+      *range_end = stream->cur_segment_base->indexRange->first_byte_pos - 1;
+    }
+  } else if (stream->cur_seg_template
+      && stream->cur_seg_template->initialization) {
+    *uri =
+        gst_mpdparser_build_URL_from_template (stream->
+        cur_seg_template->initialization, stream->cur_representation->id, 0,
+        stream->cur_representation->bandwidth, 0);
+  }
+
+  return *uri == NULL ? FALSE : TRUE;
+}
+
+gboolean
+gst_mpd_client_get_next_header_index (GstMpdClient * client, gchar ** uri,
+    guint stream_idx, gint64 * range_start, gint64 * range_end)
+{
+  GstActiveStream *stream;
+  GstStreamPeriod *stream_period;
+
+  stream = gst_mpd_client_get_active_stream_by_index (client, stream_idx);
+  g_return_val_if_fail (stream != NULL, FALSE);
+  g_return_val_if_fail (stream->cur_representation != NULL, FALSE);
+  stream_period = gst_mpd_client_get_stream_period (client);
+  g_return_val_if_fail (stream_period != NULL, FALSE);
+  g_return_val_if_fail (stream_period->period != NULL, FALSE);
+
+  *range_start = 0;
+  *range_end = -1;
+
+  GST_DEBUG ("Looking for current representation index");
+  *uri = NULL;
+  if (stream->cur_segment_base && stream->cur_segment_base->indexRange) {
+    *uri =
+        g_strdup (gst_mpdparser_get_initializationURL (stream,
+            stream->cur_segment_base->RepresentationIndex));
+    *range_start = stream->cur_segment_base->indexRange->first_byte_pos;
+    *range_end = stream->cur_segment_base->indexRange->last_byte_pos;
+  } else if (stream->cur_seg_template && stream->cur_seg_template->index) {
+    *uri =
+        gst_mpdparser_build_URL_from_template (stream->cur_seg_template->index,
+        stream->cur_representation->id, 0,
+        stream->cur_representation->bandwidth, 0);
+  }
+
+  return *uri == NULL ? FALSE : TRUE;
+}
+
+GstClockTime
+gst_mpd_client_get_next_fragment_duration (GstMpdClient * client,
+    GstActiveStream * stream)
+{
+  GstMediaSegment *media_segment = NULL;
+  gint seg_idx;
+
+  g_return_val_if_fail (stream != NULL, 0);
+
+  seg_idx = stream->segment_index;
+
+  if (stream->segments) {
+    if (seg_idx < stream->segments->len && seg_idx >= 0)
+      media_segment = g_ptr_array_index (stream->segments, seg_idx);
+
+    return media_segment == NULL ? 0 : media_segment->duration;
+  } else {
+    GstClockTime duration =
+        gst_mpd_client_get_segment_duration (client, stream, NULL);
+    guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
+
+    g_return_val_if_fail (stream->cur_seg_template->MultSegBaseType->
+        SegmentTimeline == NULL, 0);
+
+    if (!GST_CLOCK_TIME_IS_VALID (duration) || (segments_count > 0
+            && seg_idx >= segments_count)) {
+      return 0;
+    }
+    return duration;
+  }
+}
+
+GstClockTime
+gst_mpd_client_get_media_presentation_duration (GstMpdClient * client)
+{
+  GstClockTime duration;
+
+  g_return_val_if_fail (client != NULL, GST_CLOCK_TIME_NONE);
+
+  if (client->mpd_node->mediaPresentationDuration != -1) {
+    duration = client->mpd_node->mediaPresentationDuration * GST_MSECOND;
+  } else {
+    /* We can only get the duration for on-demand streams */
+    duration = GST_CLOCK_TIME_NONE;
+  }
+
+  return duration;
+}
+
+gboolean
+gst_mpd_client_set_period_id (GstMpdClient * client, const gchar * period_id)
+{
+  GstStreamPeriod *next_stream_period;
+  gboolean ret = FALSE;
+  GList *iter;
+  guint period_idx;
+
+  g_return_val_if_fail (client != NULL, FALSE);
+  g_return_val_if_fail (client->periods != NULL, FALSE);
+  g_return_val_if_fail (period_id != NULL, FALSE);
+
+  if (!gst_mpd_client_setup_media_presentation (client, GST_CLOCK_TIME_NONE, -1,
+          period_id))
+    return FALSE;
+
+  for (period_idx = 0, iter = client->periods; iter;
+      period_idx++, iter = g_list_next (iter)) {
+    next_stream_period = iter->data;
+
+    if (next_stream_period->period->id
+        && strcmp (next_stream_period->period->id, period_id) == 0) {
+      ret = TRUE;
+      client->period_idx = period_idx;
+      break;
+    }
+  }
+
+  return ret;
+}
+
+gboolean
+gst_mpd_client_set_period_index (GstMpdClient * client, guint period_idx)
+{
+  GstStreamPeriod *next_stream_period;
+  gboolean ret = FALSE;
+
+  g_return_val_if_fail (client != NULL, FALSE);
+  g_return_val_if_fail (client->periods != NULL, FALSE);
+
+  if (!gst_mpd_client_setup_media_presentation (client, -1, period_idx, NULL))
+    return FALSE;
+
+  next_stream_period = g_list_nth_data (client->periods, period_idx);
+  if (next_stream_period != NULL) {
+    client->period_idx = period_idx;
+    ret = TRUE;
+  }
+
+  return ret;
+}
+
+guint
+gst_mpd_client_get_period_index (GstMpdClient * client)
+{
+  guint period_idx;
+
+  g_return_val_if_fail (client != NULL, 0);
+  period_idx = client->period_idx;
+
+  return period_idx;
+}
+
+const gchar *
+gst_mpd_client_get_period_id (GstMpdClient * client)
+{
+  GstStreamPeriod *period;
+  gchar *period_id = NULL;
+
+  g_return_val_if_fail (client != NULL, 0);
+  period = g_list_nth_data (client->periods, client->period_idx);
+  if (period && period->period)
+    period_id = period->period->id;
+
+  return period_id;
+}
+
+gboolean
+gst_mpd_client_has_next_period (GstMpdClient * client)
+{
+  GList *next_stream_period;
+  g_return_val_if_fail (client != NULL, FALSE);
+  g_return_val_if_fail (client->periods != NULL, FALSE);
+
+  if (!gst_mpd_client_setup_media_presentation (client, GST_CLOCK_TIME_NONE,
+          client->period_idx + 1, NULL))
+    return FALSE;
+
+  next_stream_period =
+      g_list_nth_data (client->periods, client->period_idx + 1);
+  return next_stream_period != NULL;
+}
+
+gboolean
+gst_mpd_client_has_previous_period (GstMpdClient * client)
+{
+  GList *next_stream_period;
+  g_return_val_if_fail (client != NULL, FALSE);
+  g_return_val_if_fail (client->periods != NULL, FALSE);
+
+  if (!gst_mpd_client_setup_media_presentation (client, GST_CLOCK_TIME_NONE,
+          client->period_idx - 1, NULL))
+    return FALSE;
+
+  next_stream_period =
+      g_list_nth_data (client->periods, client->period_idx - 1);
+
+  return next_stream_period != NULL;
+}
+
+gint
+gst_mpd_client_get_rep_idx_with_min_bandwidth (GList * Representations)
+{
+  GList *list = NULL, *lowest = NULL;
+  GstRepresentationNode *rep = NULL;
+  gint lowest_bandwidth = -1;
+
+  if (Representations == NULL)
+    return -1;
+
+  for (list = g_list_first (Representations); list; list = g_list_next (list)) {
+    rep = (GstRepresentationNode *) list->data;
+    if (rep && (!lowest || rep->bandwidth < lowest_bandwidth)) {
+      lowest = list;
+      lowest_bandwidth = rep->bandwidth;
+    }
+  }
+
+  return lowest ? g_list_position (Representations, lowest) : -1;
+}
+
+gint
+gst_mpd_client_get_rep_idx_with_max_bandwidth (GList * Representations,
+    gint64 max_bandwidth, gint max_video_width, gint max_video_height, gint
+    max_video_framerate_n, gint max_video_framerate_d)
+{
+  GList *list = NULL, *best = NULL;
+  GstRepresentationNode *representation;
+  gint best_bandwidth = 0;
+
+  GST_DEBUG ("max_bandwidth = %" G_GINT64_FORMAT, max_bandwidth);
+
+  if (Representations == NULL)
+    return -1;
+
+  if (max_bandwidth <= 0)       /* 0 => get lowest representation available */
+    return gst_mpd_client_get_rep_idx_with_min_bandwidth (Representations);
+
+  for (list = g_list_first (Representations); list; list = g_list_next (list)) {
+    GstXMLFrameRate *framerate = NULL;
+
+    representation = (GstRepresentationNode *) list->data;
+
+    /* FIXME: Really? */
+    if (!representation)
+      continue;
+
+    framerate = representation->RepresentationBase->frameRate;
+    if (!framerate)
+      framerate = representation->RepresentationBase->maxFrameRate;
+
+    if (framerate && max_video_framerate_n > 0) {
+      if (gst_util_fraction_compare (framerate->num, framerate->den,
+              max_video_framerate_n, max_video_framerate_d) > 0)
+        continue;
+    }
+
+    if (max_video_width > 0
+        && representation->RepresentationBase->width > max_video_width)
+      continue;
+    if (max_video_height > 0
+        && representation->RepresentationBase->height > max_video_height)
+      continue;
+
+    if (representation->bandwidth <= max_bandwidth &&
+        representation->bandwidth > best_bandwidth) {
+      best = list;
+      best_bandwidth = representation->bandwidth;
+    }
+  }
+
+  return best ? g_list_position (Representations, best) : -1;
+}
+
+void
+gst_mpd_client_seek_to_first_segment (GstMpdClient * client)
+{
+  GList *list;
+
+  g_return_if_fail (client != NULL);
+  g_return_if_fail (client->active_streams != NULL);
+
+  for (list = g_list_first (client->active_streams); list;
+      list = g_list_next (list)) {
+    GstActiveStream *stream = (GstActiveStream *) list->data;
+    if (stream) {
+      stream->segment_index = 0;
+      stream->segment_repeat_index = 0;
+    }
+  }
+}
+
+static guint
+gst_mpd_client_get_segments_counts (GstMpdClient * client,
+    GstActiveStream * stream)
+{
+  GstStreamPeriod *stream_period;
+
+  g_return_val_if_fail (stream != NULL, 0);
+
+  if (stream->segments)
+    return stream->segments->len;
+  g_return_val_if_fail (stream->cur_seg_template->MultSegBaseType->
+      SegmentTimeline == NULL, 0);
+
+  stream_period = gst_mpd_client_get_stream_period (client);
+  if (stream_period->duration != -1)
+    return gst_util_uint64_scale_ceil (stream_period->duration, 1,
+        gst_mpd_client_get_segment_duration (client, stream, NULL));
+
+  return 0;
+}
+
+gboolean
+gst_mpd_client_is_live (GstMpdClient * client)
+{
+  g_return_val_if_fail (client != NULL, FALSE);
+  g_return_val_if_fail (client->mpd_node != NULL, FALSE);
+
+  return client->mpd_node->type == GST_MPD_FILE_TYPE_DYNAMIC;
+}
+
+guint
+gst_mpd_client_get_nb_active_stream (GstMpdClient * client)
+{
+  g_return_val_if_fail (client != NULL, 0);
+
+  return g_list_length (client->active_streams);
+}
+
+guint
+gst_mpd_client_get_nb_adaptationSet (GstMpdClient * client)
+{
+  GstStreamPeriod *stream_period;
+
+  stream_period = gst_mpd_client_get_stream_period (client);
+  g_return_val_if_fail (stream_period != NULL, 0);
+  g_return_val_if_fail (stream_period->period != NULL, 0);
+
+  return g_list_length (stream_period->period->AdaptationSets);
+}
+
+GstActiveStream *
+gst_mpd_client_get_active_stream_by_index (GstMpdClient * client,
+    guint stream_idx)
+{
+  g_return_val_if_fail (client != NULL, NULL);
+  g_return_val_if_fail (client->active_streams != NULL, NULL);
+
+  return g_list_nth_data (client->active_streams, stream_idx);
+}
+
+gboolean
+gst_mpd_client_active_stream_contains_subtitles (GstActiveStream * stream)
+{
+  const gchar *mimeType;
+  const gchar *adapt_set_codecs;
+  const gchar *rep_codecs;
+
+  mimeType = stream->cur_representation->RepresentationBase->mimeType;
+  if (!mimeType)
+    mimeType = stream->cur_adapt_set->RepresentationBase->mimeType;
+
+  if (g_strcmp0 (mimeType, "application/ttml+xml") == 0 ||
+      g_strcmp0 (mimeType, "text/vtt") == 0)
+    return TRUE;
+
+  adapt_set_codecs = stream->cur_adapt_set->RepresentationBase->codecs;
+  rep_codecs = stream->cur_representation->RepresentationBase->codecs;
+
+  return (adapt_set_codecs && g_str_has_prefix (adapt_set_codecs, "stpp"))
+      || (rep_codecs && g_str_has_prefix (rep_codecs, "stpp"));
+}
+
+GstCaps *
+gst_mpd_client_get_stream_caps (GstActiveStream * stream)
+{
+  const gchar *mimeType, *caps_string;
+  GstCaps *ret = NULL;
+
+  if (stream == NULL || stream->cur_adapt_set == NULL
+      || stream->cur_representation == NULL)
+    return NULL;
+
+  mimeType = stream->cur_representation->RepresentationBase->mimeType;
+  if (mimeType == NULL) {
+    mimeType = stream->cur_adapt_set->RepresentationBase->mimeType;
+  }
+
+  caps_string = gst_mpdparser_mimetype_to_caps (mimeType);
+
+  if ((g_strcmp0 (caps_string, "application/mp4") == 0)
+      && gst_mpd_client_active_stream_contains_subtitles (stream))
+    caps_string = "video/quicktime";
+
+  if (caps_string)
+    ret = gst_caps_from_string (caps_string);
+
+  return ret;
+}
+
+gboolean
+gst_mpd_client_get_bitstream_switching_flag (GstActiveStream * stream)
+{
+  if (stream == NULL || stream->cur_adapt_set == NULL)
+    return FALSE;
+
+  return stream->cur_adapt_set->bitstreamSwitching;
+}
+
+guint
+gst_mpd_client_get_video_stream_width (GstActiveStream * stream)
+{
+  guint width;
+
+  if (stream == NULL || stream->cur_adapt_set == NULL
+      || stream->cur_representation == NULL)
+    return 0;
+
+  width = stream->cur_representation->RepresentationBase->width;
+  if (width == 0) {
+    width = stream->cur_adapt_set->RepresentationBase->width;
+  }
+
+  return width;
+}
+
+guint
+gst_mpd_client_get_video_stream_height (GstActiveStream * stream)
+{
+  guint height;
+
+  if (stream == NULL || stream->cur_adapt_set == NULL
+      || stream->cur_representation == NULL)
+    return 0;
+
+  height = stream->cur_representation->RepresentationBase->height;
+  if (height == 0) {
+    height = stream->cur_adapt_set->RepresentationBase->height;
+  }
+
+  return height;
+}
+
+gboolean
+gst_mpd_client_get_video_stream_framerate (GstActiveStream * stream,
+    gint * fps_num, gint * fps_den)
+{
+  if (stream == NULL)
+    return FALSE;
+
+  if (stream->cur_adapt_set &&
+      stream->cur_adapt_set->RepresentationBase->frameRate != NULL) {
+    *fps_num = stream->cur_adapt_set->RepresentationBase->frameRate->num;
+    *fps_den = stream->cur_adapt_set->RepresentationBase->frameRate->den;
+    return TRUE;
+  }
+
+  if (stream->cur_adapt_set &&
+      stream->cur_adapt_set->RepresentationBase->maxFrameRate != NULL) {
+    *fps_num = stream->cur_adapt_set->RepresentationBase->maxFrameRate->num;
+    *fps_den = stream->cur_adapt_set->RepresentationBase->maxFrameRate->den;
+    return TRUE;
+  }
+
+  if (stream->cur_representation &&
+      stream->cur_representation->RepresentationBase->frameRate != NULL) {
+    *fps_num = stream->cur_representation->RepresentationBase->frameRate->num;
+    *fps_den = stream->cur_representation->RepresentationBase->frameRate->den;
+    return TRUE;
+  }
+
+  if (stream->cur_representation &&
+      stream->cur_representation->RepresentationBase->maxFrameRate != NULL) {
+    *fps_num =
+        stream->cur_representation->RepresentationBase->maxFrameRate->num;
+    *fps_den =
+        stream->cur_representation->RepresentationBase->maxFrameRate->den;
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+guint
+gst_mpd_client_get_audio_stream_rate (GstActiveStream * stream)
+{
+  const gchar *rate;
+
+  if (stream == NULL || stream->cur_adapt_set == NULL
+      || stream->cur_representation == NULL)
+    return 0;
+
+  rate = stream->cur_representation->RepresentationBase->audioSamplingRate;
+  if (rate == NULL) {
+    rate = stream->cur_adapt_set->RepresentationBase->audioSamplingRate;
+  }
+
+  return rate ? atoi (rate) : 0;
+}
+
+guint
+gst_mpd_client_get_audio_stream_num_channels (GstActiveStream * stream)
+{
+  if (stream == NULL || stream->cur_adapt_set == NULL
+      || stream->cur_representation == NULL)
+    return 0;
+  /* TODO: here we have to parse the AudioChannelConfiguration descriptors */
+  return 0;
+}
+
+guint
+gst_mpd_client_get_list_and_nb_of_audio_language (GstMpdClient * client,
+    GList ** lang)
+{
+  GstStreamPeriod *stream_period;
+  GstAdaptationSetNode *adapt_set;
+  GList *adaptation_sets, *list;
+  const gchar *this_mimeType = "audio";
+  gchar *mimeType = NULL;
+  guint nb_adaptation_set = 0;
+
+  stream_period = gst_mpd_client_get_stream_period (client);
+  g_return_val_if_fail (stream_period != NULL, 0);
+  g_return_val_if_fail (stream_period->period != NULL, 0);
+
+  adaptation_sets =
+      gst_mpd_client_get_adaptation_sets_for_period (client, stream_period);
+  for (list = adaptation_sets; list; list = g_list_next (list)) {
+    adapt_set = (GstAdaptationSetNode *) list->data;
+    if (adapt_set && adapt_set->lang) {
+      gchar *this_lang = adapt_set->lang;
+      GstRepresentationNode *rep;
+      rep =
+          gst_mpd_client_get_lowest_representation (adapt_set->Representations);
+      mimeType = NULL;
+      if (rep->RepresentationBase)
+        mimeType = rep->RepresentationBase->mimeType;
+      if (!mimeType && adapt_set->RepresentationBase) {
+        mimeType = adapt_set->RepresentationBase->mimeType;
+      }
+
+      if (strncmp_ext (mimeType, this_mimeType) == 0) {
+        nb_adaptation_set++;
+        *lang = g_list_append (*lang, this_lang);
+      }
+    }
+  }
+
+  return nb_adaptation_set;
+}
+
+
+GstDateTime *
+gst_mpd_client_get_next_segment_availability_start_time (GstMpdClient * client,
+    GstActiveStream * stream)
+{
+  GstDateTime *availability_start_time, *rv;
+  gint seg_idx;
+  GstMediaSegment *segment;
+  GstClockTime segmentEndTime;
+  const GstStreamPeriod *stream_period;
+  GstClockTime period_start = 0;
+
+  g_return_val_if_fail (client != NULL, NULL);
+  g_return_val_if_fail (stream != NULL, NULL);
+
+  stream_period = gst_mpd_client_get_stream_period (client);
+  if (stream_period && stream_period->period) {
+    period_start = stream_period->start;
+  }
+
+  seg_idx = stream->segment_index;
+
+  if (stream->segments) {
+    segment = g_ptr_array_index (stream->segments, seg_idx);
+
+    if (segment->repeat >= 0) {
+      segmentEndTime = segment->start + (stream->segment_repeat_index + 1) *
+          segment->duration;
+    } else if (seg_idx < stream->segments->len - 1) {
+      const GstMediaSegment *next_segment =
+          g_ptr_array_index (stream->segments, seg_idx + 1);
+      segmentEndTime = next_segment->start;
+    } else {
+      g_return_val_if_fail (stream_period != NULL, NULL);
+      segmentEndTime = period_start + stream_period->duration;
+    }
+  } else {
+    GstClockTime seg_duration;
+    seg_duration = gst_mpd_client_get_segment_duration (client, stream, NULL);
+    if (seg_duration == 0)
+      return NULL;
+    segmentEndTime = period_start + (1 + seg_idx) * seg_duration;
+  }
+
+  availability_start_time = gst_mpd_client_get_availability_start_time (client);
+  if (availability_start_time == NULL) {
+    GST_WARNING_OBJECT (client, "Failed to get availability_start_time");
+    return NULL;
+  }
+
+  rv = gst_mpd_client_add_time_difference (availability_start_time,
+      segmentEndTime / GST_USECOND);
+  gst_date_time_unref (availability_start_time);
+  if (rv == NULL) {
+    GST_WARNING_OBJECT (client, "Failed to offset availability_start_time");
+    return NULL;
+  }
+
+  return rv;
+}
+
+gboolean
+gst_mpd_client_seek_to_time (GstMpdClient * client, GDateTime * time)
+{
+  GDateTime *start;
+  GTimeSpan ts_microseconds;
+  GstClockTime ts;
+  gboolean ret = TRUE;
+  GList *stream;
+
+  g_return_val_if_fail (gst_mpd_client_is_live (client), FALSE);
+  g_return_val_if_fail (client->mpd_node->availabilityStartTime != NULL, FALSE);
+
+  start =
+      gst_date_time_to_g_date_time (client->mpd_node->availabilityStartTime);
+
+  ts_microseconds = g_date_time_difference (time, start);
+  g_date_time_unref (start);
+
+  /* Clamp to availability start time, otherwise calculations wrap around */
+  if (ts_microseconds < 0)
+    ts_microseconds = 0;
+
+  ts = ts_microseconds * GST_USECOND;
+  for (stream = client->active_streams; stream; stream = g_list_next (stream)) {
+    ret =
+        ret & gst_mpd_client_stream_seek (client, stream->data, TRUE, 0, ts,
+        NULL);
+  }
+  return ret;
+}
+
+gboolean
+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;
+}
+
+GstClockTime
+gst_mpd_client_get_maximum_segment_duration (GstMpdClient * client)
+{
+  GstClockTime ret = GST_CLOCK_TIME_NONE, dur;
+  GList *stream;
+
+  g_return_val_if_fail (client != NULL, GST_CLOCK_TIME_NONE);
+  g_return_val_if_fail (client->mpd_node != NULL, GST_CLOCK_TIME_NONE);
+
+  if (client->mpd_node->maxSegmentDuration != GST_MPD_DURATION_NONE) {
+    return client->mpd_node->maxSegmentDuration * GST_MSECOND;
+  }
+
+  /* According to the DASH specification, if maxSegmentDuration is not present:
+     "If not present, then the maximum Segment duration shall be the maximum
+     duration of any Segment documented in this MPD"
+   */
+  for (stream = client->active_streams; stream; stream = g_list_next (stream)) {
+    dur = gst_mpd_client_get_segment_duration (client, stream->data, NULL);
+    if (dur != GST_CLOCK_TIME_NONE && (dur > ret || ret == GST_CLOCK_TIME_NONE)) {
+      ret = dur;
+    }
+  }
+  return ret;
+}
+
+guint
+gst_mpd_client_get_period_index_at_time (GstMpdClient * client,
+    GstDateTime * time)
+{
+  GList *iter;
+  guint period_idx = G_MAXUINT;
+  guint idx;
+  gint64 time_offset;
+  GstDateTime *avail_start =
+      gst_mpd_client_get_availability_start_time (client);
+  GstStreamPeriod *stream_period;
+
+  if (avail_start == NULL)
+    return 0;
+
+  time_offset = gst_mpd_client_calculate_time_difference (avail_start, time);
+  gst_date_time_unref (avail_start);
+
+  if (time_offset < 0)
+    return 0;
+
+  if (!gst_mpd_client_setup_media_presentation (client, time_offset, -1, NULL))
+    return 0;
+
+  for (idx = 0, iter = client->periods; iter; idx++, iter = g_list_next (iter)) {
+    stream_period = iter->data;
+    if (stream_period->start <= time_offset
+        && (!GST_CLOCK_TIME_IS_VALID (stream_period->duration)
+            || stream_period->start + stream_period->duration > time_offset)) {
+      period_idx = idx;
+      break;
+    }
+  }
+
+  return period_idx;
+}
diff --git a/ext/dash/gstmpdclient.h b/ext/dash/gstmpdclient.h
new file mode 100644 (file)
index 0000000..fbe287c
--- /dev/null
@@ -0,0 +1,142 @@
+/* GStreamer
+ *
+ * Copyright (C) 2019 Collabora Ltd.
+ *   Author: Stéphane Cerveau <scerveau@collabora.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library (COPYING); if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef __GST_MPDCLIENT_H__
+#define __GST_MPDCLIENT_H__
+
+#include "gstmpdparser.h"
+
+G_BEGIN_DECLS
+
+#define GST_MPD_CLIENT_LOCK(c) g_mutex_lock (&c->lock);
+#define GST_MPD_CLIENT_UNLOCK(c) g_mutex_unlock (&c->lock);
+
+typedef struct _GstMpdClient              GstMpdClient;
+
+struct _GstMpdClient
+{
+  GstMPDNode *mpd_node;                       /* active MPD manifest file */
+
+  GList *periods;                             /* list of GstStreamPeriod */
+  guint period_idx;                           /* index of current Period */
+
+  GList *active_streams;                      /* list of GstActiveStream */
+
+  guint update_failed_count;
+  gchar *mpd_uri;                             /* manifest file URI */
+  gchar *mpd_base_uri;                        /* base URI for resolving relative URIs.
+                                               * this will be different for redirects */
+
+  /* profiles */
+  gboolean profile_isoff_ondemand;
+
+  GstUriDownloader * downloader;
+};
+
+/* Basic initialization/deinitialization functions */
+GstMpdClient *gst_mpd_client_new (void);
+void gst_mpd_client_active_streams_free (GstMpdClient * client);
+void gst_mpd_client_free (GstMpdClient * client);
+
+/* main mpd parsing methods from xml data */
+gboolean gst_mpd_client_parse (GstMpdClient * client, const gchar * data, gint size);
+
+void gst_mpd_client_set_uri_downloader (GstMpdClient * client, GstUriDownloader * download);
+void  gst_mpd_client_check_profiles (GstMpdClient * client);
+void gst_mpd_client_fetch_on_load_external_resources (GstMpdClient * client);
+
+/* Streaming management */
+gboolean gst_mpd_client_setup_media_presentation (GstMpdClient *client, GstClockTime time, gint period_index, const gchar *period_id);
+gboolean gst_mpd_client_setup_streaming (GstMpdClient * client, GstAdaptationSetNode * adapt_set);
+gboolean gst_mpd_client_setup_representation (GstMpdClient *client, GstActiveStream *stream, GstRepresentationNode *representation);
+
+GstClockTime gst_mpd_client_get_next_fragment_duration (GstMpdClient * client, GstActiveStream * stream);
+GstClockTime gst_mpd_client_get_media_presentation_duration (GstMpdClient *client);
+GstClockTime gst_mpd_client_get_maximum_segment_duration (GstMpdClient * client);
+gboolean gst_mpd_client_get_last_fragment_timestamp_end (GstMpdClient * client, guint stream_idx, GstClockTime * ts);
+gboolean gst_mpd_client_get_next_fragment_timestamp (GstMpdClient * client, guint stream_idx, GstClockTime * ts);
+gboolean gst_mpd_client_get_next_fragment (GstMpdClient *client, guint indexStream, GstMediaFragmentInfo * fragment);
+gboolean gst_mpd_client_get_next_header (GstMpdClient *client, gchar **uri, guint stream_idx, gint64 * range_start, gint64 * range_end);
+gboolean gst_mpd_client_get_next_header_index (GstMpdClient *client, gchar **uri, guint stream_idx, gint64 * range_start, gint64 * range_end);
+gboolean gst_mpd_client_is_live (GstMpdClient * client);
+gboolean gst_mpd_client_stream_seek (GstMpdClient * client, GstActiveStream * stream, gboolean forward, GstSeekFlags flags, GstClockTime ts, GstClockTime * final_ts);
+gboolean gst_mpd_client_seek_to_time (GstMpdClient * client, GDateTime * time);
+GstClockTime gst_mpd_client_get_stream_presentation_offset (GstMpdClient *client, guint stream_idx);
+gchar** gst_mpd_client_get_utc_timing_sources (GstMpdClient *client, guint methods, GstMPDUTCTimingType *selected_method);
+GstClockTime gst_mpd_client_get_period_start_time (GstMpdClient *client);
+
+/* Period selection */
+guint gst_mpd_client_get_period_index_at_time (GstMpdClient * client, GstDateTime * time);
+gboolean gst_mpd_client_set_period_index (GstMpdClient *client, guint period_idx);
+gboolean gst_mpd_client_set_period_id (GstMpdClient *client, const gchar * period_id);
+guint gst_mpd_client_get_period_index (GstMpdClient *client);
+const gchar *gst_mpd_client_get_period_id (GstMpdClient *client);
+gboolean gst_mpd_client_has_next_period (GstMpdClient *client);
+gboolean gst_mpd_client_has_previous_period (GstMpdClient * client);
+
+/* Representation selection */
+gint gst_mpd_client_get_rep_idx_with_max_bandwidth (GList *Representations, gint64 max_bandwidth, gint max_video_width, gint max_video_height, gint max_video_framerate_n, gint max_video_framerate_d);
+gint gst_mpd_client_get_rep_idx_with_min_bandwidth (GList * Representations);
+
+GstDateTime *
+gst_mpd_client_get_availability_start_time (GstMpdClient * client);
+
+/* URL management */
+const gchar *gst_mpd_client_get_baseURL (GstMpdClient *client, guint indexStream);
+gchar *gst_mpd_client_parse_baseURL (GstMpdClient * client, GstActiveStream * stream, gchar ** query);
+
+/* Active stream */
+guint gst_mpd_client_get_nb_active_stream (GstMpdClient *client);
+GstActiveStream *gst_mpd_client_get_active_stream_by_index (GstMpdClient *client, guint stream_idx);
+gboolean gst_mpd_client_active_stream_contains_subtitles (GstActiveStream * stream);
+
+/* AdaptationSet */
+guint gst_mpd_client_get_nb_adaptationSet (GstMpdClient *client);
+GList * gst_mpd_client_get_adaptation_sets (GstMpdClient * client);
+
+/* Segment */
+gboolean gst_mpd_client_has_next_segment (GstMpdClient * client, GstActiveStream * stream, gboolean forward);
+GstFlowReturn gst_mpd_client_advance_segment (GstMpdClient * client, GstActiveStream * stream, gboolean forward);
+void gst_mpd_client_seek_to_first_segment (GstMpdClient * client);
+GstDateTime *gst_mpd_client_get_next_segment_availability_start_time (GstMpdClient * client, GstActiveStream * stream);
+
+/* Get audio/video stream parameters (caps, width, height, rate, number of channels) */
+GstCaps * gst_mpd_client_get_stream_caps (GstActiveStream * stream);
+gboolean gst_mpd_client_get_bitstream_switching_flag (GstActiveStream * stream);
+guint gst_mpd_client_get_video_stream_width (GstActiveStream * stream);
+guint gst_mpd_client_get_video_stream_height (GstActiveStream * stream);
+gboolean gst_mpd_client_get_video_stream_framerate (GstActiveStream * stream, gint * fps_num, gint * fps_den);
+guint gst_mpd_client_get_audio_stream_rate (GstActiveStream * stream);
+guint gst_mpd_client_get_audio_stream_num_channels (GstActiveStream * stream);
+
+/* Support multi language */
+guint gst_mpd_client_get_list_and_nb_of_audio_language (GstMpdClient *client, GList **lang);
+
+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);
+
+
+G_END_DECLS
+
+#endif /* __GST_MPDCLIENT_H__ */
diff --git a/ext/dash/gstmpdhelper.c b/ext/dash/gstmpdhelper.c
new file mode 100644 (file)
index 0000000..ed2cfca
--- /dev/null
@@ -0,0 +1,78 @@
+/* GStreamer
+ *
+ * Copyright (C) 2019 Collabora Ltd.
+ *   Author: Stéphane Cerveau <scerveau@collabora.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ */
+
+#include "gstmpdhelper.h"
+
+gboolean
+gst_mpd_helper_get_mpd_type (xmlNode * a_node,
+    const gchar * property_name, GstMPDFileType * property_value)
+{
+  xmlChar *prop_string;
+  gboolean exists = FALSE;
+
+  *property_value = GST_MPD_FILE_TYPE_STATIC;   /* default */
+  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
+  if (prop_string) {
+    if (xmlStrcmp (prop_string, (xmlChar *) "OnDemand") == 0
+        || xmlStrcmp (prop_string, (xmlChar *) "static") == 0) {
+      exists = TRUE;
+      *property_value = GST_MPD_FILE_TYPE_STATIC;
+      GST_LOG (" - %s: static", property_name);
+    } else if (xmlStrcmp (prop_string, (xmlChar *) "Live") == 0
+        || xmlStrcmp (prop_string, (xmlChar *) "dynamic") == 0) {
+      exists = TRUE;
+      *property_value = GST_MPD_FILE_TYPE_DYNAMIC;
+      GST_LOG (" - %s: dynamic", property_name);
+    } else {
+      GST_WARNING ("failed to parse MPD type property %s from xml string %s",
+          property_name, prop_string);
+    }
+    xmlFree (prop_string);
+  }
+
+  return exists;
+}
+
+gboolean
+gst_mpd_helper_get_SAP_type (xmlNode * a_node,
+    const gchar * property_name, GstMPDSAPType * property_value)
+{
+  xmlChar *prop_string;
+  guint prop_SAP_type = 0;
+  gboolean exists = FALSE;
+
+  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
+  if (prop_string) {
+    if (sscanf ((gchar *) prop_string, "%u", &prop_SAP_type) == 1
+        && prop_SAP_type <= 6) {
+      exists = TRUE;
+      *property_value = (GstMPDSAPType) prop_SAP_type;
+      GST_LOG (" - %s: %u", property_name, prop_SAP_type);
+    } else {
+      GST_WARNING
+          ("failed to parse unsigned integer property %s from xml string %s",
+          property_name, prop_string);
+    }
+    xmlFree (prop_string);
+  }
+
+  return exists;
+}
diff --git a/ext/dash/gstmpdhelper.h b/ext/dash/gstmpdhelper.h
new file mode 100644 (file)
index 0000000..3da5df4
--- /dev/null
@@ -0,0 +1,49 @@
+/* GStreamer
+ *
+ * Copyright (C) 2019 Collabora Ltd.
+ *   Author: Stéphane Cerveau <scerveau@collabora.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library (COPYING); if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef __GST_MPDHELPER_H__
+#define __GST_MPDHELPER_H__
+
+#include "gstxmlhelper.h"
+
+G_BEGIN_DECLS
+
+typedef enum
+{
+  GST_MPD_FILE_TYPE_STATIC,
+  GST_MPD_FILE_TYPE_DYNAMIC
+} GstMPDFileType;
+
+typedef enum
+{
+  GST_SAP_TYPE_0 = 0,
+  GST_SAP_TYPE_1,
+  GST_SAP_TYPE_2,
+  GST_SAP_TYPE_3,
+  GST_SAP_TYPE_4,
+  GST_SAP_TYPE_5,
+  GST_SAP_TYPE_6
+} GstMPDSAPType;
+
+
+gboolean gst_mpd_helper_get_mpd_type (xmlNode * a_node, const gchar * property_name, GstMPDFileType * property_value);
+gboolean gst_mpd_helper_get_SAP_type (xmlNode * a_node, const gchar * property_name, GstMPDSAPType * property_value);
+G_END_DECLS
+#endif /* __GST_MPDHELPER_H__ */
index 3713296..a924678 100644 (file)
  */
 
 #include <string.h>
-#include <libxml/parser.h>
-#include <libxml/tree.h>
+
 #include "gstmpdparser.h"
 #include "gstdash_debug.h"
 
 #define GST_CAT_DEFAULT gst_dash_demux_debug
 
-/* Property parsing */
-static gboolean gst_mpdparser_get_xml_prop_validated_string (xmlNode * a_node,
-    const gchar * property_name, gchar ** property_value,
-    gboolean (*validator) (const char *));
-static gboolean gst_mpdparser_get_xml_prop_string (xmlNode * a_node,
-    const gchar * property_name, gchar ** property_value);
-static gboolean gst_mpdparser_get_xml_prop_string_stripped (xmlNode * a_node,
-    const gchar * property_name, gchar ** property_value);
-static gboolean gst_mpdparser_get_xml_ns_prop_string (xmlNode * a_node,
-    const gchar * ns_name, const gchar * property_name,
-    gchar ** property_value);
-static gboolean gst_mpdparser_get_xml_prop_string_vector_type (xmlNode * a_node,
-    const gchar * property_name, gchar *** property_value);
-static gboolean gst_mpdparser_get_xml_prop_signed_integer (xmlNode * a_node,
-    const gchar * property_name, gint default_val, gint * property_value);
-static gboolean gst_mpdparser_get_xml_prop_unsigned_integer (xmlNode * a_node,
-    const gchar * property_name, guint default_val, guint * property_value);
-static gboolean gst_mpdparser_get_xml_prop_unsigned_integer_64 (xmlNode *
-    a_node, const gchar * property_name, guint64 default_val,
-    guint64 * property_value);
-static gboolean gst_mpdparser_get_xml_prop_uint_vector_type (xmlNode * a_node,
-    const gchar * property_name, guint ** property_value, guint * value_size);
-static gboolean gst_mpdparser_get_xml_prop_double (xmlNode * a_node,
-    const gchar * property_name, gdouble * property_value);
-static gboolean gst_mpdparser_get_xml_prop_boolean (xmlNode * a_node,
-    const gchar * property_name, gboolean default_val,
-    gboolean * property_value);
-static gboolean gst_mpdparser_get_xml_prop_type (xmlNode * a_node,
-    const gchar * property_name, GstMPDFileType * property_value);
-static gboolean gst_mpdparser_get_xml_prop_SAP_type (xmlNode * a_node,
-    const gchar * property_name, GstSAPType * property_value);
-static gboolean gst_mpdparser_get_xml_prop_range (xmlNode * a_node,
-    const gchar * property_name, GstRange ** property_value);
-static gboolean gst_mpdparser_get_xml_prop_ratio (xmlNode * a_node,
-    const gchar * property_name, GstRatio ** property_value);
-static gboolean gst_mpdparser_get_xml_prop_framerate (xmlNode * a_node,
-    const gchar * property_name, GstFrameRate ** property_value);
-static gboolean gst_mpdparser_get_xml_prop_cond_uint (xmlNode * a_node,
-    const gchar * property_name, GstConditionalUintType ** property_value);
-static gboolean gst_mpdparser_get_xml_prop_dateTime (xmlNode * a_node,
-    const gchar * property_name, GstDateTime ** property_value);
-static gboolean gst_mpdparser_get_xml_prop_duration (xmlNode * a_node,
-    const gchar * property_name, guint64 default_value,
-    guint64 * property_value);
-static gboolean gst_mpdparser_get_xml_node_content (xmlNode * a_node,
-    gchar ** content);
-static gchar *gst_mpdparser_get_xml_node_namespace (xmlNode * a_node,
-    const gchar * prefix);
-static gboolean gst_mpdparser_get_xml_node_as_string (xmlNode * a_node,
-    gchar ** content);
 
 /* XML node parsing */
 static void gst_mpdparser_parse_baseURL_node (GList ** list, xmlNode * a_node);
@@ -130,73 +79,27 @@ static gboolean gst_mpdparser_parse_root_node (GstMPDNode ** pointer,
 static void gst_mpdparser_parse_utctiming_node (GList ** list,
     xmlNode * a_node);
 
-/* Helper functions */
-static guint convert_to_millisecs (guint decimals, gint pos);
-static int strncmp_ext (const char *s1, const char *s2);
-static GstStreamPeriod *gst_mpdparser_get_stream_period (GstMpdClient * client);
 static GstSNode *gst_mpdparser_clone_s_node (GstSNode * pointer);
 static GstSegmentTimelineNode
     * gst_mpdparser_clone_segment_timeline (GstSegmentTimelineNode * pointer);
-static GstRange *gst_mpdparser_clone_range (GstRange * range);
+
 static GstURLType *gst_mpdparser_clone_URL (GstURLType * url);
-static gchar *gst_mpdparser_parse_baseURL (GstMpdClient * client,
-    GstActiveStream * stream, gchar ** query);
 static GstSegmentURLNode *gst_mpdparser_clone_segment_url (GstSegmentURLNode *
     seg_url);
-static gchar *gst_mpdparser_get_mediaURL (GstActiveStream * stream,
-    GstSegmentURLNode * segmentURL);
-static const gchar *gst_mpdparser_get_initializationURL (GstActiveStream *
-    stream, GstURLType * InitializationURL);
-static gchar *gst_mpdparser_build_URL_from_template (const gchar * url_template,
-    const gchar * id, guint number, guint bandwidth, guint64 time);
-static gboolean gst_mpd_client_add_media_segment (GstActiveStream * stream,
-    GstSegmentURLNode * url_node, guint number, gint repeat,
-    guint64 scale_start, guint64 scale_duration, GstClockTime start,
-    GstClockTime duration);
-static const gchar *gst_mpdparser_mimetype_to_caps (const gchar * mimeType);
-static GstClockTime gst_mpd_client_get_segment_duration (GstMpdClient * client,
-    GstActiveStream * stream, guint64 * scale_duration);
-static GstDateTime *gst_mpd_client_get_availability_start_time (GstMpdClient *
-    client);
-
-/* Representation */
-static GstRepresentationNode *gst_mpdparser_get_lowest_representation (GList *
-    Representations);
-#if 0
-static GstRepresentationNode *gst_mpdparser_get_highest_representation (GList *
-    Representations);
-static GstRepresentationNode
-    * gst_mpdparser_get_representation_with_max_bandwidth (GList *
-    Representations, gint max_bandwidth);
-#endif
-static GstSegmentBaseType *gst_mpdparser_get_segment_base (GstPeriodNode *
-    Period, GstAdaptationSetNode * AdaptationSet,
-    GstRepresentationNode * Representation);
-static GstSegmentListNode *gst_mpdparser_get_segment_list (GstMpdClient *
-    client, GstPeriodNode * Period, GstAdaptationSetNode * AdaptationSet,
-    GstRepresentationNode * Representation);
-
-/* Segments */
-static guint gst_mpd_client_get_segments_counts (GstMpdClient * client,
-    GstActiveStream * stream);
 
 /* Memory management */
 static GstSegmentTimelineNode *gst_mpdparser_segment_timeline_node_new (void);
-static void gst_mpdparser_free_mpd_node (GstMPDNode * mpd_node);
 static void gst_mpdparser_free_prog_info_node (GstProgramInformationNode *
     prog_info_node);
 static void gst_mpdparser_free_metrics_node (GstMetricsNode * metrics_node);
 static void gst_mpdparser_free_metrics_range_node (GstMetricsRangeNode *
     metrics_range_node);
-static void gst_mpdparser_free_period_node (GstPeriodNode * period_node);
 static void gst_mpdparser_free_subset_node (GstSubsetNode * subset_node);
 static void gst_mpdparser_free_segment_template_node (GstSegmentTemplateNode *
     segment_template_node);
 static void
 gst_mpdparser_free_representation_base_type (GstRepresentationBaseType *
     representation_base);
-static void gst_mpdparser_free_adaptation_set_node (GstAdaptationSetNode *
-    adaptation_set_node);
 static void gst_mpdparser_free_representation_node (GstRepresentationNode *
     representation_node);
 static void gst_mpdparser_free_subrepresentation_node (GstSubRepresentationNode
@@ -209,27 +112,13 @@ static void gst_mpdparser_free_seg_base_type_ext (GstSegmentBaseType *
     seg_base_type);
 static void gst_mpdparser_free_mult_seg_base_type_ext (GstMultSegmentBaseType *
     mult_seg_base_type);
-static void gst_mpdparser_free_segment_list_node (GstSegmentListNode *
-    segment_list_node);
 static void gst_mpdparser_free_segment_url_node (GstSegmentURLNode *
     segment_url);
-static void gst_mpdparser_free_base_url_node (GstBaseURL * base_url_node);
 static void gst_mpdparser_free_descriptor_type_node (GstDescriptorType *
     descriptor_type);
 static void gst_mpdparser_free_content_component_node (GstContentComponentNode *
     content_component_node);
 static void gst_mpdparser_free_utctiming_node (GstUTCTimingNode * timing_type);
-static void gst_mpdparser_free_stream_period (GstStreamPeriod * stream_period);
-static void gst_mpdparser_free_media_segment (GstMediaSegment * media_segment);
-static void gst_mpdparser_free_active_stream (GstActiveStream * active_stream);
-
-static GstUri *combine_urls (GstUri * base, GList * list, gchar ** query,
-    guint idx);
-
-static GList *gst_mpd_client_fetch_external_period (GstMpdClient * client,
-    GstPeriodNode * period_node);
-static GList *gst_mpd_client_fetch_external_adaptation_set (GstMpdClient *
-    client, GstPeriodNode * period, GstAdaptationSetNode * adapt_set);
 
 struct GstMpdParserUtcTimingMethod
 {
@@ -261,1624 +150,581 @@ static const struct GstMpdParserUtcTimingMethod
   {NULL, 0}
 };
 
-/* functions to parse node namespaces, content and properties */
-static gboolean
-gst_mpdparser_get_xml_prop_validated_string (xmlNode * a_node,
-    const gchar * property_name, gchar ** property_value,
-    gboolean (*validate) (const char *))
-{
-  xmlChar *prop_string;
-  gboolean exists = FALSE;
-
-  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
-  if (prop_string) {
-    if (validate && !(*validate) ((const char *) prop_string)) {
-      GST_WARNING ("Validation failure: %s", prop_string);
-      xmlFree (prop_string);
-      return FALSE;
-    }
-    *property_value = (gchar *) prop_string;
-    exists = TRUE;
-    GST_LOG (" - %s: %s", property_name, prop_string);
-  }
 
-  return exists;
-}
 
-static gboolean
-gst_mpdparser_get_xml_ns_prop_string (xmlNode * a_node,
-    const gchar * ns_name, const gchar * property_name, gchar ** property_value)
-{
-  xmlChar *prop_string;
-  gboolean exists = FALSE;
-
-  prop_string =
-      xmlGetNsProp (a_node, (const xmlChar *) property_name,
-      (const xmlChar *) ns_name);
-  if (prop_string) {
-    *property_value = (gchar *) prop_string;
-    exists = TRUE;
-    GST_LOG (" - %s:%s: %s", ns_name, property_name, prop_string);
-  }
 
-  return exists;
-}
+/*
+  Duration Data Type
 
-static gboolean
-gst_mpdparser_get_xml_prop_string (xmlNode * a_node,
-    const gchar * property_name, gchar ** property_value)
-{
-  return gst_mpdparser_get_xml_prop_validated_string (a_node, property_name,
-      property_value, NULL);
-}
+  The duration data type is used to specify a time interval.
 
-static gboolean
-gst_mpdparser_get_xml_prop_string_stripped (xmlNode * a_node,
-    const gchar * property_name, gchar ** property_value)
-{
-  gboolean ret;
-  ret =
-      gst_mpdparser_get_xml_prop_string (a_node, property_name, property_value);
-  if (ret)
-    *property_value = g_strstrip (*property_value);
-  return ret;
-}
+  The time interval is specified in the following form "-PnYnMnDTnHnMnS" where:
 
-static gboolean
-gst_mpdparser_validate_no_whitespace (const char *s)
-{
-  return !strpbrk (s, "\r\n\t ");
-}
+    * - indicates the negative sign (optional)
+    * P indicates the period (required)
+    * nY indicates the number of years
+    * nM indicates the number of months
+    * nD indicates the number of days
+    * T indicates the start of a time section (required if you are going to specify hours, minutes, or seconds)
+    * nH indicates the number of hours
+    * nM indicates the number of minutes
+    * nS indicates the number of seconds
+*/
 
-static gboolean
-gst_mpdparser_get_xml_prop_string_no_whitespace (xmlNode * a_node,
-    const gchar * property_name, gchar ** property_value)
-{
-  return gst_mpdparser_get_xml_prop_validated_string (a_node, property_name,
-      property_value, gst_mpdparser_validate_no_whitespace);
-}
 
-static gboolean
-gst_mpdparser_get_xml_prop_string_vector_type (xmlNode * a_node,
-    const gchar * property_name, gchar *** property_value)
-{
-  xmlChar *prop_string;
-  gchar **prop_string_vector = NULL;
-  guint i = 0;
-  gboolean exists = FALSE;
-
-  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
-  if (prop_string) {
-    prop_string_vector = g_strsplit ((gchar *) prop_string, " ", -1);
-    if (prop_string_vector) {
-      exists = TRUE;
-      *property_value = prop_string_vector;
-      GST_LOG (" - %s:", property_name);
-      while (prop_string_vector[i]) {
-        GST_LOG ("    %s", prop_string_vector[i]);
-        i++;
-      }
-    } else {
-      GST_WARNING ("Scan of string vector property failed!");
-    }
-    xmlFree (prop_string);
-  }
 
-  return exists;
-}
 
-static gboolean
-gst_mpdparser_get_xml_prop_signed_integer (xmlNode * a_node,
-    const gchar * property_name, gint default_val, gint * property_value)
+static void
+gst_mpdparser_parse_baseURL_node (GList ** list, xmlNode * a_node)
 {
-  xmlChar *prop_string;
-  gboolean exists = FALSE;
-
-  *property_value = default_val;
-  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
-  if (prop_string) {
-    if (sscanf ((const gchar *) prop_string, "%d", property_value) == 1) {
-      exists = TRUE;
-      GST_LOG (" - %s: %d", property_name, *property_value);
-    } else {
-      GST_WARNING
-          ("failed to parse signed integer property %s from xml string %s",
-          property_name, prop_string);
-    }
-    xmlFree (prop_string);
-  }
+  GstBaseURL *new_base_url;
 
-  return exists;
-}
+  new_base_url = g_slice_new0 (GstBaseURL);
+  *list = g_list_append (*list, new_base_url);
 
-static gboolean
-gst_mpdparser_get_xml_prop_unsigned_integer (xmlNode * a_node,
-    const gchar * property_name, guint default_val, guint * property_value)
-{
-  xmlChar *prop_string;
-  gboolean exists = FALSE;
-
-  *property_value = default_val;
-  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
-  if (prop_string) {
-    if (sscanf ((gchar *) prop_string, "%u", property_value) == 1 &&
-        strstr ((gchar *) prop_string, "-") == NULL) {
-      exists = TRUE;
-      GST_LOG (" - %s: %u", property_name, *property_value);
-    } else {
-      GST_WARNING
-          ("failed to parse unsigned integer property %s from xml string %s",
-          property_name, prop_string);
-      /* sscanf might have written to *property_value. Restore to default */
-      *property_value = default_val;
-    }
-    xmlFree (prop_string);
-  }
+  GST_LOG ("content of BaseURL node:");
+  gst_xml_helper_get_node_content (a_node, &new_base_url->baseURL);
 
-  return exists;
+  GST_LOG ("attributes of BaseURL node:");
+  gst_xml_helper_get_prop_string (a_node, "serviceLocation",
+      &new_base_url->serviceLocation);
+  gst_xml_helper_get_prop_string (a_node, "byteRange",
+      &new_base_url->byteRange);
 }
 
-static gboolean
-gst_mpdparser_get_xml_prop_unsigned_integer_64 (xmlNode * a_node,
-    const gchar * property_name, guint64 default_val, guint64 * property_value)
+static void
+gst_mpdparser_parse_descriptor_type_node (GList ** list, xmlNode * a_node)
 {
-  xmlChar *prop_string;
-  gboolean exists = FALSE;
-
-  *property_value = default_val;
-  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
-  if (prop_string) {
-    if (sscanf ((gchar *) prop_string, "%" G_GUINT64_FORMAT,
-            property_value) == 1 &&
-        strstr ((gchar *) prop_string, "-") == NULL) {
-      exists = TRUE;
-      GST_LOG (" - %s: %" G_GUINT64_FORMAT, property_name, *property_value);
-    } else {
-      GST_WARNING
-          ("failed to parse unsigned integer property %s from xml string %s",
-          property_name, prop_string);
-      /* sscanf might have written to *property_value. Restore to default */
-      *property_value = default_val;
-    }
-    xmlFree (prop_string);
-  }
+  GstDescriptorType *new_descriptor;
 
-  return exists;
-}
+  new_descriptor = g_slice_new0 (GstDescriptorType);
+  *list = g_list_append (*list, new_descriptor);
 
-static gboolean
-gst_mpdparser_get_xml_prop_uint_vector_type (xmlNode * a_node,
-    const gchar * property_name, guint ** property_value, guint * value_size)
-{
-  xmlChar *prop_string;
-  gchar **str_vector;
-  guint *prop_uint_vector = NULL, i;
-  gboolean exists = FALSE;
-
-  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
-  if (prop_string) {
-    str_vector = g_strsplit ((gchar *) prop_string, " ", -1);
-    if (str_vector) {
-      *value_size = g_strv_length (str_vector);
-      prop_uint_vector = g_malloc (*value_size * sizeof (guint));
-      if (prop_uint_vector) {
-        exists = TRUE;
-        GST_LOG (" - %s:", property_name);
-        for (i = 0; i < *value_size; i++) {
-          if (sscanf ((gchar *) str_vector[i], "%u", &prop_uint_vector[i]) == 1
-              && strstr (str_vector[i], "-") == NULL) {
-            GST_LOG ("    %u", prop_uint_vector[i]);
-          } else {
-            GST_WARNING
-                ("failed to parse uint vector type property %s from xml string %s",
-                property_name, str_vector[i]);
-            /* there is no special value to put in prop_uint_vector[i] to
-             * signal it is invalid, so we just clean everything and return
-             * FALSE
-             */
-            g_free (prop_uint_vector);
-            prop_uint_vector = NULL;
-            exists = FALSE;
-            break;
-          }
-        }
-        *property_value = prop_uint_vector;
-      } else {
-        GST_WARNING ("Array allocation failed!");
-      }
-    } else {
-      GST_WARNING ("Scan of uint vector property failed!");
-    }
-    xmlFree (prop_string);
-    g_strfreev (str_vector);
+  GST_LOG ("attributes of %s node:", a_node->name);
+  gst_xml_helper_get_prop_string_stripped (a_node, "schemeIdUri",
+      &new_descriptor->schemeIdUri);
+  if (!gst_xml_helper_get_prop_string (a_node, "value", &new_descriptor->value)) {
+    /* if no value attribute, use XML string representation of the node */
+    gst_xml_helper_get_node_as_string (a_node, &new_descriptor->value);
   }
-
-  return exists;
 }
 
-static gboolean
-gst_mpdparser_get_xml_prop_double (xmlNode * a_node,
-    const gchar * property_name, gdouble * property_value)
+static void
+gst_mpdparser_parse_content_component_node (GList ** list, xmlNode * a_node)
 {
-  xmlChar *prop_string;
-  gboolean exists = FALSE;
-
-  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
-  if (prop_string) {
-    if (sscanf ((gchar *) prop_string, "%lf", property_value) == 1) {
-      exists = TRUE;
-      GST_LOG (" - %s: %lf", property_name, *property_value);
-    } else {
-      GST_WARNING ("failed to parse double property %s from xml string %s",
-          property_name, prop_string);
-    }
-    xmlFree (prop_string);
-  }
-
-  return exists;
-}
+  xmlNode *cur_node;
+  GstContentComponentNode *new_content_component;
 
-static gboolean
-gst_mpdparser_get_xml_prop_boolean (xmlNode * a_node,
-    const gchar * property_name, gboolean default_val,
-    gboolean * property_value)
-{
-  xmlChar *prop_string;
-  gboolean exists = FALSE;
-
-  *property_value = default_val;
-  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
-  if (prop_string) {
-    if (xmlStrcmp (prop_string, (xmlChar *) "false") == 0) {
-      exists = TRUE;
-      *property_value = FALSE;
-      GST_LOG (" - %s: false", property_name);
-    } else if (xmlStrcmp (prop_string, (xmlChar *) "true") == 0) {
-      exists = TRUE;
-      *property_value = TRUE;
-      GST_LOG (" - %s: true", property_name);
-    } else {
-      GST_WARNING ("failed to parse boolean property %s from xml string %s",
-          property_name, prop_string);
-    }
-    xmlFree (prop_string);
-  }
+  new_content_component = g_slice_new0 (GstContentComponentNode);
+  *list = g_list_append (*list, new_content_component);
 
-  return exists;
-}
+  GST_LOG ("attributes of ContentComponent node:");
+  gst_xml_helper_get_prop_unsigned_integer (a_node, "id", 0,
+      &new_content_component->id);
+  gst_xml_helper_get_prop_string (a_node, "lang", &new_content_component->lang);
+  gst_xml_helper_get_prop_string (a_node, "contentType",
+      &new_content_component->contentType);
+  gst_xml_helper_get_prop_ratio (a_node, "par", &new_content_component->par);
 
-static gboolean
-gst_mpdparser_get_xml_prop_type (xmlNode * a_node,
-    const gchar * property_name, GstMPDFileType * property_value)
-{
-  xmlChar *prop_string;
-  gboolean exists = FALSE;
-
-  *property_value = GST_MPD_FILE_TYPE_STATIC;   /* default */
-  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
-  if (prop_string) {
-    if (xmlStrcmp (prop_string, (xmlChar *) "OnDemand") == 0
-        || xmlStrcmp (prop_string, (xmlChar *) "static") == 0) {
-      exists = TRUE;
-      *property_value = GST_MPD_FILE_TYPE_STATIC;
-      GST_LOG (" - %s: static", property_name);
-    } else if (xmlStrcmp (prop_string, (xmlChar *) "Live") == 0
-        || xmlStrcmp (prop_string, (xmlChar *) "dynamic") == 0) {
-      exists = TRUE;
-      *property_value = GST_MPD_FILE_TYPE_DYNAMIC;
-      GST_LOG (" - %s: dynamic", property_name);
-    } else {
-      GST_WARNING ("failed to parse MPD type property %s from xml string %s",
-          property_name, prop_string);
+  /* explore children nodes */
+  for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
+    if (cur_node->type == XML_ELEMENT_NODE) {
+      if (xmlStrcmp (cur_node->name, (xmlChar *) "Accessibility") == 0) {
+        gst_mpdparser_parse_descriptor_type_node
+            (&new_content_component->Accessibility, cur_node);
+      } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Role") == 0) {
+        gst_mpdparser_parse_descriptor_type_node (&new_content_component->Role,
+            cur_node);
+      } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Rating") == 0) {
+        gst_mpdparser_parse_descriptor_type_node
+            (&new_content_component->Rating, cur_node);
+      } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Viewpoint") == 0) {
+        gst_mpdparser_parse_descriptor_type_node
+            (&new_content_component->Viewpoint, cur_node);
+      }
     }
-    xmlFree (prop_string);
   }
-
-  return exists;
 }
 
-static gboolean
-gst_mpdparser_get_xml_prop_SAP_type (xmlNode * a_node,
-    const gchar * property_name, GstSAPType * property_value)
+static void
+gst_mpdparser_parse_location_node (GList ** list, xmlNode * a_node)
 {
-  xmlChar *prop_string;
-  guint prop_SAP_type = 0;
-  gboolean exists = FALSE;
-
-  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
-  if (prop_string) {
-    if (sscanf ((gchar *) prop_string, "%u", &prop_SAP_type) == 1
-        && prop_SAP_type <= 6) {
-      exists = TRUE;
-      *property_value = (GstSAPType) prop_SAP_type;
-      GST_LOG (" - %s: %u", property_name, prop_SAP_type);
-    } else {
-      GST_WARNING
-          ("failed to parse unsigned integer property %s from xml string %s",
-          property_name, prop_string);
-    }
-    xmlFree (prop_string);
-  }
+  gchar *location = NULL;
 
-  return exists;
+  GST_LOG ("content of Location node:");
+  if (gst_xml_helper_get_node_content (a_node, &location))
+    *list = g_list_append (*list, location);
 }
 
-static gboolean
-gst_mpdparser_get_xml_prop_range (xmlNode * a_node, const gchar * property_name,
-    GstRange ** property_value)
+static void
+gst_mpdparser_parse_subrepresentation_node (GList ** list, xmlNode * a_node)
 {
-  xmlChar *prop_string;
-  guint64 first_byte_pos = 0, last_byte_pos = -1;
-  guint len, pos;
-  gchar *str;
-  gboolean exists = FALSE;
-
-  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
-  if (prop_string) {
-    len = xmlStrlen (prop_string);
-    str = (gchar *) prop_string;
-    GST_TRACE ("range: %s, len %d", str, len);
-
-    /* read "-" */
-    pos = strcspn (str, "-");
-    if (pos >= len) {
-      GST_TRACE ("pos %d >= len %d", pos, len);
-      goto error;
-    }
-    /* read first_byte_pos */
-    if (pos != 0) {
-      /* replace str[pos] with '\0' to allow sscanf to not be confused by
-       * the minus sign (eg " -1" (observe the space before -) would otherwise
-       * be interpreted as range -1 to 1)
-       */
-      str[pos] = 0;
-      if (sscanf (str, "%" G_GUINT64_FORMAT, &first_byte_pos) != 1 ||
-          strstr (str, "-") != NULL) {
-        /* sscanf failed or it found a negative number */
-        /* restore the '-' sign */
-        str[pos] = '-';
-        goto error;
-      }
-      /* restore the '-' sign */
-      str[pos] = '-';
-    }
-    /* read last_byte_pos */
-    if (pos < (len - 1)) {
-      if (sscanf (str + pos + 1, "%" G_GUINT64_FORMAT, &last_byte_pos) != 1 ||
-          strstr (str + pos + 1, "-") != NULL) {
-        goto error;
-      }
-    }
-    /* malloc return data structure */
-    *property_value = g_slice_new0 (GstRange);
-    exists = TRUE;
-    (*property_value)->first_byte_pos = first_byte_pos;
-    (*property_value)->last_byte_pos = last_byte_pos;
-    xmlFree (prop_string);
-    GST_LOG (" - %s: %" G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT,
-        property_name, first_byte_pos, last_byte_pos);
-  }
-
-  return exists;
-
-error:
-  GST_WARNING ("failed to parse property %s from xml string %s", property_name,
-      prop_string);
-  xmlFree (prop_string);
-  return FALSE;
-}
+  GstSubRepresentationNode *new_subrep;
 
-static gboolean
-gst_mpdparser_get_xml_prop_ratio (xmlNode * a_node,
-    const gchar * property_name, GstRatio ** property_value)
-{
-  xmlChar *prop_string;
-  guint num = 0, den = 1;
-  guint len, pos;
-  gchar *str;
-  gboolean exists = FALSE;
-
-  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
-  if (prop_string) {
-    len = xmlStrlen (prop_string);
-    str = (gchar *) prop_string;
-    GST_TRACE ("ratio: %s, len %d", str, len);
-
-    /* read ":" */
-    pos = strcspn (str, ":");
-    if (pos >= len) {
-      GST_TRACE ("pos %d >= len %d", pos, len);
-      goto error;
-    }
-    /* search for negative sign */
-    if (strstr (str, "-") != NULL) {
-      goto error;
-    }
-    /* read num */
-    if (pos != 0) {
-      if (sscanf (str, "%u", &num) != 1) {
-        goto error;
-      }
-    }
-    /* read den */
-    if (pos < (len - 1)) {
-      if (sscanf (str + pos + 1, "%u", &den) != 1) {
-        goto error;
-      }
-    }
-    /* malloc return data structure */
-    *property_value = g_slice_new0 (GstRatio);
-    exists = TRUE;
-    (*property_value)->num = num;
-    (*property_value)->den = den;
-    xmlFree (prop_string);
-    GST_LOG (" - %s: %u:%u", property_name, num, den);
-  }
+  new_subrep = g_slice_new0 (GstSubRepresentationNode);
+  *list = g_list_append (*list, new_subrep);
 
-  return exists;
+  GST_LOG ("attributes of SubRepresentation node:");
+  gst_xml_helper_get_prop_unsigned_integer (a_node, "level", 0,
+      &new_subrep->level);
+  gst_xml_helper_get_prop_uint_vector_type (a_node, "dependencyLevel",
+      &new_subrep->dependencyLevel, &new_subrep->size);
+  gst_xml_helper_get_prop_unsigned_integer (a_node, "bandwidth", 0,
+      &new_subrep->bandwidth);
+  gst_xml_helper_get_prop_string_vector_type (a_node,
+      "contentComponent", &new_subrep->contentComponent);
 
-error:
-  GST_WARNING ("failed to parse property %s from xml string %s", property_name,
-      prop_string);
-  xmlFree (prop_string);
-  return FALSE;
+  /* RepresentationBase extension */
+  gst_mpdparser_parse_representation_base_type (&new_subrep->RepresentationBase,
+      a_node);
 }
 
-static gboolean
-gst_mpdparser_get_xml_prop_framerate (xmlNode * a_node,
-    const gchar * property_name, GstFrameRate ** property_value)
+static GstSegmentURLNode *
+gst_mpdparser_clone_segment_url (GstSegmentURLNode * seg_url)
 {
-  xmlChar *prop_string;
-  guint num = 0, den = 1;
-  guint len, pos;
-  gchar *str;
-  gboolean exists = FALSE;
-
-  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
-  if (prop_string) {
-    len = xmlStrlen (prop_string);
-    str = (gchar *) prop_string;
-    GST_TRACE ("framerate: %s, len %d", str, len);
-
-    /* search for negative sign */
-    if (strstr (str, "-") != NULL) {
-      goto error;
-    }
+  GstSegmentURLNode *clone = NULL;
 
-    /* read "/" if available */
-    pos = strcspn (str, "/");
-    /* read num */
-    if (pos != 0) {
-      if (sscanf (str, "%u", &num) != 1) {
-        goto error;
-      }
-    }
-    /* read den (if available) */
-    if (pos < (len - 1)) {
-      if (sscanf (str + pos + 1, "%u", &den) != 1) {
-        goto error;
-      }
-    }
-    /* alloc return data structure */
-    *property_value = g_slice_new0 (GstFrameRate);
-    exists = TRUE;
-    (*property_value)->num = num;
-    (*property_value)->den = den;
-    xmlFree (prop_string);
-    if (den == 1)
-      GST_LOG (" - %s: %u", property_name, num);
-    else
-      GST_LOG (" - %s: %u/%u", property_name, num, den);
+  if (seg_url) {
+    clone = g_slice_new0 (GstSegmentURLNode);
+    clone->media = xmlMemStrdup (seg_url->media);
+    clone->mediaRange = gst_xml_helper_clone_range (seg_url->mediaRange);
+    clone->index = xmlMemStrdup (seg_url->index);
+    clone->indexRange = gst_xml_helper_clone_range (seg_url->indexRange);
   }
 
-  return exists;
-
-error:
-  GST_WARNING ("failed to parse property %s from xml string %s", property_name,
-      prop_string);
-  xmlFree (prop_string);
-  return FALSE;
+  return clone;
 }
 
-static gboolean
-gst_mpdparser_get_xml_prop_cond_uint (xmlNode * a_node,
-    const gchar * property_name, GstConditionalUintType ** property_value)
+static void
+gst_mpdparser_parse_segment_url_node (GList ** list, xmlNode * a_node)
 {
-  xmlChar *prop_string;
-  gchar *str;
-  gboolean flag;
-  guint val;
-  gboolean exists = FALSE;
-
-  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
-  if (prop_string) {
-    str = (gchar *) prop_string;
-    GST_TRACE ("conditional uint: %s", str);
-
-    if (strcmp (str, "false") == 0) {
-      flag = FALSE;
-      val = 0;
-    } else if (strcmp (str, "true") == 0) {
-      flag = TRUE;
-      val = 0;
-    } else {
-      flag = TRUE;
-      if (sscanf (str, "%u", &val) != 1 || strstr (str, "-") != NULL)
-        goto error;
-    }
-
-    /* alloc return data structure */
-    *property_value = g_slice_new0 (GstConditionalUintType);
-    exists = TRUE;
-    (*property_value)->flag = flag;
-    (*property_value)->value = val;
-    xmlFree (prop_string);
-    GST_LOG (" - %s: flag=%s val=%u", property_name, flag ? "true" : "false",
-        val);
-  }
+  GstSegmentURLNode *new_segment_url;
 
-  return exists;
+  new_segment_url = g_slice_new0 (GstSegmentURLNode);
+  *list = g_list_append (*list, new_segment_url);
 
-error:
-  GST_WARNING ("failed to parse property %s from xml string %s", property_name,
-      prop_string);
-  xmlFree (prop_string);
-  return FALSE;
+  GST_LOG ("attributes of SegmentURL node:");
+  gst_xml_helper_get_prop_string (a_node, "media", &new_segment_url->media);
+  gst_xml_helper_get_prop_range (a_node, "mediaRange",
+      &new_segment_url->mediaRange);
+  gst_xml_helper_get_prop_string (a_node, "index", &new_segment_url->index);
+  gst_xml_helper_get_prop_range (a_node, "indexRange",
+      &new_segment_url->indexRange);
 }
 
-/*
-  DateTime Data Type
-
-  The dateTime data type is used to specify a date and a time.
+static void
+gst_mpdparser_parse_url_type_node (GstURLType ** pointer, xmlNode * a_node)
+{
+  GstURLType *new_url_type;
 
-  The lexical form of xs:dateTime is YYYY-MM-DDThh:mm:ss[Z|(+|-)hh:mm]
+  gst_mpdparser_free_url_type_node (*pointer);
+  *pointer = new_url_type = g_slice_new0 (GstURLType);
 
-    * YYYY indicates the year
-    * MM indicates the month
-    * DD indicates the day
-    * T indicates the start of the required time section
-    * hh indicates the hour
-    * mm indicates the minute
-    * ss indicates the second
+  GST_LOG ("attributes of URLType node:");
+  gst_xml_helper_get_prop_string (a_node, "sourceURL",
+      &new_url_type->sourceURL);
+  gst_xml_helper_get_prop_range (a_node, "range", &new_url_type->range);
+}
 
-  The time zone may be specified as Z (UTC) or (+|-)hh:mm
-*/
-static gboolean
-gst_mpdparser_get_xml_prop_dateTime (xmlNode * a_node,
-    const gchar * property_name, GstDateTime ** property_value)
+static void
+gst_mpdparser_parse_seg_base_type_ext (GstSegmentBaseType ** pointer,
+    xmlNode * a_node, GstSegmentBaseType * parent)
 {
-  xmlChar *prop_string;
-  gchar *str;
-  gint ret, pos;
-  gint year, month, day, hour, minute;
-  gdouble second;
-  gboolean exists = FALSE;
-  gfloat tzoffset = 0.0;
-  gint gmt_offset_hour = -99, gmt_offset_min = -99;
-
-  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
-  if (prop_string) {
-    str = (gchar *) prop_string;
-    GST_TRACE ("dateTime: %s, len %d", str, xmlStrlen (prop_string));
-    /* parse year */
-    ret = sscanf (str, "%d", &year);
-    if (ret != 1 || year <= 0)
-      goto error;
-    pos = strcspn (str, "-");
-    str += (pos + 1);
-    GST_TRACE (" - year %d", year);
-    /* parse month */
-    ret = sscanf (str, "%d", &month);
-    if (ret != 1 || month <= 0)
-      goto error;
-    pos = strcspn (str, "-");
-    str += (pos + 1);
-    GST_TRACE (" - month %d", month);
-    /* parse day */
-    ret = sscanf (str, "%d", &day);
-    if (ret != 1 || day <= 0)
-      goto error;
-    pos = strcspn (str, "T");
-    str += (pos + 1);
-    GST_TRACE (" - day %d", day);
-    /* parse hour */
-    ret = sscanf (str, "%d", &hour);
-    if (ret != 1 || hour < 0)
-      goto error;
-    pos = strcspn (str, ":");
-    str += (pos + 1);
-    GST_TRACE (" - hour %d", hour);
-    /* parse minute */
-    ret = sscanf (str, "%d", &minute);
-    if (ret != 1 || minute < 0)
-      goto error;
-    pos = strcspn (str, ":");
-    str += (pos + 1);
-    GST_TRACE (" - minute %d", minute);
-    /* parse second */
-    ret = sscanf (str, "%lf", &second);
-    if (ret != 1 || second < 0)
-      goto error;
-    GST_TRACE (" - second %lf", second);
-
-    GST_LOG (" - %s: %4d/%02d/%02d %02d:%02d:%09.6lf", property_name,
-        year, month, day, hour, minute, second);
-
-    if (strrchr (str, '+') || strrchr (str, '-')) {
-      /* reuse some code from gst-plugins-base/gst-libs/gst/tag/gstxmptag.c */
-      gint gmt_offset = -1;
-      gchar *plus_pos = NULL;
-      gchar *neg_pos = NULL;
-      gchar *pos = NULL;
-
-      GST_LOG ("Checking for timezone information");
-
-      /* check if there is timezone info */
-      plus_pos = strrchr (str, '+');
-      neg_pos = strrchr (str, '-');
-      if (plus_pos)
-        pos = plus_pos + 1;
-      else if (neg_pos)
-        pos = neg_pos + 1;
-
-      if (pos && strlen (pos) >= 3) {
-        gint ret_tz;
-        if (pos[2] == ':')
-          ret_tz = sscanf (pos, "%d:%d", &gmt_offset_hour, &gmt_offset_min);
-        else
-          ret_tz = sscanf (pos, "%02d%02d", &gmt_offset_hour, &gmt_offset_min);
-
-        GST_DEBUG ("Parsing timezone: %s", pos);
-
-        if (ret_tz == 2) {
-          if (neg_pos != NULL && neg_pos + 1 == pos) {
-            gmt_offset_hour *= -1;
-            gmt_offset_min *= -1;
-          }
-          gmt_offset = gmt_offset_hour * 60 + gmt_offset_min;
+  xmlNode *cur_node;
+  GstSegmentBaseType *seg_base_type;
+  guint intval;
+  guint64 int64val;
+  gboolean boolval;
+  GstXMLRange *rangeval;
 
-          tzoffset = gmt_offset / 60.0;
+  gst_mpdparser_free_seg_base_type_ext (*pointer);
+  *pointer = seg_base_type = g_slice_new0 (GstSegmentBaseType);
 
-          GST_LOG ("Timezone offset: %f (%d minutes)", tzoffset, gmt_offset);
-        } else
-          GST_WARNING ("Failed to parse timezone information");
-      }
-    }
+  /* Initialize values that have defaults */
+  seg_base_type->indexRangeExact = FALSE;
+  seg_base_type->timescale = 1;
 
-    exists = TRUE;
-    *property_value =
-        gst_date_time_new (tzoffset, year, month, day, hour, minute, second);
-    xmlFree (prop_string);
+  /* Inherit attribute values from parent */
+  if (parent) {
+    seg_base_type->timescale = parent->timescale;
+    seg_base_type->presentationTimeOffset = parent->presentationTimeOffset;
+    seg_base_type->indexRange = gst_xml_helper_clone_range (parent->indexRange);
+    seg_base_type->indexRangeExact = parent->indexRangeExact;
+    seg_base_type->Initialization =
+        gst_mpdparser_clone_URL (parent->Initialization);
+    seg_base_type->RepresentationIndex =
+        gst_mpdparser_clone_URL (parent->RepresentationIndex);
   }
 
-  return exists;
-
-error:
-  GST_WARNING ("failed to parse property %s from xml string %s", property_name,
-      prop_string);
-  xmlFree (prop_string);
-  return FALSE;
-}
-
-
-/*
-  Duration Data Type
-
-  The duration data type is used to specify a time interval.
-
-  The time interval is specified in the following form "-PnYnMnDTnHnMnS" where:
-
-    * - indicates the negative sign (optional)
-    * P indicates the period (required)
-    * nY indicates the number of years
-    * nM indicates the number of months
-    * nD indicates the number of days
-    * T indicates the start of a time section (required if you are going to specify hours, minutes, or seconds)
-    * nH indicates the number of hours
-    * nM indicates the number of minutes
-    * nS indicates the number of seconds
-*/
-
-/* this function computes decimals * 10 ^ (3 - pos) */
-static guint
-convert_to_millisecs (guint decimals, gint pos)
-{
-  guint num = 1, den = 1;
-  gint i = 3 - pos;
-
-  while (i < 0) {
-    den *= 10;
-    i++;
-  }
-  while (i > 0) {
-    num *= 10;
-    i--;
-  }
-  /* if i == 0 we have exactly 3 decimals and nothing to do */
-  return decimals * num / den;
-}
-
-static gboolean
-accumulate (guint64 * v, guint64 mul, guint64 add)
-{
-  guint64 tmp;
-
-  if (*v > G_MAXUINT64 / mul)
-    return FALSE;
-  tmp = *v * mul;
-  if (tmp > G_MAXUINT64 - add)
-    return FALSE;
-  *v = tmp + add;
-  return TRUE;
-}
-
-static gboolean
-gst_mpdparser_parse_duration (const char *str, guint64 * value)
-{
-  gint ret, len, pos, posT;
-  gint years = -1, months = -1, days = -1, hours = -1, minutes = -1, seconds =
-      -1, decimals = -1, read;
-  gboolean have_ms = FALSE;
-  guint64 tmp_value;
-
-  len = strlen (str);
-  GST_TRACE ("duration: %s, len %d", str, len);
-  if (strspn (str, "PT0123456789., \tHMDSY") < len) {
-    GST_WARNING ("Invalid character found: '%s'", str);
-    goto error;
+  /* We must retrieve each value first to see if it exists.  If it does not
+   * exist, we do not want to overwrite an inherited value */
+  GST_LOG ("attributes of SegmentBaseType extension:");
+  if (gst_xml_helper_get_prop_unsigned_integer (a_node, "timescale", 1,
+          &intval)) {
+    seg_base_type->timescale = intval;
   }
-  /* skip leading/trailing whitespace */
-  while (g_ascii_isspace (str[0])) {
-    str++;
-    len--;
+  if (gst_xml_helper_get_prop_unsigned_integer_64 (a_node,
+          "presentationTimeOffset", 0, &int64val)) {
+    seg_base_type->presentationTimeOffset = int64val;
   }
-  while (len > 0 && g_ascii_isspace (str[len - 1]))
-    --len;
-
-  /* read "P" for period */
-  if (str[0] != 'P') {
-    GST_WARNING ("P not found at the beginning of the string!");
-    goto error;
+  if (gst_xml_helper_get_prop_range (a_node, "indexRange", &rangeval)) {
+    if (seg_base_type->indexRange) {
+      g_slice_free (GstXMLRange, seg_base_type->indexRange);
+    }
+    seg_base_type->indexRange = rangeval;
   }
-  str++;
-  len--;
-
-  /* read "T" for time (if present) */
-  posT = strcspn (str, "T");
-  len -= posT;
-  if (posT > 0) {
-    /* there is some room between P and T, so there must be a period section */
-    /* read years, months, days */
-    do {
-      GST_TRACE ("parsing substring %s", str);
-      pos = strcspn (str, "YMD");
-      ret = sscanf (str, "%u", &read);
-      if (ret != 1) {
-        GST_WARNING ("can not read integer value from string %s!", str);
-        goto error;
-      }
-      switch (str[pos]) {
-        case 'Y':
-          if (years != -1 || months != -1 || days != -1) {
-            GST_WARNING ("year, month or day was already set");
-            goto error;
-          }
-          years = read;
-          break;
-        case 'M':
-          if (months != -1 || days != -1) {
-            GST_WARNING ("month or day was already set");
-            goto error;
-          }
-          months = read;
-          if (months >= 12) {
-            GST_WARNING ("Month out of range");
-            goto error;
-          }
-          break;
-        case 'D':
-          if (days != -1) {
-            GST_WARNING ("day was already set");
-            goto error;
-          }
-          days = read;
-          if (days >= 31) {
-            GST_WARNING ("Day out of range");
-            goto error;
-          }
-          break;
-        default:
-          GST_WARNING ("unexpected char %c!", str[pos]);
-          goto error;
-          break;
-      }
-      GST_TRACE ("read number %u type %c", read, str[pos]);
-      str += (pos + 1);
-      posT -= (pos + 1);
-    } while (posT > 0);
+  if (gst_xml_helper_get_prop_boolean (a_node, "indexRangeExact",
+          FALSE, &boolval)) {
+    seg_base_type->indexRangeExact = boolval;
   }
 
-  if (years == -1)
-    years = 0;
-  if (months == -1)
-    months = 0;
-  if (days == -1)
-    days = 0;
-
-  GST_TRACE ("Y:M:D=%d:%d:%d", years, months, days);
-
-  /* read "T" for time (if present) */
-  /* here T is at pos == 0 */
-  str++;
-  len--;
-  pos = 0;
-  if (pos < len) {
-    /* T found, there is a time section */
-    /* read hours, minutes, seconds, hundredths of second */
-    do {
-      GST_TRACE ("parsing substring %s", str);
-      pos = strcspn (str, "HMS,.");
-      ret = sscanf (str, "%u", &read);
-      if (ret != 1) {
-        GST_WARNING ("can not read integer value from string %s!", str);
-        goto error;
-      }
-      switch (str[pos]) {
-        case 'H':
-          if (hours != -1 || minutes != -1 || seconds != -1) {
-            GST_WARNING ("hour, minute or second was already set");
-            goto error;
-          }
-          hours = read;
-          if (hours >= 24) {
-            GST_WARNING ("Hour out of range");
-            goto error;
-          }
-          break;
-        case 'M':
-          if (minutes != -1 || seconds != -1) {
-            GST_WARNING ("minute or second was already set");
-            goto error;
-          }
-          minutes = read;
-          if (minutes >= 60) {
-            GST_WARNING ("Minute out of range");
-            goto error;
-          }
-          break;
-        case 'S':
-          if (have_ms) {
-            /* we have read the decimal part of the seconds */
-            decimals = convert_to_millisecs (read, pos);
-            GST_TRACE ("decimal number %u (%d digits) -> %d ms", read, pos,
-                decimals);
-          } else {
-            if (seconds != -1) {
-              GST_WARNING ("second was already set");
-              goto error;
-            }
-            /* no decimals */
-            seconds = read;
-          }
-          break;
-        case '.':
-        case ',':
-          /* we have read the integer part of a decimal number in seconds */
-          if (seconds != -1) {
-            GST_WARNING ("second was already set");
-            goto error;
-          }
-          seconds = read;
-          have_ms = TRUE;
-          break;
-        default:
-          GST_WARNING ("unexpected char %c!", str[pos]);
-          goto error;
-          break;
+  /* explore children nodes */
+  for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
+    if (cur_node->type == XML_ELEMENT_NODE) {
+      if (xmlStrcmp (cur_node->name, (xmlChar *) "Initialization") == 0 ||
+          xmlStrcmp (cur_node->name, (xmlChar *) "Initialisation") == 0) {
+        /* parse will free the previous pointer to create a new one */
+        gst_mpdparser_parse_url_type_node (&seg_base_type->Initialization,
+            cur_node);
+      } else if (xmlStrcmp (cur_node->name,
+              (xmlChar *) "RepresentationIndex") == 0) {
+        /* parse will free the previous pointer to create a new one */
+        gst_mpdparser_parse_url_type_node (&seg_base_type->RepresentationIndex,
+            cur_node);
       }
-      GST_TRACE ("read number %u type %c", read, str[pos]);
-      str += pos + 1;
-      len -= (pos + 1);
-    } while (len > 0);
+    }
   }
-
-  if (hours == -1)
-    hours = 0;
-  if (minutes == -1)
-    minutes = 0;
-  if (seconds == -1)
-    seconds = 0;
-  if (decimals == -1)
-    decimals = 0;
-  GST_TRACE ("H:M:S.MS=%d:%d:%d.%03d", hours, minutes, seconds, decimals);
-
-  tmp_value = 0;
-  if (!accumulate (&tmp_value, 1, years)
-      || !accumulate (&tmp_value, 365, months * 30)
-      || !accumulate (&tmp_value, 1, days)
-      || !accumulate (&tmp_value, 24, hours)
-      || !accumulate (&tmp_value, 60, minutes)
-      || !accumulate (&tmp_value, 60, seconds)
-      || !accumulate (&tmp_value, 1000, decimals))
-    goto error;
-
-  /* ensure it can be converted from milliseconds to nanoseconds */
-  if (tmp_value > G_MAXUINT64 / 1000000)
-    goto error;
-
-  *value = tmp_value;
-  return TRUE;
-
-error:
-  return FALSE;
 }
 
-static gboolean
-gst_mpdparser_get_xml_prop_duration (xmlNode * a_node,
-    const gchar * property_name, guint64 default_value,
-    guint64 * property_value)
+static GstSNode *
+gst_mpdparser_clone_s_node (GstSNode * pointer)
 {
-  xmlChar *prop_string;
-  gchar *str;
-  gboolean exists = FALSE;
-
-  *property_value = default_value;
-  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
-  if (prop_string) {
-    str = (gchar *) prop_string;
-    if (!gst_mpdparser_parse_duration (str, property_value))
-      goto error;
-    GST_LOG (" - %s: %" G_GUINT64_FORMAT, property_name, *property_value);
-    xmlFree (prop_string);
-    exists = TRUE;
-  }
-  return exists;
-
-error:
-  xmlFree (prop_string);
-  return FALSE;
-}
+  GstSNode *clone = NULL;
 
-static gboolean
-gst_mpdparser_get_xml_node_content (xmlNode * a_node, gchar ** content)
-{
-  xmlChar *node_content = NULL;
-  gboolean exists = FALSE;
-
-  node_content = xmlNodeGetContent (a_node);
-  if (node_content) {
-    exists = TRUE;
-    *content = (gchar *) node_content;
-    GST_LOG (" - %s: %s", a_node->name, *content);
+  if (pointer) {
+    clone = g_slice_new0 (GstSNode);
+    clone->t = pointer->t;
+    clone->d = pointer->d;
+    clone->r = pointer->r;
   }
 
-  return exists;
+  return clone;
 }
 
-static gboolean
-gst_mpdparser_get_xml_node_as_string (xmlNode * a_node, gchar ** content)
+static void
+gst_mpdparser_parse_s_node (GQueue * queue, xmlNode * a_node)
 {
-  gboolean exists = FALSE;
-  const char *txt_encoding;
-  xmlOutputBufferPtr out_buf;
-
-  txt_encoding = (const char *) a_node->doc->encoding;
-  out_buf = xmlAllocOutputBuffer (NULL);
-  g_assert (out_buf != NULL);
-  xmlNodeDumpOutput (out_buf, a_node->doc, a_node, 0, 0, txt_encoding);
-  xmlOutputBufferFlush (out_buf);
-#ifdef LIBXML2_NEW_BUFFER
-  if (xmlOutputBufferGetSize (out_buf) > 0) {
-    *content =
-        (gchar *) xmlStrndup (xmlOutputBufferGetContent (out_buf),
-        xmlOutputBufferGetSize (out_buf));
-    exists = TRUE;
-  }
-#else
-  if (out_buf->conv && out_buf->conv->use > 0) {
-    *content =
-        (gchar *) xmlStrndup (out_buf->conv->content, out_buf->conv->use);
-    exists = TRUE;
-  } else if (out_buf->buffer && out_buf->buffer->use > 0) {
-    *content =
-        (gchar *) xmlStrndup (out_buf->buffer->content, out_buf->buffer->use);
-    exists = TRUE;
-  }
-#endif // LIBXML2_NEW_BUFFER
-  (void) xmlOutputBufferClose (out_buf);
+  GstSNode *new_s_node;
 
-  if (exists) {
-    GST_LOG (" - %s: %s", a_node->name, *content);
-  }
-  return exists;
+  new_s_node = g_slice_new0 (GstSNode);
+  g_queue_push_tail (queue, new_s_node);
+
+  GST_LOG ("attributes of S node:");
+  gst_xml_helper_get_prop_unsigned_integer_64 (a_node, "t", 0, &new_s_node->t);
+  gst_xml_helper_get_prop_unsigned_integer_64 (a_node, "d", 0, &new_s_node->d);
+  gst_xml_helper_get_prop_signed_integer (a_node, "r", 0, &new_s_node->r);
 }
 
-static gchar *
-gst_mpdparser_get_xml_node_namespace (xmlNode * a_node, const gchar * prefix)
+static GstSegmentTimelineNode *
+gst_mpdparser_clone_segment_timeline (GstSegmentTimelineNode * pointer)
 {
-  xmlNs *curr_ns;
-  gchar *namespace = NULL;
-
-  if (prefix == NULL) {
-    /* return the default namespace */
-    if (a_node->ns) {
-      namespace = xmlMemStrdup ((const gchar *) a_node->ns->href);
-      if (namespace) {
-        GST_LOG (" - default namespace: %s", namespace);
-      }
-    }
-  } else {
-    /* look for the specified prefix in the namespace list */
-    for (curr_ns = a_node->ns; curr_ns; curr_ns = curr_ns->next) {
-      if (xmlStrcmp (curr_ns->prefix, (xmlChar *) prefix) == 0) {
-        namespace = xmlMemStrdup ((const gchar *) curr_ns->href);
-        if (namespace) {
-          GST_LOG (" - %s namespace: %s", curr_ns->prefix, curr_ns->href);
+  GstSegmentTimelineNode *clone = NULL;
+
+  if (pointer) {
+    clone = gst_mpdparser_segment_timeline_node_new ();
+    if (clone) {
+      GList *list;
+      for (list = g_queue_peek_head_link (&pointer->S); list;
+          list = g_list_next (list)) {
+        GstSNode *s_node;
+        s_node = (GstSNode *) list->data;
+        if (s_node) {
+          g_queue_push_tail (&clone->S, gst_mpdparser_clone_s_node (s_node));
         }
       }
+    } else {
+      GST_WARNING ("Allocation of SegmentTimeline node failed!");
     }
   }
 
-  return namespace;
+  return clone;
 }
 
 static void
-gst_mpdparser_parse_baseURL_node (GList ** list, xmlNode * a_node)
+gst_mpdparser_parse_segment_timeline_node (GstSegmentTimelineNode ** pointer,
+    xmlNode * a_node)
 {
-  GstBaseURL *new_base_url;
-
-  new_base_url = g_slice_new0 (GstBaseURL);
-  *list = g_list_append (*list, new_base_url);
+  xmlNode *cur_node;
+  GstSegmentTimelineNode *new_seg_timeline;
 
-  GST_LOG ("content of BaseURL node:");
-  gst_mpdparser_get_xml_node_content (a_node, &new_base_url->baseURL);
+  gst_mpdparser_free_segment_timeline_node (*pointer);
+  *pointer = new_seg_timeline = gst_mpdparser_segment_timeline_node_new ();
+  if (new_seg_timeline == NULL) {
+    GST_WARNING ("Allocation of SegmentTimeline node failed!");
+    return;
+  }
 
-  GST_LOG ("attributes of BaseURL node:");
-  gst_mpdparser_get_xml_prop_string (a_node, "serviceLocation",
-      &new_base_url->serviceLocation);
-  gst_mpdparser_get_xml_prop_string (a_node, "byteRange",
-      &new_base_url->byteRange);
+  /* explore children nodes */
+  for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
+    if (cur_node->type == XML_ELEMENT_NODE) {
+      if (xmlStrcmp (cur_node->name, (xmlChar *) "S") == 0) {
+        gst_mpdparser_parse_s_node (&new_seg_timeline->S, cur_node);
+      }
+    }
+  }
 }
 
-static void
-gst_mpdparser_parse_descriptor_type_node (GList ** list, xmlNode * a_node)
+static gboolean
+gst_mpdparser_parse_mult_seg_base_type_ext (GstMultSegmentBaseType ** pointer,
+    xmlNode * a_node, GstMultSegmentBaseType * parent)
 {
-  GstDescriptorType *new_descriptor;
+  xmlNode *cur_node;
+  GstMultSegmentBaseType *mult_seg_base_type;
+  guint intval;
+  gboolean has_timeline = FALSE, has_duration = FALSE;
 
-  new_descriptor = g_slice_new0 (GstDescriptorType);
-  *list = g_list_append (*list, new_descriptor);
+  gst_mpdparser_free_mult_seg_base_type_ext (*pointer);
+  mult_seg_base_type = g_slice_new0 (GstMultSegmentBaseType);
 
-  GST_LOG ("attributes of %s node:", a_node->name);
-  gst_mpdparser_get_xml_prop_string_stripped (a_node, "schemeIdUri",
-      &new_descriptor->schemeIdUri);
-  if (!gst_mpdparser_get_xml_prop_string (a_node, "value",
-          &new_descriptor->value)) {
-    /* if no value attribute, use XML string representation of the node */
-    gst_mpdparser_get_xml_node_as_string (a_node, &new_descriptor->value);
+  mult_seg_base_type->duration = 0;
+  mult_seg_base_type->startNumber = 1;
+
+  /* Inherit attribute values from parent */
+  if (parent) {
+    mult_seg_base_type->duration = parent->duration;
+    mult_seg_base_type->startNumber = parent->startNumber;
+    mult_seg_base_type->SegmentTimeline =
+        gst_mpdparser_clone_segment_timeline (parent->SegmentTimeline);
+    mult_seg_base_type->BitstreamSwitching =
+        gst_mpdparser_clone_URL (parent->BitstreamSwitching);
   }
-}
 
-static void
-gst_mpdparser_parse_content_component_node (GList ** list, xmlNode * a_node)
-{
-  xmlNode *cur_node;
-  GstContentComponentNode *new_content_component;
+  GST_LOG ("attributes of MultipleSegmentBaseType extension:");
+  if (gst_xml_helper_get_prop_unsigned_integer (a_node, "duration", 0, &intval)) {
+    mult_seg_base_type->duration = intval;
+  }
 
-  new_content_component = g_slice_new0 (GstContentComponentNode);
-  *list = g_list_append (*list, new_content_component);
+  /* duration might be specified from parent */
+  if (mult_seg_base_type->duration)
+    has_duration = TRUE;
 
-  GST_LOG ("attributes of ContentComponent node:");
-  gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "id", 0,
-      &new_content_component->id);
-  gst_mpdparser_get_xml_prop_string (a_node, "lang",
-      &new_content_component->lang);
-  gst_mpdparser_get_xml_prop_string (a_node, "contentType",
-      &new_content_component->contentType);
-  gst_mpdparser_get_xml_prop_ratio (a_node, "par", &new_content_component->par);
+  if (gst_xml_helper_get_prop_unsigned_integer (a_node, "startNumber", 1,
+          &intval)) {
+    mult_seg_base_type->startNumber = intval;
+  }
+
+  GST_LOG ("extension of MultipleSegmentBaseType extension:");
+  gst_mpdparser_parse_seg_base_type_ext (&mult_seg_base_type->SegBaseType,
+      a_node, (parent ? parent->SegBaseType : NULL));
 
   /* explore children nodes */
   for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
     if (cur_node->type == XML_ELEMENT_NODE) {
-      if (xmlStrcmp (cur_node->name, (xmlChar *) "Accessibility") == 0) {
-        gst_mpdparser_parse_descriptor_type_node
-            (&new_content_component->Accessibility, cur_node);
-      } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Role") == 0) {
-        gst_mpdparser_parse_descriptor_type_node (&new_content_component->Role,
-            cur_node);
-      } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Rating") == 0) {
-        gst_mpdparser_parse_descriptor_type_node
-            (&new_content_component->Rating, cur_node);
-      } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Viewpoint") == 0) {
-        gst_mpdparser_parse_descriptor_type_node
-            (&new_content_component->Viewpoint, cur_node);
+      if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentTimeline") == 0) {
+        /* parse frees the segmenttimeline if any */
+        gst_mpdparser_parse_segment_timeline_node
+            (&mult_seg_base_type->SegmentTimeline, cur_node);
+      } else if (xmlStrcmp (cur_node->name,
+              (xmlChar *) "BitstreamSwitching") == 0) {
+        /* parse frees the old url before setting the new one */
+        gst_mpdparser_parse_url_type_node
+            (&mult_seg_base_type->BitstreamSwitching, cur_node);
       }
     }
   }
-}
 
-static void
-gst_mpdparser_parse_location_node (GList ** list, xmlNode * a_node)
-{
-  gchar *location = NULL;
+  has_timeline = mult_seg_base_type->SegmentTimeline != NULL;
 
-  GST_LOG ("content of Location node:");
-  if (gst_mpdparser_get_xml_node_content (a_node, &location))
-    *list = g_list_append (*list, location);
-}
+  /* Checking duration and timeline only at Representation's child level */
+  if (xmlStrcmp (a_node->parent->name, (xmlChar *) "Representation") == 0
+      && !has_duration && !has_timeline) {
+    GST_ERROR ("segment has neither duration nor timeline");
+    goto error;
+  }
 
-static void
-gst_mpdparser_parse_subrepresentation_node (GList ** list, xmlNode * a_node)
-{
-  GstSubRepresentationNode *new_subrep;
+  *pointer = mult_seg_base_type;
+  return TRUE;
 
-  new_subrep = g_slice_new0 (GstSubRepresentationNode);
-  *list = g_list_append (*list, new_subrep);
-
-  GST_LOG ("attributes of SubRepresentation node:");
-  gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "level", 0,
-      &new_subrep->level);
-  gst_mpdparser_get_xml_prop_uint_vector_type (a_node, "dependencyLevel",
-      &new_subrep->dependencyLevel, &new_subrep->size);
-  gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "bandwidth", 0,
-      &new_subrep->bandwidth);
-  gst_mpdparser_get_xml_prop_string_vector_type (a_node,
-      "contentComponent", &new_subrep->contentComponent);
-
-  /* RepresentationBase extension */
-  gst_mpdparser_parse_representation_base_type (&new_subrep->RepresentationBase,
-      a_node);
-}
-
-static GstSegmentURLNode *
-gst_mpdparser_clone_segment_url (GstSegmentURLNode * seg_url)
-{
-  GstSegmentURLNode *clone = NULL;
-
-  if (seg_url) {
-    clone = g_slice_new0 (GstSegmentURLNode);
-    clone->media = xmlMemStrdup (seg_url->media);
-    clone->mediaRange = gst_mpdparser_clone_range (seg_url->mediaRange);
-    clone->index = xmlMemStrdup (seg_url->index);
-    clone->indexRange = gst_mpdparser_clone_range (seg_url->indexRange);
-  }
-
-  return clone;
-}
-
-static void
-gst_mpdparser_parse_segment_url_node (GList ** list, xmlNode * a_node)
-{
-  GstSegmentURLNode *new_segment_url;
-
-  new_segment_url = g_slice_new0 (GstSegmentURLNode);
-  *list = g_list_append (*list, new_segment_url);
-
-  GST_LOG ("attributes of SegmentURL node:");
-  gst_mpdparser_get_xml_prop_string (a_node, "media", &new_segment_url->media);
-  gst_mpdparser_get_xml_prop_range (a_node, "mediaRange",
-      &new_segment_url->mediaRange);
-  gst_mpdparser_get_xml_prop_string (a_node, "index", &new_segment_url->index);
-  gst_mpdparser_get_xml_prop_range (a_node, "indexRange",
-      &new_segment_url->indexRange);
-}
-
-static void
-gst_mpdparser_parse_url_type_node (GstURLType ** pointer, xmlNode * a_node)
-{
-  GstURLType *new_url_type;
-
-  gst_mpdparser_free_url_type_node (*pointer);
-  *pointer = new_url_type = g_slice_new0 (GstURLType);
-
-  GST_LOG ("attributes of URLType node:");
-  gst_mpdparser_get_xml_prop_string (a_node, "sourceURL",
-      &new_url_type->sourceURL);
-  gst_mpdparser_get_xml_prop_range (a_node, "range", &new_url_type->range);
+error:
+  gst_mpdparser_free_mult_seg_base_type_ext (mult_seg_base_type);
+  return FALSE;
 }
 
-static void
-gst_mpdparser_parse_seg_base_type_ext (GstSegmentBaseType ** pointer,
-    xmlNode * a_node, GstSegmentBaseType * parent)
+static gboolean
+gst_mpdparser_parse_segment_list_node (GstSegmentListNode ** pointer,
+    xmlNode * a_node, GstSegmentListNode * parent)
 {
   xmlNode *cur_node;
-  GstSegmentBaseType *seg_base_type;
-  guint intval;
-  guint64 int64val;
-  gboolean boolval;
-  GstRange *rangeval;
-
-  gst_mpdparser_free_seg_base_type_ext (*pointer);
-  *pointer = seg_base_type = g_slice_new0 (GstSegmentBaseType);
+  GstSegmentListNode *new_segment_list;
+  gchar *actuate;
+  gboolean segment_urls_inherited_from_parent = FALSE;
 
-  /* Initialize values that have defaults */
-  seg_base_type->indexRangeExact = FALSE;
-  seg_base_type->timescale = 1;
+  gst_mpdparser_free_segment_list_node (*pointer);
+  new_segment_list = g_slice_new0 (GstSegmentListNode);
 
   /* Inherit attribute values from parent */
   if (parent) {
-    seg_base_type->timescale = parent->timescale;
-    seg_base_type->presentationTimeOffset = parent->presentationTimeOffset;
-    seg_base_type->indexRange = gst_mpdparser_clone_range (parent->indexRange);
-    seg_base_type->indexRangeExact = parent->indexRangeExact;
-    seg_base_type->Initialization =
-        gst_mpdparser_clone_URL (parent->Initialization);
-    seg_base_type->RepresentationIndex =
-        gst_mpdparser_clone_URL (parent->RepresentationIndex);
-  }
-
-  /* We must retrieve each value first to see if it exists.  If it does not
-   * exist, we do not want to overwrite an inherited value */
-  GST_LOG ("attributes of SegmentBaseType extension:");
-  if (gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "timescale", 1,
-          &intval)) {
-    seg_base_type->timescale = intval;
-  }
-  if (gst_mpdparser_get_xml_prop_unsigned_integer_64 (a_node,
-          "presentationTimeOffset", 0, &int64val)) {
-    seg_base_type->presentationTimeOffset = int64val;
-  }
-  if (gst_mpdparser_get_xml_prop_range (a_node, "indexRange", &rangeval)) {
-    if (seg_base_type->indexRange) {
-      g_slice_free (GstRange, seg_base_type->indexRange);
+    GList *list;
+    GstSegmentURLNode *seg_url;
+    for (list = g_list_first (parent->SegmentURL); list;
+        list = g_list_next (list)) {
+      seg_url = (GstSegmentURLNode *) list->data;
+      new_segment_list->SegmentURL =
+          g_list_append (new_segment_list->SegmentURL,
+          gst_mpdparser_clone_segment_url (seg_url));
+      segment_urls_inherited_from_parent = TRUE;
     }
-    seg_base_type->indexRange = rangeval;
   }
-  if (gst_mpdparser_get_xml_prop_boolean (a_node, "indexRangeExact",
-          FALSE, &boolval)) {
-    seg_base_type->indexRangeExact = boolval;
+
+  new_segment_list->actuate = GST_XLINK_ACTUATE_ON_REQUEST;
+  if (gst_xml_helper_get_ns_prop_string (a_node,
+          "http://www.w3.org/1999/xlink", "href", &new_segment_list->xlink_href)
+      && gst_xml_helper_get_ns_prop_string (a_node,
+          "http://www.w3.org/1999/xlink", "actuate", &actuate)) {
+    if (strcmp (actuate, "onLoad") == 0)
+      new_segment_list->actuate = GST_XLINK_ACTUATE_ON_LOAD;
+    xmlFree (actuate);
   }
 
+  GST_LOG ("extension of SegmentList node:");
+  if (!gst_mpdparser_parse_mult_seg_base_type_ext
+      (&new_segment_list->MultSegBaseType, a_node,
+          (parent ? parent->MultSegBaseType : NULL)))
+    goto error;
+
   /* explore children nodes */
   for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
     if (cur_node->type == XML_ELEMENT_NODE) {
-      if (xmlStrcmp (cur_node->name, (xmlChar *) "Initialization") == 0 ||
-          xmlStrcmp (cur_node->name, (xmlChar *) "Initialisation") == 0) {
-        /* parse will free the previous pointer to create a new one */
-        gst_mpdparser_parse_url_type_node (&seg_base_type->Initialization,
-            cur_node);
-      } else if (xmlStrcmp (cur_node->name,
-              (xmlChar *) "RepresentationIndex") == 0) {
-        /* parse will free the previous pointer to create a new one */
-        gst_mpdparser_parse_url_type_node (&seg_base_type->RepresentationIndex,
+      if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentURL") == 0) {
+        if (segment_urls_inherited_from_parent) {
+          /*
+           * SegmentBase, SegmentTemplate and SegmentList shall inherit
+           * attributes and elements from the same element on a higher level.
+           * If the same attribute or element is present on both levels,
+           * the one on the lower level shall take precedence over the one
+           * on the higher level.
+           */
+
+          /* Clear the list of inherited segment URLs */
+          g_list_free_full (new_segment_list->SegmentURL,
+              (GDestroyNotify) gst_mpdparser_free_segment_url_node);
+          new_segment_list->SegmentURL = NULL;
+
+          /* mark the fact that we cleared the list, so that it is not tried again */
+          segment_urls_inherited_from_parent = FALSE;
+        }
+        gst_mpdparser_parse_segment_url_node (&new_segment_list->SegmentURL,
             cur_node);
       }
     }
   }
-}
-
-static GstSNode *
-gst_mpdparser_clone_s_node (GstSNode * pointer)
-{
-  GstSNode *clone = NULL;
 
-  if (pointer) {
-    clone = g_slice_new0 (GstSNode);
-    clone->t = pointer->t;
-    clone->d = pointer->d;
-    clone->r = pointer->r;
-  }
+  *pointer = new_segment_list;
+  return TRUE;
 
-  return clone;
+error:
+  gst_mpdparser_free_segment_list_node (new_segment_list);
+  return FALSE;
 }
 
 static void
-gst_mpdparser_parse_s_node (GQueue * queue, xmlNode * a_node)
+gst_mpdparser_parse_content_protection_node (GList ** list, xmlNode * a_node)
 {
-  GstSNode *new_s_node;
-
-  new_s_node = g_slice_new0 (GstSNode);
-  g_queue_push_tail (queue, new_s_node);
-
-  GST_LOG ("attributes of S node:");
-  gst_mpdparser_get_xml_prop_unsigned_integer_64 (a_node, "t", 0,
-      &new_s_node->t);
-  gst_mpdparser_get_xml_prop_unsigned_integer_64 (a_node, "d", 0,
-      &new_s_node->d);
-  gst_mpdparser_get_xml_prop_signed_integer (a_node, "r", 0, &new_s_node->r);
-}
+  gchar *value = NULL;
+  if (gst_xml_helper_get_prop_string (a_node, "value", &value)) {
+    if (!g_strcmp0 (value, "MSPR 2.0")) {
+      xmlNode *cur_node;
+      for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
+        if (cur_node->type == XML_ELEMENT_NODE) {
+          if (xmlStrcmp (cur_node->name, (xmlChar *) "pro") == 0) {
+            GstDescriptorType *new_descriptor;
+            new_descriptor = g_slice_new0 (GstDescriptorType);
+            *list = g_list_append (*list, new_descriptor);
 
-static GstSegmentTimelineNode *
-gst_mpdparser_clone_segment_timeline (GstSegmentTimelineNode * pointer)
-{
-  GstSegmentTimelineNode *clone = NULL;
+            gst_xml_helper_get_prop_string_stripped (a_node, "schemeIdUri",
+                &new_descriptor->schemeIdUri);
 
-  if (pointer) {
-    clone = gst_mpdparser_segment_timeline_node_new ();
-    if (clone) {
-      GList *list;
-      for (list = g_queue_peek_head_link (&pointer->S); list;
-          list = g_list_next (list)) {
-        GstSNode *s_node;
-        s_node = (GstSNode *) list->data;
-        if (s_node) {
-          g_queue_push_tail (&clone->S, gst_mpdparser_clone_s_node (s_node));
+            gst_xml_helper_get_node_content (cur_node, &new_descriptor->value);
+            goto beach;
+          }
         }
       }
     } else {
-      GST_WARNING ("Allocation of SegmentTimeline node failed!");
+      gst_mpdparser_parse_descriptor_type_node (list, a_node);
     }
+  } else {
+    gst_mpdparser_parse_descriptor_type_node (list, a_node);
   }
-
-  return clone;
+beach:
+  if (value)
+    g_free (value);
 }
 
 static void
-gst_mpdparser_parse_segment_timeline_node (GstSegmentTimelineNode ** pointer,
-    xmlNode * a_node)
+gst_mpdparser_parse_representation_base_type (GstRepresentationBaseType **
+    pointer, xmlNode * a_node)
 {
   xmlNode *cur_node;
-  GstSegmentTimelineNode *new_seg_timeline;
+  GstRepresentationBaseType *representation_base;
 
-  gst_mpdparser_free_segment_timeline_node (*pointer);
-  *pointer = new_seg_timeline = gst_mpdparser_segment_timeline_node_new ();
-  if (new_seg_timeline == NULL) {
-    GST_WARNING ("Allocation of SegmentTimeline node failed!");
-    return;
-  }
+  gst_mpdparser_free_representation_base_type (*pointer);
+  *pointer = representation_base = g_slice_new0 (GstRepresentationBaseType);
+
+  GST_LOG ("attributes of RepresentationBaseType extension:");
+  gst_xml_helper_get_prop_string (a_node, "profiles",
+      &representation_base->profiles);
+  gst_xml_helper_get_prop_unsigned_integer (a_node, "width", 0,
+      &representation_base->width);
+  gst_xml_helper_get_prop_unsigned_integer (a_node, "height", 0,
+      &representation_base->height);
+  gst_xml_helper_get_prop_ratio (a_node, "sar", &representation_base->sar);
+  gst_xml_helper_get_prop_framerate (a_node, "frameRate",
+      &representation_base->frameRate);
+  gst_xml_helper_get_prop_framerate (a_node, "minFrameRate",
+      &representation_base->minFrameRate);
+  gst_xml_helper_get_prop_framerate (a_node, "maxFrameRate",
+      &representation_base->maxFrameRate);
+  gst_xml_helper_get_prop_string (a_node, "audioSamplingRate",
+      &representation_base->audioSamplingRate);
+  gst_xml_helper_get_prop_string (a_node, "mimeType",
+      &representation_base->mimeType);
+  gst_xml_helper_get_prop_string (a_node, "segmentProfiles",
+      &representation_base->segmentProfiles);
+  gst_xml_helper_get_prop_string (a_node, "codecs",
+      &representation_base->codecs);
+  gst_xml_helper_get_prop_double (a_node, "maximumSAPPeriod",
+      &representation_base->maximumSAPPeriod);
+  gst_mpd_helper_get_SAP_type (a_node, "startWithSAP",
+      &representation_base->startWithSAP);
+  gst_xml_helper_get_prop_double (a_node, "maxPlayoutRate",
+      &representation_base->maxPlayoutRate);
+  gst_xml_helper_get_prop_boolean (a_node, "codingDependency",
+      FALSE, &representation_base->codingDependency);
+  gst_xml_helper_get_prop_string (a_node, "scanType",
+      &representation_base->scanType);
 
   /* explore children nodes */
   for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
     if (cur_node->type == XML_ELEMENT_NODE) {
-      if (xmlStrcmp (cur_node->name, (xmlChar *) "S") == 0) {
-        gst_mpdparser_parse_s_node (&new_seg_timeline->S, cur_node);
+      if (xmlStrcmp (cur_node->name, (xmlChar *) "FramePacking") == 0) {
+        gst_mpdparser_parse_descriptor_type_node
+            (&representation_base->FramePacking, cur_node);
+      } else if (xmlStrcmp (cur_node->name,
+              (xmlChar *) "AudioChannelConfiguration") == 0) {
+        gst_mpdparser_parse_descriptor_type_node
+            (&representation_base->AudioChannelConfiguration, cur_node);
+      } else if (xmlStrcmp (cur_node->name,
+              (xmlChar *) "ContentProtection") == 0) {
+        gst_mpdparser_parse_content_protection_node
+            (&representation_base->ContentProtection, cur_node);
       }
     }
   }
 }
 
 static gboolean
-gst_mpdparser_parse_mult_seg_base_type_ext (GstMultSegmentBaseType ** pointer,
-    xmlNode * a_node, GstMultSegmentBaseType * parent)
-{
-  xmlNode *cur_node;
-  GstMultSegmentBaseType *mult_seg_base_type;
-  guint intval;
-  gboolean has_timeline = FALSE, has_duration = FALSE;
-
-  gst_mpdparser_free_mult_seg_base_type_ext (*pointer);
-  mult_seg_base_type = g_slice_new0 (GstMultSegmentBaseType);
-
-  mult_seg_base_type->duration = 0;
-  mult_seg_base_type->startNumber = 1;
-
-  /* Inherit attribute values from parent */
-  if (parent) {
-    mult_seg_base_type->duration = parent->duration;
-    mult_seg_base_type->startNumber = parent->startNumber;
-    mult_seg_base_type->SegmentTimeline =
-        gst_mpdparser_clone_segment_timeline (parent->SegmentTimeline);
-    mult_seg_base_type->BitstreamSwitching =
-        gst_mpdparser_clone_URL (parent->BitstreamSwitching);
-  }
-
-  GST_LOG ("attributes of MultipleSegmentBaseType extension:");
-  if (gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "duration", 0,
-          &intval)) {
-    mult_seg_base_type->duration = intval;
-  }
-
-  /* duration might be specified from parent */
-  if (mult_seg_base_type->duration)
-    has_duration = TRUE;
-
-  if (gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "startNumber", 1,
-          &intval)) {
-    mult_seg_base_type->startNumber = intval;
-  }
-
-  GST_LOG ("extension of MultipleSegmentBaseType extension:");
-  gst_mpdparser_parse_seg_base_type_ext (&mult_seg_base_type->SegBaseType,
-      a_node, (parent ? parent->SegBaseType : NULL));
-
-  /* explore children nodes */
-  for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
-    if (cur_node->type == XML_ELEMENT_NODE) {
-      if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentTimeline") == 0) {
-        /* parse frees the segmenttimeline if any */
-        gst_mpdparser_parse_segment_timeline_node
-            (&mult_seg_base_type->SegmentTimeline, cur_node);
-      } else if (xmlStrcmp (cur_node->name,
-              (xmlChar *) "BitstreamSwitching") == 0) {
-        /* parse frees the old url before setting the new one */
-        gst_mpdparser_parse_url_type_node
-            (&mult_seg_base_type->BitstreamSwitching, cur_node);
-      }
-    }
-  }
-
-  has_timeline = mult_seg_base_type->SegmentTimeline != NULL;
-
-  /* Checking duration and timeline only at Representation's child level */
-  if (xmlStrcmp (a_node->parent->name, (xmlChar *) "Representation") == 0
-      && !has_duration && !has_timeline) {
-    GST_ERROR ("segment has neither duration nor timeline");
-    goto error;
-  }
-
-  *pointer = mult_seg_base_type;
-  return TRUE;
-
-error:
-  gst_mpdparser_free_mult_seg_base_type_ext (mult_seg_base_type);
-  return FALSE;
-}
-
-static gboolean
-gst_mpdparser_parse_segment_list_node (GstSegmentListNode ** pointer,
-    xmlNode * a_node, GstSegmentListNode * parent)
-{
-  xmlNode *cur_node;
-  GstSegmentListNode *new_segment_list;
-  gchar *actuate;
-  gboolean segment_urls_inherited_from_parent = FALSE;
-
-  gst_mpdparser_free_segment_list_node (*pointer);
-  new_segment_list = g_slice_new0 (GstSegmentListNode);
-
-  /* Inherit attribute values from parent */
-  if (parent) {
-    GList *list;
-    GstSegmentURLNode *seg_url;
-    for (list = g_list_first (parent->SegmentURL); list;
-        list = g_list_next (list)) {
-      seg_url = (GstSegmentURLNode *) list->data;
-      new_segment_list->SegmentURL =
-          g_list_append (new_segment_list->SegmentURL,
-          gst_mpdparser_clone_segment_url (seg_url));
-      segment_urls_inherited_from_parent = TRUE;
-    }
-  }
-
-  new_segment_list->actuate = GST_XLINK_ACTUATE_ON_REQUEST;
-  if (gst_mpdparser_get_xml_ns_prop_string (a_node,
-          "http://www.w3.org/1999/xlink", "href", &new_segment_list->xlink_href)
-      && gst_mpdparser_get_xml_ns_prop_string (a_node,
-          "http://www.w3.org/1999/xlink", "actuate", &actuate)) {
-    if (strcmp (actuate, "onLoad") == 0)
-      new_segment_list->actuate = GST_XLINK_ACTUATE_ON_LOAD;
-    xmlFree (actuate);
-  }
-
-  GST_LOG ("extension of SegmentList node:");
-  if (!gst_mpdparser_parse_mult_seg_base_type_ext
-      (&new_segment_list->MultSegBaseType, a_node,
-          (parent ? parent->MultSegBaseType : NULL)))
-    goto error;
-
-  /* explore children nodes */
-  for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
-    if (cur_node->type == XML_ELEMENT_NODE) {
-      if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentURL") == 0) {
-        if (segment_urls_inherited_from_parent) {
-          /*
-           * SegmentBase, SegmentTemplate and SegmentList shall inherit
-           * attributes and elements from the same element on a higher level.
-           * If the same attribute or element is present on both levels,
-           * the one on the lower level shall take precedence over the one
-           * on the higher level.
-           */
-
-          /* Clear the list of inherited segment URLs */
-          g_list_free_full (new_segment_list->SegmentURL,
-              (GDestroyNotify) gst_mpdparser_free_segment_url_node);
-          new_segment_list->SegmentURL = NULL;
-
-          /* mark the fact that we cleared the list, so that it is not tried again */
-          segment_urls_inherited_from_parent = FALSE;
-        }
-        gst_mpdparser_parse_segment_url_node (&new_segment_list->SegmentURL,
-            cur_node);
-      }
-    }
-  }
-
-  *pointer = new_segment_list;
-  return TRUE;
-
-error:
-  gst_mpdparser_free_segment_list_node (new_segment_list);
-  return FALSE;
-}
-
-static void
-gst_mpdparser_parse_content_protection_node (GList ** list, xmlNode * a_node)
-{
-  gchar *value = NULL;
-  if (gst_mpdparser_get_xml_prop_string (a_node, "value", &value)) {
-    if (!g_strcmp0 (value, "MSPR 2.0")) {
-      xmlNode *cur_node;
-      for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
-        if (cur_node->type == XML_ELEMENT_NODE) {
-          if (xmlStrcmp (cur_node->name, (xmlChar *) "pro") == 0) {
-            GstDescriptorType *new_descriptor;
-            new_descriptor = g_slice_new0 (GstDescriptorType);
-            *list = g_list_append (*list, new_descriptor);
-
-            gst_mpdparser_get_xml_prop_string_stripped (a_node, "schemeIdUri",
-                &new_descriptor->schemeIdUri);
-
-            gst_mpdparser_get_xml_node_content (cur_node,
-                &new_descriptor->value);
-            goto beach;
-          }
-        }
-      }
-    } else {
-      gst_mpdparser_parse_descriptor_type_node (list, a_node);
-    }
-  } else {
-    gst_mpdparser_parse_descriptor_type_node (list, a_node);
-  }
-beach:
-  if (value)
-    g_free (value);
-}
-
-static void
-gst_mpdparser_parse_representation_base_type (GstRepresentationBaseType **
-    pointer, xmlNode * a_node)
-{
-  xmlNode *cur_node;
-  GstRepresentationBaseType *representation_base;
-
-  gst_mpdparser_free_representation_base_type (*pointer);
-  *pointer = representation_base = g_slice_new0 (GstRepresentationBaseType);
-
-  GST_LOG ("attributes of RepresentationBaseType extension:");
-  gst_mpdparser_get_xml_prop_string (a_node, "profiles",
-      &representation_base->profiles);
-  gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "width", 0,
-      &representation_base->width);
-  gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "height", 0,
-      &representation_base->height);
-  gst_mpdparser_get_xml_prop_ratio (a_node, "sar", &representation_base->sar);
-  gst_mpdparser_get_xml_prop_framerate (a_node, "frameRate",
-      &representation_base->frameRate);
-  gst_mpdparser_get_xml_prop_framerate (a_node, "minFrameRate",
-      &representation_base->minFrameRate);
-  gst_mpdparser_get_xml_prop_framerate (a_node, "maxFrameRate",
-      &representation_base->maxFrameRate);
-  gst_mpdparser_get_xml_prop_string (a_node, "audioSamplingRate",
-      &representation_base->audioSamplingRate);
-  gst_mpdparser_get_xml_prop_string (a_node, "mimeType",
-      &representation_base->mimeType);
-  gst_mpdparser_get_xml_prop_string (a_node, "segmentProfiles",
-      &representation_base->segmentProfiles);
-  gst_mpdparser_get_xml_prop_string (a_node, "codecs",
-      &representation_base->codecs);
-  gst_mpdparser_get_xml_prop_double (a_node, "maximumSAPPeriod",
-      &representation_base->maximumSAPPeriod);
-  gst_mpdparser_get_xml_prop_SAP_type (a_node, "startWithSAP",
-      &representation_base->startWithSAP);
-  gst_mpdparser_get_xml_prop_double (a_node, "maxPlayoutRate",
-      &representation_base->maxPlayoutRate);
-  gst_mpdparser_get_xml_prop_boolean (a_node, "codingDependency",
-      FALSE, &representation_base->codingDependency);
-  gst_mpdparser_get_xml_prop_string (a_node, "scanType",
-      &representation_base->scanType);
-
-  /* explore children nodes */
-  for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
-    if (cur_node->type == XML_ELEMENT_NODE) {
-      if (xmlStrcmp (cur_node->name, (xmlChar *) "FramePacking") == 0) {
-        gst_mpdparser_parse_descriptor_type_node
-            (&representation_base->FramePacking, cur_node);
-      } else if (xmlStrcmp (cur_node->name,
-              (xmlChar *) "AudioChannelConfiguration") == 0) {
-        gst_mpdparser_parse_descriptor_type_node
-            (&representation_base->AudioChannelConfiguration, cur_node);
-      } else if (xmlStrcmp (cur_node->name,
-              (xmlChar *) "ContentProtection") == 0) {
-        gst_mpdparser_parse_content_protection_node
-            (&representation_base->ContentProtection, cur_node);
-      }
-    }
-  }
-}
-
-static gboolean
-gst_mpdparser_parse_representation_node (GList ** list, xmlNode * a_node,
-    GstAdaptationSetNode * parent, GstPeriodNode * period_node)
+gst_mpdparser_parse_representation_node (GList ** list, xmlNode * a_node,
+    GstAdaptationSetNode * parent, GstPeriodNode * period_node)
 {
   xmlNode *cur_node;
   GstRepresentationNode *new_representation;
@@ -1886,21 +732,21 @@ gst_mpdparser_parse_representation_node (GList ** list, xmlNode * a_node,
   new_representation = g_slice_new0 (GstRepresentationNode);
 
   GST_LOG ("attributes of Representation node:");
-  if (!gst_mpdparser_get_xml_prop_string_no_whitespace (a_node, "id",
+  if (!gst_xml_helper_get_prop_string_no_whitespace (a_node, "id",
           &new_representation->id)) {
     GST_ERROR ("Cannot parse Representation id, invalid manifest");
     goto error;
   }
-  if (!gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "bandwidth", 0,
+  if (!gst_xml_helper_get_prop_unsigned_integer (a_node, "bandwidth", 0,
           &new_representation->bandwidth)) {
     GST_ERROR ("Cannot parse Representation bandwidth, invalid manifest");
     goto error;
   }
-  gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "qualityRanking", 0,
+  gst_xml_helper_get_prop_unsigned_integer (a_node, "qualityRanking", 0,
       &new_representation->qualityRanking);
-  gst_mpdparser_get_xml_prop_string_vector_type (a_node, "dependencyId",
+  gst_xml_helper_get_prop_string_vector_type (a_node, "dependencyId",
       &new_representation->dependencyId);
-  gst_mpdparser_get_xml_prop_string_vector_type (a_node,
+  gst_xml_helper_get_prop_string_vector_type (a_node,
       "mediaStreamStructureId", &new_representation->mediaStreamStructureId);
 
   /* RepresentationBase extension */
@@ -1960,38 +806,37 @@ gst_mpdparser_parse_adaptation_set_node (GList ** list, xmlNode * a_node,
   GST_LOG ("attributes of AdaptationSet node:");
 
   new_adap_set->actuate = GST_XLINK_ACTUATE_ON_REQUEST;
-  if (gst_mpdparser_get_xml_ns_prop_string (a_node,
+  if (gst_xml_helper_get_ns_prop_string (a_node,
           "http://www.w3.org/1999/xlink", "href", &new_adap_set->xlink_href)
-      && gst_mpdparser_get_xml_ns_prop_string (a_node,
+      && gst_xml_helper_get_ns_prop_string (a_node,
           "http://www.w3.org/1999/xlink", "actuate", &actuate)) {
     if (strcmp (actuate, "onLoad") == 0)
       new_adap_set->actuate = GST_XLINK_ACTUATE_ON_LOAD;
     xmlFree (actuate);
   }
 
-  gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "id", 0,
-      &new_adap_set->id);
-  gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "group", 0,
+  gst_xml_helper_get_prop_unsigned_integer (a_node, "id", 0, &new_adap_set->id);
+  gst_xml_helper_get_prop_unsigned_integer (a_node, "group", 0,
       &new_adap_set->group);
-  gst_mpdparser_get_xml_prop_string (a_node, "lang", &new_adap_set->lang);
-  gst_mpdparser_get_xml_prop_string (a_node, "contentType",
+  gst_xml_helper_get_prop_string (a_node, "lang", &new_adap_set->lang);
+  gst_xml_helper_get_prop_string (a_node, "contentType",
       &new_adap_set->contentType);
-  gst_mpdparser_get_xml_prop_ratio (a_node, "par", &new_adap_set->par);
-  gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "minBandwidth", 0,
+  gst_xml_helper_get_prop_ratio (a_node, "par", &new_adap_set->par);
+  gst_xml_helper_get_prop_unsigned_integer (a_node, "minBandwidth", 0,
       &new_adap_set->minBandwidth);
-  gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "maxBandwidth", 0,
+  gst_xml_helper_get_prop_unsigned_integer (a_node, "maxBandwidth", 0,
       &new_adap_set->maxBandwidth);
-  gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "minWidth", 0,
+  gst_xml_helper_get_prop_unsigned_integer (a_node, "minWidth", 0,
       &new_adap_set->minWidth);
-  gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "maxWidth", 0,
+  gst_xml_helper_get_prop_unsigned_integer (a_node, "maxWidth", 0,
       &new_adap_set->maxWidth);
-  gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "minHeight", 0,
+  gst_xml_helper_get_prop_unsigned_integer (a_node, "minHeight", 0,
       &new_adap_set->minHeight);
-  gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "maxHeight", 0,
+  gst_xml_helper_get_prop_unsigned_integer (a_node, "maxHeight", 0,
       &new_adap_set->maxHeight);
-  gst_mpdparser_get_xml_prop_cond_uint (a_node, "segmentAlignment",
+  gst_xml_helper_get_prop_cond_uint (a_node, "segmentAlignment",
       &new_adap_set->segmentAlignment);
-  gst_mpdparser_get_xml_prop_boolean (a_node, "bitstreamSwitching",
+  gst_xml_helper_get_prop_boolean (a_node, "bitstreamSwitching",
       parent->bitstreamSwitching, &new_adap_set->bitstreamSwitching);
   if (parent->bitstreamSwitching && !new_adap_set->bitstreamSwitching) {
     /* according to the standard, if the Period's bitstreamSwitching attribute
@@ -2002,9 +847,9 @@ gst_mpdparser_parse_adaptation_set_node (GList ** list, xmlNode * a_node,
      */
     new_adap_set->bitstreamSwitching = parent->bitstreamSwitching;
   }
-  gst_mpdparser_get_xml_prop_cond_uint (a_node, "subsegmentAlignment",
+  gst_xml_helper_get_prop_cond_uint (a_node, "subsegmentAlignment",
       &new_adap_set->subsegmentAlignment);
-  gst_mpdparser_get_xml_prop_SAP_type (a_node, "subsegmentStartsWithSAP",
+  gst_mpd_helper_get_SAP_type (a_node, "subsegmentStartsWithSAP",
       &new_adap_set->subsegmentStartsWithSAP);
 
   /* RepresentationBase extension */
@@ -2078,7 +923,7 @@ gst_mpdparser_parse_subset_node (GList ** list, xmlNode * a_node)
   *list = g_list_append (*list, new_subset);
 
   GST_LOG ("attributes of Subset node:");
-  gst_mpdparser_get_xml_prop_uint_vector_type (a_node, "contains",
+  gst_xml_helper_get_prop_uint_vector_type (a_node, "contains",
       &new_subset->contains, &new_subset->size);
 }
 
@@ -2100,26 +945,26 @@ gst_mpdparser_parse_segment_template_node (GstSegmentTemplateNode ** pointer,
 
   /* Inherit attribute values from parent when the value isn't found */
   GST_LOG ("attributes of SegmentTemplate node:");
-  if (gst_mpdparser_get_xml_prop_string (a_node, "media", &strval)) {
+  if (gst_xml_helper_get_prop_string (a_node, "media", &strval)) {
     new_segment_template->media = strval;
   } else if (parent) {
     new_segment_template->media = xmlMemStrdup (parent->media);
   }
 
-  if (gst_mpdparser_get_xml_prop_string (a_node, "index", &strval)) {
+  if (gst_xml_helper_get_prop_string (a_node, "index", &strval)) {
     new_segment_template->index = strval;
   } else if (parent) {
     new_segment_template->index = xmlMemStrdup (parent->index);
   }
 
-  if (gst_mpdparser_get_xml_prop_string (a_node, "initialization", &strval)) {
+  if (gst_xml_helper_get_prop_string (a_node, "initialization", &strval)) {
     new_segment_template->initialization = strval;
   } else if (parent) {
     new_segment_template->initialization =
         xmlMemStrdup (parent->initialization);
   }
 
-  if (gst_mpdparser_get_xml_prop_string (a_node, "bitstreamSwitching", &strval)) {
+  if (gst_xml_helper_get_prop_string (a_node, "bitstreamSwitching", &strval)) {
     new_segment_template->bitstreamSwitching = strval;
   } else if (parent) {
     new_segment_template->bitstreamSwitching =
@@ -2146,21 +991,21 @@ gst_mpdparser_parse_period_node (GList ** list, xmlNode * a_node)
   GST_LOG ("attributes of Period node:");
 
   new_period->actuate = GST_XLINK_ACTUATE_ON_REQUEST;
-  if (gst_mpdparser_get_xml_ns_prop_string (a_node,
+  if (gst_xml_helper_get_ns_prop_string (a_node,
           "http://www.w3.org/1999/xlink", "href", &new_period->xlink_href)
-      && gst_mpdparser_get_xml_ns_prop_string (a_node,
+      && gst_xml_helper_get_ns_prop_string (a_node,
           "http://www.w3.org/1999/xlink", "actuate", &actuate)) {
     if (strcmp (actuate, "onLoad") == 0)
       new_period->actuate = GST_XLINK_ACTUATE_ON_LOAD;
     xmlFree (actuate);
   }
 
-  gst_mpdparser_get_xml_prop_string (a_node, "id", &new_period->id);
-  gst_mpdparser_get_xml_prop_duration (a_node, "start", GST_MPD_DURATION_NONE,
+  gst_xml_helper_get_prop_string (a_node, "id", &new_period->id);
+  gst_xml_helper_get_prop_duration (a_node, "start", GST_MPD_DURATION_NONE,
       &new_period->start);
-  gst_mpdparser_get_xml_prop_duration (a_node, "duration",
+  gst_xml_helper_get_prop_duration (a_node, "duration",
       GST_MPD_DURATION_NONE, &new_period->duration);
-  gst_mpdparser_get_xml_prop_boolean (a_node, "bitstreamSwitching", FALSE,
+  gst_xml_helper_get_prop_boolean (a_node, "bitstreamSwitching", FALSE,
       &new_period->bitstreamSwitching);
 
   /* explore children nodes */
@@ -2217,8 +1062,8 @@ gst_mpdparser_parse_program_info_node (GList ** list, xmlNode * a_node)
   *list = g_list_append (*list, new_prog_info);
 
   GST_LOG ("attributes of ProgramInformation node:");
-  gst_mpdparser_get_xml_prop_string (a_node, "lang", &new_prog_info->lang);
-  gst_mpdparser_get_xml_prop_string (a_node, "moreInformationURL",
+  gst_xml_helper_get_prop_string (a_node, "lang", &new_prog_info->lang);
+  gst_xml_helper_get_prop_string (a_node, "moreInformationURL",
       &new_prog_info->moreInformationURL);
 
   /* explore children nodes */
@@ -2226,12 +1071,11 @@ gst_mpdparser_parse_program_info_node (GList ** list, xmlNode * a_node)
   for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
     if (cur_node->type == XML_ELEMENT_NODE) {
       if (xmlStrcmp (cur_node->name, (xmlChar *) "Title") == 0) {
-        gst_mpdparser_get_xml_node_content (cur_node, &new_prog_info->Title);
+        gst_xml_helper_get_node_content (cur_node, &new_prog_info->Title);
       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Source") == 0) {
-        gst_mpdparser_get_xml_node_content (cur_node, &new_prog_info->Source);
+        gst_xml_helper_get_node_content (cur_node, &new_prog_info->Source);
       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Copyright") == 0) {
-        gst_mpdparser_get_xml_node_content (cur_node,
-            &new_prog_info->Copyright);
+        gst_xml_helper_get_node_content (cur_node, &new_prog_info->Copyright);
       }
     }
   }
@@ -2246,9 +1090,9 @@ gst_mpdparser_parse_metrics_range_node (GList ** list, xmlNode * a_node)
   *list = g_list_append (*list, new_metrics_range);
 
   GST_LOG ("attributes of Metrics Range node:");
-  gst_mpdparser_get_xml_prop_duration (a_node, "starttime",
+  gst_xml_helper_get_prop_duration (a_node, "starttime",
       GST_MPD_DURATION_NONE, &new_metrics_range->starttime);
-  gst_mpdparser_get_xml_prop_duration (a_node, "duration",
+  gst_xml_helper_get_prop_duration (a_node, "duration",
       GST_MPD_DURATION_NONE, &new_metrics_range->duration);
 }
 
@@ -2262,7 +1106,7 @@ gst_mpdparser_parse_metrics_node (GList ** list, xmlNode * a_node)
   *list = g_list_append (*list, new_metrics);
 
   GST_LOG ("attributes of Metrics node:");
-  gst_mpdparser_get_xml_prop_string (a_node, "metrics", &new_metrics->metrics);
+  gst_xml_helper_get_prop_string (a_node, "metrics", &new_metrics->metrics);
 
   /* explore children nodes */
   GST_LOG ("children of Metrics node:");
@@ -2294,7 +1138,7 @@ gst_mpdparser_parse_utctiming_node (GList ** list, xmlNode * a_node)
   new_timing = g_slice_new0 (GstUTCTimingNode);
 
   GST_LOG ("attributes of UTCTiming node:");
-  if (gst_mpdparser_get_xml_prop_string (a_node, "schemeIdUri", &method)) {
+  if (gst_xml_helper_get_prop_string (a_node, "schemeIdUri", &method)) {
     int i;
 
     for (i = 0; gst_mpdparser_utc_timing_methods[i].name; ++i) {
@@ -2307,7 +1151,7 @@ gst_mpdparser_parse_utctiming_node (GList ** list, xmlNode * a_node)
     xmlFree (method);
   }
 
-  if (gst_mpdparser_get_xml_prop_string (a_node, "value", &value)) {
+  if (gst_xml_helper_get_prop_string (a_node, "value", &value)) {
     int max_tokens = 0;
     if (GST_MPD_UTCTIMING_TYPE_DIRECT == new_timing->method) {
       /* The GST_MPD_UTCTIMING_TYPE_DIRECT method is a special case
@@ -2339,34 +1183,33 @@ gst_mpdparser_parse_root_node (GstMPDNode ** pointer, xmlNode * a_node)
   new_mpd = g_slice_new0 (GstMPDNode);
 
   GST_LOG ("namespaces of root MPD node:");
-  new_mpd->default_namespace =
-      gst_mpdparser_get_xml_node_namespace (a_node, NULL);
-  new_mpd->namespace_xsi = gst_mpdparser_get_xml_node_namespace (a_node, "xsi");
-  new_mpd->namespace_ext = gst_mpdparser_get_xml_node_namespace (a_node, "ext");
+  new_mpd->default_namespace = gst_xml_helper_get_node_namespace (a_node, NULL);
+  new_mpd->namespace_xsi = gst_xml_helper_get_node_namespace (a_node, "xsi");
+  new_mpd->namespace_ext = gst_xml_helper_get_node_namespace (a_node, "ext");
 
   GST_LOG ("attributes of root MPD node:");
-  gst_mpdparser_get_xml_prop_string (a_node, "schemaLocation",
+  gst_xml_helper_get_prop_string (a_node, "schemaLocation",
       &new_mpd->schemaLocation);
-  gst_mpdparser_get_xml_prop_string (a_node, "id", &new_mpd->id);
-  gst_mpdparser_get_xml_prop_string (a_node, "profiles", &new_mpd->profiles);
-  gst_mpdparser_get_xml_prop_type (a_node, "type", &new_mpd->type);
-  gst_mpdparser_get_xml_prop_dateTime (a_node, "availabilityStartTime",
+  gst_xml_helper_get_prop_string (a_node, "id", &new_mpd->id);
+  gst_xml_helper_get_prop_string (a_node, "profiles", &new_mpd->profiles);
+  gst_mpd_helper_get_mpd_type (a_node, "type", &new_mpd->type);
+  gst_xml_helper_get_prop_dateTime (a_node, "availabilityStartTime",
       &new_mpd->availabilityStartTime);
-  gst_mpdparser_get_xml_prop_dateTime (a_node, "availabilityEndTime",
+  gst_xml_helper_get_prop_dateTime (a_node, "availabilityEndTime",
       &new_mpd->availabilityEndTime);
-  gst_mpdparser_get_xml_prop_duration (a_node, "mediaPresentationDuration",
+  gst_xml_helper_get_prop_duration (a_node, "mediaPresentationDuration",
       GST_MPD_DURATION_NONE, &new_mpd->mediaPresentationDuration);
-  gst_mpdparser_get_xml_prop_duration (a_node, "minimumUpdatePeriod",
+  gst_xml_helper_get_prop_duration (a_node, "minimumUpdatePeriod",
       GST_MPD_DURATION_NONE, &new_mpd->minimumUpdatePeriod);
-  gst_mpdparser_get_xml_prop_duration (a_node, "minBufferTime",
+  gst_xml_helper_get_prop_duration (a_node, "minBufferTime",
       GST_MPD_DURATION_NONE, &new_mpd->minBufferTime);
-  gst_mpdparser_get_xml_prop_duration (a_node, "timeShiftBufferDepth",
+  gst_xml_helper_get_prop_duration (a_node, "timeShiftBufferDepth",
       GST_MPD_DURATION_NONE, &new_mpd->timeShiftBufferDepth);
-  gst_mpdparser_get_xml_prop_duration (a_node, "suggestedPresentationDelay",
+  gst_xml_helper_get_prop_duration (a_node, "suggestedPresentationDelay",
       GST_MPD_DURATION_NONE, &new_mpd->suggestedPresentationDelay);
-  gst_mpdparser_get_xml_prop_duration (a_node, "maxSegmentDuration",
+  gst_xml_helper_get_prop_duration (a_node, "maxSegmentDuration",
       GST_MPD_DURATION_NONE, &new_mpd->maxSegmentDuration);
-  gst_mpdparser_get_xml_prop_duration (a_node, "maxSubsegmentDuration",
+  gst_xml_helper_get_prop_duration (a_node, "maxSubsegmentDuration",
       GST_MPD_DURATION_NONE, &new_mpd->maxSubsegmentDuration);
 
   /* explore children Period nodes */
@@ -2398,415 +1241,27 @@ error:
   return FALSE;
 }
 
-/* comparison functions */
-static int
-strncmp_ext (const char *s1, const char *s2)
+/* internal memory management functions */
+static void
+gst_mpdparser_free_prog_info_node (GstProgramInformationNode * prog_info_node)
 {
-  if (s1 == NULL && s2 == NULL)
-    return 0;
-  if (s1 == NULL && s2 != NULL)
-    return 1;
-  if (s2 == NULL && s1 != NULL)
-    return 1;
-  return strncmp (s1, s2, strlen (s2));
+  if (prog_info_node) {
+    if (prog_info_node->lang)
+      xmlFree (prog_info_node->lang);
+    if (prog_info_node->moreInformationURL)
+      xmlFree (prog_info_node->moreInformationURL);
+    if (prog_info_node->Title)
+      xmlFree (prog_info_node->Title);
+    if (prog_info_node->Source)
+      xmlFree (prog_info_node->Source);
+    if (prog_info_node->Copyright)
+      xmlFree (prog_info_node->Copyright);
+    g_slice_free (GstProgramInformationNode, prog_info_node);
+  }
 }
 
-/* navigation functions */
-static GstStreamMimeType
-gst_mpdparser_representation_get_mimetype (GstAdaptationSetNode * adapt_set,
-    GstRepresentationNode * rep)
-{
-  gchar *mime = NULL;
-  if (rep->RepresentationBase)
-    mime = rep->RepresentationBase->mimeType;
-  if (mime == NULL && adapt_set->RepresentationBase) {
-    mime = adapt_set->RepresentationBase->mimeType;
-  }
-
-  if (strncmp_ext (mime, "audio") == 0)
-    return GST_STREAM_AUDIO;
-  if (strncmp_ext (mime, "video") == 0)
-    return GST_STREAM_VIDEO;
-  if (strncmp_ext (mime, "application") == 0 || strncmp_ext (mime, "text") == 0)
-    return GST_STREAM_APPLICATION;
-
-  return GST_STREAM_UNKNOWN;
-}
-
-static GstRepresentationNode *
-gst_mpdparser_get_lowest_representation (GList * Representations)
-{
-  GList *list = NULL;
-  GstRepresentationNode *rep = NULL;
-  GstRepresentationNode *lowest = NULL;
-
-  if (Representations == NULL)
-    return NULL;
-
-  for (list = g_list_first (Representations); list; list = g_list_next (list)) {
-    rep = (GstRepresentationNode *) list->data;
-    if (rep && (!lowest || rep->bandwidth < lowest->bandwidth)) {
-      lowest = rep;
-    }
-  }
-
-  return lowest;
-}
-
-#if 0
-static GstRepresentationNode *
-gst_mpdparser_get_highest_representation (GList * Representations)
-{
-  GList *list = NULL;
-
-  if (Representations == NULL)
-    return NULL;
-
-  list = g_list_last (Representations);
-
-  return list ? (GstRepresentationNode *) list->data : NULL;
-}
-
-static GstRepresentationNode *
-gst_mpdparser_get_representation_with_max_bandwidth (GList * Representations,
-    gint max_bandwidth)
-{
-  GList *list = NULL;
-  GstRepresentationNode *representation, *best_rep = NULL;
-
-  if (Representations == NULL)
-    return NULL;
-
-  if (max_bandwidth <= 0)       /* 0 => get highest representation available */
-    return gst_mpdparser_get_highest_representation (Representations);
-
-  for (list = g_list_first (Representations); list; list = g_list_next (list)) {
-    representation = (GstRepresentationNode *) list->data;
-    if (representation && representation->bandwidth <= max_bandwidth) {
-      best_rep = representation;
-    }
-  }
-
-  return best_rep;
-}
-#endif
-
-static GstSegmentBaseType *
-gst_mpdparser_get_segment_base (GstPeriodNode * Period,
-    GstAdaptationSetNode * AdaptationSet,
-    GstRepresentationNode * Representation)
-{
-  GstSegmentBaseType *SegmentBase = NULL;
-
-  if (Representation && Representation->SegmentBase) {
-    SegmentBase = Representation->SegmentBase;
-  } else if (AdaptationSet && AdaptationSet->SegmentBase) {
-    SegmentBase = AdaptationSet->SegmentBase;
-  } else if (Period && Period->SegmentBase) {
-    SegmentBase = Period->SegmentBase;
-  }
-  /* the SegmentBase element could be encoded also inside a SegmentList element */
-  if (SegmentBase == NULL) {
-    if (Representation && Representation->SegmentList
-        && Representation->SegmentList->MultSegBaseType
-        && Representation->SegmentList->MultSegBaseType->SegBaseType) {
-      SegmentBase = Representation->SegmentList->MultSegBaseType->SegBaseType;
-    } else if (AdaptationSet && AdaptationSet->SegmentList
-        && AdaptationSet->SegmentList->MultSegBaseType
-        && AdaptationSet->SegmentList->MultSegBaseType->SegBaseType) {
-      SegmentBase = AdaptationSet->SegmentList->MultSegBaseType->SegBaseType;
-    } else if (Period && Period->SegmentList
-        && Period->SegmentList->MultSegBaseType
-        && Period->SegmentList->MultSegBaseType->SegBaseType) {
-      SegmentBase = Period->SegmentList->MultSegBaseType->SegBaseType;
-    }
-  }
-
-  return SegmentBase;
-}
-
-gint
-gst_mpdparser_get_rep_idx_with_min_bandwidth (GList * Representations)
-{
-  GList *list = NULL, *lowest = NULL;
-  GstRepresentationNode *rep = NULL;
-  gint lowest_bandwidth = -1;
-
-  if (Representations == NULL)
-    return -1;
-
-  for (list = g_list_first (Representations); list; list = g_list_next (list)) {
-    rep = (GstRepresentationNode *) list->data;
-    if (rep && (!lowest || rep->bandwidth < lowest_bandwidth)) {
-      lowest = list;
-      lowest_bandwidth = rep->bandwidth;
-    }
-  }
-
-  return lowest ? g_list_position (Representations, lowest) : -1;
-}
-
-gint
-gst_mpdparser_get_rep_idx_with_max_bandwidth (GList * Representations,
-    gint64 max_bandwidth, gint max_video_width, gint max_video_height, gint
-    max_video_framerate_n, gint max_video_framerate_d)
-{
-  GList *list = NULL, *best = NULL;
-  GstRepresentationNode *representation;
-  gint best_bandwidth = 0;
-
-  GST_DEBUG ("max_bandwidth = %" G_GINT64_FORMAT, max_bandwidth);
-
-  if (Representations == NULL)
-    return -1;
-
-  if (max_bandwidth <= 0)       /* 0 => get lowest representation available */
-    return gst_mpdparser_get_rep_idx_with_min_bandwidth (Representations);
-
-  for (list = g_list_first (Representations); list; list = g_list_next (list)) {
-    GstFrameRate *framerate = NULL;
-
-    representation = (GstRepresentationNode *) list->data;
-
-    /* FIXME: Really? */
-    if (!representation)
-      continue;
-
-    framerate = representation->RepresentationBase->frameRate;
-    if (!framerate)
-      framerate = representation->RepresentationBase->maxFrameRate;
-
-    if (framerate && max_video_framerate_n > 0) {
-      if (gst_util_fraction_compare (framerate->num, framerate->den,
-              max_video_framerate_n, max_video_framerate_d) > 0)
-        continue;
-    }
-
-    if (max_video_width > 0
-        && representation->RepresentationBase->width > max_video_width)
-      continue;
-    if (max_video_height > 0
-        && representation->RepresentationBase->height > max_video_height)
-      continue;
-
-    if (representation->bandwidth <= max_bandwidth &&
-        representation->bandwidth > best_bandwidth) {
-      best = list;
-      best_bandwidth = representation->bandwidth;
-    }
-  }
-
-  return best ? g_list_position (Representations, best) : -1;
-}
-
-static GstSegmentListNode *
-gst_mpd_client_fetch_external_segment_list (GstMpdClient * client,
-    GstPeriodNode * Period,
-    GstAdaptationSetNode * AdaptationSet,
-    GstRepresentationNode * Representation,
-    GstSegmentListNode * parent, GstSegmentListNode * segment_list)
-{
-  GstFragment *download;
-  GstBuffer *segment_list_buffer;
-  GstMapInfo map;
-  GError *err = NULL;
-  xmlDocPtr doc = NULL;
-  GstUri *base_uri, *uri;
-  gchar *query = NULL;
-  gchar *uri_string;
-  GstSegmentListNode *new_segment_list = NULL;
-
-  /* ISO/IEC 23009-1:2014 5.5.3 4)
-   * Remove nodes that resolve to nothing when resolving
-   */
-  if (strcmp (segment_list->xlink_href,
-          "urn:mpeg:dash:resolve-to-zero:2013") == 0) {
-    return NULL;
-  }
-
-  if (!client->downloader) {
-    return NULL;
-  }
-
-  /* Build absolute URI */
-
-  /* Get base URI at the MPD level */
-  base_uri =
-      gst_uri_from_string (client->
-      mpd_base_uri ? client->mpd_base_uri : client->mpd_uri);
-
-  /* combine a BaseURL at the MPD level with the current base url */
-  base_uri = combine_urls (base_uri, client->mpd_node->BaseURLs, &query, 0);
-
-  /* combine a BaseURL at the Period level with the current base url */
-  base_uri = combine_urls (base_uri, Period->BaseURLs, &query, 0);
-
-  if (AdaptationSet) {
-    /* combine a BaseURL at the AdaptationSet level with the current base url */
-    base_uri = combine_urls (base_uri, AdaptationSet->BaseURLs, &query, 0);
-
-    if (Representation) {
-      /* combine a BaseURL at the Representation level with the current base url */
-      base_uri = combine_urls (base_uri, Representation->BaseURLs, &query, 0);
-    }
-  }
-
-  uri = gst_uri_from_string_with_base (base_uri, segment_list->xlink_href);
-  if (query)
-    gst_uri_set_query_string (uri, query);
-  g_free (query);
-  uri_string = gst_uri_to_string (uri);
-  gst_uri_unref (base_uri);
-  gst_uri_unref (uri);
-
-  download =
-      gst_uri_downloader_fetch_uri (client->downloader,
-      uri_string, client->mpd_uri, TRUE, FALSE, TRUE, &err);
-  g_free (uri_string);
-
-  if (!download) {
-    GST_ERROR ("Failed to download external SegmentList node at '%s': %s",
-        segment_list->xlink_href, err->message);
-    g_clear_error (&err);
-    return NULL;
-  }
-
-  segment_list_buffer = gst_fragment_get_buffer (download);
-  g_object_unref (download);
-
-  gst_buffer_map (segment_list_buffer, &map, GST_MAP_READ);
-
-  doc =
-      xmlReadMemory ((const gchar *) map.data, map.size, "noname.xml", NULL,
-      XML_PARSE_NONET);
-
-  gst_buffer_unmap (segment_list_buffer, &map);
-  gst_buffer_unref (segment_list_buffer);
-
-  /* NOTE: ISO/IEC 23009-1:2014 5.3.9.3.2 is saying that one or multiple SegmentList
-   * in external xml is allowed, however, multiple SegmentList does not make sense
-   * because Period/AdaptationSet/Representation allow only one SegmentList */
-  if (doc) {
-    xmlNode *root_element = xmlDocGetRootElement (doc);
-
-    if (root_element->type != XML_ELEMENT_NODE ||
-        xmlStrcmp (root_element->name, (xmlChar *) "SegmentList") != 0) {
-      goto error;
-    }
-
-    gst_mpdparser_parse_segment_list_node (&new_segment_list, root_element,
-        parent);
-  } else {
-    goto error;
-  }
-
-done:
-  if (doc)
-    xmlFreeDoc (doc);
-
-  return new_segment_list;
-
-error:
-  GST_ERROR ("Failed to parse segment list node XML");
-  goto done;
-}
-
-static GstSegmentListNode *
-gst_mpdparser_get_segment_list (GstMpdClient * client, GstPeriodNode * Period,
-    GstAdaptationSetNode * AdaptationSet,
-    GstRepresentationNode * Representation)
-{
-  GstSegmentListNode **SegmentList;
-  GstSegmentListNode *ParentSegmentList = NULL;
-
-  if (Representation && Representation->SegmentList) {
-    SegmentList = &Representation->SegmentList;
-    ParentSegmentList = AdaptationSet->SegmentList;
-  } else if (AdaptationSet && AdaptationSet->SegmentList) {
-    SegmentList = &AdaptationSet->SegmentList;
-    ParentSegmentList = Period->SegmentList;
-    Representation = NULL;
-  } else {
-    Representation = NULL;
-    AdaptationSet = NULL;
-    SegmentList = &Period->SegmentList;
-  }
-
-  /* Resolve external segment list here. */
-  if (*SegmentList && (*SegmentList)->xlink_href) {
-    GstSegmentListNode *new_segment_list;
-
-    /* TODO: Use SegmentList of parent if
-     * - Parent has its own SegmentList
-     * - Fail to get SegmentList from external xml
-     */
-    new_segment_list =
-        gst_mpd_client_fetch_external_segment_list (client, Period,
-        AdaptationSet, Representation, ParentSegmentList, *SegmentList);
-
-    gst_mpdparser_free_segment_list_node (*SegmentList);
-    *SegmentList = new_segment_list;
-  }
-
-  return *SegmentList;
-}
-
-/* memory management functions */
-static void
-gst_mpdparser_free_mpd_node (GstMPDNode * mpd_node)
-{
-  if (mpd_node) {
-    if (mpd_node->default_namespace)
-      xmlFree (mpd_node->default_namespace);
-    if (mpd_node->namespace_xsi)
-      xmlFree (mpd_node->namespace_xsi);
-    if (mpd_node->namespace_ext)
-      xmlFree (mpd_node->namespace_ext);
-    if (mpd_node->schemaLocation)
-      xmlFree (mpd_node->schemaLocation);
-    if (mpd_node->id)
-      xmlFree (mpd_node->id);
-    if (mpd_node->profiles)
-      xmlFree (mpd_node->profiles);
-    if (mpd_node->availabilityStartTime)
-      gst_date_time_unref (mpd_node->availabilityStartTime);
-    if (mpd_node->availabilityEndTime)
-      gst_date_time_unref (mpd_node->availabilityEndTime);
-    g_list_free_full (mpd_node->ProgramInfo,
-        (GDestroyNotify) gst_mpdparser_free_prog_info_node);
-    g_list_free_full (mpd_node->BaseURLs,
-        (GDestroyNotify) gst_mpdparser_free_base_url_node);
-    g_list_free_full (mpd_node->Locations, (GDestroyNotify) xmlFree);
-    g_list_free_full (mpd_node->Periods,
-        (GDestroyNotify) gst_mpdparser_free_period_node);
-    g_list_free_full (mpd_node->Metrics,
-        (GDestroyNotify) gst_mpdparser_free_metrics_node);
-    g_list_free_full (mpd_node->UTCTiming,
-        (GDestroyNotify) gst_mpdparser_free_utctiming_node);
-    g_slice_free (GstMPDNode, mpd_node);
-  }
-}
-
-static void
-gst_mpdparser_free_prog_info_node (GstProgramInformationNode * prog_info_node)
-{
-  if (prog_info_node) {
-    if (prog_info_node->lang)
-      xmlFree (prog_info_node->lang);
-    if (prog_info_node->moreInformationURL)
-      xmlFree (prog_info_node->moreInformationURL);
-    if (prog_info_node->Title)
-      xmlFree (prog_info_node->Title);
-    if (prog_info_node->Source)
-      xmlFree (prog_info_node->Source);
-    if (prog_info_node->Copyright)
-      xmlFree (prog_info_node->Copyright);
-    g_slice_free (GstProgramInformationNode, prog_info_node);
-  }
-}
-
-static void
-gst_mpdparser_free_metrics_node (GstMetricsNode * metrics_node)
+static void
+gst_mpdparser_free_metrics_node (GstMetricsNode * metrics_node)
 {
   if (metrics_node) {
     if (metrics_node->metrics)
@@ -2826,27 +1281,6 @@ gst_mpdparser_free_metrics_range_node (GstMetricsRangeNode * metrics_range_node)
 }
 
 static void
-gst_mpdparser_free_period_node (GstPeriodNode * period_node)
-{
-  if (period_node) {
-    if (period_node->id)
-      xmlFree (period_node->id);
-    gst_mpdparser_free_seg_base_type_ext (period_node->SegmentBase);
-    gst_mpdparser_free_segment_list_node (period_node->SegmentList);
-    gst_mpdparser_free_segment_template_node (period_node->SegmentTemplate);
-    g_list_free_full (period_node->AdaptationSets,
-        (GDestroyNotify) gst_mpdparser_free_adaptation_set_node);
-    g_list_free_full (period_node->Subsets,
-        (GDestroyNotify) gst_mpdparser_free_subset_node);
-    g_list_free_full (period_node->BaseURLs,
-        (GDestroyNotify) gst_mpdparser_free_base_url_node);
-    if (period_node->xlink_href)
-      xmlFree (period_node->xlink_href);
-    g_slice_free (GstPeriodNode, period_node);
-  }
-}
-
-static void
 gst_mpdparser_free_subset_node (GstSubsetNode * subset_node)
 {
   if (subset_node) {
@@ -2883,10 +1317,10 @@ gst_mpdparser_free_representation_base_type (GstRepresentationBaseType *
   if (representation_base) {
     if (representation_base->profiles)
       xmlFree (representation_base->profiles);
-    g_slice_free (GstRatio, representation_base->sar);
-    g_slice_free (GstFrameRate, representation_base->frameRate);
-    g_slice_free (GstFrameRate, representation_base->minFrameRate);
-    g_slice_free (GstFrameRate, representation_base->maxFrameRate);
+    g_slice_free (GstXMLRatio, representation_base->sar);
+    g_slice_free (GstXMLFrameRate, representation_base->frameRate);
+    g_slice_free (GstXMLFrameRate, representation_base->minFrameRate);
+    g_slice_free (GstXMLFrameRate, representation_base->maxFrameRate);
     if (representation_base->audioSamplingRate)
       xmlFree (representation_base->audioSamplingRate);
     if (representation_base->mimeType)
@@ -2908,46 +1342,6 @@ gst_mpdparser_free_representation_base_type (GstRepresentationBaseType *
 }
 
 static void
-gst_mpdparser_free_adaptation_set_node (GstAdaptationSetNode *
-    adaptation_set_node)
-{
-  if (adaptation_set_node) {
-    if (adaptation_set_node->lang)
-      xmlFree (adaptation_set_node->lang);
-    if (adaptation_set_node->contentType)
-      xmlFree (adaptation_set_node->contentType);
-    g_slice_free (GstRatio, adaptation_set_node->par);
-    g_slice_free (GstConditionalUintType,
-        adaptation_set_node->segmentAlignment);
-    g_slice_free (GstConditionalUintType,
-        adaptation_set_node->subsegmentAlignment);
-    g_list_free_full (adaptation_set_node->Accessibility,
-        (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
-    g_list_free_full (adaptation_set_node->Role,
-        (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
-    g_list_free_full (adaptation_set_node->Rating,
-        (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
-    g_list_free_full (adaptation_set_node->Viewpoint,
-        (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
-    gst_mpdparser_free_representation_base_type
-        (adaptation_set_node->RepresentationBase);
-    gst_mpdparser_free_seg_base_type_ext (adaptation_set_node->SegmentBase);
-    gst_mpdparser_free_segment_list_node (adaptation_set_node->SegmentList);
-    gst_mpdparser_free_segment_template_node
-        (adaptation_set_node->SegmentTemplate);
-    g_list_free_full (adaptation_set_node->BaseURLs,
-        (GDestroyNotify) gst_mpdparser_free_base_url_node);
-    g_list_free_full (adaptation_set_node->Representations,
-        (GDestroyNotify) gst_mpdparser_free_representation_node);
-    g_list_free_full (adaptation_set_node->ContentComponents,
-        (GDestroyNotify) gst_mpdparser_free_content_component_node);
-    if (adaptation_set_node->xlink_href)
-      xmlFree (adaptation_set_node->xlink_href);
-    g_slice_free (GstAdaptationSetNode, adaptation_set_node);
-  }
-}
-
-static void
 gst_mpdparser_free_representation_node (GstRepresentationNode *
     representation_node)
 {
@@ -2978,3188 +1372,788 @@ gst_mpdparser_free_subrepresentation_node (GstSubRepresentationNode *
     gst_mpdparser_free_representation_base_type
         (subrep_node->RepresentationBase);
     if (subrep_node->dependencyLevel)
-      xmlFree (subrep_node->dependencyLevel);
-    g_strfreev (subrep_node->contentComponent);
-    g_slice_free (GstSubRepresentationNode, subrep_node);
-  }
-}
-
-static void
-gst_mpdparser_free_s_node (GstSNode * s_node)
-{
-  if (s_node) {
-    g_slice_free (GstSNode, s_node);
-  }
-}
-
-static GstSegmentTimelineNode *
-gst_mpdparser_segment_timeline_node_new (void)
-{
-  GstSegmentTimelineNode *node = g_slice_new0 (GstSegmentTimelineNode);
-
-  g_queue_init (&node->S);
-
-  return node;
-}
-
-static void
-gst_mpdparser_free_segment_timeline_node (GstSegmentTimelineNode * seg_timeline)
-{
-  if (seg_timeline) {
-    g_queue_foreach (&seg_timeline->S, (GFunc) gst_mpdparser_free_s_node, NULL);
-    g_queue_clear (&seg_timeline->S);
-    g_slice_free (GstSegmentTimelineNode, seg_timeline);
-  }
-}
-
-static void
-gst_mpdparser_free_url_type_node (GstURLType * url_type_node)
-{
-  if (url_type_node) {
-    if (url_type_node->sourceURL)
-      xmlFree (url_type_node->sourceURL);
-    g_slice_free (GstRange, url_type_node->range);
-    g_slice_free (GstURLType, url_type_node);
-  }
-}
-
-static void
-gst_mpdparser_free_seg_base_type_ext (GstSegmentBaseType * seg_base_type)
-{
-  if (seg_base_type) {
-    if (seg_base_type->indexRange)
-      g_slice_free (GstRange, seg_base_type->indexRange);
-    gst_mpdparser_free_url_type_node (seg_base_type->Initialization);
-    gst_mpdparser_free_url_type_node (seg_base_type->RepresentationIndex);
-    g_slice_free (GstSegmentBaseType, seg_base_type);
-  }
-}
-
-static void
-gst_mpdparser_free_mult_seg_base_type_ext (GstMultSegmentBaseType *
-    mult_seg_base_type)
-{
-  if (mult_seg_base_type) {
-    /* SegmentBaseType extension */
-    gst_mpdparser_free_seg_base_type_ext (mult_seg_base_type->SegBaseType);
-    gst_mpdparser_free_segment_timeline_node
-        (mult_seg_base_type->SegmentTimeline);
-    gst_mpdparser_free_url_type_node (mult_seg_base_type->BitstreamSwitching);
-    g_slice_free (GstMultSegmentBaseType, mult_seg_base_type);
-  }
-}
-
-static void
-gst_mpdparser_free_segment_list_node (GstSegmentListNode * segment_list_node)
-{
-  if (segment_list_node) {
-    g_list_free_full (segment_list_node->SegmentURL,
-        (GDestroyNotify) gst_mpdparser_free_segment_url_node);
-    /* MultipleSegmentBaseType extension */
-    gst_mpdparser_free_mult_seg_base_type_ext
-        (segment_list_node->MultSegBaseType);
-    if (segment_list_node->xlink_href)
-      xmlFree (segment_list_node->xlink_href);
-    g_slice_free (GstSegmentListNode, segment_list_node);
-  }
-}
-
-static void
-gst_mpdparser_free_segment_url_node (GstSegmentURLNode * segment_url)
-{
-  if (segment_url) {
-    if (segment_url->media)
-      xmlFree (segment_url->media);
-    g_slice_free (GstRange, segment_url->mediaRange);
-    if (segment_url->index)
-      xmlFree (segment_url->index);
-    g_slice_free (GstRange, segment_url->indexRange);
-    g_slice_free (GstSegmentURLNode, segment_url);
-  }
-}
-
-static void
-gst_mpdparser_free_base_url_node (GstBaseURL * base_url_node)
-{
-  if (base_url_node) {
-    if (base_url_node->baseURL)
-      xmlFree (base_url_node->baseURL);
-    if (base_url_node->serviceLocation)
-      xmlFree (base_url_node->serviceLocation);
-    if (base_url_node->byteRange)
-      xmlFree (base_url_node->byteRange);
-    g_slice_free (GstBaseURL, base_url_node);
-  }
-}
-
-static void
-gst_mpdparser_free_descriptor_type_node (GstDescriptorType * descriptor_type)
-{
-  if (descriptor_type) {
-    if (descriptor_type->schemeIdUri)
-      xmlFree (descriptor_type->schemeIdUri);
-    if (descriptor_type->value)
-      xmlFree (descriptor_type->value);
-    g_slice_free (GstDescriptorType, descriptor_type);
-  }
-}
-
-static void
-gst_mpdparser_free_content_component_node (GstContentComponentNode *
-    content_component_node)
-{
-  if (content_component_node) {
-    if (content_component_node->lang)
-      xmlFree (content_component_node->lang);
-    if (content_component_node->contentType)
-      xmlFree (content_component_node->contentType);
-    g_slice_free (GstRatio, content_component_node->par);
-    g_list_free_full (content_component_node->Accessibility,
-        (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
-    g_list_free_full (content_component_node->Role,
-        (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
-    g_list_free_full (content_component_node->Rating,
-        (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
-    g_list_free_full (content_component_node->Viewpoint,
-        (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
-    g_slice_free (GstContentComponentNode, content_component_node);
-  }
-}
-
-static void
-gst_mpdparser_free_utctiming_node (GstUTCTimingNode * timing_type)
-{
-  if (timing_type) {
-    if (timing_type->urls)
-      g_strfreev (timing_type->urls);
-    g_slice_free (GstUTCTimingNode, timing_type);
-  }
-}
-
-static void
-gst_mpdparser_free_stream_period (GstStreamPeriod * stream_period)
-{
-  if (stream_period) {
-    g_slice_free (GstStreamPeriod, stream_period);
-  }
-}
-
-static void
-gst_mpdparser_free_media_segment (GstMediaSegment * media_segment)
-{
-  if (media_segment) {
-    g_slice_free (GstMediaSegment, media_segment);
-  }
-}
-
-static void
-gst_mpdparser_init_active_stream_segments (GstActiveStream * stream)
-{
-  g_assert (stream->segments == NULL);
-  stream->segments = g_ptr_array_new ();
-  g_ptr_array_set_free_func (stream->segments,
-      (GDestroyNotify) gst_mpdparser_free_media_segment);
-}
-
-static void
-gst_mpdparser_free_active_stream (GstActiveStream * active_stream)
-{
-  if (active_stream) {
-    g_free (active_stream->baseURL);
-    active_stream->baseURL = NULL;
-    g_free (active_stream->queryURL);
-    active_stream->queryURL = NULL;
-    if (active_stream->segments)
-      g_ptr_array_unref (active_stream->segments);
-    g_slice_free (GstActiveStream, active_stream);
-  }
-}
-
-static gchar *
-gst_mpdparser_get_mediaURL (GstActiveStream * stream,
-    GstSegmentURLNode * segmentURL)
-{
-  const gchar *url_prefix;
-
-  g_return_val_if_fail (stream != NULL, NULL);
-  g_return_val_if_fail (segmentURL != NULL, NULL);
-
-  url_prefix = segmentURL->media ? segmentURL->media : stream->baseURL;
-  g_return_val_if_fail (url_prefix != NULL, NULL);
-
-  return segmentURL->media;
-}
-
-static const gchar *
-gst_mpdparser_get_initializationURL (GstActiveStream * stream,
-    GstURLType * InitializationURL)
-{
-  const gchar *url_prefix;
-
-  g_return_val_if_fail (stream != NULL, NULL);
-
-  url_prefix = (InitializationURL
-      && InitializationURL->sourceURL) ? InitializationURL->
-      sourceURL : stream->baseURL;
-
-  return url_prefix;
-}
-
-/* ISO/IEC 23009-1:2004 5.3.9.4.4 */
-static gboolean
-validate_format (const gchar * format)
-{
-  const gchar *p = format;
-
-  /* Check if it starts with % */
-  if (!p || p[0] != '%')
-    return FALSE;
-  p++;
-
-  /* the spec mandates a format like %0[width]d */
-  /* Following the %, we must have a 0 */
-  if (p[0] != '0')
-    return FALSE;
-
-  /* Following the % must be a number starting with 0
-   */
-  while (g_ascii_isdigit (*p))
-    p++;
-
-  /* After any 0 and alphanumeric values, there must be a d.
-   */
-  if (p[0] != 'd')
-    return FALSE;
-  p++;
-
-  /* And then potentially more characters without any
-   * further %, even if the spec does not mention this
-   */
-  p = strchr (p, '%');
-  if (p)
-    return FALSE;
-
-  return TRUE;
-}
-
-static gchar *
-promote_format_to_uint64 (const gchar * format)
-{
-  const gchar *p = format;
-  gchar *promoted_format;
-
-  /* Must be called with a validated format! */
-  g_return_val_if_fail (validate_format (format), NULL);
-
-  /* it starts with % */
-  p++;
-
-  /* Following the % must be a 0, or any of d, x or u.
-   * x and u are not part of the spec, but don't hurt us
-   */
-  if (p[0] == '0') {
-    p++;
-
-    while (g_ascii_isdigit (*p))
-      p++;
-  }
-
-  /* After any 0 and alphanumeric values, there must be a d.
-   * Otherwise validation would have failed
-   */
-  g_assert (p[0] == 'd');
-
-  promoted_format =
-      g_strdup_printf ("%.*s" G_GINT64_MODIFIER "%s", (gint) (p - format),
-      format, p);
-
-  return promoted_format;
-}
-
-static gboolean
-gst_mpdparser_validate_rfc1738_url (const char *s)
-{
-  while (*s) {
-    if (!strchr
-        (";:@&=aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ0123456789$-_.+!*'(),%/",
-            *s))
-      return FALSE;
-    if (*s == '%') {
-      /* g_ascii_isdigit returns FALSE for NUL, and || is a short circuiting
-         operator, so this is safe for strings ending before two hex digits */
-      if (!g_ascii_isxdigit (s[1]) || !g_ascii_isxdigit (s[2]))
-        return FALSE;
-      s += 2;
-    }
-    s++;
-  }
-  return TRUE;
-}
-
-static gchar *
-gst_mpdparser_build_URL_from_template (const gchar * url_template,
-    const gchar * id, guint number, guint bandwidth, guint64 time)
-{
-  static const gchar default_format[] = "%01d";
-  gchar **tokens, *token, *ret;
-  const gchar *format;
-  gint i, num_tokens;
-
-  g_return_val_if_fail (url_template != NULL, NULL);
-  tokens = g_strsplit_set (url_template, "$", -1);
-  if (!tokens) {
-    GST_WARNING ("Scan of URL template failed!");
-    return NULL;
-  }
-  num_tokens = g_strv_length (tokens);
-
-  /*
-   * each identifier is guarded by 2 $, which means that we must have an odd number of tokens
-   * An even number of tokens means the string is not valid.
-   */
-  if ((num_tokens & 1) == 0) {
-    GST_ERROR ("Invalid number of tokens (%d). url_template is '%s'",
-        num_tokens, url_template);
-    g_strfreev (tokens);
-    return NULL;
-  }
-
-  for (i = 0; i < num_tokens; i++) {
-    token = tokens[i];
-    format = default_format;
-
-    /* the tokens to replace must be provided between $ characters, eg $token$
-     * For a string like token0$token1$token2$token3$token4, only the odd number
-     * tokens (1,3,...) must be parsed.
-     *
-     * Skip even tokens
-     */
-    if ((i & 1) == 0)
-      continue;
-
-    if (!g_strcmp0 (token, "RepresentationID")) {
-      if (!gst_mpdparser_validate_rfc1738_url (id))
-        goto invalid_representation_id;
-
-      tokens[i] = g_strdup_printf ("%s", id);
-      g_free (token);
-    } else if (!strncmp (token, "Number", 6)) {
-      if (strlen (token) > 6) {
-        format = token + 6;     /* format tag */
-      }
-      if (!validate_format (format))
-        goto invalid_format;
-
-      tokens[i] = g_strdup_printf (format, number);
-      g_free (token);
-    } else if (!strncmp (token, "Bandwidth", 9)) {
-      if (strlen (token) > 9) {
-        format = token + 9;     /* format tag */
-      }
-      if (!validate_format (format))
-        goto invalid_format;
-
-      tokens[i] = g_strdup_printf (format, bandwidth);
-      g_free (token);
-    } else if (!strncmp (token, "Time", 4)) {
-      gchar *promoted_format;
-
-      if (strlen (token) > 4) {
-        format = token + 4;     /* format tag */
-      }
-      if (!validate_format (format))
-        goto invalid_format;
-
-      promoted_format = promote_format_to_uint64 (format);
-      tokens[i] = g_strdup_printf (promoted_format, time);
-      g_free (promoted_format);
-      g_free (token);
-    } else if (!g_strcmp0 (token, "")) {
-      tokens[i] = g_strdup_printf ("%s", "$");
-      g_free (token);
-    } else {
-      /* unexpected identifier found between $ signs
-       *
-       * "If the URL contains unescaped $ symbols which do not enclose a valid
-       * identifier then the result of URL formation is undefined"
-       */
-      goto invalid_format;
-    }
-  }
-
-  ret = g_strjoinv (NULL, tokens);
-
-  g_strfreev (tokens);
-
-  return ret;
-
-invalid_format:
-  {
-    GST_ERROR ("Invalid format '%s' in '%s'", format, token);
-
-    g_strfreev (tokens);
-
-    return NULL;
-  }
-invalid_representation_id:
-  {
-    GST_ERROR
-        ("Representation ID string '%s' has characters invalid in an RFC 1738 URL",
-        id);
-
-    g_strfreev (tokens);
-
-    return NULL;
-  }
-}
-
-guint
-gst_mpd_client_get_period_index_at_time (GstMpdClient * client,
-    GstDateTime * time)
-{
-  GList *iter;
-  guint period_idx = G_MAXUINT;
-  guint idx;
-  gint64 time_offset;
-  GstDateTime *avail_start =
-      gst_mpd_client_get_availability_start_time (client);
-  GstStreamPeriod *stream_period;
-
-  if (avail_start == NULL)
-    return 0;
-
-  time_offset = gst_mpd_client_calculate_time_difference (avail_start, time);
-  gst_date_time_unref (avail_start);
-
-  if (time_offset < 0)
-    return 0;
-
-  if (!gst_mpd_client_setup_media_presentation (client, time_offset, -1, NULL))
-    return 0;
-
-  for (idx = 0, iter = client->periods; iter; idx++, iter = g_list_next (iter)) {
-    stream_period = iter->data;
-    if (stream_period->start <= time_offset
-        && (!GST_CLOCK_TIME_IS_VALID (stream_period->duration)
-            || stream_period->start + stream_period->duration > time_offset)) {
-      period_idx = idx;
-      break;
-    }
-  }
-
-  return period_idx;
-}
-
-static GstStreamPeriod *
-gst_mpdparser_get_stream_period (GstMpdClient * client)
-{
-  g_return_val_if_fail (client != NULL, NULL);
-  g_return_val_if_fail (client->periods != NULL, NULL);
-
-  return g_list_nth_data (client->periods, client->period_idx);
-}
-
-static GstRange *
-gst_mpdparser_clone_range (GstRange * range)
-{
-  GstRange *clone = NULL;
-
-  if (range) {
-    clone = g_slice_new0 (GstRange);
-    clone->first_byte_pos = range->first_byte_pos;
-    clone->last_byte_pos = range->last_byte_pos;
-  }
-
-  return clone;
-}
-
-static GstURLType *
-gst_mpdparser_clone_URL (GstURLType * url)
-{
-
-  GstURLType *clone = NULL;
-
-  if (url) {
-    clone = g_slice_new0 (GstURLType);
-    if (url->sourceURL) {
-      clone->sourceURL = xmlMemStrdup (url->sourceURL);
-    }
-    clone->range = gst_mpdparser_clone_range (url->range);
-  }
-
-  return clone;
-}
-
-/*
- * Combine a base url with the current stream base url from the list of
- * baseURLs. Takes ownership of base and returns a new base.
- */
-static GstUri *
-combine_urls (GstUri * base, GList * list, gchar ** query, guint idx)
-{
-  GstBaseURL *baseURL;
-  GstUri *ret = base;
-
-  if (list != NULL) {
-    baseURL = g_list_nth_data (list, idx);
-    if (!baseURL) {
-      baseURL = list->data;
-    }
-
-    ret = gst_uri_from_string_with_base (base, baseURL->baseURL);
-    gst_uri_unref (base);
-
-    if (ret && query) {
-      g_free (*query);
-      *query = gst_uri_get_query_string (ret);
-      if (*query) {
-        ret = gst_uri_make_writable (ret);
-        gst_uri_set_query_table (ret, NULL);
-      }
-    }
-  }
-
-  return ret;
-}
-
-/* select a stream and extract the baseURL (if present) */
-static gchar *
-gst_mpdparser_parse_baseURL (GstMpdClient * client, GstActiveStream * stream,
-    gchar ** query)
-{
-  GstStreamPeriod *stream_period;
-  static const gchar empty[] = "";
-  gchar *ret = NULL;
-  GstUri *abs_url;
-
-  g_return_val_if_fail (stream != NULL, g_strdup (empty));
-  stream_period = gst_mpdparser_get_stream_period (client);
-  g_return_val_if_fail (stream_period != NULL, g_strdup (empty));
-  g_return_val_if_fail (stream_period->period != NULL, g_strdup (empty));
-
-  /* NULLify query return before we start */
-  if (query)
-    *query = NULL;
-
-  /* initialise base url */
-  abs_url =
-      gst_uri_from_string (client->
-      mpd_base_uri ? client->mpd_base_uri : client->mpd_uri);
-
-  /* combine a BaseURL at the MPD level with the current base url */
-  abs_url =
-      combine_urls (abs_url, client->mpd_node->BaseURLs, query,
-      stream->baseURL_idx);
-
-  /* combine a BaseURL at the Period level with the current base url */
-  abs_url =
-      combine_urls (abs_url, stream_period->period->BaseURLs, query,
-      stream->baseURL_idx);
-
-  GST_DEBUG ("Current adaptation set id %i (%s)", stream->cur_adapt_set->id,
-      stream->cur_adapt_set->contentType);
-  /* combine a BaseURL at the AdaptationSet level with the current base url */
-  abs_url =
-      combine_urls (abs_url, stream->cur_adapt_set->BaseURLs, query,
-      stream->baseURL_idx);
-
-  /* combine a BaseURL at the Representation level with the current base url */
-  abs_url =
-      combine_urls (abs_url, stream->cur_representation->BaseURLs, query,
-      stream->baseURL_idx);
-
-  ret = gst_uri_to_string (abs_url);
-  gst_uri_unref (abs_url);
-
-  return ret;
-}
-
-static GstClockTime
-gst_mpd_client_get_segment_duration (GstMpdClient * client,
-    GstActiveStream * stream, guint64 * scale_dur)
-{
-  GstStreamPeriod *stream_period;
-  GstMultSegmentBaseType *base = NULL;
-  GstClockTime duration = 0;
-
-  g_return_val_if_fail (stream != NULL, GST_CLOCK_TIME_NONE);
-  stream_period = gst_mpdparser_get_stream_period (client);
-  g_return_val_if_fail (stream_period != NULL, GST_CLOCK_TIME_NONE);
-
-  if (stream->cur_segment_list) {
-    base = stream->cur_segment_list->MultSegBaseType;
-  } else if (stream->cur_seg_template) {
-    base = stream->cur_seg_template->MultSegBaseType;
-  }
-
-  if (base == NULL || base->SegBaseType == NULL) {
-    /* this may happen when we have a single segment */
-    duration = stream_period->duration;
-    if (scale_dur)
-      *scale_dur = duration;
-  } else {
-    /* duration is guint so this cannot overflow */
-    duration = base->duration * GST_SECOND;
-    if (scale_dur)
-      *scale_dur = duration;
-    duration /= base->SegBaseType->timescale;
-  }
-
-  return duration;
-}
-
-/*****************************/
-/******* API functions *******/
-/*****************************/
-
-GstMpdClient *
-gst_mpd_client_new (void)
-{
-  GstMpdClient *client;
-
-  client = g_new0 (GstMpdClient, 1);
-
-  return client;
-}
-
-void
-gst_active_streams_free (GstMpdClient * client)
-{
-  if (client->active_streams) {
-    g_list_foreach (client->active_streams,
-        (GFunc) gst_mpdparser_free_active_stream, NULL);
-    g_list_free (client->active_streams);
-    client->active_streams = NULL;
-  }
-}
-
-void
-gst_mpd_client_free (GstMpdClient * client)
-{
-  g_return_if_fail (client != NULL);
-
-  if (client->mpd_node)
-    gst_mpdparser_free_mpd_node (client->mpd_node);
-
-  if (client->periods) {
-    g_list_free_full (client->periods,
-        (GDestroyNotify) gst_mpdparser_free_stream_period);
-  }
-
-  gst_active_streams_free (client);
-
-  g_free (client->mpd_uri);
-  client->mpd_uri = NULL;
-  g_free (client->mpd_base_uri);
-  client->mpd_base_uri = NULL;
-
-  if (client->downloader)
-    gst_object_unref (client->downloader);
-  client->downloader = NULL;
-
-  g_free (client);
-}
-
-void
-gst_mpd_client_set_uri_downloader (GstMpdClient * client,
-    GstUriDownloader * downloader)
-{
-  if (client->downloader)
-    gst_object_unref (client->downloader);
-  client->downloader = gst_object_ref (downloader);
-}
-
-static void
-gst_mpd_client_check_profiles (GstMpdClient * client)
-{
-  GST_DEBUG ("Profiles: %s",
-      client->mpd_node->profiles ? client->mpd_node->profiles : "<none>");
-
-  if (!client->mpd_node->profiles)
-    return;
-
-  if (g_strstr_len (client->mpd_node->profiles, -1,
-          "urn:mpeg:dash:profile:isoff-on-demand:2011")) {
-    client->profile_isoff_ondemand = TRUE;
-    GST_DEBUG ("Found ISOFF on demand profile (2011)");
-  }
-}
-
-static void
-gst_mpd_client_fetch_on_load_external_resources (GstMpdClient * client)
-{
-  GList *l;
-
-  for (l = client->mpd_node->Periods; l; /* explicitly advanced below */ ) {
-    GstPeriodNode *period = l->data;
-    GList *m;
-
-    if (period->xlink_href && period->actuate == GST_XLINK_ACTUATE_ON_LOAD) {
-      GList *new_periods, *prev, *next;
-
-      new_periods = gst_mpd_client_fetch_external_period (client, period);
-
-      prev = l->prev;
-      client->mpd_node->Periods =
-          g_list_delete_link (client->mpd_node->Periods, l);
-      gst_mpdparser_free_period_node (period);
-      period = NULL;
-
-      /* Get new next node, we will insert before this */
-      if (prev)
-        next = prev->next;
-      else
-        next = client->mpd_node->Periods;
-
-      while (new_periods) {
-        client->mpd_node->Periods =
-            g_list_insert_before (client->mpd_node->Periods, next,
-            new_periods->data);
-        new_periods = g_list_delete_link (new_periods, new_periods);
-      }
-      next = NULL;
-
-      /* Update our iterator to the first new period if any, or the next */
-      if (prev)
-        l = prev->next;
-      else
-        l = client->mpd_node->Periods;
-
-      continue;
-    }
-
-    if (period->SegmentList && period->SegmentList->xlink_href
-        && period->SegmentList->actuate == GST_XLINK_ACTUATE_ON_LOAD) {
-      GstSegmentListNode *new_segment_list;
-
-      new_segment_list =
-          gst_mpd_client_fetch_external_segment_list (client, period, NULL,
-          NULL, NULL, period->SegmentList);
-
-      gst_mpdparser_free_segment_list_node (period->SegmentList);
-      period->SegmentList = new_segment_list;
-    }
-
-    for (m = period->AdaptationSets; m; /* explicitly advanced below */ ) {
-      GstAdaptationSetNode *adapt_set = m->data;
-      GList *n;
-
-      if (adapt_set->xlink_href
-          && adapt_set->actuate == GST_XLINK_ACTUATE_ON_LOAD) {
-        GList *new_adapt_sets, *prev, *next;
-
-        new_adapt_sets =
-            gst_mpd_client_fetch_external_adaptation_set (client, period,
-            adapt_set);
-
-        prev = m->prev;
-        period->AdaptationSets = g_list_delete_link (period->AdaptationSets, m);
-        gst_mpdparser_free_adaptation_set_node (adapt_set);
-        adapt_set = NULL;
-
-        /* Get new next node, we will insert before this */
-        if (prev)
-          next = prev->next;
-        else
-          next = period->AdaptationSets;
-
-        while (new_adapt_sets) {
-          period->AdaptationSets =
-              g_list_insert_before (period->AdaptationSets, next,
-              new_adapt_sets->data);
-          new_adapt_sets = g_list_delete_link (new_adapt_sets, new_adapt_sets);
-        }
-        next = NULL;
-
-        /* Update our iterator to the first new adapt_set if any, or the next */
-        if (prev)
-          m = prev->next;
-        else
-          m = period->AdaptationSets;
-
-        continue;
-      }
-
-      if (adapt_set->SegmentList && adapt_set->SegmentList->xlink_href
-          && adapt_set->SegmentList->actuate == GST_XLINK_ACTUATE_ON_LOAD) {
-        GstSegmentListNode *new_segment_list;
-
-        new_segment_list =
-            gst_mpd_client_fetch_external_segment_list (client, period,
-            adapt_set, NULL, period->SegmentList, adapt_set->SegmentList);
-
-        gst_mpdparser_free_segment_list_node (adapt_set->SegmentList);
-        adapt_set->SegmentList = new_segment_list;
-      }
-
-      for (n = adapt_set->Representations; n; n = n->next) {
-        GstRepresentationNode *representation = n->data;
-
-        if (representation->SegmentList
-            && representation->SegmentList->xlink_href
-            && representation->SegmentList->actuate ==
-            GST_XLINK_ACTUATE_ON_LOAD) {
-
-          GstSegmentListNode *new_segment_list;
-
-          new_segment_list =
-              gst_mpd_client_fetch_external_segment_list (client, period,
-              adapt_set, representation, adapt_set->SegmentList,
-              representation->SegmentList);
-
-          gst_mpdparser_free_segment_list_node (representation->SegmentList);
-          representation->SegmentList = new_segment_list;
-
-        }
-      }
-
-      m = m->next;
-    }
-
-    l = l->next;
-  }
-}
-
-gboolean
-gst_mpd_parse (GstMpdClient * client, const gchar * data, gint size)
-{
-  gboolean ret = FALSE;
-
-  if (data) {
-    xmlDocPtr doc;
-    xmlNode *root_element = NULL;
-
-    GST_DEBUG ("MPD file fully buffered, start parsing...");
-
-    /* parse the complete MPD file into a tree (using the libxml2 default parser API) */
-
-    /* this initialize the library and check potential ABI mismatches
-     * between the version it was compiled for and the actual shared
-     * library used
-     */
-    LIBXML_TEST_VERSION;
-
-    /* parse "data" into a document (which is a libxml2 tree structure xmlDoc) */
-    doc = xmlReadMemory (data, size, "noname.xml", NULL, XML_PARSE_NONET);
-    if (doc == NULL) {
-      GST_ERROR ("failed to parse the MPD file");
-      ret = FALSE;
-    } else {
-      /* get the root element node */
-      root_element = xmlDocGetRootElement (doc);
-
-      if (root_element->type != XML_ELEMENT_NODE
-          || xmlStrcmp (root_element->name, (xmlChar *) "MPD") != 0) {
-        GST_ERROR
-            ("can not find the root element MPD, failed to parse the MPD file");
-        ret = FALSE;            /* used to return TRUE before, but this seems wrong */
-      } else {
-        /* now we can parse the MPD root node and all children nodes, recursively */
-        ret = gst_mpdparser_parse_root_node (&client->mpd_node, root_element);
-      }
-      /* free the document */
-      xmlFreeDoc (doc);
-    }
-
-    if (ret) {
-      gst_mpd_client_check_profiles (client);
-      gst_mpd_client_fetch_on_load_external_resources (client);
-    }
-  }
-
-  return ret;
-}
-
-const gchar *
-gst_mpdparser_get_baseURL (GstMpdClient * client, guint indexStream)
-{
-  GstActiveStream *stream;
-
-  g_return_val_if_fail (client != NULL, NULL);
-  g_return_val_if_fail (client->active_streams != NULL, NULL);
-  stream = g_list_nth_data (client->active_streams, indexStream);
-  g_return_val_if_fail (stream != NULL, NULL);
-
-  return stream->baseURL;
-}
-
-static GstClockTime
-gst_mpdparser_get_segment_end_time (GstMpdClient * client, GPtrArray * segments,
-    const GstMediaSegment * segment, gint index)
-{
-  const GstStreamPeriod *stream_period;
-  GstClockTime end;
-
-  if (segment->repeat >= 0)
-    return segment->start + (segment->repeat + 1) * segment->duration;
-
-  if (index < segments->len - 1) {
-    const GstMediaSegment *next_segment =
-        g_ptr_array_index (segments, index + 1);
-    end = next_segment->start;
-  } else {
-    stream_period = gst_mpdparser_get_stream_period (client);
-    end = stream_period->start + stream_period->duration;
-  }
-  return end;
-}
-
-static gboolean
-gst_mpd_client_add_media_segment (GstActiveStream * stream,
-    GstSegmentURLNode * url_node, guint number, gint repeat,
-    guint64 scale_start, guint64 scale_duration,
-    GstClockTime start, GstClockTime duration)
-{
-  GstMediaSegment *media_segment;
-
-  g_return_val_if_fail (stream->segments != NULL, FALSE);
-
-  media_segment = g_slice_new0 (GstMediaSegment);
-
-  media_segment->SegmentURL = url_node;
-  media_segment->number = number;
-  media_segment->scale_start = scale_start;
-  media_segment->scale_duration = scale_duration;
-  media_segment->start = start;
-  media_segment->duration = duration;
-  media_segment->repeat = repeat;
-
-  g_ptr_array_add (stream->segments, media_segment);
-  GST_LOG ("Added new segment: number %d, repeat %d, "
-      "ts: %" GST_TIME_FORMAT ", dur: %"
-      GST_TIME_FORMAT, number, repeat,
-      GST_TIME_ARGS (start), GST_TIME_ARGS (duration));
-
-  return TRUE;
-}
-
-static void
-gst_mpd_client_stream_update_presentation_time_offset (GstMpdClient * client,
-    GstActiveStream * stream)
-{
-  GstSegmentBaseType *segbase = NULL;
-
-  /* Find the used segbase */
-  if (stream->cur_segment_list) {
-    segbase = stream->cur_segment_list->MultSegBaseType->SegBaseType;
-  } else if (stream->cur_seg_template) {
-    segbase = stream->cur_seg_template->MultSegBaseType->SegBaseType;
-  } else if (stream->cur_segment_base) {
-    segbase = stream->cur_segment_base;
-  }
-
-  if (segbase) {
-    /* Avoid overflows */
-    stream->presentationTimeOffset =
-        gst_util_uint64_scale (segbase->presentationTimeOffset, GST_SECOND,
-        segbase->timescale);
-  } else {
-    stream->presentationTimeOffset = 0;
-  }
-
-  GST_LOG ("Setting stream's presentation time offset to %" GST_TIME_FORMAT,
-      GST_TIME_ARGS (stream->presentationTimeOffset));
-}
-
-gboolean
-gst_mpd_client_setup_representation (GstMpdClient * client,
-    GstActiveStream * stream, GstRepresentationNode * representation)
-{
-  GstStreamPeriod *stream_period;
-  GList *rep_list;
-  GstClockTime PeriodStart, PeriodEnd, start_time, duration;
-  guint i;
-  guint64 start;
-
-  if (stream->cur_adapt_set == NULL) {
-    GST_WARNING ("No valid AdaptationSet node in the MPD file, aborting...");
-    return FALSE;
-  }
-
-  rep_list = stream->cur_adapt_set->Representations;
-  stream->cur_representation = representation;
-  stream->representation_idx = g_list_index (rep_list, representation);
-
-  /* clean the old segment list, if any */
-  if (stream->segments) {
-    g_ptr_array_unref (stream->segments);
-    stream->segments = NULL;
-  }
-
-  stream_period = gst_mpdparser_get_stream_period (client);
-  g_return_val_if_fail (stream_period != NULL, FALSE);
-  g_return_val_if_fail (stream_period->period != NULL, FALSE);
-
-  PeriodStart = stream_period->start;
-  if (GST_CLOCK_TIME_IS_VALID (stream_period->duration))
-    PeriodEnd = stream_period->start + stream_period->duration;
-  else
-    PeriodEnd = GST_CLOCK_TIME_NONE;
-
-  GST_LOG ("Building segment list for Period from %" GST_TIME_FORMAT " to %"
-      GST_TIME_FORMAT, GST_TIME_ARGS (PeriodStart), GST_TIME_ARGS (PeriodEnd));
-
-  if (representation->SegmentBase != NULL
-      || representation->SegmentList != NULL) {
-    GList *SegmentURL;
-
-    /* We have a fixed list of segments for any of the cases here,
-     * init the segments list */
-    gst_mpdparser_init_active_stream_segments (stream);
-
-    /* get the first segment_base of the selected representation */
-    if ((stream->cur_segment_base =
-            gst_mpdparser_get_segment_base (stream_period->period,
-                stream->cur_adapt_set, representation)) == NULL) {
-      GST_DEBUG ("No useful SegmentBase node for the current Representation");
-    }
-
-    /* get the first segment_list of the selected representation */
-    if ((stream->cur_segment_list =
-            gst_mpdparser_get_segment_list (client, stream_period->period,
-                stream->cur_adapt_set, representation)) == NULL) {
-      GST_DEBUG ("No useful SegmentList node for the current Representation");
-      /* here we should have a single segment for each representation, whose URL is encoded in the baseURL element */
-      if (!gst_mpd_client_add_media_segment (stream, NULL, 1, 0, 0,
-              PeriodEnd - PeriodStart, PeriodStart, PeriodEnd - PeriodStart)) {
-        return FALSE;
-      }
-    } else {
-      /* build the list of GstMediaSegment nodes from the SegmentList node */
-      SegmentURL = stream->cur_segment_list->SegmentURL;
-      if (SegmentURL == NULL) {
-        GST_WARNING
-            ("No valid list of SegmentURL nodes in the MPD file, aborting...");
-        return FALSE;
-      }
-
-      /* build segment list */
-      i = stream->cur_segment_list->MultSegBaseType->startNumber;
-      start = 0;
-      start_time = PeriodStart;
-
-      GST_LOG ("Building media segment list using a SegmentList node");
-      if (stream->cur_segment_list->MultSegBaseType->SegmentTimeline) {
-        GstSegmentTimelineNode *timeline;
-        GstSNode *S;
-        GList *list;
-        GstClockTime presentationTimeOffset;
-        GstSegmentBaseType *segbase;
-
-        segbase = stream->cur_segment_list->MultSegBaseType->SegBaseType;
-        presentationTimeOffset =
-            gst_util_uint64_scale (segbase->presentationTimeOffset, GST_SECOND,
-            segbase->timescale);
-        GST_LOG ("presentationTimeOffset = %" G_GUINT64_FORMAT,
-            presentationTimeOffset);
-        timeline = stream->cur_segment_list->MultSegBaseType->SegmentTimeline;
-        for (list = g_queue_peek_head_link (&timeline->S); list;
-            list = g_list_next (list)) {
-          guint timescale;
-
-          S = (GstSNode *) list->data;
-          GST_LOG ("Processing S node: d=%" G_GUINT64_FORMAT " r=%d t=%"
-              G_GUINT64_FORMAT, S->d, S->r, S->t);
-          timescale =
-              stream->cur_segment_list->MultSegBaseType->SegBaseType->timescale;
-          duration = gst_util_uint64_scale (S->d, GST_SECOND, timescale);
-
-          if (S->t > 0) {
-            start = S->t;
-            start_time = gst_util_uint64_scale (S->t, GST_SECOND, timescale)
-                + PeriodStart - presentationTimeOffset;
-          }
-
-          if (!SegmentURL) {
-            GST_WARNING
-                ("SegmentTimeline does not have a matching SegmentURL, aborting...");
-            return FALSE;
-          }
-
-          if (!gst_mpd_client_add_media_segment (stream, SegmentURL->data, i,
-                  S->r, start, S->d, start_time, duration)) {
-            return FALSE;
-          }
-          i += S->r + 1;
-          start_time += duration * (S->r + 1);
-          start += S->d * (S->r + 1);
-          SegmentURL = g_list_next (SegmentURL);
-        }
-      } else {
-        guint64 scale_dur;
-
-        duration =
-            gst_mpd_client_get_segment_duration (client, stream, &scale_dur);
-        if (!GST_CLOCK_TIME_IS_VALID (duration))
-          return FALSE;
-
-        while (SegmentURL) {
-          if (!gst_mpd_client_add_media_segment (stream, SegmentURL->data, i,
-                  0, start, scale_dur, start_time, duration)) {
-            return FALSE;
-          }
-          i++;
-          start += scale_dur;
-          start_time += duration;
-          SegmentURL = g_list_next (SegmentURL);
-        }
-      }
-    }
-  } else {
-    if (representation->SegmentTemplate != NULL) {
-      stream->cur_seg_template = representation->SegmentTemplate;
-    } else if (stream->cur_adapt_set->SegmentTemplate != NULL) {
-      stream->cur_seg_template = stream->cur_adapt_set->SegmentTemplate;
-    } else if (stream_period->period->SegmentTemplate != NULL) {
-      stream->cur_seg_template = stream_period->period->SegmentTemplate;
-    }
-
-    if (stream->cur_seg_template == NULL
-        || stream->cur_seg_template->MultSegBaseType == NULL) {
-
-      gst_mpdparser_init_active_stream_segments (stream);
-      /* here we should have a single segment for each representation, whose URL is encoded in the baseURL element */
-      if (!gst_mpd_client_add_media_segment (stream, NULL, 1, 0, 0,
-              PeriodEnd - PeriodStart, 0, PeriodEnd - PeriodStart)) {
-        return FALSE;
-      }
-    } else {
-      GstClockTime presentationTimeOffset;
-      GstMultSegmentBaseType *mult_seg =
-          stream->cur_seg_template->MultSegBaseType;
-
-      presentationTimeOffset =
-          gst_util_uint64_scale (mult_seg->SegBaseType->presentationTimeOffset,
-          GST_SECOND, mult_seg->SegBaseType->timescale);
-      GST_LOG ("presentationTimeOffset = %" GST_TIME_FORMAT,
-          GST_TIME_ARGS (presentationTimeOffset));
-      /* build segment list */
-      i = mult_seg->startNumber;
-      start = 0;
-      start_time = 0;
-
-      GST_LOG ("Building media segment list using this template: %s",
-          stream->cur_seg_template->media);
-
-      if (mult_seg->SegmentTimeline) {
-        GstSegmentTimelineNode *timeline;
-        GstSNode *S;
-        GList *list;
-
-        timeline = mult_seg->SegmentTimeline;
-        gst_mpdparser_init_active_stream_segments (stream);
-        for (list = g_queue_peek_head_link (&timeline->S); list;
-            list = g_list_next (list)) {
-          guint timescale;
-
-          S = (GstSNode *) list->data;
-          GST_LOG ("Processing S node: d=%" G_GUINT64_FORMAT " r=%u t=%"
-              G_GUINT64_FORMAT, S->d, S->r, S->t);
-          timescale = mult_seg->SegBaseType->timescale;
-          duration = gst_util_uint64_scale (S->d, GST_SECOND, timescale);
-          if (S->t > 0) {
-            start = S->t;
-            start_time = gst_util_uint64_scale (S->t, GST_SECOND, timescale)
-                + PeriodStart - presentationTimeOffset;
-          }
-
-          if (!gst_mpd_client_add_media_segment (stream, NULL, i, S->r, start,
-                  S->d, start_time, duration)) {
-            return FALSE;
-          }
-          i += S->r + 1;
-          start += S->d * (S->r + 1);
-          start_time += duration * (S->r + 1);
-        }
-      } else {
-        /* NOP - The segment is created on demand with the template, no need
-         * to build a list */
-      }
-    }
-  }
-
-  /* clip duration of segments to stop at period end */
-  if (stream->segments && stream->segments->len) {
-    if (GST_CLOCK_TIME_IS_VALID (PeriodEnd)) {
-      guint n;
-
-      for (n = 0; n < stream->segments->len; ++n) {
-        GstMediaSegment *media_segment =
-            g_ptr_array_index (stream->segments, n);
-        if (media_segment) {
-          if (media_segment->start + media_segment->duration > PeriodEnd) {
-            GstClockTime stop = PeriodEnd;
-            if (n < stream->segments->len - 1) {
-              GstMediaSegment *next_segment =
-                  g_ptr_array_index (stream->segments, n + 1);
-              if (next_segment && next_segment->start < PeriodEnd)
-                stop = next_segment->start;
-            }
-            media_segment->duration =
-                media_segment->start > stop ? 0 : stop - media_segment->start;
-            GST_LOG ("Fixed duration of segment %u: %" GST_TIME_FORMAT, n,
-                GST_TIME_ARGS (media_segment->duration));
-
-            /* If the segment was clipped entirely, we discard it and all
-             * subsequent ones */
-            if (media_segment->duration == 0) {
-              GST_WARNING ("Discarding %u segments outside period",
-                  stream->segments->len - n);
-              /* _set_size should properly unref elements */
-              g_ptr_array_set_size (stream->segments, n);
-              break;
-            }
-          }
-        }
-      }
-    }
-#ifndef GST_DISABLE_GST_DEBUG
-    if (stream->segments->len > 0) {
-      GstMediaSegment *last_media_segment =
-          g_ptr_array_index (stream->segments, stream->segments->len - 1);
-      GST_LOG ("Built a list of %d segments", last_media_segment->number);
-    } else {
-      GST_LOG ("All media segments were clipped");
-    }
-#endif
-  }
-
-  g_free (stream->baseURL);
-  g_free (stream->queryURL);
-  stream->baseURL =
-      gst_mpdparser_parse_baseURL (client, stream, &stream->queryURL);
-
-  gst_mpd_client_stream_update_presentation_time_offset (client, stream);
-
-  return TRUE;
-}
-
-#define CUSTOM_WRAPPER_START "<custom_wrapper>"
-#define CUSTOM_WRAPPER_END "</custom_wrapper>"
-
-static GList *
-gst_mpd_client_fetch_external_period (GstMpdClient * client,
-    GstPeriodNode * period_node)
-{
-  GstFragment *download;
-  GstAdapter *adapter;
-  GstBuffer *period_buffer;
-  GError *err = NULL;
-  xmlDocPtr doc = NULL;
-  GstUri *base_uri, *uri;
-  gchar *query = NULL;
-  gchar *uri_string, *wrapper;
-  GList *new_periods = NULL;
-  const gchar *data;
-
-  /* ISO/IEC 23009-1:2014 5.5.3 4)
-   * Remove nodes that resolve to nothing when resolving
-   */
-  if (strcmp (period_node->xlink_href,
-          "urn:mpeg:dash:resolve-to-zero:2013") == 0) {
-    return NULL;
-  }
-
-  if (!client->downloader) {
-    return NULL;
-  }
-
-  /* Build absolute URI */
-
-  /* Get base URI at the MPD level */
-  base_uri =
-      gst_uri_from_string (client->
-      mpd_base_uri ? client->mpd_base_uri : client->mpd_uri);
-
-  /* combine a BaseURL at the MPD level with the current base url */
-  base_uri = combine_urls (base_uri, client->mpd_node->BaseURLs, &query, 0);
-  uri = gst_uri_from_string_with_base (base_uri, period_node->xlink_href);
-  if (query)
-    gst_uri_set_query_string (uri, query);
-  g_free (query);
-  uri_string = gst_uri_to_string (uri);
-  gst_uri_unref (base_uri);
-  gst_uri_unref (uri);
-
-  download =
-      gst_uri_downloader_fetch_uri (client->downloader,
-      uri_string, client->mpd_uri, TRUE, FALSE, TRUE, &err);
-  g_free (uri_string);
-
-  if (!download) {
-    GST_ERROR ("Failed to download external Period node at '%s': %s",
-        period_node->xlink_href, err->message);
-    g_clear_error (&err);
-    return NULL;
-  }
-
-  period_buffer = gst_fragment_get_buffer (download);
-  g_object_unref (download);
-
-  /* external xml could have multiple period without root xmlNode.
-   * To avoid xml parsing error caused by no root node, wrapping it with
-   * custom root node */
-  adapter = gst_adapter_new ();
-
-  wrapper = g_new (gchar, strlen (CUSTOM_WRAPPER_START));
-  memcpy (wrapper, CUSTOM_WRAPPER_START, strlen (CUSTOM_WRAPPER_START));
-  gst_adapter_push (adapter,
-      gst_buffer_new_wrapped (wrapper, strlen (CUSTOM_WRAPPER_START)));
-
-  gst_adapter_push (adapter, period_buffer);
-
-  wrapper = g_strdup (CUSTOM_WRAPPER_END);
-  gst_adapter_push (adapter,
-      gst_buffer_new_wrapped (wrapper, strlen (CUSTOM_WRAPPER_END) + 1));
-
-  data = gst_adapter_map (adapter, gst_adapter_available (adapter));
-
-  doc =
-      xmlReadMemory (data, gst_adapter_available (adapter), "noname.xml", NULL,
-      XML_PARSE_NONET);
-
-  gst_adapter_unmap (adapter);
-  gst_adapter_clear (adapter);
-  gst_object_unref (adapter);
-
-  if (doc) {
-    xmlNode *root_element = xmlDocGetRootElement (doc);
-    xmlNode *iter;
-
-    if (root_element->type != XML_ELEMENT_NODE)
-      goto error;
-
-    for (iter = root_element->children; iter; iter = iter->next) {
-      if (iter->type == XML_ELEMENT_NODE) {
-        if (xmlStrcmp (iter->name, (xmlChar *) "Period") == 0) {
-          gst_mpdparser_parse_period_node (&new_periods, iter);
-        } else {
-          goto error;
-        }
-      }
-    }
-  } else {
-    goto error;
-  }
-
-done:
-  if (doc)
-    xmlFreeDoc (doc);
-
-  return new_periods;
-
-error:
-  GST_ERROR ("Failed to parse period node XML");
-
-  if (new_periods) {
-    g_list_free_full (new_periods,
-        (GDestroyNotify) gst_mpdparser_free_period_node);
-    new_periods = NULL;
-  }
-  goto done;
-}
-
-gboolean
-gst_mpd_client_setup_media_presentation (GstMpdClient * client,
-    GstClockTime time, gint period_idx, const gchar * period_id)
-{
-  GstStreamPeriod *stream_period;
-  GstClockTime start, duration;
-  GList *list, *next;
-  guint idx;
-  gboolean ret = FALSE;
-
-  g_return_val_if_fail (client != NULL, FALSE);
-  g_return_val_if_fail (client->mpd_node != NULL, FALSE);
-
-  /* Check if we set up the media presentation far enough already */
-  for (list = client->periods; list; list = list->next) {
-    GstStreamPeriod *stream_period = list->data;
-
-    if ((time != GST_CLOCK_TIME_NONE
-            && stream_period->duration != GST_CLOCK_TIME_NONE
-            && stream_period->start + stream_period->duration >= time)
-        || (time != GST_CLOCK_TIME_NONE && stream_period->start >= time))
-      return TRUE;
-
-    if (period_idx != -1 && stream_period->number >= period_idx)
-      return TRUE;
-
-    if (period_id != NULL && stream_period->period->id != NULL
-        && strcmp (stream_period->period->id, period_id) == 0)
-      return TRUE;
-
-  }
-
-  GST_DEBUG ("Building the list of Periods in the Media Presentation");
-  /* clean the old period list, if any */
-  /* TODO: In theory we could reuse the ones we have so far but that
-   * seems more complicated than the overhead caused here
-   */
-  if (client->periods) {
-    g_list_foreach (client->periods,
-        (GFunc) gst_mpdparser_free_stream_period, NULL);
-    g_list_free (client->periods);
-    client->periods = NULL;
-  }
-
-  idx = 0;
-  start = 0;
-  duration = GST_CLOCK_TIME_NONE;
-
-  if (client->mpd_node->mediaPresentationDuration <= 0 &&
-      client->mpd_node->mediaPresentationDuration != -1) {
-    /* Invalid MPD file: MPD duration is negative or zero */
-    goto syntax_error;
-  }
-
-  for (list = client->mpd_node->Periods; list; /* explicitly advanced below */ ) {
-    GstPeriodNode *period_node = list->data;
-    GstPeriodNode *next_period_node = NULL;
-
-    /* Download external period */
-    if (period_node->xlink_href) {
-      GList *new_periods;
-      GList *prev;
-
-      new_periods = gst_mpd_client_fetch_external_period (client, period_node);
-
-      prev = list->prev;
-      client->mpd_node->Periods =
-          g_list_delete_link (client->mpd_node->Periods, list);
-      gst_mpdparser_free_period_node (period_node);
-      period_node = NULL;
-
-      /* Get new next node, we will insert before this */
-      if (prev)
-        next = prev->next;
-      else
-        next = client->mpd_node->Periods;
-
-      while (new_periods) {
-        client->mpd_node->Periods =
-            g_list_insert_before (client->mpd_node->Periods, next,
-            new_periods->data);
-        new_periods = g_list_delete_link (new_periods, new_periods);
-      }
-      next = NULL;
-
-      /* Update our iterator to the first new period if any, or the next */
-      if (prev)
-        list = prev->next;
-      else
-        list = client->mpd_node->Periods;
-
-      /* And try again */
-      continue;
-    }
-
-    if (period_node->start != -1) {
-      /* we have a regular period */
-      /* start cannot be smaller than previous start */
-      if (list != g_list_first (client->mpd_node->Periods)
-          && start >= period_node->start * GST_MSECOND) {
-        /* Invalid MPD file: duration would be negative or zero */
-        goto syntax_error;
-      }
-      start = period_node->start * GST_MSECOND;
-    } else if (duration != GST_CLOCK_TIME_NONE) {
-      /* start time inferred from previous period, this is still a regular period */
-      start += duration;
-    } else if (idx == 0 && client->mpd_node->type == GST_MPD_FILE_TYPE_STATIC) {
-      /* first period of a static MPD file, start time is 0 */
-      start = 0;
-    } else if (client->mpd_node->type == GST_MPD_FILE_TYPE_DYNAMIC) {
-      /* this should be a live stream, let this pass */
-    } else {
-      /* this is an 'Early Available Period' */
-      goto early;
-    }
-
-    /* compute duration.
-       If there is a start time for the next period, or this is the last period
-       and mediaPresentationDuration was set, those values will take precedence
-       over a configured period duration in computing this period's duration
-
-       ISO/IEC 23009-1:2014(E), chapter 5.3.2.1
-       "The Period extends until the PeriodStart of the next Period, or until
-       the end of the Media Presentation in the case of the last Period."
-     */
-
-    while ((next = g_list_next (list)) != NULL) {
-      /* try to infer this period duration from the start time of the next period */
-      next_period_node = next->data;
-
-      if (next_period_node->xlink_href) {
-        GList *new_periods;
-
-        new_periods =
-            gst_mpd_client_fetch_external_period (client, next_period_node);
-
-        client->mpd_node->Periods =
-            g_list_delete_link (client->mpd_node->Periods, next);
-        gst_mpdparser_free_period_node (next_period_node);
-        next_period_node = NULL;
-        /* Get new next node, we will insert before this */
-        next = g_list_next (list);
-        while (new_periods) {
-          client->mpd_node->Periods =
-              g_list_insert_before (client->mpd_node->Periods, next,
-              new_periods->data);
-          new_periods = g_list_delete_link (new_periods, new_periods);
-        }
-
-        /* And try again, getting the next list element which is now our newly
-         * inserted nodes. If any */
-      } else {
-        /* Got the next period and it doesn't have to be downloaded first */
-        break;
-      }
-    }
-
-    if (next_period_node) {
-      if (next_period_node->start != -1) {
-        if (start >= next_period_node->start * GST_MSECOND) {
-          /* Invalid MPD file: duration would be negative or zero */
-          goto syntax_error;
-        }
-        duration = next_period_node->start * GST_MSECOND - start;
-      } else if (period_node->duration != -1) {
-        if (period_node->duration <= 0) {
-          /* Invalid MPD file: duration would be negative or zero */
-          goto syntax_error;
-        }
-        duration = period_node->duration * GST_MSECOND;
-      } else if (client->mpd_node->type == GST_MPD_FILE_TYPE_DYNAMIC) {
-        /* might be a live file, ignore unspecified duration */
-      } else {
-        /* Invalid MPD file! */
-        goto syntax_error;
-      }
-    } else if (client->mpd_node->mediaPresentationDuration != -1) {
-      /* last Period of the Media Presentation */
-      if (client->mpd_node->mediaPresentationDuration * GST_MSECOND <= start) {
-        /* Invalid MPD file: duration would be negative or zero */
-        goto syntax_error;
-      }
-      duration =
-          client->mpd_node->mediaPresentationDuration * GST_MSECOND - start;
-    } else if (period_node->duration != -1) {
-      duration = period_node->duration * GST_MSECOND;
-    } else if (client->mpd_node->type == GST_MPD_FILE_TYPE_DYNAMIC) {
-      /* might be a live file, ignore unspecified duration */
-    } else {
-      /* Invalid MPD file! */
-      goto syntax_error;
-    }
-
-    stream_period = g_slice_new0 (GstStreamPeriod);
-    client->periods = g_list_append (client->periods, stream_period);
-    stream_period->period = period_node;
-    stream_period->number = idx++;
-    stream_period->start = start;
-    stream_period->duration = duration;
-    ret = TRUE;
-    GST_LOG (" - added Period %d start=%" GST_TIME_FORMAT " duration=%"
-        GST_TIME_FORMAT, idx, GST_TIME_ARGS (start), GST_TIME_ARGS (duration));
-
-    if ((time != GST_CLOCK_TIME_NONE
-            && stream_period->duration != GST_CLOCK_TIME_NONE
-            && stream_period->start + stream_period->duration >= time)
-        || (time != GST_CLOCK_TIME_NONE && stream_period->start >= time))
-      break;
-
-    if (period_idx != -1 && stream_period->number >= period_idx)
-      break;
-
-    if (period_id != NULL && stream_period->period->id != NULL
-        && strcmp (stream_period->period->id, period_id) == 0)
-      break;
-
-    list = list->next;
-  }
-
-  GST_DEBUG
-      ("Found a total of %d valid Periods in the Media Presentation up to this point",
-      idx);
-  return ret;
-
-early:
-  GST_WARNING
-      ("Found an Early Available Period, skipping the rest of the Media Presentation");
-  return ret;
-
-syntax_error:
-  GST_WARNING
-      ("Cannot get the duration of the Period %d, skipping the rest of the Media Presentation",
-      idx);
-  return ret;
-}
-
-static GList *
-gst_mpd_client_fetch_external_adaptation_set (GstMpdClient * client,
-    GstPeriodNode * period, GstAdaptationSetNode * adapt_set)
-{
-  GstFragment *download;
-  GstBuffer *adapt_set_buffer;
-  GstMapInfo map;
-  GError *err = NULL;
-  xmlDocPtr doc = NULL;
-  GstUri *base_uri, *uri;
-  gchar *query = NULL;
-  gchar *uri_string;
-  GList *new_adapt_sets = NULL;
-
-  /* ISO/IEC 23009-1:2014 5.5.3 4)
-   * Remove nodes that resolve to nothing when resolving
-   */
-  if (strcmp (adapt_set->xlink_href, "urn:mpeg:dash:resolve-to-zero:2013") == 0) {
-    return NULL;
-  }
-
-  if (!client->downloader) {
-    return NULL;
-  }
-
-  /* Build absolute URI */
-
-  /* Get base URI at the MPD level */
-  base_uri =
-      gst_uri_from_string (client->
-      mpd_base_uri ? client->mpd_base_uri : client->mpd_uri);
-
-  /* combine a BaseURL at the MPD level with the current base url */
-  base_uri = combine_urls (base_uri, client->mpd_node->BaseURLs, &query, 0);
-
-  /* combine a BaseURL at the Period level with the current base url */
-  base_uri = combine_urls (base_uri, period->BaseURLs, &query, 0);
-
-  uri = gst_uri_from_string_with_base (base_uri, adapt_set->xlink_href);
-  if (query)
-    gst_uri_set_query_string (uri, query);
-  g_free (query);
-  uri_string = gst_uri_to_string (uri);
-  gst_uri_unref (base_uri);
-  gst_uri_unref (uri);
-
-  download =
-      gst_uri_downloader_fetch_uri (client->downloader,
-      uri_string, client->mpd_uri, TRUE, FALSE, TRUE, &err);
-  g_free (uri_string);
-
-  if (!download) {
-    GST_ERROR ("Failed to download external AdaptationSet node at '%s': %s",
-        adapt_set->xlink_href, err->message);
-    g_clear_error (&err);
-    return NULL;
-  }
-
-  adapt_set_buffer = gst_fragment_get_buffer (download);
-  g_object_unref (download);
-
-  gst_buffer_map (adapt_set_buffer, &map, GST_MAP_READ);
-
-  doc =
-      xmlReadMemory ((const gchar *) map.data, map.size, "noname.xml", NULL,
-      XML_PARSE_NONET);
-
-  gst_buffer_unmap (adapt_set_buffer, &map);
-  gst_buffer_unref (adapt_set_buffer);
-
-  /* NOTE: ISO/IEC 23009-1:2014 5.3.3.2 is saying that exactly one AdaptationSet
-   * in external xml is allowed */
-  if (doc) {
-    xmlNode *root_element = xmlDocGetRootElement (doc);
-
-    if (root_element->type != XML_ELEMENT_NODE ||
-        xmlStrcmp (root_element->name, (xmlChar *) "AdaptationSet") != 0) {
-      goto error;
-    }
-
-    gst_mpdparser_parse_adaptation_set_node (&new_adapt_sets, root_element,
-        period);
-  } else {
-    goto error;
-  }
-
-done:
-  if (doc)
-    xmlFreeDoc (doc);
-
-  return new_adapt_sets;
-
-error:
-  GST_ERROR ("Failed to parse adaptation set node XML");
-  goto done;
-}
-
-static GList *
-gst_mpd_client_get_adaptation_sets_for_period (GstMpdClient * client,
-    GstStreamPeriod * period)
-{
-  GList *list;
-
-  g_return_val_if_fail (period != NULL, NULL);
-
-  /* Resolve all external adaptation sets of this period. Every user of
-   * the adaptation sets would need to know the content of all adaptation sets
-   * to decide which one to use, so we have to resolve them all here
-   */
-  for (list = period->period->AdaptationSets; list;
-      /* advanced explicitly below */ ) {
-    GstAdaptationSetNode *adapt_set = (GstAdaptationSetNode *) list->data;
-    GList *new_adapt_sets = NULL, *prev, *next;
-
-    if (!adapt_set->xlink_href) {
-      list = list->next;
-      continue;
-    }
-
-    new_adapt_sets =
-        gst_mpd_client_fetch_external_adaptation_set (client, period->period,
-        adapt_set);
-
-    prev = list->prev;
-    period->period->AdaptationSets =
-        g_list_delete_link (period->period->AdaptationSets, list);
-    gst_mpdparser_free_adaptation_set_node (adapt_set);
-    adapt_set = NULL;
-
-    /* Get new next node, we will insert before this */
-    if (prev)
-      next = prev->next;
-    else
-      next = period->period->AdaptationSets;
-
-    while (new_adapt_sets) {
-      period->period->AdaptationSets =
-          g_list_insert_before (period->period->AdaptationSets, next,
-          new_adapt_sets->data);
-      new_adapt_sets = g_list_delete_link (new_adapt_sets, new_adapt_sets);
-    }
-
-    /* Update our iterator to the first new adaptation set if any, or the next */
-    if (prev)
-      list = prev->next;
-    else
-      list = period->period->AdaptationSets;
-  }
-
-  return period->period->AdaptationSets;
-}
-
-GList *
-gst_mpd_client_get_adaptation_sets (GstMpdClient * client)
-{
-  GstStreamPeriod *stream_period;
-
-  stream_period = gst_mpdparser_get_stream_period (client);
-  if (stream_period == NULL || stream_period->period == NULL) {
-    GST_DEBUG ("No more Period nodes in the MPD file, terminating...");
-    return NULL;
-  }
-
-  return gst_mpd_client_get_adaptation_sets_for_period (client, stream_period);
-}
-
-gboolean
-gst_mpd_client_setup_streaming (GstMpdClient * client,
-    GstAdaptationSetNode * adapt_set)
-{
-  GstRepresentationNode *representation;
-  GList *rep_list = NULL;
-  GstActiveStream *stream;
-
-  rep_list = adapt_set->Representations;
-  if (!rep_list) {
-    GST_WARNING ("Can not retrieve any representation, aborting...");
-    return FALSE;
-  }
-
-  stream = g_slice_new0 (GstActiveStream);
-  gst_mpdparser_init_active_stream_segments (stream);
-
-  stream->baseURL_idx = 0;
-  stream->cur_adapt_set = adapt_set;
-
-  GST_DEBUG ("0. Current stream %p", stream);
-
-#if 0
-  /* fast start */
-  representation =
-      gst_mpdparser_get_representation_with_max_bandwidth (rep_list,
-      stream->max_bandwidth);
-
-  if (!representation) {
-    GST_WARNING
-        ("Can not retrieve a representation with the requested bandwidth");
-    representation = gst_mpdparser_get_lowest_representation (rep_list);
-  }
-#else
-  /* slow start */
-  representation = gst_mpdparser_get_lowest_representation (rep_list);
-#endif
-
-  if (!representation) {
-    GST_WARNING ("No valid representation in the MPD file, aborting...");
-    gst_mpdparser_free_active_stream (stream);
-    return FALSE;
-  }
-  stream->mimeType =
-      gst_mpdparser_representation_get_mimetype (adapt_set, representation);
-  if (stream->mimeType == GST_STREAM_UNKNOWN) {
-    GST_WARNING ("Unknown mime type in the representation, aborting...");
-    gst_mpdparser_free_active_stream (stream);
-    return FALSE;
-  }
-
-  client->active_streams = g_list_append (client->active_streams, stream);
-  if (!gst_mpd_client_setup_representation (client, stream, representation)) {
-    GST_WARNING ("Failed to setup the representation, aborting...");
-    return FALSE;
-  }
-
-  GST_INFO ("Successfully setup the download pipeline for mimeType %d",
-      stream->mimeType);
-
-  return TRUE;
-}
-
-gboolean
-gst_mpd_client_stream_seek (GstMpdClient * client, GstActiveStream * stream,
-    gboolean forward, GstSeekFlags flags, GstClockTime ts,
-    GstClockTime * final_ts)
-{
-  gint index = 0;
-  gint repeat_index = 0;
-  GstMediaSegment *selectedChunk = NULL;
-
-  g_return_val_if_fail (stream != NULL, 0);
-
-  if (stream->segments) {
-    for (index = 0; index < stream->segments->len; index++) {
-      gboolean in_segment = FALSE;
-      GstMediaSegment *segment = g_ptr_array_index (stream->segments, index);
-      GstClockTime end_time;
-
-      GST_DEBUG ("Looking at fragment sequence chunk %d / %d", index,
-          stream->segments->len);
-
-      end_time =
-          gst_mpdparser_get_segment_end_time (client, stream->segments,
-          segment, index);
-
-      /* avoid downloading another fragment just for 1ns in reverse mode */
-      if (forward)
-        in_segment = ts < end_time;
-      else
-        in_segment = ts <= end_time;
-
-      if (in_segment) {
-        GstClockTime chunk_time;
-
-        selectedChunk = segment;
-        repeat_index = (ts - segment->start) / segment->duration;
-
-        chunk_time = segment->start + segment->duration * repeat_index;
-
-        /* At the end of a segment in reverse mode, start from the previous fragment */
-        if (!forward && repeat_index > 0
-            && ((ts - segment->start) % segment->duration == 0))
-          repeat_index--;
-
-        if ((flags & GST_SEEK_FLAG_SNAP_NEAREST) == GST_SEEK_FLAG_SNAP_NEAREST) {
-          if (repeat_index + 1 < segment->repeat) {
-            if (ts - chunk_time > chunk_time + segment->duration - ts)
-              repeat_index++;
-          } else if (index + 1 < stream->segments->len) {
-            GstMediaSegment *next_segment =
-                g_ptr_array_index (stream->segments, index + 1);
-
-            if (ts - chunk_time > next_segment->start - ts) {
-              repeat_index = 0;
-              selectedChunk = next_segment;
-              index++;
-            }
-          }
-        } else if (((forward && flags & GST_SEEK_FLAG_SNAP_AFTER) ||
-                (!forward && flags & GST_SEEK_FLAG_SNAP_BEFORE)) &&
-            ts != chunk_time) {
-
-          if (repeat_index + 1 < segment->repeat) {
-            repeat_index++;
-          } else {
-            repeat_index = 0;
-            if (index + 1 >= stream->segments->len) {
-              selectedChunk = NULL;
-            } else {
-              selectedChunk = g_ptr_array_index (stream->segments, ++index);
-            }
-          }
-        }
-        break;
-      }
-    }
-
-    if (selectedChunk == NULL) {
-      stream->segment_index = stream->segments->len;
-      stream->segment_repeat_index = 0;
-      GST_DEBUG ("Seek to after last segment");
-      return FALSE;
-    }
-
-    if (final_ts)
-      *final_ts = selectedChunk->start + selectedChunk->duration * repeat_index;
-  } else {
-    GstClockTime duration =
-        gst_mpd_client_get_segment_duration (client, stream, NULL);
-    GstStreamPeriod *stream_period = gst_mpdparser_get_stream_period (client);
-    guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
-    GstClockTime index_time;
-
-    g_return_val_if_fail (stream->cur_seg_template->
-        MultSegBaseType->SegmentTimeline == NULL, FALSE);
-    if (!GST_CLOCK_TIME_IS_VALID (duration)) {
-      return FALSE;
-    }
-
-    if (ts > stream_period->start)
-      ts -= stream_period->start;
-    else
-      ts = 0;
-
-    index = ts / duration;
-
-    /* At the end of a segment in reverse mode, start from the previous fragment */
-    if (!forward && index > 0 && ts % duration == 0)
-      index--;
-
-    index_time = index * duration;
-
-    if ((flags & GST_SEEK_FLAG_SNAP_NEAREST) == GST_SEEK_FLAG_SNAP_NEAREST) {
-      if (ts - index_time > index_time + duration - ts)
-        index++;
-    } else if (((forward && flags & GST_SEEK_FLAG_SNAP_AFTER) ||
-            (!forward && flags & GST_SEEK_FLAG_SNAP_BEFORE))
-        && ts != index_time) {
-      index++;
-    }
-
-    if (segments_count > 0 && index >= segments_count) {
-      stream->segment_index = segments_count;
-      stream->segment_repeat_index = 0;
-      GST_DEBUG ("Seek to after last segment");
-      return FALSE;
-    }
-    if (final_ts)
-      *final_ts = index * duration;
-  }
-
-  stream->segment_repeat_index = repeat_index;
-  stream->segment_index = index;
-
-  return TRUE;
-}
-
-gint64
-gst_mpd_client_calculate_time_difference (const GstDateTime * t1,
-    const GstDateTime * t2)
-{
-  GDateTime *gdt1, *gdt2;
-  GTimeSpan diff;
-
-  g_assert (t1 != NULL && t2 != NULL);
-  gdt1 = gst_date_time_to_g_date_time ((GstDateTime *) t1);
-  gdt2 = gst_date_time_to_g_date_time ((GstDateTime *) t2);
-  diff = g_date_time_difference (gdt2, gdt1);
-  g_date_time_unref (gdt1);
-  g_date_time_unref (gdt2);
-  return diff * GST_USECOND;
+      xmlFree (subrep_node->dependencyLevel);
+    g_strfreev (subrep_node->contentComponent);
+    g_slice_free (GstSubRepresentationNode, subrep_node);
+  }
 }
 
-GstDateTime *
-gst_mpd_client_add_time_difference (GstDateTime * t1, gint64 usecs)
+static void
+gst_mpdparser_free_s_node (GstSNode * s_node)
 {
-  GDateTime *gdt;
-  GDateTime *gdt2;
-  GstDateTime *rv;
-
-  g_assert (t1 != NULL);
-  gdt = gst_date_time_to_g_date_time (t1);
-  g_assert (gdt != NULL);
-  gdt2 = g_date_time_add (gdt, usecs);
-  g_assert (gdt2 != NULL);
-  g_date_time_unref (gdt);
-  rv = gst_date_time_new_from_g_date_time (gdt2);
-
-  /* Don't g_date_time_unref(gdt2) because gst_date_time_new_from_g_date_time takes
-   * ownership of the GDateTime pointer.
-   */
-
-  return rv;
+  if (s_node) {
+    g_slice_free (GstSNode, s_node);
+  }
 }
 
-static GstDateTime *
-gst_mpd_client_get_availability_start_time (GstMpdClient * client)
+static GstSegmentTimelineNode *
+gst_mpdparser_segment_timeline_node_new (void)
 {
-  GstDateTime *start_time;
+  GstSegmentTimelineNode *node = g_slice_new0 (GstSegmentTimelineNode);
 
-  if (client == NULL)
-    return (GstDateTime *) NULL;
+  g_queue_init (&node->S);
 
-  start_time = client->mpd_node->availabilityStartTime;
-  if (start_time)
-    gst_date_time_ref (start_time);
-  return start_time;
+  return node;
 }
 
-gboolean
-gst_mpd_client_get_last_fragment_timestamp_end (GstMpdClient * client,
-    guint stream_idx, GstClockTime * ts)
+static void
+gst_mpdparser_free_segment_timeline_node (GstSegmentTimelineNode * seg_timeline)
 {
-  GstActiveStream *stream;
-  gint segment_idx;
-  GstMediaSegment *currentChunk;
-  GstStreamPeriod *stream_period;
-
-  GST_DEBUG ("Stream index: %i", stream_idx);
-  stream = g_list_nth_data (client->active_streams, stream_idx);
-  g_return_val_if_fail (stream != NULL, 0);
-
-  if (!stream->segments) {
-    stream_period = gst_mpdparser_get_stream_period (client);
-    *ts = stream_period->start + stream_period->duration;
-  } else {
-    segment_idx = gst_mpd_client_get_segments_counts (client, stream) - 1;
-    if (segment_idx >= stream->segments->len) {
-      GST_WARNING ("Segment index %d is outside of segment list of length %d",
-          segment_idx, stream->segments->len);
-      return FALSE;
-    }
-    currentChunk = g_ptr_array_index (stream->segments, segment_idx);
-
-    if (currentChunk->repeat >= 0) {
-      *ts =
-          currentChunk->start + (currentChunk->duration * (1 +
-              currentChunk->repeat));
-    } else {
-      /* 5.3.9.6.1: negative repeat means repeat till the end of the
-       * period, or the next update of the MPD (which I think is
-       * implicit, as this will all get deleted/recreated), or the
-       * start of the next segment, if any. */
-      stream_period = gst_mpdparser_get_stream_period (client);
-      *ts = stream_period->start + stream_period->duration;
-    }
+  if (seg_timeline) {
+    g_queue_foreach (&seg_timeline->S, (GFunc) gst_mpdparser_free_s_node, NULL);
+    g_queue_clear (&seg_timeline->S);
+    g_slice_free (GstSegmentTimelineNode, seg_timeline);
   }
-
-  return TRUE;
 }
 
-gboolean
-gst_mpd_client_get_next_fragment_timestamp (GstMpdClient * client,
-    guint stream_idx, GstClockTime * ts)
+static void
+gst_mpdparser_free_url_type_node (GstURLType * url_type_node)
 {
-  GstActiveStream *stream;
-  GstMediaSegment *currentChunk;
-
-  GST_DEBUG ("Stream index: %i", stream_idx);
-  stream = g_list_nth_data (client->active_streams, stream_idx);
-  g_return_val_if_fail (stream != NULL, 0);
-
-  if (stream->segments) {
-    GST_DEBUG ("Looking for fragment sequence chunk %d / %d",
-        stream->segment_index, stream->segments->len);
-    if (stream->segment_index >= stream->segments->len)
-      return FALSE;
-    currentChunk = g_ptr_array_index (stream->segments, stream->segment_index);
-
-    *ts =
-        currentChunk->start +
-        (currentChunk->duration * stream->segment_repeat_index);
-  } else {
-    GstClockTime duration =
-        gst_mpd_client_get_segment_duration (client, stream, NULL);
-    guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
-
-    g_return_val_if_fail (stream->cur_seg_template->
-        MultSegBaseType->SegmentTimeline == NULL, FALSE);
-    if (!GST_CLOCK_TIME_IS_VALID (duration) || (segments_count > 0
-            && stream->segment_index >= segments_count)) {
-      return FALSE;
-    }
-    *ts = stream->segment_index * duration;
+  if (url_type_node) {
+    if (url_type_node->sourceURL)
+      xmlFree (url_type_node->sourceURL);
+    g_slice_free (GstXMLRange, url_type_node->range);
+    g_slice_free (GstURLType, url_type_node);
   }
-
-  return TRUE;
 }
 
-GstClockTime
-gst_mpd_parser_get_stream_presentation_offset (GstMpdClient * client,
-    guint stream_idx)
+static void
+gst_mpdparser_free_seg_base_type_ext (GstSegmentBaseType * seg_base_type)
 {
-  GstActiveStream *stream = NULL;
-
-  g_return_val_if_fail (client != NULL, 0);
-  g_return_val_if_fail (client->active_streams != NULL, 0);
-  stream = g_list_nth_data (client->active_streams, stream_idx);
-  g_return_val_if_fail (stream != NULL, 0);
-
-  return stream->presentationTimeOffset;
+  if (seg_base_type) {
+    if (seg_base_type->indexRange)
+      g_slice_free (GstXMLRange, seg_base_type->indexRange);
+    gst_mpdparser_free_url_type_node (seg_base_type->Initialization);
+    gst_mpdparser_free_url_type_node (seg_base_type->RepresentationIndex);
+    g_slice_free (GstSegmentBaseType, seg_base_type);
+  }
 }
 
-GstClockTime
-gst_mpd_parser_get_period_start_time (GstMpdClient * client)
+static void
+gst_mpdparser_free_mult_seg_base_type_ext (GstMultSegmentBaseType *
+    mult_seg_base_type)
 {
-  GstStreamPeriod *stream_period = NULL;
-
-  g_return_val_if_fail (client != NULL, 0);
-  stream_period = gst_mpdparser_get_stream_period (client);
-  g_return_val_if_fail (stream_period != NULL, 0);
-
-  return stream_period->start;
+  if (mult_seg_base_type) {
+    /* SegmentBaseType extension */
+    gst_mpdparser_free_seg_base_type_ext (mult_seg_base_type->SegBaseType);
+    gst_mpdparser_free_segment_timeline_node
+        (mult_seg_base_type->SegmentTimeline);
+    gst_mpdparser_free_url_type_node (mult_seg_base_type->BitstreamSwitching);
+    g_slice_free (GstMultSegmentBaseType, mult_seg_base_type);
+  }
 }
 
-/**
- * gst_mpd_client_get_utc_timing_sources:
- * @client: #GstMpdClient to check for UTCTiming elements
- * @methods: A bit mask of #GstMPDUTCTimingType that specifies the methods
- *     to search for.
- * @selected_method: (nullable): The selected method
- * Returns: (transfer none): A NULL terminated array of URLs of servers
- *     that use @selected_method to provide a realtime clock.
- *
- * Searches the UTCTiming elements found in the manifest for an element
- * that uses one of the UTC timing methods specified in @selected_method.
- * If multiple UTCTiming elements are present that support one of the
- * methods specified in @selected_method, the first one is returned.
- *
- * Since: 1.6
- */
-gchar **
-gst_mpd_client_get_utc_timing_sources (GstMpdClient * client,
-    guint methods, GstMPDUTCTimingType * selected_method)
+static void
+gst_mpdparser_free_segment_url_node (GstSegmentURLNode * segment_url)
 {
-  GList *list;
-
-  g_return_val_if_fail (client != NULL, NULL);
-  g_return_val_if_fail (client->mpd_node != NULL, NULL);
-  for (list = g_list_first (client->mpd_node->UTCTiming); list;
-      list = g_list_next (list)) {
-    const GstUTCTimingNode *node = (const GstUTCTimingNode *) list->data;
-    if (node->method & methods) {
-      if (selected_method) {
-        *selected_method = node->method;
-      }
-      return node->urls;
-    }
+  if (segment_url) {
+    if (segment_url->media)
+      xmlFree (segment_url->media);
+    g_slice_free (GstXMLRange, segment_url->mediaRange);
+    if (segment_url->index)
+      xmlFree (segment_url->index);
+    g_slice_free (GstXMLRange, segment_url->indexRange);
+    g_slice_free (GstSegmentURLNode, segment_url);
   }
-  return NULL;
 }
 
-gboolean
-gst_mpd_client_get_next_fragment (GstMpdClient * client,
-    guint indexStream, GstMediaFragmentInfo * fragment)
+static void
+gst_mpdparser_free_descriptor_type_node (GstDescriptorType * descriptor_type)
 {
-  GstActiveStream *stream = NULL;
-  GstMediaSegment *currentChunk;
-  gchar *mediaURL = NULL;
-  gchar *indexURL = NULL;
-  GstUri *base_url, *frag_url;
-
-  /* select stream */
-  g_return_val_if_fail (client != NULL, FALSE);
-  g_return_val_if_fail (client->active_streams != NULL, FALSE);
-  stream = g_list_nth_data (client->active_streams, indexStream);
-  g_return_val_if_fail (stream != NULL, FALSE);
-  g_return_val_if_fail (stream->cur_representation != NULL, FALSE);
-
-  if (stream->segments) {
-    GST_DEBUG ("Looking for fragment sequence chunk %d / %d",
-        stream->segment_index, stream->segments->len);
-    if (stream->segment_index >= stream->segments->len)
-      return FALSE;
-  } else {
-    GstClockTime duration = gst_mpd_client_get_segment_duration (client,
-        stream, NULL);
-    guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
-
-    g_return_val_if_fail (stream->cur_seg_template->
-        MultSegBaseType->SegmentTimeline == NULL, FALSE);
-    if (!GST_CLOCK_TIME_IS_VALID (duration) || (segments_count > 0
-            && stream->segment_index >= segments_count)) {
-      return FALSE;
-    }
-    fragment->duration = duration;
-  }
-
-  /* FIXME rework discont checking */
-  /* fragment->discontinuity = segment_idx != currentChunk.number; */
-  fragment->range_start = 0;
-  fragment->range_end = -1;
-  fragment->index_uri = NULL;
-  fragment->index_range_start = 0;
-  fragment->index_range_end = -1;
-
-  if (stream->segments) {
-    currentChunk = g_ptr_array_index (stream->segments, stream->segment_index);
-
-    GST_DEBUG ("currentChunk->SegmentURL = %p", currentChunk->SegmentURL);
-    if (currentChunk->SegmentURL != NULL) {
-      mediaURL =
-          g_strdup (gst_mpdparser_get_mediaURL (stream,
-              currentChunk->SegmentURL));
-      indexURL = g_strdup (currentChunk->SegmentURL->index);
-    } else if (stream->cur_seg_template != NULL) {
-      mediaURL =
-          gst_mpdparser_build_URL_from_template (stream->
-          cur_seg_template->media, stream->cur_representation->id,
-          currentChunk->number + stream->segment_repeat_index,
-          stream->cur_representation->bandwidth,
-          currentChunk->scale_start +
-          stream->segment_repeat_index * currentChunk->scale_duration);
-      if (stream->cur_seg_template->index) {
-        indexURL =
-            gst_mpdparser_build_URL_from_template (stream->
-            cur_seg_template->index, stream->cur_representation->id,
-            currentChunk->number + stream->segment_repeat_index,
-            stream->cur_representation->bandwidth,
-            currentChunk->scale_start +
-            stream->segment_repeat_index * currentChunk->scale_duration);
-      }
-    }
-    GST_DEBUG ("mediaURL = %s", mediaURL);
-    GST_DEBUG ("indexURL = %s", indexURL);
-
-    fragment->timestamp =
-        currentChunk->start +
-        stream->segment_repeat_index * currentChunk->duration;
-    fragment->duration = currentChunk->duration;
-    if (currentChunk->SegmentURL) {
-      if (currentChunk->SegmentURL->mediaRange) {
-        fragment->range_start =
-            currentChunk->SegmentURL->mediaRange->first_byte_pos;
-        fragment->range_end =
-            currentChunk->SegmentURL->mediaRange->last_byte_pos;
-      }
-      if (currentChunk->SegmentURL->indexRange) {
-        fragment->index_range_start =
-            currentChunk->SegmentURL->indexRange->first_byte_pos;
-        fragment->index_range_end =
-            currentChunk->SegmentURL->indexRange->last_byte_pos;
-      }
-    }
-  } else {
-    if (stream->cur_seg_template != NULL) {
-      mediaURL =
-          gst_mpdparser_build_URL_from_template (stream->
-          cur_seg_template->media, stream->cur_representation->id,
-          stream->segment_index +
-          stream->cur_seg_template->MultSegBaseType->startNumber,
-          stream->cur_representation->bandwidth,
-          stream->segment_index * fragment->duration);
-      if (stream->cur_seg_template->index) {
-        indexURL =
-            gst_mpdparser_build_URL_from_template (stream->
-            cur_seg_template->index, stream->cur_representation->id,
-            stream->segment_index +
-            stream->cur_seg_template->MultSegBaseType->startNumber,
-            stream->cur_representation->bandwidth,
-            stream->segment_index * fragment->duration);
-      }
-    } else {
-      return FALSE;
-    }
-
-    GST_DEBUG ("mediaURL = %s", mediaURL);
-    GST_DEBUG ("indexURL = %s", indexURL);
-
-    fragment->timestamp = stream->segment_index * fragment->duration;
+  if (descriptor_type) {
+    if (descriptor_type->schemeIdUri)
+      xmlFree (descriptor_type->schemeIdUri);
+    if (descriptor_type->value)
+      xmlFree (descriptor_type->value);
+    g_slice_free (GstDescriptorType, descriptor_type);
   }
+}
 
-  base_url = gst_uri_from_string (stream->baseURL);
-  frag_url = gst_uri_from_string_with_base (base_url, mediaURL);
-  g_free (mediaURL);
-  if (stream->queryURL) {
-    frag_url = gst_uri_make_writable (frag_url);
-    gst_uri_set_query_string (frag_url, stream->queryURL);
-  }
-  fragment->uri = gst_uri_to_string (frag_url);
-  gst_uri_unref (frag_url);
-
-  if (indexURL != NULL) {
-    frag_url = gst_uri_make_writable (gst_uri_from_string_with_base (base_url,
-            indexURL));
-    gst_uri_set_query_string (frag_url, stream->queryURL);
-    fragment->index_uri = gst_uri_to_string (frag_url);
-    gst_uri_unref (frag_url);
-    g_free (indexURL);
-  } else if (indexURL == NULL && (fragment->index_range_start
-          || fragment->index_range_end != -1)) {
-    /* index has no specific URL but has a range, we should only use this if
-     * the media also has a range, otherwise we are serving some data twice
-     * (in the media fragment and again in the index) */
-    if (!(fragment->range_start || fragment->range_end != -1)) {
-      GST_WARNING ("Ignoring index ranges because there isn't a media range "
-          "and URIs would be the same");
-      /* removing index information */
-      fragment->index_range_start = 0;
-      fragment->index_range_end = -1;
-    }
+static void
+gst_mpdparser_free_content_component_node (GstContentComponentNode *
+    content_component_node)
+{
+  if (content_component_node) {
+    if (content_component_node->lang)
+      xmlFree (content_component_node->lang);
+    if (content_component_node->contentType)
+      xmlFree (content_component_node->contentType);
+    g_slice_free (GstXMLRatio, content_component_node->par);
+    g_list_free_full (content_component_node->Accessibility,
+        (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
+    g_list_free_full (content_component_node->Role,
+        (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
+    g_list_free_full (content_component_node->Rating,
+        (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
+    g_list_free_full (content_component_node->Viewpoint,
+        (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
+    g_slice_free (GstContentComponentNode, content_component_node);
   }
-
-  gst_uri_unref (base_url);
-
-  GST_DEBUG ("Loading chunk with URL %s", fragment->uri);
-
-  return TRUE;
 }
 
-gboolean
-gst_mpd_client_has_next_segment (GstMpdClient * client,
-    GstActiveStream * stream, gboolean forward)
+static void
+gst_mpdparser_free_utctiming_node (GstUTCTimingNode * timing_type)
 {
-  if (forward) {
-    guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
-
-    if (segments_count > 0 && stream->segments
-        && stream->segment_index + 1 == segments_count) {
-      GstMediaSegment *segment;
-
-      segment = g_ptr_array_index (stream->segments, stream->segment_index);
-      if (segment->repeat >= 0
-          && stream->segment_repeat_index >= segment->repeat)
-        return FALSE;
-    } else if (segments_count > 0
-        && stream->segment_index + 1 >= segments_count) {
-      return FALSE;
-    }
-  } else {
-    if (stream->segment_index < 0)
-      return FALSE;
+  if (timing_type) {
+    if (timing_type->urls)
+      g_strfreev (timing_type->urls);
+    g_slice_free (GstUTCTimingNode, timing_type);
   }
-
-  return TRUE;
 }
 
-GstFlowReturn
-gst_mpd_client_advance_segment (GstMpdClient * client, GstActiveStream * stream,
-    gboolean forward)
+/* ISO/IEC 23009-1:2004 5.3.9.4.4 */
+static gboolean
+validate_format (const gchar * format)
 {
-  GstMediaSegment *segment;
-  GstFlowReturn ret = GST_FLOW_OK;
-  guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
-
-  GST_DEBUG ("Advancing segment. Current: %d / %d r:%d", stream->segment_index,
-      segments_count, stream->segment_repeat_index);
-
-  /* handle special cases first */
-  if (forward) {
-    if (segments_count > 0 && stream->segment_index >= segments_count) {
-      ret = GST_FLOW_EOS;
-      goto done;
-    }
-
-    if (stream->segments == NULL) {
-      if (stream->segment_index < 0) {
-        stream->segment_index = 0;
-      } else {
-        stream->segment_index++;
-        if (segments_count > 0 && stream->segment_index >= segments_count) {
-          ret = GST_FLOW_EOS;
-        }
-      }
-      goto done;
-    }
+  const gchar *p = format;
 
-    /* special case for when playback direction is reverted right at *
-     * the end of the segment list */
-    if (stream->segment_index < 0) {
-      stream->segment_index = 0;
-      goto done;
-    }
-  } else {
-    if (stream->segments == NULL)
-      stream->segment_index--;
-    if (stream->segment_index < 0) {
-      stream->segment_index = -1;
-      ret = GST_FLOW_EOS;
-      goto done;
-    }
-    if (stream->segments == NULL)
-      goto done;
-
-    /* special case for when playback direction is reverted right at *
-     * the end of the segment list */
-    if (stream->segment_index >= segments_count) {
-      stream->segment_index = segments_count - 1;
-      segment = g_ptr_array_index (stream->segments, stream->segment_index);
-      if (segment->repeat >= 0) {
-        stream->segment_repeat_index = segment->repeat;
-      } else {
-        GstClockTime start = segment->start;
-        GstClockTime end =
-            gst_mpdparser_get_segment_end_time (client, stream->segments,
-            segment,
-            stream->segment_index);
-        stream->segment_repeat_index =
-            (guint) (end - start) / segment->duration;
-      }
-      goto done;
-    }
-  }
+  /* Check if it starts with % */
+  if (!p || p[0] != '%')
+    return FALSE;
+  p++;
 
-  /* for the normal cases we can get the segment safely here */
-  segment = g_ptr_array_index (stream->segments, stream->segment_index);
-  if (forward) {
-    if (segment->repeat >= 0 && stream->segment_repeat_index >= segment->repeat) {
-      stream->segment_repeat_index = 0;
-      stream->segment_index++;
-      if (segments_count > 0 && stream->segment_index >= segments_count) {
-        ret = GST_FLOW_EOS;
-        goto done;
-      }
-    } else {
-      stream->segment_repeat_index++;
-    }
-  } else {
-    if (stream->segment_repeat_index == 0) {
-      stream->segment_index--;
-      if (stream->segment_index < 0) {
-        ret = GST_FLOW_EOS;
-        goto done;
-      }
+  /* the spec mandates a format like %0[width]d */
+  /* Following the %, we must have a 0 */
+  if (p[0] != '0')
+    return FALSE;
 
-      segment = g_ptr_array_index (stream->segments, stream->segment_index);
-      /* negative repeats only seem to make sense at the end of a list,
-       * so this one will probably not be. Needs some sanity checking
-       * when loading the XML data. */
-      if (segment->repeat >= 0) {
-        stream->segment_repeat_index = segment->repeat;
-      } else {
-        GstClockTime start = segment->start;
-        GstClockTime end =
-            gst_mpdparser_get_segment_end_time (client, stream->segments,
-            segment,
-            stream->segment_index);
-        stream->segment_repeat_index =
-            (guint) (end - start) / segment->duration;
-      }
-    } else {
-      stream->segment_repeat_index--;
-    }
-  }
+  /* Following the % must be a number starting with 0
+   */
+  while (g_ascii_isdigit (*p))
+    p++;
 
-done:
-  GST_DEBUG ("Advanced to segment: %d / %d r:%d (ret: %s)",
-      stream->segment_index, segments_count,
-      stream->segment_repeat_index, gst_flow_get_name (ret));
-  return ret;
-}
+  /* After any 0 and alphanumeric values, there must be a d.
+   */
+  if (p[0] != 'd')
+    return FALSE;
+  p++;
 
-gboolean
-gst_mpd_client_get_next_header (GstMpdClient * client, gchar ** uri,
-    guint stream_idx, gint64 * range_start, gint64 * range_end)
-{
-  GstActiveStream *stream;
-  GstStreamPeriod *stream_period;
-
-  stream = gst_mpdparser_get_active_stream_by_index (client, stream_idx);
-  g_return_val_if_fail (stream != NULL, FALSE);
-  g_return_val_if_fail (stream->cur_representation != NULL, FALSE);
-  stream_period = gst_mpdparser_get_stream_period (client);
-  g_return_val_if_fail (stream_period != NULL, FALSE);
-  g_return_val_if_fail (stream_period->period != NULL, FALSE);
-
-  *range_start = 0;
-  *range_end = -1;
-
-  GST_DEBUG ("Looking for current representation header");
-  *uri = NULL;
-  if (stream->cur_segment_base) {
-    if (stream->cur_segment_base->Initialization) {
-      *uri =
-          g_strdup (gst_mpdparser_get_initializationURL (stream,
-              stream->cur_segment_base->Initialization));
-      if (stream->cur_segment_base->Initialization->range) {
-        *range_start =
-            stream->cur_segment_base->Initialization->range->first_byte_pos;
-        *range_end =
-            stream->cur_segment_base->Initialization->range->last_byte_pos;
-      }
-    } else if (stream->cur_segment_base->indexRange) {
-      *uri =
-          g_strdup (gst_mpdparser_get_initializationURL (stream,
-              stream->cur_segment_base->Initialization));
-      *range_start = 0;
-      *range_end = stream->cur_segment_base->indexRange->first_byte_pos - 1;
-    }
-  } else if (stream->cur_seg_template
-      && stream->cur_seg_template->initialization) {
-    *uri =
-        gst_mpdparser_build_URL_from_template (stream->
-        cur_seg_template->initialization, stream->cur_representation->id, 0,
-        stream->cur_representation->bandwidth, 0);
-  }
+  /* And then potentially more characters without any
+   * further %, even if the spec does not mention this
+   */
+  p = strchr (p, '%');
+  if (p)
+    return FALSE;
 
-  return *uri == NULL ? FALSE : TRUE;
+  return TRUE;
 }
 
-gboolean
-gst_mpd_client_get_next_header_index (GstMpdClient * client, gchar ** uri,
-    guint stream_idx, gint64 * range_start, gint64 * range_end)
+static gchar *
+promote_format_to_uint64 (const gchar * format)
 {
-  GstActiveStream *stream;
-  GstStreamPeriod *stream_period;
-
-  stream = gst_mpdparser_get_active_stream_by_index (client, stream_idx);
-  g_return_val_if_fail (stream != NULL, FALSE);
-  g_return_val_if_fail (stream->cur_representation != NULL, FALSE);
-  stream_period = gst_mpdparser_get_stream_period (client);
-  g_return_val_if_fail (stream_period != NULL, FALSE);
-  g_return_val_if_fail (stream_period->period != NULL, FALSE);
-
-  *range_start = 0;
-  *range_end = -1;
-
-  GST_DEBUG ("Looking for current representation index");
-  *uri = NULL;
-  if (stream->cur_segment_base && stream->cur_segment_base->indexRange) {
-    *uri =
-        g_strdup (gst_mpdparser_get_initializationURL (stream,
-            stream->cur_segment_base->RepresentationIndex));
-    *range_start = stream->cur_segment_base->indexRange->first_byte_pos;
-    *range_end = stream->cur_segment_base->indexRange->last_byte_pos;
-  } else if (stream->cur_seg_template && stream->cur_seg_template->index) {
-    *uri =
-        gst_mpdparser_build_URL_from_template (stream->cur_seg_template->index,
-        stream->cur_representation->id, 0,
-        stream->cur_representation->bandwidth, 0);
-  }
+  const gchar *p = format;
+  gchar *promoted_format;
 
-  return *uri == NULL ? FALSE : TRUE;
-}
+  /* Must be called with a validated format! */
+  g_return_val_if_fail (validate_format (format), NULL);
 
-GstClockTime
-gst_mpd_client_get_next_fragment_duration (GstMpdClient * client,
-    GstActiveStream * stream)
-{
-  GstMediaSegment *media_segment = NULL;
-  gint seg_idx;
+  /* it starts with % */
+  p++;
 
-  g_return_val_if_fail (stream != NULL, 0);
+  /* Following the % must be a 0, or any of d, x or u.
+   * x and u are not part of the spec, but don't hurt us
+   */
+  if (p[0] == '0') {
+    p++;
 
-  seg_idx = stream->segment_index;
+    while (g_ascii_isdigit (*p))
+      p++;
+  }
 
-  if (stream->segments) {
-    if (seg_idx < stream->segments->len && seg_idx >= 0)
-      media_segment = g_ptr_array_index (stream->segments, seg_idx);
+  /* After any 0 and alphanumeric values, there must be a d.
+   * Otherwise validation would have failed
+   */
+  g_assert (p[0] == 'd');
 
-    return media_segment == NULL ? 0 : media_segment->duration;
-  } else {
-    GstClockTime duration =
-        gst_mpd_client_get_segment_duration (client, stream, NULL);
-    guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
+  promoted_format =
+      g_strdup_printf ("%.*s" G_GINT64_MODIFIER "%s", (gint) (p - format),
+      format, p);
 
-    g_return_val_if_fail (stream->cur_seg_template->MultSegBaseType->
-        SegmentTimeline == NULL, 0);
+  return promoted_format;
+}
 
-    if (!GST_CLOCK_TIME_IS_VALID (duration) || (segments_count > 0
-            && seg_idx >= segments_count)) {
-      return 0;
+static gboolean
+gst_mpdparser_validate_rfc1738_url (const char *s)
+{
+  while (*s) {
+    if (!strchr
+        (";:@&=aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ0123456789$-_.+!*'(),%/",
+            *s))
+      return FALSE;
+    if (*s == '%') {
+      /* g_ascii_isdigit returns FALSE for NUL, and || is a short circuiting
+         operator, so this is safe for strings ending before two hex digits */
+      if (!g_ascii_isxdigit (s[1]) || !g_ascii_isxdigit (s[2]))
+        return FALSE;
+      s += 2;
     }
-    return duration;
+    s++;
   }
+  return TRUE;
 }
 
-GstClockTime
-gst_mpd_client_get_media_presentation_duration (GstMpdClient * client)
+
+
+static GstURLType *
+gst_mpdparser_clone_URL (GstURLType * url)
 {
-  GstClockTime duration;
 
-  g_return_val_if_fail (client != NULL, GST_CLOCK_TIME_NONE);
+  GstURLType *clone = NULL;
 
-  if (client->mpd_node->mediaPresentationDuration != -1) {
-    duration = client->mpd_node->mediaPresentationDuration * GST_MSECOND;
-  } else {
-    /* We can only get the duration for on-demand streams */
-    duration = GST_CLOCK_TIME_NONE;
+  if (url) {
+    clone = g_slice_new0 (GstURLType);
+    if (url->sourceURL) {
+      clone->sourceURL = xmlMemStrdup (url->sourceURL);
+    }
+    clone->range = gst_xml_helper_clone_range (url->range);
   }
 
-  return duration;
+  return clone;
+}
+
+static void
+gst_media_fragment_info_clear (GstMediaFragmentInfo * fragment)
+{
+  g_free (fragment->uri);
+  g_free (fragment->index_uri);
 }
 
+/* API */
 gboolean
-gst_mpd_client_set_period_id (GstMpdClient * client, const gchar * period_id)
+gst_mpdparser_get_mpd_node (GstMPDNode ** mpd_node, const gchar * data,
+    gint size)
 {
-  GstStreamPeriod *next_stream_period;
   gboolean ret = FALSE;
-  GList *iter;
-  guint period_idx;
 
-  g_return_val_if_fail (client != NULL, FALSE);
-  g_return_val_if_fail (client->periods != NULL, FALSE);
-  g_return_val_if_fail (period_id != NULL, FALSE);
+  if (data) {
+    xmlDocPtr doc;
+    xmlNode *root_element = NULL;
 
-  if (!gst_mpd_client_setup_media_presentation (client, GST_CLOCK_TIME_NONE, -1,
-          period_id))
-    return FALSE;
+    GST_DEBUG ("MPD file fully buffered, start parsing...");
+
+    /* parse the complete MPD file into a tree (using the libxml2 default parser API) */
+
+    /* this initialize the library and check potential ABI mismatches
+     * between the version it was compiled for and the actual shared
+     * library used
+     */
+    LIBXML_TEST_VERSION;
 
-  for (period_idx = 0, iter = client->periods; iter;
-      period_idx++, iter = g_list_next (iter)) {
-    next_stream_period = iter->data;
+    /* parse "data" into a document (which is a libxml2 tree structure xmlDoc) */
+    doc = xmlReadMemory (data, size, "noname.xml", NULL, XML_PARSE_NONET);
+    if (doc == NULL) {
+      GST_ERROR ("failed to parse the MPD file");
+      ret = FALSE;
+    } else {
+      /* get the root element node */
+      root_element = xmlDocGetRootElement (doc);
 
-    if (next_stream_period->period->id
-        && strcmp (next_stream_period->period->id, period_id) == 0) {
-      ret = TRUE;
-      client->period_idx = period_idx;
-      break;
+      if (root_element->type != XML_ELEMENT_NODE
+          || xmlStrcmp (root_element->name, (xmlChar *) "MPD") != 0) {
+        GST_ERROR
+            ("can not find the root element MPD, failed to parse the MPD file");
+        ret = FALSE;            /* used to return TRUE before, but this seems wrong */
+      } else {
+        /* now we can parse the MPD root node and all children nodes, recursively */
+        ret = gst_mpdparser_parse_root_node (mpd_node, root_element);
+      }
+      /* free the document */
+      xmlFreeDoc (doc);
     }
   }
 
   return ret;
 }
 
-gboolean
-gst_mpd_client_set_period_index (GstMpdClient * client, guint period_idx)
+GstSegmentListNode *
+gst_mpdparser_get_external_segment_list (const gchar * data, gint size,
+    GstSegmentListNode * parent)
 {
-  GstStreamPeriod *next_stream_period;
-  gboolean ret = FALSE;
+  xmlDocPtr doc = NULL;
+  GstSegmentListNode *new_segment_list = NULL;
 
-  g_return_val_if_fail (client != NULL, FALSE);
-  g_return_val_if_fail (client->periods != NULL, FALSE);
+  doc = xmlReadMemory (data, size, "noname.xml", NULL, XML_PARSE_NONET);
 
-  if (!gst_mpd_client_setup_media_presentation (client, -1, period_idx, NULL))
-    return FALSE;
 
-  next_stream_period = g_list_nth_data (client->periods, period_idx);
-  if (next_stream_period != NULL) {
-    client->period_idx = period_idx;
-    ret = TRUE;
-  }
+  /* NOTE: ISO/IEC 23009-1:2014 5.3.9.3.2 is saying that one or multiple SegmentList
+   * in external xml is allowed, however, multiple SegmentList does not make sense
+   * because Period/AdaptationSet/Representation allow only one SegmentList */
+  if (doc) {
+    xmlNode *root_element = xmlDocGetRootElement (doc);
 
-  return ret;
-}
 
-guint
-gst_mpd_client_get_period_index (GstMpdClient * client)
-{
-  guint period_idx;
+    if (root_element->type == XML_ELEMENT_NODE &&
+        xmlStrcmp (root_element->name, (xmlChar *) "SegmentList") == 0) {
+      gst_mpdparser_parse_segment_list_node (&new_segment_list, root_element,
+          parent);
+    }
+  }
 
-  g_return_val_if_fail (client != NULL, 0);
-  period_idx = client->period_idx;
+  if (doc)
+    xmlFreeDoc (doc);
 
-  return period_idx;
+  return new_segment_list;
 }
 
-const gchar *
-gst_mpd_client_get_period_id (GstMpdClient * client)
+GList *
+gst_mpdparser_get_external_periods (const gchar * data, gint size)
 {
-  GstStreamPeriod *period;
-  gchar *period_id = NULL;
-
-  g_return_val_if_fail (client != NULL, 0);
-  period = g_list_nth_data (client->periods, client->period_idx);
-  if (period && period->period)
-    period_id = period->period->id;
+  xmlDocPtr doc = NULL;
+  GList *new_periods = NULL;
 
-  return period_id;
-}
+  doc = xmlReadMemory (data, size, "noname.xml", NULL, XML_PARSE_NONET);
 
-gboolean
-gst_mpd_client_has_previous_period (GstMpdClient * client)
-{
-  GList *next_stream_period;
-  g_return_val_if_fail (client != NULL, FALSE);
-  g_return_val_if_fail (client->periods != NULL, FALSE);
 
-  if (!gst_mpd_client_setup_media_presentation (client, GST_CLOCK_TIME_NONE,
-          client->period_idx - 1, NULL))
-    return FALSE;
+  if (doc) {
+    xmlNode *root_element = xmlDocGetRootElement (doc);
+    xmlNode *iter;
 
-  next_stream_period =
-      g_list_nth_data (client->periods, client->period_idx - 1);
+    for (iter = root_element->children; iter; iter = iter->next) {
+      if (iter->type == XML_ELEMENT_NODE) {
+        if (xmlStrcmp (iter->name, (xmlChar *) "Period") == 0) {
+          gst_mpdparser_parse_period_node (&new_periods, iter);
+        } else {
+          goto error;
+        }
+      }
+    }
+  }
 
-  return next_stream_period != NULL;
-}
+done:
+  if (doc)
+    xmlFreeDoc (doc);
 
-gboolean
-gst_mpd_client_has_next_period (GstMpdClient * client)
-{
-  GList *next_stream_period;
-  g_return_val_if_fail (client != NULL, FALSE);
-  g_return_val_if_fail (client->periods != NULL, FALSE);
+  return new_periods;
 
-  if (!gst_mpd_client_setup_media_presentation (client, GST_CLOCK_TIME_NONE,
-          client->period_idx + 1, NULL))
-    return FALSE;
+error:
+  GST_ERROR ("Failed to parse period node XML");
 
-  next_stream_period =
-      g_list_nth_data (client->periods, client->period_idx + 1);
-  return next_stream_period != NULL;
+  if (new_periods) {
+    g_list_free_full (new_periods,
+        (GDestroyNotify) gst_mpdparser_free_period_node);
+    new_periods = NULL;
+  }
+  goto done;
 }
 
-void
-gst_mpd_client_seek_to_first_segment (GstMpdClient * client)
+GList *
+gst_mpdparser_get_external_adaptation_sets (const gchar * data, gint size,
+    GstPeriodNode * period)
 {
-  GList *list;
+  xmlDocPtr doc = NULL;
+  GList *new_adaptation_sets = NULL;
 
-  g_return_if_fail (client != NULL);
-  g_return_if_fail (client->active_streams != NULL);
+  doc = xmlReadMemory (data, size, "noname.xml", NULL, XML_PARSE_NONET);
 
-  for (list = g_list_first (client->active_streams); list;
-      list = g_list_next (list)) {
-    GstActiveStream *stream = (GstActiveStream *) list->data;
-    if (stream) {
-      stream->segment_index = 0;
-      stream->segment_repeat_index = 0;
+  /* NOTE: ISO/IEC 23009-1:2014 5.3.3.2 is saying that exactly one AdaptationSet
+   * in external xml is allowed */
+  if (doc) {
+    xmlNode *root_element = xmlDocGetRootElement (doc);
+    if (root_element->type == XML_ELEMENT_NODE &&
+        xmlStrcmp (root_element->name, (xmlChar *) "AdaptationSet") == 0) {
+      gst_mpdparser_parse_adaptation_set_node (&new_adaptation_sets,
+          root_element, period);
     }
   }
-}
-
-static guint
-gst_mpd_client_get_segments_counts (GstMpdClient * client,
-    GstActiveStream * stream)
-{
-  GstStreamPeriod *stream_period;
-
-  g_return_val_if_fail (stream != NULL, 0);
 
-  if (stream->segments)
-    return stream->segments->len;
-  g_return_val_if_fail (stream->cur_seg_template->MultSegBaseType->
-      SegmentTimeline == NULL, 0);
-
-  stream_period = gst_mpdparser_get_stream_period (client);
-  if (stream_period->duration != -1)
-    return gst_util_uint64_scale_ceil (stream_period->duration, 1,
-        gst_mpd_client_get_segment_duration (client, stream, NULL));
+  if (doc)
+    xmlFreeDoc (doc);
 
-  return 0;
+  return new_adaptation_sets;
 }
 
-gboolean
-gst_mpd_client_is_live (GstMpdClient * client)
+void
+gst_mpdparser_free_mpd_node (GstMPDNode * mpd_node)
 {
-  g_return_val_if_fail (client != NULL, FALSE);
-  g_return_val_if_fail (client->mpd_node != NULL, FALSE);
-
-  return client->mpd_node->type == GST_MPD_FILE_TYPE_DYNAMIC;
+  if (mpd_node) {
+    if (mpd_node->default_namespace)
+      xmlFree (mpd_node->default_namespace);
+    if (mpd_node->namespace_xsi)
+      xmlFree (mpd_node->namespace_xsi);
+    if (mpd_node->namespace_ext)
+      xmlFree (mpd_node->namespace_ext);
+    if (mpd_node->schemaLocation)
+      xmlFree (mpd_node->schemaLocation);
+    if (mpd_node->id)
+      xmlFree (mpd_node->id);
+    if (mpd_node->profiles)
+      xmlFree (mpd_node->profiles);
+    if (mpd_node->availabilityStartTime)
+      gst_date_time_unref (mpd_node->availabilityStartTime);
+    if (mpd_node->availabilityEndTime)
+      gst_date_time_unref (mpd_node->availabilityEndTime);
+    g_list_free_full (mpd_node->ProgramInfo,
+        (GDestroyNotify) gst_mpdparser_free_prog_info_node);
+    g_list_free_full (mpd_node->BaseURLs,
+        (GDestroyNotify) gst_mpdparser_free_base_url_node);
+    g_list_free_full (mpd_node->Locations, (GDestroyNotify) xmlFree);
+    g_list_free_full (mpd_node->Periods,
+        (GDestroyNotify) gst_mpdparser_free_period_node);
+    g_list_free_full (mpd_node->Metrics,
+        (GDestroyNotify) gst_mpdparser_free_metrics_node);
+    g_list_free_full (mpd_node->UTCTiming,
+        (GDestroyNotify) gst_mpdparser_free_utctiming_node);
+    g_slice_free (GstMPDNode, mpd_node);
+  }
 }
 
-guint
-gst_mpdparser_get_nb_active_stream (GstMpdClient * client)
+void
+gst_mpdparser_free_period_node (GstPeriodNode * period_node)
 {
-  g_return_val_if_fail (client != NULL, 0);
-
-  return g_list_length (client->active_streams);
+  if (period_node) {
+    if (period_node->id)
+      xmlFree (period_node->id);
+    gst_mpdparser_free_seg_base_type_ext (period_node->SegmentBase);
+    gst_mpdparser_free_segment_list_node (period_node->SegmentList);
+    gst_mpdparser_free_segment_template_node (period_node->SegmentTemplate);
+    g_list_free_full (period_node->AdaptationSets,
+        (GDestroyNotify) gst_mpdparser_free_adaptation_set_node);
+    g_list_free_full (period_node->Subsets,
+        (GDestroyNotify) gst_mpdparser_free_subset_node);
+    g_list_free_full (period_node->BaseURLs,
+        (GDestroyNotify) gst_mpdparser_free_base_url_node);
+    if (period_node->xlink_href)
+      xmlFree (period_node->xlink_href);
+    g_slice_free (GstPeriodNode, period_node);
+  }
 }
 
-guint
-gst_mpdparser_get_nb_adaptationSet (GstMpdClient * client)
+void
+gst_mpdparser_free_adaptation_set_node (GstAdaptationSetNode *
+    adaptation_set_node)
 {
-  GstStreamPeriod *stream_period;
-
-  stream_period = gst_mpdparser_get_stream_period (client);
-  g_return_val_if_fail (stream_period != NULL, 0);
-  g_return_val_if_fail (stream_period->period != NULL, 0);
-
-  return g_list_length (stream_period->period->AdaptationSets);
+  if (adaptation_set_node) {
+    if (adaptation_set_node->lang)
+      xmlFree (adaptation_set_node->lang);
+    if (adaptation_set_node->contentType)
+      xmlFree (adaptation_set_node->contentType);
+    g_slice_free (GstXMLRatio, adaptation_set_node->par);
+    g_slice_free (GstXMLConditionalUintType,
+        adaptation_set_node->segmentAlignment);
+    g_slice_free (GstXMLConditionalUintType,
+        adaptation_set_node->subsegmentAlignment);
+    g_list_free_full (adaptation_set_node->Accessibility,
+        (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
+    g_list_free_full (adaptation_set_node->Role,
+        (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
+    g_list_free_full (adaptation_set_node->Rating,
+        (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
+    g_list_free_full (adaptation_set_node->Viewpoint,
+        (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
+    gst_mpdparser_free_representation_base_type
+        (adaptation_set_node->RepresentationBase);
+    gst_mpdparser_free_seg_base_type_ext (adaptation_set_node->SegmentBase);
+    gst_mpdparser_free_segment_list_node (adaptation_set_node->SegmentList);
+    gst_mpdparser_free_segment_template_node
+        (adaptation_set_node->SegmentTemplate);
+    g_list_free_full (adaptation_set_node->BaseURLs,
+        (GDestroyNotify) gst_mpdparser_free_base_url_node);
+    g_list_free_full (adaptation_set_node->Representations,
+        (GDestroyNotify) gst_mpdparser_free_representation_node);
+    g_list_free_full (adaptation_set_node->ContentComponents,
+        (GDestroyNotify) gst_mpdparser_free_content_component_node);
+    if (adaptation_set_node->xlink_href)
+      xmlFree (adaptation_set_node->xlink_href);
+    g_slice_free (GstAdaptationSetNode, adaptation_set_node);
+  }
 }
 
-GstActiveStream *
-gst_mpdparser_get_active_stream_by_index (GstMpdClient * client,
-    guint stream_idx)
+void
+gst_mpdparser_free_segment_list_node (GstSegmentListNode * segment_list_node)
 {
-  g_return_val_if_fail (client != NULL, NULL);
-  g_return_val_if_fail (client->active_streams != NULL, NULL);
-
-  return g_list_nth_data (client->active_streams, stream_idx);
+  if (segment_list_node) {
+    g_list_free_full (segment_list_node->SegmentURL,
+        (GDestroyNotify) gst_mpdparser_free_segment_url_node);
+    /* MultipleSegmentBaseType extension */
+    gst_mpdparser_free_mult_seg_base_type_ext
+        (segment_list_node->MultSegBaseType);
+    if (segment_list_node->xlink_href)
+      xmlFree (segment_list_node->xlink_href);
+    g_slice_free (GstSegmentListNode, segment_list_node);
+  }
 }
 
-gboolean
-gst_mpd_client_active_stream_contains_subtitles (GstActiveStream * stream)
+void
+gst_mpdparser_free_base_url_node (GstBaseURL * base_url_node)
 {
-  const gchar *mimeType;
-  const gchar *adapt_set_codecs;
-  const gchar *rep_codecs;
-
-  mimeType = stream->cur_representation->RepresentationBase->mimeType;
-  if (!mimeType)
-    mimeType = stream->cur_adapt_set->RepresentationBase->mimeType;
-
-  if (g_strcmp0 (mimeType, "application/ttml+xml") == 0 ||
-      g_strcmp0 (mimeType, "text/vtt") == 0)
-    return TRUE;
-
-  adapt_set_codecs = stream->cur_adapt_set->RepresentationBase->codecs;
-  rep_codecs = stream->cur_representation->RepresentationBase->codecs;
-
-  return (adapt_set_codecs && g_str_has_prefix (adapt_set_codecs, "stpp"))
-      || (rep_codecs && g_str_has_prefix (rep_codecs, "stpp"));
+  if (base_url_node) {
+    if (base_url_node->baseURL)
+      xmlFree (base_url_node->baseURL);
+    if (base_url_node->serviceLocation)
+      xmlFree (base_url_node->serviceLocation);
+    if (base_url_node->byteRange)
+      xmlFree (base_url_node->byteRange);
+    g_slice_free (GstBaseURL, base_url_node);
+  }
 }
 
-static const gchar *
-gst_mpdparser_mimetype_to_caps (const gchar * mimeType)
+void
+gst_mpdparser_free_stream_period (GstStreamPeriod * stream_period)
 {
-  if (mimeType == NULL)
-    return NULL;
-  if (strcmp (mimeType, "video/mp2t") == 0) {
-    return "video/mpegts, systemstream=(bool) true";
-  } else if (strcmp (mimeType, "video/mp4") == 0) {
-    return "video/quicktime";
-  } else if (strcmp (mimeType, "audio/mp4") == 0) {
-    return "audio/x-m4a";
-  } else if (strcmp (mimeType, "text/vtt") == 0) {
-    return "application/x-subtitle-vtt";
-  } else
-    return mimeType;
+  if (stream_period) {
+    g_slice_free (GstStreamPeriod, stream_period);
+  }
 }
 
-GstCaps *
-gst_mpd_client_get_stream_caps (GstActiveStream * stream)
+void
+gst_mpdparser_free_media_segment (GstMediaSegment * media_segment)
 {
-  const gchar *mimeType, *caps_string;
-  GstCaps *ret = NULL;
-
-  if (stream == NULL || stream->cur_adapt_set == NULL
-      || stream->cur_representation == NULL)
-    return NULL;
-
-  mimeType = stream->cur_representation->RepresentationBase->mimeType;
-  if (mimeType == NULL) {
-    mimeType = stream->cur_adapt_set->RepresentationBase->mimeType;
+  if (media_segment) {
+    g_slice_free (GstMediaSegment, media_segment);
   }
-
-  caps_string = gst_mpdparser_mimetype_to_caps (mimeType);
-
-  if ((g_strcmp0 (caps_string, "application/mp4") == 0)
-      && gst_mpd_client_active_stream_contains_subtitles (stream))
-    caps_string = "video/quicktime";
-
-  if (caps_string)
-    ret = gst_caps_from_string (caps_string);
-
-  return ret;
 }
 
-gboolean
-gst_mpd_client_get_bitstream_switching_flag (GstActiveStream * stream)
+void
+gst_mpdparser_init_active_stream_segments (GstActiveStream * stream)
 {
-  if (stream == NULL || stream->cur_adapt_set == NULL)
-    return FALSE;
-
-  return stream->cur_adapt_set->bitstreamSwitching;
+  g_assert (stream->segments == NULL);
+  stream->segments = g_ptr_array_new ();
+  g_ptr_array_set_free_func (stream->segments,
+      (GDestroyNotify) gst_mpdparser_free_media_segment);
 }
 
-guint
-gst_mpd_client_get_video_stream_width (GstActiveStream * stream)
+void
+gst_mpdparser_free_active_stream (GstActiveStream * active_stream)
 {
-  guint width;
-
-  if (stream == NULL || stream->cur_adapt_set == NULL
-      || stream->cur_representation == NULL)
-    return 0;
-
-  width = stream->cur_representation->RepresentationBase->width;
-  if (width == 0) {
-    width = stream->cur_adapt_set->RepresentationBase->width;
+  if (active_stream) {
+    g_free (active_stream->baseURL);
+    active_stream->baseURL = NULL;
+    g_free (active_stream->queryURL);
+    active_stream->queryURL = NULL;
+    if (active_stream->segments)
+      g_ptr_array_unref (active_stream->segments);
+    g_slice_free (GstActiveStream, active_stream);
   }
-
-  return width;
 }
 
-guint
-gst_mpd_client_get_video_stream_height (GstActiveStream * stream)
+const gchar *
+gst_mpdparser_get_initializationURL (GstActiveStream * stream,
+    GstURLType * InitializationURL)
 {
-  guint height;
+  const gchar *url_prefix;
 
-  if (stream == NULL || stream->cur_adapt_set == NULL
-      || stream->cur_representation == NULL)
-    return 0;
+  g_return_val_if_fail (stream != NULL, NULL);
 
-  height = stream->cur_representation->RepresentationBase->height;
-  if (height == 0) {
-    height = stream->cur_adapt_set->RepresentationBase->height;
-  }
+  url_prefix = (InitializationURL
+      && InitializationURL->sourceURL) ? InitializationURL->
+      sourceURL : stream->baseURL;
 
-  return height;
+  return url_prefix;
 }
 
-gboolean
-gst_mpd_client_get_video_stream_framerate (GstActiveStream * stream,
-    gint * fps_num, gint * fps_den)
+gchar *
+gst_mpdparser_get_mediaURL (GstActiveStream * stream,
+    GstSegmentURLNode * segmentURL)
 {
-  if (stream == NULL)
-    return FALSE;
+  const gchar *url_prefix;
 
-  if (stream->cur_adapt_set &&
-      stream->cur_adapt_set->RepresentationBase->frameRate != NULL) {
-    *fps_num = stream->cur_adapt_set->RepresentationBase->frameRate->num;
-    *fps_den = stream->cur_adapt_set->RepresentationBase->frameRate->den;
-    return TRUE;
-  }
+  g_return_val_if_fail (stream != NULL, NULL);
+  g_return_val_if_fail (segmentURL != NULL, NULL);
 
-  if (stream->cur_adapt_set &&
-      stream->cur_adapt_set->RepresentationBase->maxFrameRate != NULL) {
-    *fps_num = stream->cur_adapt_set->RepresentationBase->maxFrameRate->num;
-    *fps_den = stream->cur_adapt_set->RepresentationBase->maxFrameRate->den;
-    return TRUE;
-  }
+  url_prefix = segmentURL->media ? segmentURL->media : stream->baseURL;
+  g_return_val_if_fail (url_prefix != NULL, NULL);
 
-  if (stream->cur_representation &&
-      stream->cur_representation->RepresentationBase->frameRate != NULL) {
-    *fps_num = stream->cur_representation->RepresentationBase->frameRate->num;
-    *fps_den = stream->cur_representation->RepresentationBase->frameRate->den;
-    return TRUE;
-  }
+  return segmentURL->media;
+}
 
-  if (stream->cur_representation &&
-      stream->cur_representation->RepresentationBase->maxFrameRate != NULL) {
-    *fps_num =
-        stream->cur_representation->RepresentationBase->maxFrameRate->num;
-    *fps_den =
-        stream->cur_representation->RepresentationBase->maxFrameRate->den;
-    return TRUE;
+/* navigation functions */
+GstStreamMimeType
+gst_mpdparser_representation_get_mimetype (GstAdaptationSetNode * adapt_set,
+    GstRepresentationNode * rep)
+{
+  gchar *mime = NULL;
+  if (rep->RepresentationBase)
+    mime = rep->RepresentationBase->mimeType;
+  if (mime == NULL && adapt_set->RepresentationBase) {
+    mime = adapt_set->RepresentationBase->mimeType;
   }
 
-  return FALSE;
+  if (strncmp_ext (mime, "audio") == 0)
+    return GST_STREAM_AUDIO;
+  if (strncmp_ext (mime, "video") == 0)
+    return GST_STREAM_VIDEO;
+  if (strncmp_ext (mime, "application") == 0 || strncmp_ext (mime, "text") == 0)
+    return GST_STREAM_APPLICATION;
+
+  return GST_STREAM_UNKNOWN;
 }
 
-guint
-gst_mpd_client_get_audio_stream_rate (GstActiveStream * stream)
+/* Helper methods */
+gchar *
+gst_mpdparser_build_URL_from_template (const gchar * url_template,
+    const gchar * id, guint number, guint bandwidth, guint64 time)
 {
-  const gchar *rate;
+  static const gchar default_format[] = "%01d";
+  gchar **tokens, *token, *ret;
+  const gchar *format;
+  gint i, num_tokens;
 
-  if (stream == NULL || stream->cur_adapt_set == NULL
-      || stream->cur_representation == NULL)
-    return 0;
+  g_return_val_if_fail (url_template != NULL, NULL);
+  tokens = g_strsplit_set (url_template, "$", -1);
+  if (!tokens) {
+    GST_WARNING ("Scan of URL template failed!");
+    return NULL;
+  }
+  num_tokens = g_strv_length (tokens);
 
-  rate = stream->cur_representation->RepresentationBase->audioSamplingRate;
-  if (rate == NULL) {
-    rate = stream->cur_adapt_set->RepresentationBase->audioSamplingRate;
+  /*
+   * each identifier is guarded by 2 $, which means that we must have an odd number of tokens
+   * An even number of tokens means the string is not valid.
+   */
+  if ((num_tokens & 1) == 0) {
+    GST_ERROR ("Invalid number of tokens (%d). url_template is '%s'",
+        num_tokens, url_template);
+    g_strfreev (tokens);
+    return NULL;
   }
 
-  return rate ? atoi (rate) : 0;
-}
+  for (i = 0; i < num_tokens; i++) {
+    token = tokens[i];
+    format = default_format;
 
-guint
-gst_mpd_client_get_audio_stream_num_channels (GstActiveStream * stream)
-{
-  if (stream == NULL || stream->cur_adapt_set == NULL
-      || stream->cur_representation == NULL)
-    return 0;
-  /* TODO: here we have to parse the AudioChannelConfiguration descriptors */
-  return 0;
-}
+    /* the tokens to replace must be provided between $ characters, eg $token$
+     * For a string like token0$token1$token2$token3$token4, only the odd number
+     * tokens (1,3,...) must be parsed.
+     *
+     * Skip even tokens
+     */
+    if ((i & 1) == 0)
+      continue;
 
-guint
-gst_mpdparser_get_list_and_nb_of_audio_language (GstMpdClient * client,
-    GList ** lang)
-{
-  GstStreamPeriod *stream_period;
-  GstAdaptationSetNode *adapt_set;
-  GList *adaptation_sets, *list;
-  const gchar *this_mimeType = "audio";
-  gchar *mimeType = NULL;
-  guint nb_adaptation_set = 0;
-
-  stream_period = gst_mpdparser_get_stream_period (client);
-  g_return_val_if_fail (stream_period != NULL, 0);
-  g_return_val_if_fail (stream_period->period != NULL, 0);
-
-  adaptation_sets =
-      gst_mpd_client_get_adaptation_sets_for_period (client, stream_period);
-  for (list = adaptation_sets; list; list = g_list_next (list)) {
-    adapt_set = (GstAdaptationSetNode *) list->data;
-    if (adapt_set && adapt_set->lang) {
-      gchar *this_lang = adapt_set->lang;
-      GstRepresentationNode *rep;
-      rep =
-          gst_mpdparser_get_lowest_representation (adapt_set->Representations);
-      mimeType = NULL;
-      if (rep->RepresentationBase)
-        mimeType = rep->RepresentationBase->mimeType;
-      if (!mimeType && adapt_set->RepresentationBase) {
-        mimeType = adapt_set->RepresentationBase->mimeType;
-      }
+    if (!g_strcmp0 (token, "RepresentationID")) {
+      if (!gst_mpdparser_validate_rfc1738_url (id))
+        goto invalid_representation_id;
 
-      if (strncmp_ext (mimeType, this_mimeType) == 0) {
-        nb_adaptation_set++;
-        *lang = g_list_append (*lang, this_lang);
+      tokens[i] = g_strdup_printf ("%s", id);
+      g_free (token);
+    } else if (!strncmp (token, "Number", 6)) {
+      if (strlen (token) > 6) {
+        format = token + 6;     /* format tag */
       }
-    }
-  }
+      if (!validate_format (format))
+        goto invalid_format;
 
-  return nb_adaptation_set;
-}
+      tokens[i] = g_strdup_printf (format, number);
+      g_free (token);
+    } else if (!strncmp (token, "Bandwidth", 9)) {
+      if (strlen (token) > 9) {
+        format = token + 9;     /* format tag */
+      }
+      if (!validate_format (format))
+        goto invalid_format;
 
+      tokens[i] = g_strdup_printf (format, bandwidth);
+      g_free (token);
+    } else if (!strncmp (token, "Time", 4)) {
+      gchar *promoted_format;
 
-GstDateTime *
-gst_mpd_client_get_next_segment_availability_start_time (GstMpdClient * client,
-    GstActiveStream * stream)
-{
-  GstDateTime *availability_start_time, *rv;
-  gint seg_idx;
-  GstMediaSegment *segment;
-  GstClockTime segmentEndTime;
-  const GstStreamPeriod *stream_period;
-  GstClockTime period_start = 0;
-
-  g_return_val_if_fail (client != NULL, NULL);
-  g_return_val_if_fail (stream != NULL, NULL);
+      if (strlen (token) > 4) {
+        format = token + 4;     /* format tag */
+      }
+      if (!validate_format (format))
+        goto invalid_format;
 
-  stream_period = gst_mpdparser_get_stream_period (client);
-  if (stream_period && stream_period->period) {
-    period_start = stream_period->start;
+      promoted_format = promote_format_to_uint64 (format);
+      tokens[i] = g_strdup_printf (promoted_format, time);
+      g_free (promoted_format);
+      g_free (token);
+    } else if (!g_strcmp0 (token, "")) {
+      tokens[i] = g_strdup_printf ("%s", "$");
+      g_free (token);
+    } else {
+      /* unexpected identifier found between $ signs
+       *
+       * "If the URL contains unescaped $ symbols which do not enclose a valid
+       * identifier then the result of URL formation is undefined"
+       */
+      goto invalid_format;
+    }
   }
 
-  seg_idx = stream->segment_index;
+  ret = g_strjoinv (NULL, tokens);
 
-  if (stream->segments) {
-    segment = g_ptr_array_index (stream->segments, seg_idx);
+  g_strfreev (tokens);
 
-    if (segment->repeat >= 0) {
-      segmentEndTime = segment->start + (stream->segment_repeat_index + 1) *
-          segment->duration;
-    } else if (seg_idx < stream->segments->len - 1) {
-      const GstMediaSegment *next_segment =
-          g_ptr_array_index (stream->segments, seg_idx + 1);
-      segmentEndTime = next_segment->start;
-    } else {
-      g_return_val_if_fail (stream_period != NULL, NULL);
-      segmentEndTime = period_start + stream_period->duration;
-    }
-  } else {
-    GstClockTime seg_duration;
-    seg_duration = gst_mpd_client_get_segment_duration (client, stream, NULL);
-    if (seg_duration == 0)
-      return NULL;
-    segmentEndTime = period_start + (1 + seg_idx) * seg_duration;
-  }
+  return ret;
 
-  availability_start_time = gst_mpd_client_get_availability_start_time (client);
-  if (availability_start_time == NULL) {
-    GST_WARNING_OBJECT (client, "Failed to get availability_start_time");
-    return NULL;
-  }
+invalid_format:
+  {
+    GST_ERROR ("Invalid format '%s' in '%s'", format, token);
+
+    g_strfreev (tokens);
 
-  rv = gst_mpd_client_add_time_difference (availability_start_time,
-      segmentEndTime / GST_USECOND);
-  gst_date_time_unref (availability_start_time);
-  if (rv == NULL) {
-    GST_WARNING_OBJECT (client, "Failed to offset availability_start_time");
     return NULL;
   }
+invalid_representation_id:
+  {
+    GST_ERROR
+        ("Representation ID string '%s' has characters invalid in an RFC 1738 URL",
+        id);
 
-  return rv;
-}
+    g_strfreev (tokens);
 
-gboolean
-gst_mpd_client_seek_to_time (GstMpdClient * client, GDateTime * time)
-{
-  GDateTime *start;
-  GTimeSpan ts_microseconds;
-  GstClockTime ts;
-  gboolean ret = TRUE;
-  GList *stream;
-
-  g_return_val_if_fail (gst_mpd_client_is_live (client), FALSE);
-  g_return_val_if_fail (client->mpd_node->availabilityStartTime != NULL, FALSE);
-
-  start =
-      gst_date_time_to_g_date_time (client->mpd_node->availabilityStartTime);
-
-  ts_microseconds = g_date_time_difference (time, start);
-  g_date_time_unref (start);
-
-  /* Clamp to availability start time, otherwise calculations wrap around */
-  if (ts_microseconds < 0)
-    ts_microseconds = 0;
-
-  ts = ts_microseconds * GST_USECOND;
-  for (stream = client->active_streams; stream; stream = g_list_next (stream)) {
-    ret =
-        ret & gst_mpd_client_stream_seek (client, stream->data, TRUE, 0, ts,
-        NULL);
+    return NULL;
   }
-  return ret;
-}
-
-void
-gst_media_fragment_info_clear (GstMediaFragmentInfo * fragment)
-{
-  g_free (fragment->uri);
-  g_free (fragment->index_uri);
 }
 
-gboolean
-gst_mpd_client_has_isoff_ondemand_profile (GstMpdClient * client)
+const gchar *
+gst_mpdparser_mimetype_to_caps (const gchar * mimeType)
 {
-  return client->profile_isoff_ondemand;
+  if (mimeType == NULL)
+    return NULL;
+  if (strcmp (mimeType, "video/mp2t") == 0) {
+    return "video/mpegts, systemstream=(bool) true";
+  } else if (strcmp (mimeType, "video/mp4") == 0) {
+    return "video/quicktime";
+  } else if (strcmp (mimeType, "audio/mp4") == 0) {
+    return "audio/x-m4a";
+  } else if (strcmp (mimeType, "text/vtt") == 0) {
+    return "application/x-subtitle-vtt";
+  } else
+    return mimeType;
 }
 
-/**
- * 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
+/*
+ * Combine a base url with the current stream base url from the list of
+ * baseURLs. Takes ownership of base and returns a new base.
  */
-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;
-}
-
-GstClockTime
-gst_mpd_client_get_maximum_segment_duration (GstMpdClient * client)
+GstUri *
+combine_urls (GstUri * base, GList * list, gchar ** query, guint idx)
 {
-  GstClockTime ret = GST_CLOCK_TIME_NONE, dur;
-  GList *stream;
+  GstBaseURL *baseURL;
+  GstUri *ret = base;
 
-  g_return_val_if_fail (client != NULL, GST_CLOCK_TIME_NONE);
-  g_return_val_if_fail (client->mpd_node != NULL, GST_CLOCK_TIME_NONE);
+  if (list != NULL) {
+    baseURL = g_list_nth_data (list, idx);
+    if (!baseURL) {
+      baseURL = list->data;
+    }
 
-  if (client->mpd_node->maxSegmentDuration != GST_MPD_DURATION_NONE) {
-    return client->mpd_node->maxSegmentDuration * GST_MSECOND;
-  }
+    ret = gst_uri_from_string_with_base (base, baseURL->baseURL);
+    gst_uri_unref (base);
 
-  /* According to the DASH specification, if maxSegmentDuration is not present:
-     "If not present, then the maximum Segment duration shall be the maximum
-     duration of any Segment documented in this MPD"
-   */
-  for (stream = client->active_streams; stream; stream = g_list_next (stream)) {
-    dur = gst_mpd_client_get_segment_duration (client, stream->data, NULL);
-    if (dur != GST_CLOCK_TIME_NONE && (dur > ret || ret == GST_CLOCK_TIME_NONE)) {
-      ret = dur;
+    if (ret && query) {
+      g_free (*query);
+      *query = gst_uri_get_query_string (ret);
+      if (*query) {
+        ret = gst_uri_make_writable (ret);
+        gst_uri_set_query_table (ret, NULL);
+      }
     }
   }
+
   return ret;
 }
+
+/* comparison functions */
+int
+strncmp_ext (const char *s1, const char *s2)
+{
+  if (s1 == NULL && s2 == NULL)
+    return 0;
+  if (s1 == NULL && s2 != NULL)
+    return 1;
+  if (s2 == NULL && s1 != NULL)
+    return 1;
+  return strncmp (s1, s2, strlen (s2));
+}
index 9c78d3f..fb55704 100644 (file)
 #include <gst/gst.h>
 #include <gst/uridownloader/gsturidownloader.h>
 #include <gst/base/gstadapter.h>
+#include "gstmpdhelper.h"
 
 G_BEGIN_DECLS
 
-typedef struct _GstMpdClient              GstMpdClient;
 typedef struct _GstActiveStream           GstActiveStream;
 typedef struct _GstStreamPeriod           GstStreamPeriod;
 typedef struct _GstMediaFragmentInfo      GstMediaFragmentInfo;
@@ -50,10 +50,7 @@ typedef struct _GstSegmentListNode        GstSegmentListNode;
 typedef struct _GstSegmentTemplateNode    GstSegmentTemplateNode;
 typedef struct _GstSegmentURLNode         GstSegmentURLNode;
 typedef struct _GstBaseURL                GstBaseURL;
-typedef struct _GstRange                  GstRange;
-typedef struct _GstRatio                  GstRatio;
-typedef struct _GstFrameRate              GstFrameRate;
-typedef struct _GstConditionalUintType    GstConditionalUintType;
+
 typedef struct _GstSubsetNode             GstSubsetNode;
 typedef struct _GstProgramInformationNode GstProgramInformationNode;
 typedef struct _GstMetricsRangeNode       GstMetricsRangeNode;
@@ -65,8 +62,6 @@ typedef struct _GstSegmentBaseType        GstSegmentBaseType;
 typedef struct _GstURLType                GstURLType;
 typedef struct _GstMultSegmentBaseType    GstMultSegmentBaseType;
 
-#define GST_MPD_CLIENT_LOCK(c) g_mutex_lock (&c->lock);
-#define GST_MPD_CLIENT_UNLOCK(c) g_mutex_unlock (&c->lock);
 
 #define GST_MPD_DURATION_NONE ((guint64)-1)
 
@@ -78,22 +73,6 @@ typedef enum
   GST_STREAM_APPLICATION      /* application stream (optional): for timed text/subtitles */
 } GstStreamMimeType;
 
-typedef enum
-{
-  GST_MPD_FILE_TYPE_STATIC,
-  GST_MPD_FILE_TYPE_DYNAMIC
-} GstMPDFileType;
-
-typedef enum
-{
-  GST_SAP_TYPE_0 = 0,
-  GST_SAP_TYPE_1,
-  GST_SAP_TYPE_2,
-  GST_SAP_TYPE_3,
-  GST_SAP_TYPE_4,
-  GST_SAP_TYPE_5,
-  GST_SAP_TYPE_6
-} GstSAPType;
 
 typedef enum
 {
@@ -120,30 +99,6 @@ struct _GstBaseURL
   gchar *byteRange;
 };
 
-struct _GstRange
-{
-  guint64 first_byte_pos;
-  guint64 last_byte_pos;
-};
-
-struct _GstRatio
-{
-  guint num;
-  guint den;
-};
-
-struct _GstFrameRate
-{
-  guint num;
-  guint den;
-};
-
-struct _GstConditionalUintType
-{
-  gboolean flag;
-  guint value;
-};
-
 struct _GstSNode
 {
   guint64 t;
@@ -160,14 +115,14 @@ struct _GstSegmentTimelineNode
 struct _GstURLType
 {
   gchar *sourceURL;
-  GstRange *range;
+  GstXMLRange *range;
 };
 
 struct _GstSegmentBaseType
 {
   guint timescale;
   guint64 presentationTimeOffset;
-  GstRange *indexRange;
+  GstXMLRange *indexRange;
   gboolean indexRangeExact;
   /* Initialization node */
   GstURLType *Initialization;
@@ -211,9 +166,9 @@ struct _GstSegmentTemplateNode
 struct _GstSegmentURLNode
 {
   gchar *media;
-  GstRange *mediaRange;
+  GstXMLRange *mediaRange;
   gchar *index;
-  GstRange *indexRange;
+  GstXMLRange *indexRange;
 };
 
 struct _GstRepresentationBaseType
@@ -221,16 +176,16 @@ struct _GstRepresentationBaseType
   gchar *profiles;
   guint width;
   guint height;
-  GstRatio *sar;
-  GstFrameRate *minFrameRate;
-  GstFrameRate *maxFrameRate;
-  GstFrameRate *frameRate;
+  GstXMLRatio *sar;
+  GstXMLFrameRate *minFrameRate;
+  GstXMLFrameRate *maxFrameRate;
+  GstXMLFrameRate *frameRate;
   gchar *audioSamplingRate;
   gchar *mimeType;
   gchar *segmentProfiles;
   gchar *codecs;
   gdouble maximumSAPPeriod;
-  GstSAPType startWithSAP;
+  GstMPDSAPType startWithSAP;
   gdouble maxPlayoutRate;
   gboolean codingDependency;
   gchar *scanType;
@@ -285,7 +240,7 @@ struct _GstContentComponentNode
   guint id;
   gchar *lang;                      /* LangVectorType RFC 5646 */
   gchar *contentType;
-  GstRatio *par;
+  GstXMLRatio *par;
   /* list of Accessibility DescriptorType nodes */
   GList *Accessibility;
   /* list of Role DescriptorType nodes */
@@ -302,16 +257,16 @@ struct _GstAdaptationSetNode
   guint group;
   gchar *lang;                      /* LangVectorType RFC 5646 */
   gchar *contentType;
-  GstRatio *par;
+  GstXMLRatio *par;
   guint minBandwidth;
   guint maxBandwidth;
   guint minWidth;
   guint maxWidth;
   guint minHeight;
   guint maxHeight;
-  GstConditionalUintType *segmentAlignment;
-  GstConditionalUintType *subsegmentAlignment;
-  GstSAPType subsegmentStartsWithSAP;
+  GstXMLConditionalUintType *segmentAlignment;
+  GstXMLConditionalUintType *subsegmentAlignment;
+  GstMPDSAPType subsegmentStartsWithSAP;
   gboolean bitstreamSwitching;
   /* list of Accessibility DescriptorType nodes */
   GList *Accessibility;
@@ -502,106 +457,35 @@ struct _GstActiveStream
   GstClockTime presentationTimeOffset;        /* presentation time offset of the current segment */
 };
 
-struct _GstMpdClient
-{
-  GstMPDNode *mpd_node;                       /* active MPD manifest file */
-
-  GList *periods;                             /* list of GstStreamPeriod */
-  guint period_idx;                           /* index of current Period */
-
-  GList *active_streams;                      /* list of GstActiveStream */
-
-  guint update_failed_count;
-  gchar *mpd_uri;                             /* manifest file URI */
-  gchar *mpd_base_uri;                        /* base URI for resolving relative URIs.
-                                               * this will be different for redirects */
-
-  /* profiles */
-  gboolean profile_isoff_ondemand;
-
-  GstUriDownloader * downloader;
-};
-
-/* Basic initialization/deinitialization functions */
-GstMpdClient *gst_mpd_client_new (void);
-void gst_active_streams_free (GstMpdClient * client);
-void gst_mpd_client_free (GstMpdClient * client);
-void gst_media_fragment_info_clear (GstMediaFragmentInfo * fragment);
-
-void gst_mpd_client_set_uri_downloader (GstMpdClient * client, GstUriDownloader * download);
-
 /* MPD file parsing */
-gboolean gst_mpd_parse (GstMpdClient *client, const gchar *data, gint size);
-
-/* Streaming management */
-gboolean gst_mpd_client_setup_media_presentation (GstMpdClient *client, GstClockTime time, gint period_index, const gchar *period_id);
-gboolean gst_mpd_client_setup_streaming (GstMpdClient * client, GstAdaptationSetNode * adapt_set);
-gboolean gst_mpd_client_setup_representation (GstMpdClient *client, GstActiveStream *stream, GstRepresentationNode *representation);
-GstClockTime gst_mpd_client_get_next_fragment_duration (GstMpdClient * client, GstActiveStream * stream);
-GstClockTime gst_mpd_client_get_media_presentation_duration (GstMpdClient *client);
-GstClockTime gst_mpd_client_get_maximum_segment_duration (GstMpdClient * client);
-gboolean gst_mpd_client_get_last_fragment_timestamp_end (GstMpdClient * client, guint stream_idx, GstClockTime * ts);
-gboolean gst_mpd_client_get_next_fragment_timestamp (GstMpdClient * client, guint stream_idx, GstClockTime * ts);
-gboolean gst_mpd_client_get_next_fragment (GstMpdClient *client, guint indexStream, GstMediaFragmentInfo * fragment);
-gboolean gst_mpd_client_get_next_header (GstMpdClient *client, gchar **uri, guint stream_idx, gint64 * range_start, gint64 * range_end);
-gboolean gst_mpd_client_get_next_header_index (GstMpdClient *client, gchar **uri, guint stream_idx, gint64 * range_start, gint64 * range_end);
-gboolean gst_mpd_client_is_live (GstMpdClient * client);
-gboolean gst_mpd_client_stream_seek (GstMpdClient * client, GstActiveStream * stream, gboolean forward, GstSeekFlags flags, GstClockTime ts, GstClockTime * final_ts);
-gboolean gst_mpd_client_seek_to_time (GstMpdClient * client, GDateTime * time);
-GstClockTime gst_mpd_parser_get_stream_presentation_offset (GstMpdClient *client, guint stream_idx);
-gchar** gst_mpd_client_get_utc_timing_sources (GstMpdClient *client, guint methods, GstMPDUTCTimingType *selected_method);
-GstClockTime gst_mpd_parser_get_period_start_time (GstMpdClient *client);
-
-/* Period selection */
-guint gst_mpd_client_get_period_index_at_time (GstMpdClient * client, GstDateTime * time);
-gboolean gst_mpd_client_set_period_index (GstMpdClient *client, guint period_idx);
-gboolean gst_mpd_client_set_period_id (GstMpdClient *client, const gchar * period_id);
-guint gst_mpd_client_get_period_index (GstMpdClient *client);
-const gchar *gst_mpd_client_get_period_id (GstMpdClient *client);
-gboolean gst_mpd_client_has_next_period (GstMpdClient *client);
-gboolean gst_mpd_client_has_previous_period (GstMpdClient * client);
-
-/* Representation selection */
-gint gst_mpdparser_get_rep_idx_with_max_bandwidth (GList *Representations, gint64 max_bandwidth, gint max_video_width, gint max_video_height, gint max_video_framerate_n, gint max_video_framerate_d);
-gint gst_mpdparser_get_rep_idx_with_min_bandwidth (GList * Representations);
-
-/* URL management */
-const gchar *gst_mpdparser_get_baseURL (GstMpdClient *client, guint indexStream);
-
-/* Active stream */
-guint gst_mpdparser_get_nb_active_stream (GstMpdClient *client);
-GstActiveStream *gst_mpdparser_get_active_stream_by_index (GstMpdClient *client, guint stream_idx);
-gboolean gst_mpd_client_active_stream_contains_subtitles (GstActiveStream * stream);
-
-/* AdaptationSet */
-guint gst_mpdparser_get_nb_adaptationSet (GstMpdClient *client);
-GList * gst_mpd_client_get_adaptation_sets (GstMpdClient * client);
-
-/* Segment */
-gboolean gst_mpd_client_has_next_segment (GstMpdClient * client, GstActiveStream * stream, gboolean forward);
-GstFlowReturn gst_mpd_client_advance_segment (GstMpdClient * client, GstActiveStream * stream, gboolean forward);
-void gst_mpd_client_seek_to_first_segment (GstMpdClient * client);
-GstDateTime *gst_mpd_client_get_next_segment_availability_start_time (GstMpdClient * client, GstActiveStream * stream);
-
-/* Get audio/video stream parameters (caps, width, height, rate, number of channels) */
-GstCaps * gst_mpd_client_get_stream_caps (GstActiveStream * stream);
-gboolean gst_mpd_client_get_bitstream_switching_flag (GstActiveStream * stream);
-guint gst_mpd_client_get_video_stream_width (GstActiveStream * stream);
-guint gst_mpd_client_get_video_stream_height (GstActiveStream * stream);
-gboolean gst_mpd_client_get_video_stream_framerate (GstActiveStream * stream, gint * fps_num, gint * fps_den);
-guint gst_mpd_client_get_audio_stream_rate (GstActiveStream * stream);
-guint gst_mpd_client_get_audio_stream_num_channels (GstActiveStream * stream);
-
-/* Support multi language */
-guint gst_mpdparser_get_list_and_nb_of_audio_language (GstMpdClient *client, GList **lang);
-
-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);
-
+gboolean gst_mpdparser_get_mpd_node (GstMPDNode ** mpd_node, const gchar * data, gint size);
+GstSegmentListNode * gst_mpdparser_get_external_segment_list (const gchar * data, gint size, GstSegmentListNode * parent);
+GList * gst_mpdparser_get_external_periods (const gchar * data, gint size);
+GList * gst_mpdparser_get_external_adaptation_sets (const gchar * data, gint size, GstPeriodNode* period);
+
+/* navigation functions */
+GstStreamMimeType gst_mpdparser_representation_get_mimetype (GstAdaptationSetNode * adapt_set, GstRepresentationNode * rep);
+
+/* Memory management */
+void gst_mpdparser_free_mpd_node (GstMPDNode * mpd_node);
+void gst_mpdparser_free_period_node (GstPeriodNode * period_node);
+void gst_mpdparser_free_adaptation_set_node (GstAdaptationSetNode * adaptation_set_node);
+void gst_mpdparser_free_segment_list_node (GstSegmentListNode * segment_list_node);
+void gst_mpdparser_free_stream_period (GstStreamPeriod * stream_period);
+void gst_mpdparser_free_media_segment (GstMediaSegment * media_segment);
+void gst_mpdparser_free_active_stream (GstActiveStream * active_stream);
+void gst_mpdparser_free_base_url_node (GstBaseURL * base_url_node);
+
+/* Active stream methods*/
+void gst_mpdparser_init_active_stream_segments (GstActiveStream * stream);
+gchar *gst_mpdparser_get_mediaURL (GstActiveStream * stream, GstSegmentURLNode * segmentURL);
+const gchar *gst_mpdparser_get_initializationURL (GstActiveStream * stream, GstURLType * InitializationURL);
+
+/*Helper methods */
+gchar *gst_mpdparser_build_URL_from_template (const gchar * url_template, const gchar * id, guint number, guint bandwidth, guint64 time);
+const gchar *gst_mpdparser_mimetype_to_caps (const gchar * mimeType);
+GstUri *combine_urls (GstUri * base, GList * list, gchar ** query, guint idx);
+int strncmp_ext (const char *s1, const char *s2);
 G_END_DECLS
 
 #endif /* __GST_MPDPARSER_H__ */
diff --git a/ext/dash/gstxmlhelper.c b/ext/dash/gstxmlhelper.c
new file mode 100644 (file)
index 0000000..ff97981
--- /dev/null
@@ -0,0 +1,1024 @@
+/* GStreamer
+ *
+ * Copyright (C) 2019 Collabora Ltd.
+ *   Author: Stéphane Cerveau <scerveau@collabora.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ */
+
+#include "gstxmlhelper.h"
+
+/* static methods */
+/* this function computes decimals * 10 ^ (3 - pos) */
+static guint
+_mpd_helper_convert_to_millisecs (guint decimals, gint pos)
+{
+  guint num = 1, den = 1;
+  gint i = 3 - pos;
+
+  while (i < 0) {
+    den *= 10;
+    i++;
+  }
+  while (i > 0) {
+    num *= 10;
+    i--;
+  }
+  /* if i == 0 we have exactly 3 decimals and nothing to do */
+  return decimals * num / den;
+}
+
+static gboolean
+_mpd_helper_accumulate (guint64 * v, guint64 mul, guint64 add)
+{
+  guint64 tmp;
+
+  if (*v > G_MAXUINT64 / mul)
+    return FALSE;
+  tmp = *v * mul;
+  if (tmp > G_MAXUINT64 - add)
+    return FALSE;
+  *v = tmp + add;
+  return TRUE;
+}
+
+/*
+  Duration Data Type
+
+  The duration data type is used to specify a time interval.
+
+  The time interval is specified in the following form "-PnYnMnDTnHnMnS" where:
+
+    * - indicates the negative sign (optional)
+    * P indicates the period (required)
+    * nY indicates the number of years
+    * nM indicates the number of months
+    * nD indicates the number of days
+    * T indicates the start of a time section (required if you are going to specify hours, minutes, or seconds)
+    * nH indicates the number of hours
+    * nM indicates the number of minutes
+    * nS indicates the number of seconds
+*/
+static gboolean
+_mpd_helper_parse_duration (const char *str, guint64 * value)
+{
+  gint ret, len, pos, posT;
+  gint years = -1, months = -1, days = -1, hours = -1, minutes = -1, seconds =
+      -1, decimals = -1, read;
+  gboolean have_ms = FALSE;
+  guint64 tmp_value;
+
+  len = strlen (str);
+  GST_TRACE ("duration: %s, len %d", str, len);
+  if (strspn (str, "PT0123456789., \tHMDSY") < len) {
+    GST_WARNING ("Invalid character found: '%s'", str);
+    goto error;
+  }
+  /* skip leading/trailing whitespace */
+  while (g_ascii_isspace (str[0])) {
+    str++;
+    len--;
+  }
+  while (len > 0 && g_ascii_isspace (str[len - 1]))
+    --len;
+
+  /* read "P" for period */
+  if (str[0] != 'P') {
+    GST_WARNING ("P not found at the beginning of the string!");
+    goto error;
+  }
+  str++;
+  len--;
+
+  /* read "T" for time (if present) */
+  posT = strcspn (str, "T");
+  len -= posT;
+  if (posT > 0) {
+    /* there is some room between P and T, so there must be a period section */
+    /* read years, months, days */
+    do {
+      GST_TRACE ("parsing substring %s", str);
+      pos = strcspn (str, "YMD");
+      ret = sscanf (str, "%u", &read);
+      if (ret != 1) {
+        GST_WARNING ("can not read integer value from string %s!", str);
+        goto error;
+      }
+      switch (str[pos]) {
+        case 'Y':
+          if (years != -1 || months != -1 || days != -1) {
+            GST_WARNING ("year, month or day was already set");
+            goto error;
+          }
+          years = read;
+          break;
+        case 'M':
+          if (months != -1 || days != -1) {
+            GST_WARNING ("month or day was already set");
+            goto error;
+          }
+          months = read;
+          if (months >= 12) {
+            GST_WARNING ("Month out of range");
+            goto error;
+          }
+          break;
+        case 'D':
+          if (days != -1) {
+            GST_WARNING ("day was already set");
+            goto error;
+          }
+          days = read;
+          if (days >= 31) {
+            GST_WARNING ("Day out of range");
+            goto error;
+          }
+          break;
+        default:
+          GST_WARNING ("unexpected char %c!", str[pos]);
+          goto error;
+          break;
+      }
+      GST_TRACE ("read number %u type %c", read, str[pos]);
+      str += (pos + 1);
+      posT -= (pos + 1);
+    } while (posT > 0);
+  }
+
+  if (years == -1)
+    years = 0;
+  if (months == -1)
+    months = 0;
+  if (days == -1)
+    days = 0;
+
+  GST_TRACE ("Y:M:D=%d:%d:%d", years, months, days);
+
+  /* read "T" for time (if present) */
+  /* here T is at pos == 0 */
+  str++;
+  len--;
+  pos = 0;
+  if (pos < len) {
+    /* T found, there is a time section */
+    /* read hours, minutes, seconds, hundredths of second */
+    do {
+      GST_TRACE ("parsing substring %s", str);
+      pos = strcspn (str, "HMS,.");
+      ret = sscanf (str, "%u", &read);
+      if (ret != 1) {
+        GST_WARNING ("can not read integer value from string %s!", str);
+        goto error;
+      }
+      switch (str[pos]) {
+        case 'H':
+          if (hours != -1 || minutes != -1 || seconds != -1) {
+            GST_WARNING ("hour, minute or second was already set");
+            goto error;
+          }
+          hours = read;
+          if (hours >= 24) {
+            GST_WARNING ("Hour out of range");
+            goto error;
+          }
+          break;
+        case 'M':
+          if (minutes != -1 || seconds != -1) {
+            GST_WARNING ("minute or second was already set");
+            goto error;
+          }
+          minutes = read;
+          if (minutes >= 60) {
+            GST_WARNING ("Minute out of range");
+            goto error;
+          }
+          break;
+        case 'S':
+          if (have_ms) {
+            /* we have read the decimal part of the seconds */
+            decimals = _mpd_helper_convert_to_millisecs (read, pos);
+            GST_TRACE ("decimal number %u (%d digits) -> %d ms", read, pos,
+                decimals);
+          } else {
+            if (seconds != -1) {
+              GST_WARNING ("second was already set");
+              goto error;
+            }
+            /* no decimals */
+            seconds = read;
+          }
+          break;
+        case '.':
+        case ',':
+          /* we have read the integer part of a decimal number in seconds */
+          if (seconds != -1) {
+            GST_WARNING ("second was already set");
+            goto error;
+          }
+          seconds = read;
+          have_ms = TRUE;
+          break;
+        default:
+          GST_WARNING ("unexpected char %c!", str[pos]);
+          goto error;
+          break;
+      }
+      GST_TRACE ("read number %u type %c", read, str[pos]);
+      str += pos + 1;
+      len -= (pos + 1);
+    } while (len > 0);
+  }
+
+  if (hours == -1)
+    hours = 0;
+  if (minutes == -1)
+    minutes = 0;
+  if (seconds == -1)
+    seconds = 0;
+  if (decimals == -1)
+    decimals = 0;
+  GST_TRACE ("H:M:S.MS=%d:%d:%d.%03d", hours, minutes, seconds, decimals);
+
+  tmp_value = 0;
+  if (!_mpd_helper_accumulate (&tmp_value, 1, years)
+      || !_mpd_helper_accumulate (&tmp_value, 365, months * 30)
+      || !_mpd_helper_accumulate (&tmp_value, 1, days)
+      || !_mpd_helper_accumulate (&tmp_value, 24, hours)
+      || !_mpd_helper_accumulate (&tmp_value, 60, minutes)
+      || !_mpd_helper_accumulate (&tmp_value, 60, seconds)
+      || !_mpd_helper_accumulate (&tmp_value, 1000, decimals))
+    goto error;
+
+  /* ensure it can be converted from milliseconds to nanoseconds */
+  if (tmp_value > G_MAXUINT64 / 1000000)
+    goto error;
+
+  *value = tmp_value;
+  return TRUE;
+
+error:
+  return FALSE;
+}
+
+static gboolean
+_mpd_helper_validate_no_whitespace (const char *s)
+{
+  return !strpbrk (s, "\r\n\t ");
+}
+
+/* API */
+
+GstXMLRange *
+gst_xml_helper_clone_range (GstXMLRange * range)
+{
+  GstXMLRange *clone = NULL;
+
+  if (range) {
+    clone = g_slice_new0 (GstXMLRange);
+    clone->first_byte_pos = range->first_byte_pos;
+    clone->last_byte_pos = range->last_byte_pos;
+  }
+
+  return clone;
+}
+
+/* XML property get method */
+gboolean
+gst_xml_helper_get_prop_validated_string (xmlNode * a_node,
+    const gchar * property_name, gchar ** property_value,
+    gboolean (*validate) (const char *))
+{
+  xmlChar *prop_string;
+  gboolean exists = FALSE;
+
+  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
+  if (prop_string) {
+    if (validate && !(*validate) ((const char *) prop_string)) {
+      GST_WARNING ("Validation failure: %s", prop_string);
+      xmlFree (prop_string);
+      return FALSE;
+    }
+    *property_value = (gchar *) prop_string;
+    exists = TRUE;
+    GST_LOG (" - %s: %s", property_name, prop_string);
+  }
+
+  return exists;
+}
+
+gboolean
+gst_xml_helper_get_ns_prop_string (xmlNode * a_node,
+    const gchar * ns_name, const gchar * property_name, gchar ** property_value)
+{
+  xmlChar *prop_string;
+  gboolean exists = FALSE;
+
+  prop_string =
+      xmlGetNsProp (a_node, (const xmlChar *) property_name,
+      (const xmlChar *) ns_name);
+  if (prop_string) {
+    *property_value = (gchar *) prop_string;
+    exists = TRUE;
+    GST_LOG (" - %s:%s: %s", ns_name, property_name, prop_string);
+  }
+
+  return exists;
+}
+
+gboolean
+gst_xml_helper_get_prop_string (xmlNode * a_node,
+    const gchar * property_name, gchar ** property_value)
+{
+  return gst_xml_helper_get_prop_validated_string (a_node, property_name,
+      property_value, NULL);
+}
+
+gboolean
+gst_xml_helper_get_prop_string_vector_type (xmlNode * a_node,
+    const gchar * property_name, gchar *** property_value)
+{
+  xmlChar *prop_string;
+  gchar **prop_string_vector = NULL;
+  guint i = 0;
+  gboolean exists = FALSE;
+
+  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
+  if (prop_string) {
+    prop_string_vector = g_strsplit ((gchar *) prop_string, " ", -1);
+    if (prop_string_vector) {
+      exists = TRUE;
+      *property_value = prop_string_vector;
+      GST_LOG (" - %s:", property_name);
+      while (prop_string_vector[i]) {
+        GST_LOG ("    %s", prop_string_vector[i]);
+        i++;
+      }
+    } else {
+      GST_WARNING ("Scan of string vector property failed!");
+    }
+    xmlFree (prop_string);
+  }
+
+  return exists;
+}
+
+gboolean
+gst_xml_helper_get_prop_signed_integer (xmlNode * a_node,
+    const gchar * property_name, gint default_val, gint * property_value)
+{
+  xmlChar *prop_string;
+  gboolean exists = FALSE;
+
+  *property_value = default_val;
+  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
+  if (prop_string) {
+    if (sscanf ((const gchar *) prop_string, "%d", property_value) == 1) {
+      exists = TRUE;
+      GST_LOG (" - %s: %d", property_name, *property_value);
+    } else {
+      GST_WARNING
+          ("failed to parse signed integer property %s from xml string %s",
+          property_name, prop_string);
+    }
+    xmlFree (prop_string);
+  }
+
+  return exists;
+}
+
+gboolean
+gst_xml_helper_get_prop_unsigned_integer (xmlNode * a_node,
+    const gchar * property_name, guint default_val, guint * property_value)
+{
+  xmlChar *prop_string;
+  gboolean exists = FALSE;
+
+  *property_value = default_val;
+  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
+  if (prop_string) {
+    if (sscanf ((gchar *) prop_string, "%u", property_value) == 1 &&
+        strstr ((gchar *) prop_string, "-") == NULL) {
+      exists = TRUE;
+      GST_LOG (" - %s: %u", property_name, *property_value);
+    } else {
+      GST_WARNING
+          ("failed to parse unsigned integer property %s from xml string %s",
+          property_name, prop_string);
+      /* sscanf might have written to *property_value. Restore to default */
+      *property_value = default_val;
+    }
+    xmlFree (prop_string);
+  }
+
+  return exists;
+}
+
+gboolean
+gst_xml_helper_get_prop_unsigned_integer_64 (xmlNode * a_node,
+    const gchar * property_name, guint64 default_val, guint64 * property_value)
+{
+  xmlChar *prop_string;
+  gboolean exists = FALSE;
+
+  *property_value = default_val;
+  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
+  if (prop_string) {
+    if (sscanf ((gchar *) prop_string, "%" G_GUINT64_FORMAT,
+            property_value) == 1 &&
+        strstr ((gchar *) prop_string, "-") == NULL) {
+      exists = TRUE;
+      GST_LOG (" - %s: %" G_GUINT64_FORMAT, property_name, *property_value);
+    } else {
+      GST_WARNING
+          ("failed to parse unsigned integer property %s from xml string %s",
+          property_name, prop_string);
+      /* sscanf might have written to *property_value. Restore to default */
+      *property_value = default_val;
+    }
+    xmlFree (prop_string);
+  }
+
+  return exists;
+}
+
+gboolean
+gst_xml_helper_get_prop_uint_vector_type (xmlNode * a_node,
+    const gchar * property_name, guint ** property_value, guint * value_size)
+{
+  xmlChar *prop_string;
+  gchar **str_vector;
+  guint *prop_uint_vector = NULL, i;
+  gboolean exists = FALSE;
+
+  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
+  if (prop_string) {
+    str_vector = g_strsplit ((gchar *) prop_string, " ", -1);
+    if (str_vector) {
+      *value_size = g_strv_length (str_vector);
+      prop_uint_vector = g_malloc (*value_size * sizeof (guint));
+      if (prop_uint_vector) {
+        exists = TRUE;
+        GST_LOG (" - %s:", property_name);
+        for (i = 0; i < *value_size; i++) {
+          if (sscanf ((gchar *) str_vector[i], "%u", &prop_uint_vector[i]) == 1
+              && strstr (str_vector[i], "-") == NULL) {
+            GST_LOG ("    %u", prop_uint_vector[i]);
+          } else {
+            GST_WARNING
+                ("failed to parse uint vector type property %s from xml string %s",
+                property_name, str_vector[i]);
+            /* there is no special value to put in prop_uint_vector[i] to
+             * signal it is invalid, so we just clean everything and return
+             * FALSE
+             */
+            g_free (prop_uint_vector);
+            prop_uint_vector = NULL;
+            exists = FALSE;
+            break;
+          }
+        }
+        *property_value = prop_uint_vector;
+      } else {
+        GST_WARNING ("Array allocation failed!");
+      }
+    } else {
+      GST_WARNING ("Scan of uint vector property failed!");
+    }
+    xmlFree (prop_string);
+    g_strfreev (str_vector);
+  }
+
+  return exists;
+}
+
+gboolean
+gst_xml_helper_get_prop_double (xmlNode * a_node,
+    const gchar * property_name, gdouble * property_value)
+{
+  xmlChar *prop_string;
+  gboolean exists = FALSE;
+
+  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
+  if (prop_string) {
+    if (sscanf ((gchar *) prop_string, "%lf", property_value) == 1) {
+      exists = TRUE;
+      GST_LOG (" - %s: %lf", property_name, *property_value);
+    } else {
+      GST_WARNING ("failed to parse double property %s from xml string %s",
+          property_name, prop_string);
+    }
+    xmlFree (prop_string);
+  }
+
+  return exists;
+}
+
+gboolean
+gst_xml_helper_get_prop_boolean (xmlNode * a_node,
+    const gchar * property_name, gboolean default_val,
+    gboolean * property_value)
+{
+  xmlChar *prop_string;
+  gboolean exists = FALSE;
+
+  *property_value = default_val;
+  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
+  if (prop_string) {
+    if (xmlStrcmp (prop_string, (xmlChar *) "false") == 0) {
+      exists = TRUE;
+      *property_value = FALSE;
+      GST_LOG (" - %s: false", property_name);
+    } else if (xmlStrcmp (prop_string, (xmlChar *) "true") == 0) {
+      exists = TRUE;
+      *property_value = TRUE;
+      GST_LOG (" - %s: true", property_name);
+    } else {
+      GST_WARNING ("failed to parse boolean property %s from xml string %s",
+          property_name, prop_string);
+    }
+    xmlFree (prop_string);
+  }
+
+  return exists;
+}
+
+gboolean
+gst_xml_helper_get_prop_range (xmlNode * a_node,
+    const gchar * property_name, GstXMLRange ** property_value)
+{
+  xmlChar *prop_string;
+  guint64 first_byte_pos = 0, last_byte_pos = -1;
+  guint len, pos;
+  gchar *str;
+  gboolean exists = FALSE;
+
+  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
+  if (prop_string) {
+    len = xmlStrlen (prop_string);
+    str = (gchar *) prop_string;
+    GST_TRACE ("range: %s, len %d", str, len);
+
+    /* read "-" */
+    pos = strcspn (str, "-");
+    if (pos >= len) {
+      GST_TRACE ("pos %d >= len %d", pos, len);
+      goto error;
+    }
+    /* read first_byte_pos */
+    if (pos != 0) {
+      /* replace str[pos] with '\0' to allow sscanf to not be confused by
+       * the minus sign (eg " -1" (observe the space before -) would otherwise
+       * be interpreted as range -1 to 1)
+       */
+      str[pos] = 0;
+      if (sscanf (str, "%" G_GUINT64_FORMAT, &first_byte_pos) != 1 ||
+          strstr (str, "-") != NULL) {
+        /* sscanf failed or it found a negative number */
+        /* restore the '-' sign */
+        str[pos] = '-';
+        goto error;
+      }
+      /* restore the '-' sign */
+      str[pos] = '-';
+    }
+    /* read last_byte_pos */
+    if (pos < (len - 1)) {
+      if (sscanf (str + pos + 1, "%" G_GUINT64_FORMAT, &last_byte_pos) != 1 ||
+          strstr (str + pos + 1, "-") != NULL) {
+        goto error;
+      }
+    }
+    /* malloc return data structure */
+    *property_value = g_slice_new0 (GstXMLRange);
+    exists = TRUE;
+    (*property_value)->first_byte_pos = first_byte_pos;
+    (*property_value)->last_byte_pos = last_byte_pos;
+    xmlFree (prop_string);
+    GST_LOG (" - %s: %" G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT,
+        property_name, first_byte_pos, last_byte_pos);
+  }
+
+  return exists;
+
+error:
+  GST_WARNING ("failed to parse property %s from xml string %s", property_name,
+      prop_string);
+  xmlFree (prop_string);
+  return FALSE;
+}
+
+gboolean
+gst_xml_helper_get_prop_ratio (xmlNode * a_node,
+    const gchar * property_name, GstXMLRatio ** property_value)
+{
+  xmlChar *prop_string;
+  guint num = 0, den = 1;
+  guint len, pos;
+  gchar *str;
+  gboolean exists = FALSE;
+
+  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
+  if (prop_string) {
+    len = xmlStrlen (prop_string);
+    str = (gchar *) prop_string;
+    GST_TRACE ("ratio: %s, len %d", str, len);
+
+    /* read ":" */
+    pos = strcspn (str, ":");
+    if (pos >= len) {
+      GST_TRACE ("pos %d >= len %d", pos, len);
+      goto error;
+    }
+    /* search for negative sign */
+    if (strstr (str, "-") != NULL) {
+      goto error;
+    }
+    /* read num */
+    if (pos != 0) {
+      if (sscanf (str, "%u", &num) != 1) {
+        goto error;
+      }
+    }
+    /* read den */
+    if (pos < (len - 1)) {
+      if (sscanf (str + pos + 1, "%u", &den) != 1) {
+        goto error;
+      }
+    }
+    /* malloc return data structure */
+    *property_value = g_slice_new0 (GstXMLRatio);
+    exists = TRUE;
+    (*property_value)->num = num;
+    (*property_value)->den = den;
+    xmlFree (prop_string);
+    GST_LOG (" - %s: %u:%u", property_name, num, den);
+  }
+
+  return exists;
+
+error:
+  GST_WARNING ("failed to parse property %s from xml string %s", property_name,
+      prop_string);
+  xmlFree (prop_string);
+  return FALSE;
+}
+
+gboolean
+gst_xml_helper_get_prop_framerate (xmlNode * a_node,
+    const gchar * property_name, GstXMLFrameRate ** property_value)
+{
+  xmlChar *prop_string;
+  guint num = 0, den = 1;
+  guint len, pos;
+  gchar *str;
+  gboolean exists = FALSE;
+
+  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
+  if (prop_string) {
+    len = xmlStrlen (prop_string);
+    str = (gchar *) prop_string;
+    GST_TRACE ("framerate: %s, len %d", str, len);
+
+    /* search for negative sign */
+    if (strstr (str, "-") != NULL) {
+      goto error;
+    }
+
+    /* read "/" if available */
+    pos = strcspn (str, "/");
+    /* read num */
+    if (pos != 0) {
+      if (sscanf (str, "%u", &num) != 1) {
+        goto error;
+      }
+    }
+    /* read den (if available) */
+    if (pos < (len - 1)) {
+      if (sscanf (str + pos + 1, "%u", &den) != 1) {
+        goto error;
+      }
+    }
+    /* alloc return data structure */
+    *property_value = g_slice_new0 (GstXMLFrameRate);
+    exists = TRUE;
+    (*property_value)->num = num;
+    (*property_value)->den = den;
+    xmlFree (prop_string);
+    if (den == 1)
+      GST_LOG (" - %s: %u", property_name, num);
+    else
+      GST_LOG (" - %s: %u/%u", property_name, num, den);
+  }
+
+  return exists;
+
+error:
+  GST_WARNING ("failed to parse property %s from xml string %s", property_name,
+      prop_string);
+  xmlFree (prop_string);
+  return FALSE;
+}
+
+gboolean
+gst_xml_helper_get_prop_cond_uint (xmlNode * a_node,
+    const gchar * property_name, GstXMLConditionalUintType ** property_value)
+{
+  xmlChar *prop_string;
+  gchar *str;
+  gboolean flag;
+  guint val;
+  gboolean exists = FALSE;
+
+  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
+  if (prop_string) {
+    str = (gchar *) prop_string;
+    GST_TRACE ("conditional uint: %s", str);
+
+    if (strcmp (str, "false") == 0) {
+      flag = FALSE;
+      val = 0;
+    } else if (strcmp (str, "true") == 0) {
+      flag = TRUE;
+      val = 0;
+    } else {
+      flag = TRUE;
+      if (sscanf (str, "%u", &val) != 1 || strstr (str, "-") != NULL)
+        goto error;
+    }
+
+    /* alloc return data structure */
+    *property_value = g_slice_new0 (GstXMLConditionalUintType);
+    exists = TRUE;
+    (*property_value)->flag = flag;
+    (*property_value)->value = val;
+    xmlFree (prop_string);
+    GST_LOG (" - %s: flag=%s val=%u", property_name, flag ? "true" : "false",
+        val);
+  }
+
+  return exists;
+
+error:
+  GST_WARNING ("failed to parse property %s from xml string %s", property_name,
+      prop_string);
+  xmlFree (prop_string);
+  return FALSE;
+}
+
+gboolean
+gst_xml_helper_get_prop_dateTime (xmlNode * a_node,
+    const gchar * property_name, GstDateTime ** property_value)
+{
+  xmlChar *prop_string;
+  gchar *str;
+  gint ret, pos;
+  gint year, month, day, hour, minute;
+  gdouble second;
+  gboolean exists = FALSE;
+  gfloat tzoffset = 0.0;
+  gint gmt_offset_hour = -99, gmt_offset_min = -99;
+
+  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
+  if (prop_string) {
+    str = (gchar *) prop_string;
+    GST_TRACE ("dateTime: %s, len %d", str, xmlStrlen (prop_string));
+    /* parse year */
+    ret = sscanf (str, "%d", &year);
+    if (ret != 1 || year <= 0)
+      goto error;
+    pos = strcspn (str, "-");
+    str += (pos + 1);
+    GST_TRACE (" - year %d", year);
+    /* parse month */
+    ret = sscanf (str, "%d", &month);
+    if (ret != 1 || month <= 0)
+      goto error;
+    pos = strcspn (str, "-");
+    str += (pos + 1);
+    GST_TRACE (" - month %d", month);
+    /* parse day */
+    ret = sscanf (str, "%d", &day);
+    if (ret != 1 || day <= 0)
+      goto error;
+    pos = strcspn (str, "T");
+    str += (pos + 1);
+    GST_TRACE (" - day %d", day);
+    /* parse hour */
+    ret = sscanf (str, "%d", &hour);
+    if (ret != 1 || hour < 0)
+      goto error;
+    pos = strcspn (str, ":");
+    str += (pos + 1);
+    GST_TRACE (" - hour %d", hour);
+    /* parse minute */
+    ret = sscanf (str, "%d", &minute);
+    if (ret != 1 || minute < 0)
+      goto error;
+    pos = strcspn (str, ":");
+    str += (pos + 1);
+    GST_TRACE (" - minute %d", minute);
+    /* parse second */
+    ret = sscanf (str, "%lf", &second);
+    if (ret != 1 || second < 0)
+      goto error;
+    GST_TRACE (" - second %lf", second);
+
+    GST_LOG (" - %s: %4d/%02d/%02d %02d:%02d:%09.6lf", property_name,
+        year, month, day, hour, minute, second);
+
+    if (strrchr (str, '+') || strrchr (str, '-')) {
+      /* reuse some code from gst-plugins-base/gst-libs/gst/tag/gstxmptag.c */
+      gint gmt_offset = -1;
+      gchar *plus_pos = NULL;
+      gchar *neg_pos = NULL;
+      gchar *pos = NULL;
+
+      GST_LOG ("Checking for timezone information");
+
+      /* check if there is timezone info */
+      plus_pos = strrchr (str, '+');
+      neg_pos = strrchr (str, '-');
+      if (plus_pos)
+        pos = plus_pos + 1;
+      else if (neg_pos)
+        pos = neg_pos + 1;
+
+      if (pos && strlen (pos) >= 3) {
+        gint ret_tz;
+        if (pos[2] == ':')
+          ret_tz = sscanf (pos, "%d:%d", &gmt_offset_hour, &gmt_offset_min);
+        else
+          ret_tz = sscanf (pos, "%02d%02d", &gmt_offset_hour, &gmt_offset_min);
+
+        GST_DEBUG ("Parsing timezone: %s", pos);
+
+        if (ret_tz == 2) {
+          if (neg_pos != NULL && neg_pos + 1 == pos) {
+            gmt_offset_hour *= -1;
+            gmt_offset_min *= -1;
+          }
+          gmt_offset = gmt_offset_hour * 60 + gmt_offset_min;
+
+          tzoffset = gmt_offset / 60.0;
+
+          GST_LOG ("Timezone offset: %f (%d minutes)", tzoffset, gmt_offset);
+        } else
+          GST_WARNING ("Failed to parse timezone information");
+      }
+    }
+
+    exists = TRUE;
+    *property_value =
+        gst_date_time_new (tzoffset, year, month, day, hour, minute, second);
+    xmlFree (prop_string);
+  }
+
+  return exists;
+
+error:
+  GST_WARNING ("failed to parse property %s from xml string %s", property_name,
+      prop_string);
+  xmlFree (prop_string);
+  return FALSE;
+}
+
+gboolean
+gst_xml_helper_get_prop_duration (xmlNode * a_node,
+    const gchar * property_name, guint64 default_value,
+    guint64 * property_value)
+{
+  xmlChar *prop_string;
+  gchar *str;
+  gboolean exists = FALSE;
+
+  *property_value = default_value;
+  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
+  if (prop_string) {
+    str = (gchar *) prop_string;
+    if (!_mpd_helper_parse_duration (str, property_value))
+      goto error;
+    GST_LOG (" - %s: %" G_GUINT64_FORMAT, property_name, *property_value);
+    xmlFree (prop_string);
+    exists = TRUE;
+  }
+  return exists;
+
+error:
+  xmlFree (prop_string);
+  return FALSE;
+}
+
+gboolean
+gst_xml_helper_get_node_content (xmlNode * a_node, gchar ** content)
+{
+  xmlChar *node_content = NULL;
+  gboolean exists = FALSE;
+
+  node_content = xmlNodeGetContent (a_node);
+  if (node_content) {
+    exists = TRUE;
+    *content = (gchar *) node_content;
+    GST_LOG (" - %s: %s", a_node->name, *content);
+  }
+
+  return exists;
+}
+
+gboolean
+gst_xml_helper_get_node_as_string (xmlNode * a_node, gchar ** content)
+{
+  gboolean exists = FALSE;
+  const char *txt_encoding;
+  xmlOutputBufferPtr out_buf;
+
+  txt_encoding = (const char *) a_node->doc->encoding;
+  out_buf = xmlAllocOutputBuffer (NULL);
+  g_assert (out_buf != NULL);
+  xmlNodeDumpOutput (out_buf, a_node->doc, a_node, 0, 0, txt_encoding);
+  xmlOutputBufferFlush (out_buf);
+#ifdef LIBXML2_NEW_BUFFER
+  if (xmlOutputBufferGetSize (out_buf) > 0) {
+    *content =
+        (gchar *) xmlStrndup (xmlOutputBufferGetContent (out_buf),
+        xmlOutputBufferGetSize (out_buf));
+    exists = TRUE;
+  }
+#else
+  if (out_buf->conv && out_buf->conv->use > 0) {
+    *content =
+        (gchar *) xmlStrndup (out_buf->conv->content, out_buf->conv->use);
+    exists = TRUE;
+  } else if (out_buf->buffer && out_buf->buffer->use > 0) {
+    *content =
+        (gchar *) xmlStrndup (out_buf->buffer->content, out_buf->buffer->use);
+    exists = TRUE;
+  }
+#endif // LIBXML2_NEW_BUFFER
+  (void) xmlOutputBufferClose (out_buf);
+
+  if (exists) {
+    GST_LOG (" - %s: %s", a_node->name, *content);
+  }
+  return exists;
+}
+
+gchar *
+gst_xml_helper_get_node_namespace (xmlNode * a_node, const gchar * prefix)
+{
+  xmlNs *curr_ns;
+  gchar *namespace = NULL;
+
+  if (prefix == NULL) {
+    /* return the default namespace */
+    if (a_node->ns) {
+      namespace = xmlMemStrdup ((const gchar *) a_node->ns->href);
+      if (namespace) {
+        GST_LOG (" - default namespace: %s", namespace);
+      }
+    }
+  } else {
+    /* look for the specified prefix in the namespace list */
+    for (curr_ns = a_node->ns; curr_ns; curr_ns = curr_ns->next) {
+      if (xmlStrcmp (curr_ns->prefix, (xmlChar *) prefix) == 0) {
+        namespace = xmlMemStrdup ((const gchar *) curr_ns->href);
+        if (namespace) {
+          GST_LOG (" - %s namespace: %s", curr_ns->prefix, curr_ns->href);
+        }
+      }
+    }
+  }
+
+  return namespace;
+}
+
+gboolean
+gst_xml_helper_get_prop_string_stripped (xmlNode * a_node,
+    const gchar * property_name, gchar ** property_value)
+{
+  gboolean ret;
+  ret = gst_xml_helper_get_prop_string (a_node, property_name, property_value);
+  if (ret)
+    *property_value = g_strstrip (*property_value);
+  return ret;
+}
+
+gboolean
+gst_xml_helper_get_prop_string_no_whitespace (xmlNode * a_node,
+    const gchar * property_name, gchar ** property_value)
+{
+  return gst_xml_helper_get_prop_validated_string (a_node, property_name,
+      property_value, _mpd_helper_validate_no_whitespace);
+}
diff --git a/ext/dash/gstxmlhelper.h b/ext/dash/gstxmlhelper.h
new file mode 100644 (file)
index 0000000..ce9de9a
--- /dev/null
@@ -0,0 +1,113 @@
+/* GStreamer
+ *
+ * Copyright (C) 2019 Collabora Ltd.
+ *   Author: Stéphane Cerveau <scerveau@collabora.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library (COPYING); if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef __GST_XMLHELPER_H__
+#define __GST_XMLHELPER_H__
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GstXMLRange                  GstXMLRange;
+typedef struct _GstXMLRatio                  GstXMLRatio;
+typedef struct _GstXMLFrameRate              GstXMLFrameRate;
+typedef struct _GstXMLConditionalUintType    GstXMLConditionalUintType;
+
+struct _GstXMLRange
+{
+  guint64 first_byte_pos;
+  guint64 last_byte_pos;
+};
+
+struct _GstXMLRatio
+{
+  guint num;
+  guint den;
+};
+
+struct _GstXMLFrameRate
+{
+  guint num;
+  guint den;
+};
+
+struct _GstXMLConditionalUintType
+{
+  gboolean flag;
+  guint value;
+};
+
+GstXMLRange *gst_xml_helper_clone_range (GstXMLRange * range);
+
+/* XML property get method */
+gboolean gst_xml_helper_get_prop_validated_string (xmlNode * a_node,
+    const gchar * property_name, gchar ** property_value,
+    gboolean (*validator) (const char *));
+gboolean gst_xml_helper_get_prop_string (xmlNode * a_node,
+    const gchar * property_name, gchar ** property_value);
+gboolean gst_xml_helper_get_prop_string_stripped (xmlNode * a_node,
+    const gchar * property_name, gchar ** property_value);
+gboolean gst_xml_helper_get_ns_prop_string (xmlNode * a_node,
+    const gchar * ns_name, const gchar * property_name,
+    gchar ** property_value);
+gboolean gst_xml_helper_get_prop_string_vector_type (xmlNode * a_node,
+    const gchar * property_name, gchar *** property_value);
+gboolean gst_xml_helper_get_prop_signed_integer (xmlNode * a_node,
+    const gchar * property_name, gint default_val, gint * property_value);
+gboolean gst_xml_helper_get_prop_unsigned_integer (xmlNode * a_node,
+    const gchar * property_name, guint default_val, guint * property_value);
+gboolean gst_xml_helper_get_prop_unsigned_integer_64 (xmlNode *
+    a_node, const gchar * property_name, guint64 default_val,
+    guint64 * property_value);
+gboolean gst_xml_helper_get_prop_uint_vector_type (xmlNode * a_node,
+    const gchar * property_name, guint ** property_value, guint * value_size);
+gboolean gst_xml_helper_get_prop_double (xmlNode * a_node,
+    const gchar * property_name, gdouble * property_value);
+gboolean gst_xml_helper_get_prop_boolean (xmlNode * a_node,
+    const gchar * property_name, gboolean default_val,
+    gboolean * property_value);
+gboolean gst_xml_helper_get_prop_range (xmlNode * a_node,
+    const gchar * property_name, GstXMLRange ** property_value);
+gboolean gst_xml_helper_get_prop_ratio (xmlNode * a_node,
+    const gchar * property_name, GstXMLRatio ** property_value);
+gboolean gst_xml_helper_get_prop_framerate (xmlNode * a_node,
+    const gchar * property_name, GstXMLFrameRate ** property_value);
+gboolean gst_xml_helper_get_prop_cond_uint (xmlNode * a_node,
+    const gchar * property_name, GstXMLConditionalUintType ** property_value);
+gboolean gst_xml_helper_get_prop_dateTime (xmlNode * a_node,
+    const gchar * property_name, GstDateTime ** property_value);
+gboolean gst_xml_helper_get_prop_duration (xmlNode * a_node,
+    const gchar * property_name, guint64 default_value,
+    guint64 * property_value);
+gboolean gst_xml_helper_get_prop_string_no_whitespace (xmlNode * a_node,
+    const gchar * property_name, gchar ** property_value);
+
+/* XML node get method */
+gboolean gst_xml_helper_get_node_content (xmlNode * a_node,
+    gchar ** content);
+gchar *gst_xml_helper_get_node_namespace (xmlNode * a_node,
+    const gchar * prefix);
+gboolean gst_xml_helper_get_node_as_string (xmlNode * a_node,
+    gchar ** content);
+
+G_END_DECLS
+#endif /* __GST_XMLHELPER_H__ */
index a6f1b5d..c3e1f09 100644 (file)
@@ -1,6 +1,9 @@
 dash_sources = [
   'gstdashdemux.c',
+  'gstxmlhelper.c',
+  'gstmpdhelper.c',
   'gstmpdparser.c',
+  'gstmpdclient.c',
   'gstplugin.c',
 ]
 
index 7b88ce5..c6c9697 100644 (file)
@@ -19,6 +19,9 @@
  */
 
 #include "../../ext/dash/gstmpdparser.c"
+#include "../../ext/dash/gstxmlhelper.c"
+#include "../../ext/dash/gstmpdhelper.c"
+#include "../../ext/dash/gstmpdclient.c"
 #undef GST_CAT_DEFAULT
 
 #include <gst/check/gstcheck.h>
@@ -30,7 +33,7 @@ GST_DEBUG_CATEGORY (gst_dash_demux_debug);
  * year, month, day, hour, minute, second, millisecond
  *
  * This function must use the same conversion algorithm implemented in
- * gst_mpdparser_get_xml_prop_duration from gstmpdparser.c file.
+ * gst_xml_helper_get_prop_duration from gstmpdparser.c file.
  */
 static guint64
 duration_to_ms (guint year, guint month, guint day, guint hour, guint minute,
@@ -66,7 +69,7 @@ GST_START_TEST (dash_mpdparser_validsimplempd)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   /* check that unset elements with default values are properly configured */
@@ -107,7 +110,7 @@ GST_START_TEST (dash_mpdparser_mpd)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   assert_equals_string (mpdclient->mpd_node->default_namespace,
@@ -186,7 +189,7 @@ GST_START_TEST (dash_mpdparser_programInformation)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   program =
@@ -219,7 +222,7 @@ GST_START_TEST (dash_mpdparser_baseURL)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   baseURL = (GstBaseURL *) mpdclient->mpd_node->BaseURLs->data;
@@ -248,7 +251,7 @@ GST_START_TEST (dash_mpdparser_location)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   location = (gchar *) mpdclient->mpd_node->Locations->data;
@@ -275,7 +278,7 @@ GST_START_TEST (dash_mpdparser_metrics)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   metricsNode = (GstMetricsNode *) mpdclient->mpd_node->Metrics->data;
@@ -306,7 +309,7 @@ GST_START_TEST (dash_mpdparser_metrics_range)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   metricsNode = (GstMetricsNode *) mpdclient->mpd_node->Metrics->data;
@@ -338,7 +341,7 @@ GST_START_TEST (dash_mpdparser_metrics_reporting)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   metricsNode = (GstMetricsNode *) mpdclient->mpd_node->Metrics->data;
@@ -368,7 +371,7 @@ GST_START_TEST (dash_mpdparser_period)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -404,7 +407,7 @@ GST_START_TEST (dash_mpdparser_period_baseURL)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -440,7 +443,7 @@ GST_START_TEST (dash_mpdparser_period_segmentBase)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -478,7 +481,7 @@ GST_START_TEST (dash_mpdparser_period_segmentBase_initialization)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -515,7 +518,7 @@ GST_START_TEST (dash_mpdparser_period_segmentBase_representationIndex)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -547,7 +550,7 @@ GST_START_TEST (dash_mpdparser_period_segmentList)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -580,7 +583,7 @@ GST_START_TEST (dash_mpdparser_period_segmentList_multipleSegmentBaseType)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -620,7 +623,7 @@ GST_START_TEST
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -661,7 +664,7 @@ GST_START_TEST
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -700,7 +703,7 @@ GST_START_TEST
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -741,7 +744,7 @@ GST_START_TEST
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -781,7 +784,7 @@ GST_START_TEST (dash_mpdparser_period_segmentList_segmentURL)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -822,7 +825,7 @@ GST_START_TEST (dash_mpdparser_period_segmentTemplate)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -878,7 +881,7 @@ GST_START_TEST (dash_mpdparser_period_segmentTemplateWithPresentationTimeOffset)
   GstSegmentTemplateNode *segmentTemplate;
 
   mpdclient = gst_mpd_client_new ();
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   ret =
@@ -899,7 +902,7 @@ GST_START_TEST (dash_mpdparser_period_segmentTemplateWithPresentationTimeOffset)
   fail_if (adapt_set == NULL);
   ret = gst_mpd_client_setup_streaming (mpdclient, adapt_set);
   assert_equals_int (ret, TRUE);
-  activeStream = gst_mpdparser_get_active_stream_by_index (mpdclient, 0);
+  activeStream = gst_mpd_client_get_active_stream_by_index (mpdclient, 0);
   fail_if (activeStream == NULL);
 
   segmentTemplate = adapt_set->SegmentTemplate;
@@ -950,7 +953,7 @@ GST_START_TEST (dash_mpdparser_period_segmentTemplate_multipleSegmentBaseType)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -990,7 +993,7 @@ GST_START_TEST
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -1031,7 +1034,7 @@ GST_START_TEST
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -1070,7 +1073,7 @@ GST_START_TEST
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -1111,7 +1114,7 @@ GST_START_TEST
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -1162,7 +1165,7 @@ GST_START_TEST (dash_mpdparser_period_adaptationSet)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -1232,7 +1235,7 @@ GST_START_TEST (dash_mpdparser_period_adaptationSet_representationBase)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -1285,7 +1288,7 @@ GST_START_TEST
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -1324,7 +1327,7 @@ GST_START_TEST
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -1364,7 +1367,7 @@ GST_START_TEST
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -1410,7 +1413,7 @@ GST_START_TEST (dash_mpdparser_contentProtection_no_value)
   GstMpdClient *mpdclient = gst_mpd_client_new ();
   gchar *str;
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -1469,7 +1472,7 @@ GST_START_TEST (dash_mpdparser_contentProtection_no_value_no_encoding)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -1509,7 +1512,7 @@ GST_START_TEST (dash_mpdparser_period_adaptationSet_accessibility)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -1545,7 +1548,7 @@ GST_START_TEST (dash_mpdparser_period_adaptationSet_role)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -1581,7 +1584,7 @@ GST_START_TEST (dash_mpdparser_period_adaptationSet_rating)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -1617,7 +1620,7 @@ GST_START_TEST (dash_mpdparser_period_adaptationSet_viewpoint)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -1655,7 +1658,7 @@ GST_START_TEST (dash_mpdparser_period_adaptationSet_contentComponent)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -1698,7 +1701,7 @@ GST_START_TEST
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -1738,7 +1741,7 @@ GST_START_TEST (dash_mpdparser_period_adaptationSet_contentComponent_role)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -1779,7 +1782,7 @@ GST_START_TEST (dash_mpdparser_period_adaptationSet_contentComponent_rating)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -1820,7 +1823,7 @@ GST_START_TEST (dash_mpdparser_period_adaptationSet_contentComponent_viewpoint)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -1858,7 +1861,7 @@ GST_START_TEST (dash_mpdparser_period_adaptationSet_baseURL)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -1897,7 +1900,7 @@ GST_START_TEST (dash_mpdparser_period_adaptationSet_segmentBase)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -1938,7 +1941,7 @@ GST_START_TEST (dash_mpdparser_period_adaptationSet_segmentBase_initialization)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -1979,7 +1982,7 @@ GST_START_TEST
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -2015,7 +2018,7 @@ GST_START_TEST (dash_mpdparser_period_adaptationSet_segmentList)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -2053,7 +2056,7 @@ GST_START_TEST (dash_mpdparser_period_adaptationSet_segmentTemplate)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -2096,7 +2099,7 @@ GST_START_TEST
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -2142,7 +2145,7 @@ GST_START_TEST
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -2184,7 +2187,7 @@ GST_START_TEST (dash_mpdparser_adapt_repr_segmentTemplate_inherit)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -2233,7 +2236,7 @@ GST_START_TEST (dash_mpdparser_period_adaptationSet_segmentTemplate_inherit)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -2276,7 +2279,7 @@ GST_START_TEST (dash_mpdparser_period_adaptationSet_representation)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -2319,7 +2322,7 @@ GST_START_TEST
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -2359,7 +2362,7 @@ GST_START_TEST (dash_mpdparser_period_adaptationSet_representation_baseURL)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -2403,7 +2406,7 @@ GST_START_TEST
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -2453,7 +2456,7 @@ GST_START_TEST
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -2495,7 +2498,7 @@ GST_START_TEST (dash_mpdparser_period_adaptationSet_representation_segmentBase)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -2534,7 +2537,7 @@ GST_START_TEST (dash_mpdparser_period_adaptationSet_representation_segmentList)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -2573,7 +2576,7 @@ GST_START_TEST
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -2605,7 +2608,7 @@ GST_START_TEST (dash_mpdparser_period_subset)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -2639,7 +2642,7 @@ GST_START_TEST (dash_mpdparser_utctiming)
   GstMPDUTCTimingType selected_method;
   gchar **urls;
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
 
   assert_equals_int (ret, TRUE);
   fail_if (mpdclient->mpd_node == NULL);
@@ -2708,7 +2711,7 @@ GST_START_TEST (dash_mpdparser_utctiming_invalid_value)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
 
   assert_equals_int (ret, TRUE);
   fail_if (mpdclient->mpd_node == NULL);
@@ -2734,7 +2737,7 @@ GST_START_TEST (dash_mpdparser_type_dynamic)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   isLive = gst_mpd_client_is_live (mpdclient);
@@ -2827,7 +2830,7 @@ GST_START_TEST (dash_mpdparser_isoff_ondemand_profile)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   hasOnDemandProfile = gst_mpd_client_has_isoff_ondemand_profile (mpdclient);
@@ -2915,7 +2918,7 @@ GST_START_TEST (dash_mpdparser_bitstreamSwitching_inheritance)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   /* process the xml data */
@@ -2940,11 +2943,11 @@ GST_START_TEST (dash_mpdparser_bitstreamSwitching_inheritance)
   assert_equals_int (ret, TRUE);
 
   /* 2 active streams */
-  activeStreams = gst_mpdparser_get_nb_active_stream (mpdclient);
+  activeStreams = gst_mpd_client_get_nb_active_stream (mpdclient);
   assert_equals_int (activeStreams, 2);
 
   /* get details of the first active stream */
-  activeStream = gst_mpdparser_get_active_stream_by_index (mpdclient, 0);
+  activeStream = gst_mpd_client_get_active_stream_by_index (mpdclient, 0);
   fail_if (activeStream == NULL);
 
   assert_equals_int (activeStream->mimeType, GST_STREAM_VIDEO);
@@ -2960,7 +2963,7 @@ GST_START_TEST (dash_mpdparser_bitstreamSwitching_inheritance)
   assert_equals_int (bitstreamSwitchingFlag, TRUE);
 
   /* get details of the second active stream */
-  activeStream = gst_mpdparser_get_active_stream_by_index (mpdclient, 1);
+  activeStream = gst_mpd_client_get_active_stream_by_index (mpdclient, 1);
   fail_if (activeStream == NULL);
 
   assert_equals_int (activeStream->mimeType, GST_STREAM_AUDIO);
@@ -3006,7 +3009,7 @@ GST_START_TEST (dash_mpdparser_various_duration_formats)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   ret =
@@ -3083,7 +3086,7 @@ GST_START_TEST (dash_mpdparser_setup_media_presentation)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   /* process the xml data */
@@ -3120,7 +3123,7 @@ GST_START_TEST (dash_mpdparser_setup_streaming)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   /* process the xml data */
@@ -3165,7 +3168,7 @@ GST_START_TEST (dash_mpdparser_period_selection)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   /* period_idx should be 0 and we should have no active periods */
@@ -3237,7 +3240,7 @@ GST_START_TEST (dash_mpdparser_get_period_at_time)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   /* process the xml data */
@@ -3306,7 +3309,7 @@ GST_START_TEST (dash_mpdparser_adaptationSet_handling)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   /* process the xml data */
@@ -3319,13 +3322,13 @@ GST_START_TEST (dash_mpdparser_adaptationSet_handling)
   fail_unless (mpdclient->periods != NULL);
   periodName = gst_mpd_client_get_period_id (mpdclient);
   assert_equals_string (periodName, "Period0");
-  adaptation_sets_count = gst_mpdparser_get_nb_adaptationSet (mpdclient);
+  adaptation_sets_count = gst_mpd_client_get_nb_adaptationSet (mpdclient);
   assert_equals_int (adaptation_sets_count, 1);
 
   /* period1 has 2 adaptation set */
   ret = gst_mpd_client_set_period_id (mpdclient, "Period1");
   assert_equals_int (ret, TRUE);
-  adaptation_sets_count = gst_mpdparser_get_nb_adaptationSet (mpdclient);
+  adaptation_sets_count = gst_mpd_client_get_nb_adaptationSet (mpdclient);
   assert_equals_int (adaptation_sets_count, 2);
 
   /* check the id for the 2 adaptation sets from period 1 */
@@ -3370,7 +3373,7 @@ GST_START_TEST (dash_mpdparser_representation_selection)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   /* process the xml data */
@@ -3390,26 +3393,26 @@ GST_START_TEST (dash_mpdparser_representation_selection)
   fail_if (representations == NULL);
 
   represendationIndex =
-      gst_mpdparser_get_rep_idx_with_min_bandwidth (representations);
+      gst_mpd_client_get_rep_idx_with_min_bandwidth (representations);
   assert_equals_int (represendationIndex, 1);
 
   represendationIndex =
-      gst_mpdparser_get_rep_idx_with_max_bandwidth (representations, 0, 0, 0, 0,
-      1);
+      gst_mpd_client_get_rep_idx_with_max_bandwidth (representations, 0, 0, 0,
+      0, 1);
   assert_equals_int (represendationIndex, 1);
 
   represendationIndex =
-      gst_mpdparser_get_rep_idx_with_max_bandwidth (representations, 100000, 0,
+      gst_mpd_client_get_rep_idx_with_max_bandwidth (representations, 100000, 0,
       0, 0, 1);
   assert_equals_int (represendationIndex, -1);
 
   represendationIndex =
-      gst_mpdparser_get_rep_idx_with_max_bandwidth (representations, 300000, 0,
+      gst_mpd_client_get_rep_idx_with_max_bandwidth (representations, 300000, 0,
       0, 0, 1);
   assert_equals_int (represendationIndex, 1);
 
   represendationIndex =
-      gst_mpdparser_get_rep_idx_with_max_bandwidth (representations, 500000, 0,
+      gst_mpd_client_get_rep_idx_with_max_bandwidth (representations, 500000, 0,
       0, 0, 1);
   assert_equals_int (represendationIndex, 0);
 
@@ -3449,7 +3452,7 @@ GST_START_TEST (dash_mpdparser_activeStream_selection)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   /* process the xml data */
@@ -3463,7 +3466,7 @@ GST_START_TEST (dash_mpdparser_activeStream_selection)
   fail_if (adaptationSets == NULL);
 
   /* no active streams yet */
-  activeStreams = gst_mpdparser_get_nb_active_stream (mpdclient);
+  activeStreams = gst_mpd_client_get_nb_active_stream (mpdclient);
   assert_equals_int (activeStreams, 0);
 
   /* setup streaming from the first adaptation set */
@@ -3473,7 +3476,7 @@ GST_START_TEST (dash_mpdparser_activeStream_selection)
   assert_equals_int (ret, TRUE);
 
   /* 1 active streams */
-  activeStreams = gst_mpdparser_get_nb_active_stream (mpdclient);
+  activeStreams = gst_mpd_client_get_nb_active_stream (mpdclient);
   assert_equals_int (activeStreams, 1);
 
   /* setup streaming from the second adaptation set */
@@ -3483,7 +3486,7 @@ GST_START_TEST (dash_mpdparser_activeStream_selection)
   assert_equals_int (ret, TRUE);
 
   /* 2 active streams */
-  activeStreams = gst_mpdparser_get_nb_active_stream (mpdclient);
+  activeStreams = gst_mpd_client_get_nb_active_stream (mpdclient);
   assert_equals_int (activeStreams, 2);
 
   /* setup streaming from the third adaptation set */
@@ -3493,21 +3496,21 @@ GST_START_TEST (dash_mpdparser_activeStream_selection)
   assert_equals_int (ret, TRUE);
 
   /* 3 active streams */
-  activeStreams = gst_mpdparser_get_nb_active_stream (mpdclient);
+  activeStreams = gst_mpd_client_get_nb_active_stream (mpdclient);
   assert_equals_int (activeStreams, 3);
 
   /* get details of the first active stream */
-  activeStream = gst_mpdparser_get_active_stream_by_index (mpdclient, 0);
+  activeStream = gst_mpd_client_get_active_stream_by_index (mpdclient, 0);
   fail_if (activeStream == NULL);
   assert_equals_int (activeStream->mimeType, GST_STREAM_VIDEO);
 
   /* get details of the second active stream */
-  activeStream = gst_mpdparser_get_active_stream_by_index (mpdclient, 1);
+  activeStream = gst_mpd_client_get_active_stream_by_index (mpdclient, 1);
   fail_if (activeStream == NULL);
   assert_equals_int (activeStream->mimeType, GST_STREAM_AUDIO);
 
   /* get details of the third active stream */
-  activeStream = gst_mpdparser_get_active_stream_by_index (mpdclient, 2);
+  activeStream = gst_mpd_client_get_active_stream_by_index (mpdclient, 2);
   fail_if (activeStream == NULL);
   assert_equals_int (activeStream->mimeType, GST_STREAM_APPLICATION);
 
@@ -3552,7 +3555,7 @@ GST_START_TEST (dash_mpdparser_activeStream_parameters)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   /* process the xml data */
@@ -3572,11 +3575,11 @@ GST_START_TEST (dash_mpdparser_activeStream_parameters)
   assert_equals_int (ret, TRUE);
 
   /* 1 active streams */
-  activeStreams = gst_mpdparser_get_nb_active_stream (mpdclient);
+  activeStreams = gst_mpd_client_get_nb_active_stream (mpdclient);
   assert_equals_int (activeStreams, 1);
 
   /* get details of the first active stream */
-  activeStream = gst_mpdparser_get_active_stream_by_index (mpdclient, 0);
+  activeStream = gst_mpd_client_get_active_stream_by_index (mpdclient, 0);
   fail_if (activeStream == NULL);
 
   assert_equals_int (activeStream->mimeType, GST_STREAM_VIDEO);
@@ -3642,7 +3645,7 @@ GST_START_TEST (dash_mpdparser_get_audio_languages)
   GstMpdClient *mpdclient = gst_mpd_client_new ();
   gint i;
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   /* process the xml data */
@@ -3656,18 +3659,18 @@ GST_START_TEST (dash_mpdparser_get_audio_languages)
   fail_if (adaptationSets == NULL);
 
   /* setup streaming from all adaptation sets */
-  adaptationSetsCount = gst_mpdparser_get_nb_adaptationSet (mpdclient);
+  adaptationSetsCount = gst_mpd_client_get_nb_adaptationSet (mpdclient);
   for (i = 0; i < adaptationSetsCount; i++) {
     adapt_set = (GstAdaptationSetNode *) g_list_nth_data (adaptationSets, i);
     fail_if (adapt_set == NULL);
     ret = gst_mpd_client_setup_streaming (mpdclient, adapt_set);
     assert_equals_int (ret, TRUE);
   }
-  activeStreams = gst_mpdparser_get_nb_active_stream (mpdclient);
+  activeStreams = gst_mpd_client_get_nb_active_stream (mpdclient);
   assert_equals_int (activeStreams, adaptationSetsCount);
 
   languagesCount =
-      gst_mpdparser_get_list_and_nb_of_audio_language (mpdclient, &languages);
+      gst_mpd_client_get_list_and_nb_of_audio_language (mpdclient, &languages);
   assert_equals_int (languagesCount, 2);
   assert_equals_string ((gchar *) g_list_nth_data (languages, 0), "en");
   assert_equals_string ((gchar *) g_list_nth_data (languages, 1), "fr");
@@ -3694,7 +3697,7 @@ setup_mpd_client (const gchar * xml)
   GstMpdClient *mpdclient = gst_mpd_client_new ();
   gint i;
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   /* process the xml data */
@@ -3708,14 +3711,14 @@ setup_mpd_client (const gchar * xml)
   fail_if (adaptationSets == NULL);
 
   /* setup streaming from all adaptation sets */
-  adaptationSetsCount = gst_mpdparser_get_nb_adaptationSet (mpdclient);
+  adaptationSetsCount = gst_mpd_client_get_nb_adaptationSet (mpdclient);
   for (i = 0; i < adaptationSetsCount; i++) {
     adapt_set = (GstAdaptationSetNode *) g_list_nth_data (adaptationSets, i);
     fail_if (adapt_set == NULL);
     ret = gst_mpd_client_setup_streaming (mpdclient, adapt_set);
     assert_equals_int (ret, TRUE);
   }
-  activeStreams = gst_mpdparser_get_nb_active_stream (mpdclient);
+  activeStreams = gst_mpd_client_get_nb_active_stream (mpdclient);
   assert_equals_int (activeStreams, adaptationSetsCount);
 
   return mpdclient;
@@ -3736,7 +3739,7 @@ GST_START_TEST (dash_mpdparser_get_baseURL1)
 
   GstMpdClient *mpdclient = setup_mpd_client (xml);
 
-  baseURL = gst_mpdparser_get_baseURL (mpdclient, 0);
+  baseURL = gst_mpd_client_get_baseURL (mpdclient, 0);
   fail_if (baseURL == NULL);
   assert_equals_string (baseURL, "http://example.com/");
 
@@ -3778,7 +3781,7 @@ GST_START_TEST (dash_mpdparser_get_baseURL2)
    * value does not end in /, everything after the last / will be overwritten.
    * baseURL becomes "/period_base_url/representation_base_url"
    */
-  baseURL = gst_mpdparser_get_baseURL (mpdclient, 0);
+  baseURL = gst_mpd_client_get_baseURL (mpdclient, 0);
   fail_if (baseURL == NULL);
   assert_equals_string (baseURL, "/period_base_url/representation_base_url");
 
@@ -3818,7 +3821,7 @@ GST_START_TEST (dash_mpdparser_get_baseURL3)
    *  - update the value with BaseURL element from Representation. Because this
    * is an absolute url, it will replace everything again"
    */
-  baseURL = gst_mpdparser_get_baseURL (mpdclient, 0);
+  baseURL = gst_mpd_client_get_baseURL (mpdclient, 0);
   fail_if (baseURL == NULL);
   assert_equals_string (baseURL, "/representation_base_url");
 
@@ -3858,7 +3861,7 @@ GST_START_TEST (dash_mpdparser_get_baseURL4)
    *  - update the value with BaseURL element from Representation. Because this
    * is an relative url, it will update the current value."
    */
-  baseURL = gst_mpdparser_get_baseURL (mpdclient, 0);
+  baseURL = gst_mpd_client_get_baseURL (mpdclient, 0);
   fail_if (baseURL == NULL);
   assert_equals_string (baseURL,
       "/period_base_url/adaptation_base_url/representation_base_url/");
@@ -3954,7 +3957,7 @@ GST_START_TEST (dash_mpdparser_get_baseURL5)
    *  - update the value with BaseURL element from Representation. Because this
    * is an relative url, it will update the current value."
    */
-  baseURL = gst_mpdparser_get_baseURL (mpdclient, 0);
+  baseURL = gst_mpd_client_get_baseURL (mpdclient, 0);
   fail_if (baseURL == NULL);
   assert_equals_string (baseURL,
       "/mpd_base_url1/period_base_url1/adaptation_base_url1/representation_base_url1/");
@@ -3979,7 +3982,7 @@ GST_START_TEST (dash_mpdparser_get_baseURL6)
 
   GstMpdClient *mpdclient = setup_mpd_client (xml);
 
-  baseURL = gst_mpdparser_get_baseURL (mpdclient, 0);
+  baseURL = gst_mpd_client_get_baseURL (mpdclient, 0);
   fail_if (baseURL == NULL);
   assert_equals_string (baseURL, "");
 
@@ -4006,7 +4009,7 @@ GST_START_TEST (dash_mpdparser_get_baseURL7)
 
   mpdclient = setup_mpd_client (xml);
 
-  baseURL = gst_mpdparser_get_baseURL (mpdclient, 0);
+  baseURL = gst_mpd_client_get_baseURL (mpdclient, 0);
   fail_if (baseURL == NULL);
   assert_equals_string (baseURL, "/x/example.com/");
 
@@ -4032,7 +4035,7 @@ GST_START_TEST (dash_mpdparser_get_baseURL8)
 
   GstMpdClient *mpdclient = setup_mpd_client (xml);
 
-  baseURL = gst_mpdparser_get_baseURL (mpdclient, 0);
+  baseURL = gst_mpd_client_get_baseURL (mpdclient, 0);
   fail_if (baseURL == NULL);
   assert_equals_string (baseURL, "x:y/example.com/");
 
@@ -4058,7 +4061,7 @@ GST_START_TEST (dash_mpdparser_get_mediaPresentationDuration)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   mediaPresentationDuration =
@@ -4094,7 +4097,7 @@ GST_START_TEST (dash_mpdparser_get_streamPresentationOffset)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   /* process the xml data */
@@ -4114,7 +4117,7 @@ GST_START_TEST (dash_mpdparser_get_streamPresentationOffset)
   assert_equals_int (ret, TRUE);
 
   /* test the stream presentation time offset */
-  offset = gst_mpd_parser_get_stream_presentation_offset (mpdclient, 0);
+  offset = gst_mpd_client_get_stream_presentation_offset (mpdclient, 0);
   /* seems to be set only for template segments, so here it is 0 */
   assert_equals_int (offset, 0);
 
@@ -4165,7 +4168,7 @@ GST_START_TEST (dash_mpdparser_segments)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   /* process the xml data */
@@ -4184,7 +4187,7 @@ GST_START_TEST (dash_mpdparser_segments)
   ret = gst_mpd_client_setup_streaming (mpdclient, adapt_set);
   assert_equals_int (ret, TRUE);
 
-  activeStream = gst_mpdparser_get_active_stream_by_index (mpdclient, 0);
+  activeStream = gst_mpd_client_get_active_stream_by_index (mpdclient, 0);
   fail_if (activeStream == NULL);
 
   /* segment_index 0, segment_count 2.
@@ -4300,7 +4303,7 @@ GST_START_TEST (dash_mpdparser_headers)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   /* process the xml data */
@@ -4376,7 +4379,7 @@ GST_START_TEST (dash_mpdparser_fragments)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   /* process the xml data */
@@ -4394,7 +4397,7 @@ GST_START_TEST (dash_mpdparser_fragments)
   fail_if (adapt_set == NULL);
   ret = gst_mpd_client_setup_streaming (mpdclient, adapt_set);
   assert_equals_int (ret, TRUE);
-  activeStream = gst_mpdparser_get_active_stream_by_index (mpdclient, 0);
+  activeStream = gst_mpd_client_get_active_stream_by_index (mpdclient, 0);
   fail_if (activeStream == NULL);
 
   /* expected duration of the next fragment */
@@ -4411,7 +4414,7 @@ GST_START_TEST (dash_mpdparser_fragments)
   assert_equals_uint64 (fragment.timestamp, expectedTimestamp * GST_MSECOND);
   gst_media_fragment_info_clear (&fragment);
 
-  periodStartTime = gst_mpd_parser_get_period_start_time (mpdclient);
+  periodStartTime = gst_mpd_client_get_period_start_time (mpdclient);
   assert_equals_uint64 (periodStartTime, 10 * GST_SECOND);
 
   nextFragmentDuration =
@@ -4462,7 +4465,7 @@ GST_START_TEST (dash_mpdparser_inherited_segmentBase)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -4525,7 +4528,7 @@ GST_START_TEST (dash_mpdparser_inherited_segmentURL)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   /* process the xml data */
@@ -4544,7 +4547,7 @@ GST_START_TEST (dash_mpdparser_inherited_segmentURL)
   ret = gst_mpd_client_setup_streaming (mpdclient, adapt_set);
   assert_equals_int (ret, TRUE);
 
-  activeStream = gst_mpdparser_get_active_stream_by_index (mpdclient, 0);
+  activeStream = gst_mpd_client_get_active_stream_by_index (mpdclient, 0);
   fail_if (activeStream == NULL);
 
   /* expected duration of the next fragment
@@ -4611,7 +4614,7 @@ GST_START_TEST (dash_mpdparser_segment_list)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   /* process the xml data */
@@ -4630,7 +4633,7 @@ GST_START_TEST (dash_mpdparser_segment_list)
   ret = gst_mpd_client_setup_streaming (mpdclient, adapt_set);
   assert_equals_int (ret, TRUE);
 
-  activeStream = gst_mpdparser_get_active_stream_by_index (mpdclient, 0);
+  activeStream = gst_mpd_client_get_active_stream_by_index (mpdclient, 0);
   fail_if (activeStream == NULL);
 
   /* expected duration of the next fragment
@@ -4692,7 +4695,7 @@ GST_START_TEST (dash_mpdparser_segment_template)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   /* process the xml data */
@@ -4711,7 +4714,7 @@ GST_START_TEST (dash_mpdparser_segment_template)
   ret = gst_mpd_client_setup_streaming (mpdclient, adapt_set);
   assert_equals_int (ret, TRUE);
 
-  activeStream = gst_mpdparser_get_active_stream_by_index (mpdclient, 0);
+  activeStream = gst_mpd_client_get_active_stream_by_index (mpdclient, 0);
   fail_if (activeStream == NULL);
 
   /* expected duration of the next fragment
@@ -4740,10 +4743,10 @@ GST_START_TEST (dash_mpdparser_segment_template)
   assert_equals_uint64 (fragment.duration, expectedDuration * GST_MSECOND);
   assert_equals_uint64 (fragment.timestamp, expectedTimestamp * GST_MSECOND);
 
-  periodStartTime = gst_mpd_parser_get_period_start_time (mpdclient);
+  periodStartTime = gst_mpd_client_get_period_start_time (mpdclient);
   assert_equals_uint64 (periodStartTime, 10 * GST_SECOND);
 
-  offset = gst_mpd_parser_get_stream_presentation_offset (mpdclient, 0);
+  offset = gst_mpd_client_get_stream_presentation_offset (mpdclient, 0);
   assert_equals_uint64 (offset, 15 * GST_SECOND);
 
   gst_media_fragment_info_clear (&fragment);
@@ -4810,7 +4813,7 @@ GST_START_TEST (dash_mpdparser_segment_timeline)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   /* process the xml data */
@@ -4829,7 +4832,7 @@ GST_START_TEST (dash_mpdparser_segment_timeline)
   ret = gst_mpd_client_setup_streaming (mpdclient, adapt_set);
   assert_equals_int (ret, TRUE);
 
-  activeStream = gst_mpdparser_get_active_stream_by_index (mpdclient, 0);
+  activeStream = gst_mpd_client_get_active_stream_by_index (mpdclient, 0);
   fail_if (activeStream == NULL);
 
   /* expected duration of the next fragment */
@@ -4995,7 +4998,7 @@ GST_START_TEST (dash_mpdparser_multiple_inherited_segmentURL)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   /* process the xml data */
@@ -5013,7 +5016,7 @@ GST_START_TEST (dash_mpdparser_multiple_inherited_segmentURL)
   ret = gst_mpd_client_setup_streaming (mpdclient, adapt_set);
   assert_equals_int (ret, TRUE);
 
-  activeStream = gst_mpdparser_get_active_stream_by_index (mpdclient, 0);
+  activeStream = gst_mpd_client_get_active_stream_by_index (mpdclient, 0);
   fail_if (activeStream == NULL);
 
   expectedDuration = duration_to_ms (0, 0, 0, 0, 0, 8, 0);
@@ -5112,7 +5115,7 @@ GST_START_TEST (dash_mpdparser_multipleSegmentURL)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   /* process the xml data */
@@ -5131,7 +5134,7 @@ GST_START_TEST (dash_mpdparser_multipleSegmentURL)
   ret = gst_mpd_client_setup_streaming (mpdclient, adapt_set);
   assert_equals_int (ret, TRUE);
 
-  activeStream = gst_mpdparser_get_active_stream_by_index (mpdclient, 0);
+  activeStream = gst_mpd_client_get_active_stream_by_index (mpdclient, 0);
   fail_if (activeStream == NULL);
 
   expectedDuration = duration_to_ms (0, 0, 0, 0, 0, 20, 0);
@@ -5174,7 +5177,7 @@ GST_START_TEST (dash_mpdparser_missing_xml)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, FALSE);
 
   gst_mpd_client_free (mpdclient);
@@ -5193,7 +5196,7 @@ GST_START_TEST (dash_mpdparser_missing_mpd)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, FALSE);
 
   gst_mpd_client_free (mpdclient);
@@ -5214,7 +5217,7 @@ GST_START_TEST (dash_mpdparser_no_end_tag)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, FALSE);
 
   gst_mpd_client_free (mpdclient);
@@ -5234,7 +5237,7 @@ GST_START_TEST (dash_mpdparser_no_default_namespace)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, strlen (xml));
   assert_equals_int (ret, TRUE);
 
   gst_mpd_client_free (mpdclient);
@@ -5263,7 +5266,7 @@ GST_START_TEST (dash_mpdparser_wrong_period_duration_inferred_from_next_period)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   /* period_idx should be 0 and we should have no active periods */
@@ -5308,7 +5311,7 @@ GST_START_TEST
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   /* period_idx should be 0 and we should have no active periods */
@@ -5330,13 +5333,13 @@ GST_END_TEST;
 
 GST_START_TEST (dash_mpdparser_whitespace_strings)
 {
-  fail_unless (gst_mpdparser_validate_no_whitespace ("") == TRUE);
-  fail_unless (gst_mpdparser_validate_no_whitespace ("/") == TRUE);
-  fail_unless (gst_mpdparser_validate_no_whitespace (" ") == FALSE);
-  fail_unless (gst_mpdparser_validate_no_whitespace ("aaaaaaaa ") == FALSE);
-  fail_unless (gst_mpdparser_validate_no_whitespace ("a\ta") == FALSE);
-  fail_unless (gst_mpdparser_validate_no_whitespace ("a\ra") == FALSE);
-  fail_unless (gst_mpdparser_validate_no_whitespace ("a\na") == FALSE);
+  fail_unless (_mpd_helper_validate_no_whitespace ("") == TRUE);
+  fail_unless (_mpd_helper_validate_no_whitespace ("/") == TRUE);
+  fail_unless (_mpd_helper_validate_no_whitespace (" ") == FALSE);
+  fail_unless (_mpd_helper_validate_no_whitespace ("aaaaaaaa ") == FALSE);
+  fail_unless (_mpd_helper_validate_no_whitespace ("a\ta") == FALSE);
+  fail_unless (_mpd_helper_validate_no_whitespace ("a\ra") == FALSE);
+  fail_unless (_mpd_helper_validate_no_whitespace ("a\na") == FALSE);
 }
 
 GST_END_TEST;
@@ -5388,7 +5391,7 @@ GST_START_TEST (dash_mpdparser_negative_period_duration)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   /* process the xml data
@@ -5436,7 +5439,7 @@ GST_START_TEST (dash_mpdparser_read_unsigned_from_negative_values)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   periodNode = (GstPeriodNode *) mpdclient->mpd_node->Periods->data;
@@ -5494,7 +5497,7 @@ GST_START_TEST (dash_mpdparser_negative_mediaPresentationDuration)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   /* process the xml data
@@ -5521,7 +5524,7 @@ GST_START_TEST (dash_mpdparser_no_profiles)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, strlen (xml));
 
   assert_equals_int (ret, TRUE);
 
@@ -5562,7 +5565,7 @@ GST_START_TEST (dash_mpdparser_unmatched_segmentTimeline_segmentURL)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   /* process the xml data */
@@ -5605,7 +5608,7 @@ GST_START_TEST (dash_mpdparser_default_presentation_delay)
   GstMpdClient *mpdclient = gst_mpd_client_new ();
   gint64 value;
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_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);
@@ -5638,54 +5641,54 @@ GST_START_TEST (dash_mpdparser_duration)
 {
   guint64 v;
 
-  fail_unless (gst_mpdparser_parse_duration ("", &v) == FALSE);
-  fail_unless (gst_mpdparser_parse_duration (" ", &v) == FALSE);
-  fail_unless (gst_mpdparser_parse_duration ("0", &v) == FALSE);
-  fail_unless (gst_mpdparser_parse_duration ("D-1", &v) == FALSE);
-  fail_unless (gst_mpdparser_parse_duration ("T", &v) == FALSE);
-
-  fail_unless (gst_mpdparser_parse_duration ("P", &v) == TRUE);
-  fail_unless (gst_mpdparser_parse_duration ("PT", &v) == TRUE);
-  fail_unless (gst_mpdparser_parse_duration ("PX", &v) == FALSE);
-  fail_unless (gst_mpdparser_parse_duration ("PPT", &v) == FALSE);
-  fail_unless (gst_mpdparser_parse_duration ("PTT", &v) == FALSE);
-
-  fail_unless (gst_mpdparser_parse_duration ("P1D", &v) == TRUE);
-  fail_unless (gst_mpdparser_parse_duration ("P1D1D", &v) == FALSE);
-  fail_unless (gst_mpdparser_parse_duration ("P1D1M", &v) == FALSE);
-  fail_unless (gst_mpdparser_parse_duration ("P1M1D", &v) == TRUE);
-  fail_unless (gst_mpdparser_parse_duration ("P1M1D1M", &v) == FALSE);
-  fail_unless (gst_mpdparser_parse_duration ("P1M1D1D", &v) == FALSE);
-
-  fail_unless (gst_mpdparser_parse_duration ("P0M0D", &v) == TRUE);
-  fail_unless (gst_mpdparser_parse_duration ("P-1M", &v) == FALSE);
-  fail_unless (gst_mpdparser_parse_duration ("P15M", &v) == FALSE);
-  fail_unless (gst_mpdparser_parse_duration ("P-1D", &v) == FALSE);
-  fail_unless (gst_mpdparser_parse_duration ("P35D", &v) == FALSE);
-  fail_unless (gst_mpdparser_parse_duration ("P-1Y", &v) == FALSE);
-  fail_unless (gst_mpdparser_parse_duration ("PT-1H", &v) == FALSE);
-  fail_unless (gst_mpdparser_parse_duration ("PT25H", &v) == FALSE);
-  fail_unless (gst_mpdparser_parse_duration ("PT-1M", &v) == FALSE);
-  fail_unless (gst_mpdparser_parse_duration ("PT65M", &v) == FALSE);
-  fail_unless (gst_mpdparser_parse_duration ("PT-1S", &v) == FALSE);
+  fail_unless (_mpd_helper_parse_duration ("", &v) == FALSE);
+  fail_unless (_mpd_helper_parse_duration (" ", &v) == FALSE);
+  fail_unless (_mpd_helper_parse_duration ("0", &v) == FALSE);
+  fail_unless (_mpd_helper_parse_duration ("D-1", &v) == FALSE);
+  fail_unless (_mpd_helper_parse_duration ("T", &v) == FALSE);
+
+  fail_unless (_mpd_helper_parse_duration ("P", &v) == TRUE);
+  fail_unless (_mpd_helper_parse_duration ("PT", &v) == TRUE);
+  fail_unless (_mpd_helper_parse_duration ("PX", &v) == FALSE);
+  fail_unless (_mpd_helper_parse_duration ("PPT", &v) == FALSE);
+  fail_unless (_mpd_helper_parse_duration ("PTT", &v) == FALSE);
+
+  fail_unless (_mpd_helper_parse_duration ("P1D", &v) == TRUE);
+  fail_unless (_mpd_helper_parse_duration ("P1D1D", &v) == FALSE);
+  fail_unless (_mpd_helper_parse_duration ("P1D1M", &v) == FALSE);
+  fail_unless (_mpd_helper_parse_duration ("P1M1D", &v) == TRUE);
+  fail_unless (_mpd_helper_parse_duration ("P1M1D1M", &v) == FALSE);
+  fail_unless (_mpd_helper_parse_duration ("P1M1D1D", &v) == FALSE);
+
+  fail_unless (_mpd_helper_parse_duration ("P0M0D", &v) == TRUE);
+  fail_unless (_mpd_helper_parse_duration ("P-1M", &v) == FALSE);
+  fail_unless (_mpd_helper_parse_duration ("P15M", &v) == FALSE);
+  fail_unless (_mpd_helper_parse_duration ("P-1D", &v) == FALSE);
+  fail_unless (_mpd_helper_parse_duration ("P35D", &v) == FALSE);
+  fail_unless (_mpd_helper_parse_duration ("P-1Y", &v) == FALSE);
+  fail_unless (_mpd_helper_parse_duration ("PT-1H", &v) == FALSE);
+  fail_unless (_mpd_helper_parse_duration ("PT25H", &v) == FALSE);
+  fail_unless (_mpd_helper_parse_duration ("PT-1M", &v) == FALSE);
+  fail_unless (_mpd_helper_parse_duration ("PT65M", &v) == FALSE);
+  fail_unless (_mpd_helper_parse_duration ("PT-1S", &v) == FALSE);
   /* seconds are allowed to be larger than 60 */
-  fail_unless (gst_mpdparser_parse_duration ("PT65S", &v) == TRUE);
-
-  fail_unless (gst_mpdparser_parse_duration ("PT1.1H", &v) == FALSE);
-  fail_unless (gst_mpdparser_parse_duration ("PT1-1H", &v) == FALSE);
-  fail_unless (gst_mpdparser_parse_duration ("PT1-H", &v) == FALSE);
-  fail_unless (gst_mpdparser_parse_duration ("PT-H", &v) == FALSE);
-  fail_unless (gst_mpdparser_parse_duration ("PTH", &v) == FALSE);
-  fail_unless (gst_mpdparser_parse_duration ("PT0", &v) == FALSE);
-  fail_unless (gst_mpdparser_parse_duration ("PT1.1S", &v) == TRUE);
-  fail_unless (gst_mpdparser_parse_duration ("PT1.1.1S", &v) == FALSE);
-
-  fail_unless (gst_mpdparser_parse_duration ("P585Y", &v) == FALSE);
-  fail_unless (gst_mpdparser_parse_duration ("P584Y", &v) == TRUE);
-
-  fail_unless (gst_mpdparser_parse_duration (" P10DT8H", &v) == TRUE);
-  fail_unless (gst_mpdparser_parse_duration ("P10D T8H", &v) == FALSE);
-  fail_unless (gst_mpdparser_parse_duration ("P10DT8H ", &v) == TRUE);
+  fail_unless (_mpd_helper_parse_duration ("PT65S", &v) == TRUE);
+
+  fail_unless (_mpd_helper_parse_duration ("PT1.1H", &v) == FALSE);
+  fail_unless (_mpd_helper_parse_duration ("PT1-1H", &v) == FALSE);
+  fail_unless (_mpd_helper_parse_duration ("PT1-H", &v) == FALSE);
+  fail_unless (_mpd_helper_parse_duration ("PT-H", &v) == FALSE);
+  fail_unless (_mpd_helper_parse_duration ("PTH", &v) == FALSE);
+  fail_unless (_mpd_helper_parse_duration ("PT0", &v) == FALSE);
+  fail_unless (_mpd_helper_parse_duration ("PT1.1S", &v) == TRUE);
+  fail_unless (_mpd_helper_parse_duration ("PT1.1.1S", &v) == FALSE);
+
+  fail_unless (_mpd_helper_parse_duration ("P585Y", &v) == FALSE);
+  fail_unless (_mpd_helper_parse_duration ("P584Y", &v) == TRUE);
+
+  fail_unless (_mpd_helper_parse_duration (" P10DT8H", &v) == TRUE);
+  fail_unless (_mpd_helper_parse_duration ("P10D T8H", &v) == FALSE);
+  fail_unless (_mpd_helper_parse_duration ("P10DT8H ", &v) == TRUE);
 }
 
 GST_END_TEST;
@@ -5721,7 +5724,7 @@ GST_START_TEST (dash_mpdparser_maximum_segment_duration)
 
   xml = g_strdup_printf (xml_template, "maxSegmentDuration=\"PT4.5S\"");
   mpdclient = gst_mpd_client_new ();
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   g_free (xml);
   assert_equals_int (ret, TRUE);
 
@@ -5737,7 +5740,7 @@ GST_START_TEST (dash_mpdparser_maximum_segment_duration)
    */
   xml = g_strdup_printf (xml_template, "");
   mpdclient = gst_mpd_client_new ();
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   g_free (xml);
   assert_equals_int (ret, TRUE);
   ret =
@@ -5821,7 +5824,8 @@ GST_START_TEST (dash_mpdparser_xlink_period)
       xml_uri_front, (const char *) file_uri_double_period, xml_uri_rear,
       xml_frag_end, NULL);
 
-  ret = gst_mpd_parse (mpdclient, xml_joined, (gint) strlen (xml_joined));
+  ret =
+      gst_mpd_client_parse (mpdclient, xml_joined, (gint) strlen (xml_joined));
   assert_equals_int (ret, TRUE);
 
   period_list = mpdclient->mpd_node->Periods;
@@ -5892,7 +5896,7 @@ GST_START_TEST (dash_mpdparser_datetime_with_tz_offset)
   gboolean ret;
   GstMpdClient *mpdclient = gst_mpd_client_new ();
 
-  ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
+  ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml));
   assert_equals_int (ret, TRUE);
 
   availabilityStartTime = mpdclient->mpd_node->availabilityStartTime;