--- /dev/null
+/* 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;
+}
*/
#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);
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
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
{
{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;
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 */
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
*/
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 */
*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);
}
/* 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 =
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 */
*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 */
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);
}
}
}
*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);
}
*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:");
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) {
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
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 */
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)
}
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) {
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)
}
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)
{
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));
+}
*/
#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>
* 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,
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 */
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,
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 =
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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 =
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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);
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);
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);
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);
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 */
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);
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);
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 =
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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);
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 */
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 */
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 */
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 */
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);
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 */
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);
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 */
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");
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 */
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;
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/");
* 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");
* - 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");
* - 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/");
* - 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/");
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, "");
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/");
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/");
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 =
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 */
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);
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 */
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.
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 */
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 */
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 */
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 =
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;
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 */
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
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 */
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
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 */
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
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);
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 */
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 */
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 */
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);
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 */
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);
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);
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);
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);
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);
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 */
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 */
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;
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
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;
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
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);
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 */
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);
{
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;
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);
*/
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 =
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;
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;