3 * Copyright (C) 2019 Collabora Ltd.
4 * Author: Stéphane Cerveau <scerveau@collabora.com>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 #include "gstmpdclient.h"
23 #include "gstmpdparser.h"
25 GST_DEBUG_CATEGORY_STATIC (gst_dash_mpd_client_debug);
26 #undef GST_CAT_DEFAULT
27 #define GST_CAT_DEFAULT gst_dash_mpd_client_debug
29 G_DEFINE_TYPE (GstMPDClient, gst_mpd_client, GST_TYPE_OBJECT);
31 static GstMPDSegmentBaseNode *gst_mpd_client_get_segment_base (GstMPDPeriodNode
32 * Period, GstMPDAdaptationSetNode * AdaptationSet,
33 GstMPDRepresentationNode * Representation);
34 static GstMPDSegmentListNode *gst_mpd_client_get_segment_list (GstMPDClient *
35 client, GstMPDPeriodNode * Period, GstMPDAdaptationSetNode * AdaptationSet,
36 GstMPDRepresentationNode * Representation);
38 static guint gst_mpd_client_get_segments_counts (GstMPDClient * client,
39 GstActiveStream * stream);
41 static GList *gst_mpd_client_fetch_external_periods (GstMPDClient * client,
42 GstMPDPeriodNode * period_node);
43 static GList *gst_mpd_client_fetch_external_adaptation_set (GstMPDClient *
44 client, GstMPDPeriodNode * period, GstMPDAdaptationSetNode * adapt_set);
46 static GstMPDRepresentationNode *gst_mpd_client_get_lowest_representation (GList
48 static GstStreamPeriod *gst_mpd_client_get_stream_period (GstMPDClient *
51 typedef GstMPDNode *(*MpdClientStringIDFilter) (GList * list, gchar * data);
52 typedef GstMPDNode *(*MpdClientIDFilter) (GList * list, guint data);
55 gst_mpd_client_get_period_with_id (GList * periods, gchar * period_id)
57 GstMPDPeriodNode *period;
60 for (list = g_list_first (periods); list; list = g_list_next (list)) {
61 period = (GstMPDPeriodNode *) list->data;
62 if (!g_strcmp0 (period->id, period_id))
63 return GST_MPD_NODE (period);
69 gst_mpd_client_get_adaptation_set_with_id (GList * adaptation_sets, guint id)
71 GstMPDAdaptationSetNode *adaptation_set;
74 for (list = g_list_first (adaptation_sets); list; list = g_list_next (list)) {
75 adaptation_set = (GstMPDAdaptationSetNode *) list->data;
76 if (adaptation_set->id == id)
77 return GST_MPD_NODE (adaptation_set);
82 GstMPDRepresentationNode *
83 gst_mpd_client_get_representation_with_id (GList * representations,
86 GstMPDRepresentationNode *representation;
89 for (list = g_list_first (representations); list; list = g_list_next (list)) {
90 representation = (GstMPDRepresentationNode *) list->data;
91 if (!g_strcmp0 (representation->id, rep_id))
92 return GST_MPD_REPRESENTATION_NODE (representation);
98 gst_mpd_client_get_representation_with_id_filter (GList * representations,
101 GstMPDRepresentationNode *representation =
102 gst_mpd_client_get_representation_with_id (representations, rep_id);
104 if (representation != NULL)
105 return GST_MPD_NODE (representation);
111 _generate_new_string_id (GList * list, const gchar * tuple,
112 MpdClientStringIDFilter filter)
119 id = g_strdup_printf (tuple, i);
120 node = filter (list, id);
128 _generate_new_id (GList * list, MpdClientIDFilter filter)
133 node = filter (list, id);
140 static GstMPDRepresentationNode *
141 gst_mpd_client_get_lowest_representation (GList * Representations)
144 GstMPDRepresentationNode *rep = NULL;
145 GstMPDRepresentationNode *lowest = NULL;
147 if (Representations == NULL)
150 for (list = g_list_first (Representations); list; list = g_list_next (list)) {
151 rep = (GstMPDRepresentationNode *) list->data;
152 if (rep && (!lowest || rep->bandwidth < lowest->bandwidth)) {
161 static GstMPDRepresentationNode *
162 gst_mpdparser_get_highest_representation (GList * Representations)
166 if (Representations == NULL)
169 list = g_list_last (Representations);
171 return list ? (GstMPDRepresentationNode *) list->data : NULL;
174 static GstMPDRepresentationNode *
175 gst_mpdparser_get_representation_with_max_bandwidth (GList * Representations,
179 GstMPDRepresentationNode *representation, *best_rep = NULL;
181 if (Representations == NULL)
184 if (max_bandwidth <= 0) /* 0 => get highest representation available */
185 return gst_mpdparser_get_highest_representation (Representations);
187 for (list = g_list_first (Representations); list; list = g_list_next (list)) {
188 representation = (GstMPDRepresentationNode *) list->data;
189 if (representation && representation->bandwidth <= max_bandwidth) {
190 best_rep = representation;
198 static GstMPDSegmentListNode *
199 gst_mpd_client_fetch_external_segment_list (GstMPDClient * client,
200 GstMPDPeriodNode * Period,
201 GstMPDAdaptationSetNode * AdaptationSet,
202 GstMPDRepresentationNode * Representation,
203 GstMPDSegmentListNode * parent, GstMPDSegmentListNode * segment_list)
205 GstFragment *download;
206 GstBuffer *segment_list_buffer = NULL;
210 GstUri *base_uri, *uri;
213 GstMPDSegmentListNode *new_segment_list = NULL;
215 /* ISO/IEC 23009-1:2014 5.5.3 4)
216 * Remove nodes that resolve to nothing when resolving
218 if (strcmp (segment_list->xlink_href,
219 "urn:mpeg:dash:resolve-to-zero:2013") == 0) {
223 if (!client->downloader) {
227 /* Build absolute URI */
229 /* Get base URI at the MPD level */
231 gst_uri_from_string (client->mpd_base_uri ? client->
232 mpd_base_uri : client->mpd_uri);
234 /* combine a BaseURL at the MPD level with the current base url */
236 gst_mpd_helper_combine_urls (base_uri, client->mpd_root_node->BaseURLs,
239 /* combine a BaseURL at the Period level with the current base url */
241 gst_mpd_helper_combine_urls (base_uri, Period->BaseURLs, &query, 0);
244 /* combine a BaseURL at the AdaptationSet level with the current base url */
246 gst_mpd_helper_combine_urls (base_uri, AdaptationSet->BaseURLs, &query,
249 if (Representation) {
250 /* combine a BaseURL at the Representation level with the current base url */
252 gst_mpd_helper_combine_urls (base_uri, Representation->BaseURLs,
257 uri = gst_uri_from_string_with_base (base_uri, segment_list->xlink_href);
259 gst_uri_set_query_string (uri, query);
261 uri_string = gst_uri_to_string (uri);
262 gst_uri_unref (base_uri);
266 gst_uri_downloader_fetch_uri (client->downloader,
267 uri_string, client->mpd_uri, TRUE, FALSE, TRUE, &err);
271 GST_ERROR ("Failed to download external SegmentList node at '%s': %s",
272 segment_list->xlink_href, err->message);
273 g_clear_error (&err);
277 segment_list_buffer = gst_fragment_get_buffer (download);
278 g_object_unref (download);
280 gst_buffer_map (segment_list_buffer, &map, GST_MAP_READ);
283 gst_mpdparser_get_external_segment_list ((const gchar *) map.data,
286 if (segment_list_buffer) {
287 gst_buffer_unmap (segment_list_buffer, &map);
288 gst_buffer_unref (segment_list_buffer);
291 return new_segment_list;
294 static GstMPDSegmentBaseNode *
295 gst_mpd_client_get_segment_base (GstMPDPeriodNode * Period,
296 GstMPDAdaptationSetNode * AdaptationSet,
297 GstMPDRepresentationNode * Representation)
299 GstMPDSegmentBaseNode *SegmentBase = NULL;
301 if (Representation && Representation->SegmentBase) {
302 SegmentBase = Representation->SegmentBase;
303 } else if (AdaptationSet && AdaptationSet->SegmentBase) {
304 SegmentBase = AdaptationSet->SegmentBase;
305 } else if (Period && Period->SegmentBase) {
306 SegmentBase = Period->SegmentBase;
308 /* the SegmentBase element could be encoded also inside a SegmentList element */
309 if (SegmentBase == NULL) {
310 if (Representation && Representation->SegmentList
311 && GST_MPD_MULT_SEGMENT_BASE_NODE (Representation->SegmentList)
312 && GST_MPD_MULT_SEGMENT_BASE_NODE (Representation->
313 SegmentList)->SegmentBase) {
315 GST_MPD_MULT_SEGMENT_BASE_NODE (Representation->
316 SegmentList)->SegmentBase;
317 } else if (AdaptationSet && AdaptationSet->SegmentList
318 && GST_MPD_MULT_SEGMENT_BASE_NODE (AdaptationSet->SegmentList)
319 && GST_MPD_MULT_SEGMENT_BASE_NODE (AdaptationSet->
320 SegmentList)->SegmentBase) {
322 GST_MPD_MULT_SEGMENT_BASE_NODE (AdaptationSet->
323 SegmentList)->SegmentBase;
324 } else if (Period && Period->SegmentList
325 && GST_MPD_MULT_SEGMENT_BASE_NODE (Period->SegmentList)
326 && GST_MPD_MULT_SEGMENT_BASE_NODE (Period->SegmentList)->SegmentBase) {
328 GST_MPD_MULT_SEGMENT_BASE_NODE (Period->SegmentList)->SegmentBase;
335 static GstMPDSegmentListNode *
336 gst_mpd_client_get_segment_list (GstMPDClient * client,
337 GstMPDPeriodNode * Period, GstMPDAdaptationSetNode * AdaptationSet,
338 GstMPDRepresentationNode * Representation)
340 GstMPDSegmentListNode **SegmentList;
341 GstMPDSegmentListNode *ParentSegmentList = NULL;
343 if (Representation && Representation->SegmentList) {
344 SegmentList = &Representation->SegmentList;
345 ParentSegmentList = AdaptationSet->SegmentList;
346 } else if (AdaptationSet && AdaptationSet->SegmentList) {
347 SegmentList = &AdaptationSet->SegmentList;
348 ParentSegmentList = Period->SegmentList;
349 Representation = NULL;
351 Representation = NULL;
352 AdaptationSet = NULL;
353 SegmentList = &Period->SegmentList;
356 /* Resolve external segment list here. */
357 if (*SegmentList && (*SegmentList)->xlink_href) {
358 GstMPDSegmentListNode *new_segment_list;
360 /* TODO: Use SegmentList of parent if
361 * - Parent has its own SegmentList
362 * - Fail to get SegmentList from external xml
365 gst_mpd_client_fetch_external_segment_list (client, Period,
366 AdaptationSet, Representation, ParentSegmentList, *SegmentList);
368 gst_mpd_segment_list_node_free (*SegmentList);
369 *SegmentList = new_segment_list;
376 gst_mpd_client_get_segment_duration (GstMPDClient * client,
377 GstActiveStream * stream, guint64 * scale_dur)
379 GstStreamPeriod *stream_period;
380 GstMPDMultSegmentBaseNode *base = NULL;
381 GstClockTime duration = 0;
383 g_return_val_if_fail (stream != NULL, GST_CLOCK_TIME_NONE);
384 stream_period = gst_mpd_client_get_stream_period (client);
385 g_return_val_if_fail (stream_period != NULL, GST_CLOCK_TIME_NONE);
387 if (stream->cur_segment_list) {
388 base = GST_MPD_MULT_SEGMENT_BASE_NODE (stream->cur_segment_list);
389 } else if (stream->cur_seg_template) {
390 base = GST_MPD_MULT_SEGMENT_BASE_NODE (stream->cur_seg_template);
393 if (base == NULL || base->SegmentBase == NULL) {
394 /* this may happen when we have a single segment */
395 duration = stream_period->duration;
397 *scale_dur = duration;
399 /* duration is guint so this cannot overflow */
400 duration = base->duration * GST_SECOND;
402 *scale_dur = duration;
403 duration /= base->SegmentBase->timescale;
410 gst_mpd_client_active_streams_free (GstMPDClient * client)
412 if (client->active_streams) {
413 g_list_foreach (client->active_streams,
414 (GFunc) gst_mpdparser_free_active_stream, NULL);
415 g_list_free (client->active_streams);
416 client->active_streams = NULL;
421 gst_mpd_client_finalize (GObject * object)
423 GstMPDClient *client = GST_MPD_CLIENT (object);
425 if (client->mpd_root_node)
426 gst_mpd_root_node_free (client->mpd_root_node);
428 if (client->periods) {
429 g_list_free_full (client->periods,
430 (GDestroyNotify) gst_mpdparser_free_stream_period);
433 gst_mpd_client_active_streams_free (client);
435 g_free (client->mpd_uri);
436 client->mpd_uri = NULL;
437 g_free (client->mpd_base_uri);
438 client->mpd_base_uri = NULL;
440 if (client->downloader)
441 gst_object_unref (client->downloader);
442 client->downloader = NULL;
444 G_OBJECT_CLASS (gst_mpd_client_parent_class)->finalize (object);
448 gst_mpd_client_class_init (GstMPDClientClass * klass)
450 GObjectClass *object_class = G_OBJECT_CLASS (klass);
451 object_class->finalize = gst_mpd_client_finalize;
455 gst_mpd_client_init (GstMPDClient * client)
460 gst_mpd_client_new (void)
462 GST_DEBUG_CATEGORY_INIT (gst_dash_mpd_client_debug, "dashmpdclient", 0,
464 return g_object_new (GST_TYPE_MPD_CLIENT, NULL);
468 gst_mpd_client_new_static (void)
470 GstMPDClient *client = gst_mpd_client_new ();
472 client->mpd_root_node = gst_mpd_root_node_new ();
473 client->mpd_root_node->default_namespace =
474 g_strdup ("urn:mpeg:dash:schema:mpd:2011");
475 client->mpd_root_node->profiles =
476 g_strdup ("urn:mpeg:dash:profile:isoff-main:2011");
477 client->mpd_root_node->type = GST_MPD_FILE_TYPE_STATIC;
478 client->mpd_root_node->minBufferTime = 1500;
484 gst_mpd_client_free (GstMPDClient * client)
487 gst_object_unref (client);
491 gst_mpd_client_parse (GstMPDClient * client, const gchar * data, gint size)
493 gboolean ret = FALSE;
496 ret = gst_mpdparser_get_mpd_root_node (&client->mpd_root_node, data, size);
499 gst_mpd_client_check_profiles (client);
500 gst_mpd_client_fetch_on_load_external_resources (client);
508 gst_mpd_client_get_xml_content (GstMPDClient * client, gchar ** data,
511 gboolean ret = FALSE;
513 g_return_val_if_fail (client != NULL, ret);
514 g_return_val_if_fail (client->mpd_root_node != NULL, ret);
516 ret = gst_mpd_node_get_xml_buffer (GST_MPD_NODE (client->mpd_root_node),
523 gst_mpd_client_get_availability_start_time (GstMPDClient * client)
525 GstDateTime *start_time;
528 return (GstDateTime *) NULL;
530 start_time = client->mpd_root_node->availabilityStartTime;
532 gst_date_time_ref (start_time);
537 gst_mpd_client_set_uri_downloader (GstMPDClient * client,
538 GstUriDownloader * downloader)
540 if (client->downloader)
541 gst_object_unref (client->downloader);
542 client->downloader = gst_object_ref (downloader);
546 gst_mpd_client_check_profiles (GstMPDClient * client)
548 GST_DEBUG ("Profiles: %s",
549 client->mpd_root_node->profiles ? client->mpd_root_node->
550 profiles : "<none>");
552 if (!client->mpd_root_node->profiles)
555 if (g_strstr_len (client->mpd_root_node->profiles, -1,
556 "urn:mpeg:dash:profile:isoff-on-demand:2011")) {
557 client->profile_isoff_ondemand = TRUE;
558 GST_DEBUG ("Found ISOFF on demand profile (2011)");
563 gst_mpd_client_fetch_on_load_external_resources (GstMPDClient * client)
567 for (l = client->mpd_root_node->Periods; l; /* explicitly advanced below */ ) {
568 GstMPDPeriodNode *period = l->data;
571 if (period->xlink_href && period->actuate == GST_MPD_XLINK_ACTUATE_ON_LOAD) {
572 GList *new_periods, *prev, *next;
574 new_periods = gst_mpd_client_fetch_external_periods (client, period);
577 client->mpd_root_node->Periods =
578 g_list_delete_link (client->mpd_root_node->Periods, l);
579 gst_mpd_period_node_free (period);
582 /* Get new next node, we will insert before this */
586 next = client->mpd_root_node->Periods;
588 while (new_periods) {
589 client->mpd_root_node->Periods =
590 g_list_insert_before (client->mpd_root_node->Periods, next,
592 new_periods = g_list_delete_link (new_periods, new_periods);
596 /* Update our iterator to the first new period if any, or the next */
600 l = client->mpd_root_node->Periods;
605 if (period->SegmentList && period->SegmentList->xlink_href
606 && period->SegmentList->actuate == GST_MPD_XLINK_ACTUATE_ON_LOAD) {
607 GstMPDSegmentListNode *new_segment_list;
610 gst_mpd_client_fetch_external_segment_list (client, period, NULL,
611 NULL, NULL, period->SegmentList);
613 gst_mpd_segment_list_node_free (period->SegmentList);
614 period->SegmentList = new_segment_list;
617 for (m = period->AdaptationSets; m; /* explicitly advanced below */ ) {
618 GstMPDAdaptationSetNode *adapt_set = m->data;
621 if (adapt_set->xlink_href
622 && adapt_set->actuate == GST_MPD_XLINK_ACTUATE_ON_LOAD) {
623 GList *new_adapt_sets, *prev, *next;
626 gst_mpd_client_fetch_external_adaptation_set (client, period,
630 period->AdaptationSets = g_list_delete_link (period->AdaptationSets, m);
631 gst_mpd_adaptation_set_node_free (adapt_set);
634 /* Get new next node, we will insert before this */
638 next = period->AdaptationSets;
640 while (new_adapt_sets) {
641 period->AdaptationSets =
642 g_list_insert_before (period->AdaptationSets, next,
643 new_adapt_sets->data);
644 new_adapt_sets = g_list_delete_link (new_adapt_sets, new_adapt_sets);
648 /* Update our iterator to the first new adapt_set if any, or the next */
652 m = period->AdaptationSets;
657 if (adapt_set->SegmentList && adapt_set->SegmentList->xlink_href
658 && adapt_set->SegmentList->actuate == GST_MPD_XLINK_ACTUATE_ON_LOAD) {
659 GstMPDSegmentListNode *new_segment_list;
662 gst_mpd_client_fetch_external_segment_list (client, period,
663 adapt_set, NULL, period->SegmentList, adapt_set->SegmentList);
665 gst_mpd_segment_list_node_free (adapt_set->SegmentList);
666 adapt_set->SegmentList = new_segment_list;
669 for (n = adapt_set->Representations; n; n = n->next) {
670 GstMPDRepresentationNode *representation = n->data;
672 if (representation->SegmentList
673 && representation->SegmentList->xlink_href
674 && representation->SegmentList->actuate ==
675 GST_MPD_XLINK_ACTUATE_ON_LOAD) {
677 GstMPDSegmentListNode *new_segment_list;
680 gst_mpd_client_fetch_external_segment_list (client, period,
681 adapt_set, representation, adapt_set->SegmentList,
682 representation->SegmentList);
684 gst_mpd_segment_list_node_free (representation->SegmentList);
685 representation->SegmentList = new_segment_list;
698 static GstStreamPeriod *
699 gst_mpd_client_get_stream_period (GstMPDClient * client)
701 g_return_val_if_fail (client != NULL, NULL);
702 g_return_val_if_fail (client->periods != NULL, NULL);
704 return g_list_nth_data (client->periods, client->period_idx);
708 gst_mpd_client_get_baseURL (GstMPDClient * client, guint indexStream)
710 GstActiveStream *stream;
712 g_return_val_if_fail (client != NULL, NULL);
713 g_return_val_if_fail (client->active_streams != NULL, NULL);
714 stream = g_list_nth_data (client->active_streams, indexStream);
715 g_return_val_if_fail (stream != NULL, NULL);
717 return stream->baseURL;
720 /* select a stream and extract the baseURL (if present) */
722 gst_mpd_client_parse_baseURL (GstMPDClient * client, GstActiveStream * stream,
725 GstStreamPeriod *stream_period;
726 static const gchar empty[] = "";
730 g_return_val_if_fail (stream != NULL, g_strdup (empty));
731 stream_period = gst_mpd_client_get_stream_period (client);
732 g_return_val_if_fail (stream_period != NULL, g_strdup (empty));
733 g_return_val_if_fail (stream_period->period != NULL, g_strdup (empty));
735 /* NULLify query return before we start */
739 /* initialise base url */
741 gst_uri_from_string (client->mpd_base_uri ? client->
742 mpd_base_uri : client->mpd_uri);
744 /* combine a BaseURL at the MPD level with the current base url */
746 gst_mpd_helper_combine_urls (abs_url, client->mpd_root_node->BaseURLs,
747 query, stream->baseURL_idx);
749 /* combine a BaseURL at the Period level with the current base url */
751 gst_mpd_helper_combine_urls (abs_url, stream_period->period->BaseURLs,
752 query, stream->baseURL_idx);
754 GST_DEBUG ("Current adaptation set id %i (%s)", stream->cur_adapt_set->id,
755 stream->cur_adapt_set->contentType);
756 /* combine a BaseURL at the AdaptationSet level with the current base url */
758 gst_mpd_helper_combine_urls (abs_url, stream->cur_adapt_set->BaseURLs,
759 query, stream->baseURL_idx);
761 /* combine a BaseURL at the Representation level with the current base url */
763 gst_mpd_helper_combine_urls (abs_url,
764 stream->cur_representation->BaseURLs, query, stream->baseURL_idx);
766 ret = gst_uri_to_string (abs_url);
767 gst_uri_unref (abs_url);
773 gst_mpd_client_get_segment_end_time (GstMPDClient * client,
774 GPtrArray * segments, const GstMediaSegment * segment, gint index)
776 const GstStreamPeriod *stream_period;
779 if (segment->repeat >= 0)
780 return segment->start + (segment->repeat + 1) * segment->duration;
782 if (index < segments->len - 1) {
783 const GstMediaSegment *next_segment =
784 g_ptr_array_index (segments, index + 1);
785 end = next_segment->start;
787 stream_period = gst_mpd_client_get_stream_period (client);
788 end = stream_period->start + stream_period->duration;
794 gst_mpd_client_add_media_segment (GstActiveStream * stream,
795 GstMPDSegmentURLNode * url_node, guint number, gint repeat,
796 guint64 scale_start, guint64 scale_duration,
797 GstClockTime start, GstClockTime duration)
799 GstMediaSegment *media_segment;
801 g_return_val_if_fail (stream->segments != NULL, FALSE);
803 media_segment = g_slice_new0 (GstMediaSegment);
805 media_segment->SegmentURL = url_node;
806 media_segment->number = number;
807 media_segment->scale_start = scale_start;
808 media_segment->scale_duration = scale_duration;
809 media_segment->start = start;
810 media_segment->duration = duration;
811 media_segment->repeat = repeat;
813 g_ptr_array_add (stream->segments, media_segment);
814 GST_LOG ("Added new segment: number %d, repeat %d, "
815 "ts: %" GST_TIME_FORMAT ", dur: %"
816 GST_TIME_FORMAT, number, repeat,
817 GST_TIME_ARGS (start), GST_TIME_ARGS (duration));
823 gst_mpd_client_stream_update_presentation_time_offset (GstMPDClient * client,
824 GstActiveStream * stream)
826 GstMPDSegmentBaseNode *segbase = NULL;
828 /* Find the used segbase */
829 if (stream->cur_segment_list) {
831 GST_MPD_MULT_SEGMENT_BASE_NODE (stream->cur_segment_list)->SegmentBase;
832 } else if (stream->cur_seg_template) {
834 GST_MPD_MULT_SEGMENT_BASE_NODE (stream->cur_seg_template)->SegmentBase;
835 } else if (stream->cur_segment_base) {
836 segbase = stream->cur_segment_base;
840 /* Avoid overflows */
841 stream->presentationTimeOffset =
842 gst_util_uint64_scale (segbase->presentationTimeOffset, GST_SECOND,
845 stream->presentationTimeOffset = 0;
848 GST_LOG ("Setting stream's presentation time offset to %" GST_TIME_FORMAT,
849 GST_TIME_ARGS (stream->presentationTimeOffset));
853 gst_mpd_client_setup_representation (GstMPDClient * client,
854 GstActiveStream * stream, GstMPDRepresentationNode * representation)
856 GstStreamPeriod *stream_period;
858 GstClockTime PeriodStart, PeriodEnd, start_time, duration;
862 if (stream->cur_adapt_set == NULL) {
863 GST_WARNING ("No valid AdaptationSet node in the MPD file, aborting...");
867 rep_list = stream->cur_adapt_set->Representations;
868 stream->cur_representation = representation;
869 stream->representation_idx = g_list_index (rep_list, representation);
871 /* clean the old segment list, if any */
872 if (stream->segments) {
873 g_ptr_array_unref (stream->segments);
874 stream->segments = NULL;
877 stream_period = gst_mpd_client_get_stream_period (client);
878 g_return_val_if_fail (stream_period != NULL, FALSE);
879 g_return_val_if_fail (stream_period->period != NULL, FALSE);
881 PeriodStart = stream_period->start;
882 if (GST_CLOCK_TIME_IS_VALID (stream_period->duration))
883 PeriodEnd = stream_period->start + stream_period->duration;
885 PeriodEnd = GST_CLOCK_TIME_NONE;
887 GST_LOG ("Building segment list for Period from %" GST_TIME_FORMAT " to %"
888 GST_TIME_FORMAT, GST_TIME_ARGS (PeriodStart), GST_TIME_ARGS (PeriodEnd));
890 if (representation->SegmentBase != NULL
891 || representation->SegmentList != NULL) {
894 /* We have a fixed list of segments for any of the cases here,
895 * init the segments list */
896 gst_mpdparser_init_active_stream_segments (stream);
898 /* get the first segment_base of the selected representation */
899 if ((stream->cur_segment_base =
900 gst_mpd_client_get_segment_base (stream_period->period,
901 stream->cur_adapt_set, representation)) == NULL) {
902 GST_DEBUG ("No useful SegmentBase node for the current Representation");
905 /* get the first segment_list of the selected representation */
906 if ((stream->cur_segment_list =
907 gst_mpd_client_get_segment_list (client, stream_period->period,
908 stream->cur_adapt_set, representation)) == NULL) {
909 GST_DEBUG ("No useful SegmentList node for the current Representation");
910 /* here we should have a single segment for each representation, whose URL is encoded in the baseURL element */
911 if (!gst_mpd_client_add_media_segment (stream, NULL, 1, 0, 0,
912 PeriodEnd - PeriodStart, PeriodStart, PeriodEnd - PeriodStart)) {
916 /* build the list of GstMediaSegment nodes from the SegmentList node */
917 SegmentURL = stream->cur_segment_list->SegmentURL;
918 if (SegmentURL == NULL) {
920 ("No valid list of SegmentURL nodes in the MPD file, aborting...");
924 /* build segment list */
925 i = GST_MPD_MULT_SEGMENT_BASE_NODE (stream->
926 cur_segment_list)->startNumber;
928 start_time = PeriodStart;
930 GST_LOG ("Building media segment list using a SegmentList node");
931 if (GST_MPD_MULT_SEGMENT_BASE_NODE (stream->
932 cur_segment_list)->SegmentTimeline) {
933 GstMPDSegmentTimelineNode *timeline;
936 GstClockTime presentationTimeOffset;
937 GstMPDSegmentBaseNode *segbase;
940 GST_MPD_MULT_SEGMENT_BASE_NODE (stream->
941 cur_segment_list)->SegmentBase;
942 presentationTimeOffset =
943 gst_util_uint64_scale (segbase->presentationTimeOffset, GST_SECOND,
945 GST_LOG ("presentationTimeOffset = %" G_GUINT64_FORMAT,
946 presentationTimeOffset);
949 GST_MPD_MULT_SEGMENT_BASE_NODE (stream->
950 cur_segment_list)->SegmentTimeline;
951 for (list = g_queue_peek_head_link (&timeline->S); list;
952 list = g_list_next (list)) {
955 S = (GstMPDSNode *) list->data;
956 GST_LOG ("Processing S node: d=%" G_GUINT64_FORMAT " r=%d t=%"
957 G_GUINT64_FORMAT, S->d, S->r, S->t);
959 GST_MPD_MULT_SEGMENT_BASE_NODE (stream->
960 cur_segment_list)->SegmentBase->timescale;
961 duration = gst_util_uint64_scale (S->d, GST_SECOND, timescale);
965 start_time = gst_util_uint64_scale (S->t, GST_SECOND, timescale)
966 + PeriodStart - presentationTimeOffset;
971 ("SegmentTimeline does not have a matching SegmentURL, aborting...");
975 if (!gst_mpd_client_add_media_segment (stream, SegmentURL->data, i,
976 S->r, start, S->d, start_time, duration)) {
980 start_time += duration * (S->r + 1);
981 start += S->d * (S->r + 1);
982 SegmentURL = g_list_next (SegmentURL);
988 gst_mpd_client_get_segment_duration (client, stream, &scale_dur);
989 if (!GST_CLOCK_TIME_IS_VALID (duration))
993 if (!gst_mpd_client_add_media_segment (stream, SegmentURL->data, i,
994 0, start, scale_dur, start_time, duration)) {
999 start_time += duration;
1000 SegmentURL = g_list_next (SegmentURL);
1005 if (representation->SegmentTemplate != NULL) {
1006 stream->cur_seg_template = representation->SegmentTemplate;
1007 } else if (stream->cur_adapt_set->SegmentTemplate != NULL) {
1008 stream->cur_seg_template = stream->cur_adapt_set->SegmentTemplate;
1009 } else if (stream_period->period->SegmentTemplate != NULL) {
1010 stream->cur_seg_template = stream_period->period->SegmentTemplate;
1013 if (stream->cur_seg_template == NULL) {
1015 gst_mpdparser_init_active_stream_segments (stream);
1016 /* here we should have a single segment for each representation, whose URL is encoded in the baseURL element */
1017 if (!gst_mpd_client_add_media_segment (stream, NULL, 1, 0, 0,
1018 PeriodEnd - PeriodStart, 0, PeriodEnd - PeriodStart)) {
1022 GstClockTime presentationTimeOffset;
1023 GstMPDMultSegmentBaseNode *mult_seg =
1024 GST_MPD_MULT_SEGMENT_BASE_NODE (stream->cur_seg_template);
1025 presentationTimeOffset =
1026 gst_util_uint64_scale (mult_seg->SegmentBase->presentationTimeOffset,
1027 GST_SECOND, mult_seg->SegmentBase->timescale);
1028 GST_LOG ("presentationTimeOffset = %" GST_TIME_FORMAT,
1029 GST_TIME_ARGS (presentationTimeOffset));
1030 /* build segment list */
1031 i = mult_seg->startNumber;
1035 GST_LOG ("Building media segment list using this template: %s",
1036 stream->cur_seg_template->media);
1038 if (mult_seg->SegmentTimeline) {
1039 GstMPDSegmentTimelineNode *timeline;
1043 timeline = mult_seg->SegmentTimeline;
1044 gst_mpdparser_init_active_stream_segments (stream);
1045 for (list = g_queue_peek_head_link (&timeline->S); list;
1046 list = g_list_next (list)) {
1049 S = (GstMPDSNode *) list->data;
1050 GST_LOG ("Processing S node: d=%" G_GUINT64_FORMAT " r=%u t=%"
1051 G_GUINT64_FORMAT, S->d, S->r, S->t);
1052 timescale = mult_seg->SegmentBase->timescale;
1053 duration = gst_util_uint64_scale (S->d, GST_SECOND, timescale);
1056 start_time = gst_util_uint64_scale (S->t, GST_SECOND, timescale)
1057 + PeriodStart - presentationTimeOffset;
1060 if (!gst_mpd_client_add_media_segment (stream, NULL, i, S->r, start,
1061 S->d, start_time, duration)) {
1065 start += S->d * (S->r + 1);
1066 start_time += duration * (S->r + 1);
1069 /* NOP - The segment is created on demand with the template, no need
1070 * to build a list */
1075 /* clip duration of segments to stop at period end */
1076 if (stream->segments && stream->segments->len) {
1077 if (GST_CLOCK_TIME_IS_VALID (PeriodEnd)) {
1080 for (n = 0; n < stream->segments->len; ++n) {
1081 GstMediaSegment *media_segment =
1082 g_ptr_array_index (stream->segments, n);
1083 if (media_segment) {
1084 if (media_segment->start + media_segment->duration > PeriodEnd) {
1085 GstClockTime stop = PeriodEnd;
1086 if (n < stream->segments->len - 1) {
1087 GstMediaSegment *next_segment =
1088 g_ptr_array_index (stream->segments, n + 1);
1089 if (next_segment && next_segment->start < PeriodEnd)
1090 stop = next_segment->start;
1092 media_segment->duration =
1093 media_segment->start > stop ? 0 : stop - media_segment->start;
1094 GST_LOG ("Fixed duration of segment %u: %" GST_TIME_FORMAT, n,
1095 GST_TIME_ARGS (media_segment->duration));
1097 /* If the segment was clipped entirely, we discard it and all
1098 * subsequent ones */
1099 if (media_segment->duration == 0) {
1100 GST_WARNING ("Discarding %u segments outside period",
1101 stream->segments->len - n);
1102 /* _set_size should properly unref elements */
1103 g_ptr_array_set_size (stream->segments, n);
1110 #ifndef GST_DISABLE_GST_DEBUG
1111 if (stream->segments->len > 0) {
1112 GstMediaSegment *last_media_segment =
1113 g_ptr_array_index (stream->segments, stream->segments->len - 1);
1114 GST_LOG ("Built a list of %d segments", last_media_segment->number);
1116 GST_LOG ("All media segments were clipped");
1121 g_free (stream->baseURL);
1122 g_free (stream->queryURL);
1124 gst_mpd_client_parse_baseURL (client, stream, &stream->queryURL);
1126 gst_mpd_client_stream_update_presentation_time_offset (client, stream);
1131 #define CUSTOM_WRAPPER_START "<custom_wrapper>"
1132 #define CUSTOM_WRAPPER_END "</custom_wrapper>"
1135 gst_mpd_client_fetch_external_periods (GstMPDClient * client,
1136 GstMPDPeriodNode * period_node)
1138 GstFragment *download;
1139 GstAdapter *adapter;
1140 GstBuffer *period_buffer;
1143 GstUri *base_uri, *uri;
1144 gchar *query = NULL;
1145 gchar *uri_string, *wrapper;
1146 GList *new_periods = NULL;
1149 /* ISO/IEC 23009-1:2014 5.5.3 4)
1150 * Remove nodes that resolve to nothing when resolving
1152 if (strcmp (period_node->xlink_href,
1153 "urn:mpeg:dash:resolve-to-zero:2013") == 0) {
1157 if (!client->downloader) {
1161 /* Build absolute URI */
1163 /* Get base URI at the MPD level */
1165 gst_uri_from_string (client->mpd_base_uri ? client->
1166 mpd_base_uri : client->mpd_uri);
1168 /* combine a BaseURL at the MPD level with the current base url */
1170 gst_mpd_helper_combine_urls (base_uri, client->mpd_root_node->BaseURLs,
1172 uri = gst_uri_from_string_with_base (base_uri, period_node->xlink_href);
1174 gst_uri_set_query_string (uri, query);
1176 uri_string = gst_uri_to_string (uri);
1177 gst_uri_unref (base_uri);
1178 gst_uri_unref (uri);
1181 gst_uri_downloader_fetch_uri (client->downloader,
1182 uri_string, client->mpd_uri, TRUE, FALSE, TRUE, &err);
1183 g_free (uri_string);
1186 GST_ERROR ("Failed to download external Period node at '%s': %s",
1187 period_node->xlink_href, err->message);
1188 g_clear_error (&err);
1192 period_buffer = gst_fragment_get_buffer (download);
1193 g_object_unref (download);
1195 /* external xml could have multiple period without root xmlNode.
1196 * To avoid xml parsing error caused by no root node, wrapping it with
1197 * custom root node */
1198 adapter = gst_adapter_new ();
1200 wrapper = g_new (gchar, strlen (CUSTOM_WRAPPER_START));
1201 memcpy (wrapper, CUSTOM_WRAPPER_START, strlen (CUSTOM_WRAPPER_START));
1202 gst_adapter_push (adapter,
1203 gst_buffer_new_wrapped (wrapper, strlen (CUSTOM_WRAPPER_START)));
1205 gst_adapter_push (adapter, period_buffer);
1207 wrapper = g_strdup (CUSTOM_WRAPPER_END);
1208 gst_adapter_push (adapter,
1209 gst_buffer_new_wrapped (wrapper, strlen (CUSTOM_WRAPPER_END) + 1));
1211 data = gst_adapter_map (adapter, gst_adapter_available (adapter));
1214 gst_mpdparser_get_external_periods (data,
1215 gst_adapter_available (adapter));
1217 gst_adapter_unmap (adapter);
1218 gst_adapter_clear (adapter);
1219 gst_object_unref (adapter);
1225 gst_mpd_client_setup_media_presentation (GstMPDClient * client,
1226 GstClockTime time, gint period_idx, const gchar * period_id)
1228 GstStreamPeriod *stream_period;
1229 GstClockTime start, duration;
1232 gboolean ret = FALSE;
1234 g_return_val_if_fail (client != NULL, FALSE);
1235 g_return_val_if_fail (client->mpd_root_node != NULL, FALSE);
1237 /* Check if we set up the media presentation far enough already */
1238 for (list = client->periods; list; list = list->next) {
1239 GstStreamPeriod *stream_period = list->data;
1241 if ((time != GST_CLOCK_TIME_NONE
1242 && stream_period->duration != GST_CLOCK_TIME_NONE
1243 && stream_period->start + stream_period->duration >= time)
1244 || (time != GST_CLOCK_TIME_NONE && stream_period->start >= time))
1247 if (period_idx != -1 && stream_period->number >= period_idx)
1250 if (period_id != NULL && stream_period->period->id != NULL
1251 && strcmp (stream_period->period->id, period_id) == 0)
1256 GST_DEBUG ("Building the list of Periods in the Media Presentation");
1257 /* clean the old period list, if any */
1258 /* TODO: In theory we could reuse the ones we have so far but that
1259 * seems more complicated than the overhead caused here
1261 if (client->periods) {
1262 g_list_foreach (client->periods,
1263 (GFunc) gst_mpdparser_free_stream_period, NULL);
1264 g_list_free (client->periods);
1265 client->periods = NULL;
1270 duration = GST_CLOCK_TIME_NONE;
1272 if (client->mpd_root_node->mediaPresentationDuration <= 0 &&
1273 client->mpd_root_node->mediaPresentationDuration != -1) {
1274 /* Invalid MPD file: MPD duration is negative or zero */
1278 for (list = client->mpd_root_node->Periods; list;
1279 /* explicitly advanced below */ ) {
1280 GstMPDPeriodNode *period_node = list->data;
1281 GstMPDPeriodNode *next_period_node = NULL;
1283 /* Download external period */
1284 if (period_node->xlink_href) {
1288 new_periods = gst_mpd_client_fetch_external_periods (client, period_node);
1291 client->mpd_root_node->Periods =
1292 g_list_delete_link (client->mpd_root_node->Periods, list);
1293 gst_mpd_period_node_free (period_node);
1296 /* Get new next node, we will insert before this */
1300 next = client->mpd_root_node->Periods;
1302 while (new_periods) {
1303 client->mpd_root_node->Periods =
1304 g_list_insert_before (client->mpd_root_node->Periods, next,
1306 new_periods = g_list_delete_link (new_periods, new_periods);
1310 /* Update our iterator to the first new period if any, or the next */
1314 list = client->mpd_root_node->Periods;
1320 if (period_node->start != -1) {
1321 /* we have a regular period */
1322 /* start cannot be smaller than previous start */
1323 if (list != g_list_first (client->mpd_root_node->Periods)
1324 && start >= period_node->start * GST_MSECOND) {
1325 /* Invalid MPD file: duration would be negative or zero */
1328 start = period_node->start * GST_MSECOND;
1329 } else if (duration != GST_CLOCK_TIME_NONE) {
1330 /* start time inferred from previous period, this is still a regular period */
1333 && client->mpd_root_node->type == GST_MPD_FILE_TYPE_STATIC) {
1334 /* first period of a static MPD file, start time is 0 */
1336 } else if (client->mpd_root_node->type == GST_MPD_FILE_TYPE_DYNAMIC) {
1337 /* this should be a live stream, let this pass */
1339 /* this is an 'Early Available Period' */
1343 /* compute duration.
1344 If there is a start time for the next period, or this is the last period
1345 and mediaPresentationDuration was set, those values will take precedence
1346 over a configured period duration in computing this period's duration
1348 ISO/IEC 23009-1:2014(E), chapter 5.3.2.1
1349 "The Period extends until the PeriodStart of the next Period, or until
1350 the end of the Media Presentation in the case of the last Period."
1353 while ((next = g_list_next (list)) != NULL) {
1354 /* try to infer this period duration from the start time of the next period */
1355 next_period_node = next->data;
1357 if (next_period_node->xlink_href) {
1361 gst_mpd_client_fetch_external_periods (client, next_period_node);
1363 client->mpd_root_node->Periods =
1364 g_list_delete_link (client->mpd_root_node->Periods, next);
1365 gst_mpd_period_node_free (next_period_node);
1366 next_period_node = NULL;
1367 /* Get new next node, we will insert before this */
1368 next = g_list_next (list);
1369 while (new_periods) {
1370 client->mpd_root_node->Periods =
1371 g_list_insert_before (client->mpd_root_node->Periods, next,
1373 new_periods = g_list_delete_link (new_periods, new_periods);
1376 /* And try again, getting the next list element which is now our newly
1377 * inserted nodes. If any */
1379 /* Got the next period and it doesn't have to be downloaded first */
1384 if (next_period_node) {
1385 if (next_period_node->start != -1) {
1386 if (start >= next_period_node->start * GST_MSECOND) {
1387 /* Invalid MPD file: duration would be negative or zero */
1390 duration = next_period_node->start * GST_MSECOND - start;
1391 } else if (period_node->duration != -1) {
1392 if (period_node->duration <= 0) {
1393 /* Invalid MPD file: duration would be negative or zero */
1396 duration = period_node->duration * GST_MSECOND;
1397 } else if (client->mpd_root_node->type == GST_MPD_FILE_TYPE_DYNAMIC) {
1398 /* might be a live file, ignore unspecified duration */
1400 /* Invalid MPD file! */
1403 } else if (client->mpd_root_node->mediaPresentationDuration != -1) {
1404 /* last Period of the Media Presentation */
1405 if (client->mpd_root_node->mediaPresentationDuration * GST_MSECOND <=
1407 /* Invalid MPD file: duration would be negative or zero */
1411 client->mpd_root_node->mediaPresentationDuration * GST_MSECOND -
1413 } else if (period_node->duration != -1) {
1414 duration = period_node->duration * GST_MSECOND;
1415 } else if (client->mpd_root_node->type == GST_MPD_FILE_TYPE_DYNAMIC) {
1416 /* might be a live file, ignore unspecified duration */
1418 /* Invalid MPD file! */
1420 ("Invalid MPD file. The MPD is static without a valid duration");
1424 stream_period = g_slice_new0 (GstStreamPeriod);
1425 client->periods = g_list_append (client->periods, stream_period);
1426 stream_period->period = period_node;
1427 stream_period->number = idx++;
1428 stream_period->start = start;
1429 stream_period->duration = duration;
1431 GST_LOG (" - added Period %d start=%" GST_TIME_FORMAT " duration=%"
1432 GST_TIME_FORMAT, idx, GST_TIME_ARGS (start), GST_TIME_ARGS (duration));
1434 if ((time != GST_CLOCK_TIME_NONE
1435 && stream_period->duration != GST_CLOCK_TIME_NONE
1436 && stream_period->start + stream_period->duration >= time)
1437 || (time != GST_CLOCK_TIME_NONE && stream_period->start >= time))
1440 if (period_idx != -1 && stream_period->number >= period_idx)
1443 if (period_id != NULL && stream_period->period->id != NULL
1444 && strcmp (stream_period->period->id, period_id) == 0)
1451 ("Found a total of %d valid Periods in the Media Presentation up to this point",
1457 ("Found an Early Available Period, skipping the rest of the Media Presentation");
1462 ("Cannot get the duration of the Period %d, skipping the rest of the Media Presentation",
1468 gst_mpd_client_fetch_external_adaptation_set (GstMPDClient * client,
1469 GstMPDPeriodNode * period, GstMPDAdaptationSetNode * adapt_set)
1471 GstFragment *download;
1472 GstBuffer *adapt_set_buffer;
1475 GstUri *base_uri, *uri;
1476 gchar *query = NULL;
1478 GList *new_adapt_sets = NULL;
1480 /* ISO/IEC 23009-1:2014 5.5.3 4)
1481 * Remove nodes that resolve to nothing when resolving
1483 if (strcmp (adapt_set->xlink_href, "urn:mpeg:dash:resolve-to-zero:2013") == 0) {
1487 if (!client->downloader) {
1491 /* Build absolute URI */
1493 /* Get base URI at the MPD level */
1495 gst_uri_from_string (client->mpd_base_uri ? client->
1496 mpd_base_uri : client->mpd_uri);
1498 /* combine a BaseURL at the MPD level with the current base url */
1500 gst_mpd_helper_combine_urls (base_uri, client->mpd_root_node->BaseURLs,
1503 /* combine a BaseURL at the Period level with the current base url */
1505 gst_mpd_helper_combine_urls (base_uri, period->BaseURLs, &query, 0);
1507 uri = gst_uri_from_string_with_base (base_uri, adapt_set->xlink_href);
1509 gst_uri_set_query_string (uri, query);
1511 uri_string = gst_uri_to_string (uri);
1512 gst_uri_unref (base_uri);
1513 gst_uri_unref (uri);
1516 gst_uri_downloader_fetch_uri (client->downloader,
1517 uri_string, client->mpd_uri, TRUE, FALSE, TRUE, &err);
1518 g_free (uri_string);
1521 GST_ERROR ("Failed to download external AdaptationSet node at '%s': %s",
1522 adapt_set->xlink_href, err->message);
1523 g_clear_error (&err);
1527 adapt_set_buffer = gst_fragment_get_buffer (download);
1528 g_object_unref (download);
1530 gst_buffer_map (adapt_set_buffer, &map, GST_MAP_READ);
1533 gst_mpdparser_get_external_adaptation_sets ((const gchar *) map.data,
1536 gst_buffer_unmap (adapt_set_buffer, &map);
1537 gst_buffer_unref (adapt_set_buffer);
1539 return new_adapt_sets;
1543 gst_mpd_client_get_adaptation_sets_for_period (GstMPDClient * client,
1544 GstStreamPeriod * period)
1548 g_return_val_if_fail (period != NULL, NULL);
1550 /* Resolve all external adaptation sets of this period. Every user of
1551 * the adaptation sets would need to know the content of all adaptation sets
1552 * to decide which one to use, so we have to resolve them all here
1554 for (list = period->period->AdaptationSets; list;
1555 /* advanced explicitly below */ ) {
1556 GstMPDAdaptationSetNode *adapt_set = (GstMPDAdaptationSetNode *) list->data;
1557 GList *new_adapt_sets = NULL, *prev, *next;
1559 if (!adapt_set->xlink_href) {
1565 gst_mpd_client_fetch_external_adaptation_set (client, period->period,
1569 period->period->AdaptationSets =
1570 g_list_delete_link (period->period->AdaptationSets, list);
1571 gst_mpd_adaptation_set_node_free (adapt_set);
1574 /* Get new next node, we will insert before this */
1578 next = period->period->AdaptationSets;
1580 while (new_adapt_sets) {
1581 period->period->AdaptationSets =
1582 g_list_insert_before (period->period->AdaptationSets, next,
1583 new_adapt_sets->data);
1584 new_adapt_sets = g_list_delete_link (new_adapt_sets, new_adapt_sets);
1587 /* Update our iterator to the first new adaptation set if any, or the next */
1591 list = period->period->AdaptationSets;
1594 return period->period->AdaptationSets;
1598 gst_mpd_client_get_adaptation_sets (GstMPDClient * client)
1600 GstStreamPeriod *stream_period;
1602 stream_period = gst_mpd_client_get_stream_period (client);
1603 if (stream_period == NULL || stream_period->period == NULL) {
1604 GST_DEBUG ("No more Period nodes in the MPD file, terminating...");
1608 return gst_mpd_client_get_adaptation_sets_for_period (client, stream_period);
1612 gst_mpd_client_setup_streaming (GstMPDClient * client,
1613 GstMPDAdaptationSetNode * adapt_set)
1615 GstMPDRepresentationNode *representation;
1616 GList *rep_list = NULL;
1617 GstActiveStream *stream;
1619 rep_list = adapt_set->Representations;
1621 GST_WARNING ("Can not retrieve any representation, aborting...");
1625 stream = g_slice_new0 (GstActiveStream);
1626 gst_mpdparser_init_active_stream_segments (stream);
1628 stream->baseURL_idx = 0;
1629 stream->cur_adapt_set = adapt_set;
1631 GST_DEBUG ("0. Current stream %p", stream);
1636 gst_mpdparser_get_representation_with_max_bandwidth (rep_list,
1637 stream->max_bandwidth);
1639 if (!representation) {
1641 ("Can not retrieve a representation with the requested bandwidth");
1642 representation = gst_mpd_client_get_lowest_representation (rep_list);
1646 representation = gst_mpd_client_get_lowest_representation (rep_list);
1649 if (!representation) {
1650 GST_WARNING ("No valid representation in the MPD file, aborting...");
1651 gst_mpdparser_free_active_stream (stream);
1655 gst_mpdparser_representation_get_mimetype (adapt_set, representation);
1656 if (stream->mimeType == GST_STREAM_UNKNOWN) {
1657 GST_WARNING ("Unknown mime type in the representation, aborting...");
1658 gst_mpdparser_free_active_stream (stream);
1662 client->active_streams = g_list_append (client->active_streams, stream);
1663 if (!gst_mpd_client_setup_representation (client, stream, representation)) {
1664 GST_WARNING ("Failed to setup the representation, aborting...");
1668 GST_INFO ("Successfully setup the download pipeline for mimeType %d",
1675 gst_mpd_client_stream_seek (GstMPDClient * client, GstActiveStream * stream,
1676 gboolean forward, GstSeekFlags flags, GstClockTime ts,
1677 GstClockTime * final_ts)
1680 gint repeat_index = 0;
1681 GstMediaSegment *selectedChunk = NULL;
1683 g_return_val_if_fail (stream != NULL, 0);
1685 if (stream->segments) {
1686 for (index = 0; index < stream->segments->len; index++) {
1687 gboolean in_segment = FALSE;
1688 GstMediaSegment *segment = g_ptr_array_index (stream->segments, index);
1689 GstClockTime end_time;
1691 GST_DEBUG ("Looking at fragment sequence chunk %d / %d", index,
1692 stream->segments->len);
1695 gst_mpd_client_get_segment_end_time (client, stream->segments,
1698 /* avoid downloading another fragment just for 1ns in reverse mode */
1700 in_segment = ts < end_time;
1702 in_segment = ts <= end_time;
1705 GstClockTime chunk_time;
1707 selectedChunk = segment;
1708 repeat_index = (ts - segment->start) / segment->duration;
1710 chunk_time = segment->start + segment->duration * repeat_index;
1712 /* At the end of a segment in reverse mode, start from the previous fragment */
1713 if (!forward && repeat_index > 0
1714 && ((ts - segment->start) % segment->duration == 0))
1717 if ((flags & GST_SEEK_FLAG_SNAP_NEAREST) == GST_SEEK_FLAG_SNAP_NEAREST) {
1718 if (repeat_index + 1 < segment->repeat) {
1719 if (ts - chunk_time > chunk_time + segment->duration - ts)
1721 } else if (index + 1 < stream->segments->len) {
1722 GstMediaSegment *next_segment =
1723 g_ptr_array_index (stream->segments, index + 1);
1725 if (ts - chunk_time > next_segment->start - ts) {
1727 selectedChunk = next_segment;
1731 } else if (((forward && flags & GST_SEEK_FLAG_SNAP_AFTER) ||
1732 (!forward && flags & GST_SEEK_FLAG_SNAP_BEFORE)) &&
1735 if (repeat_index + 1 < segment->repeat) {
1739 if (index + 1 >= stream->segments->len) {
1740 selectedChunk = NULL;
1742 selectedChunk = g_ptr_array_index (stream->segments, ++index);
1750 if (selectedChunk == NULL) {
1751 stream->segment_index = stream->segments->len;
1752 stream->segment_repeat_index = 0;
1753 GST_DEBUG ("Seek to after last segment");
1758 *final_ts = selectedChunk->start + selectedChunk->duration * repeat_index;
1760 GstClockTime duration =
1761 gst_mpd_client_get_segment_duration (client, stream, NULL);
1762 GstStreamPeriod *stream_period = gst_mpd_client_get_stream_period (client);
1763 guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
1764 GstClockTime index_time;
1766 g_return_val_if_fail (GST_MPD_MULT_SEGMENT_BASE_NODE
1767 (stream->cur_seg_template)->SegmentTimeline == NULL, FALSE);
1768 if (!GST_CLOCK_TIME_IS_VALID (duration)) {
1772 if (ts > stream_period->start)
1773 ts -= stream_period->start;
1777 index = ts / duration;
1779 /* At the end of a segment in reverse mode, start from the previous fragment */
1780 if (!forward && index > 0 && ts % duration == 0)
1783 index_time = index * duration;
1785 if ((flags & GST_SEEK_FLAG_SNAP_NEAREST) == GST_SEEK_FLAG_SNAP_NEAREST) {
1786 if (ts - index_time > index_time + duration - ts)
1788 } else if (((forward && flags & GST_SEEK_FLAG_SNAP_AFTER) ||
1789 (!forward && flags & GST_SEEK_FLAG_SNAP_BEFORE))
1790 && ts != index_time) {
1794 if (segments_count > 0 && index >= segments_count) {
1795 stream->segment_index = segments_count;
1796 stream->segment_repeat_index = 0;
1797 GST_DEBUG ("Seek to after last segment");
1801 *final_ts = index * duration;
1804 stream->segment_repeat_index = repeat_index;
1805 stream->segment_index = index;
1811 gst_mpd_client_calculate_time_difference (const GstDateTime * t1,
1812 const GstDateTime * t2)
1814 GDateTime *gdt1, *gdt2;
1817 g_assert (t1 != NULL && t2 != NULL);
1818 gdt1 = gst_date_time_to_g_date_time ((GstDateTime *) t1);
1819 gdt2 = gst_date_time_to_g_date_time ((GstDateTime *) t2);
1820 diff = g_date_time_difference (gdt2, gdt1);
1821 g_date_time_unref (gdt1);
1822 g_date_time_unref (gdt2);
1823 return diff * GST_USECOND;
1827 gst_mpd_client_add_time_difference (GstDateTime * t1, gint64 usecs)
1833 g_assert (t1 != NULL);
1834 gdt = gst_date_time_to_g_date_time (t1);
1835 g_assert (gdt != NULL);
1836 gdt2 = g_date_time_add (gdt, usecs);
1837 g_assert (gdt2 != NULL);
1838 g_date_time_unref (gdt);
1839 rv = gst_date_time_new_from_g_date_time (gdt2);
1841 /* Don't g_date_time_unref(gdt2) because gst_date_time_new_from_g_date_time takes
1842 * ownership of the GDateTime pointer.
1849 gst_mpd_client_get_last_fragment_timestamp_end (GstMPDClient * client,
1850 guint stream_idx, GstClockTime * ts)
1852 GstActiveStream *stream;
1854 GstMediaSegment *currentChunk;
1855 GstStreamPeriod *stream_period;
1857 GST_DEBUG ("Stream index: %i", stream_idx);
1858 stream = g_list_nth_data (client->active_streams, stream_idx);
1859 g_return_val_if_fail (stream != NULL, 0);
1861 if (!stream->segments) {
1862 stream_period = gst_mpd_client_get_stream_period (client);
1863 *ts = stream_period->start + stream_period->duration;
1865 segment_idx = gst_mpd_client_get_segments_counts (client, stream) - 1;
1866 if (segment_idx >= stream->segments->len) {
1867 GST_WARNING ("Segment index %d is outside of segment list of length %d",
1868 segment_idx, stream->segments->len);
1871 currentChunk = g_ptr_array_index (stream->segments, segment_idx);
1873 if (currentChunk->repeat >= 0) {
1875 currentChunk->start + (currentChunk->duration * (1 +
1876 currentChunk->repeat));
1878 /* 5.3.9.6.1: negative repeat means repeat till the end of the
1879 * period, or the next update of the MPD (which I think is
1880 * implicit, as this will all get deleted/recreated), or the
1881 * start of the next segment, if any. */
1882 stream_period = gst_mpd_client_get_stream_period (client);
1883 *ts = stream_period->start + stream_period->duration;
1891 gst_mpd_client_get_next_fragment_timestamp (GstMPDClient * client,
1892 guint stream_idx, GstClockTime * ts)
1894 GstActiveStream *stream;
1895 GstMediaSegment *currentChunk;
1897 GST_DEBUG ("Stream index: %i", stream_idx);
1898 stream = g_list_nth_data (client->active_streams, stream_idx);
1899 g_return_val_if_fail (stream != NULL, 0);
1901 if (stream->segments) {
1902 GST_DEBUG ("Looking for fragment sequence chunk %d / %d",
1903 stream->segment_index, stream->segments->len);
1904 if (stream->segment_index >= stream->segments->len)
1906 currentChunk = g_ptr_array_index (stream->segments, stream->segment_index);
1909 currentChunk->start +
1910 (currentChunk->duration * stream->segment_repeat_index);
1912 GstClockTime duration =
1913 gst_mpd_client_get_segment_duration (client, stream, NULL);
1914 guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
1916 g_return_val_if_fail (GST_MPD_MULT_SEGMENT_BASE_NODE
1917 (stream->cur_seg_template)->SegmentTimeline == NULL, FALSE);
1918 if (!GST_CLOCK_TIME_IS_VALID (duration) || (segments_count > 0
1919 && stream->segment_index >= segments_count)) {
1922 *ts = stream->segment_index * duration;
1929 gst_mpd_client_get_stream_presentation_offset (GstMPDClient * client,
1932 GstActiveStream *stream = NULL;
1934 g_return_val_if_fail (client != NULL, 0);
1935 g_return_val_if_fail (client->active_streams != NULL, 0);
1936 stream = g_list_nth_data (client->active_streams, stream_idx);
1937 g_return_val_if_fail (stream != NULL, 0);
1939 return stream->presentationTimeOffset;
1943 gst_mpd_client_get_period_start_time (GstMPDClient * client)
1945 GstStreamPeriod *stream_period = NULL;
1947 g_return_val_if_fail (client != NULL, 0);
1948 stream_period = gst_mpd_client_get_stream_period (client);
1949 g_return_val_if_fail (stream_period != NULL, 0);
1951 return stream_period->start;
1955 * gst_mpd_client_get_utc_timing_sources:
1956 * @client: #GstMPDClient to check for UTCTiming elements
1957 * @methods: A bit mask of #GstMPDUTCTimingType that specifies the methods
1959 * @selected_method: (nullable): The selected method
1960 * Returns: (transfer none): A NULL terminated array of URLs of servers
1961 * that use @selected_method to provide a realtime clock.
1963 * Searches the UTCTiming elements found in the manifest for an element
1964 * that uses one of the UTC timing methods specified in @selected_method.
1965 * If multiple UTCTiming elements are present that support one of the
1966 * methods specified in @selected_method, the first one is returned.
1971 gst_mpd_client_get_utc_timing_sources (GstMPDClient * client,
1972 guint methods, GstMPDUTCTimingType * selected_method)
1976 g_return_val_if_fail (client != NULL, NULL);
1977 g_return_val_if_fail (client->mpd_root_node != NULL, NULL);
1978 for (list = g_list_first (client->mpd_root_node->UTCTimings); list;
1979 list = g_list_next (list)) {
1980 const GstMPDUTCTimingNode *node = (const GstMPDUTCTimingNode *) list->data;
1981 if (node->method & methods) {
1982 if (selected_method) {
1983 *selected_method = node->method;
1993 gst_mpd_client_get_next_fragment (GstMPDClient * client,
1994 guint indexStream, GstMediaFragmentInfo * fragment)
1996 GstActiveStream *stream = NULL;
1997 GstMediaSegment *currentChunk;
1998 gchar *mediaURL = NULL;
1999 gchar *indexURL = NULL;
2000 GstUri *base_url, *frag_url;
2003 g_return_val_if_fail (client != NULL, FALSE);
2004 g_return_val_if_fail (client->active_streams != NULL, FALSE);
2005 stream = g_list_nth_data (client->active_streams, indexStream);
2006 g_return_val_if_fail (stream != NULL, FALSE);
2007 g_return_val_if_fail (stream->cur_representation != NULL, FALSE);
2009 if (stream->segments) {
2010 GST_DEBUG ("Looking for fragment sequence chunk %d / %d",
2011 stream->segment_index, stream->segments->len);
2012 if (stream->segment_index >= stream->segments->len)
2015 GstClockTime duration = gst_mpd_client_get_segment_duration (client,
2017 guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
2019 g_return_val_if_fail (GST_MPD_MULT_SEGMENT_BASE_NODE
2020 (stream->cur_seg_template)->SegmentTimeline == NULL, FALSE);
2021 if (!GST_CLOCK_TIME_IS_VALID (duration) || (segments_count > 0
2022 && stream->segment_index >= segments_count)) {
2025 fragment->duration = duration;
2028 /* FIXME rework discont checking */
2029 /* fragment->discontinuity = segment_idx != currentChunk.number; */
2030 fragment->range_start = 0;
2031 fragment->range_end = -1;
2032 fragment->index_uri = NULL;
2033 fragment->index_range_start = 0;
2034 fragment->index_range_end = -1;
2036 if (stream->segments) {
2037 currentChunk = g_ptr_array_index (stream->segments, stream->segment_index);
2039 GST_DEBUG ("currentChunk->SegmentURL = %p", currentChunk->SegmentURL);
2040 if (currentChunk->SegmentURL != NULL) {
2042 g_strdup (gst_mpdparser_get_mediaURL (stream,
2043 currentChunk->SegmentURL));
2044 indexURL = g_strdup (currentChunk->SegmentURL->index);
2045 } else if (stream->cur_seg_template != NULL) {
2047 gst_mpdparser_build_URL_from_template (stream->cur_seg_template->
2048 media, stream->cur_representation->id,
2049 currentChunk->number + stream->segment_repeat_index,
2050 stream->cur_representation->bandwidth,
2051 currentChunk->scale_start +
2052 stream->segment_repeat_index * currentChunk->scale_duration);
2053 if (stream->cur_seg_template->index) {
2055 gst_mpdparser_build_URL_from_template (stream->cur_seg_template->
2056 index, stream->cur_representation->id,
2057 currentChunk->number + stream->segment_repeat_index,
2058 stream->cur_representation->bandwidth,
2059 currentChunk->scale_start +
2060 stream->segment_repeat_index * currentChunk->scale_duration);
2063 GST_DEBUG ("mediaURL = %s", mediaURL);
2064 GST_DEBUG ("indexURL = %s", indexURL);
2066 fragment->timestamp =
2067 currentChunk->start +
2068 stream->segment_repeat_index * currentChunk->duration;
2069 fragment->duration = currentChunk->duration;
2070 if (currentChunk->SegmentURL) {
2071 if (currentChunk->SegmentURL->mediaRange) {
2072 fragment->range_start =
2073 currentChunk->SegmentURL->mediaRange->first_byte_pos;
2074 fragment->range_end =
2075 currentChunk->SegmentURL->mediaRange->last_byte_pos;
2077 if (currentChunk->SegmentURL->indexRange) {
2078 fragment->index_range_start =
2079 currentChunk->SegmentURL->indexRange->first_byte_pos;
2080 fragment->index_range_end =
2081 currentChunk->SegmentURL->indexRange->last_byte_pos;
2085 if (stream->cur_seg_template != NULL) {
2087 gst_mpdparser_build_URL_from_template (stream->cur_seg_template->
2088 media, stream->cur_representation->id,
2089 stream->segment_index +
2090 GST_MPD_MULT_SEGMENT_BASE_NODE (stream->
2091 cur_seg_template)->startNumber,
2092 stream->cur_representation->bandwidth,
2093 stream->segment_index * fragment->duration);
2094 if (stream->cur_seg_template->index) {
2096 gst_mpdparser_build_URL_from_template (stream->cur_seg_template->
2097 index, stream->cur_representation->id,
2098 stream->segment_index +
2099 GST_MPD_MULT_SEGMENT_BASE_NODE (stream->
2100 cur_seg_template)->startNumber,
2101 stream->cur_representation->bandwidth,
2102 stream->segment_index * fragment->duration);
2108 GST_DEBUG ("mediaURL = %s", mediaURL);
2109 GST_DEBUG ("indexURL = %s", indexURL);
2111 fragment->timestamp = stream->segment_index * fragment->duration;
2114 base_url = gst_uri_from_string (stream->baseURL);
2115 frag_url = gst_uri_from_string_with_base (base_url, mediaURL);
2117 if (stream->queryURL) {
2118 frag_url = gst_uri_make_writable (frag_url);
2119 gst_uri_set_query_string (frag_url, stream->queryURL);
2121 fragment->uri = gst_uri_to_string (frag_url);
2122 gst_uri_unref (frag_url);
2124 if (indexURL != NULL) {
2125 frag_url = gst_uri_make_writable (gst_uri_from_string_with_base (base_url,
2127 gst_uri_set_query_string (frag_url, stream->queryURL);
2128 fragment->index_uri = gst_uri_to_string (frag_url);
2129 gst_uri_unref (frag_url);
2131 } else if (indexURL == NULL && (fragment->index_range_start
2132 || fragment->index_range_end != -1)) {
2133 /* index has no specific URL but has a range, we should only use this if
2134 * the media also has a range, otherwise we are serving some data twice
2135 * (in the media fragment and again in the index) */
2136 if (!(fragment->range_start || fragment->range_end != -1)) {
2137 GST_WARNING ("Ignoring index ranges because there isn't a media range "
2138 "and URIs would be the same");
2139 /* removing index information */
2140 fragment->index_range_start = 0;
2141 fragment->index_range_end = -1;
2145 gst_uri_unref (base_url);
2147 GST_DEBUG ("Loading chunk with URL %s", fragment->uri);
2153 gst_mpd_client_has_next_segment (GstMPDClient * client,
2154 GstActiveStream * stream, gboolean forward)
2157 guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
2159 if (segments_count > 0 && stream->segments
2160 && stream->segment_index + 1 == segments_count) {
2161 GstMediaSegment *segment;
2163 segment = g_ptr_array_index (stream->segments, stream->segment_index);
2164 if (segment->repeat >= 0
2165 && stream->segment_repeat_index >= segment->repeat)
2167 } else if (segments_count > 0
2168 && stream->segment_index + 1 >= segments_count) {
2172 if (stream->segment_index < 0)
2180 gst_mpd_client_advance_segment (GstMPDClient * client, GstActiveStream * stream,
2183 GstMediaSegment *segment;
2184 GstFlowReturn ret = GST_FLOW_OK;
2185 guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
2187 GST_DEBUG ("Advancing segment. Current: %d / %d r:%d", stream->segment_index,
2188 segments_count, stream->segment_repeat_index);
2190 /* handle special cases first */
2192 if (segments_count > 0 && stream->segment_index >= segments_count) {
2197 if (stream->segments == NULL) {
2198 if (stream->segment_index < 0) {
2199 stream->segment_index = 0;
2201 stream->segment_index++;
2202 if (segments_count > 0 && stream->segment_index >= segments_count) {
2209 /* special case for when playback direction is reverted right at *
2210 * the end of the segment list */
2211 if (stream->segment_index < 0) {
2212 stream->segment_index = 0;
2216 if (stream->segments == NULL)
2217 stream->segment_index--;
2218 if (stream->segment_index < 0) {
2219 stream->segment_index = -1;
2223 if (stream->segments == NULL)
2226 /* special case for when playback direction is reverted right at *
2227 * the end of the segment list */
2228 if (stream->segment_index >= segments_count) {
2229 stream->segment_index = segments_count - 1;
2230 segment = g_ptr_array_index (stream->segments, stream->segment_index);
2231 if (segment->repeat >= 0) {
2232 stream->segment_repeat_index = segment->repeat;
2234 GstClockTime start = segment->start;
2236 gst_mpd_client_get_segment_end_time (client, stream->segments,
2238 stream->segment_index);
2239 stream->segment_repeat_index =
2240 (guint) (end - start) / segment->duration;
2246 /* for the normal cases we can get the segment safely here */
2247 segment = g_ptr_array_index (stream->segments, stream->segment_index);
2249 if (segment->repeat >= 0 && stream->segment_repeat_index >= segment->repeat) {
2250 stream->segment_repeat_index = 0;
2251 stream->segment_index++;
2252 if (segments_count > 0 && stream->segment_index >= segments_count) {
2257 stream->segment_repeat_index++;
2260 if (stream->segment_repeat_index == 0) {
2261 stream->segment_index--;
2262 if (stream->segment_index < 0) {
2267 segment = g_ptr_array_index (stream->segments, stream->segment_index);
2268 /* negative repeats only seem to make sense at the end of a list,
2269 * so this one will probably not be. Needs some sanity checking
2270 * when loading the XML data. */
2271 if (segment->repeat >= 0) {
2272 stream->segment_repeat_index = segment->repeat;
2274 GstClockTime start = segment->start;
2276 gst_mpd_client_get_segment_end_time (client, stream->segments,
2278 stream->segment_index);
2279 stream->segment_repeat_index =
2280 (guint) (end - start) / segment->duration;
2283 stream->segment_repeat_index--;
2288 GST_DEBUG ("Advanced to segment: %d / %d r:%d (ret: %s)",
2289 stream->segment_index, segments_count,
2290 stream->segment_repeat_index, gst_flow_get_name (ret));
2295 gst_mpd_client_get_next_header (GstMPDClient * client, gchar ** uri,
2296 guint stream_idx, gint64 * range_start, gint64 * range_end)
2298 GstActiveStream *stream;
2299 GstStreamPeriod *stream_period;
2301 stream = gst_mpd_client_get_active_stream_by_index (client, stream_idx);
2302 g_return_val_if_fail (stream != NULL, FALSE);
2303 g_return_val_if_fail (stream->cur_representation != NULL, FALSE);
2304 stream_period = gst_mpd_client_get_stream_period (client);
2305 g_return_val_if_fail (stream_period != NULL, FALSE);
2306 g_return_val_if_fail (stream_period->period != NULL, FALSE);
2311 GST_DEBUG ("Looking for current representation header");
2313 if (stream->cur_segment_base) {
2314 if (stream->cur_segment_base->Initialization) {
2316 g_strdup (gst_mpdparser_get_initializationURL (stream,
2317 stream->cur_segment_base->Initialization));
2318 if (stream->cur_segment_base->Initialization->range) {
2320 stream->cur_segment_base->Initialization->range->first_byte_pos;
2322 stream->cur_segment_base->Initialization->range->last_byte_pos;
2324 } else if (stream->cur_segment_base->indexRange) {
2326 g_strdup (gst_mpdparser_get_initializationURL (stream,
2327 stream->cur_segment_base->Initialization));
2329 *range_end = stream->cur_segment_base->indexRange->first_byte_pos - 1;
2331 } else if (stream->cur_seg_template
2332 && stream->cur_seg_template->initialization) {
2334 gst_mpdparser_build_URL_from_template (stream->cur_seg_template->
2335 initialization, stream->cur_representation->id, 0,
2336 stream->cur_representation->bandwidth, 0);
2339 return *uri == NULL ? FALSE : TRUE;
2343 gst_mpd_client_get_next_header_index (GstMPDClient * client, gchar ** uri,
2344 guint stream_idx, gint64 * range_start, gint64 * range_end)
2346 GstActiveStream *stream;
2347 GstStreamPeriod *stream_period;
2349 stream = gst_mpd_client_get_active_stream_by_index (client, stream_idx);
2350 g_return_val_if_fail (stream != NULL, FALSE);
2351 g_return_val_if_fail (stream->cur_representation != NULL, FALSE);
2352 stream_period = gst_mpd_client_get_stream_period (client);
2353 g_return_val_if_fail (stream_period != NULL, FALSE);
2354 g_return_val_if_fail (stream_period->period != NULL, FALSE);
2359 GST_DEBUG ("Looking for current representation index");
2361 if (stream->cur_segment_base && stream->cur_segment_base->indexRange) {
2363 g_strdup (gst_mpdparser_get_initializationURL (stream,
2364 stream->cur_segment_base->RepresentationIndex));
2365 *range_start = stream->cur_segment_base->indexRange->first_byte_pos;
2366 *range_end = stream->cur_segment_base->indexRange->last_byte_pos;
2367 } else if (stream->cur_seg_template && stream->cur_seg_template->index) {
2369 gst_mpdparser_build_URL_from_template (stream->cur_seg_template->index,
2370 stream->cur_representation->id, 0,
2371 stream->cur_representation->bandwidth, 0);
2374 return *uri == NULL ? FALSE : TRUE;
2378 gst_mpd_client_get_next_fragment_duration (GstMPDClient * client,
2379 GstActiveStream * stream)
2381 GstMediaSegment *media_segment = NULL;
2384 g_return_val_if_fail (stream != NULL, 0);
2386 seg_idx = stream->segment_index;
2388 if (stream->segments) {
2389 if (seg_idx < stream->segments->len && seg_idx >= 0)
2390 media_segment = g_ptr_array_index (stream->segments, seg_idx);
2392 return media_segment == NULL ? 0 : media_segment->duration;
2394 GstClockTime duration =
2395 gst_mpd_client_get_segment_duration (client, stream, NULL);
2396 guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
2398 g_return_val_if_fail (GST_MPD_MULT_SEGMENT_BASE_NODE
2399 (stream->cur_seg_template)->SegmentTimeline == NULL, 0);
2401 if (!GST_CLOCK_TIME_IS_VALID (duration) || (segments_count > 0
2402 && seg_idx >= segments_count)) {
2410 gst_mpd_client_get_media_presentation_duration (GstMPDClient * client)
2412 GstClockTime duration;
2414 g_return_val_if_fail (client != NULL, GST_CLOCK_TIME_NONE);
2416 if (client->mpd_root_node->mediaPresentationDuration != -1) {
2417 duration = client->mpd_root_node->mediaPresentationDuration * GST_MSECOND;
2419 /* We can only get the duration for on-demand streams */
2420 duration = GST_CLOCK_TIME_NONE;
2427 gst_mpd_client_set_period_id (GstMPDClient * client, const gchar * period_id)
2429 GstStreamPeriod *next_stream_period;
2430 gboolean ret = FALSE;
2434 g_return_val_if_fail (client != NULL, FALSE);
2435 g_return_val_if_fail (client->periods != NULL, FALSE);
2436 g_return_val_if_fail (period_id != NULL, FALSE);
2438 if (!gst_mpd_client_setup_media_presentation (client, GST_CLOCK_TIME_NONE, -1,
2442 for (period_idx = 0, iter = client->periods; iter;
2443 period_idx++, iter = g_list_next (iter)) {
2444 next_stream_period = iter->data;
2446 if (next_stream_period->period->id
2447 && strcmp (next_stream_period->period->id, period_id) == 0) {
2449 client->period_idx = period_idx;
2458 gst_mpd_client_set_period_index (GstMPDClient * client, guint period_idx)
2460 GstStreamPeriod *next_stream_period;
2461 gboolean ret = FALSE;
2463 g_return_val_if_fail (client != NULL, FALSE);
2464 g_return_val_if_fail (client->periods != NULL, FALSE);
2466 if (!gst_mpd_client_setup_media_presentation (client, -1, period_idx, NULL))
2469 next_stream_period = g_list_nth_data (client->periods, period_idx);
2470 if (next_stream_period != NULL) {
2471 client->period_idx = period_idx;
2479 gst_mpd_client_get_period_index (GstMPDClient * client)
2483 g_return_val_if_fail (client != NULL, 0);
2484 period_idx = client->period_idx;
2490 gst_mpd_client_get_period_id (GstMPDClient * client)
2492 GstStreamPeriod *period;
2493 gchar *period_id = NULL;
2495 g_return_val_if_fail (client != NULL, 0);
2496 period = g_list_nth_data (client->periods, client->period_idx);
2497 if (period && period->period)
2498 period_id = period->period->id;
2504 gst_mpd_client_has_next_period (GstMPDClient * client)
2506 GList *next_stream_period;
2507 g_return_val_if_fail (client != NULL, FALSE);
2508 g_return_val_if_fail (client->periods != NULL, FALSE);
2510 if (!gst_mpd_client_setup_media_presentation (client, GST_CLOCK_TIME_NONE,
2511 client->period_idx + 1, NULL))
2514 next_stream_period =
2515 g_list_nth_data (client->periods, client->period_idx + 1);
2516 return next_stream_period != NULL;
2520 gst_mpd_client_has_previous_period (GstMPDClient * client)
2522 GList *next_stream_period;
2523 g_return_val_if_fail (client != NULL, FALSE);
2524 g_return_val_if_fail (client->periods != NULL, FALSE);
2526 if (!gst_mpd_client_setup_media_presentation (client, GST_CLOCK_TIME_NONE,
2527 client->period_idx - 1, NULL))
2530 next_stream_period =
2531 g_list_nth_data (client->periods, client->period_idx - 1);
2533 return next_stream_period != NULL;
2537 gst_mpd_client_get_rep_idx_with_min_bandwidth (GList * Representations)
2539 GList *list = NULL, *lowest = NULL;
2540 GstMPDRepresentationNode *rep = NULL;
2541 gint lowest_bandwidth = -1;
2543 if (Representations == NULL)
2546 for (list = g_list_first (Representations); list; list = g_list_next (list)) {
2547 rep = (GstMPDRepresentationNode *) list->data;
2548 if (rep && (!lowest || rep->bandwidth < lowest_bandwidth)) {
2550 lowest_bandwidth = rep->bandwidth;
2554 return lowest ? g_list_position (Representations, lowest) : -1;
2558 gst_mpd_client_get_rep_idx_with_max_bandwidth (GList * Representations,
2559 gint64 max_bandwidth, gint max_video_width, gint max_video_height, gint
2560 max_video_framerate_n, gint max_video_framerate_d)
2562 GList *list = NULL, *best = NULL;
2563 GstMPDRepresentationNode *representation;
2564 gint best_bandwidth = 0;
2566 GST_DEBUG ("max_bandwidth = %" G_GINT64_FORMAT, max_bandwidth);
2568 if (Representations == NULL)
2571 if (max_bandwidth <= 0) /* 0 => get lowest representation available */
2572 return gst_mpd_client_get_rep_idx_with_min_bandwidth (Representations);
2574 for (list = g_list_first (Representations); list; list = g_list_next (list)) {
2575 GstXMLFrameRate *framerate = NULL;
2577 representation = (GstMPDRepresentationNode *) list->data;
2579 /* FIXME: Really? */
2580 if (!representation)
2583 framerate = GST_MPD_REPRESENTATION_BASE_NODE (representation)->frameRate;
2586 GST_MPD_REPRESENTATION_BASE_NODE (representation)->maxFrameRate;
2588 if (framerate && max_video_framerate_n > 0) {
2589 if (gst_util_fraction_compare (framerate->num, framerate->den,
2590 max_video_framerate_n, max_video_framerate_d) > 0)
2594 if (max_video_width > 0
2595 && GST_MPD_REPRESENTATION_BASE_NODE (representation)->width >
2598 if (max_video_height > 0
2599 && GST_MPD_REPRESENTATION_BASE_NODE (representation)->height >
2603 if (representation->bandwidth <= max_bandwidth &&
2604 representation->bandwidth > best_bandwidth) {
2606 best_bandwidth = representation->bandwidth;
2610 return best ? g_list_position (Representations, best) : -1;
2614 gst_mpd_client_seek_to_first_segment (GstMPDClient * client)
2618 g_return_if_fail (client != NULL);
2619 g_return_if_fail (client->active_streams != NULL);
2621 for (list = g_list_first (client->active_streams); list;
2622 list = g_list_next (list)) {
2623 GstActiveStream *stream = (GstActiveStream *) list->data;
2625 stream->segment_index = 0;
2626 stream->segment_repeat_index = 0;
2632 gst_mpd_client_get_segments_counts (GstMPDClient * client,
2633 GstActiveStream * stream)
2635 GstStreamPeriod *stream_period;
2637 g_return_val_if_fail (stream != NULL, 0);
2639 if (stream->segments)
2640 return stream->segments->len;
2641 g_return_val_if_fail (GST_MPD_MULT_SEGMENT_BASE_NODE
2642 (stream->cur_seg_template)->SegmentTimeline == NULL, 0);
2644 stream_period = gst_mpd_client_get_stream_period (client);
2645 if (stream_period->duration != -1)
2646 return gst_util_uint64_scale_ceil (stream_period->duration, 1,
2647 gst_mpd_client_get_segment_duration (client, stream, NULL));
2653 gst_mpd_client_is_live (GstMPDClient * client)
2655 g_return_val_if_fail (client != NULL, FALSE);
2656 g_return_val_if_fail (client->mpd_root_node != NULL, FALSE);
2658 return client->mpd_root_node->type == GST_MPD_FILE_TYPE_DYNAMIC;
2662 gst_mpd_client_get_nb_active_stream (GstMPDClient * client)
2664 g_return_val_if_fail (client != NULL, 0);
2666 return g_list_length (client->active_streams);
2670 gst_mpd_client_get_nb_adaptationSet (GstMPDClient * client)
2672 GstStreamPeriod *stream_period;
2674 stream_period = gst_mpd_client_get_stream_period (client);
2675 g_return_val_if_fail (stream_period != NULL, 0);
2676 g_return_val_if_fail (stream_period->period != NULL, 0);
2678 return g_list_length (stream_period->period->AdaptationSets);
2682 gst_mpd_client_get_active_stream_by_index (GstMPDClient * client,
2685 g_return_val_if_fail (client != NULL, NULL);
2686 g_return_val_if_fail (client->active_streams != NULL, NULL);
2688 return g_list_nth_data (client->active_streams, stream_idx);
2692 gst_mpd_client_active_stream_contains_subtitles (GstActiveStream * stream)
2694 const gchar *mimeType;
2695 const gchar *adapt_set_codecs;
2696 const gchar *rep_codecs;
2699 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_representation)->mimeType;
2702 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->mimeType;
2704 if (g_strcmp0 (mimeType, "application/ttml+xml") == 0 ||
2705 g_strcmp0 (mimeType, "text/vtt") == 0)
2709 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->codecs;
2711 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_representation)->codecs;
2713 return (adapt_set_codecs && g_str_has_prefix (adapt_set_codecs, "stpp"))
2714 || (rep_codecs && g_str_has_prefix (rep_codecs, "stpp"));
2718 gst_mpd_client_get_stream_caps (GstActiveStream * stream)
2720 const gchar *mimeType, *caps_string;
2721 GstCaps *ret = NULL;
2723 if (stream == NULL || stream->cur_adapt_set == NULL
2724 || stream->cur_representation == NULL)
2728 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_representation)->mimeType;
2729 if (mimeType == NULL) {
2731 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->mimeType;
2734 caps_string = gst_mpd_helper_mimetype_to_caps (mimeType);
2736 if ((g_strcmp0 (caps_string, "application/mp4") == 0)
2737 && gst_mpd_client_active_stream_contains_subtitles (stream))
2738 caps_string = "video/quicktime";
2741 ret = gst_caps_from_string (caps_string);
2747 gst_mpd_client_get_bitstream_switching_flag (GstActiveStream * stream)
2749 if (stream == NULL || stream->cur_adapt_set == NULL)
2752 return stream->cur_adapt_set->bitstreamSwitching;
2756 gst_mpd_client_get_video_stream_width (GstActiveStream * stream)
2760 if (stream == NULL || stream->cur_adapt_set == NULL
2761 || stream->cur_representation == NULL)
2764 width = GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_representation)->width;
2766 width = GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->width;
2773 gst_mpd_client_get_video_stream_height (GstActiveStream * stream)
2777 if (stream == NULL || stream->cur_adapt_set == NULL
2778 || stream->cur_representation == NULL)
2782 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_representation)->height;
2784 height = GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->height;
2791 gst_mpd_client_get_video_stream_framerate (GstActiveStream * stream,
2792 gint * fps_num, gint * fps_den)
2797 if (stream->cur_adapt_set &&
2798 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->frameRate !=
2801 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->
2804 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->
2809 if (stream->cur_adapt_set &&
2810 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->maxFrameRate !=
2813 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->
2816 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->
2821 if (stream->cur_representation &&
2822 GST_MPD_REPRESENTATION_BASE_NODE (stream->
2823 cur_representation)->frameRate != NULL) {
2825 GST_MPD_REPRESENTATION_BASE_NODE (stream->
2826 cur_representation)->frameRate->num;
2828 GST_MPD_REPRESENTATION_BASE_NODE (stream->
2829 cur_representation)->frameRate->den;
2833 if (stream->cur_representation &&
2834 GST_MPD_REPRESENTATION_BASE_NODE (stream->
2835 cur_representation)->maxFrameRate != NULL) {
2837 GST_MPD_REPRESENTATION_BASE_NODE (stream->
2838 cur_representation)->maxFrameRate->num;
2840 GST_MPD_REPRESENTATION_BASE_NODE (stream->
2841 cur_representation)->maxFrameRate->den;
2849 gst_mpd_client_get_audio_stream_rate (GstActiveStream * stream)
2853 if (stream == NULL || stream->cur_adapt_set == NULL
2854 || stream->cur_representation == NULL)
2858 GST_MPD_REPRESENTATION_BASE_NODE (stream->
2859 cur_representation)->audioSamplingRate;
2862 GST_MPD_REPRESENTATION_BASE_NODE (stream->
2863 cur_adapt_set)->audioSamplingRate;
2866 return rate ? atoi (rate) : 0;
2870 gst_mpd_client_get_audio_stream_num_channels (GstActiveStream * stream)
2872 if (stream == NULL || stream->cur_adapt_set == NULL
2873 || stream->cur_representation == NULL)
2875 /* TODO: here we have to parse the AudioChannelConfiguration descriptors */
2880 gst_mpd_client_get_list_and_nb_of_audio_language (GstMPDClient * client,
2883 GstStreamPeriod *stream_period;
2884 GstMPDAdaptationSetNode *adapt_set;
2885 GList *adaptation_sets, *list;
2886 const gchar *this_mimeType = "audio";
2887 gchar *mimeType = NULL;
2888 guint nb_adaptation_set = 0;
2890 stream_period = gst_mpd_client_get_stream_period (client);
2891 g_return_val_if_fail (stream_period != NULL, 0);
2892 g_return_val_if_fail (stream_period->period != NULL, 0);
2895 gst_mpd_client_get_adaptation_sets_for_period (client, stream_period);
2896 for (list = adaptation_sets; list; list = g_list_next (list)) {
2897 adapt_set = (GstMPDAdaptationSetNode *) list->data;
2898 if (adapt_set && adapt_set->lang) {
2899 gchar *this_lang = adapt_set->lang;
2900 GstMPDRepresentationNode *rep;
2902 gst_mpd_client_get_lowest_representation (adapt_set->Representations);
2904 if (GST_MPD_REPRESENTATION_BASE_NODE (rep))
2905 mimeType = GST_MPD_REPRESENTATION_BASE_NODE (rep)->mimeType;
2906 if (!mimeType && GST_MPD_REPRESENTATION_BASE_NODE (adapt_set)) {
2907 mimeType = GST_MPD_REPRESENTATION_BASE_NODE (adapt_set)->mimeType;
2910 if (gst_mpd_helper_strncmp_ext (mimeType, this_mimeType) == 0) {
2911 nb_adaptation_set++;
2912 *lang = g_list_append (*lang, this_lang);
2917 return nb_adaptation_set;
2922 gst_mpd_client_get_next_segment_availability_start_time (GstMPDClient * client,
2923 GstActiveStream * stream)
2925 GstDateTime *availability_start_time, *rv;
2927 GstMediaSegment *segment;
2928 GstClockTime segmentEndTime;
2929 const GstStreamPeriod *stream_period;
2930 GstClockTime period_start = 0;
2932 g_return_val_if_fail (client != NULL, NULL);
2933 g_return_val_if_fail (stream != NULL, NULL);
2935 stream_period = gst_mpd_client_get_stream_period (client);
2936 if (stream_period && stream_period->period) {
2937 period_start = stream_period->start;
2940 seg_idx = stream->segment_index;
2942 if (stream->segments) {
2943 segment = g_ptr_array_index (stream->segments, seg_idx);
2945 if (segment->repeat >= 0) {
2946 segmentEndTime = segment->start + (stream->segment_repeat_index + 1) *
2948 } else if (seg_idx < stream->segments->len - 1) {
2949 const GstMediaSegment *next_segment =
2950 g_ptr_array_index (stream->segments, seg_idx + 1);
2951 segmentEndTime = next_segment->start;
2953 g_return_val_if_fail (stream_period != NULL, NULL);
2954 segmentEndTime = period_start + stream_period->duration;
2957 GstClockTime seg_duration;
2958 seg_duration = gst_mpd_client_get_segment_duration (client, stream, NULL);
2959 if (seg_duration == 0)
2961 segmentEndTime = period_start + (1 + seg_idx) * seg_duration;
2964 availability_start_time = gst_mpd_client_get_availability_start_time (client);
2965 if (availability_start_time == NULL) {
2966 GST_WARNING_OBJECT (client, "Failed to get availability_start_time");
2970 rv = gst_mpd_client_add_time_difference (availability_start_time,
2971 segmentEndTime / GST_USECOND);
2972 gst_date_time_unref (availability_start_time);
2974 GST_WARNING_OBJECT (client, "Failed to offset availability_start_time");
2982 gst_mpd_client_seek_to_time (GstMPDClient * client, GDateTime * time)
2985 GTimeSpan ts_microseconds;
2987 gboolean ret = TRUE;
2990 g_return_val_if_fail (gst_mpd_client_is_live (client), FALSE);
2991 g_return_val_if_fail (client->mpd_root_node->availabilityStartTime != NULL,
2995 gst_date_time_to_g_date_time (client->mpd_root_node->
2996 availabilityStartTime);
2998 ts_microseconds = g_date_time_difference (time, start);
2999 g_date_time_unref (start);
3001 /* Clamp to availability start time, otherwise calculations wrap around */
3002 if (ts_microseconds < 0)
3003 ts_microseconds = 0;
3005 ts = ts_microseconds * GST_USECOND;
3006 for (stream = client->active_streams; stream; stream = g_list_next (stream)) {
3008 ret & gst_mpd_client_stream_seek (client, stream->data, TRUE, 0, ts,
3015 gst_mpd_client_has_isoff_ondemand_profile (GstMPDClient * client)
3017 return client->profile_isoff_ondemand;
3021 * gst_mpd_client_parse_default_presentation_delay:
3022 * @client: #GstMPDClient that has a parsed manifest
3023 * @default_presentation_delay: A string that specifies a time period
3024 * in fragments (e.g. "5 f"), seconds ("12 s") or milliseconds
3026 * Returns: the parsed string in milliseconds
3031 gst_mpd_client_parse_default_presentation_delay (GstMPDClient * client,
3032 const gchar * default_presentation_delay)
3035 char *endptr = NULL;
3037 g_return_val_if_fail (client != NULL, 0);
3038 g_return_val_if_fail (default_presentation_delay != NULL, 0);
3039 value = strtol (default_presentation_delay, &endptr, 10);
3040 if (endptr == default_presentation_delay || value == 0) {
3043 while (*endptr == ' ')
3045 if (*endptr == 's' || *endptr == 'S') {
3046 value *= 1000; /* convert to ms */
3047 } else if (*endptr == 'f' || *endptr == 'F') {
3048 gint64 segment_duration;
3049 g_assert (client->mpd_root_node != NULL);
3050 segment_duration = client->mpd_root_node->maxSegmentDuration;
3051 value *= segment_duration;
3052 } else if (*endptr != 'm' && *endptr != 'M') {
3053 GST_ERROR ("Unable to parse default presentation delay: %s",
3054 default_presentation_delay);
3061 gst_mpd_client_get_maximum_segment_duration (GstMPDClient * client)
3063 GstClockTime ret = GST_CLOCK_TIME_NONE, dur;
3066 g_return_val_if_fail (client != NULL, GST_CLOCK_TIME_NONE);
3067 g_return_val_if_fail (client->mpd_root_node != NULL, GST_CLOCK_TIME_NONE);
3069 if (client->mpd_root_node->maxSegmentDuration != GST_MPD_DURATION_NONE) {
3070 return client->mpd_root_node->maxSegmentDuration * GST_MSECOND;
3073 /* According to the DASH specification, if maxSegmentDuration is not present:
3074 "If not present, then the maximum Segment duration shall be the maximum
3075 duration of any Segment documented in this MPD"
3077 for (stream = client->active_streams; stream; stream = g_list_next (stream)) {
3078 dur = gst_mpd_client_get_segment_duration (client, stream->data, NULL);
3079 if (dur != GST_CLOCK_TIME_NONE && (dur > ret || ret == GST_CLOCK_TIME_NONE)) {
3087 gst_mpd_client_get_period_index_at_time (GstMPDClient * client,
3091 guint period_idx = G_MAXUINT;
3094 GstDateTime *avail_start =
3095 gst_mpd_client_get_availability_start_time (client);
3096 GstStreamPeriod *stream_period;
3098 if (avail_start == NULL)
3101 time_offset = gst_mpd_client_calculate_time_difference (avail_start, time);
3102 gst_date_time_unref (avail_start);
3104 if (time_offset < 0)
3107 if (!gst_mpd_client_setup_media_presentation (client, time_offset, -1, NULL))
3110 for (idx = 0, iter = client->periods; iter; idx++, iter = g_list_next (iter)) {
3111 stream_period = iter->data;
3112 if (stream_period->start <= time_offset
3113 && (!GST_CLOCK_TIME_IS_VALID (stream_period->duration)
3114 || stream_period->start + stream_period->duration > time_offset)) {
3123 /* add or set node methods */
3126 gst_mpd_client_set_root_node (GstMPDClient * client,
3127 const gchar * property_name, ...)
3130 g_return_val_if_fail (client != NULL, FALSE);
3132 if (!client->mpd_root_node)
3133 client->mpd_root_node = gst_mpd_root_node_new ();
3135 va_start (myargs, property_name);
3136 g_object_set_valist (G_OBJECT (client->mpd_root_node), property_name, myargs);
3143 gst_mpd_client_add_baseurl_node (GstMPDClient * client,
3144 const gchar * property_name, ...)
3146 GstMPDBaseURLNode *baseurl_node = NULL;
3149 g_return_val_if_fail (client != NULL, FALSE);
3150 g_return_val_if_fail (client->mpd_root_node != NULL, FALSE);
3152 va_start (myargs, property_name);
3154 baseurl_node = gst_mpd_baseurl_node_new ();
3155 g_object_set_valist (G_OBJECT (baseurl_node), property_name, myargs);
3156 client->mpd_root_node->BaseURLs =
3157 g_list_append (client->mpd_root_node->BaseURLs, baseurl_node);
3163 /* returns a period id */
3165 gst_mpd_client_set_period_node (GstMPDClient * client,
3166 gchar * period_id, const gchar * property_name, ...)
3168 GstMPDPeriodNode *period_node = NULL;
3171 g_return_val_if_fail (client != NULL, NULL);
3172 g_return_val_if_fail (client->mpd_root_node != NULL, NULL);
3175 GST_MPD_PERIOD_NODE (gst_mpd_client_get_period_with_id
3176 (client->mpd_root_node->Periods, period_id));
3178 period_node = gst_mpd_period_node_new ();
3180 period_node->id = g_strdup (period_id);
3183 _generate_new_string_id (client->mpd_root_node->Periods,
3184 "period_%.2d", gst_mpd_client_get_period_with_id);
3185 client->mpd_root_node->Periods =
3186 g_list_append (client->mpd_root_node->Periods, period_node);
3189 va_start (myargs, property_name);
3190 g_object_set_valist (G_OBJECT (period_node), property_name, myargs);
3193 return period_node->id;
3196 /* returns an adaptation set id */
3198 gst_mpd_client_set_adaptation_set_node (GstMPDClient * client,
3199 gchar * period_id, guint adaptation_set_id, const gchar * property_name,
3202 GstMPDAdaptationSetNode *adap_node = NULL;
3203 GstMPDPeriodNode *period_node = NULL;
3206 g_return_val_if_fail (client != NULL, 0);
3207 g_return_val_if_fail (client->mpd_root_node != NULL, 0);
3210 GST_MPD_PERIOD_NODE (gst_mpd_client_get_period_with_id
3211 (client->mpd_root_node->Periods, period_id));
3212 g_return_val_if_fail (period_node != NULL, 0);
3214 GST_MPD_ADAPTATION_SET_NODE (gst_mpd_client_get_adaptation_set_with_id
3215 (period_node->AdaptationSets, adaptation_set_id));
3217 adap_node = gst_mpd_adaptation_set_node_new ();
3218 if (adaptation_set_id)
3219 adap_node->id = adaptation_set_id;
3222 _generate_new_id (period_node->AdaptationSets,
3223 gst_mpd_client_get_adaptation_set_with_id);
3224 GST_DEBUG_OBJECT (client, "Add a new adaptation set with id %d",
3226 period_node->AdaptationSets =
3227 g_list_append (period_node->AdaptationSets, adap_node);
3230 va_start (myargs, property_name);
3231 g_object_set_valist (G_OBJECT (adap_node), property_name, myargs);
3234 return adap_node->id;
3237 /* returns a representation id */
3239 gst_mpd_client_set_representation_node (GstMPDClient * client,
3240 gchar * period_id, guint adaptation_set_id, gchar * representation_id,
3241 const gchar * property_name, ...)
3243 GstMPDRepresentationNode *rep_node = NULL;
3244 GstMPDAdaptationSetNode *adap_set_node = NULL;
3245 GstMPDPeriodNode *period_node = NULL;
3248 g_return_val_if_fail (client != NULL, NULL);
3249 g_return_val_if_fail (client->mpd_root_node != NULL, NULL);
3252 GST_MPD_PERIOD_NODE (gst_mpd_client_get_period_with_id
3253 (client->mpd_root_node->Periods, period_id));
3255 GST_MPD_ADAPTATION_SET_NODE (gst_mpd_client_get_adaptation_set_with_id
3256 (period_node->AdaptationSets, adaptation_set_id));
3257 g_return_val_if_fail (adap_set_node != NULL, NULL);
3259 gst_mpd_client_get_representation_with_id (adap_set_node->Representations,
3262 rep_node = gst_mpd_representation_node_new ();
3263 if (representation_id)
3264 rep_node->id = g_strdup (representation_id);
3267 _generate_new_string_id (adap_set_node->Representations,
3268 "representation_%.2d",
3269 gst_mpd_client_get_representation_with_id_filter);
3270 GST_DEBUG_OBJECT (client, "Add a new representation with id %s",
3272 adap_set_node->Representations =
3273 g_list_append (adap_set_node->Representations, rep_node);
3276 va_start (myargs, property_name);
3277 g_object_set_valist (G_OBJECT (rep_node), property_name, myargs);
3280 return rep_node->id;
3283 /* add/set a segment list node */
3285 gst_mpd_client_set_segment_list (GstMPDClient * client,
3286 gchar * period_id, guint adap_set_id, gchar * rep_id,
3287 const gchar * property_name, ...)
3289 GstMPDRepresentationNode *representation = NULL;
3290 GstMPDAdaptationSetNode *adaptation_set = NULL;
3291 GstMPDPeriodNode *period = NULL;
3294 g_return_val_if_fail (client != NULL, FALSE);
3295 g_return_val_if_fail (client->mpd_root_node != NULL, FALSE);
3298 GST_MPD_PERIOD_NODE (gst_mpd_client_get_period_with_id
3299 (client->mpd_root_node->Periods, period_id));
3301 GST_MPD_ADAPTATION_SET_NODE (gst_mpd_client_get_adaptation_set_with_id
3302 (period->AdaptationSets, adap_set_id));
3303 g_return_val_if_fail (adaptation_set != NULL, FALSE);
3306 gst_mpd_client_get_representation_with_id
3307 (adaptation_set->Representations, rep_id);
3308 if (!representation->SegmentList) {
3309 representation->SegmentList = gst_mpd_segment_list_node_new ();
3312 va_start (myargs, property_name);
3313 g_object_set_valist (G_OBJECT (representation->SegmentList), property_name,
3320 /* add/set a segment template node */
3322 gst_mpd_client_set_segment_template (GstMPDClient * client,
3323 gchar * period_id, guint adap_set_id, gchar * rep_id,
3324 const gchar * property_name, ...)
3326 GstMPDRepresentationNode *representation = NULL;
3327 GstMPDAdaptationSetNode *adaptation_set = NULL;
3328 GstMPDPeriodNode *period = NULL;
3331 g_return_val_if_fail (client != NULL, FALSE);
3332 g_return_val_if_fail (client->mpd_root_node != NULL, FALSE);
3335 GST_MPD_PERIOD_NODE (gst_mpd_client_get_period_with_id
3336 (client->mpd_root_node->Periods, period_id));
3338 GST_MPD_ADAPTATION_SET_NODE (gst_mpd_client_get_adaptation_set_with_id
3339 (period->AdaptationSets, adap_set_id));
3340 g_return_val_if_fail (adaptation_set != NULL, FALSE);
3343 gst_mpd_client_get_representation_with_id
3344 (adaptation_set->Representations, rep_id);
3345 if (!representation->SegmentTemplate) {
3346 representation->SegmentTemplate = gst_mpd_segment_template_node_new ();
3349 va_start (myargs, property_name);
3350 g_object_set_valist (G_OBJECT (representation->SegmentTemplate),
3351 property_name, myargs);
3357 /* add a segmentURL node with to a SegmentList node */
3359 gst_mpd_client_add_segment_url (GstMPDClient * client,
3360 gchar * period_id, guint adap_set_id, gchar * rep_id,
3361 const gchar * property_name, ...)
3363 GstMPDRepresentationNode *representation = NULL;
3364 GstMPDAdaptationSetNode *adaptation_set = NULL;
3365 GstMPDPeriodNode *period = NULL;
3366 GstMPDSegmentURLNode *segment_url = NULL;
3367 guint64 media_presentation_duration = 0;
3370 g_return_val_if_fail (client != NULL, FALSE);
3371 g_return_val_if_fail (client->mpd_root_node != NULL, FALSE);
3374 GST_MPD_PERIOD_NODE (gst_mpd_client_get_period_with_id
3375 (client->mpd_root_node->Periods, period_id));
3377 GST_MPD_ADAPTATION_SET_NODE (gst_mpd_client_get_adaptation_set_with_id
3378 (period->AdaptationSets, adap_set_id));
3379 g_return_val_if_fail (adaptation_set != NULL, FALSE);
3382 gst_mpd_client_get_representation_with_id
3383 (adaptation_set->Representations, rep_id);
3385 if (!representation->SegmentList) {
3386 representation->SegmentList = gst_mpd_segment_list_node_new ();
3389 segment_url = gst_mpd_segment_url_node_new ();
3391 va_start (myargs, property_name);
3392 g_object_set_valist (G_OBJECT (segment_url), property_name, myargs);
3395 gst_mpd_segment_list_node_add_segment (representation->SegmentList,
3398 /* Set the media presentation time according to the new segment duration added */
3399 g_object_get (client->mpd_root_node, "media-presentation-duration",
3400 &media_presentation_duration, NULL);
3401 media_presentation_duration +=
3402 GST_MPD_MULT_SEGMENT_BASE_NODE (representation->SegmentList)->duration;
3403 g_object_set (client->mpd_root_node, "media-presentation-duration",
3404 media_presentation_duration, NULL);