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