Merging gst-plugins-ugly
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / ext / dash / gstmpdclient.c
1 /* GStreamer
2  *
3  * Copyright (C) 2019 Collabora Ltd.
4  *   Author: Stéphane Cerveau <scerveau@collabora.com>
5  *
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.
10  *
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.
15  *
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
19  *
20  */
21
22 #include "gstmpdclient.h"
23 #include "gstmpdparser.h"
24
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
28
29 G_DEFINE_TYPE (GstMPDClient, gst_mpd_client, GST_TYPE_OBJECT);
30
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);
37 /* Segments */
38 static guint gst_mpd_client_get_segments_counts (GstMPDClient * client,
39     GstActiveStream * stream);
40
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);
45
46 static GstMPDRepresentationNode *gst_mpd_client_get_lowest_representation (GList
47     * Representations);
48 static GstStreamPeriod *gst_mpd_client_get_stream_period (GstMPDClient *
49     client);
50
51 typedef GstMPDNode *(*MpdClientStringIDFilter) (GList * list, gchar * data);
52 typedef GstMPDNode *(*MpdClientIDFilter) (GList * list, guint data);
53
54 static GstMPDNode *
55 gst_mpd_client_get_period_with_id (GList * periods, gchar * period_id)
56 {
57   GstMPDPeriodNode *period;
58   GList *list = NULL;
59
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);
64   }
65   return NULL;
66 }
67
68 static GstMPDNode *
69 gst_mpd_client_get_adaptation_set_with_id (GList * adaptation_sets, guint id)
70 {
71   GstMPDAdaptationSetNode *adaptation_set;
72   GList *list = NULL;
73
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);
78   }
79   return NULL;
80 }
81
82 static GstMPDNode *
83 gst_mpd_client_get_representation_with_id (GList * representations,
84     gchar * rep_id)
85 {
86   GstMPDRepresentationNode *representation;
87   GList *list = NULL;
88
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_NODE (representation);
93   }
94   return NULL;
95 }
96
97 static gchar *
98 _generate_new_string_id (GList * list, const gchar * tuple,
99     MpdClientStringIDFilter filter)
100 {
101   guint i = 0;
102   gchar *id = NULL;
103   GstMPDNode *node;
104   do {
105     g_free (id);
106     id = g_strdup_printf (tuple, i);
107     node = filter (list, id);
108     i++;
109   } while (node);
110
111   return id;
112 }
113
114 static guint
115 _generate_new_id (GList * list, MpdClientIDFilter filter)
116 {
117   guint id = 0;
118   GstMPDNode *node;
119   do {
120     node = filter (list, id);
121     id++;
122   } while (node);
123
124   return id;
125 }
126
127 static GstMPDRepresentationNode *
128 gst_mpd_client_get_lowest_representation (GList * Representations)
129 {
130   GList *list = NULL;
131   GstMPDRepresentationNode *rep = NULL;
132   GstMPDRepresentationNode *lowest = NULL;
133
134   if (Representations == NULL)
135     return NULL;
136
137   for (list = g_list_first (Representations); list; list = g_list_next (list)) {
138     rep = (GstMPDRepresentationNode *) list->data;
139     if (rep && (!lowest || rep->bandwidth < lowest->bandwidth)) {
140       lowest = rep;
141     }
142   }
143
144   return lowest;
145 }
146
147 #if 0
148 static GstMPDRepresentationNode *
149 gst_mpdparser_get_highest_representation (GList * Representations)
150 {
151   GList *list = NULL;
152
153   if (Representations == NULL)
154     return NULL;
155
156   list = g_list_last (Representations);
157
158   return list ? (GstMPDRepresentationNode *) list->data : NULL;
159 }
160
161 static GstMPDRepresentationNode *
162 gst_mpdparser_get_representation_with_max_bandwidth (GList * Representations,
163     gint max_bandwidth)
164 {
165   GList *list = NULL;
166   GstMPDRepresentationNode *representation, *best_rep = NULL;
167
168   if (Representations == NULL)
169     return NULL;
170
171   if (max_bandwidth <= 0)       /* 0 => get highest representation available */
172     return gst_mpdparser_get_highest_representation (Representations);
173
174   for (list = g_list_first (Representations); list; list = g_list_next (list)) {
175     representation = (GstMPDRepresentationNode *) list->data;
176     if (representation && representation->bandwidth <= max_bandwidth) {
177       best_rep = representation;
178     }
179   }
180
181   return best_rep;
182 }
183 #endif
184
185 static GstMPDSegmentListNode *
186 gst_mpd_client_fetch_external_segment_list (GstMPDClient * client,
187     GstMPDPeriodNode * Period,
188     GstMPDAdaptationSetNode * AdaptationSet,
189     GstMPDRepresentationNode * Representation,
190     GstMPDSegmentListNode * parent, GstMPDSegmentListNode * segment_list)
191 {
192   GstFragment *download;
193   GstBuffer *segment_list_buffer = NULL;
194   GstMapInfo map;
195   GError *err = NULL;
196
197   GstUri *base_uri, *uri;
198   gchar *query = NULL;
199   gchar *uri_string;
200   GstMPDSegmentListNode *new_segment_list = NULL;
201
202   /* ISO/IEC 23009-1:2014 5.5.3 4)
203    * Remove nodes that resolve to nothing when resolving
204    */
205   if (strcmp (segment_list->xlink_href,
206           "urn:mpeg:dash:resolve-to-zero:2013") == 0) {
207     return NULL;
208   }
209
210   if (!client->downloader) {
211     return NULL;
212   }
213
214   /* Build absolute URI */
215
216   /* Get base URI at the MPD level */
217   base_uri =
218       gst_uri_from_string (client->mpd_base_uri ? client->
219       mpd_base_uri : client->mpd_uri);
220
221   /* combine a BaseURL at the MPD level with the current base url */
222   base_uri =
223       gst_mpd_helper_combine_urls (base_uri, client->mpd_root_node->BaseURLs,
224       &query, 0);
225
226   /* combine a BaseURL at the Period level with the current base url */
227   base_uri =
228       gst_mpd_helper_combine_urls (base_uri, Period->BaseURLs, &query, 0);
229
230   if (AdaptationSet) {
231     /* combine a BaseURL at the AdaptationSet level with the current base url */
232     base_uri =
233         gst_mpd_helper_combine_urls (base_uri, AdaptationSet->BaseURLs, &query,
234         0);
235
236     if (Representation) {
237       /* combine a BaseURL at the Representation level with the current base url */
238       base_uri =
239           gst_mpd_helper_combine_urls (base_uri, Representation->BaseURLs,
240           &query, 0);
241     }
242   }
243
244   uri = gst_uri_from_string_with_base (base_uri, segment_list->xlink_href);
245   if (query)
246     gst_uri_set_query_string (uri, query);
247   g_free (query);
248   uri_string = gst_uri_to_string (uri);
249   gst_uri_unref (base_uri);
250   gst_uri_unref (uri);
251
252   download =
253       gst_uri_downloader_fetch_uri (client->downloader,
254       uri_string, client->mpd_uri, TRUE, FALSE, TRUE, &err);
255   g_free (uri_string);
256
257   if (!download) {
258     GST_ERROR ("Failed to download external SegmentList node at '%s': %s",
259         segment_list->xlink_href, err->message);
260     g_clear_error (&err);
261     return NULL;
262   }
263
264   segment_list_buffer = gst_fragment_get_buffer (download);
265   g_object_unref (download);
266
267   gst_buffer_map (segment_list_buffer, &map, GST_MAP_READ);
268
269   new_segment_list =
270       gst_mpdparser_get_external_segment_list ((const gchar *) map.data,
271       map.size, parent);
272
273   if (segment_list_buffer) {
274     gst_buffer_unmap (segment_list_buffer, &map);
275     gst_buffer_unref (segment_list_buffer);
276   }
277
278   return new_segment_list;
279 }
280
281 static GstMPDSegmentBaseNode *
282 gst_mpd_client_get_segment_base (GstMPDPeriodNode * Period,
283     GstMPDAdaptationSetNode * AdaptationSet,
284     GstMPDRepresentationNode * Representation)
285 {
286   GstMPDSegmentBaseNode *SegmentBase = NULL;
287
288   if (Representation && Representation->SegmentBase) {
289     SegmentBase = Representation->SegmentBase;
290   } else if (AdaptationSet && AdaptationSet->SegmentBase) {
291     SegmentBase = AdaptationSet->SegmentBase;
292   } else if (Period && Period->SegmentBase) {
293     SegmentBase = Period->SegmentBase;
294   }
295   /* the SegmentBase element could be encoded also inside a SegmentList element */
296   if (SegmentBase == NULL) {
297     if (Representation && Representation->SegmentList
298         && GST_MPD_MULT_SEGMENT_BASE_NODE (Representation->SegmentList)
299         && GST_MPD_MULT_SEGMENT_BASE_NODE (Representation->
300             SegmentList)->SegmentBase) {
301       SegmentBase =
302           GST_MPD_MULT_SEGMENT_BASE_NODE (Representation->
303           SegmentList)->SegmentBase;
304     } else if (AdaptationSet && AdaptationSet->SegmentList
305         && GST_MPD_MULT_SEGMENT_BASE_NODE (AdaptationSet->SegmentList)
306         && GST_MPD_MULT_SEGMENT_BASE_NODE (AdaptationSet->
307             SegmentList)->SegmentBase) {
308       SegmentBase =
309           GST_MPD_MULT_SEGMENT_BASE_NODE (AdaptationSet->
310           SegmentList)->SegmentBase;
311     } else if (Period && Period->SegmentList
312         && GST_MPD_MULT_SEGMENT_BASE_NODE (Period->SegmentList)
313         && GST_MPD_MULT_SEGMENT_BASE_NODE (Period->SegmentList)->SegmentBase) {
314       SegmentBase =
315           GST_MPD_MULT_SEGMENT_BASE_NODE (Period->SegmentList)->SegmentBase;
316     }
317   }
318
319   return SegmentBase;
320 }
321
322 static GstMPDSegmentListNode *
323 gst_mpd_client_get_segment_list (GstMPDClient * client,
324     GstMPDPeriodNode * Period, GstMPDAdaptationSetNode * AdaptationSet,
325     GstMPDRepresentationNode * Representation)
326 {
327   GstMPDSegmentListNode **SegmentList;
328   GstMPDSegmentListNode *ParentSegmentList = NULL;
329
330   if (Representation && Representation->SegmentList) {
331     SegmentList = &Representation->SegmentList;
332     ParentSegmentList = AdaptationSet->SegmentList;
333   } else if (AdaptationSet && AdaptationSet->SegmentList) {
334     SegmentList = &AdaptationSet->SegmentList;
335     ParentSegmentList = Period->SegmentList;
336     Representation = NULL;
337   } else {
338     Representation = NULL;
339     AdaptationSet = NULL;
340     SegmentList = &Period->SegmentList;
341   }
342
343   /* Resolve external segment list here. */
344   if (*SegmentList && (*SegmentList)->xlink_href) {
345     GstMPDSegmentListNode *new_segment_list;
346
347     /* TODO: Use SegmentList of parent if
348      * - Parent has its own SegmentList
349      * - Fail to get SegmentList from external xml
350      */
351     new_segment_list =
352         gst_mpd_client_fetch_external_segment_list (client, Period,
353         AdaptationSet, Representation, ParentSegmentList, *SegmentList);
354
355     gst_mpd_segment_list_node_free (*SegmentList);
356     *SegmentList = new_segment_list;
357   }
358
359   return *SegmentList;
360 }
361
362 static GstClockTime
363 gst_mpd_client_get_segment_duration (GstMPDClient * client,
364     GstActiveStream * stream, guint64 * scale_dur)
365 {
366   GstStreamPeriod *stream_period;
367   GstMPDMultSegmentBaseNode *base = NULL;
368   GstClockTime duration = 0;
369
370   g_return_val_if_fail (stream != NULL, GST_CLOCK_TIME_NONE);
371   stream_period = gst_mpd_client_get_stream_period (client);
372   g_return_val_if_fail (stream_period != NULL, GST_CLOCK_TIME_NONE);
373
374   if (stream->cur_segment_list) {
375     base = GST_MPD_MULT_SEGMENT_BASE_NODE (stream->cur_segment_list);
376   } else if (stream->cur_seg_template) {
377     base = GST_MPD_MULT_SEGMENT_BASE_NODE (stream->cur_seg_template);
378   }
379
380   if (base == NULL || base->SegmentBase == NULL) {
381     /* this may happen when we have a single segment */
382     duration = stream_period->duration;
383     if (scale_dur)
384       *scale_dur = duration;
385   } else {
386     /* duration is guint so this cannot overflow */
387     duration = base->duration * GST_SECOND;
388     if (scale_dur)
389       *scale_dur = duration;
390     duration /= base->SegmentBase->timescale;
391   }
392
393   return duration;
394 }
395
396 void
397 gst_mpd_client_active_streams_free (GstMPDClient * client)
398 {
399   if (client->active_streams) {
400     g_list_foreach (client->active_streams,
401         (GFunc) gst_mpdparser_free_active_stream, NULL);
402     g_list_free (client->active_streams);
403     client->active_streams = NULL;
404   }
405 }
406
407 static void
408 gst_mpd_client_finalize (GObject * object)
409 {
410   GstMPDClient *client = GST_MPD_CLIENT (object);
411
412   if (client->mpd_root_node)
413     gst_mpd_root_node_free (client->mpd_root_node);
414
415   if (client->periods) {
416     g_list_free_full (client->periods,
417         (GDestroyNotify) gst_mpdparser_free_stream_period);
418   }
419
420   gst_mpd_client_active_streams_free (client);
421
422   g_free (client->mpd_uri);
423   client->mpd_uri = NULL;
424   g_free (client->mpd_base_uri);
425   client->mpd_base_uri = NULL;
426
427   if (client->downloader)
428     gst_object_unref (client->downloader);
429   client->downloader = NULL;
430
431   G_OBJECT_CLASS (gst_mpd_client_parent_class)->finalize (object);
432 }
433
434 static void
435 gst_mpd_client_class_init (GstMPDClientClass * klass)
436 {
437   GObjectClass *object_class = G_OBJECT_CLASS (klass);
438   object_class->finalize = gst_mpd_client_finalize;
439 }
440
441 static void
442 gst_mpd_client_init (GstMPDClient * client)
443 {
444 }
445
446 GstMPDClient *
447 gst_mpd_client_new (void)
448 {
449   GST_DEBUG_CATEGORY_INIT (gst_dash_mpd_client_debug, "dashmpdclient", 0,
450       "DashmMpdClient");
451   return g_object_new (GST_TYPE_MPD_CLIENT, NULL);
452 }
453
454 GstMPDClient *
455 gst_mpd_client_new_static (void)
456 {
457   GstMPDClient *client = gst_mpd_client_new ();
458
459   client->mpd_root_node = gst_mpd_root_node_new ();
460   client->mpd_root_node->default_namespace =
461       g_strdup ("urn:mpeg:dash:schema:mpd:2011");
462   client->mpd_root_node->profiles =
463       g_strdup ("urn:mpeg:dash:profile:isoff-main:2011");
464   client->mpd_root_node->type = GST_MPD_FILE_TYPE_STATIC;
465   client->mpd_root_node->minBufferTime = 1500;
466
467   return client;
468 }
469
470 void
471 gst_mpd_client_free (GstMPDClient * client)
472 {
473   if (client)
474     gst_object_unref (client);
475 }
476
477 gboolean
478 gst_mpd_client_parse (GstMPDClient * client, const gchar * data, gint size)
479 {
480   gboolean ret = FALSE;
481
482
483   ret = gst_mpdparser_get_mpd_root_node (&client->mpd_root_node, data, size);
484
485   if (ret) {
486     gst_mpd_client_check_profiles (client);
487     gst_mpd_client_fetch_on_load_external_resources (client);
488   }
489
490   return ret;
491 }
492
493
494 gboolean
495 gst_mpd_client_get_xml_content (GstMPDClient * client, gchar ** data,
496     gint * size)
497 {
498   gboolean ret = FALSE;
499
500   g_return_val_if_fail (client != NULL, ret);
501   g_return_val_if_fail (client->mpd_root_node != NULL, ret);
502
503   ret = gst_mpd_node_get_xml_buffer (GST_MPD_NODE (client->mpd_root_node),
504       data, (int *) size);
505
506   return ret;
507 }
508
509 GstDateTime *
510 gst_mpd_client_get_availability_start_time (GstMPDClient * client)
511 {
512   GstDateTime *start_time;
513
514   if (client == NULL)
515     return (GstDateTime *) NULL;
516
517   start_time = client->mpd_root_node->availabilityStartTime;
518   if (start_time)
519     gst_date_time_ref (start_time);
520   return start_time;
521 }
522
523 void
524 gst_mpd_client_set_uri_downloader (GstMPDClient * client,
525     GstUriDownloader * downloader)
526 {
527   if (client->downloader)
528     gst_object_unref (client->downloader);
529   client->downloader = gst_object_ref (downloader);
530 }
531
532 void
533 gst_mpd_client_check_profiles (GstMPDClient * client)
534 {
535   GST_DEBUG ("Profiles: %s",
536       client->mpd_root_node->profiles ? client->mpd_root_node->
537       profiles : "<none>");
538
539   if (!client->mpd_root_node->profiles)
540     return;
541
542   if (g_strstr_len (client->mpd_root_node->profiles, -1,
543           "urn:mpeg:dash:profile:isoff-on-demand:2011")) {
544     client->profile_isoff_ondemand = TRUE;
545     GST_DEBUG ("Found ISOFF on demand profile (2011)");
546   }
547 }
548
549 void
550 gst_mpd_client_fetch_on_load_external_resources (GstMPDClient * client)
551 {
552   GList *l;
553
554   for (l = client->mpd_root_node->Periods; l; /* explicitly advanced below */ ) {
555     GstMPDPeriodNode *period = l->data;
556     GList *m;
557
558     if (period->xlink_href && period->actuate == GST_MPD_XLINK_ACTUATE_ON_LOAD) {
559       GList *new_periods, *prev, *next;
560
561       new_periods = gst_mpd_client_fetch_external_periods (client, period);
562
563       prev = l->prev;
564       client->mpd_root_node->Periods =
565           g_list_delete_link (client->mpd_root_node->Periods, l);
566       gst_mpd_period_node_free (period);
567       period = NULL;
568
569       /* Get new next node, we will insert before this */
570       if (prev)
571         next = prev->next;
572       else
573         next = client->mpd_root_node->Periods;
574
575       while (new_periods) {
576         client->mpd_root_node->Periods =
577             g_list_insert_before (client->mpd_root_node->Periods, next,
578             new_periods->data);
579         new_periods = g_list_delete_link (new_periods, new_periods);
580       }
581       next = NULL;
582
583       /* Update our iterator to the first new period if any, or the next */
584       if (prev)
585         l = prev->next;
586       else
587         l = client->mpd_root_node->Periods;
588
589       continue;
590     }
591
592     if (period->SegmentList && period->SegmentList->xlink_href
593         && period->SegmentList->actuate == GST_MPD_XLINK_ACTUATE_ON_LOAD) {
594       GstMPDSegmentListNode *new_segment_list;
595
596       new_segment_list =
597           gst_mpd_client_fetch_external_segment_list (client, period, NULL,
598           NULL, NULL, period->SegmentList);
599
600       gst_mpd_segment_list_node_free (period->SegmentList);
601       period->SegmentList = new_segment_list;
602     }
603
604     for (m = period->AdaptationSets; m; /* explicitly advanced below */ ) {
605       GstMPDAdaptationSetNode *adapt_set = m->data;
606       GList *n;
607
608       if (adapt_set->xlink_href
609           && adapt_set->actuate == GST_MPD_XLINK_ACTUATE_ON_LOAD) {
610         GList *new_adapt_sets, *prev, *next;
611
612         new_adapt_sets =
613             gst_mpd_client_fetch_external_adaptation_set (client, period,
614             adapt_set);
615
616         prev = m->prev;
617         period->AdaptationSets = g_list_delete_link (period->AdaptationSets, m);
618         gst_mpd_adaptation_set_node_free (adapt_set);
619         adapt_set = NULL;
620
621         /* Get new next node, we will insert before this */
622         if (prev)
623           next = prev->next;
624         else
625           next = period->AdaptationSets;
626
627         while (new_adapt_sets) {
628           period->AdaptationSets =
629               g_list_insert_before (period->AdaptationSets, next,
630               new_adapt_sets->data);
631           new_adapt_sets = g_list_delete_link (new_adapt_sets, new_adapt_sets);
632         }
633         next = NULL;
634
635         /* Update our iterator to the first new adapt_set if any, or the next */
636         if (prev)
637           m = prev->next;
638         else
639           m = period->AdaptationSets;
640
641         continue;
642       }
643
644       if (adapt_set->SegmentList && adapt_set->SegmentList->xlink_href
645           && adapt_set->SegmentList->actuate == GST_MPD_XLINK_ACTUATE_ON_LOAD) {
646         GstMPDSegmentListNode *new_segment_list;
647
648         new_segment_list =
649             gst_mpd_client_fetch_external_segment_list (client, period,
650             adapt_set, NULL, period->SegmentList, adapt_set->SegmentList);
651
652         gst_mpd_segment_list_node_free (adapt_set->SegmentList);
653         adapt_set->SegmentList = new_segment_list;
654       }
655
656       for (n = adapt_set->Representations; n; n = n->next) {
657         GstMPDRepresentationNode *representation = n->data;
658
659         if (representation->SegmentList
660             && representation->SegmentList->xlink_href
661             && representation->SegmentList->actuate ==
662             GST_MPD_XLINK_ACTUATE_ON_LOAD) {
663
664           GstMPDSegmentListNode *new_segment_list;
665
666           new_segment_list =
667               gst_mpd_client_fetch_external_segment_list (client, period,
668               adapt_set, representation, adapt_set->SegmentList,
669               representation->SegmentList);
670
671           gst_mpd_segment_list_node_free (representation->SegmentList);
672           representation->SegmentList = new_segment_list;
673
674         }
675       }
676
677       m = m->next;
678     }
679
680     l = l->next;
681   }
682 }
683
684
685 static GstStreamPeriod *
686 gst_mpd_client_get_stream_period (GstMPDClient * client)
687 {
688   g_return_val_if_fail (client != NULL, NULL);
689   g_return_val_if_fail (client->periods != NULL, NULL);
690
691   return g_list_nth_data (client->periods, client->period_idx);
692 }
693
694 const gchar *
695 gst_mpd_client_get_baseURL (GstMPDClient * client, guint indexStream)
696 {
697   GstActiveStream *stream;
698
699   g_return_val_if_fail (client != NULL, NULL);
700   g_return_val_if_fail (client->active_streams != NULL, NULL);
701   stream = g_list_nth_data (client->active_streams, indexStream);
702   g_return_val_if_fail (stream != NULL, NULL);
703
704   return stream->baseURL;
705 }
706
707 /* select a stream and extract the baseURL (if present) */
708 gchar *
709 gst_mpd_client_parse_baseURL (GstMPDClient * client, GstActiveStream * stream,
710     gchar ** query)
711 {
712   GstStreamPeriod *stream_period;
713   static const gchar empty[] = "";
714   gchar *ret = NULL;
715   GstUri *abs_url;
716
717   g_return_val_if_fail (stream != NULL, g_strdup (empty));
718   stream_period = gst_mpd_client_get_stream_period (client);
719   g_return_val_if_fail (stream_period != NULL, g_strdup (empty));
720   g_return_val_if_fail (stream_period->period != NULL, g_strdup (empty));
721
722   /* NULLify query return before we start */
723   if (query)
724     *query = NULL;
725
726   /* initialise base url */
727   abs_url =
728       gst_uri_from_string (client->mpd_base_uri ? client->
729       mpd_base_uri : client->mpd_uri);
730
731   /* combine a BaseURL at the MPD level with the current base url */
732   abs_url =
733       gst_mpd_helper_combine_urls (abs_url, client->mpd_root_node->BaseURLs,
734       query, stream->baseURL_idx);
735
736   /* combine a BaseURL at the Period level with the current base url */
737   abs_url =
738       gst_mpd_helper_combine_urls (abs_url, stream_period->period->BaseURLs,
739       query, stream->baseURL_idx);
740
741   GST_DEBUG ("Current adaptation set id %i (%s)", stream->cur_adapt_set->id,
742       stream->cur_adapt_set->contentType);
743   /* combine a BaseURL at the AdaptationSet level with the current base url */
744   abs_url =
745       gst_mpd_helper_combine_urls (abs_url, stream->cur_adapt_set->BaseURLs,
746       query, stream->baseURL_idx);
747
748   /* combine a BaseURL at the Representation level with the current base url */
749   abs_url =
750       gst_mpd_helper_combine_urls (abs_url,
751       stream->cur_representation->BaseURLs, query, stream->baseURL_idx);
752
753   ret = gst_uri_to_string (abs_url);
754   gst_uri_unref (abs_url);
755
756   return ret;
757 }
758
759 static GstClockTime
760 gst_mpd_client_get_segment_end_time (GstMPDClient * client,
761     GPtrArray * segments, const GstMediaSegment * segment, gint index)
762 {
763   const GstStreamPeriod *stream_period;
764   GstClockTime end;
765
766   if (segment->repeat >= 0)
767     return segment->start + (segment->repeat + 1) * segment->duration;
768
769   if (index < segments->len - 1) {
770     const GstMediaSegment *next_segment =
771         g_ptr_array_index (segments, index + 1);
772     end = next_segment->start;
773   } else {
774     stream_period = gst_mpd_client_get_stream_period (client);
775     end = stream_period->start + stream_period->duration;
776   }
777   return end;
778 }
779
780 static gboolean
781 gst_mpd_client_add_media_segment (GstActiveStream * stream,
782     GstMPDSegmentURLNode * url_node, guint number, gint repeat,
783     guint64 scale_start, guint64 scale_duration,
784     GstClockTime start, GstClockTime duration)
785 {
786   GstMediaSegment *media_segment;
787
788   g_return_val_if_fail (stream->segments != NULL, FALSE);
789
790   media_segment = g_slice_new0 (GstMediaSegment);
791
792   media_segment->SegmentURL = url_node;
793   media_segment->number = number;
794   media_segment->scale_start = scale_start;
795   media_segment->scale_duration = scale_duration;
796   media_segment->start = start;
797   media_segment->duration = duration;
798   media_segment->repeat = repeat;
799
800   g_ptr_array_add (stream->segments, media_segment);
801   GST_LOG ("Added new segment: number %d, repeat %d, "
802       "ts: %" GST_TIME_FORMAT ", dur: %"
803       GST_TIME_FORMAT, number, repeat,
804       GST_TIME_ARGS (start), GST_TIME_ARGS (duration));
805
806   return TRUE;
807 }
808
809 static void
810 gst_mpd_client_stream_update_presentation_time_offset (GstMPDClient * client,
811     GstActiveStream * stream)
812 {
813   GstMPDSegmentBaseNode *segbase = NULL;
814
815   /* Find the used segbase */
816   if (stream->cur_segment_list) {
817     segbase =
818         GST_MPD_MULT_SEGMENT_BASE_NODE (stream->cur_segment_list)->SegmentBase;
819   } else if (stream->cur_seg_template) {
820     segbase =
821         GST_MPD_MULT_SEGMENT_BASE_NODE (stream->cur_seg_template)->SegmentBase;
822   } else if (stream->cur_segment_base) {
823     segbase = stream->cur_segment_base;
824   }
825
826   if (segbase) {
827     /* Avoid overflows */
828     stream->presentationTimeOffset =
829         gst_util_uint64_scale (segbase->presentationTimeOffset, GST_SECOND,
830         segbase->timescale);
831   } else {
832     stream->presentationTimeOffset = 0;
833   }
834
835   GST_LOG ("Setting stream's presentation time offset to %" GST_TIME_FORMAT,
836       GST_TIME_ARGS (stream->presentationTimeOffset));
837 }
838
839 gboolean
840 gst_mpd_client_setup_representation (GstMPDClient * client,
841     GstActiveStream * stream, GstMPDRepresentationNode * representation)
842 {
843   GstStreamPeriod *stream_period;
844   GList *rep_list;
845   GstClockTime PeriodStart, PeriodEnd, start_time, duration;
846   guint i;
847   guint64 start;
848
849   if (stream->cur_adapt_set == NULL) {
850     GST_WARNING ("No valid AdaptationSet node in the MPD file, aborting...");
851     return FALSE;
852   }
853
854   rep_list = stream->cur_adapt_set->Representations;
855   stream->cur_representation = representation;
856   stream->representation_idx = g_list_index (rep_list, representation);
857
858   /* clean the old segment list, if any */
859   if (stream->segments) {
860     g_ptr_array_unref (stream->segments);
861     stream->segments = NULL;
862   }
863
864   stream_period = gst_mpd_client_get_stream_period (client);
865   g_return_val_if_fail (stream_period != NULL, FALSE);
866   g_return_val_if_fail (stream_period->period != NULL, FALSE);
867
868   PeriodStart = stream_period->start;
869   if (GST_CLOCK_TIME_IS_VALID (stream_period->duration))
870     PeriodEnd = stream_period->start + stream_period->duration;
871   else
872     PeriodEnd = GST_CLOCK_TIME_NONE;
873
874   GST_LOG ("Building segment list for Period from %" GST_TIME_FORMAT " to %"
875       GST_TIME_FORMAT, GST_TIME_ARGS (PeriodStart), GST_TIME_ARGS (PeriodEnd));
876
877   if (representation->SegmentBase != NULL
878       || representation->SegmentList != NULL) {
879     GList *SegmentURL;
880
881     /* We have a fixed list of segments for any of the cases here,
882      * init the segments list */
883     gst_mpdparser_init_active_stream_segments (stream);
884
885     /* get the first segment_base of the selected representation */
886     if ((stream->cur_segment_base =
887             gst_mpd_client_get_segment_base (stream_period->period,
888                 stream->cur_adapt_set, representation)) == NULL) {
889       GST_DEBUG ("No useful SegmentBase node for the current Representation");
890     }
891
892     /* get the first segment_list of the selected representation */
893     if ((stream->cur_segment_list =
894             gst_mpd_client_get_segment_list (client, stream_period->period,
895                 stream->cur_adapt_set, representation)) == NULL) {
896       GST_DEBUG ("No useful SegmentList node for the current Representation");
897       /* here we should have a single segment for each representation, whose URL is encoded in the baseURL element */
898       if (!gst_mpd_client_add_media_segment (stream, NULL, 1, 0, 0,
899               PeriodEnd - PeriodStart, PeriodStart, PeriodEnd - PeriodStart)) {
900         return FALSE;
901       }
902     } else {
903       /* build the list of GstMediaSegment nodes from the SegmentList node */
904       SegmentURL = stream->cur_segment_list->SegmentURL;
905       if (SegmentURL == NULL) {
906         GST_WARNING
907             ("No valid list of SegmentURL nodes in the MPD file, aborting...");
908         return FALSE;
909       }
910
911       /* build segment list */
912       i = GST_MPD_MULT_SEGMENT_BASE_NODE (stream->
913           cur_segment_list)->startNumber;
914       start = 0;
915       start_time = PeriodStart;
916
917       GST_LOG ("Building media segment list using a SegmentList node");
918       if (GST_MPD_MULT_SEGMENT_BASE_NODE (stream->
919               cur_segment_list)->SegmentTimeline) {
920         GstMPDSegmentTimelineNode *timeline;
921         GstMPDSNode *S;
922         GList *list;
923         GstClockTime presentationTimeOffset;
924         GstMPDSegmentBaseNode *segbase;
925
926         segbase =
927             GST_MPD_MULT_SEGMENT_BASE_NODE (stream->
928             cur_segment_list)->SegmentBase;
929         presentationTimeOffset =
930             gst_util_uint64_scale (segbase->presentationTimeOffset, GST_SECOND,
931             segbase->timescale);
932         GST_LOG ("presentationTimeOffset = %" G_GUINT64_FORMAT,
933             presentationTimeOffset);
934
935         timeline =
936             GST_MPD_MULT_SEGMENT_BASE_NODE (stream->
937             cur_segment_list)->SegmentTimeline;
938         for (list = g_queue_peek_head_link (&timeline->S); list;
939             list = g_list_next (list)) {
940           guint timescale;
941
942           S = (GstMPDSNode *) list->data;
943           GST_LOG ("Processing S node: d=%" G_GUINT64_FORMAT " r=%d t=%"
944               G_GUINT64_FORMAT, S->d, S->r, S->t);
945           timescale =
946               GST_MPD_MULT_SEGMENT_BASE_NODE (stream->
947               cur_segment_list)->SegmentBase->timescale;
948           duration = gst_util_uint64_scale (S->d, GST_SECOND, timescale);
949
950           if (S->t > 0) {
951             start = S->t;
952             start_time = gst_util_uint64_scale (S->t, GST_SECOND, timescale)
953                 + PeriodStart - presentationTimeOffset;
954           }
955
956           if (!SegmentURL) {
957             GST_WARNING
958                 ("SegmentTimeline does not have a matching SegmentURL, aborting...");
959             return FALSE;
960           }
961
962           if (!gst_mpd_client_add_media_segment (stream, SegmentURL->data, i,
963                   S->r, start, S->d, start_time, duration)) {
964             return FALSE;
965           }
966           i += S->r + 1;
967           start_time += duration * (S->r + 1);
968           start += S->d * (S->r + 1);
969           SegmentURL = g_list_next (SegmentURL);
970         }
971       } else {
972         guint64 scale_dur;
973
974         duration =
975             gst_mpd_client_get_segment_duration (client, stream, &scale_dur);
976         if (!GST_CLOCK_TIME_IS_VALID (duration))
977           return FALSE;
978
979         while (SegmentURL) {
980           if (!gst_mpd_client_add_media_segment (stream, SegmentURL->data, i,
981                   0, start, scale_dur, start_time, duration)) {
982             return FALSE;
983           }
984           i++;
985           start += scale_dur;
986           start_time += duration;
987           SegmentURL = g_list_next (SegmentURL);
988         }
989       }
990     }
991   } else {
992     if (representation->SegmentTemplate != NULL) {
993       stream->cur_seg_template = representation->SegmentTemplate;
994     } else if (stream->cur_adapt_set->SegmentTemplate != NULL) {
995       stream->cur_seg_template = stream->cur_adapt_set->SegmentTemplate;
996     } else if (stream_period->period->SegmentTemplate != NULL) {
997       stream->cur_seg_template = stream_period->period->SegmentTemplate;
998     }
999
1000     if (stream->cur_seg_template == NULL) {
1001
1002       gst_mpdparser_init_active_stream_segments (stream);
1003       /* here we should have a single segment for each representation, whose URL is encoded in the baseURL element */
1004       if (!gst_mpd_client_add_media_segment (stream, NULL, 1, 0, 0,
1005               PeriodEnd - PeriodStart, 0, PeriodEnd - PeriodStart)) {
1006         return FALSE;
1007       }
1008     } else {
1009       GstClockTime presentationTimeOffset;
1010       GstMPDMultSegmentBaseNode *mult_seg =
1011           GST_MPD_MULT_SEGMENT_BASE_NODE (stream->cur_seg_template);
1012       presentationTimeOffset =
1013           gst_util_uint64_scale (mult_seg->SegmentBase->presentationTimeOffset,
1014           GST_SECOND, mult_seg->SegmentBase->timescale);
1015       GST_LOG ("presentationTimeOffset = %" GST_TIME_FORMAT,
1016           GST_TIME_ARGS (presentationTimeOffset));
1017       /* build segment list */
1018       i = mult_seg->startNumber;
1019       start = 0;
1020       start_time = 0;
1021
1022       GST_LOG ("Building media segment list using this template: %s",
1023           stream->cur_seg_template->media);
1024
1025       if (mult_seg->SegmentTimeline) {
1026         GstMPDSegmentTimelineNode *timeline;
1027         GstMPDSNode *S;
1028         GList *list;
1029
1030         timeline = mult_seg->SegmentTimeline;
1031         gst_mpdparser_init_active_stream_segments (stream);
1032         for (list = g_queue_peek_head_link (&timeline->S); list;
1033             list = g_list_next (list)) {
1034           guint timescale;
1035
1036           S = (GstMPDSNode *) list->data;
1037           GST_LOG ("Processing S node: d=%" G_GUINT64_FORMAT " r=%u t=%"
1038               G_GUINT64_FORMAT, S->d, S->r, S->t);
1039           timescale = mult_seg->SegmentBase->timescale;
1040           duration = gst_util_uint64_scale (S->d, GST_SECOND, timescale);
1041           if (S->t > 0) {
1042             start = S->t;
1043             start_time = gst_util_uint64_scale (S->t, GST_SECOND, timescale)
1044                 + PeriodStart - presentationTimeOffset;
1045           }
1046
1047           if (!gst_mpd_client_add_media_segment (stream, NULL, i, S->r, start,
1048                   S->d, start_time, duration)) {
1049             return FALSE;
1050           }
1051           i += S->r + 1;
1052           start += S->d * (S->r + 1);
1053           start_time += duration * (S->r + 1);
1054         }
1055       } else {
1056         /* NOP - The segment is created on demand with the template, no need
1057          * to build a list */
1058       }
1059     }
1060   }
1061
1062   /* clip duration of segments to stop at period end */
1063   if (stream->segments && stream->segments->len) {
1064     if (GST_CLOCK_TIME_IS_VALID (PeriodEnd)) {
1065       guint n;
1066
1067       for (n = 0; n < stream->segments->len; ++n) {
1068         GstMediaSegment *media_segment =
1069             g_ptr_array_index (stream->segments, n);
1070         if (media_segment) {
1071           if (media_segment->start + media_segment->duration > PeriodEnd) {
1072             GstClockTime stop = PeriodEnd;
1073             if (n < stream->segments->len - 1) {
1074               GstMediaSegment *next_segment =
1075                   g_ptr_array_index (stream->segments, n + 1);
1076               if (next_segment && next_segment->start < PeriodEnd)
1077                 stop = next_segment->start;
1078             }
1079             media_segment->duration =
1080                 media_segment->start > stop ? 0 : stop - media_segment->start;
1081             GST_LOG ("Fixed duration of segment %u: %" GST_TIME_FORMAT, n,
1082                 GST_TIME_ARGS (media_segment->duration));
1083
1084             /* If the segment was clipped entirely, we discard it and all
1085              * subsequent ones */
1086             if (media_segment->duration == 0) {
1087               GST_WARNING ("Discarding %u segments outside period",
1088                   stream->segments->len - n);
1089               /* _set_size should properly unref elements */
1090               g_ptr_array_set_size (stream->segments, n);
1091               break;
1092             }
1093           }
1094         }
1095       }
1096     }
1097 #ifndef GST_DISABLE_GST_DEBUG
1098     if (stream->segments->len > 0) {
1099       GstMediaSegment *last_media_segment =
1100           g_ptr_array_index (stream->segments, stream->segments->len - 1);
1101       GST_LOG ("Built a list of %d segments", last_media_segment->number);
1102     } else {
1103       GST_LOG ("All media segments were clipped");
1104     }
1105 #endif
1106   }
1107
1108   g_free (stream->baseURL);
1109   g_free (stream->queryURL);
1110   stream->baseURL =
1111       gst_mpd_client_parse_baseURL (client, stream, &stream->queryURL);
1112
1113   gst_mpd_client_stream_update_presentation_time_offset (client, stream);
1114
1115   return TRUE;
1116 }
1117
1118 #define CUSTOM_WRAPPER_START "<custom_wrapper>"
1119 #define CUSTOM_WRAPPER_END "</custom_wrapper>"
1120
1121 static GList *
1122 gst_mpd_client_fetch_external_periods (GstMPDClient * client,
1123     GstMPDPeriodNode * period_node)
1124 {
1125   GstFragment *download;
1126   GstAdapter *adapter;
1127   GstBuffer *period_buffer;
1128   GError *err = NULL;
1129
1130   GstUri *base_uri, *uri;
1131   gchar *query = NULL;
1132   gchar *uri_string, *wrapper;
1133   GList *new_periods = NULL;
1134   const gchar *data;
1135
1136   /* ISO/IEC 23009-1:2014 5.5.3 4)
1137    * Remove nodes that resolve to nothing when resolving
1138    */
1139   if (strcmp (period_node->xlink_href,
1140           "urn:mpeg:dash:resolve-to-zero:2013") == 0) {
1141     return NULL;
1142   }
1143
1144   if (!client->downloader) {
1145     return NULL;
1146   }
1147
1148   /* Build absolute URI */
1149
1150   /* Get base URI at the MPD level */
1151   base_uri =
1152       gst_uri_from_string (client->mpd_base_uri ? client->
1153       mpd_base_uri : client->mpd_uri);
1154
1155   /* combine a BaseURL at the MPD level with the current base url */
1156   base_uri =
1157       gst_mpd_helper_combine_urls (base_uri, client->mpd_root_node->BaseURLs,
1158       &query, 0);
1159   uri = gst_uri_from_string_with_base (base_uri, period_node->xlink_href);
1160   if (query)
1161     gst_uri_set_query_string (uri, query);
1162   g_free (query);
1163   uri_string = gst_uri_to_string (uri);
1164   gst_uri_unref (base_uri);
1165   gst_uri_unref (uri);
1166
1167   download =
1168       gst_uri_downloader_fetch_uri (client->downloader,
1169       uri_string, client->mpd_uri, TRUE, FALSE, TRUE, &err);
1170   g_free (uri_string);
1171
1172   if (!download) {
1173     GST_ERROR ("Failed to download external Period node at '%s': %s",
1174         period_node->xlink_href, err->message);
1175     g_clear_error (&err);
1176     return NULL;
1177   }
1178
1179   period_buffer = gst_fragment_get_buffer (download);
1180   g_object_unref (download);
1181
1182   /* external xml could have multiple period without root xmlNode.
1183    * To avoid xml parsing error caused by no root node, wrapping it with
1184    * custom root node */
1185   adapter = gst_adapter_new ();
1186
1187   wrapper = g_new (gchar, strlen (CUSTOM_WRAPPER_START));
1188   memcpy (wrapper, CUSTOM_WRAPPER_START, strlen (CUSTOM_WRAPPER_START));
1189   gst_adapter_push (adapter,
1190       gst_buffer_new_wrapped (wrapper, strlen (CUSTOM_WRAPPER_START)));
1191
1192   gst_adapter_push (adapter, period_buffer);
1193
1194   wrapper = g_strdup (CUSTOM_WRAPPER_END);
1195   gst_adapter_push (adapter,
1196       gst_buffer_new_wrapped (wrapper, strlen (CUSTOM_WRAPPER_END) + 1));
1197
1198   data = gst_adapter_map (adapter, gst_adapter_available (adapter));
1199
1200   new_periods =
1201       gst_mpdparser_get_external_periods (data,
1202       gst_adapter_available (adapter));
1203
1204   gst_adapter_unmap (adapter);
1205   gst_adapter_clear (adapter);
1206   gst_object_unref (adapter);
1207
1208   return new_periods;
1209 }
1210
1211 gboolean
1212 gst_mpd_client_setup_media_presentation (GstMPDClient * client,
1213     GstClockTime time, gint period_idx, const gchar * period_id)
1214 {
1215   GstStreamPeriod *stream_period;
1216   GstClockTime start, duration;
1217   GList *list, *next;
1218   guint idx;
1219   gboolean ret = FALSE;
1220
1221   g_return_val_if_fail (client != NULL, FALSE);
1222   g_return_val_if_fail (client->mpd_root_node != NULL, FALSE);
1223
1224   /* Check if we set up the media presentation far enough already */
1225   for (list = client->periods; list; list = list->next) {
1226     GstStreamPeriod *stream_period = list->data;
1227
1228     if ((time != GST_CLOCK_TIME_NONE
1229             && stream_period->duration != GST_CLOCK_TIME_NONE
1230             && stream_period->start + stream_period->duration >= time)
1231         || (time != GST_CLOCK_TIME_NONE && stream_period->start >= time))
1232       return TRUE;
1233
1234     if (period_idx != -1 && stream_period->number >= period_idx)
1235       return TRUE;
1236
1237     if (period_id != NULL && stream_period->period->id != NULL
1238         && strcmp (stream_period->period->id, period_id) == 0)
1239       return TRUE;
1240
1241   }
1242
1243   GST_DEBUG ("Building the list of Periods in the Media Presentation");
1244   /* clean the old period list, if any */
1245   /* TODO: In theory we could reuse the ones we have so far but that
1246    * seems more complicated than the overhead caused here
1247    */
1248   if (client->periods) {
1249     g_list_foreach (client->periods,
1250         (GFunc) gst_mpdparser_free_stream_period, NULL);
1251     g_list_free (client->periods);
1252     client->periods = NULL;
1253   }
1254
1255   idx = 0;
1256   start = 0;
1257   duration = GST_CLOCK_TIME_NONE;
1258
1259   if (client->mpd_root_node->mediaPresentationDuration <= 0 &&
1260       client->mpd_root_node->mediaPresentationDuration != -1) {
1261     /* Invalid MPD file: MPD duration is negative or zero */
1262     goto syntax_error;
1263   }
1264
1265   for (list = client->mpd_root_node->Periods; list;
1266       /* explicitly advanced below */ ) {
1267     GstMPDPeriodNode *period_node = list->data;
1268     GstMPDPeriodNode *next_period_node = NULL;
1269
1270     /* Download external period */
1271     if (period_node->xlink_href) {
1272       GList *new_periods;
1273       GList *prev;
1274
1275       new_periods = gst_mpd_client_fetch_external_periods (client, period_node);
1276
1277       prev = list->prev;
1278       client->mpd_root_node->Periods =
1279           g_list_delete_link (client->mpd_root_node->Periods, list);
1280       gst_mpd_period_node_free (period_node);
1281       period_node = NULL;
1282
1283       /* Get new next node, we will insert before this */
1284       if (prev)
1285         next = prev->next;
1286       else
1287         next = client->mpd_root_node->Periods;
1288
1289       while (new_periods) {
1290         client->mpd_root_node->Periods =
1291             g_list_insert_before (client->mpd_root_node->Periods, next,
1292             new_periods->data);
1293         new_periods = g_list_delete_link (new_periods, new_periods);
1294       }
1295       next = NULL;
1296
1297       /* Update our iterator to the first new period if any, or the next */
1298       if (prev)
1299         list = prev->next;
1300       else
1301         list = client->mpd_root_node->Periods;
1302
1303       /* And try again */
1304       continue;
1305     }
1306
1307     if (period_node->start != -1) {
1308       /* we have a regular period */
1309       /* start cannot be smaller than previous start */
1310       if (list != g_list_first (client->mpd_root_node->Periods)
1311           && start >= period_node->start * GST_MSECOND) {
1312         /* Invalid MPD file: duration would be negative or zero */
1313         goto syntax_error;
1314       }
1315       start = period_node->start * GST_MSECOND;
1316     } else if (duration != GST_CLOCK_TIME_NONE) {
1317       /* start time inferred from previous period, this is still a regular period */
1318       start += duration;
1319     } else if (idx == 0
1320         && client->mpd_root_node->type == GST_MPD_FILE_TYPE_STATIC) {
1321       /* first period of a static MPD file, start time is 0 */
1322       start = 0;
1323     } else if (client->mpd_root_node->type == GST_MPD_FILE_TYPE_DYNAMIC) {
1324       /* this should be a live stream, let this pass */
1325     } else {
1326       /* this is an 'Early Available Period' */
1327       goto early;
1328     }
1329
1330     /* compute duration.
1331        If there is a start time for the next period, or this is the last period
1332        and mediaPresentationDuration was set, those values will take precedence
1333        over a configured period duration in computing this period's duration
1334
1335        ISO/IEC 23009-1:2014(E), chapter 5.3.2.1
1336        "The Period extends until the PeriodStart of the next Period, or until
1337        the end of the Media Presentation in the case of the last Period."
1338      */
1339
1340     while ((next = g_list_next (list)) != NULL) {
1341       /* try to infer this period duration from the start time of the next period */
1342       next_period_node = next->data;
1343
1344       if (next_period_node->xlink_href) {
1345         GList *new_periods;
1346
1347         new_periods =
1348             gst_mpd_client_fetch_external_periods (client, next_period_node);
1349
1350         client->mpd_root_node->Periods =
1351             g_list_delete_link (client->mpd_root_node->Periods, next);
1352         gst_mpd_period_node_free (next_period_node);
1353         next_period_node = NULL;
1354         /* Get new next node, we will insert before this */
1355         next = g_list_next (list);
1356         while (new_periods) {
1357           client->mpd_root_node->Periods =
1358               g_list_insert_before (client->mpd_root_node->Periods, next,
1359               new_periods->data);
1360           new_periods = g_list_delete_link (new_periods, new_periods);
1361         }
1362
1363         /* And try again, getting the next list element which is now our newly
1364          * inserted nodes. If any */
1365       } else {
1366         /* Got the next period and it doesn't have to be downloaded first */
1367         break;
1368       }
1369     }
1370
1371     if (next_period_node) {
1372       if (next_period_node->start != -1) {
1373         if (start >= next_period_node->start * GST_MSECOND) {
1374           /* Invalid MPD file: duration would be negative or zero */
1375           goto syntax_error;
1376         }
1377         duration = next_period_node->start * GST_MSECOND - start;
1378       } else if (period_node->duration != -1) {
1379         if (period_node->duration <= 0) {
1380           /* Invalid MPD file: duration would be negative or zero */
1381           goto syntax_error;
1382         }
1383         duration = period_node->duration * GST_MSECOND;
1384       } else if (client->mpd_root_node->type == GST_MPD_FILE_TYPE_DYNAMIC) {
1385         /* might be a live file, ignore unspecified duration */
1386       } else {
1387         /* Invalid MPD file! */
1388         goto syntax_error;
1389       }
1390     } else if (client->mpd_root_node->mediaPresentationDuration != -1) {
1391       /* last Period of the Media Presentation */
1392       if (client->mpd_root_node->mediaPresentationDuration * GST_MSECOND <=
1393           start) {
1394         /* Invalid MPD file: duration would be negative or zero */
1395         goto syntax_error;
1396       }
1397       duration =
1398           client->mpd_root_node->mediaPresentationDuration * GST_MSECOND -
1399           start;
1400     } else if (period_node->duration != -1) {
1401       duration = period_node->duration * GST_MSECOND;
1402     } else if (client->mpd_root_node->type == GST_MPD_FILE_TYPE_DYNAMIC) {
1403       /* might be a live file, ignore unspecified duration */
1404     } else {
1405       /* Invalid MPD file! */
1406       GST_ERROR
1407           ("Invalid MPD file. The MPD is static without a valid duration");
1408       goto syntax_error;
1409     }
1410
1411     stream_period = g_slice_new0 (GstStreamPeriod);
1412     client->periods = g_list_append (client->periods, stream_period);
1413     stream_period->period = period_node;
1414     stream_period->number = idx++;
1415     stream_period->start = start;
1416     stream_period->duration = duration;
1417     ret = TRUE;
1418     GST_LOG (" - added Period %d start=%" GST_TIME_FORMAT " duration=%"
1419         GST_TIME_FORMAT, idx, GST_TIME_ARGS (start), GST_TIME_ARGS (duration));
1420
1421     if ((time != GST_CLOCK_TIME_NONE
1422             && stream_period->duration != GST_CLOCK_TIME_NONE
1423             && stream_period->start + stream_period->duration >= time)
1424         || (time != GST_CLOCK_TIME_NONE && stream_period->start >= time))
1425       break;
1426
1427     if (period_idx != -1 && stream_period->number >= period_idx)
1428       break;
1429
1430     if (period_id != NULL && stream_period->period->id != NULL
1431         && strcmp (stream_period->period->id, period_id) == 0)
1432       break;
1433
1434     list = list->next;
1435   }
1436
1437   GST_DEBUG
1438       ("Found a total of %d valid Periods in the Media Presentation up to this point",
1439       idx);
1440   return ret;
1441
1442 early:
1443   GST_WARNING
1444       ("Found an Early Available Period, skipping the rest of the Media Presentation");
1445   return ret;
1446
1447 syntax_error:
1448   GST_WARNING
1449       ("Cannot get the duration of the Period %d, skipping the rest of the Media Presentation",
1450       idx);
1451   return ret;
1452 }
1453
1454 static GList *
1455 gst_mpd_client_fetch_external_adaptation_set (GstMPDClient * client,
1456     GstMPDPeriodNode * period, GstMPDAdaptationSetNode * adapt_set)
1457 {
1458   GstFragment *download;
1459   GstBuffer *adapt_set_buffer;
1460   GstMapInfo map;
1461   GError *err = NULL;
1462   GstUri *base_uri, *uri;
1463   gchar *query = NULL;
1464   gchar *uri_string;
1465   GList *new_adapt_sets = NULL;
1466
1467   /* ISO/IEC 23009-1:2014 5.5.3 4)
1468    * Remove nodes that resolve to nothing when resolving
1469    */
1470   if (strcmp (adapt_set->xlink_href, "urn:mpeg:dash:resolve-to-zero:2013") == 0) {
1471     return NULL;
1472   }
1473
1474   if (!client->downloader) {
1475     return NULL;
1476   }
1477
1478   /* Build absolute URI */
1479
1480   /* Get base URI at the MPD level */
1481   base_uri =
1482       gst_uri_from_string (client->mpd_base_uri ? client->
1483       mpd_base_uri : client->mpd_uri);
1484
1485   /* combine a BaseURL at the MPD level with the current base url */
1486   base_uri =
1487       gst_mpd_helper_combine_urls (base_uri, client->mpd_root_node->BaseURLs,
1488       &query, 0);
1489
1490   /* combine a BaseURL at the Period level with the current base url */
1491   base_uri =
1492       gst_mpd_helper_combine_urls (base_uri, period->BaseURLs, &query, 0);
1493
1494   uri = gst_uri_from_string_with_base (base_uri, adapt_set->xlink_href);
1495   if (query)
1496     gst_uri_set_query_string (uri, query);
1497   g_free (query);
1498   uri_string = gst_uri_to_string (uri);
1499   gst_uri_unref (base_uri);
1500   gst_uri_unref (uri);
1501
1502   download =
1503       gst_uri_downloader_fetch_uri (client->downloader,
1504       uri_string, client->mpd_uri, TRUE, FALSE, TRUE, &err);
1505   g_free (uri_string);
1506
1507   if (!download) {
1508     GST_ERROR ("Failed to download external AdaptationSet node at '%s': %s",
1509         adapt_set->xlink_href, err->message);
1510     g_clear_error (&err);
1511     return NULL;
1512   }
1513
1514   adapt_set_buffer = gst_fragment_get_buffer (download);
1515   g_object_unref (download);
1516
1517   gst_buffer_map (adapt_set_buffer, &map, GST_MAP_READ);
1518
1519   new_adapt_sets =
1520       gst_mpdparser_get_external_adaptation_sets ((const gchar *) map.data,
1521       map.size, period);
1522
1523   gst_buffer_unmap (adapt_set_buffer, &map);
1524   gst_buffer_unref (adapt_set_buffer);
1525
1526   return new_adapt_sets;
1527 }
1528
1529 static GList *
1530 gst_mpd_client_get_adaptation_sets_for_period (GstMPDClient * client,
1531     GstStreamPeriod * period)
1532 {
1533   GList *list;
1534
1535   g_return_val_if_fail (period != NULL, NULL);
1536
1537   /* Resolve all external adaptation sets of this period. Every user of
1538    * the adaptation sets would need to know the content of all adaptation sets
1539    * to decide which one to use, so we have to resolve them all here
1540    */
1541   for (list = period->period->AdaptationSets; list;
1542       /* advanced explicitly below */ ) {
1543     GstMPDAdaptationSetNode *adapt_set = (GstMPDAdaptationSetNode *) list->data;
1544     GList *new_adapt_sets = NULL, *prev, *next;
1545
1546     if (!adapt_set->xlink_href) {
1547       list = list->next;
1548       continue;
1549     }
1550
1551     new_adapt_sets =
1552         gst_mpd_client_fetch_external_adaptation_set (client, period->period,
1553         adapt_set);
1554
1555     prev = list->prev;
1556     period->period->AdaptationSets =
1557         g_list_delete_link (period->period->AdaptationSets, list);
1558     gst_mpd_adaptation_set_node_free (adapt_set);
1559     adapt_set = NULL;
1560
1561     /* Get new next node, we will insert before this */
1562     if (prev)
1563       next = prev->next;
1564     else
1565       next = period->period->AdaptationSets;
1566
1567     while (new_adapt_sets) {
1568       period->period->AdaptationSets =
1569           g_list_insert_before (period->period->AdaptationSets, next,
1570           new_adapt_sets->data);
1571       new_adapt_sets = g_list_delete_link (new_adapt_sets, new_adapt_sets);
1572     }
1573
1574     /* Update our iterator to the first new adaptation set if any, or the next */
1575     if (prev)
1576       list = prev->next;
1577     else
1578       list = period->period->AdaptationSets;
1579   }
1580
1581   return period->period->AdaptationSets;
1582 }
1583
1584 GList *
1585 gst_mpd_client_get_adaptation_sets (GstMPDClient * client)
1586 {
1587   GstStreamPeriod *stream_period;
1588
1589   stream_period = gst_mpd_client_get_stream_period (client);
1590   if (stream_period == NULL || stream_period->period == NULL) {
1591     GST_DEBUG ("No more Period nodes in the MPD file, terminating...");
1592     return NULL;
1593   }
1594
1595   return gst_mpd_client_get_adaptation_sets_for_period (client, stream_period);
1596 }
1597
1598 gboolean
1599 gst_mpd_client_setup_streaming (GstMPDClient * client,
1600     GstMPDAdaptationSetNode * adapt_set)
1601 {
1602   GstMPDRepresentationNode *representation;
1603   GList *rep_list = NULL;
1604   GstActiveStream *stream;
1605
1606   rep_list = adapt_set->Representations;
1607   if (!rep_list) {
1608     GST_WARNING ("Can not retrieve any representation, aborting...");
1609     return FALSE;
1610   }
1611
1612   stream = g_slice_new0 (GstActiveStream);
1613   gst_mpdparser_init_active_stream_segments (stream);
1614
1615   stream->baseURL_idx = 0;
1616   stream->cur_adapt_set = adapt_set;
1617
1618   GST_DEBUG ("0. Current stream %p", stream);
1619
1620 #if 0
1621   /* fast start */
1622   representation =
1623       gst_mpdparser_get_representation_with_max_bandwidth (rep_list,
1624       stream->max_bandwidth);
1625
1626   if (!representation) {
1627     GST_WARNING
1628         ("Can not retrieve a representation with the requested bandwidth");
1629     representation = gst_mpd_client_get_lowest_representation (rep_list);
1630   }
1631 #else
1632   /* slow start */
1633   representation = gst_mpd_client_get_lowest_representation (rep_list);
1634 #endif
1635
1636   if (!representation) {
1637     GST_WARNING ("No valid representation in the MPD file, aborting...");
1638     gst_mpdparser_free_active_stream (stream);
1639     return FALSE;
1640   }
1641   stream->mimeType =
1642       gst_mpdparser_representation_get_mimetype (adapt_set, representation);
1643   if (stream->mimeType == GST_STREAM_UNKNOWN) {
1644     GST_WARNING ("Unknown mime type in the representation, aborting...");
1645     gst_mpdparser_free_active_stream (stream);
1646     return FALSE;
1647   }
1648
1649   client->active_streams = g_list_append (client->active_streams, stream);
1650   if (!gst_mpd_client_setup_representation (client, stream, representation)) {
1651     GST_WARNING ("Failed to setup the representation, aborting...");
1652     return FALSE;
1653   }
1654
1655   GST_INFO ("Successfully setup the download pipeline for mimeType %d",
1656       stream->mimeType);
1657
1658   return TRUE;
1659 }
1660
1661 gboolean
1662 gst_mpd_client_stream_seek (GstMPDClient * client, GstActiveStream * stream,
1663     gboolean forward, GstSeekFlags flags, GstClockTime ts,
1664     GstClockTime * final_ts)
1665 {
1666   gint index = 0;
1667   gint repeat_index = 0;
1668   GstMediaSegment *selectedChunk = NULL;
1669
1670   g_return_val_if_fail (stream != NULL, 0);
1671
1672   if (stream->segments) {
1673     for (index = 0; index < stream->segments->len; index++) {
1674       gboolean in_segment = FALSE;
1675       GstMediaSegment *segment = g_ptr_array_index (stream->segments, index);
1676       GstClockTime end_time;
1677
1678       GST_DEBUG ("Looking at fragment sequence chunk %d / %d", index,
1679           stream->segments->len);
1680
1681       end_time =
1682           gst_mpd_client_get_segment_end_time (client, stream->segments,
1683           segment, index);
1684
1685       /* avoid downloading another fragment just for 1ns in reverse mode */
1686       if (forward)
1687         in_segment = ts < end_time;
1688       else
1689         in_segment = ts <= end_time;
1690
1691       if (in_segment) {
1692         GstClockTime chunk_time;
1693
1694         selectedChunk = segment;
1695         repeat_index = (ts - segment->start) / segment->duration;
1696
1697         chunk_time = segment->start + segment->duration * repeat_index;
1698
1699         /* At the end of a segment in reverse mode, start from the previous fragment */
1700         if (!forward && repeat_index > 0
1701             && ((ts - segment->start) % segment->duration == 0))
1702           repeat_index--;
1703
1704         if ((flags & GST_SEEK_FLAG_SNAP_NEAREST) == GST_SEEK_FLAG_SNAP_NEAREST) {
1705           if (repeat_index + 1 < segment->repeat) {
1706             if (ts - chunk_time > chunk_time + segment->duration - ts)
1707               repeat_index++;
1708           } else if (index + 1 < stream->segments->len) {
1709             GstMediaSegment *next_segment =
1710                 g_ptr_array_index (stream->segments, index + 1);
1711
1712             if (ts - chunk_time > next_segment->start - ts) {
1713               repeat_index = 0;
1714               selectedChunk = next_segment;
1715               index++;
1716             }
1717           }
1718         } else if (((forward && flags & GST_SEEK_FLAG_SNAP_AFTER) ||
1719                 (!forward && flags & GST_SEEK_FLAG_SNAP_BEFORE)) &&
1720             ts != chunk_time) {
1721
1722           if (repeat_index + 1 < segment->repeat) {
1723             repeat_index++;
1724           } else {
1725             repeat_index = 0;
1726             if (index + 1 >= stream->segments->len) {
1727               selectedChunk = NULL;
1728             } else {
1729               selectedChunk = g_ptr_array_index (stream->segments, ++index);
1730             }
1731           }
1732         }
1733         break;
1734       }
1735     }
1736
1737     if (selectedChunk == NULL) {
1738       stream->segment_index = stream->segments->len;
1739       stream->segment_repeat_index = 0;
1740       GST_DEBUG ("Seek to after last segment");
1741       return FALSE;
1742     }
1743
1744     if (final_ts)
1745       *final_ts = selectedChunk->start + selectedChunk->duration * repeat_index;
1746   } else {
1747     GstClockTime duration =
1748         gst_mpd_client_get_segment_duration (client, stream, NULL);
1749     GstStreamPeriod *stream_period = gst_mpd_client_get_stream_period (client);
1750     guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
1751     GstClockTime index_time;
1752
1753     g_return_val_if_fail (GST_MPD_MULT_SEGMENT_BASE_NODE
1754         (stream->cur_seg_template)->SegmentTimeline == NULL, FALSE);
1755     if (!GST_CLOCK_TIME_IS_VALID (duration)) {
1756       return FALSE;
1757     }
1758
1759     if (ts > stream_period->start)
1760       ts -= stream_period->start;
1761     else
1762       ts = 0;
1763
1764     index = ts / duration;
1765
1766     /* At the end of a segment in reverse mode, start from the previous fragment */
1767     if (!forward && index > 0 && ts % duration == 0)
1768       index--;
1769
1770     index_time = index * duration;
1771
1772     if ((flags & GST_SEEK_FLAG_SNAP_NEAREST) == GST_SEEK_FLAG_SNAP_NEAREST) {
1773       if (ts - index_time > index_time + duration - ts)
1774         index++;
1775     } else if (((forward && flags & GST_SEEK_FLAG_SNAP_AFTER) ||
1776             (!forward && flags & GST_SEEK_FLAG_SNAP_BEFORE))
1777         && ts != index_time) {
1778       index++;
1779     }
1780
1781     if (segments_count > 0 && index >= segments_count) {
1782       stream->segment_index = segments_count;
1783       stream->segment_repeat_index = 0;
1784       GST_DEBUG ("Seek to after last segment");
1785       return FALSE;
1786     }
1787     if (final_ts)
1788       *final_ts = index * duration;
1789   }
1790
1791   stream->segment_repeat_index = repeat_index;
1792   stream->segment_index = index;
1793
1794   return TRUE;
1795 }
1796
1797 gint64
1798 gst_mpd_client_calculate_time_difference (const GstDateTime * t1,
1799     const GstDateTime * t2)
1800 {
1801   GDateTime *gdt1, *gdt2;
1802   GTimeSpan diff;
1803
1804   g_assert (t1 != NULL && t2 != NULL);
1805   gdt1 = gst_date_time_to_g_date_time ((GstDateTime *) t1);
1806   gdt2 = gst_date_time_to_g_date_time ((GstDateTime *) t2);
1807   diff = g_date_time_difference (gdt2, gdt1);
1808   g_date_time_unref (gdt1);
1809   g_date_time_unref (gdt2);
1810   return diff * GST_USECOND;
1811 }
1812
1813 GstDateTime *
1814 gst_mpd_client_add_time_difference (GstDateTime * t1, gint64 usecs)
1815 {
1816   GDateTime *gdt;
1817   GDateTime *gdt2;
1818   GstDateTime *rv;
1819
1820   g_assert (t1 != NULL);
1821   gdt = gst_date_time_to_g_date_time (t1);
1822   g_assert (gdt != NULL);
1823   gdt2 = g_date_time_add (gdt, usecs);
1824   g_assert (gdt2 != NULL);
1825   g_date_time_unref (gdt);
1826   rv = gst_date_time_new_from_g_date_time (gdt2);
1827
1828   /* Don't g_date_time_unref(gdt2) because gst_date_time_new_from_g_date_time takes
1829    * ownership of the GDateTime pointer.
1830    */
1831
1832   return rv;
1833 }
1834
1835 gboolean
1836 gst_mpd_client_get_last_fragment_timestamp_end (GstMPDClient * client,
1837     guint stream_idx, GstClockTime * ts)
1838 {
1839   GstActiveStream *stream;
1840   gint segment_idx;
1841   GstMediaSegment *currentChunk;
1842   GstStreamPeriod *stream_period;
1843
1844   GST_DEBUG ("Stream index: %i", stream_idx);
1845   stream = g_list_nth_data (client->active_streams, stream_idx);
1846   g_return_val_if_fail (stream != NULL, 0);
1847
1848   if (!stream->segments) {
1849     stream_period = gst_mpd_client_get_stream_period (client);
1850     *ts = stream_period->start + stream_period->duration;
1851   } else {
1852     segment_idx = gst_mpd_client_get_segments_counts (client, stream) - 1;
1853     if (segment_idx >= stream->segments->len) {
1854       GST_WARNING ("Segment index %d is outside of segment list of length %d",
1855           segment_idx, stream->segments->len);
1856       return FALSE;
1857     }
1858     currentChunk = g_ptr_array_index (stream->segments, segment_idx);
1859
1860     if (currentChunk->repeat >= 0) {
1861       *ts =
1862           currentChunk->start + (currentChunk->duration * (1 +
1863               currentChunk->repeat));
1864     } else {
1865       /* 5.3.9.6.1: negative repeat means repeat till the end of the
1866        * period, or the next update of the MPD (which I think is
1867        * implicit, as this will all get deleted/recreated), or the
1868        * start of the next segment, if any. */
1869       stream_period = gst_mpd_client_get_stream_period (client);
1870       *ts = stream_period->start + stream_period->duration;
1871     }
1872   }
1873
1874   return TRUE;
1875 }
1876
1877 gboolean
1878 gst_mpd_client_get_next_fragment_timestamp (GstMPDClient * client,
1879     guint stream_idx, GstClockTime * ts)
1880 {
1881   GstActiveStream *stream;
1882   GstMediaSegment *currentChunk;
1883
1884   GST_DEBUG ("Stream index: %i", stream_idx);
1885   stream = g_list_nth_data (client->active_streams, stream_idx);
1886   g_return_val_if_fail (stream != NULL, 0);
1887
1888   if (stream->segments) {
1889     GST_DEBUG ("Looking for fragment sequence chunk %d / %d",
1890         stream->segment_index, stream->segments->len);
1891     if (stream->segment_index >= stream->segments->len)
1892       return FALSE;
1893     currentChunk = g_ptr_array_index (stream->segments, stream->segment_index);
1894
1895     *ts =
1896         currentChunk->start +
1897         (currentChunk->duration * stream->segment_repeat_index);
1898   } else {
1899     GstClockTime duration =
1900         gst_mpd_client_get_segment_duration (client, stream, NULL);
1901     guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
1902
1903     g_return_val_if_fail (GST_MPD_MULT_SEGMENT_BASE_NODE
1904         (stream->cur_seg_template)->SegmentTimeline == NULL, FALSE);
1905     if (!GST_CLOCK_TIME_IS_VALID (duration) || (segments_count > 0
1906             && stream->segment_index >= segments_count)) {
1907       return FALSE;
1908     }
1909     *ts = stream->segment_index * duration;
1910   }
1911
1912   return TRUE;
1913 }
1914
1915 GstClockTime
1916 gst_mpd_client_get_stream_presentation_offset (GstMPDClient * client,
1917     guint stream_idx)
1918 {
1919   GstActiveStream *stream = NULL;
1920
1921   g_return_val_if_fail (client != NULL, 0);
1922   g_return_val_if_fail (client->active_streams != NULL, 0);
1923   stream = g_list_nth_data (client->active_streams, stream_idx);
1924   g_return_val_if_fail (stream != NULL, 0);
1925
1926   return stream->presentationTimeOffset;
1927 }
1928
1929 GstClockTime
1930 gst_mpd_client_get_period_start_time (GstMPDClient * client)
1931 {
1932   GstStreamPeriod *stream_period = NULL;
1933
1934   g_return_val_if_fail (client != NULL, 0);
1935   stream_period = gst_mpd_client_get_stream_period (client);
1936   g_return_val_if_fail (stream_period != NULL, 0);
1937
1938   return stream_period->start;
1939 }
1940
1941 /**
1942  * gst_mpd_client_get_utc_timing_sources:
1943  * @client: #GstMPDClient to check for UTCTiming elements
1944  * @methods: A bit mask of #GstMPDUTCTimingType that specifies the methods
1945  *     to search for.
1946  * @selected_method: (nullable): The selected method
1947  * Returns: (transfer none): A NULL terminated array of URLs of servers
1948  *     that use @selected_method to provide a realtime clock.
1949  *
1950  * Searches the UTCTiming elements found in the manifest for an element
1951  * that uses one of the UTC timing methods specified in @selected_method.
1952  * If multiple UTCTiming elements are present that support one of the
1953  * methods specified in @selected_method, the first one is returned.
1954  *
1955  * Since: 1.6
1956  */
1957 gchar **
1958 gst_mpd_client_get_utc_timing_sources (GstMPDClient * client,
1959     guint methods, GstMPDUTCTimingType * selected_method)
1960 {
1961   GList *list;
1962
1963   g_return_val_if_fail (client != NULL, NULL);
1964   g_return_val_if_fail (client->mpd_root_node != NULL, NULL);
1965   for (list = g_list_first (client->mpd_root_node->UTCTimings); list;
1966       list = g_list_next (list)) {
1967     const GstMPDUTCTimingNode *node = (const GstMPDUTCTimingNode *) list->data;
1968     if (node->method & methods) {
1969       if (selected_method) {
1970         *selected_method = node->method;
1971       }
1972       return node->urls;
1973     }
1974   }
1975   return NULL;
1976 }
1977
1978
1979 gboolean
1980 gst_mpd_client_get_next_fragment (GstMPDClient * client,
1981     guint indexStream, GstMediaFragmentInfo * fragment)
1982 {
1983   GstActiveStream *stream = NULL;
1984   GstMediaSegment *currentChunk;
1985   gchar *mediaURL = NULL;
1986   gchar *indexURL = NULL;
1987   GstUri *base_url, *frag_url;
1988
1989   /* select stream */
1990   g_return_val_if_fail (client != NULL, FALSE);
1991   g_return_val_if_fail (client->active_streams != NULL, FALSE);
1992   stream = g_list_nth_data (client->active_streams, indexStream);
1993   g_return_val_if_fail (stream != NULL, FALSE);
1994   g_return_val_if_fail (stream->cur_representation != NULL, FALSE);
1995
1996   if (stream->segments) {
1997     GST_DEBUG ("Looking for fragment sequence chunk %d / %d",
1998         stream->segment_index, stream->segments->len);
1999     if (stream->segment_index >= stream->segments->len)
2000       return FALSE;
2001   } else {
2002     GstClockTime duration = gst_mpd_client_get_segment_duration (client,
2003         stream, NULL);
2004     guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
2005
2006     g_return_val_if_fail (GST_MPD_MULT_SEGMENT_BASE_NODE
2007         (stream->cur_seg_template)->SegmentTimeline == NULL, FALSE);
2008     if (!GST_CLOCK_TIME_IS_VALID (duration) || (segments_count > 0
2009             && stream->segment_index >= segments_count)) {
2010       return FALSE;
2011     }
2012     fragment->duration = duration;
2013   }
2014
2015   /* FIXME rework discont checking */
2016   /* fragment->discontinuity = segment_idx != currentChunk.number; */
2017   fragment->range_start = 0;
2018   fragment->range_end = -1;
2019   fragment->index_uri = NULL;
2020   fragment->index_range_start = 0;
2021   fragment->index_range_end = -1;
2022
2023   if (stream->segments) {
2024     currentChunk = g_ptr_array_index (stream->segments, stream->segment_index);
2025
2026     GST_DEBUG ("currentChunk->SegmentURL = %p", currentChunk->SegmentURL);
2027     if (currentChunk->SegmentURL != NULL) {
2028       mediaURL =
2029           g_strdup (gst_mpdparser_get_mediaURL (stream,
2030               currentChunk->SegmentURL));
2031       indexURL = g_strdup (currentChunk->SegmentURL->index);
2032     } else if (stream->cur_seg_template != NULL) {
2033       mediaURL =
2034           gst_mpdparser_build_URL_from_template (stream->cur_seg_template->
2035           media, stream->cur_representation->id,
2036           currentChunk->number + stream->segment_repeat_index,
2037           stream->cur_representation->bandwidth,
2038           currentChunk->scale_start +
2039           stream->segment_repeat_index * currentChunk->scale_duration);
2040       if (stream->cur_seg_template->index) {
2041         indexURL =
2042             gst_mpdparser_build_URL_from_template (stream->cur_seg_template->
2043             index, stream->cur_representation->id,
2044             currentChunk->number + stream->segment_repeat_index,
2045             stream->cur_representation->bandwidth,
2046             currentChunk->scale_start +
2047             stream->segment_repeat_index * currentChunk->scale_duration);
2048       }
2049     }
2050     GST_DEBUG ("mediaURL = %s", mediaURL);
2051     GST_DEBUG ("indexURL = %s", indexURL);
2052
2053     fragment->timestamp =
2054         currentChunk->start +
2055         stream->segment_repeat_index * currentChunk->duration;
2056     fragment->duration = currentChunk->duration;
2057     if (currentChunk->SegmentURL) {
2058       if (currentChunk->SegmentURL->mediaRange) {
2059         fragment->range_start =
2060             currentChunk->SegmentURL->mediaRange->first_byte_pos;
2061         fragment->range_end =
2062             currentChunk->SegmentURL->mediaRange->last_byte_pos;
2063       }
2064       if (currentChunk->SegmentURL->indexRange) {
2065         fragment->index_range_start =
2066             currentChunk->SegmentURL->indexRange->first_byte_pos;
2067         fragment->index_range_end =
2068             currentChunk->SegmentURL->indexRange->last_byte_pos;
2069       }
2070     }
2071   } else {
2072     if (stream->cur_seg_template != NULL) {
2073       mediaURL =
2074           gst_mpdparser_build_URL_from_template (stream->cur_seg_template->
2075           media, stream->cur_representation->id,
2076           stream->segment_index +
2077           GST_MPD_MULT_SEGMENT_BASE_NODE (stream->
2078               cur_seg_template)->startNumber,
2079           stream->cur_representation->bandwidth,
2080           stream->segment_index * fragment->duration);
2081       if (stream->cur_seg_template->index) {
2082         indexURL =
2083             gst_mpdparser_build_URL_from_template (stream->cur_seg_template->
2084             index, stream->cur_representation->id,
2085             stream->segment_index +
2086             GST_MPD_MULT_SEGMENT_BASE_NODE (stream->
2087                 cur_seg_template)->startNumber,
2088             stream->cur_representation->bandwidth,
2089             stream->segment_index * fragment->duration);
2090       }
2091     } else {
2092       return FALSE;
2093     }
2094
2095     GST_DEBUG ("mediaURL = %s", mediaURL);
2096     GST_DEBUG ("indexURL = %s", indexURL);
2097
2098     fragment->timestamp = stream->segment_index * fragment->duration;
2099   }
2100
2101   base_url = gst_uri_from_string (stream->baseURL);
2102   frag_url = gst_uri_from_string_with_base (base_url, mediaURL);
2103   g_free (mediaURL);
2104   if (stream->queryURL) {
2105     frag_url = gst_uri_make_writable (frag_url);
2106     gst_uri_set_query_string (frag_url, stream->queryURL);
2107   }
2108   fragment->uri = gst_uri_to_string (frag_url);
2109   gst_uri_unref (frag_url);
2110
2111   if (indexURL != NULL) {
2112     frag_url = gst_uri_make_writable (gst_uri_from_string_with_base (base_url,
2113             indexURL));
2114     gst_uri_set_query_string (frag_url, stream->queryURL);
2115     fragment->index_uri = gst_uri_to_string (frag_url);
2116     gst_uri_unref (frag_url);
2117     g_free (indexURL);
2118   } else if (indexURL == NULL && (fragment->index_range_start
2119           || fragment->index_range_end != -1)) {
2120     /* index has no specific URL but has a range, we should only use this if
2121      * the media also has a range, otherwise we are serving some data twice
2122      * (in the media fragment and again in the index) */
2123     if (!(fragment->range_start || fragment->range_end != -1)) {
2124       GST_WARNING ("Ignoring index ranges because there isn't a media range "
2125           "and URIs would be the same");
2126       /* removing index information */
2127       fragment->index_range_start = 0;
2128       fragment->index_range_end = -1;
2129     }
2130   }
2131
2132   gst_uri_unref (base_url);
2133
2134   GST_DEBUG ("Loading chunk with URL %s", fragment->uri);
2135
2136   return TRUE;
2137 }
2138
2139 gboolean
2140 gst_mpd_client_has_next_segment (GstMPDClient * client,
2141     GstActiveStream * stream, gboolean forward)
2142 {
2143   if (forward) {
2144     guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
2145
2146     if (segments_count > 0 && stream->segments
2147         && stream->segment_index + 1 == segments_count) {
2148       GstMediaSegment *segment;
2149
2150       segment = g_ptr_array_index (stream->segments, stream->segment_index);
2151       if (segment->repeat >= 0
2152           && stream->segment_repeat_index >= segment->repeat)
2153         return FALSE;
2154     } else if (segments_count > 0
2155         && stream->segment_index + 1 >= segments_count) {
2156       return FALSE;
2157     }
2158   } else {
2159     if (stream->segment_index < 0)
2160       return FALSE;
2161   }
2162
2163   return TRUE;
2164 }
2165
2166 GstFlowReturn
2167 gst_mpd_client_advance_segment (GstMPDClient * client, GstActiveStream * stream,
2168     gboolean forward)
2169 {
2170   GstMediaSegment *segment;
2171   GstFlowReturn ret = GST_FLOW_OK;
2172   guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
2173
2174   GST_DEBUG ("Advancing segment. Current: %d / %d r:%d", stream->segment_index,
2175       segments_count, stream->segment_repeat_index);
2176
2177   /* handle special cases first */
2178   if (forward) {
2179     if (segments_count > 0 && stream->segment_index >= segments_count) {
2180       ret = GST_FLOW_EOS;
2181       goto done;
2182     }
2183
2184     if (stream->segments == NULL) {
2185       if (stream->segment_index < 0) {
2186         stream->segment_index = 0;
2187       } else {
2188         stream->segment_index++;
2189         if (segments_count > 0 && stream->segment_index >= segments_count) {
2190           ret = GST_FLOW_EOS;
2191         }
2192       }
2193       goto done;
2194     }
2195
2196     /* special case for when playback direction is reverted right at *
2197      * the end of the segment list */
2198     if (stream->segment_index < 0) {
2199       stream->segment_index = 0;
2200       goto done;
2201     }
2202   } else {
2203     if (stream->segments == NULL)
2204       stream->segment_index--;
2205     if (stream->segment_index < 0) {
2206       stream->segment_index = -1;
2207       ret = GST_FLOW_EOS;
2208       goto done;
2209     }
2210     if (stream->segments == NULL)
2211       goto done;
2212
2213     /* special case for when playback direction is reverted right at *
2214      * the end of the segment list */
2215     if (stream->segment_index >= segments_count) {
2216       stream->segment_index = segments_count - 1;
2217       segment = g_ptr_array_index (stream->segments, stream->segment_index);
2218       if (segment->repeat >= 0) {
2219         stream->segment_repeat_index = segment->repeat;
2220       } else {
2221         GstClockTime start = segment->start;
2222         GstClockTime end =
2223             gst_mpd_client_get_segment_end_time (client, stream->segments,
2224             segment,
2225             stream->segment_index);
2226         stream->segment_repeat_index =
2227             (guint) (end - start) / segment->duration;
2228       }
2229       goto done;
2230     }
2231   }
2232
2233   /* for the normal cases we can get the segment safely here */
2234   segment = g_ptr_array_index (stream->segments, stream->segment_index);
2235   if (forward) {
2236     if (segment->repeat >= 0 && stream->segment_repeat_index >= segment->repeat) {
2237       stream->segment_repeat_index = 0;
2238       stream->segment_index++;
2239       if (segments_count > 0 && stream->segment_index >= segments_count) {
2240         ret = GST_FLOW_EOS;
2241         goto done;
2242       }
2243     } else {
2244       stream->segment_repeat_index++;
2245     }
2246   } else {
2247     if (stream->segment_repeat_index == 0) {
2248       stream->segment_index--;
2249       if (stream->segment_index < 0) {
2250         ret = GST_FLOW_EOS;
2251         goto done;
2252       }
2253
2254       segment = g_ptr_array_index (stream->segments, stream->segment_index);
2255       /* negative repeats only seem to make sense at the end of a list,
2256        * so this one will probably not be. Needs some sanity checking
2257        * when loading the XML data. */
2258       if (segment->repeat >= 0) {
2259         stream->segment_repeat_index = segment->repeat;
2260       } else {
2261         GstClockTime start = segment->start;
2262         GstClockTime end =
2263             gst_mpd_client_get_segment_end_time (client, stream->segments,
2264             segment,
2265             stream->segment_index);
2266         stream->segment_repeat_index =
2267             (guint) (end - start) / segment->duration;
2268       }
2269     } else {
2270       stream->segment_repeat_index--;
2271     }
2272   }
2273
2274 done:
2275   GST_DEBUG ("Advanced to segment: %d / %d r:%d (ret: %s)",
2276       stream->segment_index, segments_count,
2277       stream->segment_repeat_index, gst_flow_get_name (ret));
2278   return ret;
2279 }
2280
2281 gboolean
2282 gst_mpd_client_get_next_header (GstMPDClient * client, gchar ** uri,
2283     guint stream_idx, gint64 * range_start, gint64 * range_end)
2284 {
2285   GstActiveStream *stream;
2286   GstStreamPeriod *stream_period;
2287
2288   stream = gst_mpd_client_get_active_stream_by_index (client, stream_idx);
2289   g_return_val_if_fail (stream != NULL, FALSE);
2290   g_return_val_if_fail (stream->cur_representation != NULL, FALSE);
2291   stream_period = gst_mpd_client_get_stream_period (client);
2292   g_return_val_if_fail (stream_period != NULL, FALSE);
2293   g_return_val_if_fail (stream_period->period != NULL, FALSE);
2294
2295   *range_start = 0;
2296   *range_end = -1;
2297
2298   GST_DEBUG ("Looking for current representation header");
2299   *uri = NULL;
2300   if (stream->cur_segment_base) {
2301     if (stream->cur_segment_base->Initialization) {
2302       *uri =
2303           g_strdup (gst_mpdparser_get_initializationURL (stream,
2304               stream->cur_segment_base->Initialization));
2305       if (stream->cur_segment_base->Initialization->range) {
2306         *range_start =
2307             stream->cur_segment_base->Initialization->range->first_byte_pos;
2308         *range_end =
2309             stream->cur_segment_base->Initialization->range->last_byte_pos;
2310       }
2311     } else if (stream->cur_segment_base->indexRange) {
2312       *uri =
2313           g_strdup (gst_mpdparser_get_initializationURL (stream,
2314               stream->cur_segment_base->Initialization));
2315       *range_start = 0;
2316       *range_end = stream->cur_segment_base->indexRange->first_byte_pos - 1;
2317     }
2318   } else if (stream->cur_seg_template
2319       && stream->cur_seg_template->initialization) {
2320     *uri =
2321         gst_mpdparser_build_URL_from_template (stream->cur_seg_template->
2322         initialization, stream->cur_representation->id, 0,
2323         stream->cur_representation->bandwidth, 0);
2324   }
2325
2326   return *uri == NULL ? FALSE : TRUE;
2327 }
2328
2329 gboolean
2330 gst_mpd_client_get_next_header_index (GstMPDClient * client, gchar ** uri,
2331     guint stream_idx, gint64 * range_start, gint64 * range_end)
2332 {
2333   GstActiveStream *stream;
2334   GstStreamPeriod *stream_period;
2335
2336   stream = gst_mpd_client_get_active_stream_by_index (client, stream_idx);
2337   g_return_val_if_fail (stream != NULL, FALSE);
2338   g_return_val_if_fail (stream->cur_representation != NULL, FALSE);
2339   stream_period = gst_mpd_client_get_stream_period (client);
2340   g_return_val_if_fail (stream_period != NULL, FALSE);
2341   g_return_val_if_fail (stream_period->period != NULL, FALSE);
2342
2343   *range_start = 0;
2344   *range_end = -1;
2345
2346   GST_DEBUG ("Looking for current representation index");
2347   *uri = NULL;
2348   if (stream->cur_segment_base && stream->cur_segment_base->indexRange) {
2349     *uri =
2350         g_strdup (gst_mpdparser_get_initializationURL (stream,
2351             stream->cur_segment_base->RepresentationIndex));
2352     *range_start = stream->cur_segment_base->indexRange->first_byte_pos;
2353     *range_end = stream->cur_segment_base->indexRange->last_byte_pos;
2354   } else if (stream->cur_seg_template && stream->cur_seg_template->index) {
2355     *uri =
2356         gst_mpdparser_build_URL_from_template (stream->cur_seg_template->index,
2357         stream->cur_representation->id, 0,
2358         stream->cur_representation->bandwidth, 0);
2359   }
2360
2361   return *uri == NULL ? FALSE : TRUE;
2362 }
2363
2364 GstClockTime
2365 gst_mpd_client_get_next_fragment_duration (GstMPDClient * client,
2366     GstActiveStream * stream)
2367 {
2368   GstMediaSegment *media_segment = NULL;
2369   gint seg_idx;
2370
2371   g_return_val_if_fail (stream != NULL, 0);
2372
2373   seg_idx = stream->segment_index;
2374
2375   if (stream->segments) {
2376     if (seg_idx < stream->segments->len && seg_idx >= 0)
2377       media_segment = g_ptr_array_index (stream->segments, seg_idx);
2378
2379     return media_segment == NULL ? 0 : media_segment->duration;
2380   } else {
2381     GstClockTime duration =
2382         gst_mpd_client_get_segment_duration (client, stream, NULL);
2383     guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
2384
2385     g_return_val_if_fail (GST_MPD_MULT_SEGMENT_BASE_NODE
2386         (stream->cur_seg_template)->SegmentTimeline == NULL, 0);
2387
2388     if (!GST_CLOCK_TIME_IS_VALID (duration) || (segments_count > 0
2389             && seg_idx >= segments_count)) {
2390       return 0;
2391     }
2392     return duration;
2393   }
2394 }
2395
2396 GstClockTime
2397 gst_mpd_client_get_media_presentation_duration (GstMPDClient * client)
2398 {
2399   GstClockTime duration;
2400
2401   g_return_val_if_fail (client != NULL, GST_CLOCK_TIME_NONE);
2402
2403   if (client->mpd_root_node->mediaPresentationDuration != -1) {
2404     duration = client->mpd_root_node->mediaPresentationDuration * GST_MSECOND;
2405   } else {
2406     /* We can only get the duration for on-demand streams */
2407     duration = GST_CLOCK_TIME_NONE;
2408   }
2409
2410   return duration;
2411 }
2412
2413 gboolean
2414 gst_mpd_client_set_period_id (GstMPDClient * client, const gchar * period_id)
2415 {
2416   GstStreamPeriod *next_stream_period;
2417   gboolean ret = FALSE;
2418   GList *iter;
2419   guint period_idx;
2420
2421   g_return_val_if_fail (client != NULL, FALSE);
2422   g_return_val_if_fail (client->periods != NULL, FALSE);
2423   g_return_val_if_fail (period_id != NULL, FALSE);
2424
2425   if (!gst_mpd_client_setup_media_presentation (client, GST_CLOCK_TIME_NONE, -1,
2426           period_id))
2427     return FALSE;
2428
2429   for (period_idx = 0, iter = client->periods; iter;
2430       period_idx++, iter = g_list_next (iter)) {
2431     next_stream_period = iter->data;
2432
2433     if (next_stream_period->period->id
2434         && strcmp (next_stream_period->period->id, period_id) == 0) {
2435       ret = TRUE;
2436       client->period_idx = period_idx;
2437       break;
2438     }
2439   }
2440
2441   return ret;
2442 }
2443
2444 gboolean
2445 gst_mpd_client_set_period_index (GstMPDClient * client, guint period_idx)
2446 {
2447   GstStreamPeriod *next_stream_period;
2448   gboolean ret = FALSE;
2449
2450   g_return_val_if_fail (client != NULL, FALSE);
2451   g_return_val_if_fail (client->periods != NULL, FALSE);
2452
2453   if (!gst_mpd_client_setup_media_presentation (client, -1, period_idx, NULL))
2454     return FALSE;
2455
2456   next_stream_period = g_list_nth_data (client->periods, period_idx);
2457   if (next_stream_period != NULL) {
2458     client->period_idx = period_idx;
2459     ret = TRUE;
2460   }
2461
2462   return ret;
2463 }
2464
2465 guint
2466 gst_mpd_client_get_period_index (GstMPDClient * client)
2467 {
2468   guint period_idx;
2469
2470   g_return_val_if_fail (client != NULL, 0);
2471   period_idx = client->period_idx;
2472
2473   return period_idx;
2474 }
2475
2476 const gchar *
2477 gst_mpd_client_get_period_id (GstMPDClient * client)
2478 {
2479   GstStreamPeriod *period;
2480   gchar *period_id = NULL;
2481
2482   g_return_val_if_fail (client != NULL, 0);
2483   period = g_list_nth_data (client->periods, client->period_idx);
2484   if (period && period->period)
2485     period_id = period->period->id;
2486
2487   return period_id;
2488 }
2489
2490 gboolean
2491 gst_mpd_client_has_next_period (GstMPDClient * client)
2492 {
2493   GList *next_stream_period;
2494   g_return_val_if_fail (client != NULL, FALSE);
2495   g_return_val_if_fail (client->periods != NULL, FALSE);
2496
2497   if (!gst_mpd_client_setup_media_presentation (client, GST_CLOCK_TIME_NONE,
2498           client->period_idx + 1, NULL))
2499     return FALSE;
2500
2501   next_stream_period =
2502       g_list_nth_data (client->periods, client->period_idx + 1);
2503   return next_stream_period != NULL;
2504 }
2505
2506 gboolean
2507 gst_mpd_client_has_previous_period (GstMPDClient * client)
2508 {
2509   GList *next_stream_period;
2510   g_return_val_if_fail (client != NULL, FALSE);
2511   g_return_val_if_fail (client->periods != NULL, FALSE);
2512
2513   if (!gst_mpd_client_setup_media_presentation (client, GST_CLOCK_TIME_NONE,
2514           client->period_idx - 1, NULL))
2515     return FALSE;
2516
2517   next_stream_period =
2518       g_list_nth_data (client->periods, client->period_idx - 1);
2519
2520   return next_stream_period != NULL;
2521 }
2522
2523 gint
2524 gst_mpd_client_get_rep_idx_with_min_bandwidth (GList * Representations)
2525 {
2526   GList *list = NULL, *lowest = NULL;
2527   GstMPDRepresentationNode *rep = NULL;
2528   gint lowest_bandwidth = -1;
2529
2530   if (Representations == NULL)
2531     return -1;
2532
2533   for (list = g_list_first (Representations); list; list = g_list_next (list)) {
2534     rep = (GstMPDRepresentationNode *) list->data;
2535     if (rep && (!lowest || rep->bandwidth < lowest_bandwidth)) {
2536       lowest = list;
2537       lowest_bandwidth = rep->bandwidth;
2538     }
2539   }
2540
2541   return lowest ? g_list_position (Representations, lowest) : -1;
2542 }
2543
2544 gint
2545 gst_mpd_client_get_rep_idx_with_max_bandwidth (GList * Representations,
2546     gint64 max_bandwidth, gint max_video_width, gint max_video_height, gint
2547     max_video_framerate_n, gint max_video_framerate_d)
2548 {
2549   GList *list = NULL, *best = NULL;
2550   GstMPDRepresentationNode *representation;
2551   gint best_bandwidth = 0;
2552
2553   GST_DEBUG ("max_bandwidth = %" G_GINT64_FORMAT, max_bandwidth);
2554
2555   if (Representations == NULL)
2556     return -1;
2557
2558   if (max_bandwidth <= 0)       /* 0 => get lowest representation available */
2559     return gst_mpd_client_get_rep_idx_with_min_bandwidth (Representations);
2560
2561   for (list = g_list_first (Representations); list; list = g_list_next (list)) {
2562     GstXMLFrameRate *framerate = NULL;
2563
2564     representation = (GstMPDRepresentationNode *) list->data;
2565
2566     /* FIXME: Really? */
2567     if (!representation)
2568       continue;
2569
2570     framerate = GST_MPD_REPRESENTATION_BASE_NODE (representation)->frameRate;
2571     if (!framerate)
2572       framerate =
2573           GST_MPD_REPRESENTATION_BASE_NODE (representation)->maxFrameRate;
2574
2575     if (framerate && max_video_framerate_n > 0) {
2576       if (gst_util_fraction_compare (framerate->num, framerate->den,
2577               max_video_framerate_n, max_video_framerate_d) > 0)
2578         continue;
2579     }
2580
2581     if (max_video_width > 0
2582         && GST_MPD_REPRESENTATION_BASE_NODE (representation)->width >
2583         max_video_width)
2584       continue;
2585     if (max_video_height > 0
2586         && GST_MPD_REPRESENTATION_BASE_NODE (representation)->height >
2587         max_video_height)
2588       continue;
2589
2590     if (representation->bandwidth <= max_bandwidth &&
2591         representation->bandwidth > best_bandwidth) {
2592       best = list;
2593       best_bandwidth = representation->bandwidth;
2594     }
2595   }
2596
2597   return best ? g_list_position (Representations, best) : -1;
2598 }
2599
2600 void
2601 gst_mpd_client_seek_to_first_segment (GstMPDClient * client)
2602 {
2603   GList *list;
2604
2605   g_return_if_fail (client != NULL);
2606   g_return_if_fail (client->active_streams != NULL);
2607
2608   for (list = g_list_first (client->active_streams); list;
2609       list = g_list_next (list)) {
2610     GstActiveStream *stream = (GstActiveStream *) list->data;
2611     if (stream) {
2612       stream->segment_index = 0;
2613       stream->segment_repeat_index = 0;
2614     }
2615   }
2616 }
2617
2618 static guint
2619 gst_mpd_client_get_segments_counts (GstMPDClient * client,
2620     GstActiveStream * stream)
2621 {
2622   GstStreamPeriod *stream_period;
2623
2624   g_return_val_if_fail (stream != NULL, 0);
2625
2626   if (stream->segments)
2627     return stream->segments->len;
2628   g_return_val_if_fail (GST_MPD_MULT_SEGMENT_BASE_NODE
2629       (stream->cur_seg_template)->SegmentTimeline == NULL, 0);
2630
2631   stream_period = gst_mpd_client_get_stream_period (client);
2632   if (stream_period->duration != -1)
2633     return gst_util_uint64_scale_ceil (stream_period->duration, 1,
2634         gst_mpd_client_get_segment_duration (client, stream, NULL));
2635
2636   return 0;
2637 }
2638
2639 gboolean
2640 gst_mpd_client_is_live (GstMPDClient * client)
2641 {
2642   g_return_val_if_fail (client != NULL, FALSE);
2643   g_return_val_if_fail (client->mpd_root_node != NULL, FALSE);
2644
2645   return client->mpd_root_node->type == GST_MPD_FILE_TYPE_DYNAMIC;
2646 }
2647
2648 guint
2649 gst_mpd_client_get_nb_active_stream (GstMPDClient * client)
2650 {
2651   g_return_val_if_fail (client != NULL, 0);
2652
2653   return g_list_length (client->active_streams);
2654 }
2655
2656 guint
2657 gst_mpd_client_get_nb_adaptationSet (GstMPDClient * client)
2658 {
2659   GstStreamPeriod *stream_period;
2660
2661   stream_period = gst_mpd_client_get_stream_period (client);
2662   g_return_val_if_fail (stream_period != NULL, 0);
2663   g_return_val_if_fail (stream_period->period != NULL, 0);
2664
2665   return g_list_length (stream_period->period->AdaptationSets);
2666 }
2667
2668 GstActiveStream *
2669 gst_mpd_client_get_active_stream_by_index (GstMPDClient * client,
2670     guint stream_idx)
2671 {
2672   g_return_val_if_fail (client != NULL, NULL);
2673   g_return_val_if_fail (client->active_streams != NULL, NULL);
2674
2675   return g_list_nth_data (client->active_streams, stream_idx);
2676 }
2677
2678 gboolean
2679 gst_mpd_client_active_stream_contains_subtitles (GstActiveStream * stream)
2680 {
2681   const gchar *mimeType;
2682   const gchar *adapt_set_codecs;
2683   const gchar *rep_codecs;
2684
2685   mimeType =
2686       GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_representation)->mimeType;
2687   if (!mimeType)
2688     mimeType =
2689         GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->mimeType;
2690
2691   if (g_strcmp0 (mimeType, "application/ttml+xml") == 0 ||
2692       g_strcmp0 (mimeType, "text/vtt") == 0)
2693     return TRUE;
2694
2695   adapt_set_codecs =
2696       GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->codecs;
2697   rep_codecs =
2698       GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_representation)->codecs;
2699
2700   return (adapt_set_codecs && g_str_has_prefix (adapt_set_codecs, "stpp"))
2701       || (rep_codecs && g_str_has_prefix (rep_codecs, "stpp"));
2702 }
2703
2704 GstCaps *
2705 gst_mpd_client_get_stream_caps (GstActiveStream * stream)
2706 {
2707   const gchar *mimeType, *caps_string;
2708   GstCaps *ret = NULL;
2709
2710   if (stream == NULL || stream->cur_adapt_set == NULL
2711       || stream->cur_representation == NULL)
2712     return NULL;
2713
2714   mimeType =
2715       GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_representation)->mimeType;
2716   if (mimeType == NULL) {
2717     mimeType =
2718         GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->mimeType;
2719   }
2720
2721   caps_string = gst_mpd_helper_mimetype_to_caps (mimeType);
2722
2723   if ((g_strcmp0 (caps_string, "application/mp4") == 0)
2724       && gst_mpd_client_active_stream_contains_subtitles (stream))
2725     caps_string = "video/quicktime";
2726
2727   if (caps_string)
2728     ret = gst_caps_from_string (caps_string);
2729
2730   return ret;
2731 }
2732
2733 gboolean
2734 gst_mpd_client_get_bitstream_switching_flag (GstActiveStream * stream)
2735 {
2736   if (stream == NULL || stream->cur_adapt_set == NULL)
2737     return FALSE;
2738
2739   return stream->cur_adapt_set->bitstreamSwitching;
2740 }
2741
2742 guint
2743 gst_mpd_client_get_video_stream_width (GstActiveStream * stream)
2744 {
2745   guint width;
2746
2747   if (stream == NULL || stream->cur_adapt_set == NULL
2748       || stream->cur_representation == NULL)
2749     return 0;
2750
2751   width = GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_representation)->width;
2752   if (width == 0) {
2753     width = GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->width;
2754   }
2755
2756   return width;
2757 }
2758
2759 guint
2760 gst_mpd_client_get_video_stream_height (GstActiveStream * stream)
2761 {
2762   guint height;
2763
2764   if (stream == NULL || stream->cur_adapt_set == NULL
2765       || stream->cur_representation == NULL)
2766     return 0;
2767
2768   height =
2769       GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_representation)->height;
2770   if (height == 0) {
2771     height = GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->height;
2772   }
2773
2774   return height;
2775 }
2776
2777 gboolean
2778 gst_mpd_client_get_video_stream_framerate (GstActiveStream * stream,
2779     gint * fps_num, gint * fps_den)
2780 {
2781   if (stream == NULL)
2782     return FALSE;
2783
2784   if (stream->cur_adapt_set &&
2785       GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->frameRate !=
2786       NULL) {
2787     *fps_num =
2788         GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->
2789         frameRate->num;
2790     *fps_den =
2791         GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->
2792         frameRate->den;
2793     return TRUE;
2794   }
2795
2796   if (stream->cur_adapt_set &&
2797       GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->maxFrameRate !=
2798       NULL) {
2799     *fps_num =
2800         GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->
2801         maxFrameRate->num;
2802     *fps_den =
2803         GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->
2804         maxFrameRate->den;
2805     return TRUE;
2806   }
2807
2808   if (stream->cur_representation &&
2809       GST_MPD_REPRESENTATION_BASE_NODE (stream->
2810           cur_representation)->frameRate != NULL) {
2811     *fps_num =
2812         GST_MPD_REPRESENTATION_BASE_NODE (stream->
2813         cur_representation)->frameRate->num;
2814     *fps_den =
2815         GST_MPD_REPRESENTATION_BASE_NODE (stream->
2816         cur_representation)->frameRate->den;
2817     return TRUE;
2818   }
2819
2820   if (stream->cur_representation &&
2821       GST_MPD_REPRESENTATION_BASE_NODE (stream->
2822           cur_representation)->maxFrameRate != NULL) {
2823     *fps_num =
2824         GST_MPD_REPRESENTATION_BASE_NODE (stream->
2825         cur_representation)->maxFrameRate->num;
2826     *fps_den =
2827         GST_MPD_REPRESENTATION_BASE_NODE (stream->
2828         cur_representation)->maxFrameRate->den;
2829     return TRUE;
2830   }
2831
2832   return FALSE;
2833 }
2834
2835 guint
2836 gst_mpd_client_get_audio_stream_rate (GstActiveStream * stream)
2837 {
2838   const gchar *rate;
2839
2840   if (stream == NULL || stream->cur_adapt_set == NULL
2841       || stream->cur_representation == NULL)
2842     return 0;
2843
2844   rate =
2845       GST_MPD_REPRESENTATION_BASE_NODE (stream->
2846       cur_representation)->audioSamplingRate;
2847   if (rate == NULL) {
2848     rate =
2849         GST_MPD_REPRESENTATION_BASE_NODE (stream->
2850         cur_adapt_set)->audioSamplingRate;
2851   }
2852
2853   return rate ? atoi (rate) : 0;
2854 }
2855
2856 guint
2857 gst_mpd_client_get_audio_stream_num_channels (GstActiveStream * stream)
2858 {
2859   if (stream == NULL || stream->cur_adapt_set == NULL
2860       || stream->cur_representation == NULL)
2861     return 0;
2862   /* TODO: here we have to parse the AudioChannelConfiguration descriptors */
2863   return 0;
2864 }
2865
2866 guint
2867 gst_mpd_client_get_list_and_nb_of_audio_language (GstMPDClient * client,
2868     GList ** lang)
2869 {
2870   GstStreamPeriod *stream_period;
2871   GstMPDAdaptationSetNode *adapt_set;
2872   GList *adaptation_sets, *list;
2873   const gchar *this_mimeType = "audio";
2874   gchar *mimeType = NULL;
2875   guint nb_adaptation_set = 0;
2876
2877   stream_period = gst_mpd_client_get_stream_period (client);
2878   g_return_val_if_fail (stream_period != NULL, 0);
2879   g_return_val_if_fail (stream_period->period != NULL, 0);
2880
2881   adaptation_sets =
2882       gst_mpd_client_get_adaptation_sets_for_period (client, stream_period);
2883   for (list = adaptation_sets; list; list = g_list_next (list)) {
2884     adapt_set = (GstMPDAdaptationSetNode *) list->data;
2885     if (adapt_set && adapt_set->lang) {
2886       gchar *this_lang = adapt_set->lang;
2887       GstMPDRepresentationNode *rep;
2888       rep =
2889           gst_mpd_client_get_lowest_representation (adapt_set->Representations);
2890       mimeType = NULL;
2891       if (GST_MPD_REPRESENTATION_BASE_NODE (rep))
2892         mimeType = GST_MPD_REPRESENTATION_BASE_NODE (rep)->mimeType;
2893       if (!mimeType && GST_MPD_REPRESENTATION_BASE_NODE (adapt_set)) {
2894         mimeType = GST_MPD_REPRESENTATION_BASE_NODE (adapt_set)->mimeType;
2895       }
2896
2897       if (gst_mpd_helper_strncmp_ext (mimeType, this_mimeType) == 0) {
2898         nb_adaptation_set++;
2899         *lang = g_list_append (*lang, this_lang);
2900       }
2901     }
2902   }
2903
2904   return nb_adaptation_set;
2905 }
2906
2907
2908 GstDateTime *
2909 gst_mpd_client_get_next_segment_availability_start_time (GstMPDClient * client,
2910     GstActiveStream * stream)
2911 {
2912   GstDateTime *availability_start_time, *rv;
2913   gint seg_idx;
2914   GstMediaSegment *segment;
2915   GstClockTime segmentEndTime;
2916   const GstStreamPeriod *stream_period;
2917   GstClockTime period_start = 0;
2918
2919   g_return_val_if_fail (client != NULL, NULL);
2920   g_return_val_if_fail (stream != NULL, NULL);
2921
2922   stream_period = gst_mpd_client_get_stream_period (client);
2923   if (stream_period && stream_period->period) {
2924     period_start = stream_period->start;
2925   }
2926
2927   seg_idx = stream->segment_index;
2928
2929   if (stream->segments) {
2930     segment = g_ptr_array_index (stream->segments, seg_idx);
2931
2932     if (segment->repeat >= 0) {
2933       segmentEndTime = segment->start + (stream->segment_repeat_index + 1) *
2934           segment->duration;
2935     } else if (seg_idx < stream->segments->len - 1) {
2936       const GstMediaSegment *next_segment =
2937           g_ptr_array_index (stream->segments, seg_idx + 1);
2938       segmentEndTime = next_segment->start;
2939     } else {
2940       g_return_val_if_fail (stream_period != NULL, NULL);
2941       segmentEndTime = period_start + stream_period->duration;
2942     }
2943   } else {
2944     GstClockTime seg_duration;
2945     seg_duration = gst_mpd_client_get_segment_duration (client, stream, NULL);
2946     if (seg_duration == 0)
2947       return NULL;
2948     segmentEndTime = period_start + (1 + seg_idx) * seg_duration;
2949   }
2950
2951   availability_start_time = gst_mpd_client_get_availability_start_time (client);
2952   if (availability_start_time == NULL) {
2953     GST_WARNING_OBJECT (client, "Failed to get availability_start_time");
2954     return NULL;
2955   }
2956
2957   rv = gst_mpd_client_add_time_difference (availability_start_time,
2958       segmentEndTime / GST_USECOND);
2959   gst_date_time_unref (availability_start_time);
2960   if (rv == NULL) {
2961     GST_WARNING_OBJECT (client, "Failed to offset availability_start_time");
2962     return NULL;
2963   }
2964
2965   return rv;
2966 }
2967
2968 gboolean
2969 gst_mpd_client_seek_to_time (GstMPDClient * client, GDateTime * time)
2970 {
2971   GDateTime *start;
2972   GTimeSpan ts_microseconds;
2973   GstClockTime ts;
2974   gboolean ret = TRUE;
2975   GList *stream;
2976
2977   g_return_val_if_fail (gst_mpd_client_is_live (client), FALSE);
2978   g_return_val_if_fail (client->mpd_root_node->availabilityStartTime != NULL,
2979       FALSE);
2980
2981   start =
2982       gst_date_time_to_g_date_time (client->mpd_root_node->
2983       availabilityStartTime);
2984
2985   ts_microseconds = g_date_time_difference (time, start);
2986   g_date_time_unref (start);
2987
2988   /* Clamp to availability start time, otherwise calculations wrap around */
2989   if (ts_microseconds < 0)
2990     ts_microseconds = 0;
2991
2992   ts = ts_microseconds * GST_USECOND;
2993   for (stream = client->active_streams; stream; stream = g_list_next (stream)) {
2994     ret =
2995         ret & gst_mpd_client_stream_seek (client, stream->data, TRUE, 0, ts,
2996         NULL);
2997   }
2998   return ret;
2999 }
3000
3001 gboolean
3002 gst_mpd_client_has_isoff_ondemand_profile (GstMPDClient * client)
3003 {
3004   return client->profile_isoff_ondemand;
3005 }
3006
3007 /**
3008  * gst_mpd_client_parse_default_presentation_delay:
3009  * @client: #GstMPDClient that has a parsed manifest
3010  * @default_presentation_delay: A string that specifies a time period
3011  * in fragments (e.g. "5 f"), seconds ("12 s") or milliseconds
3012  * ("12000 ms")
3013  * Returns: the parsed string in milliseconds
3014  *
3015  * Since: 1.6
3016  */
3017 gint64
3018 gst_mpd_client_parse_default_presentation_delay (GstMPDClient * client,
3019     const gchar * default_presentation_delay)
3020 {
3021   gint64 value;
3022   char *endptr = NULL;
3023
3024   g_return_val_if_fail (client != NULL, 0);
3025   g_return_val_if_fail (default_presentation_delay != NULL, 0);
3026   value = strtol (default_presentation_delay, &endptr, 10);
3027   if (endptr == default_presentation_delay || value == 0) {
3028     return 0;
3029   }
3030   while (*endptr == ' ')
3031     endptr++;
3032   if (*endptr == 's' || *endptr == 'S') {
3033     value *= 1000;              /* convert to ms */
3034   } else if (*endptr == 'f' || *endptr == 'F') {
3035     gint64 segment_duration;
3036     g_assert (client->mpd_root_node != NULL);
3037     segment_duration = client->mpd_root_node->maxSegmentDuration;
3038     value *= segment_duration;
3039   } else if (*endptr != 'm' && *endptr != 'M') {
3040     GST_ERROR ("Unable to parse default presentation delay: %s",
3041         default_presentation_delay);
3042     value = 0;
3043   }
3044   return value;
3045 }
3046
3047 GstClockTime
3048 gst_mpd_client_get_maximum_segment_duration (GstMPDClient * client)
3049 {
3050   GstClockTime ret = GST_CLOCK_TIME_NONE, dur;
3051   GList *stream;
3052
3053   g_return_val_if_fail (client != NULL, GST_CLOCK_TIME_NONE);
3054   g_return_val_if_fail (client->mpd_root_node != NULL, GST_CLOCK_TIME_NONE);
3055
3056   if (client->mpd_root_node->maxSegmentDuration != GST_MPD_DURATION_NONE) {
3057     return client->mpd_root_node->maxSegmentDuration * GST_MSECOND;
3058   }
3059
3060   /* According to the DASH specification, if maxSegmentDuration is not present:
3061      "If not present, then the maximum Segment duration shall be the maximum
3062      duration of any Segment documented in this MPD"
3063    */
3064   for (stream = client->active_streams; stream; stream = g_list_next (stream)) {
3065     dur = gst_mpd_client_get_segment_duration (client, stream->data, NULL);
3066     if (dur != GST_CLOCK_TIME_NONE && (dur > ret || ret == GST_CLOCK_TIME_NONE)) {
3067       ret = dur;
3068     }
3069   }
3070   return ret;
3071 }
3072
3073 guint
3074 gst_mpd_client_get_period_index_at_time (GstMPDClient * client,
3075     GstDateTime * time)
3076 {
3077   GList *iter;
3078   guint period_idx = G_MAXUINT;
3079   guint idx;
3080   gint64 time_offset;
3081   GstDateTime *avail_start =
3082       gst_mpd_client_get_availability_start_time (client);
3083   GstStreamPeriod *stream_period;
3084
3085   if (avail_start == NULL)
3086     return 0;
3087
3088   time_offset = gst_mpd_client_calculate_time_difference (avail_start, time);
3089   gst_date_time_unref (avail_start);
3090
3091   if (time_offset < 0)
3092     return 0;
3093
3094   if (!gst_mpd_client_setup_media_presentation (client, time_offset, -1, NULL))
3095     return 0;
3096
3097   for (idx = 0, iter = client->periods; iter; idx++, iter = g_list_next (iter)) {
3098     stream_period = iter->data;
3099     if (stream_period->start <= time_offset
3100         && (!GST_CLOCK_TIME_IS_VALID (stream_period->duration)
3101             || stream_period->start + stream_period->duration > time_offset)) {
3102       period_idx = idx;
3103       break;
3104     }
3105   }
3106
3107   return period_idx;
3108 }
3109
3110 /* add or set node methods */
3111
3112 gboolean
3113 gst_mpd_client_set_root_node (GstMPDClient * client,
3114     const gchar * property_name, ...)
3115 {
3116   va_list myargs;
3117   g_return_val_if_fail (client != NULL, FALSE);
3118
3119   if (!client->mpd_root_node)
3120     client->mpd_root_node = gst_mpd_root_node_new ();
3121
3122   va_start (myargs, property_name);
3123   g_object_set_valist (G_OBJECT (client->mpd_root_node), property_name, myargs);
3124   va_end (myargs);
3125
3126   return TRUE;
3127 }
3128
3129 gboolean
3130 gst_mpd_client_add_baseurl_node (GstMPDClient * client,
3131     const gchar * property_name, ...)
3132 {
3133   GstMPDBaseURLNode *baseurl_node = NULL;
3134   va_list myargs;
3135
3136   g_return_val_if_fail (client != NULL, FALSE);
3137   g_return_val_if_fail (client->mpd_root_node != NULL, FALSE);
3138
3139   va_start (myargs, property_name);
3140
3141   baseurl_node = gst_mpd_baseurl_node_new ();
3142   g_object_set_valist (G_OBJECT (baseurl_node), property_name, myargs);
3143   client->mpd_root_node->BaseURLs =
3144       g_list_append (client->mpd_root_node->BaseURLs, baseurl_node);
3145
3146   va_end (myargs);
3147   return TRUE;
3148 }
3149
3150 /* returns a period id */
3151 gchar *
3152 gst_mpd_client_set_period_node (GstMPDClient * client,
3153     gchar * period_id, const gchar * property_name, ...)
3154 {
3155   GstMPDPeriodNode *period_node = NULL;
3156   va_list myargs;
3157
3158   g_return_val_if_fail (client != NULL, NULL);
3159   g_return_val_if_fail (client->mpd_root_node != NULL, NULL);
3160
3161   period_node =
3162       GST_MPD_PERIOD_NODE (gst_mpd_client_get_period_with_id
3163       (client->mpd_root_node->Periods, period_id));
3164   if (!period_node) {
3165     period_node = gst_mpd_period_node_new ();
3166     if (period_id)
3167       period_node->id = g_strdup (period_id);
3168     else
3169       period_node->id =
3170           _generate_new_string_id (client->mpd_root_node->Periods,
3171           "period_%.2d", gst_mpd_client_get_period_with_id);
3172     client->mpd_root_node->Periods =
3173         g_list_append (client->mpd_root_node->Periods, period_node);
3174   }
3175
3176   va_start (myargs, property_name);
3177   g_object_set_valist (G_OBJECT (period_node), property_name, myargs);
3178   va_end (myargs);
3179
3180   return period_node->id;
3181 }
3182
3183 /* returns an adaptation set id */
3184 guint
3185 gst_mpd_client_set_adaptation_set_node (GstMPDClient * client,
3186     gchar * period_id, guint adaptation_set_id, const gchar * property_name,
3187     ...)
3188 {
3189   GstMPDAdaptationSetNode *adap_node = NULL;
3190   GstMPDPeriodNode *period_node = NULL;
3191   va_list myargs;
3192
3193   g_return_val_if_fail (client != NULL, 0);
3194   g_return_val_if_fail (client->mpd_root_node != NULL, 0);
3195
3196   period_node =
3197       GST_MPD_PERIOD_NODE (gst_mpd_client_get_period_with_id
3198       (client->mpd_root_node->Periods, period_id));
3199   g_return_val_if_fail (period_node != NULL, 0);
3200   adap_node =
3201       GST_MPD_ADAPTATION_SET_NODE (gst_mpd_client_get_adaptation_set_with_id
3202       (period_node->AdaptationSets, adaptation_set_id));
3203   if (!adap_node) {
3204     adap_node = gst_mpd_adaptation_set_node_new ();
3205     if (adaptation_set_id)
3206       adap_node->id = adaptation_set_id;
3207     else
3208       adap_node->id =
3209           _generate_new_id (period_node->AdaptationSets,
3210           gst_mpd_client_get_adaptation_set_with_id);
3211     GST_DEBUG_OBJECT (client, "Add a new adaptation set with id %d",
3212         adap_node->id);
3213     period_node->AdaptationSets =
3214         g_list_append (period_node->AdaptationSets, adap_node);
3215   }
3216
3217   va_start (myargs, property_name);
3218   g_object_set_valist (G_OBJECT (adap_node), property_name, myargs);
3219   va_end (myargs);
3220
3221   return adap_node->id;
3222 }
3223
3224 /* returns a representation id */
3225 gchar *
3226 gst_mpd_client_set_representation_node (GstMPDClient * client,
3227     gchar * period_id, guint adaptation_set_id, gchar * representation_id,
3228     const gchar * property_name, ...)
3229 {
3230   GstMPDRepresentationNode *rep_node = NULL;
3231   GstMPDAdaptationSetNode *adap_set_node = NULL;
3232   GstMPDPeriodNode *period_node = NULL;
3233   va_list myargs;
3234
3235   g_return_val_if_fail (client != NULL, NULL);
3236   g_return_val_if_fail (client->mpd_root_node != NULL, NULL);
3237
3238   period_node =
3239       GST_MPD_PERIOD_NODE (gst_mpd_client_get_period_with_id
3240       (client->mpd_root_node->Periods, period_id));
3241   adap_set_node =
3242       GST_MPD_ADAPTATION_SET_NODE (gst_mpd_client_get_adaptation_set_with_id
3243       (period_node->AdaptationSets, adaptation_set_id));
3244   g_return_val_if_fail (adap_set_node != NULL, NULL);
3245   rep_node =
3246       GST_MPD_REPRESENTATION_NODE (gst_mpd_client_get_representation_with_id
3247       (adap_set_node->Representations, representation_id));
3248   if (!rep_node) {
3249     rep_node = gst_mpd_representation_node_new ();
3250     if (representation_id)
3251       rep_node->id = g_strdup (representation_id);
3252     else
3253       rep_node->id =
3254           _generate_new_string_id (adap_set_node->Representations,
3255           "representation_%.2d", gst_mpd_client_get_representation_with_id);
3256     GST_DEBUG_OBJECT (client, "Add a new representation with id %s",
3257         rep_node->id);
3258     adap_set_node->Representations =
3259         g_list_append (adap_set_node->Representations, rep_node);
3260   }
3261
3262   va_start (myargs, property_name);
3263   g_object_set_valist (G_OBJECT (rep_node), property_name, myargs);
3264   va_end (myargs);
3265
3266   return rep_node->id;
3267 }
3268
3269 /* add/set a segment list node */
3270 gboolean
3271 gst_mpd_client_set_segment_list (GstMPDClient * client,
3272     gchar * period_id, guint adap_set_id, gchar * rep_id,
3273     const gchar * property_name, ...)
3274 {
3275   GstMPDRepresentationNode *representation = NULL;
3276   GstMPDAdaptationSetNode *adaptation_set = NULL;
3277   GstMPDPeriodNode *period = NULL;
3278   va_list myargs;
3279
3280   g_return_val_if_fail (client != NULL, FALSE);
3281   g_return_val_if_fail (client->mpd_root_node != NULL, FALSE);
3282
3283   period =
3284       GST_MPD_PERIOD_NODE (gst_mpd_client_get_period_with_id
3285       (client->mpd_root_node->Periods, period_id));
3286   adaptation_set =
3287       GST_MPD_ADAPTATION_SET_NODE (gst_mpd_client_get_adaptation_set_with_id
3288       (period->AdaptationSets, adap_set_id));
3289   g_return_val_if_fail (adaptation_set != NULL, FALSE);
3290
3291   representation =
3292       GST_MPD_REPRESENTATION_NODE (gst_mpd_client_get_representation_with_id
3293       (adaptation_set->Representations, rep_id));
3294   if (!representation->SegmentList) {
3295     representation->SegmentList = gst_mpd_segment_list_node_new ();
3296   }
3297
3298   va_start (myargs, property_name);
3299   g_object_set_valist (G_OBJECT (representation->SegmentList), property_name,
3300       myargs);
3301   va_end (myargs);
3302
3303   return TRUE;
3304 }
3305
3306 /* add/set a segment template node */
3307 gboolean
3308 gst_mpd_client_set_segment_template (GstMPDClient * client,
3309     gchar * period_id, guint adap_set_id, gchar * rep_id,
3310     const gchar * property_name, ...)
3311 {
3312   GstMPDRepresentationNode *representation = NULL;
3313   GstMPDAdaptationSetNode *adaptation_set = NULL;
3314   GstMPDPeriodNode *period = NULL;
3315   va_list myargs;
3316
3317   g_return_val_if_fail (client != NULL, FALSE);
3318   g_return_val_if_fail (client->mpd_root_node != NULL, FALSE);
3319
3320   period =
3321       GST_MPD_PERIOD_NODE (gst_mpd_client_get_period_with_id
3322       (client->mpd_root_node->Periods, period_id));
3323   adaptation_set =
3324       GST_MPD_ADAPTATION_SET_NODE (gst_mpd_client_get_adaptation_set_with_id
3325       (period->AdaptationSets, adap_set_id));
3326   g_return_val_if_fail (adaptation_set != NULL, FALSE);
3327
3328   representation =
3329       GST_MPD_REPRESENTATION_NODE (gst_mpd_client_get_representation_with_id
3330       (adaptation_set->Representations, rep_id));
3331   if (!representation->SegmentTemplate) {
3332     representation->SegmentTemplate = gst_mpd_segment_template_node_new ();
3333   }
3334
3335   va_start (myargs, property_name);
3336   g_object_set_valist (G_OBJECT (representation->SegmentTemplate),
3337       property_name, myargs);
3338   va_end (myargs);
3339
3340   return TRUE;
3341 }
3342
3343 /* add a segmentURL node with to a SegmentList node */
3344 gboolean
3345 gst_mpd_client_add_segment_url (GstMPDClient * client,
3346     gchar * period_id, guint adap_set_id, gchar * rep_id,
3347     const gchar * property_name, ...)
3348 {
3349   GstMPDRepresentationNode *representation = NULL;
3350   GstMPDAdaptationSetNode *adaptation_set = NULL;
3351   GstMPDPeriodNode *period = NULL;
3352   GstMPDSegmentURLNode *segment_url = NULL;
3353   guint64 media_presentation_duration = 0;
3354   va_list myargs;
3355
3356   g_return_val_if_fail (client != NULL, FALSE);
3357   g_return_val_if_fail (client->mpd_root_node != NULL, FALSE);
3358
3359   period =
3360       GST_MPD_PERIOD_NODE (gst_mpd_client_get_period_with_id
3361       (client->mpd_root_node->Periods, period_id));
3362   adaptation_set =
3363       GST_MPD_ADAPTATION_SET_NODE (gst_mpd_client_get_adaptation_set_with_id
3364       (period->AdaptationSets, adap_set_id));
3365   g_return_val_if_fail (adaptation_set != NULL, FALSE);
3366
3367   representation =
3368       GST_MPD_REPRESENTATION_NODE (gst_mpd_client_get_representation_with_id
3369       (adaptation_set->Representations, rep_id));
3370
3371   if (!representation->SegmentList) {
3372     representation->SegmentList = gst_mpd_segment_list_node_new ();
3373   }
3374
3375   segment_url = gst_mpd_segment_url_node_new ();
3376
3377   va_start (myargs, property_name);
3378   g_object_set_valist (G_OBJECT (segment_url), property_name, myargs);
3379   va_end (myargs);
3380
3381   gst_mpd_segment_list_node_add_segment (representation->SegmentList,
3382       segment_url);
3383
3384   /* Set the media presentation time according to the new segment duration added */
3385   g_object_get (client->mpd_root_node, "media-presentation-duration",
3386       &media_presentation_duration, NULL);
3387   media_presentation_duration +=
3388       GST_MPD_MULT_SEGMENT_BASE_NODE (representation->SegmentList)->duration;
3389   g_object_set (client->mpd_root_node, "media-presentation-duration",
3390       media_presentation_duration, NULL);
3391
3392   return TRUE;
3393 }