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