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);
264 #ifdef TIZEN_FEATURE_ADAPTIVE_MODIFICATION
266 gst_uri_downloader_fetch_uri (client->downloader,
267 uri_string, client->mpd_uri, NULL, NULL, DEFAULT_ADAPTIVE_RETRY,
268 DEFAULT_ADAPTIVE_TIMEOUT, TRUE, FALSE, TRUE, &err);
271 gst_uri_downloader_fetch_uri (client->downloader,
272 uri_string, client->mpd_uri, TRUE, FALSE, TRUE, &err);
277 GST_ERROR ("Failed to download external SegmentList node at '%s': %s",
278 segment_list->xlink_href, err->message);
279 g_clear_error (&err);
283 segment_list_buffer = gst_fragment_get_buffer (download);
284 g_object_unref (download);
286 gst_buffer_map (segment_list_buffer, &map, GST_MAP_READ);
289 gst_mpdparser_get_external_segment_list ((const gchar *) map.data,
292 if (segment_list_buffer) {
293 gst_buffer_unmap (segment_list_buffer, &map);
294 gst_buffer_unref (segment_list_buffer);
297 return new_segment_list;
300 static GstMPDSegmentBaseNode *
301 gst_mpd_client_get_segment_base (GstMPDPeriodNode * Period,
302 GstMPDAdaptationSetNode * AdaptationSet,
303 GstMPDRepresentationNode * Representation)
305 GstMPDSegmentBaseNode *SegmentBase = NULL;
307 if (Representation && Representation->SegmentBase) {
308 SegmentBase = Representation->SegmentBase;
309 } else if (AdaptationSet && AdaptationSet->SegmentBase) {
310 SegmentBase = AdaptationSet->SegmentBase;
311 } else if (Period && Period->SegmentBase) {
312 SegmentBase = Period->SegmentBase;
314 /* the SegmentBase element could be encoded also inside a SegmentList element */
315 if (SegmentBase == NULL) {
316 if (Representation && Representation->SegmentList
317 && GST_MPD_MULT_SEGMENT_BASE_NODE (Representation->SegmentList)
318 && GST_MPD_MULT_SEGMENT_BASE_NODE (Representation->
319 SegmentList)->SegmentBase) {
321 GST_MPD_MULT_SEGMENT_BASE_NODE (Representation->
322 SegmentList)->SegmentBase;
323 } else if (AdaptationSet && AdaptationSet->SegmentList
324 && GST_MPD_MULT_SEGMENT_BASE_NODE (AdaptationSet->SegmentList)
325 && GST_MPD_MULT_SEGMENT_BASE_NODE (AdaptationSet->
326 SegmentList)->SegmentBase) {
328 GST_MPD_MULT_SEGMENT_BASE_NODE (AdaptationSet->
329 SegmentList)->SegmentBase;
330 } else if (Period && Period->SegmentList
331 && GST_MPD_MULT_SEGMENT_BASE_NODE (Period->SegmentList)
332 && GST_MPD_MULT_SEGMENT_BASE_NODE (Period->SegmentList)->SegmentBase) {
334 GST_MPD_MULT_SEGMENT_BASE_NODE (Period->SegmentList)->SegmentBase;
341 static GstMPDSegmentListNode *
342 gst_mpd_client_get_segment_list (GstMPDClient * client,
343 GstMPDPeriodNode * Period, GstMPDAdaptationSetNode * AdaptationSet,
344 GstMPDRepresentationNode * Representation)
346 GstMPDSegmentListNode **SegmentList;
347 GstMPDSegmentListNode *ParentSegmentList = NULL;
349 if (Representation && Representation->SegmentList) {
350 SegmentList = &Representation->SegmentList;
351 ParentSegmentList = AdaptationSet->SegmentList;
352 } else if (AdaptationSet && AdaptationSet->SegmentList) {
353 SegmentList = &AdaptationSet->SegmentList;
354 ParentSegmentList = Period->SegmentList;
355 Representation = NULL;
357 Representation = NULL;
358 AdaptationSet = NULL;
359 SegmentList = &Period->SegmentList;
362 /* Resolve external segment list here. */
363 if (*SegmentList && (*SegmentList)->xlink_href) {
364 GstMPDSegmentListNode *new_segment_list;
366 /* TODO: Use SegmentList of parent if
367 * - Parent has its own SegmentList
368 * - Fail to get SegmentList from external xml
371 gst_mpd_client_fetch_external_segment_list (client, Period,
372 AdaptationSet, Representation, ParentSegmentList, *SegmentList);
374 gst_mpd_segment_list_node_free (*SegmentList);
375 *SegmentList = new_segment_list;
382 gst_mpd_client_get_segment_duration (GstMPDClient * client,
383 GstActiveStream * stream, guint64 * scale_dur)
385 GstStreamPeriod *stream_period;
386 GstMPDMultSegmentBaseNode *base = NULL;
387 GstClockTime duration = 0;
389 g_return_val_if_fail (stream != NULL, GST_CLOCK_TIME_NONE);
390 stream_period = gst_mpd_client_get_stream_period (client);
391 g_return_val_if_fail (stream_period != NULL, GST_CLOCK_TIME_NONE);
393 if (stream->cur_segment_list) {
394 base = GST_MPD_MULT_SEGMENT_BASE_NODE (stream->cur_segment_list);
395 } else if (stream->cur_seg_template) {
396 base = GST_MPD_MULT_SEGMENT_BASE_NODE (stream->cur_seg_template);
399 if (base == NULL || base->SegmentBase == NULL) {
400 /* this may happen when we have a single segment */
401 duration = stream_period->duration;
403 *scale_dur = duration;
405 /* duration is guint so this cannot overflow */
406 duration = base->duration * GST_SECOND;
408 *scale_dur = duration;
409 duration /= base->SegmentBase->timescale;
416 gst_mpd_client_active_streams_free (GstMPDClient * client)
418 if (client->active_streams) {
419 g_list_foreach (client->active_streams,
420 (GFunc) gst_mpdparser_free_active_stream, NULL);
421 g_list_free (client->active_streams);
422 client->active_streams = NULL;
427 gst_mpd_client_finalize (GObject * object)
429 GstMPDClient *client = GST_MPD_CLIENT (object);
431 if (client->mpd_root_node)
432 gst_mpd_root_node_free (client->mpd_root_node);
434 if (client->periods) {
435 g_list_free_full (client->periods,
436 (GDestroyNotify) gst_mpdparser_free_stream_period);
439 gst_mpd_client_active_streams_free (client);
441 g_free (client->mpd_uri);
442 client->mpd_uri = NULL;
443 g_free (client->mpd_base_uri);
444 client->mpd_base_uri = NULL;
446 if (client->downloader)
447 gst_object_unref (client->downloader);
448 client->downloader = NULL;
450 G_OBJECT_CLASS (gst_mpd_client_parent_class)->finalize (object);
454 gst_mpd_client_class_init (GstMPDClientClass * klass)
456 GObjectClass *object_class = G_OBJECT_CLASS (klass);
457 object_class->finalize = gst_mpd_client_finalize;
461 gst_mpd_client_init (GstMPDClient * client)
466 gst_mpd_client_new (void)
468 GST_DEBUG_CATEGORY_INIT (gst_dash_mpd_client_debug, "dashmpdclient", 0,
470 return g_object_new (GST_TYPE_MPD_CLIENT, NULL);
474 gst_mpd_client_new_static (void)
476 GstMPDClient *client = gst_mpd_client_new ();
478 client->mpd_root_node = gst_mpd_root_node_new ();
479 client->mpd_root_node->default_namespace =
480 g_strdup ("urn:mpeg:dash:schema:mpd:2011");
481 client->mpd_root_node->profiles =
482 g_strdup ("urn:mpeg:dash:profile:isoff-main:2011");
483 client->mpd_root_node->type = GST_MPD_FILE_TYPE_STATIC;
484 client->mpd_root_node->minBufferTime = 1500;
490 gst_mpd_client_free (GstMPDClient * client)
493 gst_object_unref (client);
497 gst_mpd_client_parse (GstMPDClient * client, const gchar * data, gint size)
499 gboolean ret = FALSE;
502 ret = gst_mpdparser_get_mpd_root_node (&client->mpd_root_node, data, size);
505 gst_mpd_client_check_profiles (client);
506 gst_mpd_client_fetch_on_load_external_resources (client);
514 gst_mpd_client_get_xml_content (GstMPDClient * client, gchar ** data,
517 gboolean ret = FALSE;
519 g_return_val_if_fail (client != NULL, ret);
520 g_return_val_if_fail (client->mpd_root_node != NULL, ret);
522 ret = gst_mpd_node_get_xml_buffer (GST_MPD_NODE (client->mpd_root_node),
529 gst_mpd_client_get_availability_start_time (GstMPDClient * client)
531 GstDateTime *start_time;
534 return (GstDateTime *) NULL;
536 start_time = client->mpd_root_node->availabilityStartTime;
538 gst_date_time_ref (start_time);
543 gst_mpd_client_set_uri_downloader (GstMPDClient * client,
544 GstUriDownloader * downloader)
546 if (client->downloader)
547 gst_object_unref (client->downloader);
548 client->downloader = gst_object_ref (downloader);
552 gst_mpd_client_check_profiles (GstMPDClient * client)
554 GST_DEBUG ("Profiles: %s",
555 client->mpd_root_node->profiles ? client->mpd_root_node->
556 profiles : "<none>");
558 if (!client->mpd_root_node->profiles)
561 if (g_strstr_len (client->mpd_root_node->profiles, -1,
562 "urn:mpeg:dash:profile:isoff-on-demand:2011")) {
563 client->profile_isoff_ondemand = TRUE;
564 GST_DEBUG ("Found ISOFF on demand profile (2011)");
569 gst_mpd_client_fetch_on_load_external_resources (GstMPDClient * client)
573 for (l = client->mpd_root_node->Periods; l; /* explicitly advanced below */ ) {
574 GstMPDPeriodNode *period = l->data;
577 if (period->xlink_href && period->actuate == GST_MPD_XLINK_ACTUATE_ON_LOAD) {
578 GList *new_periods, *prev, *next;
580 new_periods = gst_mpd_client_fetch_external_periods (client, period);
583 client->mpd_root_node->Periods =
584 g_list_delete_link (client->mpd_root_node->Periods, l);
585 gst_mpd_period_node_free (period);
588 /* Get new next node, we will insert before this */
592 next = client->mpd_root_node->Periods;
594 while (new_periods) {
595 client->mpd_root_node->Periods =
596 g_list_insert_before (client->mpd_root_node->Periods, next,
598 new_periods = g_list_delete_link (new_periods, new_periods);
602 /* Update our iterator to the first new period if any, or the next */
606 l = client->mpd_root_node->Periods;
611 if (period->SegmentList && period->SegmentList->xlink_href
612 && period->SegmentList->actuate == GST_MPD_XLINK_ACTUATE_ON_LOAD) {
613 GstMPDSegmentListNode *new_segment_list;
616 gst_mpd_client_fetch_external_segment_list (client, period, NULL,
617 NULL, NULL, period->SegmentList);
619 gst_mpd_segment_list_node_free (period->SegmentList);
620 period->SegmentList = new_segment_list;
623 for (m = period->AdaptationSets; m; /* explicitly advanced below */ ) {
624 GstMPDAdaptationSetNode *adapt_set = m->data;
627 if (adapt_set->xlink_href
628 && adapt_set->actuate == GST_MPD_XLINK_ACTUATE_ON_LOAD) {
629 GList *new_adapt_sets, *prev, *next;
632 gst_mpd_client_fetch_external_adaptation_set (client, period,
636 period->AdaptationSets = g_list_delete_link (period->AdaptationSets, m);
637 gst_mpd_adaptation_set_node_free (adapt_set);
640 /* Get new next node, we will insert before this */
644 next = period->AdaptationSets;
646 while (new_adapt_sets) {
647 period->AdaptationSets =
648 g_list_insert_before (period->AdaptationSets, next,
649 new_adapt_sets->data);
650 new_adapt_sets = g_list_delete_link (new_adapt_sets, new_adapt_sets);
654 /* Update our iterator to the first new adapt_set if any, or the next */
658 m = period->AdaptationSets;
663 if (adapt_set->SegmentList && adapt_set->SegmentList->xlink_href
664 && adapt_set->SegmentList->actuate == GST_MPD_XLINK_ACTUATE_ON_LOAD) {
665 GstMPDSegmentListNode *new_segment_list;
668 gst_mpd_client_fetch_external_segment_list (client, period,
669 adapt_set, NULL, period->SegmentList, adapt_set->SegmentList);
671 gst_mpd_segment_list_node_free (adapt_set->SegmentList);
672 adapt_set->SegmentList = new_segment_list;
675 for (n = adapt_set->Representations; n; n = n->next) {
676 GstMPDRepresentationNode *representation = n->data;
678 if (representation->SegmentList
679 && representation->SegmentList->xlink_href
680 && representation->SegmentList->actuate ==
681 GST_MPD_XLINK_ACTUATE_ON_LOAD) {
683 GstMPDSegmentListNode *new_segment_list;
686 gst_mpd_client_fetch_external_segment_list (client, period,
687 adapt_set, representation, adapt_set->SegmentList,
688 representation->SegmentList);
690 gst_mpd_segment_list_node_free (representation->SegmentList);
691 representation->SegmentList = new_segment_list;
704 static GstStreamPeriod *
705 gst_mpd_client_get_stream_period (GstMPDClient * client)
707 g_return_val_if_fail (client != NULL, NULL);
708 g_return_val_if_fail (client->periods != NULL, NULL);
710 return g_list_nth_data (client->periods, client->period_idx);
714 gst_mpd_client_get_baseURL (GstMPDClient * client, guint indexStream)
716 GstActiveStream *stream;
718 g_return_val_if_fail (client != NULL, NULL);
719 g_return_val_if_fail (client->active_streams != NULL, NULL);
720 stream = g_list_nth_data (client->active_streams, indexStream);
721 g_return_val_if_fail (stream != NULL, NULL);
723 return stream->baseURL;
726 /* select a stream and extract the baseURL (if present) */
728 gst_mpd_client_parse_baseURL (GstMPDClient * client, GstActiveStream * stream,
731 GstStreamPeriod *stream_period;
732 static const gchar empty[] = "";
736 g_return_val_if_fail (stream != NULL, g_strdup (empty));
737 stream_period = gst_mpd_client_get_stream_period (client);
738 g_return_val_if_fail (stream_period != NULL, g_strdup (empty));
739 g_return_val_if_fail (stream_period->period != NULL, g_strdup (empty));
741 /* NULLify query return before we start */
745 /* initialise base url */
747 gst_uri_from_string (client->mpd_base_uri ? client->
748 mpd_base_uri : client->mpd_uri);
750 /* combine a BaseURL at the MPD level with the current base url */
752 gst_mpd_helper_combine_urls (abs_url, client->mpd_root_node->BaseURLs,
753 query, stream->baseURL_idx);
755 /* combine a BaseURL at the Period level with the current base url */
757 gst_mpd_helper_combine_urls (abs_url, stream_period->period->BaseURLs,
758 query, stream->baseURL_idx);
760 GST_DEBUG ("Current adaptation set id %i (%s)", stream->cur_adapt_set->id,
761 stream->cur_adapt_set->contentType);
762 /* combine a BaseURL at the AdaptationSet level with the current base url */
764 gst_mpd_helper_combine_urls (abs_url, stream->cur_adapt_set->BaseURLs,
765 query, stream->baseURL_idx);
767 /* combine a BaseURL at the Representation level with the current base url */
769 gst_mpd_helper_combine_urls (abs_url,
770 stream->cur_representation->BaseURLs, query, stream->baseURL_idx);
772 ret = gst_uri_to_string (abs_url);
773 gst_uri_unref (abs_url);
779 gst_mpd_client_get_segment_end_time (GstMPDClient * client,
780 GPtrArray * segments, const GstMediaSegment * segment, gint index)
782 const GstStreamPeriod *stream_period;
785 if (segment->repeat >= 0)
786 return segment->start + (segment->repeat + 1) * segment->duration;
788 if (index < segments->len - 1) {
789 const GstMediaSegment *next_segment =
790 g_ptr_array_index (segments, index + 1);
791 end = next_segment->start;
793 stream_period = gst_mpd_client_get_stream_period (client);
794 end = stream_period->start + stream_period->duration;
800 gst_mpd_client_add_media_segment (GstActiveStream * stream,
801 GstMPDSegmentURLNode * url_node, guint number, gint repeat,
802 guint64 scale_start, guint64 scale_duration,
803 GstClockTime start, GstClockTime duration)
805 GstMediaSegment *media_segment;
807 g_return_val_if_fail (stream->segments != NULL, FALSE);
809 media_segment = g_slice_new0 (GstMediaSegment);
811 media_segment->SegmentURL = url_node;
812 media_segment->number = number;
813 media_segment->scale_start = scale_start;
814 media_segment->scale_duration = scale_duration;
815 media_segment->start = start;
816 media_segment->duration = duration;
817 media_segment->repeat = repeat;
819 g_ptr_array_add (stream->segments, media_segment);
820 GST_LOG ("Added new segment: number %d, repeat %d, "
821 "ts: %" GST_TIME_FORMAT ", dur: %"
822 GST_TIME_FORMAT, number, repeat,
823 GST_TIME_ARGS (start), GST_TIME_ARGS (duration));
829 gst_mpd_client_stream_update_presentation_time_offset (GstMPDClient * client,
830 GstActiveStream * stream)
832 GstMPDSegmentBaseNode *segbase = NULL;
834 /* Find the used segbase */
835 if (stream->cur_segment_list) {
837 GST_MPD_MULT_SEGMENT_BASE_NODE (stream->cur_segment_list)->SegmentBase;
838 } else if (stream->cur_seg_template) {
840 GST_MPD_MULT_SEGMENT_BASE_NODE (stream->cur_seg_template)->SegmentBase;
841 } else if (stream->cur_segment_base) {
842 segbase = stream->cur_segment_base;
846 /* Avoid overflows */
847 stream->presentationTimeOffset =
848 gst_util_uint64_scale (segbase->presentationTimeOffset, GST_SECOND,
851 stream->presentationTimeOffset = 0;
854 GST_LOG ("Setting stream's presentation time offset to %" GST_TIME_FORMAT,
855 GST_TIME_ARGS (stream->presentationTimeOffset));
859 gst_mpd_client_setup_representation (GstMPDClient * client,
860 GstActiveStream * stream, GstMPDRepresentationNode * representation)
862 GstStreamPeriod *stream_period;
864 GstClockTime PeriodStart, PeriodEnd, start_time, duration;
868 if (stream->cur_adapt_set == NULL) {
869 GST_WARNING ("No valid AdaptationSet node in the MPD file, aborting...");
873 rep_list = stream->cur_adapt_set->Representations;
874 stream->cur_representation = representation;
875 stream->representation_idx = g_list_index (rep_list, representation);
877 /* clean the old segment list, if any */
878 if (stream->segments) {
879 g_ptr_array_unref (stream->segments);
880 stream->segments = NULL;
883 stream_period = gst_mpd_client_get_stream_period (client);
884 g_return_val_if_fail (stream_period != NULL, FALSE);
885 g_return_val_if_fail (stream_period->period != NULL, FALSE);
887 PeriodStart = stream_period->start;
888 if (GST_CLOCK_TIME_IS_VALID (stream_period->duration))
889 PeriodEnd = stream_period->start + stream_period->duration;
891 PeriodEnd = GST_CLOCK_TIME_NONE;
893 GST_LOG ("Building segment list for Period from %" GST_TIME_FORMAT " to %"
894 GST_TIME_FORMAT, GST_TIME_ARGS (PeriodStart), GST_TIME_ARGS (PeriodEnd));
896 if (representation->SegmentBase != NULL
897 || representation->SegmentList != NULL) {
900 /* We have a fixed list of segments for any of the cases here,
901 * init the segments list */
902 gst_mpdparser_init_active_stream_segments (stream);
904 /* get the first segment_base of the selected representation */
905 if ((stream->cur_segment_base =
906 gst_mpd_client_get_segment_base (stream_period->period,
907 stream->cur_adapt_set, representation)) == NULL) {
908 GST_DEBUG ("No useful SegmentBase node for the current Representation");
911 /* get the first segment_list of the selected representation */
912 if ((stream->cur_segment_list =
913 gst_mpd_client_get_segment_list (client, stream_period->period,
914 stream->cur_adapt_set, representation)) == NULL) {
915 GST_DEBUG ("No useful SegmentList node for the current Representation");
916 /* here we should have a single segment for each representation, whose URL is encoded in the baseURL element */
917 if (!gst_mpd_client_add_media_segment (stream, NULL, 1, 0, 0,
918 PeriodEnd - PeriodStart, PeriodStart, PeriodEnd - PeriodStart)) {
922 /* build the list of GstMediaSegment nodes from the SegmentList node */
923 SegmentURL = stream->cur_segment_list->SegmentURL;
924 if (SegmentURL == NULL) {
926 ("No valid list of SegmentURL nodes in the MPD file, aborting...");
930 /* build segment list */
931 i = GST_MPD_MULT_SEGMENT_BASE_NODE (stream->
932 cur_segment_list)->startNumber;
934 start_time = PeriodStart;
936 GST_LOG ("Building media segment list using a SegmentList node");
937 if (GST_MPD_MULT_SEGMENT_BASE_NODE (stream->
938 cur_segment_list)->SegmentTimeline) {
939 GstMPDSegmentTimelineNode *timeline;
942 GstClockTime presentationTimeOffset;
943 GstMPDSegmentBaseNode *segbase;
946 GST_MPD_MULT_SEGMENT_BASE_NODE (stream->
947 cur_segment_list)->SegmentBase;
948 presentationTimeOffset =
949 gst_util_uint64_scale (segbase->presentationTimeOffset, GST_SECOND,
951 GST_LOG ("presentationTimeOffset = %" G_GUINT64_FORMAT,
952 presentationTimeOffset);
955 GST_MPD_MULT_SEGMENT_BASE_NODE (stream->
956 cur_segment_list)->SegmentTimeline;
957 for (list = g_queue_peek_head_link (&timeline->S); list;
958 list = g_list_next (list)) {
961 S = (GstMPDSNode *) list->data;
962 GST_LOG ("Processing S node: d=%" G_GUINT64_FORMAT " r=%d t=%"
963 G_GUINT64_FORMAT, S->d, S->r, S->t);
965 GST_MPD_MULT_SEGMENT_BASE_NODE (stream->
966 cur_segment_list)->SegmentBase->timescale;
967 duration = gst_util_uint64_scale (S->d, GST_SECOND, timescale);
971 start_time = gst_util_uint64_scale (S->t, GST_SECOND, timescale)
972 + PeriodStart - presentationTimeOffset;
977 ("SegmentTimeline does not have a matching SegmentURL, aborting...");
981 if (!gst_mpd_client_add_media_segment (stream, SegmentURL->data, i,
982 S->r, start, S->d, start_time, duration)) {
986 start_time += duration * (S->r + 1);
987 start += S->d * (S->r + 1);
988 SegmentURL = g_list_next (SegmentURL);
994 gst_mpd_client_get_segment_duration (client, stream, &scale_dur);
995 if (!GST_CLOCK_TIME_IS_VALID (duration))
999 if (!gst_mpd_client_add_media_segment (stream, SegmentURL->data, i,
1000 0, start, scale_dur, start_time, duration)) {
1005 start_time += duration;
1006 SegmentURL = g_list_next (SegmentURL);
1011 if (representation->SegmentTemplate != NULL) {
1012 stream->cur_seg_template = representation->SegmentTemplate;
1013 } else if (stream->cur_adapt_set->SegmentTemplate != NULL) {
1014 stream->cur_seg_template = stream->cur_adapt_set->SegmentTemplate;
1015 } else if (stream_period->period->SegmentTemplate != NULL) {
1016 stream->cur_seg_template = stream_period->period->SegmentTemplate;
1019 if (stream->cur_seg_template == NULL) {
1021 gst_mpdparser_init_active_stream_segments (stream);
1022 /* here we should have a single segment for each representation, whose URL is encoded in the baseURL element */
1023 if (!gst_mpd_client_add_media_segment (stream, NULL, 1, 0, 0,
1024 PeriodEnd - PeriodStart, 0, PeriodEnd - PeriodStart)) {
1028 GstClockTime presentationTimeOffset;
1029 GstMPDMultSegmentBaseNode *mult_seg =
1030 GST_MPD_MULT_SEGMENT_BASE_NODE (stream->cur_seg_template);
1031 presentationTimeOffset =
1032 gst_util_uint64_scale (mult_seg->SegmentBase->presentationTimeOffset,
1033 GST_SECOND, mult_seg->SegmentBase->timescale);
1034 GST_LOG ("presentationTimeOffset = %" GST_TIME_FORMAT,
1035 GST_TIME_ARGS (presentationTimeOffset));
1036 /* build segment list */
1037 i = mult_seg->startNumber;
1041 GST_LOG ("Building media segment list using this template: %s",
1042 stream->cur_seg_template->media);
1044 if (mult_seg->SegmentTimeline) {
1045 GstMPDSegmentTimelineNode *timeline;
1049 timeline = mult_seg->SegmentTimeline;
1050 gst_mpdparser_init_active_stream_segments (stream);
1051 for (list = g_queue_peek_head_link (&timeline->S); list;
1052 list = g_list_next (list)) {
1055 S = (GstMPDSNode *) list->data;
1056 GST_LOG ("Processing S node: d=%" G_GUINT64_FORMAT " r=%u t=%"
1057 G_GUINT64_FORMAT, S->d, S->r, S->t);
1058 timescale = mult_seg->SegmentBase->timescale;
1059 duration = gst_util_uint64_scale (S->d, GST_SECOND, timescale);
1062 start_time = gst_util_uint64_scale (S->t, GST_SECOND, timescale)
1063 + PeriodStart - presentationTimeOffset;
1066 if (!gst_mpd_client_add_media_segment (stream, NULL, i, S->r, start,
1067 S->d, start_time, duration)) {
1071 start += S->d * (S->r + 1);
1072 start_time += duration * (S->r + 1);
1075 /* NOP - The segment is created on demand with the template, no need
1076 * to build a list */
1081 /* clip duration of segments to stop at period end */
1082 if (stream->segments && stream->segments->len) {
1083 if (GST_CLOCK_TIME_IS_VALID (PeriodEnd)) {
1086 for (n = 0; n < stream->segments->len; ++n) {
1087 GstMediaSegment *media_segment =
1088 g_ptr_array_index (stream->segments, n);
1089 if (media_segment) {
1090 if (media_segment->start + media_segment->duration > PeriodEnd) {
1091 GstClockTime stop = PeriodEnd;
1092 if (n < stream->segments->len - 1) {
1093 GstMediaSegment *next_segment =
1094 g_ptr_array_index (stream->segments, n + 1);
1095 if (next_segment && next_segment->start < PeriodEnd)
1096 stop = next_segment->start;
1098 media_segment->duration =
1099 media_segment->start > stop ? 0 : stop - media_segment->start;
1100 GST_LOG ("Fixed duration of segment %u: %" GST_TIME_FORMAT, n,
1101 GST_TIME_ARGS (media_segment->duration));
1103 /* If the segment was clipped entirely, we discard it and all
1104 * subsequent ones */
1105 if (media_segment->duration == 0) {
1106 GST_WARNING ("Discarding %u segments outside period",
1107 stream->segments->len - n);
1108 /* _set_size should properly unref elements */
1109 g_ptr_array_set_size (stream->segments, n);
1116 #ifndef GST_DISABLE_GST_DEBUG
1117 if (stream->segments->len > 0) {
1118 GstMediaSegment *last_media_segment =
1119 g_ptr_array_index (stream->segments, stream->segments->len - 1);
1120 GST_LOG ("Built a list of %d segments", last_media_segment->number);
1122 GST_LOG ("All media segments were clipped");
1127 g_free (stream->baseURL);
1128 g_free (stream->queryURL);
1130 gst_mpd_client_parse_baseURL (client, stream, &stream->queryURL);
1132 gst_mpd_client_stream_update_presentation_time_offset (client, stream);
1137 #define CUSTOM_WRAPPER_START "<custom_wrapper>"
1138 #define CUSTOM_WRAPPER_END "</custom_wrapper>"
1141 gst_mpd_client_fetch_external_periods (GstMPDClient * client,
1142 GstMPDPeriodNode * period_node)
1144 GstFragment *download;
1145 GstAdapter *adapter;
1146 GstBuffer *period_buffer;
1149 GstUri *base_uri, *uri;
1150 gchar *query = NULL;
1151 gchar *uri_string, *wrapper;
1152 GList *new_periods = NULL;
1155 /* ISO/IEC 23009-1:2014 5.5.3 4)
1156 * Remove nodes that resolve to nothing when resolving
1158 if (strcmp (period_node->xlink_href,
1159 "urn:mpeg:dash:resolve-to-zero:2013") == 0) {
1163 if (!client->downloader) {
1167 /* Build absolute URI */
1169 /* Get base URI at the MPD level */
1171 gst_uri_from_string (client->mpd_base_uri ? client->
1172 mpd_base_uri : client->mpd_uri);
1174 /* combine a BaseURL at the MPD level with the current base url */
1176 gst_mpd_helper_combine_urls (base_uri, client->mpd_root_node->BaseURLs,
1178 uri = gst_uri_from_string_with_base (base_uri, period_node->xlink_href);
1180 gst_uri_set_query_string (uri, query);
1182 uri_string = gst_uri_to_string (uri);
1183 gst_uri_unref (base_uri);
1184 gst_uri_unref (uri);
1186 #ifdef TIZEN_FEATURE_ADAPTIVE_MODIFICATION
1188 gst_uri_downloader_fetch_uri (client->downloader,
1189 uri_string, client->mpd_uri, NULL, NULL, DEFAULT_ADAPTIVE_RETRY,
1190 DEFAULT_ADAPTIVE_TIMEOUT, TRUE, FALSE, TRUE, &err);
1193 gst_uri_downloader_fetch_uri (client->downloader,
1194 uri_string, client->mpd_uri, TRUE, FALSE, TRUE, &err);
1196 g_free (uri_string);
1199 GST_ERROR ("Failed to download external Period node at '%s': %s",
1200 period_node->xlink_href, err->message);
1201 g_clear_error (&err);
1205 period_buffer = gst_fragment_get_buffer (download);
1206 g_object_unref (download);
1208 /* external xml could have multiple period without root xmlNode.
1209 * To avoid xml parsing error caused by no root node, wrapping it with
1210 * custom root node */
1211 adapter = gst_adapter_new ();
1213 wrapper = g_new (gchar, strlen (CUSTOM_WRAPPER_START));
1214 memcpy (wrapper, CUSTOM_WRAPPER_START, strlen (CUSTOM_WRAPPER_START));
1215 gst_adapter_push (adapter,
1216 gst_buffer_new_wrapped (wrapper, strlen (CUSTOM_WRAPPER_START)));
1218 gst_adapter_push (adapter, period_buffer);
1220 wrapper = g_strdup (CUSTOM_WRAPPER_END);
1221 gst_adapter_push (adapter,
1222 gst_buffer_new_wrapped (wrapper, strlen (CUSTOM_WRAPPER_END) + 1));
1224 data = gst_adapter_map (adapter, gst_adapter_available (adapter));
1227 gst_mpdparser_get_external_periods (data,
1228 gst_adapter_available (adapter));
1230 gst_adapter_unmap (adapter);
1231 gst_adapter_clear (adapter);
1232 gst_object_unref (adapter);
1238 gst_mpd_client_setup_media_presentation (GstMPDClient * client,
1239 GstClockTime time, gint period_idx, const gchar * period_id)
1241 GstStreamPeriod *stream_period;
1242 GstClockTime start, duration;
1245 gboolean ret = FALSE;
1247 g_return_val_if_fail (client != NULL, FALSE);
1248 g_return_val_if_fail (client->mpd_root_node != NULL, FALSE);
1250 /* Check if we set up the media presentation far enough already */
1251 for (list = client->periods; list; list = list->next) {
1252 GstStreamPeriod *stream_period = list->data;
1254 if ((time != GST_CLOCK_TIME_NONE
1255 && stream_period->duration != GST_CLOCK_TIME_NONE
1256 && stream_period->start + stream_period->duration >= time)
1257 || (time != GST_CLOCK_TIME_NONE && stream_period->start >= time))
1260 if (period_idx != -1 && stream_period->number >= period_idx)
1263 if (period_id != NULL && stream_period->period->id != NULL
1264 && strcmp (stream_period->period->id, period_id) == 0)
1269 GST_DEBUG ("Building the list of Periods in the Media Presentation");
1270 /* clean the old period list, if any */
1271 /* TODO: In theory we could reuse the ones we have so far but that
1272 * seems more complicated than the overhead caused here
1274 if (client->periods) {
1275 g_list_foreach (client->periods,
1276 (GFunc) gst_mpdparser_free_stream_period, NULL);
1277 g_list_free (client->periods);
1278 client->periods = NULL;
1283 duration = GST_CLOCK_TIME_NONE;
1285 if (client->mpd_root_node->mediaPresentationDuration <= 0 &&
1286 client->mpd_root_node->mediaPresentationDuration != -1) {
1287 /* Invalid MPD file: MPD duration is negative or zero */
1291 for (list = client->mpd_root_node->Periods; list;
1292 /* explicitly advanced below */ ) {
1293 GstMPDPeriodNode *period_node = list->data;
1294 GstMPDPeriodNode *next_period_node = NULL;
1296 /* Download external period */
1297 if (period_node->xlink_href) {
1301 new_periods = gst_mpd_client_fetch_external_periods (client, period_node);
1304 client->mpd_root_node->Periods =
1305 g_list_delete_link (client->mpd_root_node->Periods, list);
1306 gst_mpd_period_node_free (period_node);
1309 /* Get new next node, we will insert before this */
1313 next = client->mpd_root_node->Periods;
1315 while (new_periods) {
1316 client->mpd_root_node->Periods =
1317 g_list_insert_before (client->mpd_root_node->Periods, next,
1319 new_periods = g_list_delete_link (new_periods, new_periods);
1323 /* Update our iterator to the first new period if any, or the next */
1327 list = client->mpd_root_node->Periods;
1333 if (period_node->start != -1) {
1334 /* we have a regular period */
1335 /* start cannot be smaller than previous start */
1336 if (list != g_list_first (client->mpd_root_node->Periods)
1337 && start >= period_node->start * GST_MSECOND) {
1338 /* Invalid MPD file: duration would be negative or zero */
1341 start = period_node->start * GST_MSECOND;
1342 } else if (duration != GST_CLOCK_TIME_NONE) {
1343 /* start time inferred from previous period, this is still a regular period */
1346 && client->mpd_root_node->type == GST_MPD_FILE_TYPE_STATIC) {
1347 /* first period of a static MPD file, start time is 0 */
1349 } else if (client->mpd_root_node->type == GST_MPD_FILE_TYPE_DYNAMIC) {
1350 /* this should be a live stream, let this pass */
1352 /* this is an 'Early Available Period' */
1356 /* compute duration.
1357 If there is a start time for the next period, or this is the last period
1358 and mediaPresentationDuration was set, those values will take precedence
1359 over a configured period duration in computing this period's duration
1361 ISO/IEC 23009-1:2014(E), chapter 5.3.2.1
1362 "The Period extends until the PeriodStart of the next Period, or until
1363 the end of the Media Presentation in the case of the last Period."
1366 while ((next = g_list_next (list)) != NULL) {
1367 /* try to infer this period duration from the start time of the next period */
1368 next_period_node = next->data;
1370 if (next_period_node->xlink_href) {
1374 gst_mpd_client_fetch_external_periods (client, next_period_node);
1376 client->mpd_root_node->Periods =
1377 g_list_delete_link (client->mpd_root_node->Periods, next);
1378 gst_mpd_period_node_free (next_period_node);
1379 next_period_node = NULL;
1380 /* Get new next node, we will insert before this */
1381 next = g_list_next (list);
1382 while (new_periods) {
1383 client->mpd_root_node->Periods =
1384 g_list_insert_before (client->mpd_root_node->Periods, next,
1386 new_periods = g_list_delete_link (new_periods, new_periods);
1389 /* And try again, getting the next list element which is now our newly
1390 * inserted nodes. If any */
1392 /* Got the next period and it doesn't have to be downloaded first */
1397 if (next_period_node) {
1398 if (next_period_node->start != -1) {
1399 if (start >= next_period_node->start * GST_MSECOND) {
1400 /* Invalid MPD file: duration would be negative or zero */
1403 duration = next_period_node->start * GST_MSECOND - start;
1404 } else if (period_node->duration != -1) {
1405 if (period_node->duration <= 0) {
1406 /* Invalid MPD file: duration would be negative or zero */
1409 duration = period_node->duration * GST_MSECOND;
1410 } else if (client->mpd_root_node->type == GST_MPD_FILE_TYPE_DYNAMIC) {
1411 /* might be a live file, ignore unspecified duration */
1413 /* Invalid MPD file! */
1416 } else if (client->mpd_root_node->mediaPresentationDuration != -1) {
1417 /* last Period of the Media Presentation */
1418 if (client->mpd_root_node->mediaPresentationDuration * GST_MSECOND <=
1420 /* Invalid MPD file: duration would be negative or zero */
1424 client->mpd_root_node->mediaPresentationDuration * GST_MSECOND -
1426 } else if (period_node->duration != -1) {
1427 duration = period_node->duration * GST_MSECOND;
1428 } else if (client->mpd_root_node->type == GST_MPD_FILE_TYPE_DYNAMIC) {
1429 /* might be a live file, ignore unspecified duration */
1431 /* Invalid MPD file! */
1433 ("Invalid MPD file. The MPD is static without a valid duration");
1437 stream_period = g_slice_new0 (GstStreamPeriod);
1438 client->periods = g_list_append (client->periods, stream_period);
1439 stream_period->period = period_node;
1440 stream_period->number = idx++;
1441 stream_period->start = start;
1442 stream_period->duration = duration;
1444 GST_LOG (" - added Period %d start=%" GST_TIME_FORMAT " duration=%"
1445 GST_TIME_FORMAT, idx, GST_TIME_ARGS (start), GST_TIME_ARGS (duration));
1447 if ((time != GST_CLOCK_TIME_NONE
1448 && stream_period->duration != GST_CLOCK_TIME_NONE
1449 && stream_period->start + stream_period->duration >= time)
1450 || (time != GST_CLOCK_TIME_NONE && stream_period->start >= time))
1453 if (period_idx != -1 && stream_period->number >= period_idx)
1456 if (period_id != NULL && stream_period->period->id != NULL
1457 && strcmp (stream_period->period->id, period_id) == 0)
1464 ("Found a total of %d valid Periods in the Media Presentation up to this point",
1470 ("Found an Early Available Period, skipping the rest of the Media Presentation");
1475 ("Cannot get the duration of the Period %d, skipping the rest of the Media Presentation",
1481 gst_mpd_client_fetch_external_adaptation_set (GstMPDClient * client,
1482 GstMPDPeriodNode * period, GstMPDAdaptationSetNode * adapt_set)
1484 GstFragment *download;
1485 GstBuffer *adapt_set_buffer;
1488 GstUri *base_uri, *uri;
1489 gchar *query = NULL;
1491 GList *new_adapt_sets = NULL;
1493 /* ISO/IEC 23009-1:2014 5.5.3 4)
1494 * Remove nodes that resolve to nothing when resolving
1496 if (strcmp (adapt_set->xlink_href, "urn:mpeg:dash:resolve-to-zero:2013") == 0) {
1500 if (!client->downloader) {
1504 /* Build absolute URI */
1506 /* Get base URI at the MPD level */
1508 gst_uri_from_string (client->mpd_base_uri ? client->
1509 mpd_base_uri : client->mpd_uri);
1511 /* combine a BaseURL at the MPD level with the current base url */
1513 gst_mpd_helper_combine_urls (base_uri, client->mpd_root_node->BaseURLs,
1516 /* combine a BaseURL at the Period level with the current base url */
1518 gst_mpd_helper_combine_urls (base_uri, period->BaseURLs, &query, 0);
1520 uri = gst_uri_from_string_with_base (base_uri, adapt_set->xlink_href);
1522 gst_uri_set_query_string (uri, query);
1524 uri_string = gst_uri_to_string (uri);
1525 gst_uri_unref (base_uri);
1526 gst_uri_unref (uri);
1528 #ifdef TIZEN_FEATURE_ADAPTIVE_MODIFICATION
1530 gst_uri_downloader_fetch_uri (client->downloader,
1531 uri_string, client->mpd_uri, NULL, NULL, DEFAULT_ADAPTIVE_RETRY,
1532 DEFAULT_ADAPTIVE_TIMEOUT, TRUE, FALSE, TRUE, &err);
1535 gst_uri_downloader_fetch_uri (client->downloader,
1536 uri_string, client->mpd_uri, TRUE, FALSE, TRUE, &err);
1538 g_free (uri_string);
1541 GST_ERROR ("Failed to download external AdaptationSet node at '%s': %s",
1542 adapt_set->xlink_href, err->message);
1543 g_clear_error (&err);
1547 adapt_set_buffer = gst_fragment_get_buffer (download);
1548 g_object_unref (download);
1550 gst_buffer_map (adapt_set_buffer, &map, GST_MAP_READ);
1553 gst_mpdparser_get_external_adaptation_sets ((const gchar *) map.data,
1556 gst_buffer_unmap (adapt_set_buffer, &map);
1557 gst_buffer_unref (adapt_set_buffer);
1559 return new_adapt_sets;
1563 gst_mpd_client_get_adaptation_sets_for_period (GstMPDClient * client,
1564 GstStreamPeriod * period)
1568 g_return_val_if_fail (period != NULL, NULL);
1570 /* Resolve all external adaptation sets of this period. Every user of
1571 * the adaptation sets would need to know the content of all adaptation sets
1572 * to decide which one to use, so we have to resolve them all here
1574 for (list = period->period->AdaptationSets; list;
1575 /* advanced explicitly below */ ) {
1576 GstMPDAdaptationSetNode *adapt_set = (GstMPDAdaptationSetNode *) list->data;
1577 GList *new_adapt_sets = NULL, *prev, *next;
1579 if (!adapt_set->xlink_href) {
1585 gst_mpd_client_fetch_external_adaptation_set (client, period->period,
1589 period->period->AdaptationSets =
1590 g_list_delete_link (period->period->AdaptationSets, list);
1591 gst_mpd_adaptation_set_node_free (adapt_set);
1594 /* Get new next node, we will insert before this */
1598 next = period->period->AdaptationSets;
1600 while (new_adapt_sets) {
1601 period->period->AdaptationSets =
1602 g_list_insert_before (period->period->AdaptationSets, next,
1603 new_adapt_sets->data);
1604 new_adapt_sets = g_list_delete_link (new_adapt_sets, new_adapt_sets);
1607 /* Update our iterator to the first new adaptation set if any, or the next */
1611 list = period->period->AdaptationSets;
1614 return period->period->AdaptationSets;
1618 gst_mpd_client_get_adaptation_sets (GstMPDClient * client)
1620 GstStreamPeriod *stream_period;
1622 stream_period = gst_mpd_client_get_stream_period (client);
1623 if (stream_period == NULL || stream_period->period == NULL) {
1624 GST_DEBUG ("No more Period nodes in the MPD file, terminating...");
1628 return gst_mpd_client_get_adaptation_sets_for_period (client, stream_period);
1632 gst_mpd_client_setup_streaming (GstMPDClient * client,
1633 GstMPDAdaptationSetNode * adapt_set)
1635 GstMPDRepresentationNode *representation;
1636 GList *rep_list = NULL;
1637 GstActiveStream *stream;
1639 rep_list = adapt_set->Representations;
1641 GST_WARNING ("Can not retrieve any representation, aborting...");
1645 stream = g_slice_new0 (GstActiveStream);
1646 gst_mpdparser_init_active_stream_segments (stream);
1648 stream->baseURL_idx = 0;
1649 stream->cur_adapt_set = adapt_set;
1651 GST_DEBUG ("0. Current stream %p", stream);
1656 gst_mpdparser_get_representation_with_max_bandwidth (rep_list,
1657 stream->max_bandwidth);
1659 if (!representation) {
1661 ("Can not retrieve a representation with the requested bandwidth");
1662 representation = gst_mpd_client_get_lowest_representation (rep_list);
1666 representation = gst_mpd_client_get_lowest_representation (rep_list);
1669 if (!representation) {
1670 GST_WARNING ("No valid representation in the MPD file, aborting...");
1671 gst_mpdparser_free_active_stream (stream);
1675 gst_mpdparser_representation_get_mimetype (adapt_set, representation);
1676 if (stream->mimeType == GST_STREAM_UNKNOWN) {
1677 GST_WARNING ("Unknown mime type in the representation, aborting...");
1678 gst_mpdparser_free_active_stream (stream);
1682 client->active_streams = g_list_append (client->active_streams, stream);
1683 if (!gst_mpd_client_setup_representation (client, stream, representation)) {
1684 GST_WARNING ("Failed to setup the representation, aborting...");
1688 GST_INFO ("Successfully setup the download pipeline for mimeType %d",
1695 gst_mpd_client_stream_seek (GstMPDClient * client, GstActiveStream * stream,
1696 gboolean forward, GstSeekFlags flags, GstClockTime ts,
1697 GstClockTime * final_ts)
1700 gint repeat_index = 0;
1701 GstMediaSegment *selectedChunk = NULL;
1703 g_return_val_if_fail (stream != NULL, 0);
1705 if (stream->segments) {
1706 for (index = 0; index < stream->segments->len; index++) {
1707 gboolean in_segment = FALSE;
1708 GstMediaSegment *segment = g_ptr_array_index (stream->segments, index);
1709 GstClockTime end_time;
1711 GST_DEBUG ("Looking at fragment sequence chunk %d / %d", index,
1712 stream->segments->len);
1715 gst_mpd_client_get_segment_end_time (client, stream->segments,
1718 /* avoid downloading another fragment just for 1ns in reverse mode */
1720 in_segment = ts < end_time;
1722 in_segment = ts <= end_time;
1725 GstClockTime chunk_time;
1727 selectedChunk = segment;
1729 ((ts - segment->start) +
1730 ((GstMediaSegment *) stream->segments->pdata[0])->start) /
1733 chunk_time = segment->start + segment->duration * repeat_index;
1735 /* At the end of a segment in reverse mode, start from the previous fragment */
1736 if (!forward && repeat_index > 0
1737 && ((ts - segment->start) % segment->duration == 0))
1740 if ((flags & GST_SEEK_FLAG_SNAP_NEAREST) == GST_SEEK_FLAG_SNAP_NEAREST) {
1741 if (repeat_index + 1 < segment->repeat) {
1742 if (ts - chunk_time > chunk_time + segment->duration - ts)
1744 } else if (index + 1 < stream->segments->len) {
1745 GstMediaSegment *next_segment =
1746 g_ptr_array_index (stream->segments, index + 1);
1748 if (ts - chunk_time > next_segment->start - ts) {
1750 selectedChunk = next_segment;
1754 } else if (((forward && flags & GST_SEEK_FLAG_SNAP_AFTER) ||
1755 (!forward && flags & GST_SEEK_FLAG_SNAP_BEFORE)) &&
1758 if (repeat_index + 1 < segment->repeat) {
1762 if (index + 1 >= stream->segments->len) {
1763 selectedChunk = NULL;
1765 selectedChunk = g_ptr_array_index (stream->segments, ++index);
1773 if (selectedChunk == NULL) {
1774 stream->segment_index = stream->segments->len;
1775 stream->segment_repeat_index = 0;
1776 GST_DEBUG ("Seek to after last segment");
1781 *final_ts = selectedChunk->start + selectedChunk->duration * repeat_index;
1783 GstClockTime duration =
1784 gst_mpd_client_get_segment_duration (client, stream, NULL);
1785 GstStreamPeriod *stream_period = gst_mpd_client_get_stream_period (client);
1786 guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
1787 GstClockTime index_time;
1789 g_return_val_if_fail (GST_MPD_MULT_SEGMENT_BASE_NODE
1790 (stream->cur_seg_template)->SegmentTimeline == NULL, FALSE);
1791 if (!GST_CLOCK_TIME_IS_VALID (duration) || duration == 0) {
1795 if (ts > stream_period->start)
1796 ts -= stream_period->start;
1800 index = ts / duration;
1802 /* At the end of a segment in reverse mode, start from the previous fragment */
1803 if (!forward && index > 0 && ts % duration == 0)
1806 index_time = index * duration;
1808 if ((flags & GST_SEEK_FLAG_SNAP_NEAREST) == GST_SEEK_FLAG_SNAP_NEAREST) {
1809 if (ts - index_time > index_time + duration - ts)
1811 } else if (((forward && flags & GST_SEEK_FLAG_SNAP_AFTER) ||
1812 (!forward && flags & GST_SEEK_FLAG_SNAP_BEFORE))
1813 && ts != index_time) {
1817 if (segments_count > 0 && index >= segments_count) {
1818 stream->segment_index = segments_count;
1819 stream->segment_repeat_index = 0;
1820 GST_DEBUG ("Seek to after last segment");
1824 *final_ts = index * duration;
1827 stream->segment_repeat_index = repeat_index;
1828 stream->segment_index = index;
1834 gst_mpd_client_calculate_time_difference (const GstDateTime * t1,
1835 const GstDateTime * t2)
1837 GDateTime *gdt1, *gdt2;
1840 g_assert (t1 != NULL && t2 != NULL);
1841 gdt1 = gst_date_time_to_g_date_time ((GstDateTime *) t1);
1842 gdt2 = gst_date_time_to_g_date_time ((GstDateTime *) t2);
1843 diff = g_date_time_difference (gdt2, gdt1);
1844 g_date_time_unref (gdt1);
1845 g_date_time_unref (gdt2);
1846 return diff * GST_USECOND;
1850 gst_mpd_client_add_time_difference (GstDateTime * t1, gint64 usecs)
1856 g_assert (t1 != NULL);
1857 gdt = gst_date_time_to_g_date_time (t1);
1858 g_assert (gdt != NULL);
1859 gdt2 = g_date_time_add (gdt, usecs);
1860 g_assert (gdt2 != NULL);
1861 g_date_time_unref (gdt);
1862 rv = gst_date_time_new_from_g_date_time (gdt2);
1864 /* Don't g_date_time_unref(gdt2) because gst_date_time_new_from_g_date_time takes
1865 * ownership of the GDateTime pointer.
1872 gst_mpd_client_get_last_fragment_timestamp_end (GstMPDClient * client,
1873 guint stream_idx, GstClockTime * ts)
1875 GstActiveStream *stream;
1877 GstMediaSegment *currentChunk;
1878 GstStreamPeriod *stream_period;
1880 GST_DEBUG ("Stream index: %i", stream_idx);
1881 stream = g_list_nth_data (client->active_streams, stream_idx);
1882 g_return_val_if_fail (stream != NULL, 0);
1884 if (!stream->segments) {
1885 stream_period = gst_mpd_client_get_stream_period (client);
1886 *ts = stream_period->start + stream_period->duration;
1888 segment_idx = gst_mpd_client_get_segments_counts (client, stream) - 1;
1889 if (segment_idx >= stream->segments->len) {
1890 GST_WARNING ("Segment index %d is outside of segment list of length %d",
1891 segment_idx, stream->segments->len);
1894 currentChunk = g_ptr_array_index (stream->segments, segment_idx);
1896 if (currentChunk->repeat >= 0) {
1898 currentChunk->start + (currentChunk->duration * (1 +
1899 currentChunk->repeat));
1901 /* 5.3.9.6.1: negative repeat means repeat till the end of the
1902 * period, or the next update of the MPD (which I think is
1903 * implicit, as this will all get deleted/recreated), or the
1904 * start of the next segment, if any. */
1905 stream_period = gst_mpd_client_get_stream_period (client);
1906 *ts = stream_period->start + stream_period->duration;
1914 gst_mpd_client_get_next_fragment_timestamp (GstMPDClient * client,
1915 guint stream_idx, GstClockTime * ts)
1917 GstActiveStream *stream;
1918 GstMediaSegment *currentChunk;
1920 GST_DEBUG ("Stream index: %i", stream_idx);
1921 stream = g_list_nth_data (client->active_streams, stream_idx);
1922 g_return_val_if_fail (stream != NULL, 0);
1924 if (stream->segments) {
1925 GST_DEBUG ("Looking for fragment sequence chunk %d / %d",
1926 stream->segment_index, stream->segments->len);
1927 if (stream->segment_index >= stream->segments->len)
1929 currentChunk = g_ptr_array_index (stream->segments, stream->segment_index);
1932 currentChunk->start +
1933 (currentChunk->duration * stream->segment_repeat_index);
1935 GstClockTime duration =
1936 gst_mpd_client_get_segment_duration (client, stream, NULL);
1937 guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
1939 g_return_val_if_fail (GST_MPD_MULT_SEGMENT_BASE_NODE
1940 (stream->cur_seg_template)->SegmentTimeline == NULL, FALSE);
1941 if (!GST_CLOCK_TIME_IS_VALID (duration) || (segments_count > 0
1942 && stream->segment_index >= segments_count)) {
1945 *ts = stream->segment_index * duration;
1952 gst_mpd_client_get_stream_presentation_offset (GstMPDClient * client,
1955 GstActiveStream *stream = NULL;
1957 g_return_val_if_fail (client != NULL, 0);
1958 g_return_val_if_fail (client->active_streams != NULL, 0);
1959 stream = g_list_nth_data (client->active_streams, stream_idx);
1960 g_return_val_if_fail (stream != NULL, 0);
1962 return stream->presentationTimeOffset;
1966 gst_mpd_client_get_period_start_time (GstMPDClient * client)
1968 GstStreamPeriod *stream_period = NULL;
1970 g_return_val_if_fail (client != NULL, 0);
1971 stream_period = gst_mpd_client_get_stream_period (client);
1972 g_return_val_if_fail (stream_period != NULL, 0);
1974 return stream_period->start;
1978 * gst_mpd_client_get_utc_timing_sources:
1979 * @client: #GstMPDClient to check for UTCTiming elements
1980 * @methods: A bit mask of #GstMPDUTCTimingType that specifies the methods
1982 * @selected_method: (nullable): The selected method
1983 * Returns: (transfer none): A NULL terminated array of URLs of servers
1984 * that use @selected_method to provide a realtime clock.
1986 * Searches the UTCTiming elements found in the manifest for an element
1987 * that uses one of the UTC timing methods specified in @selected_method.
1988 * If multiple UTCTiming elements are present that support one of the
1989 * methods specified in @selected_method, the first one is returned.
1994 gst_mpd_client_get_utc_timing_sources (GstMPDClient * client,
1995 guint methods, GstMPDUTCTimingType * selected_method)
1999 g_return_val_if_fail (client != NULL, NULL);
2000 g_return_val_if_fail (client->mpd_root_node != NULL, NULL);
2001 for (list = g_list_first (client->mpd_root_node->UTCTimings); list;
2002 list = g_list_next (list)) {
2003 const GstMPDUTCTimingNode *node = (const GstMPDUTCTimingNode *) list->data;
2004 if (node->method & methods) {
2005 if (selected_method) {
2006 *selected_method = node->method;
2016 gst_mpd_client_get_next_fragment (GstMPDClient * client,
2017 guint indexStream, GstMediaFragmentInfo * fragment)
2019 GstActiveStream *stream = NULL;
2020 GstMediaSegment *currentChunk;
2021 gchar *mediaURL = NULL;
2022 gchar *indexURL = NULL;
2023 GstUri *base_url, *frag_url;
2026 g_return_val_if_fail (client != NULL, FALSE);
2027 g_return_val_if_fail (client->active_streams != NULL, FALSE);
2028 stream = g_list_nth_data (client->active_streams, indexStream);
2029 g_return_val_if_fail (stream != NULL, FALSE);
2030 g_return_val_if_fail (stream->cur_representation != NULL, FALSE);
2032 if (stream->segments) {
2033 GST_DEBUG ("Looking for fragment sequence chunk %d / %d",
2034 stream->segment_index, stream->segments->len);
2035 if (stream->segment_index >= stream->segments->len)
2038 GstClockTime duration = gst_mpd_client_get_segment_duration (client,
2040 guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
2042 g_return_val_if_fail (GST_MPD_MULT_SEGMENT_BASE_NODE
2043 (stream->cur_seg_template)->SegmentTimeline == NULL, FALSE);
2044 if (!GST_CLOCK_TIME_IS_VALID (duration) || (segments_count > 0
2045 && stream->segment_index >= segments_count)) {
2048 fragment->duration = duration;
2051 /* FIXME rework discont checking */
2052 /* fragment->discontinuity = segment_idx != currentChunk.number; */
2053 fragment->range_start = 0;
2054 fragment->range_end = -1;
2055 fragment->index_uri = NULL;
2056 fragment->index_range_start = 0;
2057 fragment->index_range_end = -1;
2059 if (stream->segments) {
2060 currentChunk = g_ptr_array_index (stream->segments, stream->segment_index);
2062 GST_DEBUG ("currentChunk->SegmentURL = %p", currentChunk->SegmentURL);
2063 if (currentChunk->SegmentURL != NULL) {
2064 mediaURL = gst_mpdparser_get_mediaURL (stream, currentChunk->SegmentURL);
2065 indexURL = g_strdup (currentChunk->SegmentURL->index);
2066 } else if (stream->cur_seg_template != NULL) {
2068 gst_mpdparser_build_URL_from_template (stream->cur_seg_template->
2069 media, stream->cur_representation->id,
2070 currentChunk->number + stream->segment_repeat_index,
2071 stream->cur_representation->bandwidth,
2072 currentChunk->scale_start +
2073 stream->segment_repeat_index * currentChunk->scale_duration);
2074 if (stream->cur_seg_template->index) {
2076 gst_mpdparser_build_URL_from_template (stream->cur_seg_template->
2077 index, stream->cur_representation->id,
2078 currentChunk->number + stream->segment_repeat_index,
2079 stream->cur_representation->bandwidth,
2080 currentChunk->scale_start +
2081 stream->segment_repeat_index * currentChunk->scale_duration);
2084 GST_DEBUG ("mediaURL = %s", mediaURL);
2085 GST_DEBUG ("indexURL = %s", indexURL);
2087 fragment->timestamp =
2088 currentChunk->start +
2089 stream->segment_repeat_index * currentChunk->duration;
2090 fragment->duration = currentChunk->duration;
2091 if (currentChunk->SegmentURL) {
2092 if (currentChunk->SegmentURL->mediaRange) {
2093 fragment->range_start =
2094 currentChunk->SegmentURL->mediaRange->first_byte_pos;
2095 fragment->range_end =
2096 currentChunk->SegmentURL->mediaRange->last_byte_pos;
2098 if (currentChunk->SegmentURL->indexRange) {
2099 fragment->index_range_start =
2100 currentChunk->SegmentURL->indexRange->first_byte_pos;
2101 fragment->index_range_end =
2102 currentChunk->SegmentURL->indexRange->last_byte_pos;
2106 if (stream->cur_seg_template != NULL) {
2108 gst_mpdparser_build_URL_from_template (stream->cur_seg_template->
2109 media, stream->cur_representation->id,
2110 stream->segment_index +
2111 GST_MPD_MULT_SEGMENT_BASE_NODE (stream->
2112 cur_seg_template)->startNumber,
2113 stream->cur_representation->bandwidth,
2114 stream->segment_index * fragment->duration);
2115 if (stream->cur_seg_template->index) {
2117 gst_mpdparser_build_URL_from_template (stream->cur_seg_template->
2118 index, stream->cur_representation->id,
2119 stream->segment_index +
2120 GST_MPD_MULT_SEGMENT_BASE_NODE (stream->
2121 cur_seg_template)->startNumber,
2122 stream->cur_representation->bandwidth,
2123 stream->segment_index * fragment->duration);
2129 GST_DEBUG ("mediaURL = %s", mediaURL);
2130 GST_DEBUG ("indexURL = %s", indexURL);
2132 fragment->timestamp = stream->segment_index * fragment->duration;
2135 base_url = gst_uri_from_string (stream->baseURL);
2136 frag_url = gst_uri_from_string_with_base (base_url, mediaURL);
2138 if (stream->queryURL) {
2139 frag_url = gst_uri_make_writable (frag_url);
2140 gst_uri_set_query_string (frag_url, stream->queryURL);
2142 fragment->uri = gst_uri_to_string (frag_url);
2143 gst_uri_unref (frag_url);
2145 if (indexURL != NULL) {
2146 frag_url = gst_uri_make_writable (gst_uri_from_string_with_base (base_url,
2148 gst_uri_set_query_string (frag_url, stream->queryURL);
2149 fragment->index_uri = gst_uri_to_string (frag_url);
2150 gst_uri_unref (frag_url);
2152 } else if (indexURL == NULL && (fragment->index_range_start
2153 || fragment->index_range_end != -1)) {
2154 /* index has no specific URL but has a range, we should only use this if
2155 * the media also has a range, otherwise we are serving some data twice
2156 * (in the media fragment and again in the index) */
2157 if (!(fragment->range_start || fragment->range_end != -1)) {
2158 GST_WARNING ("Ignoring index ranges because there isn't a media range "
2159 "and URIs would be the same");
2160 /* removing index information */
2161 fragment->index_range_start = 0;
2162 fragment->index_range_end = -1;
2166 gst_uri_unref (base_url);
2168 GST_DEBUG ("Loading chunk with URL %s", fragment->uri);
2174 gst_mpd_client_has_next_segment (GstMPDClient * client,
2175 GstActiveStream * stream, gboolean forward)
2178 guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
2180 if (segments_count > 0 && stream->segments
2181 && stream->segment_index + 1 == segments_count) {
2182 GstMediaSegment *segment;
2184 segment = g_ptr_array_index (stream->segments, stream->segment_index);
2185 if (segment->repeat >= 0
2186 && stream->segment_repeat_index >= segment->repeat)
2188 } else if (segments_count > 0
2189 && stream->segment_index + 1 >= segments_count) {
2193 if (stream->segment_index < 0)
2201 gst_mpd_client_advance_segment (GstMPDClient * client, GstActiveStream * stream,
2204 GstMediaSegment *segment;
2205 GstFlowReturn ret = GST_FLOW_OK;
2206 guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
2208 GST_DEBUG ("Advancing segment. Current: %d / %d r:%d", stream->segment_index,
2209 segments_count, stream->segment_repeat_index);
2211 /* handle special cases first */
2213 if (segments_count > 0 && stream->segment_index >= segments_count) {
2218 if (stream->segments == NULL) {
2219 if (stream->segment_index < 0) {
2220 stream->segment_index = 0;
2222 stream->segment_index++;
2223 if (segments_count > 0 && stream->segment_index >= segments_count) {
2230 /* special case for when playback direction is reverted right at *
2231 * the end of the segment list */
2232 if (stream->segment_index < 0) {
2233 stream->segment_index = 0;
2237 if (stream->segments == NULL)
2238 stream->segment_index--;
2239 if (stream->segment_index < 0) {
2240 stream->segment_index = -1;
2244 if (stream->segments == NULL)
2247 /* special case for when playback direction is reverted right at *
2248 * the end of the segment list */
2249 if (stream->segment_index >= segments_count) {
2250 stream->segment_index = segments_count - 1;
2251 segment = g_ptr_array_index (stream->segments, stream->segment_index);
2252 if (segment->repeat >= 0) {
2253 stream->segment_repeat_index = segment->repeat;
2255 GstClockTime start = segment->start;
2257 gst_mpd_client_get_segment_end_time (client, stream->segments,
2259 stream->segment_index);
2260 stream->segment_repeat_index =
2261 (guint) (end - start) / segment->duration;
2267 /* for the normal cases we can get the segment safely here */
2268 segment = g_ptr_array_index (stream->segments, stream->segment_index);
2270 if (segment->repeat >= 0 && stream->segment_repeat_index >= segment->repeat) {
2271 stream->segment_repeat_index = 0;
2272 stream->segment_index++;
2273 if (segments_count > 0 && stream->segment_index >= segments_count) {
2278 stream->segment_repeat_index++;
2281 if (stream->segment_repeat_index == 0) {
2282 stream->segment_index--;
2283 if (stream->segment_index < 0) {
2288 segment = g_ptr_array_index (stream->segments, stream->segment_index);
2289 /* negative repeats only seem to make sense at the end of a list,
2290 * so this one will probably not be. Needs some sanity checking
2291 * when loading the XML data. */
2292 if (segment->repeat >= 0) {
2293 stream->segment_repeat_index = segment->repeat;
2295 GstClockTime start = segment->start;
2297 gst_mpd_client_get_segment_end_time (client, stream->segments,
2299 stream->segment_index);
2300 stream->segment_repeat_index =
2301 (guint) (end - start) / segment->duration;
2304 stream->segment_repeat_index--;
2309 GST_DEBUG ("Advanced to segment: %d / %d r:%d (ret: %s)",
2310 stream->segment_index, segments_count,
2311 stream->segment_repeat_index, gst_flow_get_name (ret));
2316 gst_mpd_client_get_next_header (GstMPDClient * client, gchar ** uri,
2317 guint stream_idx, gint64 * range_start, gint64 * range_end)
2319 GstActiveStream *stream;
2320 GstStreamPeriod *stream_period;
2322 stream = gst_mpd_client_get_active_stream_by_index (client, stream_idx);
2323 g_return_val_if_fail (stream != NULL, FALSE);
2324 g_return_val_if_fail (stream->cur_representation != NULL, FALSE);
2325 stream_period = gst_mpd_client_get_stream_period (client);
2326 g_return_val_if_fail (stream_period != NULL, FALSE);
2327 g_return_val_if_fail (stream_period->period != NULL, FALSE);
2332 GST_DEBUG ("Looking for current representation header");
2334 if (stream->cur_segment_base) {
2335 if (stream->cur_segment_base->Initialization) {
2336 *uri = gst_mpdparser_get_initializationURL (stream,
2337 stream->cur_segment_base->Initialization);
2338 if (stream->cur_segment_base->Initialization->range) {
2340 stream->cur_segment_base->Initialization->range->first_byte_pos;
2342 stream->cur_segment_base->Initialization->range->last_byte_pos;
2344 } else if (stream->cur_segment_base->indexRange) {
2345 *uri = gst_mpdparser_get_initializationURL (stream,
2346 stream->cur_segment_base->Initialization);
2348 *range_end = stream->cur_segment_base->indexRange->first_byte_pos - 1;
2350 } else if (stream->cur_seg_template
2351 && stream->cur_seg_template->initialization) {
2353 gst_mpdparser_build_URL_from_template (stream->cur_seg_template->
2354 initialization, stream->cur_representation->id, 0,
2355 stream->cur_representation->bandwidth, 0);
2358 return *uri == NULL ? FALSE : TRUE;
2362 gst_mpd_client_get_next_header_index (GstMPDClient * client, gchar ** uri,
2363 guint stream_idx, gint64 * range_start, gint64 * range_end)
2365 GstActiveStream *stream;
2366 GstStreamPeriod *stream_period;
2368 stream = gst_mpd_client_get_active_stream_by_index (client, stream_idx);
2369 g_return_val_if_fail (stream != NULL, FALSE);
2370 g_return_val_if_fail (stream->cur_representation != NULL, FALSE);
2371 stream_period = gst_mpd_client_get_stream_period (client);
2372 g_return_val_if_fail (stream_period != NULL, FALSE);
2373 g_return_val_if_fail (stream_period->period != NULL, FALSE);
2378 GST_DEBUG ("Looking for current representation index");
2380 if (stream->cur_segment_base && stream->cur_segment_base->indexRange) {
2381 *uri = gst_mpdparser_get_initializationURL (stream,
2382 stream->cur_segment_base->RepresentationIndex);
2383 *range_start = stream->cur_segment_base->indexRange->first_byte_pos;
2384 *range_end = stream->cur_segment_base->indexRange->last_byte_pos;
2385 } else if (stream->cur_seg_template && stream->cur_seg_template->index) {
2387 gst_mpdparser_build_URL_from_template (stream->cur_seg_template->index,
2388 stream->cur_representation->id, 0,
2389 stream->cur_representation->bandwidth, 0);
2392 return *uri == NULL ? FALSE : TRUE;
2396 gst_mpd_client_get_next_fragment_duration (GstMPDClient * client,
2397 GstActiveStream * stream)
2399 GstMediaSegment *media_segment = NULL;
2402 g_return_val_if_fail (stream != NULL, 0);
2404 seg_idx = stream->segment_index;
2406 if (stream->segments) {
2407 if (seg_idx < stream->segments->len && seg_idx >= 0)
2408 media_segment = g_ptr_array_index (stream->segments, seg_idx);
2410 return media_segment == NULL ? 0 : media_segment->duration;
2412 GstClockTime duration =
2413 gst_mpd_client_get_segment_duration (client, stream, NULL);
2414 guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
2416 g_return_val_if_fail (GST_MPD_MULT_SEGMENT_BASE_NODE
2417 (stream->cur_seg_template)->SegmentTimeline == NULL, 0);
2419 if (!GST_CLOCK_TIME_IS_VALID (duration) || (segments_count > 0
2420 && seg_idx >= segments_count)) {
2428 gst_mpd_client_get_media_presentation_duration (GstMPDClient * client)
2430 GstClockTime duration;
2432 g_return_val_if_fail (client != NULL, GST_CLOCK_TIME_NONE);
2434 if (client->mpd_root_node->mediaPresentationDuration != -1) {
2435 duration = client->mpd_root_node->mediaPresentationDuration * GST_MSECOND;
2437 /* We can only get the duration for on-demand streams */
2438 duration = GST_CLOCK_TIME_NONE;
2445 gst_mpd_client_set_period_id (GstMPDClient * client, const gchar * period_id)
2447 GstStreamPeriod *next_stream_period;
2448 gboolean ret = FALSE;
2452 g_return_val_if_fail (client != NULL, FALSE);
2453 g_return_val_if_fail (client->periods != NULL, FALSE);
2454 g_return_val_if_fail (period_id != NULL, FALSE);
2456 if (!gst_mpd_client_setup_media_presentation (client, GST_CLOCK_TIME_NONE, -1,
2460 for (period_idx = 0, iter = client->periods; iter;
2461 period_idx++, iter = g_list_next (iter)) {
2462 next_stream_period = iter->data;
2464 if (next_stream_period->period->id
2465 && strcmp (next_stream_period->period->id, period_id) == 0) {
2467 client->period_idx = period_idx;
2476 gst_mpd_client_set_period_index (GstMPDClient * client, guint period_idx)
2478 GstStreamPeriod *next_stream_period;
2479 gboolean ret = FALSE;
2481 g_return_val_if_fail (client != NULL, FALSE);
2482 g_return_val_if_fail (client->periods != NULL, FALSE);
2484 if (!gst_mpd_client_setup_media_presentation (client, -1, period_idx, NULL))
2487 next_stream_period = g_list_nth_data (client->periods, period_idx);
2488 if (next_stream_period != NULL) {
2489 client->period_idx = period_idx;
2497 gst_mpd_client_get_period_index (GstMPDClient * client)
2501 g_return_val_if_fail (client != NULL, 0);
2502 period_idx = client->period_idx;
2508 gst_mpd_client_get_period_id (GstMPDClient * client)
2510 GstStreamPeriod *period;
2511 gchar *period_id = NULL;
2513 g_return_val_if_fail (client != NULL, 0);
2514 period = g_list_nth_data (client->periods, client->period_idx);
2515 if (period && period->period)
2516 period_id = period->period->id;
2522 gst_mpd_client_has_next_period (GstMPDClient * client)
2524 GList *next_stream_period;
2525 g_return_val_if_fail (client != NULL, FALSE);
2526 g_return_val_if_fail (client->periods != NULL, FALSE);
2528 if (!gst_mpd_client_setup_media_presentation (client, GST_CLOCK_TIME_NONE,
2529 client->period_idx + 1, NULL))
2532 next_stream_period =
2533 g_list_nth_data (client->periods, client->period_idx + 1);
2534 return next_stream_period != NULL;
2538 gst_mpd_client_has_previous_period (GstMPDClient * client)
2540 GList *next_stream_period;
2541 g_return_val_if_fail (client != NULL, FALSE);
2542 g_return_val_if_fail (client->periods != NULL, FALSE);
2544 if (!gst_mpd_client_setup_media_presentation (client, GST_CLOCK_TIME_NONE,
2545 client->period_idx - 1, NULL))
2548 next_stream_period =
2549 g_list_nth_data (client->periods, client->period_idx - 1);
2551 return next_stream_period != NULL;
2555 gst_mpd_client_get_rep_idx_with_min_bandwidth (GList * Representations)
2557 GList *list = NULL, *lowest = NULL;
2558 GstMPDRepresentationNode *rep = NULL;
2559 gint lowest_bandwidth = -1;
2561 if (Representations == NULL)
2564 for (list = g_list_first (Representations); list; list = g_list_next (list)) {
2565 rep = (GstMPDRepresentationNode *) list->data;
2566 if (rep && (!lowest || rep->bandwidth < lowest_bandwidth)) {
2568 lowest_bandwidth = rep->bandwidth;
2572 return lowest ? g_list_position (Representations, lowest) : -1;
2576 gst_mpd_client_get_rep_idx_with_max_bandwidth (GList * Representations,
2577 gint64 max_bandwidth, gint max_video_width, gint max_video_height, gint
2578 max_video_framerate_n, gint max_video_framerate_d)
2580 GList *list = NULL, *best = NULL;
2581 GstMPDRepresentationNode *representation;
2582 gint best_bandwidth = 0;
2584 GST_DEBUG ("max_bandwidth = %" G_GINT64_FORMAT, max_bandwidth);
2586 if (Representations == NULL)
2589 if (max_bandwidth <= 0) /* 0 => get lowest representation available */
2590 return gst_mpd_client_get_rep_idx_with_min_bandwidth (Representations);
2592 for (list = g_list_first (Representations); list; list = g_list_next (list)) {
2593 GstXMLFrameRate *framerate = NULL;
2595 representation = (GstMPDRepresentationNode *) list->data;
2597 /* FIXME: Really? */
2598 if (!representation)
2601 framerate = GST_MPD_REPRESENTATION_BASE_NODE (representation)->frameRate;
2604 GST_MPD_REPRESENTATION_BASE_NODE (representation)->maxFrameRate;
2606 if (framerate && max_video_framerate_n > 0) {
2607 if (gst_util_fraction_compare (framerate->num, framerate->den,
2608 max_video_framerate_n, max_video_framerate_d) > 0)
2612 if (max_video_width > 0
2613 && GST_MPD_REPRESENTATION_BASE_NODE (representation)->width >
2616 if (max_video_height > 0
2617 && GST_MPD_REPRESENTATION_BASE_NODE (representation)->height >
2621 if (representation->bandwidth <= max_bandwidth &&
2622 representation->bandwidth > best_bandwidth) {
2624 best_bandwidth = representation->bandwidth;
2628 return best ? g_list_position (Representations, best) : -1;
2632 gst_mpd_client_seek_to_first_segment (GstMPDClient * client)
2636 g_return_if_fail (client != NULL);
2637 g_return_if_fail (client->active_streams != NULL);
2639 for (list = g_list_first (client->active_streams); list;
2640 list = g_list_next (list)) {
2641 GstActiveStream *stream = (GstActiveStream *) list->data;
2643 stream->segment_index = 0;
2644 stream->segment_repeat_index = 0;
2650 gst_mpd_client_get_segments_counts (GstMPDClient * client,
2651 GstActiveStream * stream)
2653 GstStreamPeriod *stream_period;
2655 g_return_val_if_fail (stream != NULL, 0);
2657 if (stream->segments)
2658 return stream->segments->len;
2659 g_return_val_if_fail (GST_MPD_MULT_SEGMENT_BASE_NODE
2660 (stream->cur_seg_template)->SegmentTimeline == NULL, 0);
2662 stream_period = gst_mpd_client_get_stream_period (client);
2663 if (stream_period->duration != -1)
2664 return gst_util_uint64_scale_ceil (stream_period->duration, 1,
2665 gst_mpd_client_get_segment_duration (client, stream, NULL));
2671 gst_mpd_client_is_live (GstMPDClient * client)
2673 g_return_val_if_fail (client != NULL, FALSE);
2674 g_return_val_if_fail (client->mpd_root_node != NULL, FALSE);
2676 return client->mpd_root_node->type == GST_MPD_FILE_TYPE_DYNAMIC;
2680 gst_mpd_client_get_nb_active_stream (GstMPDClient * client)
2682 g_return_val_if_fail (client != NULL, 0);
2684 return g_list_length (client->active_streams);
2688 gst_mpd_client_get_nb_adaptationSet (GstMPDClient * client)
2690 GstStreamPeriod *stream_period;
2692 stream_period = gst_mpd_client_get_stream_period (client);
2693 g_return_val_if_fail (stream_period != NULL, 0);
2694 g_return_val_if_fail (stream_period->period != NULL, 0);
2696 return g_list_length (stream_period->period->AdaptationSets);
2700 gst_mpd_client_get_active_stream_by_index (GstMPDClient * client,
2703 g_return_val_if_fail (client != NULL, NULL);
2704 g_return_val_if_fail (client->active_streams != NULL, NULL);
2706 return g_list_nth_data (client->active_streams, stream_idx);
2710 gst_mpd_client_active_stream_contains_subtitles (GstActiveStream * stream)
2712 const gchar *mimeType;
2713 const gchar *adapt_set_codecs;
2714 const gchar *rep_codecs;
2717 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_representation)->mimeType;
2720 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->mimeType;
2722 if (g_strcmp0 (mimeType, "application/ttml+xml") == 0 ||
2723 g_strcmp0 (mimeType, "text/vtt") == 0)
2727 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->codecs;
2729 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_representation)->codecs;
2731 return (adapt_set_codecs && g_str_has_prefix (adapt_set_codecs, "stpp"))
2732 || (rep_codecs && g_str_has_prefix (rep_codecs, "stpp"));
2736 gst_mpd_client_get_stream_caps (GstActiveStream * stream)
2738 const gchar *mimeType, *caps_string;
2739 GstCaps *ret = NULL;
2741 if (stream == NULL || stream->cur_adapt_set == NULL
2742 || stream->cur_representation == NULL)
2746 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_representation)->mimeType;
2747 if (mimeType == NULL) {
2749 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->mimeType;
2752 caps_string = gst_mpd_helper_mimetype_to_caps (mimeType);
2754 if ((g_strcmp0 (caps_string, "application/mp4") == 0)
2755 && gst_mpd_client_active_stream_contains_subtitles (stream))
2756 caps_string = "video/quicktime";
2759 ret = gst_caps_from_string (caps_string);
2765 gst_mpd_client_get_bitstream_switching_flag (GstActiveStream * stream)
2767 if (stream == NULL || stream->cur_adapt_set == NULL)
2770 return stream->cur_adapt_set->bitstreamSwitching;
2774 gst_mpd_client_get_video_stream_width (GstActiveStream * stream)
2778 if (stream == NULL || stream->cur_adapt_set == NULL
2779 || stream->cur_representation == NULL)
2782 width = GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_representation)->width;
2784 width = GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->width;
2791 gst_mpd_client_get_video_stream_height (GstActiveStream * stream)
2795 if (stream == NULL || stream->cur_adapt_set == NULL
2796 || stream->cur_representation == NULL)
2800 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_representation)->height;
2802 height = GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->height;
2809 gst_mpd_client_get_video_stream_framerate (GstActiveStream * stream,
2810 gint * fps_num, gint * fps_den)
2815 if (stream->cur_adapt_set &&
2816 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->frameRate !=
2819 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->
2822 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->
2827 if (stream->cur_adapt_set &&
2828 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->maxFrameRate !=
2831 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->
2834 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->
2839 if (stream->cur_representation &&
2840 GST_MPD_REPRESENTATION_BASE_NODE (stream->
2841 cur_representation)->frameRate != NULL) {
2843 GST_MPD_REPRESENTATION_BASE_NODE (stream->
2844 cur_representation)->frameRate->num;
2846 GST_MPD_REPRESENTATION_BASE_NODE (stream->
2847 cur_representation)->frameRate->den;
2851 if (stream->cur_representation &&
2852 GST_MPD_REPRESENTATION_BASE_NODE (stream->
2853 cur_representation)->maxFrameRate != NULL) {
2855 GST_MPD_REPRESENTATION_BASE_NODE (stream->
2856 cur_representation)->maxFrameRate->num;
2858 GST_MPD_REPRESENTATION_BASE_NODE (stream->
2859 cur_representation)->maxFrameRate->den;
2867 gst_mpd_client_get_audio_stream_rate (GstActiveStream * stream)
2871 if (stream == NULL || stream->cur_adapt_set == NULL
2872 || stream->cur_representation == NULL)
2876 GST_MPD_REPRESENTATION_BASE_NODE (stream->
2877 cur_representation)->audioSamplingRate;
2880 GST_MPD_REPRESENTATION_BASE_NODE (stream->
2881 cur_adapt_set)->audioSamplingRate;
2884 return rate ? atoi (rate) : 0;
2888 gst_mpd_client_get_audio_stream_num_channels (GstActiveStream * stream)
2890 if (stream == NULL || stream->cur_adapt_set == NULL
2891 || stream->cur_representation == NULL)
2893 /* TODO: here we have to parse the AudioChannelConfiguration descriptors */
2898 gst_mpd_client_get_list_and_nb_of_audio_language (GstMPDClient * client,
2901 GstStreamPeriod *stream_period;
2902 GstMPDAdaptationSetNode *adapt_set;
2903 GList *adaptation_sets, *list;
2904 const gchar *this_mimeType = "audio";
2905 gchar *mimeType = NULL;
2906 guint nb_adaptation_set = 0;
2908 stream_period = gst_mpd_client_get_stream_period (client);
2909 g_return_val_if_fail (stream_period != NULL, 0);
2910 g_return_val_if_fail (stream_period->period != NULL, 0);
2913 gst_mpd_client_get_adaptation_sets_for_period (client, stream_period);
2914 for (list = adaptation_sets; list; list = g_list_next (list)) {
2915 adapt_set = (GstMPDAdaptationSetNode *) list->data;
2916 if (adapt_set && adapt_set->lang) {
2917 gchar *this_lang = adapt_set->lang;
2918 GstMPDRepresentationNode *rep;
2920 gst_mpd_client_get_lowest_representation (adapt_set->Representations);
2922 if (GST_MPD_REPRESENTATION_BASE_NODE (rep))
2923 mimeType = GST_MPD_REPRESENTATION_BASE_NODE (rep)->mimeType;
2924 if (!mimeType && GST_MPD_REPRESENTATION_BASE_NODE (adapt_set)) {
2925 mimeType = GST_MPD_REPRESENTATION_BASE_NODE (adapt_set)->mimeType;
2928 if (gst_mpd_helper_strncmp_ext (mimeType, this_mimeType) == 0) {
2929 nb_adaptation_set++;
2930 *lang = g_list_append (*lang, this_lang);
2935 return nb_adaptation_set;
2940 gst_mpd_client_get_next_segment_availability_start_time (GstMPDClient * client,
2941 GstActiveStream * stream)
2943 GstDateTime *availability_start_time, *rv;
2945 GstMediaSegment *segment;
2946 GstClockTime segmentEndTime;
2947 const GstStreamPeriod *stream_period;
2948 GstClockTime period_start = 0;
2950 g_return_val_if_fail (client != NULL, NULL);
2951 g_return_val_if_fail (stream != NULL, NULL);
2953 stream_period = gst_mpd_client_get_stream_period (client);
2954 if (stream_period && stream_period->period) {
2955 period_start = stream_period->start;
2958 seg_idx = stream->segment_index;
2960 if (stream->segments) {
2961 segment = g_ptr_array_index (stream->segments, seg_idx);
2963 if (segment->repeat >= 0) {
2964 segmentEndTime = segment->start + (stream->segment_repeat_index + 1) *
2966 } else if (seg_idx < stream->segments->len - 1) {
2967 const GstMediaSegment *next_segment =
2968 g_ptr_array_index (stream->segments, seg_idx + 1);
2969 segmentEndTime = next_segment->start;
2971 g_return_val_if_fail (stream_period != NULL, NULL);
2972 segmentEndTime = period_start + stream_period->duration;
2975 GstClockTime seg_duration;
2976 seg_duration = gst_mpd_client_get_segment_duration (client, stream, NULL);
2977 if (seg_duration == 0)
2979 segmentEndTime = period_start + (1 + seg_idx) * seg_duration;
2982 availability_start_time = gst_mpd_client_get_availability_start_time (client);
2983 if (availability_start_time == NULL) {
2984 GST_WARNING_OBJECT (client, "Failed to get availability_start_time");
2988 rv = gst_mpd_client_add_time_difference (availability_start_time,
2989 segmentEndTime / GST_USECOND);
2990 gst_date_time_unref (availability_start_time);
2992 GST_WARNING_OBJECT (client, "Failed to offset availability_start_time");
3000 gst_mpd_client_seek_to_time (GstMPDClient * client, GDateTime * time)
3003 GTimeSpan ts_microseconds;
3005 gboolean ret = TRUE;
3008 g_return_val_if_fail (gst_mpd_client_is_live (client), FALSE);
3009 g_return_val_if_fail (client->mpd_root_node->availabilityStartTime != NULL,
3013 gst_date_time_to_g_date_time (client->mpd_root_node->
3014 availabilityStartTime);
3016 ts_microseconds = g_date_time_difference (time, start);
3017 g_date_time_unref (start);
3019 /* Clamp to availability start time, otherwise calculations wrap around */
3020 if (ts_microseconds < 0)
3021 ts_microseconds = 0;
3023 ts = ts_microseconds * GST_USECOND;
3024 for (stream = client->active_streams; stream; stream = g_list_next (stream)) {
3026 ret & gst_mpd_client_stream_seek (client, stream->data, TRUE, 0, ts,
3033 gst_mpd_client_has_isoff_ondemand_profile (GstMPDClient * client)
3035 return client->profile_isoff_ondemand;
3039 * gst_mpd_client_parse_default_presentation_delay:
3040 * @client: #GstMPDClient that has a parsed manifest
3041 * @default_presentation_delay: A string that specifies a time period
3042 * in fragments (e.g. "5 f"), seconds ("12 s") or milliseconds
3044 * Returns: the parsed string in milliseconds
3049 gst_mpd_client_parse_default_presentation_delay (GstMPDClient * client,
3050 const gchar * default_presentation_delay)
3053 char *endptr = NULL;
3055 g_return_val_if_fail (client != NULL, 0);
3056 g_return_val_if_fail (default_presentation_delay != NULL, 0);
3057 value = strtol (default_presentation_delay, &endptr, 10);
3058 if (endptr == default_presentation_delay || value == 0) {
3061 while (*endptr == ' ')
3063 if (*endptr == 's' || *endptr == 'S') {
3064 value *= 1000; /* convert to ms */
3065 } else if (*endptr == 'f' || *endptr == 'F') {
3066 gint64 segment_duration;
3067 g_assert (client->mpd_root_node != NULL);
3068 segment_duration = client->mpd_root_node->maxSegmentDuration;
3069 value *= segment_duration;
3070 } else if (*endptr != 'm' && *endptr != 'M') {
3071 GST_ERROR ("Unable to parse default presentation delay: %s",
3072 default_presentation_delay);
3079 gst_mpd_client_get_maximum_segment_duration (GstMPDClient * client)
3081 GstClockTime ret = GST_CLOCK_TIME_NONE, dur;
3084 g_return_val_if_fail (client != NULL, GST_CLOCK_TIME_NONE);
3085 g_return_val_if_fail (client->mpd_root_node != NULL, GST_CLOCK_TIME_NONE);
3087 if (client->mpd_root_node->maxSegmentDuration != GST_MPD_DURATION_NONE) {
3088 return client->mpd_root_node->maxSegmentDuration * GST_MSECOND;
3091 /* According to the DASH specification, if maxSegmentDuration is not present:
3092 "If not present, then the maximum Segment duration shall be the maximum
3093 duration of any Segment documented in this MPD"
3095 for (stream = client->active_streams; stream; stream = g_list_next (stream)) {
3096 dur = gst_mpd_client_get_segment_duration (client, stream->data, NULL);
3097 if (dur != GST_CLOCK_TIME_NONE && (dur > ret || ret == GST_CLOCK_TIME_NONE)) {
3105 gst_mpd_client_get_period_index_at_time (GstMPDClient * client,
3109 guint period_idx = G_MAXUINT;
3112 GstDateTime *avail_start =
3113 gst_mpd_client_get_availability_start_time (client);
3114 GstStreamPeriod *stream_period;
3116 if (avail_start == NULL)
3119 time_offset = gst_mpd_client_calculate_time_difference (avail_start, time);
3120 gst_date_time_unref (avail_start);
3122 if (time_offset < 0)
3125 if (!gst_mpd_client_setup_media_presentation (client, time_offset, -1, NULL))
3128 for (idx = 0, iter = client->periods; iter; idx++, iter = g_list_next (iter)) {
3129 stream_period = iter->data;
3130 if (stream_period->start <= time_offset
3131 && (!GST_CLOCK_TIME_IS_VALID (stream_period->duration)
3132 || stream_period->start + stream_period->duration > time_offset)) {
3141 /* add or set node methods */
3144 gst_mpd_client_set_root_node (GstMPDClient * client,
3145 const gchar * property_name, ...)
3148 g_return_val_if_fail (client != NULL, FALSE);
3150 if (!client->mpd_root_node)
3151 client->mpd_root_node = gst_mpd_root_node_new ();
3153 va_start (myargs, property_name);
3154 g_object_set_valist (G_OBJECT (client->mpd_root_node), property_name, myargs);
3161 gst_mpd_client_add_baseurl_node (GstMPDClient * client,
3162 const gchar * property_name, ...)
3164 GstMPDBaseURLNode *baseurl_node = NULL;
3167 g_return_val_if_fail (client != NULL, FALSE);
3168 g_return_val_if_fail (client->mpd_root_node != NULL, FALSE);
3170 va_start (myargs, property_name);
3172 baseurl_node = gst_mpd_baseurl_node_new ();
3173 g_object_set_valist (G_OBJECT (baseurl_node), property_name, myargs);
3174 client->mpd_root_node->BaseURLs =
3175 g_list_append (client->mpd_root_node->BaseURLs, baseurl_node);
3181 /* returns a period id */
3183 gst_mpd_client_set_period_node (GstMPDClient * client,
3184 gchar * period_id, const gchar * property_name, ...)
3186 GstMPDPeriodNode *period_node = NULL;
3189 g_return_val_if_fail (client != NULL, NULL);
3190 g_return_val_if_fail (client->mpd_root_node != NULL, NULL);
3193 GST_MPD_PERIOD_NODE (gst_mpd_client_get_period_with_id
3194 (client->mpd_root_node->Periods, period_id));
3196 period_node = gst_mpd_period_node_new ();
3198 period_node->id = g_strdup (period_id);
3201 _generate_new_string_id (client->mpd_root_node->Periods,
3202 "period_%.2d", gst_mpd_client_get_period_with_id);
3203 client->mpd_root_node->Periods =
3204 g_list_append (client->mpd_root_node->Periods, period_node);
3207 va_start (myargs, property_name);
3208 g_object_set_valist (G_OBJECT (period_node), property_name, myargs);
3211 return period_node->id;
3214 /* returns an adaptation set id */
3216 gst_mpd_client_set_adaptation_set_node (GstMPDClient * client,
3217 gchar * period_id, guint adaptation_set_id, const gchar * property_name,
3220 GstMPDAdaptationSetNode *adap_node = NULL;
3221 GstMPDPeriodNode *period_node = NULL;
3224 g_return_val_if_fail (client != NULL, 0);
3225 g_return_val_if_fail (client->mpd_root_node != NULL, 0);
3228 GST_MPD_PERIOD_NODE (gst_mpd_client_get_period_with_id
3229 (client->mpd_root_node->Periods, period_id));
3230 g_return_val_if_fail (period_node != NULL, 0);
3232 GST_MPD_ADAPTATION_SET_NODE (gst_mpd_client_get_adaptation_set_with_id
3233 (period_node->AdaptationSets, adaptation_set_id));
3235 adap_node = gst_mpd_adaptation_set_node_new ();
3236 if (adaptation_set_id)
3237 adap_node->id = adaptation_set_id;
3240 _generate_new_id (period_node->AdaptationSets,
3241 gst_mpd_client_get_adaptation_set_with_id);
3242 GST_DEBUG_OBJECT (client, "Add a new adaptation set with id %d",
3244 period_node->AdaptationSets =
3245 g_list_append (period_node->AdaptationSets, adap_node);
3248 va_start (myargs, property_name);
3249 g_object_set_valist (G_OBJECT (adap_node), property_name, myargs);
3252 return adap_node->id;
3255 /* returns a representation id */
3257 gst_mpd_client_set_representation_node (GstMPDClient * client,
3258 gchar * period_id, guint adaptation_set_id, gchar * representation_id,
3259 const gchar * property_name, ...)
3261 GstMPDRepresentationNode *rep_node = NULL;
3262 GstMPDAdaptationSetNode *adap_set_node = NULL;
3263 GstMPDPeriodNode *period_node = NULL;
3266 g_return_val_if_fail (client != NULL, NULL);
3267 g_return_val_if_fail (client->mpd_root_node != NULL, NULL);
3270 GST_MPD_PERIOD_NODE (gst_mpd_client_get_period_with_id
3271 (client->mpd_root_node->Periods, period_id));
3273 GST_MPD_ADAPTATION_SET_NODE (gst_mpd_client_get_adaptation_set_with_id
3274 (period_node->AdaptationSets, adaptation_set_id));
3275 g_return_val_if_fail (adap_set_node != NULL, NULL);
3277 gst_mpd_client_get_representation_with_id (adap_set_node->Representations,
3280 rep_node = gst_mpd_representation_node_new ();
3281 if (representation_id)
3282 rep_node->id = g_strdup (representation_id);
3285 _generate_new_string_id (adap_set_node->Representations,
3286 "representation_%.2d",
3287 gst_mpd_client_get_representation_with_id_filter);
3288 GST_DEBUG_OBJECT (client, "Add a new representation with id %s",
3290 adap_set_node->Representations =
3291 g_list_append (adap_set_node->Representations, rep_node);
3294 va_start (myargs, property_name);
3295 g_object_set_valist (G_OBJECT (rep_node), property_name, myargs);
3298 return rep_node->id;
3301 /* add/set a segment list node */
3303 gst_mpd_client_set_segment_list (GstMPDClient * client,
3304 gchar * period_id, guint adap_set_id, gchar * rep_id,
3305 const gchar * property_name, ...)
3307 GstMPDRepresentationNode *representation = NULL;
3308 GstMPDAdaptationSetNode *adaptation_set = NULL;
3309 GstMPDPeriodNode *period = NULL;
3312 g_return_val_if_fail (client != NULL, FALSE);
3313 g_return_val_if_fail (client->mpd_root_node != NULL, FALSE);
3316 GST_MPD_PERIOD_NODE (gst_mpd_client_get_period_with_id
3317 (client->mpd_root_node->Periods, period_id));
3319 GST_MPD_ADAPTATION_SET_NODE (gst_mpd_client_get_adaptation_set_with_id
3320 (period->AdaptationSets, adap_set_id));
3321 g_return_val_if_fail (adaptation_set != NULL, FALSE);
3324 gst_mpd_client_get_representation_with_id
3325 (adaptation_set->Representations, rep_id);
3326 if (!representation->SegmentList) {
3327 representation->SegmentList = gst_mpd_segment_list_node_new ();
3330 va_start (myargs, property_name);
3331 g_object_set_valist (G_OBJECT (representation->SegmentList), property_name,
3338 /* add/set a segment template node */
3340 gst_mpd_client_set_segment_template (GstMPDClient * client,
3341 gchar * period_id, guint adap_set_id, gchar * rep_id,
3342 const gchar * property_name, ...)
3344 GstMPDRepresentationNode *representation = NULL;
3345 GstMPDAdaptationSetNode *adaptation_set = NULL;
3346 GstMPDPeriodNode *period = NULL;
3349 g_return_val_if_fail (client != NULL, FALSE);
3350 g_return_val_if_fail (client->mpd_root_node != NULL, FALSE);
3353 GST_MPD_PERIOD_NODE (gst_mpd_client_get_period_with_id
3354 (client->mpd_root_node->Periods, period_id));
3356 GST_MPD_ADAPTATION_SET_NODE (gst_mpd_client_get_adaptation_set_with_id
3357 (period->AdaptationSets, adap_set_id));
3358 g_return_val_if_fail (adaptation_set != NULL, FALSE);
3361 gst_mpd_client_get_representation_with_id
3362 (adaptation_set->Representations, rep_id);
3363 if (!representation->SegmentTemplate) {
3364 representation->SegmentTemplate = gst_mpd_segment_template_node_new ();
3367 va_start (myargs, property_name);
3368 g_object_set_valist (G_OBJECT (representation->SegmentTemplate),
3369 property_name, myargs);
3375 /* add a segmentURL node with to a SegmentList node */
3377 gst_mpd_client_add_segment_url (GstMPDClient * client,
3378 gchar * period_id, guint adap_set_id, gchar * rep_id,
3379 const gchar * property_name, ...)
3381 GstMPDRepresentationNode *representation = NULL;
3382 GstMPDAdaptationSetNode *adaptation_set = NULL;
3383 GstMPDPeriodNode *period = NULL;
3384 GstMPDSegmentURLNode *segment_url = NULL;
3385 guint64 media_presentation_duration = 0;
3388 g_return_val_if_fail (client != NULL, FALSE);
3389 g_return_val_if_fail (client->mpd_root_node != NULL, FALSE);
3392 GST_MPD_PERIOD_NODE (gst_mpd_client_get_period_with_id
3393 (client->mpd_root_node->Periods, period_id));
3395 GST_MPD_ADAPTATION_SET_NODE (gst_mpd_client_get_adaptation_set_with_id
3396 (period->AdaptationSets, adap_set_id));
3397 g_return_val_if_fail (adaptation_set != NULL, FALSE);
3400 gst_mpd_client_get_representation_with_id
3401 (adaptation_set->Representations, rep_id);
3403 if (!representation->SegmentList) {
3404 representation->SegmentList = gst_mpd_segment_list_node_new ();
3407 segment_url = gst_mpd_segment_url_node_new ();
3409 va_start (myargs, property_name);
3410 g_object_set_valist (G_OBJECT (segment_url), property_name, myargs);
3413 gst_mpd_segment_list_node_add_segment (representation->SegmentList,
3416 /* Set the media presentation time according to the new segment duration added */
3417 g_object_get (client->mpd_root_node, "media-presentation-duration",
3418 &media_presentation_duration, NULL);
3419 media_presentation_duration +=
3420 GST_MPD_MULT_SEGMENT_BASE_NODE (representation->SegmentList)->duration;
3421 g_object_set (client->mpd_root_node, "media-presentation-duration",
3422 media_presentation_duration, NULL);