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