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