3 * Copyright (C) 2019 Collabora Ltd.
4 * Author: Stéphane Cerveau <scerveau@collabora.com>
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.
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.
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
22 #include "gstmpdclient.h"
23 #include "gstmpdparser.h"
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
29 G_DEFINE_TYPE (GstMPDClient2, gst_mpd_client2, GST_TYPE_OBJECT);
31 static GstMPDSegmentBaseNode *gst_mpd_client2_get_segment_base (GstMPDPeriodNode
32 * Period, GstMPDAdaptationSetNode * AdaptationSet,
33 GstMPDRepresentationNode * Representation);
34 static GstMPDSegmentListNode *gst_mpd_client2_get_segment_list (GstMPDClient2 *
35 client, GstMPDPeriodNode * Period, GstMPDAdaptationSetNode * AdaptationSet,
36 GstMPDRepresentationNode * Representation);
38 static guint gst_mpd_client2_get_segments_counts (GstMPDClient2 * client,
39 GstActiveStream * stream);
41 static GList *gst_mpd_client2_fetch_external_periods (GstMPDClient2 * client,
42 GstMPDPeriodNode * period_node);
43 static GList *gst_mpd_client2_fetch_external_adaptation_set (GstMPDClient2 *
44 client, GstMPDPeriodNode * period, GstMPDAdaptationSetNode * adapt_set);
46 static GstMPDRepresentationNode
47 * gst_mpd_client2_get_lowest_representation (GList * Representations);
48 static GstStreamPeriod *gst_mpd_client2_get_stream_period (GstMPDClient2 *
51 typedef GstMPDNode *(*MpdClientStringIDFilter) (GList * list, gchar * data);
52 typedef GstMPDNode *(*MpdClientIDFilter) (GList * list, guint data);
55 gst_mpd_client2_get_period_with_id (GList * periods, gchar * period_id)
57 GstMPDPeriodNode *period;
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);
69 gst_mpd_client2_get_adaptation_set_with_id (GList * adaptation_sets, guint id)
71 GstMPDAdaptationSetNode *adaptation_set;
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);
82 GstMPDRepresentationNode *
83 gst_mpd_client2_get_representation_with_id (GList * representations,
86 GstMPDRepresentationNode *representation;
89 for (list = g_list_first (representations); list; list = g_list_next (list)) {
90 representation = (GstMPDRepresentationNode *) list->data;
91 if (!g_strcmp0 (representation->id, rep_id))
92 return GST_MPD_REPRESENTATION_NODE (representation);
98 gst_mpd_client2_get_representation_with_id_filter (GList * representations,
101 GstMPDRepresentationNode *representation =
102 gst_mpd_client2_get_representation_with_id (representations, rep_id);
104 if (representation != NULL)
105 return GST_MPD_NODE (representation);
111 _generate_new_string_id (GList * list, const gchar * tuple,
112 MpdClientStringIDFilter filter)
119 id = g_strdup_printf (tuple, i);
120 node = filter (list, id);
128 _generate_new_id (GList * list, MpdClientIDFilter filter)
133 node = filter (list, id);
140 static GstMPDRepresentationNode *
141 gst_mpd_client2_get_lowest_representation (GList * Representations)
144 GstMPDRepresentationNode *rep = NULL;
145 GstMPDRepresentationNode *lowest = NULL;
147 if (Representations == NULL)
150 for (list = g_list_first (Representations); list; list = g_list_next (list)) {
151 rep = (GstMPDRepresentationNode *) list->data;
152 if (rep && (!lowest || rep->bandwidth < lowest->bandwidth)) {
161 static GstMPDRepresentationNode *
162 gst_mpdparser_get_highest_representation (GList * Representations)
166 if (Representations == NULL)
169 list = g_list_last (Representations);
171 return list ? (GstMPDRepresentationNode *) list->data : NULL;
174 static GstMPDRepresentationNode *
175 gst_mpdparser_get_representation_with_max_bandwidth (GList * Representations,
179 GstMPDRepresentationNode *representation, *best_rep = NULL;
181 if (Representations == NULL)
184 if (max_bandwidth <= 0) /* 0 => get highest representation available */
185 return gst_mpdparser_get_highest_representation (Representations);
187 for (list = g_list_first (Representations); list; list = g_list_next (list)) {
188 representation = (GstMPDRepresentationNode *) list->data;
189 if (representation && representation->bandwidth <= max_bandwidth) {
190 best_rep = representation;
198 static GstMPDSegmentListNode *
199 gst_mpd_client2_fetch_external_segment_list (GstMPDClient2 * client,
200 GstMPDPeriodNode * Period,
201 GstMPDAdaptationSetNode * AdaptationSet,
202 GstMPDRepresentationNode * Representation,
203 GstMPDSegmentListNode * parent, GstMPDSegmentListNode * segment_list)
205 DownloadRequest *download;
206 GstBuffer *segment_list_buffer = NULL;
209 GstUri *base_uri, *uri;
212 GstMPDSegmentListNode *new_segment_list = NULL;
214 /* ISO/IEC 23009-1:2014 5.5.3 4)
215 * Remove nodes that resolve to nothing when resolving
217 if (strcmp (segment_list->xlink_href,
218 "urn:mpeg:dash:resolve-to-zero:2013") == 0) {
222 if (!client->download_helper) {
226 /* Build absolute URI */
228 /* Get base URI at the MPD level */
230 gst_uri_from_string (client->mpd_base_uri ? client->
231 mpd_base_uri : client->mpd_uri);
233 /* combine a BaseURL at the MPD level with the current base url */
235 gst_mpd_helper_combine_urls (base_uri, client->mpd_root_node->BaseURLs,
238 /* combine a BaseURL at the Period level with the current base url */
240 gst_mpd_helper_combine_urls (base_uri, Period->BaseURLs, &query, 0);
243 /* combine a BaseURL at the AdaptationSet level with the current base url */
245 gst_mpd_helper_combine_urls (base_uri, AdaptationSet->BaseURLs, &query,
248 if (Representation) {
249 /* combine a BaseURL at the Representation level with the current base url */
251 gst_mpd_helper_combine_urls (base_uri, Representation->BaseURLs,
256 uri = gst_uri_from_string_with_base (base_uri, segment_list->xlink_href);
258 gst_uri_set_query_string (uri, query);
260 uri_string = gst_uri_to_string (uri);
261 gst_uri_unref (base_uri);
265 downloadhelper_fetch_uri (client->download_helper,
266 uri_string, client->mpd_uri,
267 DOWNLOAD_FLAG_COMPRESS | DOWNLOAD_FLAG_FORCE_REFRESH, &err);
271 GST_ERROR ("Failed to download external SegmentList node at '%s': %s",
272 segment_list->xlink_href, err->message);
273 g_clear_error (&err);
277 segment_list_buffer = download_request_take_buffer (download);
278 download_request_unref (download);
280 if (segment_list_buffer) {
283 gst_buffer_map (segment_list_buffer, &map, GST_MAP_READ);
285 gst_mpdparser_get_external_segment_list ((const gchar *) map.data,
288 gst_buffer_unmap (segment_list_buffer, &map);
289 gst_buffer_unref (segment_list_buffer);
292 return new_segment_list;
295 static GstMPDSegmentBaseNode *
296 gst_mpd_client2_get_segment_base (GstMPDPeriodNode * Period,
297 GstMPDAdaptationSetNode * AdaptationSet,
298 GstMPDRepresentationNode * Representation)
300 GstMPDSegmentBaseNode *SegmentBase = NULL;
302 if (Representation && Representation->SegmentBase) {
303 SegmentBase = Representation->SegmentBase;
304 } else if (AdaptationSet && AdaptationSet->SegmentBase) {
305 SegmentBase = AdaptationSet->SegmentBase;
306 } else if (Period && Period->SegmentBase) {
307 SegmentBase = Period->SegmentBase;
309 /* the SegmentBase element could be encoded also inside a SegmentList element */
310 if (SegmentBase == NULL) {
311 if (Representation && Representation->SegmentList
312 && GST_MPD_MULT_SEGMENT_BASE_NODE (Representation->SegmentList)
313 && GST_MPD_MULT_SEGMENT_BASE_NODE (Representation->
314 SegmentList)->SegmentBase) {
316 GST_MPD_MULT_SEGMENT_BASE_NODE (Representation->
317 SegmentList)->SegmentBase;
318 } else if (AdaptationSet && AdaptationSet->SegmentList
319 && GST_MPD_MULT_SEGMENT_BASE_NODE (AdaptationSet->SegmentList)
320 && GST_MPD_MULT_SEGMENT_BASE_NODE (AdaptationSet->
321 SegmentList)->SegmentBase) {
323 GST_MPD_MULT_SEGMENT_BASE_NODE (AdaptationSet->
324 SegmentList)->SegmentBase;
325 } else if (Period && Period->SegmentList
326 && GST_MPD_MULT_SEGMENT_BASE_NODE (Period->SegmentList)
327 && GST_MPD_MULT_SEGMENT_BASE_NODE (Period->SegmentList)->SegmentBase) {
329 GST_MPD_MULT_SEGMENT_BASE_NODE (Period->SegmentList)->SegmentBase;
336 static GstMPDSegmentListNode *
337 gst_mpd_client2_get_segment_list (GstMPDClient2 * client,
338 GstMPDPeriodNode * Period, GstMPDAdaptationSetNode * AdaptationSet,
339 GstMPDRepresentationNode * Representation)
341 GstMPDSegmentListNode **SegmentList;
342 GstMPDSegmentListNode *ParentSegmentList = NULL;
344 if (Representation && Representation->SegmentList) {
345 SegmentList = &Representation->SegmentList;
346 ParentSegmentList = AdaptationSet->SegmentList;
347 } else if (AdaptationSet && AdaptationSet->SegmentList) {
348 SegmentList = &AdaptationSet->SegmentList;
349 ParentSegmentList = Period->SegmentList;
350 Representation = NULL;
352 Representation = NULL;
353 AdaptationSet = NULL;
354 SegmentList = &Period->SegmentList;
357 /* Resolve external segment list here. */
358 if (*SegmentList && (*SegmentList)->xlink_href) {
359 GstMPDSegmentListNode *new_segment_list;
361 /* TODO: Use SegmentList of parent if
362 * - Parent has its own SegmentList
363 * - Fail to get SegmentList from external xml
366 gst_mpd_client2_fetch_external_segment_list (client, Period,
367 AdaptationSet, Representation, ParentSegmentList, *SegmentList);
369 gst_mpd_segment_list_node_free (*SegmentList);
370 *SegmentList = new_segment_list;
377 gst_mpd_client2_get_segment_duration (GstMPDClient2 * client,
378 GstActiveStream * stream, guint64 * scale_dur)
380 GstStreamPeriod *stream_period;
381 GstMPDMultSegmentBaseNode *base = NULL;
382 GstClockTime duration = 0;
384 g_return_val_if_fail (stream != NULL, GST_CLOCK_TIME_NONE);
385 stream_period = gst_mpd_client2_get_stream_period (client);
386 g_return_val_if_fail (stream_period != NULL, GST_CLOCK_TIME_NONE);
388 if (stream->cur_segment_list) {
389 base = GST_MPD_MULT_SEGMENT_BASE_NODE (stream->cur_segment_list);
390 } else if (stream->cur_seg_template) {
391 base = GST_MPD_MULT_SEGMENT_BASE_NODE (stream->cur_seg_template);
394 if (base == NULL || base->SegmentBase == NULL) {
395 /* this may happen when we have a single segment */
396 duration = stream_period->duration;
398 *scale_dur = duration;
400 /* duration is guint so this cannot overflow */
401 duration = base->duration * GST_SECOND;
403 *scale_dur = duration;
404 duration /= base->SegmentBase->timescale;
411 gst_mpd_client2_active_streams_free (GstMPDClient2 * client)
413 if (client->active_streams) {
414 g_list_foreach (client->active_streams,
415 (GFunc) gst_mpdparser_free_active_stream, NULL);
416 g_list_free (client->active_streams);
417 client->active_streams = NULL;
422 gst_mpd_client2_finalize (GObject * object)
424 GstMPDClient2 *client = GST_MPD_CLIENT (object);
426 if (client->mpd_root_node)
427 gst_mpd_root_node_free (client->mpd_root_node);
429 if (client->periods) {
430 g_list_free_full (client->periods,
431 (GDestroyNotify) gst_mpdparser_free_stream_period);
434 gst_mpd_client2_active_streams_free (client);
436 g_free (client->mpd_uri);
437 client->mpd_uri = NULL;
438 g_free (client->mpd_base_uri);
439 client->mpd_base_uri = NULL;
441 G_OBJECT_CLASS (gst_mpd_client2_parent_class)->finalize (object);
445 gst_mpd_client2_class_init (GstMPDClient2Class * klass)
447 GObjectClass *object_class = G_OBJECT_CLASS (klass);
448 object_class->finalize = gst_mpd_client2_finalize;
452 gst_mpd_client2_init (GstMPDClient2 * client)
457 gst_mpd_client2_new (void)
459 GST_DEBUG_CATEGORY_INIT (gst_dash_mpd_client_debug, "dashmpdclient2", 0,
461 return g_object_new (GST_TYPE_MPD_CLIENT, NULL);
465 gst_mpd_client2_new_static (void)
467 GstMPDClient2 *client = gst_mpd_client2_new ();
469 client->mpd_root_node = gst_mpd_root_node_new ();
470 client->mpd_root_node->default_namespace =
471 g_strdup ("urn:mpeg:dash:schema:mpd:2011");
472 client->mpd_root_node->profiles =
473 g_strdup ("urn:mpeg:dash:profile:isoff-main:2011");
474 client->mpd_root_node->type = GST_MPD_FILE_TYPE_STATIC;
475 client->mpd_root_node->minBufferTime = 1500;
481 gst_mpd_client2_free (GstMPDClient2 * client)
484 gst_object_unref (client);
488 gst_mpd_client2_parse (GstMPDClient2 * client, const gchar * data, gint size)
490 gboolean ret = FALSE;
493 ret = gst_mpdparser_get_mpd_root_node (&client->mpd_root_node, data, size);
496 gst_mpd_client2_check_profiles (client);
497 gst_mpd_client2_fetch_on_load_external_resources (client);
505 gst_mpd_client2_get_xml_content (GstMPDClient2 * client, gchar ** data,
508 gboolean ret = FALSE;
510 g_return_val_if_fail (client != NULL, ret);
511 g_return_val_if_fail (client->mpd_root_node != NULL, ret);
513 ret = gst_mpd_node_get_xml_buffer (GST_MPD_NODE (client->mpd_root_node),
520 gst_mpd_client2_get_availability_start_time (GstMPDClient2 * client)
522 GstDateTime *start_time;
525 return (GstDateTime *) NULL;
527 start_time = client->mpd_root_node->availabilityStartTime;
529 gst_date_time_ref (start_time);
534 gst_mpd_client2_set_download_helper (GstMPDClient2 * client,
537 client->download_helper = dh;
541 gst_mpd_client2_check_profiles (GstMPDClient2 * client)
543 GST_DEBUG ("Profiles: %s",
544 client->mpd_root_node->profiles ? client->mpd_root_node->
545 profiles : "<none>");
547 if (!client->mpd_root_node->profiles)
550 if (g_strstr_len (client->mpd_root_node->profiles, -1,
551 "urn:mpeg:dash:profile:isoff-on-demand:2011")) {
552 client->profile_isoff_ondemand = TRUE;
553 GST_DEBUG ("Found ISOFF on demand profile (2011)");
558 gst_mpd_client2_fetch_on_load_external_resources (GstMPDClient2 * client)
562 for (l = client->mpd_root_node->Periods; l; /* explicitly advanced below */ ) {
563 GstMPDPeriodNode *period = l->data;
566 if (period->xlink_href && period->actuate == GST_MPD_XLINK_ACTUATE_ON_LOAD) {
567 GList *new_periods, *prev, *next;
569 new_periods = gst_mpd_client2_fetch_external_periods (client, period);
572 client->mpd_root_node->Periods =
573 g_list_delete_link (client->mpd_root_node->Periods, l);
574 gst_mpd_period_node_free (period);
577 /* Get new next node, we will insert before this */
581 next = client->mpd_root_node->Periods;
583 while (new_periods) {
584 client->mpd_root_node->Periods =
585 g_list_insert_before (client->mpd_root_node->Periods, next,
587 new_periods = g_list_delete_link (new_periods, new_periods);
591 /* Update our iterator to the first new period if any, or the next */
595 l = client->mpd_root_node->Periods;
600 if (period->SegmentList && period->SegmentList->xlink_href
601 && period->SegmentList->actuate == GST_MPD_XLINK_ACTUATE_ON_LOAD) {
602 GstMPDSegmentListNode *new_segment_list;
605 gst_mpd_client2_fetch_external_segment_list (client, period, NULL,
606 NULL, NULL, period->SegmentList);
608 gst_mpd_segment_list_node_free (period->SegmentList);
609 period->SegmentList = new_segment_list;
612 for (m = period->AdaptationSets; m; /* explicitly advanced below */ ) {
613 GstMPDAdaptationSetNode *adapt_set = m->data;
616 if (adapt_set->xlink_href
617 && adapt_set->actuate == GST_MPD_XLINK_ACTUATE_ON_LOAD) {
618 GList *new_adapt_sets, *prev, *next;
621 gst_mpd_client2_fetch_external_adaptation_set (client, period,
625 period->AdaptationSets = g_list_delete_link (period->AdaptationSets, m);
626 gst_mpd_adaptation_set_node_free (adapt_set);
629 /* Get new next node, we will insert before this */
633 next = period->AdaptationSets;
635 while (new_adapt_sets) {
636 period->AdaptationSets =
637 g_list_insert_before (period->AdaptationSets, next,
638 new_adapt_sets->data);
639 new_adapt_sets = g_list_delete_link (new_adapt_sets, new_adapt_sets);
643 /* Update our iterator to the first new adapt_set if any, or the next */
647 m = period->AdaptationSets;
652 if (adapt_set->SegmentList && adapt_set->SegmentList->xlink_href
653 && adapt_set->SegmentList->actuate == GST_MPD_XLINK_ACTUATE_ON_LOAD) {
654 GstMPDSegmentListNode *new_segment_list;
657 gst_mpd_client2_fetch_external_segment_list (client, period,
658 adapt_set, NULL, period->SegmentList, adapt_set->SegmentList);
660 gst_mpd_segment_list_node_free (adapt_set->SegmentList);
661 adapt_set->SegmentList = new_segment_list;
664 for (n = adapt_set->Representations; n; n = n->next) {
665 GstMPDRepresentationNode *representation = n->data;
667 if (representation->SegmentList
668 && representation->SegmentList->xlink_href
669 && representation->SegmentList->actuate ==
670 GST_MPD_XLINK_ACTUATE_ON_LOAD) {
672 GstMPDSegmentListNode *new_segment_list;
675 gst_mpd_client2_fetch_external_segment_list (client, period,
676 adapt_set, representation, adapt_set->SegmentList,
677 representation->SegmentList);
679 gst_mpd_segment_list_node_free (representation->SegmentList);
680 representation->SegmentList = new_segment_list;
693 static GstStreamPeriod *
694 gst_mpd_client2_get_stream_period (GstMPDClient2 * client)
696 g_return_val_if_fail (client != NULL, NULL);
697 g_return_val_if_fail (client->periods != NULL, NULL);
699 return g_list_nth_data (client->periods, client->period_idx);
703 gst_mpd_client2_get_baseURL (GstMPDClient2 * client, guint indexStream)
705 GstActiveStream *stream;
707 g_return_val_if_fail (client != NULL, NULL);
708 g_return_val_if_fail (client->active_streams != NULL, NULL);
709 stream = g_list_nth_data (client->active_streams, indexStream);
710 g_return_val_if_fail (stream != NULL, NULL);
712 return stream->baseURL;
715 /* select a stream and extract the baseURL (if present) */
717 gst_mpd_client2_parse_baseURL (GstMPDClient2 * client, GstActiveStream * stream,
720 GstStreamPeriod *stream_period;
721 static const gchar empty[] = "";
725 g_return_val_if_fail (stream != NULL, g_strdup (empty));
726 stream_period = gst_mpd_client2_get_stream_period (client);
727 g_return_val_if_fail (stream_period != NULL, g_strdup (empty));
728 g_return_val_if_fail (stream_period->period != NULL, g_strdup (empty));
730 /* NULLify query return before we start */
734 /* initialise base url */
736 gst_uri_from_string (client->mpd_base_uri ? client->
737 mpd_base_uri : client->mpd_uri);
739 /* combine a BaseURL at the MPD level with the current base url */
741 gst_mpd_helper_combine_urls (abs_url, client->mpd_root_node->BaseURLs,
742 query, stream->baseURL_idx);
744 /* combine a BaseURL at the Period level with the current base url */
746 gst_mpd_helper_combine_urls (abs_url, stream_period->period->BaseURLs,
747 query, stream->baseURL_idx);
749 GST_DEBUG ("Current adaptation set id %i (%s)", stream->cur_adapt_set->id,
750 stream->cur_adapt_set->contentType);
751 /* combine a BaseURL at the AdaptationSet level with the current base url */
753 gst_mpd_helper_combine_urls (abs_url, stream->cur_adapt_set->BaseURLs,
754 query, stream->baseURL_idx);
756 /* combine a BaseURL at the Representation level with the current base url */
758 gst_mpd_helper_combine_urls (abs_url,
759 stream->cur_representation->BaseURLs, query, stream->baseURL_idx);
761 ret = gst_uri_to_string (abs_url);
762 gst_uri_unref (abs_url);
768 gst_mpd_client2_get_segment_end_time (GstMPDClient2 * client,
769 GPtrArray * segments, const GstMediaSegment * segment, gint index)
771 const GstStreamPeriod *stream_period;
774 if (segment->repeat >= 0)
775 return segment->start + (segment->repeat + 1) * segment->duration;
777 if (index < segments->len - 1) {
778 const GstMediaSegment *next_segment =
779 g_ptr_array_index (segments, index + 1);
780 end = next_segment->start;
782 stream_period = gst_mpd_client2_get_stream_period (client);
783 end = stream_period->start + stream_period->duration;
789 gst_mpd_client2_add_media_segment (GstActiveStream * stream,
790 GstMPDSegmentURLNode * url_node, guint number, gint repeat,
791 guint64 scale_start, guint64 scale_duration,
792 GstClockTime start, GstClockTime duration)
794 GstMediaSegment *media_segment;
796 g_return_val_if_fail (stream->segments != NULL, FALSE);
798 media_segment = g_slice_new0 (GstMediaSegment);
800 media_segment->SegmentURL = url_node;
801 media_segment->number = number;
802 media_segment->scale_start = scale_start;
803 media_segment->scale_duration = scale_duration;
804 media_segment->start = start;
805 media_segment->duration = duration;
806 media_segment->repeat = repeat;
808 g_ptr_array_add (stream->segments, media_segment);
809 GST_LOG ("Added new segment: number %d, repeat %d, "
810 "ts: %" GST_TIME_FORMAT ", dur: %"
811 GST_TIME_FORMAT, number, repeat,
812 GST_TIME_ARGS (start), GST_TIME_ARGS (duration));
818 gst_mpd_client2_stream_update_presentation_time_offset (GstMPDClient2 * client,
819 GstActiveStream * stream)
821 GstMPDSegmentBaseNode *segbase = NULL;
823 /* Find the used segbase */
824 if (stream->cur_segment_list) {
826 GST_MPD_MULT_SEGMENT_BASE_NODE (stream->cur_segment_list)->SegmentBase;
827 } else if (stream->cur_seg_template) {
829 GST_MPD_MULT_SEGMENT_BASE_NODE (stream->cur_seg_template)->SegmentBase;
830 } else if (stream->cur_segment_base) {
831 segbase = stream->cur_segment_base;
835 /* Avoid overflows */
836 stream->presentationTimeOffset =
837 gst_util_uint64_scale (segbase->presentationTimeOffset, GST_SECOND,
840 stream->presentationTimeOffset = 0;
843 GST_LOG ("Setting stream's presentation time offset to %" GST_TIME_FORMAT,
844 GST_TIME_ARGS (stream->presentationTimeOffset));
848 gst_mpd_client2_setup_representation (GstMPDClient2 * client,
849 GstActiveStream * stream, GstMPDRepresentationNode * representation)
851 GstStreamPeriod *stream_period;
853 GstClockTime PeriodStart, PeriodEnd, start_time, duration;
857 if (stream->cur_adapt_set == NULL) {
858 GST_WARNING ("No valid AdaptationSet node in the MPD file, aborting...");
862 rep_list = stream->cur_adapt_set->Representations;
863 stream->cur_representation = representation;
864 stream->representation_idx = g_list_index (rep_list, representation);
866 /* clean the old segment list, if any */
867 if (stream->segments) {
868 g_ptr_array_unref (stream->segments);
869 stream->segments = NULL;
872 stream_period = gst_mpd_client2_get_stream_period (client);
873 g_return_val_if_fail (stream_period != NULL, FALSE);
874 g_return_val_if_fail (stream_period->period != NULL, FALSE);
876 PeriodStart = stream_period->start;
877 if (GST_CLOCK_TIME_IS_VALID (stream_period->duration))
878 PeriodEnd = stream_period->start + stream_period->duration;
880 PeriodEnd = GST_CLOCK_TIME_NONE;
882 GST_LOG ("Building segment list for Period from %" GST_TIME_FORMAT " to %"
883 GST_TIME_FORMAT, GST_TIME_ARGS (PeriodStart), GST_TIME_ARGS (PeriodEnd));
885 if (representation->SegmentBase != NULL
886 || representation->SegmentList != NULL) {
889 /* We have a fixed list of segments for any of the cases here,
890 * init the segments list */
891 gst_mpdparser_init_active_stream_segments (stream);
893 /* get the first segment_base of the selected representation */
894 if ((stream->cur_segment_base =
895 gst_mpd_client2_get_segment_base (stream_period->period,
896 stream->cur_adapt_set, representation)) == NULL) {
897 GST_DEBUG ("No useful SegmentBase node for the current Representation");
900 /* get the first segment_list of the selected representation */
901 if ((stream->cur_segment_list =
902 gst_mpd_client2_get_segment_list (client, stream_period->period,
903 stream->cur_adapt_set, representation)) == NULL) {
904 GST_DEBUG ("No useful SegmentList node for the current Representation");
905 /* here we should have a single segment for each representation, whose URL is encoded in the baseURL element */
906 if (!gst_mpd_client2_add_media_segment (stream, NULL, 1, 0, 0,
907 PeriodEnd - PeriodStart, PeriodStart, PeriodEnd - PeriodStart)) {
911 /* build the list of GstMediaSegment nodes from the SegmentList node */
912 SegmentURL = stream->cur_segment_list->SegmentURL;
913 if (SegmentURL == NULL) {
915 ("No valid list of SegmentURL nodes in the MPD file, aborting...");
919 /* build segment list */
920 i = GST_MPD_MULT_SEGMENT_BASE_NODE (stream->
921 cur_segment_list)->startNumber;
923 start_time = PeriodStart;
925 GST_LOG ("Building media segment list using a SegmentList node");
926 if (GST_MPD_MULT_SEGMENT_BASE_NODE (stream->
927 cur_segment_list)->SegmentTimeline) {
928 GstMPDSegmentTimelineNode *timeline;
931 GstClockTime presentationTimeOffset;
932 GstMPDSegmentBaseNode *segbase;
935 GST_MPD_MULT_SEGMENT_BASE_NODE (stream->
936 cur_segment_list)->SegmentBase;
937 presentationTimeOffset =
938 gst_util_uint64_scale (segbase->presentationTimeOffset, GST_SECOND,
940 GST_LOG ("presentationTimeOffset = %" G_GUINT64_FORMAT,
941 presentationTimeOffset);
944 GST_MPD_MULT_SEGMENT_BASE_NODE (stream->
945 cur_segment_list)->SegmentTimeline;
946 for (list = g_queue_peek_head_link (&timeline->S); list;
947 list = g_list_next (list)) {
950 S = (GstMPDSNode *) list->data;
951 GST_LOG ("Processing S node: d=%" G_GUINT64_FORMAT " r=%d t=%"
952 G_GUINT64_FORMAT, S->d, S->r, S->t);
954 GST_MPD_MULT_SEGMENT_BASE_NODE (stream->
955 cur_segment_list)->SegmentBase->timescale;
956 duration = gst_util_uint64_scale (S->d, GST_SECOND, timescale);
960 start_time = gst_util_uint64_scale (S->t, GST_SECOND, timescale)
961 + PeriodStart - presentationTimeOffset;
966 ("SegmentTimeline does not have a matching SegmentURL, aborting...");
970 if (!gst_mpd_client2_add_media_segment (stream, SegmentURL->data, i,
971 S->r, start, S->d, start_time, duration)) {
975 start_time += duration * (S->r + 1);
976 start += S->d * (S->r + 1);
977 SegmentURL = g_list_next (SegmentURL);
983 gst_mpd_client2_get_segment_duration (client, stream, &scale_dur);
984 if (!GST_CLOCK_TIME_IS_VALID (duration))
988 if (!gst_mpd_client2_add_media_segment (stream, SegmentURL->data, i,
989 0, start, scale_dur, start_time, duration)) {
994 start_time += duration;
995 SegmentURL = g_list_next (SegmentURL);
1000 if (representation->SegmentTemplate != NULL) {
1001 stream->cur_seg_template = representation->SegmentTemplate;
1002 } else if (stream->cur_adapt_set->SegmentTemplate != NULL) {
1003 stream->cur_seg_template = stream->cur_adapt_set->SegmentTemplate;
1004 } else if (stream_period->period->SegmentTemplate != NULL) {
1005 stream->cur_seg_template = stream_period->period->SegmentTemplate;
1008 if (stream->cur_seg_template == NULL) {
1010 gst_mpdparser_init_active_stream_segments (stream);
1011 /* here we should have a single segment for each representation, whose URL is encoded in the baseURL element */
1012 if (!gst_mpd_client2_add_media_segment (stream, NULL, 1, 0, 0,
1013 PeriodEnd - PeriodStart, 0, PeriodEnd - PeriodStart)) {
1017 GstClockTime presentationTimeOffset;
1018 GstMPDMultSegmentBaseNode *mult_seg =
1019 GST_MPD_MULT_SEGMENT_BASE_NODE (stream->cur_seg_template);
1020 presentationTimeOffset =
1021 gst_util_uint64_scale (mult_seg->SegmentBase->presentationTimeOffset,
1022 GST_SECOND, mult_seg->SegmentBase->timescale);
1023 GST_LOG ("presentationTimeOffset = %" GST_TIME_FORMAT,
1024 GST_TIME_ARGS (presentationTimeOffset));
1025 /* build segment list */
1026 i = mult_seg->startNumber;
1030 GST_LOG ("Building media segment list using this template: %s",
1031 stream->cur_seg_template->media);
1033 if (mult_seg->SegmentTimeline) {
1034 GstMPDSegmentTimelineNode *timeline;
1038 timeline = mult_seg->SegmentTimeline;
1039 gst_mpdparser_init_active_stream_segments (stream);
1040 for (list = g_queue_peek_head_link (&timeline->S); list;
1041 list = g_list_next (list)) {
1044 S = (GstMPDSNode *) list->data;
1045 GST_LOG ("Processing S node: d=%" G_GUINT64_FORMAT " r=%u t=%"
1046 G_GUINT64_FORMAT, S->d, S->r, S->t);
1047 timescale = mult_seg->SegmentBase->timescale;
1048 duration = gst_util_uint64_scale (S->d, GST_SECOND, timescale);
1051 start_time = gst_util_uint64_scale (S->t, GST_SECOND, timescale)
1052 + PeriodStart - presentationTimeOffset;
1055 if (!gst_mpd_client2_add_media_segment (stream, NULL, i, S->r, start,
1056 S->d, start_time, duration)) {
1060 start += S->d * (S->r + 1);
1061 start_time += duration * (S->r + 1);
1064 /* NOP - The segment is created on demand with the template, no need
1065 * to build a list */
1070 /* clip duration of segments to stop at period end */
1071 if (stream->segments && stream->segments->len) {
1072 if (GST_CLOCK_TIME_IS_VALID (PeriodEnd)) {
1075 for (n = 0; n < stream->segments->len; ++n) {
1076 GstMediaSegment *media_segment =
1077 g_ptr_array_index (stream->segments, n);
1078 if (media_segment) {
1079 if (media_segment->start + media_segment->duration > PeriodEnd) {
1080 GstClockTime stop = PeriodEnd;
1081 if (n < stream->segments->len - 1) {
1082 GstMediaSegment *next_segment =
1083 g_ptr_array_index (stream->segments, n + 1);
1084 if (next_segment && next_segment->start < PeriodEnd)
1085 stop = next_segment->start;
1087 media_segment->duration =
1088 media_segment->start > stop ? 0 : stop - media_segment->start;
1089 GST_LOG ("Fixed duration of segment %u: %" GST_TIME_FORMAT, n,
1090 GST_TIME_ARGS (media_segment->duration));
1092 /* If the segment was clipped entirely, we discard it and all
1093 * subsequent ones */
1094 if (media_segment->duration == 0) {
1095 GST_WARNING ("Discarding %u segments outside period",
1096 stream->segments->len - n);
1097 /* _set_size should properly unref elements */
1098 g_ptr_array_set_size (stream->segments, n);
1105 #ifndef GST_DISABLE_GST_DEBUG
1106 if (stream->segments->len > 0) {
1107 GstMediaSegment *last_media_segment =
1108 g_ptr_array_index (stream->segments, stream->segments->len - 1);
1109 GST_LOG ("Built a list of %d segments", last_media_segment->number);
1111 GST_LOG ("All media segments were clipped");
1116 g_free (stream->baseURL);
1117 g_free (stream->queryURL);
1119 gst_mpd_client2_parse_baseURL (client, stream, &stream->queryURL);
1121 gst_mpd_client2_stream_update_presentation_time_offset (client, stream);
1126 #define CUSTOM_WRAPPER_START "<custom_wrapper>"
1127 #define CUSTOM_WRAPPER_END "</custom_wrapper>"
1130 gst_mpd_client2_fetch_external_periods (GstMPDClient2 * client,
1131 GstMPDPeriodNode * period_node)
1133 DownloadRequest *download;
1134 GstBuffer *period_buffer;
1137 GstUri *base_uri, *uri;
1138 gchar *query = NULL;
1139 gchar *uri_string, *wrapper;
1140 GList *new_periods = NULL;
1143 /* ISO/IEC 23009-1:2014 5.5.3 4)
1144 * Remove nodes that resolve to nothing when resolving
1146 if (strcmp (period_node->xlink_href,
1147 "urn:mpeg:dash:resolve-to-zero:2013") == 0) {
1151 if (!client->download_helper) {
1155 /* Build absolute URI */
1157 /* Get base URI at the MPD level */
1159 gst_uri_from_string (client->mpd_base_uri ? client->
1160 mpd_base_uri : client->mpd_uri);
1162 /* combine a BaseURL at the MPD level with the current base url */
1164 gst_mpd_helper_combine_urls (base_uri, client->mpd_root_node->BaseURLs,
1166 uri = gst_uri_from_string_with_base (base_uri, period_node->xlink_href);
1168 gst_uri_set_query_string (uri, query);
1170 uri_string = gst_uri_to_string (uri);
1171 gst_uri_unref (base_uri);
1172 gst_uri_unref (uri);
1175 downloadhelper_fetch_uri (client->download_helper,
1176 uri_string, client->mpd_uri,
1177 DOWNLOAD_FLAG_COMPRESS | DOWNLOAD_FLAG_FORCE_REFRESH, &err);
1178 g_free (uri_string);
1181 GST_ERROR ("Failed to download external Period node at '%s': %s",
1182 period_node->xlink_href, err->message);
1183 g_clear_error (&err);
1187 period_buffer = download_request_take_buffer (download);
1188 download_request_unref (download);
1190 if (period_buffer) {
1191 GstAdapter *adapter;
1192 /* external xml could have multiple period without root xmlNode.
1193 * To avoid xml parsing error caused by no root node, wrapping it with
1194 * custom root node */
1195 adapter = gst_adapter_new ();
1197 wrapper = g_new (gchar, strlen (CUSTOM_WRAPPER_START));
1198 memcpy (wrapper, CUSTOM_WRAPPER_START, strlen (CUSTOM_WRAPPER_START));
1199 gst_adapter_push (adapter,
1200 gst_buffer_new_wrapped (wrapper, strlen (CUSTOM_WRAPPER_START)));
1202 gst_adapter_push (adapter, period_buffer);
1204 wrapper = g_strdup (CUSTOM_WRAPPER_END);
1205 gst_adapter_push (adapter,
1206 gst_buffer_new_wrapped (wrapper, strlen (CUSTOM_WRAPPER_END) + 1));
1208 data = gst_adapter_map (adapter, gst_adapter_available (adapter));
1211 gst_mpdparser_get_external_periods (data,
1212 gst_adapter_available (adapter));
1214 gst_adapter_unmap (adapter);
1215 gst_adapter_clear (adapter);
1216 gst_object_unref (adapter);
1223 gst_mpd_client2_setup_media_presentation (GstMPDClient2 * client,
1224 GstClockTime time, gint period_idx, const gchar * period_id)
1226 GstStreamPeriod *stream_period;
1227 GstClockTime start, duration;
1230 gboolean ret = FALSE;
1232 g_return_val_if_fail (client != NULL, FALSE);
1233 g_return_val_if_fail (client->mpd_root_node != NULL, FALSE);
1235 /* Check if we set up the media presentation far enough already */
1236 for (list = client->periods; list; list = list->next) {
1237 GstStreamPeriod *stream_period = list->data;
1239 if ((time != GST_CLOCK_TIME_NONE
1240 && stream_period->duration != GST_CLOCK_TIME_NONE
1241 && stream_period->start + stream_period->duration >= time)
1242 || (time != GST_CLOCK_TIME_NONE && stream_period->start >= time))
1245 if (period_idx != -1 && stream_period->number >= period_idx)
1248 if (period_id != NULL && stream_period->period->id != NULL
1249 && strcmp (stream_period->period->id, period_id) == 0)
1254 GST_DEBUG ("Building the list of Periods in the Media Presentation");
1255 /* clean the old period list, if any */
1256 /* TODO: In theory we could reuse the ones we have so far but that
1257 * seems more complicated than the overhead caused here
1259 if (client->periods) {
1260 g_list_foreach (client->periods,
1261 (GFunc) gst_mpdparser_free_stream_period, NULL);
1262 g_list_free (client->periods);
1263 client->periods = NULL;
1268 duration = GST_CLOCK_TIME_NONE;
1270 if (client->mpd_root_node->mediaPresentationDuration <= 0 &&
1271 client->mpd_root_node->mediaPresentationDuration != -1) {
1272 /* Invalid MPD file: MPD duration is negative or zero */
1276 for (list = client->mpd_root_node->Periods; list;
1277 /* explicitly advanced below */ ) {
1278 GstMPDPeriodNode *period_node = list->data;
1279 GstMPDPeriodNode *next_period_node = NULL;
1281 /* Download external period */
1282 if (period_node->xlink_href) {
1287 gst_mpd_client2_fetch_external_periods (client, period_node);
1290 client->mpd_root_node->Periods =
1291 g_list_delete_link (client->mpd_root_node->Periods, list);
1292 gst_mpd_period_node_free (period_node);
1295 /* Get new next node, we will insert before this */
1299 next = client->mpd_root_node->Periods;
1301 while (new_periods) {
1302 client->mpd_root_node->Periods =
1303 g_list_insert_before (client->mpd_root_node->Periods, next,
1305 new_periods = g_list_delete_link (new_periods, new_periods);
1309 /* Update our iterator to the first new period if any, or the next */
1313 list = client->mpd_root_node->Periods;
1319 if (period_node->start != -1) {
1320 /* we have a regular period */
1321 /* start cannot be smaller than previous start */
1322 if (list != g_list_first (client->mpd_root_node->Periods)
1323 && start >= period_node->start * GST_MSECOND) {
1324 /* Invalid MPD file: duration would be negative or zero */
1327 start = period_node->start * GST_MSECOND;
1328 } else if (duration != GST_CLOCK_TIME_NONE) {
1329 /* start time inferred from previous period, this is still a regular period */
1332 && client->mpd_root_node->type == GST_MPD_FILE_TYPE_STATIC) {
1333 /* first period of a static MPD file, start time is 0 */
1335 } else if (client->mpd_root_node->type == GST_MPD_FILE_TYPE_DYNAMIC) {
1336 /* this should be a live stream, let this pass */
1338 /* this is an 'Early Available Period' */
1342 /* compute duration.
1343 If there is a start time for the next period, or this is the last period
1344 and mediaPresentationDuration was set, those values will take precedence
1345 over a configured period duration in computing this period's duration
1347 ISO/IEC 23009-1:2014(E), chapter 5.3.2.1
1348 "The Period extends until the PeriodStart of the next Period, or until
1349 the end of the Media Presentation in the case of the last Period."
1352 while ((next = g_list_next (list)) != NULL) {
1353 /* try to infer this period duration from the start time of the next period */
1354 next_period_node = next->data;
1356 if (next_period_node->xlink_href) {
1360 gst_mpd_client2_fetch_external_periods (client, next_period_node);
1362 client->mpd_root_node->Periods =
1363 g_list_delete_link (client->mpd_root_node->Periods, next);
1364 gst_mpd_period_node_free (next_period_node);
1365 next_period_node = NULL;
1366 /* Get new next node, we will insert before this */
1367 next = g_list_next (list);
1368 while (new_periods) {
1369 client->mpd_root_node->Periods =
1370 g_list_insert_before (client->mpd_root_node->Periods, next,
1372 new_periods = g_list_delete_link (new_periods, new_periods);
1375 /* And try again, getting the next list element which is now our newly
1376 * inserted nodes. If any */
1378 /* Got the next period and it doesn't have to be downloaded first */
1383 if (next_period_node) {
1384 if (next_period_node->start != -1) {
1385 if (start >= next_period_node->start * GST_MSECOND) {
1386 /* Invalid MPD file: duration would be negative or zero */
1389 duration = next_period_node->start * GST_MSECOND - start;
1390 } else if (period_node->duration != -1) {
1391 if (period_node->duration <= 0) {
1392 /* Invalid MPD file: duration would be negative or zero */
1395 duration = period_node->duration * GST_MSECOND;
1396 } else if (client->mpd_root_node->type == GST_MPD_FILE_TYPE_DYNAMIC) {
1397 /* might be a live file, ignore unspecified duration */
1399 /* Invalid MPD file! */
1402 } else if (client->mpd_root_node->mediaPresentationDuration != -1) {
1403 /* last Period of the Media Presentation */
1404 if (client->mpd_root_node->mediaPresentationDuration * GST_MSECOND <=
1406 /* Invalid MPD file: duration would be negative or zero */
1410 client->mpd_root_node->mediaPresentationDuration * GST_MSECOND -
1412 } else if (period_node->duration != -1) {
1413 duration = period_node->duration * GST_MSECOND;
1414 } else if (client->mpd_root_node->type == GST_MPD_FILE_TYPE_DYNAMIC) {
1415 /* might be a live file, ignore unspecified duration */
1417 /* Invalid MPD file! */
1419 ("Invalid MPD file. The MPD is static without a valid duration");
1423 stream_period = g_slice_new0 (GstStreamPeriod);
1424 client->periods = g_list_append (client->periods, stream_period);
1425 stream_period->period = period_node;
1426 stream_period->number = idx++;
1427 stream_period->start = start;
1428 stream_period->duration = duration;
1430 GST_LOG (" - added Period %d start=%" GST_TIME_FORMAT " duration=%"
1431 GST_TIME_FORMAT, idx, GST_TIME_ARGS (start), GST_TIME_ARGS (duration));
1433 if ((time != GST_CLOCK_TIME_NONE
1434 && stream_period->duration != GST_CLOCK_TIME_NONE
1435 && stream_period->start + stream_period->duration >= time)
1436 || (time != GST_CLOCK_TIME_NONE && stream_period->start >= time))
1439 if (period_idx != -1 && stream_period->number >= period_idx)
1442 if (period_id != NULL && stream_period->period->id != NULL
1443 && strcmp (stream_period->period->id, period_id) == 0)
1450 ("Found a total of %d valid Periods in the Media Presentation up to this point",
1456 ("Found an Early Available Period, skipping the rest of the Media Presentation");
1461 ("Cannot get the duration of the Period %d, skipping the rest of the Media Presentation",
1467 gst_mpd_client2_fetch_external_adaptation_set (GstMPDClient2 * client,
1468 GstMPDPeriodNode * period, GstMPDAdaptationSetNode * adapt_set)
1470 DownloadRequest *download;
1471 GstBuffer *adapt_set_buffer;
1473 GstUri *base_uri, *uri;
1474 gchar *query = NULL;
1476 GList *new_adapt_sets = NULL;
1478 /* ISO/IEC 23009-1:2014 5.5.3 4)
1479 * Remove nodes that resolve to nothing when resolving
1481 if (strcmp (adapt_set->xlink_href, "urn:mpeg:dash:resolve-to-zero:2013") == 0) {
1485 if (!client->download_helper) {
1489 /* Build absolute URI */
1491 /* Get base URI at the MPD level */
1493 gst_uri_from_string (client->mpd_base_uri ? client->
1494 mpd_base_uri : client->mpd_uri);
1496 /* combine a BaseURL at the MPD level with the current base url */
1498 gst_mpd_helper_combine_urls (base_uri, client->mpd_root_node->BaseURLs,
1501 /* combine a BaseURL at the Period level with the current base url */
1503 gst_mpd_helper_combine_urls (base_uri, period->BaseURLs, &query, 0);
1505 uri = gst_uri_from_string_with_base (base_uri, adapt_set->xlink_href);
1507 gst_uri_set_query_string (uri, query);
1509 uri_string = gst_uri_to_string (uri);
1510 gst_uri_unref (base_uri);
1511 gst_uri_unref (uri);
1514 downloadhelper_fetch_uri (client->download_helper,
1515 uri_string, client->mpd_uri,
1516 DOWNLOAD_FLAG_COMPRESS | DOWNLOAD_FLAG_FORCE_REFRESH, &err);
1517 g_free (uri_string);
1520 GST_ERROR ("Failed to download external AdaptationSet node at '%s': %s",
1521 adapt_set->xlink_href, err->message);
1522 g_clear_error (&err);
1526 adapt_set_buffer = download_request_take_buffer (download);
1527 download_request_unref (download);
1529 if (adapt_set_buffer) {
1531 gst_buffer_map (adapt_set_buffer, &map, GST_MAP_READ);
1534 gst_mpdparser_get_external_adaptation_sets ((const gchar *) map.data,
1537 gst_buffer_unmap (adapt_set_buffer, &map);
1538 gst_buffer_unref (adapt_set_buffer);
1541 return new_adapt_sets;
1545 gst_mpd_client2_get_adaptation_sets_for_period (GstMPDClient2 * client,
1546 GstStreamPeriod * period)
1550 g_return_val_if_fail (period != NULL, NULL);
1552 /* Resolve all external adaptation sets of this period. Every user of
1553 * the adaptation sets would need to know the content of all adaptation sets
1554 * to decide which one to use, so we have to resolve them all here
1556 for (list = period->period->AdaptationSets; list;
1557 /* advanced explicitly below */ ) {
1558 GstMPDAdaptationSetNode *adapt_set = (GstMPDAdaptationSetNode *) list->data;
1559 GList *new_adapt_sets = NULL, *prev, *next;
1561 if (!adapt_set->xlink_href) {
1567 gst_mpd_client2_fetch_external_adaptation_set (client, period->period,
1571 period->period->AdaptationSets =
1572 g_list_delete_link (period->period->AdaptationSets, list);
1573 gst_mpd_adaptation_set_node_free (adapt_set);
1576 /* Get new next node, we will insert before this */
1580 next = period->period->AdaptationSets;
1582 while (new_adapt_sets) {
1583 period->period->AdaptationSets =
1584 g_list_insert_before (period->period->AdaptationSets, next,
1585 new_adapt_sets->data);
1586 new_adapt_sets = g_list_delete_link (new_adapt_sets, new_adapt_sets);
1589 /* Update our iterator to the first new adaptation set if any, or the next */
1593 list = period->period->AdaptationSets;
1596 return period->period->AdaptationSets;
1600 gst_mpd_client2_get_adaptation_sets (GstMPDClient2 * client)
1602 GstStreamPeriod *stream_period;
1604 stream_period = gst_mpd_client2_get_stream_period (client);
1605 if (stream_period == NULL || stream_period->period == NULL) {
1606 GST_DEBUG ("No more Period nodes in the MPD file, terminating...");
1610 return gst_mpd_client2_get_adaptation_sets_for_period (client, stream_period);
1614 gst_mpd_client2_setup_streaming (GstMPDClient2 * client,
1615 GstMPDAdaptationSetNode * adapt_set)
1617 GstMPDRepresentationNode *representation;
1618 GList *rep_list = NULL;
1619 GstActiveStream *stream;
1621 rep_list = adapt_set->Representations;
1623 GST_WARNING ("Can not retrieve any representation, aborting...");
1627 stream = g_slice_new0 (GstActiveStream);
1628 gst_mpdparser_init_active_stream_segments (stream);
1630 stream->baseURL_idx = 0;
1631 stream->cur_adapt_set = adapt_set;
1633 GST_DEBUG ("0. Current stream %p", stream);
1638 gst_mpdparser_get_representation_with_max_bandwidth (rep_list,
1639 stream->max_bandwidth);
1641 if (!representation) {
1643 ("Can not retrieve a representation with the requested bandwidth");
1644 representation = gst_mpd_client2_get_lowest_representation (rep_list);
1648 representation = gst_mpd_client2_get_lowest_representation (rep_list);
1651 if (!representation) {
1652 GST_WARNING ("No valid representation in the MPD file, aborting...");
1653 gst_mpdparser_free_active_stream (stream);
1657 gst_mpdparser_representation_get_mimetype (adapt_set, representation);
1658 if (stream->mimeType == GST_STREAM_UNKNOWN) {
1659 GST_WARNING ("Unknown mime type in the representation, aborting...");
1660 gst_mpdparser_free_active_stream (stream);
1664 client->active_streams = g_list_append (client->active_streams, stream);
1665 if (!gst_mpd_client2_setup_representation (client, stream, representation)) {
1666 GST_WARNING ("Failed to setup the representation, aborting...");
1670 GST_INFO ("Successfully setup the download pipeline for mimeType %d",
1677 gst_mpd_client2_stream_seek (GstMPDClient2 * client, GstActiveStream * stream,
1678 gboolean forward, GstSeekFlags flags, GstClockTime ts,
1679 GstClockTime * final_ts)
1682 gint repeat_index = 0;
1683 GstMediaSegment *selectedChunk = NULL;
1685 g_return_val_if_fail (stream != NULL, 0);
1687 if (stream->segments) {
1688 for (index = 0; index < stream->segments->len; index++) {
1689 gboolean in_segment = FALSE;
1690 GstMediaSegment *segment = g_ptr_array_index (stream->segments, index);
1691 GstClockTime end_time;
1693 GST_DEBUG ("Looking at fragment sequence chunk %d / %d", index,
1694 stream->segments->len);
1697 gst_mpd_client2_get_segment_end_time (client, stream->segments,
1700 /* avoid downloading another fragment just for 1ns in reverse mode */
1702 in_segment = ts < end_time;
1704 in_segment = ts <= end_time;
1707 GstClockTime chunk_time;
1709 selectedChunk = segment;
1711 ((ts - segment->start) +
1712 ((GstMediaSegment *) stream->segments->pdata[0])->start) /
1715 chunk_time = segment->start + segment->duration * repeat_index;
1717 /* At the end of a segment in reverse mode, start from the previous fragment */
1718 if (!forward && repeat_index > 0
1719 && ((ts - segment->start) % segment->duration == 0))
1722 if ((flags & GST_SEEK_FLAG_SNAP_NEAREST) == GST_SEEK_FLAG_SNAP_NEAREST) {
1723 if (repeat_index + 1 < segment->repeat) {
1724 if (ts - chunk_time > chunk_time + segment->duration - ts)
1726 } else if (index + 1 < stream->segments->len) {
1727 GstMediaSegment *next_segment =
1728 g_ptr_array_index (stream->segments, index + 1);
1730 if (ts - chunk_time > next_segment->start - ts) {
1732 selectedChunk = next_segment;
1736 } else if (((forward && flags & GST_SEEK_FLAG_SNAP_AFTER) ||
1737 (!forward && flags & GST_SEEK_FLAG_SNAP_BEFORE)) &&
1740 if (repeat_index + 1 < segment->repeat) {
1744 if (index + 1 >= stream->segments->len) {
1745 selectedChunk = NULL;
1747 selectedChunk = g_ptr_array_index (stream->segments, ++index);
1755 if (selectedChunk == NULL) {
1756 stream->segment_index = stream->segments->len;
1757 stream->segment_repeat_index = 0;
1758 GST_DEBUG ("Seek to after last segment");
1763 *final_ts = selectedChunk->start + selectedChunk->duration * repeat_index;
1765 GstClockTime duration =
1766 gst_mpd_client2_get_segment_duration (client, stream, NULL);
1767 GstStreamPeriod *stream_period = gst_mpd_client2_get_stream_period (client);
1768 guint segments_count = gst_mpd_client2_get_segments_counts (client, stream);
1769 GstClockTime index_time;
1771 g_return_val_if_fail (GST_MPD_MULT_SEGMENT_BASE_NODE
1772 (stream->cur_seg_template)->SegmentTimeline == NULL, FALSE);
1773 if (!GST_CLOCK_TIME_IS_VALID (duration)) {
1777 if (ts > stream_period->start)
1778 ts -= stream_period->start;
1782 index = ts / duration;
1784 /* At the end of a segment in reverse mode, start from the previous fragment */
1785 if (!forward && index > 0 && ts % duration == 0)
1788 index_time = index * duration;
1790 if ((flags & GST_SEEK_FLAG_SNAP_NEAREST) == GST_SEEK_FLAG_SNAP_NEAREST) {
1791 if (ts - index_time > index_time + duration - ts)
1793 } else if (((forward && flags & GST_SEEK_FLAG_SNAP_AFTER) ||
1794 (!forward && flags & GST_SEEK_FLAG_SNAP_BEFORE))
1795 && ts != index_time) {
1799 if (segments_count > 0 && index >= segments_count) {
1800 stream->segment_index = segments_count;
1801 stream->segment_repeat_index = 0;
1802 GST_DEBUG ("Seek to after last segment");
1806 *final_ts = index * duration;
1809 stream->segment_repeat_index = repeat_index;
1810 stream->segment_index = index;
1816 gst_mpd_client2_calculate_time_difference (const GstDateTime * t1,
1817 const GstDateTime * t2)
1819 GDateTime *gdt1, *gdt2;
1822 g_assert (t1 != NULL && t2 != NULL);
1823 gdt1 = gst_date_time_to_g_date_time ((GstDateTime *) t1);
1824 gdt2 = gst_date_time_to_g_date_time ((GstDateTime *) t2);
1825 diff = g_date_time_difference (gdt2, gdt1);
1826 g_date_time_unref (gdt1);
1827 g_date_time_unref (gdt2);
1828 return diff * GST_USECOND;
1832 gst_mpd_client2_add_time_difference (GstDateTime * t1, GstClockTimeDiff diff)
1838 g_assert (t1 != NULL);
1839 gdt = gst_date_time_to_g_date_time (t1);
1840 g_assert (gdt != NULL);
1841 gdt2 = g_date_time_add (gdt, diff / GST_USECOND);
1842 g_assert (gdt2 != NULL);
1843 g_date_time_unref (gdt);
1844 rv = gst_date_time_new_from_g_date_time (gdt2);
1846 /* Don't g_date_time_unref(gdt2) because gst_date_time_new_from_g_date_time takes
1847 * ownership of the GDateTime pointer.
1854 gst_mpd_client2_get_last_fragment_timestamp_end (GstMPDClient2 * client,
1855 guint stream_idx, GstClockTime * ts)
1857 GstActiveStream *stream;
1859 GstMediaSegment *currentChunk;
1860 GstStreamPeriod *stream_period;
1862 GST_DEBUG ("Stream index: %i", stream_idx);
1863 stream = g_list_nth_data (client->active_streams, stream_idx);
1864 g_return_val_if_fail (stream != NULL, 0);
1866 if (!stream->segments) {
1867 stream_period = gst_mpd_client2_get_stream_period (client);
1868 *ts = stream_period->duration;
1870 segment_idx = gst_mpd_client2_get_segments_counts (client, stream) - 1;
1871 if (segment_idx >= stream->segments->len) {
1872 GST_WARNING ("Segment index %d is outside of segment list of length %d",
1873 segment_idx, stream->segments->len);
1876 currentChunk = g_ptr_array_index (stream->segments, segment_idx);
1878 if (currentChunk->repeat >= 0) {
1880 currentChunk->start + (currentChunk->duration * (1 +
1881 currentChunk->repeat)) -
1882 gst_mpd_client2_get_period_start_time (client);
1884 /* 5.3.9.6.1: negative repeat means repeat till the end of the
1885 * period, or the next update of the MPD (which I think is
1886 * implicit, as this will all get deleted/recreated), or the
1887 * start of the next segment, if any. */
1888 stream_period = gst_mpd_client2_get_stream_period (client);
1889 *ts = stream_period->duration;
1897 gst_mpd_client2_get_next_fragment_timestamp (GstMPDClient2 * client,
1898 guint stream_idx, GstClockTime * ts)
1900 GstActiveStream *stream;
1901 GstMediaSegment *currentChunk;
1903 GST_DEBUG ("Stream index: %i", stream_idx);
1904 stream = g_list_nth_data (client->active_streams, stream_idx);
1905 g_return_val_if_fail (stream != NULL, 0);
1907 if (stream->segments) {
1908 GST_DEBUG ("Looking for fragment sequence chunk %d / %d",
1909 stream->segment_index, stream->segments->len);
1910 if (stream->segment_index >= stream->segments->len)
1912 currentChunk = g_ptr_array_index (stream->segments, stream->segment_index);
1915 currentChunk->start +
1916 (currentChunk->duration * stream->segment_repeat_index) -
1917 gst_mpd_client2_get_period_start_time (client);
1919 GstClockTime duration =
1920 gst_mpd_client2_get_segment_duration (client, stream, NULL);
1921 guint segments_count = gst_mpd_client2_get_segments_counts (client, stream);
1923 g_return_val_if_fail (GST_MPD_MULT_SEGMENT_BASE_NODE
1924 (stream->cur_seg_template)->SegmentTimeline == NULL, FALSE);
1925 if (!GST_CLOCK_TIME_IS_VALID (duration) || (segments_count > 0
1926 && stream->segment_index >= segments_count)) {
1929 *ts = stream->segment_index * duration;
1936 gst_mpd_client2_get_stream_presentation_offset (GstMPDClient2 * client,
1939 GstActiveStream *stream = NULL;
1941 g_return_val_if_fail (client != NULL, 0);
1942 g_return_val_if_fail (client->active_streams != NULL, 0);
1943 stream = g_list_nth_data (client->active_streams, stream_idx);
1944 g_return_val_if_fail (stream != NULL, 0);
1946 return stream->presentationTimeOffset;
1950 gst_mpd_client2_get_period_start_time (GstMPDClient2 * client)
1952 GstStreamPeriod *stream_period = NULL;
1954 g_return_val_if_fail (client != NULL, 0);
1955 stream_period = gst_mpd_client2_get_stream_period (client);
1956 g_return_val_if_fail (stream_period != NULL, 0);
1958 return stream_period->start;
1962 * gst_mpd_client2_get_utc_timing_sources:
1963 * @client: #GstMPDClient2 to check for UTCTiming elements
1964 * @methods: A bit mask of #GstMPDUTCTimingType that specifies the methods
1966 * @selected_method: (nullable): The selected method
1967 * Returns: (transfer none): A NULL terminated array of URLs of servers
1968 * that use @selected_method to provide a realtime clock.
1970 * Searches the UTCTiming elements found in the manifest for an element
1971 * that uses one of the UTC timing methods specified in @selected_method.
1972 * If multiple UTCTiming elements are present that support one of the
1973 * methods specified in @selected_method, the first one is returned.
1978 gst_mpd_client2_get_utc_timing_sources (GstMPDClient2 * client,
1979 guint methods, GstMPDUTCTimingType * selected_method)
1983 g_return_val_if_fail (client != NULL, NULL);
1984 g_return_val_if_fail (client->mpd_root_node != NULL, NULL);
1985 for (list = g_list_first (client->mpd_root_node->UTCTimings); list;
1986 list = g_list_next (list)) {
1987 const GstMPDUTCTimingNode *node = (const GstMPDUTCTimingNode *) list->data;
1988 if (node->method & methods) {
1989 if (selected_method) {
1990 *selected_method = node->method;
2000 gst_mpd_client2_get_next_fragment (GstMPDClient2 * client,
2001 guint indexStream, GstMediaFragmentInfo * fragment)
2003 GstActiveStream *stream = NULL;
2004 GstMediaSegment *currentChunk;
2005 gchar *mediaURL = NULL;
2006 gchar *indexURL = NULL;
2007 GstUri *base_url, *frag_url;
2010 g_return_val_if_fail (client != NULL, FALSE);
2011 g_return_val_if_fail (client->active_streams != NULL, FALSE);
2012 stream = g_list_nth_data (client->active_streams, indexStream);
2013 g_return_val_if_fail (stream != NULL, FALSE);
2014 g_return_val_if_fail (stream->cur_representation != NULL, FALSE);
2016 if (stream->segments) {
2017 GST_DEBUG ("Looking for fragment sequence chunk %d / %d",
2018 stream->segment_index, stream->segments->len);
2019 if (stream->segment_index >= stream->segments->len)
2022 GstClockTime duration = gst_mpd_client2_get_segment_duration (client,
2024 guint segments_count = gst_mpd_client2_get_segments_counts (client, stream);
2026 g_return_val_if_fail (GST_MPD_MULT_SEGMENT_BASE_NODE
2027 (stream->cur_seg_template)->SegmentTimeline == NULL, FALSE);
2028 if (!GST_CLOCK_TIME_IS_VALID (duration) || (segments_count > 0
2029 && stream->segment_index >= segments_count)) {
2032 fragment->duration = duration;
2035 /* FIXME rework discont checking */
2036 /* fragment->discontinuity = segment_idx != currentChunk.number; */
2037 fragment->range_start = 0;
2038 fragment->range_end = -1;
2039 fragment->index_uri = NULL;
2040 fragment->index_range_start = 0;
2041 fragment->index_range_end = -1;
2043 if (stream->segments) {
2044 currentChunk = g_ptr_array_index (stream->segments, stream->segment_index);
2046 GST_DEBUG ("currentChunk->SegmentURL = %p", currentChunk->SegmentURL);
2047 if (currentChunk->SegmentURL != NULL) {
2049 g_strdup (gst_mpdparser_get_mediaURL (stream,
2050 currentChunk->SegmentURL));
2051 indexURL = g_strdup (currentChunk->SegmentURL->index);
2052 } else if (stream->cur_seg_template != NULL) {
2054 gst_mpdparser_build_URL_from_template (stream->cur_seg_template->
2055 media, stream->cur_representation->id,
2056 currentChunk->number + stream->segment_repeat_index,
2057 stream->cur_representation->bandwidth,
2058 currentChunk->scale_start +
2059 stream->segment_repeat_index * currentChunk->scale_duration);
2060 if (stream->cur_seg_template->index) {
2062 gst_mpdparser_build_URL_from_template (stream->cur_seg_template->
2063 index, stream->cur_representation->id,
2064 currentChunk->number + stream->segment_repeat_index,
2065 stream->cur_representation->bandwidth,
2066 currentChunk->scale_start +
2067 stream->segment_repeat_index * currentChunk->scale_duration);
2070 GST_DEBUG ("mediaURL = %s", mediaURL);
2071 GST_DEBUG ("indexURL = %s", indexURL);
2073 fragment->timestamp =
2074 currentChunk->start +
2075 stream->segment_repeat_index * currentChunk->duration -
2076 gst_mpd_client2_get_period_start_time (client);
2077 fragment->duration = currentChunk->duration;
2078 if (currentChunk->SegmentURL) {
2079 if (currentChunk->SegmentURL->mediaRange) {
2080 fragment->range_start =
2081 currentChunk->SegmentURL->mediaRange->first_byte_pos;
2082 fragment->range_end =
2083 currentChunk->SegmentURL->mediaRange->last_byte_pos;
2085 if (currentChunk->SegmentURL->indexRange) {
2086 fragment->index_range_start =
2087 currentChunk->SegmentURL->indexRange->first_byte_pos;
2088 fragment->index_range_end =
2089 currentChunk->SegmentURL->indexRange->last_byte_pos;
2093 if (stream->cur_seg_template != NULL) {
2095 gst_mpdparser_build_URL_from_template (stream->cur_seg_template->
2096 media, stream->cur_representation->id,
2097 stream->segment_index +
2098 GST_MPD_MULT_SEGMENT_BASE_NODE (stream->
2099 cur_seg_template)->startNumber,
2100 stream->cur_representation->bandwidth,
2101 stream->segment_index * fragment->duration);
2102 if (stream->cur_seg_template->index) {
2104 gst_mpdparser_build_URL_from_template (stream->cur_seg_template->
2105 index, stream->cur_representation->id,
2106 stream->segment_index +
2107 GST_MPD_MULT_SEGMENT_BASE_NODE (stream->
2108 cur_seg_template)->startNumber,
2109 stream->cur_representation->bandwidth,
2110 stream->segment_index * fragment->duration);
2116 GST_DEBUG ("mediaURL = %s", mediaURL);
2117 GST_DEBUG ("indexURL = %s", indexURL);
2119 fragment->timestamp = stream->segment_index * fragment->duration;
2122 base_url = gst_uri_from_string (stream->baseURL);
2123 frag_url = gst_uri_from_string_with_base (base_url, mediaURL);
2125 if (stream->queryURL) {
2126 frag_url = gst_uri_make_writable (frag_url);
2127 gst_uri_set_query_string (frag_url, stream->queryURL);
2129 fragment->uri = gst_uri_to_string (frag_url);
2130 gst_uri_unref (frag_url);
2132 if (indexURL != NULL) {
2133 frag_url = gst_uri_make_writable (gst_uri_from_string_with_base (base_url,
2135 gst_uri_set_query_string (frag_url, stream->queryURL);
2136 fragment->index_uri = gst_uri_to_string (frag_url);
2137 gst_uri_unref (frag_url);
2139 } else if (indexURL == NULL && (fragment->index_range_start
2140 || fragment->index_range_end != -1)) {
2141 /* index has no specific URL but has a range, we should only use this if
2142 * the media also has a range, otherwise we are serving some data twice
2143 * (in the media fragment and again in the index) */
2144 if (!(fragment->range_start || fragment->range_end != -1)) {
2145 GST_WARNING ("Ignoring index ranges because there isn't a media range "
2146 "and URIs would be the same");
2147 /* removing index information */
2148 fragment->index_range_start = 0;
2149 fragment->index_range_end = -1;
2153 gst_uri_unref (base_url);
2155 GST_DEBUG ("Loading chunk with URL %s", fragment->uri);
2161 gst_mpd_client2_has_next_segment (GstMPDClient2 * client,
2162 GstActiveStream * stream, gboolean forward)
2165 guint segments_count = gst_mpd_client2_get_segments_counts (client, stream);
2167 if (segments_count > 0 && stream->segments
2168 && stream->segment_index + 1 == segments_count) {
2169 GstMediaSegment *segment;
2171 segment = g_ptr_array_index (stream->segments, stream->segment_index);
2172 if (segment->repeat >= 0
2173 && stream->segment_repeat_index >= segment->repeat)
2175 } else if (segments_count > 0
2176 && stream->segment_index + 1 >= segments_count) {
2180 if (stream->segment_index < 0)
2188 gst_mpd_client2_advance_segment (GstMPDClient2 * client,
2189 GstActiveStream * stream, gboolean forward)
2191 GstMediaSegment *segment;
2192 GstFlowReturn ret = GST_FLOW_OK;
2193 guint segments_count = gst_mpd_client2_get_segments_counts (client, stream);
2195 GST_DEBUG ("Advancing segment. Current: %d / %d r:%d", stream->segment_index,
2196 segments_count, stream->segment_repeat_index);
2198 /* handle special cases first */
2200 if (segments_count > 0 && stream->segment_index >= segments_count) {
2205 if (stream->segments == NULL) {
2206 if (stream->segment_index < 0) {
2207 stream->segment_index = 0;
2209 stream->segment_index++;
2210 if (segments_count > 0 && stream->segment_index >= segments_count) {
2217 /* special case for when playback direction is reverted right at *
2218 * the end of the segment list */
2219 if (stream->segment_index < 0) {
2220 stream->segment_index = 0;
2224 if (stream->segments == NULL)
2225 stream->segment_index--;
2226 if (stream->segment_index < 0) {
2227 stream->segment_index = -1;
2231 if (stream->segments == NULL)
2234 /* special case for when playback direction is reverted right at *
2235 * the end of the segment list */
2236 if (stream->segment_index >= segments_count) {
2237 stream->segment_index = segments_count - 1;
2238 segment = g_ptr_array_index (stream->segments, stream->segment_index);
2239 if (segment->repeat >= 0) {
2240 stream->segment_repeat_index = segment->repeat;
2242 GstClockTime start = segment->start;
2244 gst_mpd_client2_get_segment_end_time (client, stream->segments,
2246 stream->segment_index);
2247 stream->segment_repeat_index =
2248 (guint) (end - start) / segment->duration;
2254 /* for the normal cases we can get the segment safely here */
2255 segment = g_ptr_array_index (stream->segments, stream->segment_index);
2257 if (segment->repeat >= 0 && stream->segment_repeat_index >= segment->repeat) {
2258 stream->segment_repeat_index = 0;
2259 stream->segment_index++;
2260 if (segments_count > 0 && stream->segment_index >= segments_count) {
2265 stream->segment_repeat_index++;
2268 if (stream->segment_repeat_index == 0) {
2269 stream->segment_index--;
2270 if (stream->segment_index < 0) {
2275 segment = g_ptr_array_index (stream->segments, stream->segment_index);
2276 /* negative repeats only seem to make sense at the end of a list,
2277 * so this one will probably not be. Needs some sanity checking
2278 * when loading the XML data. */
2279 if (segment->repeat >= 0) {
2280 stream->segment_repeat_index = segment->repeat;
2282 GstClockTime start = segment->start;
2284 gst_mpd_client2_get_segment_end_time (client, stream->segments,
2286 stream->segment_index);
2287 stream->segment_repeat_index =
2288 (guint) (end - start) / segment->duration;
2291 stream->segment_repeat_index--;
2296 GST_DEBUG ("Advanced to segment: %d / %d r:%d (ret: %s)",
2297 stream->segment_index, segments_count,
2298 stream->segment_repeat_index, gst_flow_get_name (ret));
2303 gst_mpd_client2_get_next_header (GstMPDClient2 * client, gchar ** uri,
2304 guint stream_idx, gint64 * range_start, gint64 * range_end)
2306 GstActiveStream *stream;
2307 GstStreamPeriod *stream_period;
2309 stream = gst_mpd_client2_get_active_stream_by_index (client, stream_idx);
2310 g_return_val_if_fail (stream != NULL, FALSE);
2311 g_return_val_if_fail (stream->cur_representation != NULL, FALSE);
2312 stream_period = gst_mpd_client2_get_stream_period (client);
2313 g_return_val_if_fail (stream_period != NULL, FALSE);
2314 g_return_val_if_fail (stream_period->period != NULL, FALSE);
2319 GST_DEBUG ("Looking for current representation header");
2321 if (stream->cur_segment_base) {
2322 if (stream->cur_segment_base->Initialization) {
2324 g_strdup (gst_mpdparser_get_initializationURL (stream,
2325 stream->cur_segment_base->Initialization));
2326 if (stream->cur_segment_base->Initialization->range) {
2328 stream->cur_segment_base->Initialization->range->first_byte_pos;
2330 stream->cur_segment_base->Initialization->range->last_byte_pos;
2332 } else if (stream->cur_segment_base->indexRange) {
2334 g_strdup (gst_mpdparser_get_initializationURL (stream,
2335 stream->cur_segment_base->Initialization));
2337 *range_end = stream->cur_segment_base->indexRange->first_byte_pos - 1;
2339 } else if (stream->cur_seg_template
2340 && stream->cur_seg_template->initialization) {
2342 gst_mpdparser_build_URL_from_template (stream->cur_seg_template->
2343 initialization, stream->cur_representation->id, 0,
2344 stream->cur_representation->bandwidth, 0);
2347 return *uri == NULL ? FALSE : TRUE;
2351 gst_mpd_client2_get_next_header_index (GstMPDClient2 * client, gchar ** uri,
2352 guint stream_idx, gint64 * range_start, gint64 * range_end)
2354 GstActiveStream *stream;
2355 GstStreamPeriod *stream_period;
2357 stream = gst_mpd_client2_get_active_stream_by_index (client, stream_idx);
2358 g_return_val_if_fail (stream != NULL, FALSE);
2359 g_return_val_if_fail (stream->cur_representation != NULL, FALSE);
2360 stream_period = gst_mpd_client2_get_stream_period (client);
2361 g_return_val_if_fail (stream_period != NULL, FALSE);
2362 g_return_val_if_fail (stream_period->period != NULL, FALSE);
2367 GST_DEBUG ("Looking for current representation index");
2369 if (stream->cur_segment_base && stream->cur_segment_base->indexRange) {
2371 g_strdup (gst_mpdparser_get_initializationURL (stream,
2372 stream->cur_segment_base->RepresentationIndex));
2373 *range_start = stream->cur_segment_base->indexRange->first_byte_pos;
2374 *range_end = stream->cur_segment_base->indexRange->last_byte_pos;
2375 } else if (stream->cur_seg_template && stream->cur_seg_template->index) {
2377 gst_mpdparser_build_URL_from_template (stream->cur_seg_template->index,
2378 stream->cur_representation->id, 0,
2379 stream->cur_representation->bandwidth, 0);
2382 return *uri == NULL ? FALSE : TRUE;
2386 gst_mpd_client2_get_next_fragment_duration (GstMPDClient2 * client,
2387 GstActiveStream * stream)
2389 GstMediaSegment *media_segment = NULL;
2392 g_return_val_if_fail (stream != NULL, 0);
2394 seg_idx = stream->segment_index;
2396 if (stream->segments) {
2397 if (seg_idx < stream->segments->len && seg_idx >= 0)
2398 media_segment = g_ptr_array_index (stream->segments, seg_idx);
2400 return media_segment == NULL ? 0 : media_segment->duration;
2402 GstClockTime duration =
2403 gst_mpd_client2_get_segment_duration (client, stream, NULL);
2404 guint segments_count = gst_mpd_client2_get_segments_counts (client, stream);
2406 g_return_val_if_fail (GST_MPD_MULT_SEGMENT_BASE_NODE
2407 (stream->cur_seg_template)->SegmentTimeline == NULL, 0);
2409 if (!GST_CLOCK_TIME_IS_VALID (duration) || (segments_count > 0
2410 && seg_idx >= segments_count)) {
2418 gst_mpd_client2_get_media_presentation_duration (GstMPDClient2 * client)
2420 GstClockTime duration;
2422 g_return_val_if_fail (client != NULL, GST_CLOCK_TIME_NONE);
2424 if (client->mpd_root_node->mediaPresentationDuration != -1) {
2425 duration = client->mpd_root_node->mediaPresentationDuration * GST_MSECOND;
2427 /* We can only get the duration for on-demand streams */
2428 duration = GST_CLOCK_TIME_NONE;
2435 gst_mpd_client2_set_period_id (GstMPDClient2 * client, const gchar * period_id)
2437 GstStreamPeriod *next_stream_period;
2438 gboolean ret = FALSE;
2442 g_return_val_if_fail (client != NULL, FALSE);
2443 g_return_val_if_fail (client->periods != NULL, FALSE);
2444 g_return_val_if_fail (period_id != NULL, FALSE);
2446 if (!gst_mpd_client2_setup_media_presentation (client, GST_CLOCK_TIME_NONE,
2450 for (period_idx = 0, iter = client->periods; iter;
2451 period_idx++, iter = g_list_next (iter)) {
2452 next_stream_period = iter->data;
2454 if (next_stream_period->period->id
2455 && strcmp (next_stream_period->period->id, period_id) == 0) {
2457 client->period_idx = period_idx;
2466 gst_mpd_client2_set_period_index (GstMPDClient2 * client, guint period_idx)
2468 GstStreamPeriod *next_stream_period;
2469 gboolean ret = FALSE;
2471 g_return_val_if_fail (client != NULL, FALSE);
2472 g_return_val_if_fail (client->periods != NULL, FALSE);
2474 if (!gst_mpd_client2_setup_media_presentation (client, -1, period_idx, NULL))
2477 next_stream_period = g_list_nth_data (client->periods, period_idx);
2478 if (next_stream_period != NULL) {
2479 client->period_idx = period_idx;
2487 gst_mpd_client2_get_period_index (GstMPDClient2 * client)
2491 g_return_val_if_fail (client != NULL, 0);
2492 period_idx = client->period_idx;
2498 gst_mpd_client2_get_period_id (GstMPDClient2 * client)
2500 GstStreamPeriod *period;
2501 gchar *period_id = NULL;
2503 g_return_val_if_fail (client != NULL, 0);
2504 period = g_list_nth_data (client->periods, client->period_idx);
2505 if (period && period->period)
2506 period_id = period->period->id;
2512 gst_mpd_client2_has_next_period (GstMPDClient2 * client)
2514 GList *next_stream_period;
2515 g_return_val_if_fail (client != NULL, FALSE);
2516 g_return_val_if_fail (client->periods != NULL, FALSE);
2518 if (!gst_mpd_client2_setup_media_presentation (client, GST_CLOCK_TIME_NONE,
2519 client->period_idx + 1, NULL))
2522 next_stream_period =
2523 g_list_nth_data (client->periods, client->period_idx + 1);
2524 return next_stream_period != NULL;
2528 gst_mpd_client2_has_previous_period (GstMPDClient2 * client)
2530 GList *next_stream_period;
2531 g_return_val_if_fail (client != NULL, FALSE);
2532 g_return_val_if_fail (client->periods != NULL, FALSE);
2534 if (!gst_mpd_client2_setup_media_presentation (client, GST_CLOCK_TIME_NONE,
2535 client->period_idx - 1, NULL))
2538 next_stream_period =
2539 g_list_nth_data (client->periods, client->period_idx - 1);
2541 return next_stream_period != NULL;
2545 gst_mpd_client2_get_rep_idx_with_min_bandwidth (GList * Representations)
2547 GList *list = NULL, *lowest = NULL;
2548 GstMPDRepresentationNode *rep = NULL;
2549 gint lowest_bandwidth = -1;
2551 if (Representations == NULL)
2554 for (list = g_list_first (Representations); list; list = g_list_next (list)) {
2555 rep = (GstMPDRepresentationNode *) list->data;
2556 if (rep && (!lowest || rep->bandwidth < lowest_bandwidth)) {
2558 lowest_bandwidth = rep->bandwidth;
2562 return lowest ? g_list_position (Representations, lowest) : -1;
2566 gst_mpd_client2_get_rep_idx_with_max_bandwidth (GList * Representations,
2567 gint64 max_bandwidth, gint max_video_width, gint max_video_height, gint
2568 max_video_framerate_n, gint max_video_framerate_d)
2570 GList *list = NULL, *best = NULL;
2571 GstMPDRepresentationNode *representation;
2572 gint best_bandwidth = 0;
2574 GST_DEBUG ("max_bandwidth = %" G_GINT64_FORMAT, max_bandwidth);
2576 if (Representations == NULL)
2579 if (max_bandwidth <= 0) /* 0 => get lowest representation available */
2580 return gst_mpd_client2_get_rep_idx_with_min_bandwidth (Representations);
2582 for (list = g_list_first (Representations); list; list = g_list_next (list)) {
2583 GstXMLFrameRate *framerate = NULL;
2585 representation = (GstMPDRepresentationNode *) list->data;
2587 /* FIXME: Really? */
2588 if (!representation)
2591 framerate = GST_MPD_REPRESENTATION_BASE_NODE (representation)->frameRate;
2594 GST_MPD_REPRESENTATION_BASE_NODE (representation)->maxFrameRate;
2596 if (framerate && max_video_framerate_n > 0) {
2597 if (gst_util_fraction_compare (framerate->num, framerate->den,
2598 max_video_framerate_n, max_video_framerate_d) > 0)
2602 if (max_video_width > 0
2603 && GST_MPD_REPRESENTATION_BASE_NODE (representation)->width >
2606 if (max_video_height > 0
2607 && GST_MPD_REPRESENTATION_BASE_NODE (representation)->height >
2611 if (representation->bandwidth <= max_bandwidth &&
2612 representation->bandwidth > best_bandwidth) {
2614 best_bandwidth = representation->bandwidth;
2618 return best ? g_list_position (Representations, best) : -1;
2622 gst_mpd_client2_seek_to_first_segment (GstMPDClient2 * client)
2626 g_return_if_fail (client != NULL);
2627 g_return_if_fail (client->active_streams != NULL);
2629 for (list = g_list_first (client->active_streams); list;
2630 list = g_list_next (list)) {
2631 GstActiveStream *stream = (GstActiveStream *) list->data;
2633 stream->segment_index = 0;
2634 stream->segment_repeat_index = 0;
2640 gst_mpd_client2_get_segments_counts (GstMPDClient2 * client,
2641 GstActiveStream * stream)
2643 GstStreamPeriod *stream_period;
2645 g_return_val_if_fail (stream != NULL, 0);
2647 if (stream->segments)
2648 return stream->segments->len;
2649 g_return_val_if_fail (GST_MPD_MULT_SEGMENT_BASE_NODE
2650 (stream->cur_seg_template)->SegmentTimeline == NULL, 0);
2652 stream_period = gst_mpd_client2_get_stream_period (client);
2653 if (stream_period->duration != -1)
2654 return gst_util_uint64_scale_ceil (stream_period->duration, 1,
2655 gst_mpd_client2_get_segment_duration (client, stream, NULL));
2661 gst_mpd_client2_is_live (GstMPDClient2 * client)
2663 g_return_val_if_fail (client != NULL, FALSE);
2664 g_return_val_if_fail (client->mpd_root_node != NULL, FALSE);
2666 return client->mpd_root_node->type == GST_MPD_FILE_TYPE_DYNAMIC;
2670 gst_mpd_client2_get_nb_active_stream (GstMPDClient2 * client)
2672 g_return_val_if_fail (client != NULL, 0);
2674 return g_list_length (client->active_streams);
2678 gst_mpd_client2_get_nb_adaptationSet (GstMPDClient2 * client)
2680 GstStreamPeriod *stream_period;
2682 stream_period = gst_mpd_client2_get_stream_period (client);
2683 g_return_val_if_fail (stream_period != NULL, 0);
2684 g_return_val_if_fail (stream_period->period != NULL, 0);
2686 return g_list_length (stream_period->period->AdaptationSets);
2690 gst_mpd_client2_get_active_stream_by_index (GstMPDClient2 * client,
2693 g_return_val_if_fail (client != NULL, NULL);
2694 g_return_val_if_fail (client->active_streams != NULL, NULL);
2696 return g_list_nth_data (client->active_streams, stream_idx);
2700 gst_mpd_client2_active_stream_contains_subtitles (GstActiveStream * stream)
2702 const gchar *mimeType;
2703 const gchar *adapt_set_codecs;
2704 const gchar *rep_codecs;
2707 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_representation)->mimeType;
2710 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->mimeType;
2712 if (g_strcmp0 (mimeType, "application/ttml+xml") == 0 ||
2713 g_strcmp0 (mimeType, "application/x-subtitle-vtt") == 0 ||
2714 g_strcmp0 (mimeType, "text/vtt") == 0)
2718 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->codecs;
2720 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_representation)->codecs;
2722 if (adapt_set_codecs) {
2723 if (g_str_has_prefix (adapt_set_codecs, "stpp"))
2725 if (g_str_has_prefix (adapt_set_codecs, "wvtt"))
2729 if (g_str_has_prefix (rep_codecs, "stpp"))
2731 if (g_str_has_prefix (rep_codecs, "wvtt"))
2739 gst_mpd_client2_get_codec_caps (GstActiveStream * stream)
2741 GstCaps *ret = NULL;
2743 GstMPDAdaptationSetNode *adapt_set = stream->cur_adapt_set;
2745 if (adapt_set == NULL) {
2746 GST_WARNING ("No adaptation set => No caps");
2749 /* The adaptation set may already have caps, in which case it is the largest
2750 * set of possible caps of all representations (representations must have properties
2751 * that are smaller than the adaptation set) */
2753 if (adapt_set->parent_instance.caps) {
2754 ret = gst_caps_copy (adapt_set->parent_instance.caps);
2755 GST_DEBUG ("Adaptation set caps %" GST_PTR_FORMAT, ret);
2759 /* Iterate over the current adaptation set representation */
2760 for (iter = stream->cur_adapt_set->Representations; iter; iter = iter->next) {
2761 GstMPDRepresentationBaseNode *rep =
2762 (GstMPDRepresentationBaseNode *) iter->data;
2765 GST_DEBUG ("Adding representation caps %" GST_PTR_FORMAT, rep->caps);
2767 ret = gst_caps_merge (ret, gst_caps_ref (rep->caps));
2769 ret = gst_caps_copy (rep->caps);
2773 GST_DEBUG ("Merged caps %" GST_PTR_FORMAT, ret);
2778 gst_mpd_client2_get_stream_caps (GstActiveStream * stream)
2780 const gchar *mimeType, *caps_string;
2781 GstCaps *ret = NULL;
2783 if (stream == NULL || stream->cur_adapt_set == NULL
2784 || stream->cur_representation == NULL)
2788 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_representation)->mimeType;
2789 if (mimeType == NULL) {
2791 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->mimeType;
2794 caps_string = gst_mpd_helper_mimetype_to_caps (mimeType);
2796 if ((g_strcmp0 (caps_string, "application/mp4") == 0)
2797 && gst_mpd_client2_active_stream_contains_subtitles (stream))
2798 caps_string = "video/quicktime";
2801 ret = gst_caps_from_string (caps_string);
2807 gst_mpd_client2_get_bitstream_switching_flag (GstActiveStream * stream)
2809 if (stream == NULL || stream->cur_adapt_set == NULL)
2812 return stream->cur_adapt_set->bitstreamSwitching;
2816 gst_mpd_client2_get_video_stream_width (GstActiveStream * stream)
2820 if (stream == NULL || stream->cur_adapt_set == NULL
2821 || stream->cur_representation == NULL)
2824 width = GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_representation)->width;
2826 width = GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->width;
2833 gst_mpd_client2_get_video_stream_height (GstActiveStream * stream)
2837 if (stream == NULL || stream->cur_adapt_set == NULL
2838 || stream->cur_representation == NULL)
2842 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_representation)->height;
2844 height = GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->height;
2851 gst_mpd_client2_get_video_stream_framerate (GstActiveStream * stream,
2852 gint * fps_num, gint * fps_den)
2857 if (stream->cur_adapt_set &&
2858 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->frameRate !=
2861 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->
2864 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->
2869 if (stream->cur_adapt_set &&
2870 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->maxFrameRate !=
2873 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->
2876 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->
2881 if (stream->cur_representation &&
2882 GST_MPD_REPRESENTATION_BASE_NODE (stream->
2883 cur_representation)->frameRate != NULL) {
2885 GST_MPD_REPRESENTATION_BASE_NODE (stream->
2886 cur_representation)->frameRate->num;
2888 GST_MPD_REPRESENTATION_BASE_NODE (stream->
2889 cur_representation)->frameRate->den;
2893 if (stream->cur_representation &&
2894 GST_MPD_REPRESENTATION_BASE_NODE (stream->
2895 cur_representation)->maxFrameRate != NULL) {
2897 GST_MPD_REPRESENTATION_BASE_NODE (stream->
2898 cur_representation)->maxFrameRate->num;
2900 GST_MPD_REPRESENTATION_BASE_NODE (stream->
2901 cur_representation)->maxFrameRate->den;
2909 gst_mpd_client2_get_audio_stream_rate (GstActiveStream * stream)
2913 if (stream == NULL || stream->cur_adapt_set == NULL
2914 || stream->cur_representation == NULL)
2918 GST_MPD_REPRESENTATION_BASE_NODE (stream->
2919 cur_representation)->audioSamplingRate;
2922 GST_MPD_REPRESENTATION_BASE_NODE (stream->
2923 cur_adapt_set)->audioSamplingRate;
2926 return rate ? atoi (rate) : 0;
2930 gst_mpd_client2_get_audio_stream_num_channels (GstActiveStream * stream)
2932 if (stream == NULL || stream->cur_adapt_set == NULL
2933 || stream->cur_representation == NULL)
2935 /* TODO: here we have to parse the AudioChannelConfiguration descriptors */
2940 gst_mpd_client2_get_list_and_nb_of_audio_language (GstMPDClient2 * client,
2943 GstStreamPeriod *stream_period;
2944 GstMPDAdaptationSetNode *adapt_set;
2945 GList *adaptation_sets, *list;
2946 const gchar *this_mimeType = "audio";
2947 gchar *mimeType = NULL;
2948 guint nb_adaptation_set = 0;
2950 stream_period = gst_mpd_client2_get_stream_period (client);
2951 g_return_val_if_fail (stream_period != NULL, 0);
2952 g_return_val_if_fail (stream_period->period != NULL, 0);
2955 gst_mpd_client2_get_adaptation_sets_for_period (client, stream_period);
2956 for (list = adaptation_sets; list; list = g_list_next (list)) {
2957 adapt_set = (GstMPDAdaptationSetNode *) list->data;
2958 if (adapt_set && adapt_set->lang) {
2959 gchar *this_lang = adapt_set->lang;
2960 GstMPDRepresentationNode *rep;
2962 gst_mpd_client2_get_lowest_representation
2963 (adapt_set->Representations);
2965 if (GST_MPD_REPRESENTATION_BASE_NODE (rep))
2966 mimeType = GST_MPD_REPRESENTATION_BASE_NODE (rep)->mimeType;
2967 if (!mimeType && GST_MPD_REPRESENTATION_BASE_NODE (adapt_set)) {
2968 mimeType = GST_MPD_REPRESENTATION_BASE_NODE (adapt_set)->mimeType;
2971 if (gst_mpd_helper_strncmp_ext (mimeType, this_mimeType) == 0) {
2972 nb_adaptation_set++;
2973 *lang = g_list_append (*lang, this_lang);
2978 return nb_adaptation_set;
2983 gst_mpd_client2_get_next_segment_availability_start_time (GstMPDClient2 *
2984 client, GstActiveStream * stream)
2986 GstDateTime *availability_start_time, *rv;
2988 GstMediaSegment *segment;
2989 GstClockTime segmentEndTime;
2990 const GstStreamPeriod *stream_period;
2991 GstClockTime period_start = 0;
2993 g_return_val_if_fail (client != NULL, NULL);
2994 g_return_val_if_fail (stream != NULL, NULL);
2996 stream_period = gst_mpd_client2_get_stream_period (client);
2997 if (stream_period && stream_period->period) {
2998 period_start = stream_period->start;
3001 seg_idx = stream->segment_index;
3003 if (stream->segments && seg_idx < stream->segments->len) {
3004 segment = g_ptr_array_index (stream->segments, seg_idx);
3006 if (segment->repeat >= 0) {
3007 segmentEndTime = segment->start + (stream->segment_repeat_index + 1) *
3009 } else if (seg_idx < stream->segments->len - 1) {
3010 const GstMediaSegment *next_segment =
3011 g_ptr_array_index (stream->segments, seg_idx + 1);
3012 segmentEndTime = next_segment->start;
3014 g_return_val_if_fail (stream_period != NULL, NULL);
3015 segmentEndTime = period_start + stream_period->duration;
3018 GstClockTime seg_duration;
3019 seg_duration = gst_mpd_client2_get_segment_duration (client, stream, NULL);
3020 if (seg_duration == 0)
3022 segmentEndTime = period_start + (1 + seg_idx) * seg_duration;
3025 availability_start_time =
3026 gst_mpd_client2_get_availability_start_time (client);
3027 if (availability_start_time == NULL) {
3028 GST_WARNING_OBJECT (client, "Failed to get availability_start_time");
3032 rv = gst_mpd_client2_add_time_difference (availability_start_time,
3034 gst_date_time_unref (availability_start_time);
3036 GST_WARNING_OBJECT (client, "Failed to offset availability_start_time");
3044 gst_mpd_client2_seek_to_time (GstMPDClient2 * client, GDateTime * time)
3047 GTimeSpan ts_microseconds;
3049 gboolean ret = TRUE;
3052 g_return_val_if_fail (gst_mpd_client2_is_live (client), FALSE);
3053 g_return_val_if_fail (client->mpd_root_node->availabilityStartTime != NULL,
3057 gst_date_time_to_g_date_time (client->mpd_root_node->
3058 availabilityStartTime);
3060 ts_microseconds = g_date_time_difference (time, start);
3061 g_date_time_unref (start);
3063 /* Clamp to availability start time, otherwise calculations wrap around */
3064 if (ts_microseconds < 0)
3065 ts_microseconds = 0;
3067 ts = ts_microseconds * GST_USECOND;
3068 for (stream = client->active_streams; stream; stream = g_list_next (stream)) {
3070 ret & gst_mpd_client2_stream_seek (client, stream->data, TRUE, 0, ts,
3077 gst_mpd_client2_has_isoff_ondemand_profile (GstMPDClient2 * client)
3079 return client->profile_isoff_ondemand;
3083 * gst_mpd_client2_parse_default_presentation_delay:
3084 * @client: #GstMPDClient2 that has a parsed manifest
3085 * @default_presentation_delay: A string that specifies a time period
3086 * in fragments (e.g. "5 f"), seconds ("12 s") or milliseconds
3088 * Returns: the parsed string in milliseconds
3093 gst_mpd_client2_parse_default_presentation_delay (GstMPDClient2 * client,
3094 const gchar * default_presentation_delay)
3097 char *endptr = NULL;
3099 g_return_val_if_fail (client != NULL, 0);
3100 g_return_val_if_fail (default_presentation_delay != NULL, 0);
3101 value = strtol (default_presentation_delay, &endptr, 10);
3102 if (endptr == default_presentation_delay || value == 0) {
3105 while (*endptr == ' ')
3107 if (*endptr == 's' || *endptr == 'S') {
3108 value *= 1000; /* convert to ms */
3109 } else if (*endptr == 'f' || *endptr == 'F') {
3110 gint64 segment_duration;
3111 g_assert (client->mpd_root_node != NULL);
3112 segment_duration = client->mpd_root_node->maxSegmentDuration;
3113 value *= segment_duration;
3114 } else if (*endptr != 'm' && *endptr != 'M') {
3115 GST_ERROR ("Unable to parse default presentation delay: %s",
3116 default_presentation_delay);
3123 gst_mpd_client2_get_maximum_segment_duration (GstMPDClient2 * client)
3125 GstClockTime ret = GST_CLOCK_TIME_NONE, dur;
3128 g_return_val_if_fail (client != NULL, GST_CLOCK_TIME_NONE);
3129 g_return_val_if_fail (client->mpd_root_node != NULL, GST_CLOCK_TIME_NONE);
3131 if (client->mpd_root_node->maxSegmentDuration != GST_MPD_DURATION_NONE) {
3132 return client->mpd_root_node->maxSegmentDuration * GST_MSECOND;
3135 /* According to the DASH specification, if maxSegmentDuration is not present:
3136 "If not present, then the maximum Segment duration shall be the maximum
3137 duration of any Segment documented in this MPD"
3139 for (stream = client->active_streams; stream; stream = g_list_next (stream)) {
3140 dur = gst_mpd_client2_get_segment_duration (client, stream->data, NULL);
3141 if (dur != GST_CLOCK_TIME_NONE && (dur > ret || ret == GST_CLOCK_TIME_NONE)) {
3149 gst_mpd_client2_get_period_index_at_time (GstMPDClient2 * client,
3153 guint period_idx = G_MAXUINT;
3156 GstDateTime *avail_start =
3157 gst_mpd_client2_get_availability_start_time (client);
3158 GstStreamPeriod *stream_period;
3160 if (avail_start == NULL)
3163 time_offset = gst_mpd_client2_calculate_time_difference (avail_start, time);
3164 gst_date_time_unref (avail_start);
3166 if (time_offset < 0)
3169 if (!gst_mpd_client2_setup_media_presentation (client, time_offset, -1, NULL))
3172 for (idx = 0, iter = client->periods; iter; idx++, iter = g_list_next (iter)) {
3173 stream_period = iter->data;
3174 if (stream_period->start <= time_offset
3175 && (!GST_CLOCK_TIME_IS_VALID (stream_period->duration)
3176 || stream_period->start + stream_period->duration > time_offset)) {
3185 /* add or set node methods */
3188 gst_mpd_client2_set_root_node (GstMPDClient2 * client,
3189 const gchar * property_name, ...)
3192 g_return_val_if_fail (client != NULL, FALSE);
3194 if (!client->mpd_root_node)
3195 client->mpd_root_node = gst_mpd_root_node_new ();
3197 va_start (myargs, property_name);
3198 g_object_set_valist (G_OBJECT (client->mpd_root_node), property_name, myargs);
3205 gst_mpd_client2_add_baseurl_node (GstMPDClient2 * client,
3206 const gchar * property_name, ...)
3208 GstMPDBaseURLNode *baseurl_node = NULL;
3211 g_return_val_if_fail (client != NULL, FALSE);
3212 g_return_val_if_fail (client->mpd_root_node != NULL, FALSE);
3214 va_start (myargs, property_name);
3216 baseurl_node = gst_mpd_baseurl_node_new ();
3217 g_object_set_valist (G_OBJECT (baseurl_node), property_name, myargs);
3218 client->mpd_root_node->BaseURLs =
3219 g_list_append (client->mpd_root_node->BaseURLs, baseurl_node);
3225 /* returns a period id */
3227 gst_mpd_client2_set_period_node (GstMPDClient2 * client,
3228 gchar * period_id, const gchar * property_name, ...)
3230 GstMPDPeriodNode *period_node = NULL;
3233 g_return_val_if_fail (client != NULL, NULL);
3234 g_return_val_if_fail (client->mpd_root_node != NULL, NULL);
3237 GST_MPD_PERIOD_NODE (gst_mpd_client2_get_period_with_id
3238 (client->mpd_root_node->Periods, period_id));
3240 period_node = gst_mpd_period_node_new ();
3242 period_node->id = g_strdup (period_id);
3245 _generate_new_string_id (client->mpd_root_node->Periods,
3246 "period_%.2d", gst_mpd_client2_get_period_with_id);
3247 client->mpd_root_node->Periods =
3248 g_list_append (client->mpd_root_node->Periods, period_node);
3251 va_start (myargs, property_name);
3252 g_object_set_valist (G_OBJECT (period_node), property_name, myargs);
3255 return period_node->id;
3258 /* returns an adaptation set id */
3260 gst_mpd_client2_set_adaptation_set_node (GstMPDClient2 * client,
3261 gchar * period_id, guint adaptation_set_id, const gchar * property_name,
3264 GstMPDAdaptationSetNode *adap_node = NULL;
3265 GstMPDPeriodNode *period_node = NULL;
3268 g_return_val_if_fail (client != NULL, 0);
3269 g_return_val_if_fail (client->mpd_root_node != NULL, 0);
3272 GST_MPD_PERIOD_NODE (gst_mpd_client2_get_period_with_id
3273 (client->mpd_root_node->Periods, period_id));
3274 g_return_val_if_fail (period_node != NULL, 0);
3276 GST_MPD_ADAPTATION_SET_NODE (gst_mpd_client2_get_adaptation_set_with_id
3277 (period_node->AdaptationSets, adaptation_set_id));
3279 adap_node = gst_mpd_adaptation_set_node_new ();
3280 if (adaptation_set_id)
3281 adap_node->id = adaptation_set_id;
3284 _generate_new_id (period_node->AdaptationSets,
3285 gst_mpd_client2_get_adaptation_set_with_id);
3286 GST_DEBUG_OBJECT (client, "Add a new adaptation set with id %d",
3288 period_node->AdaptationSets =
3289 g_list_append (period_node->AdaptationSets, adap_node);
3292 va_start (myargs, property_name);
3293 g_object_set_valist (G_OBJECT (adap_node), property_name, myargs);
3296 return adap_node->id;
3299 /* returns a representation id */
3301 gst_mpd_client2_set_representation_node (GstMPDClient2 * client,
3302 gchar * period_id, guint adaptation_set_id, gchar * representation_id,
3303 const gchar * property_name, ...)
3305 GstMPDRepresentationNode *rep_node = NULL;
3306 GstMPDAdaptationSetNode *adap_set_node = NULL;
3307 GstMPDPeriodNode *period_node = NULL;
3310 g_return_val_if_fail (client != NULL, NULL);
3311 g_return_val_if_fail (client->mpd_root_node != NULL, NULL);
3314 GST_MPD_PERIOD_NODE (gst_mpd_client2_get_period_with_id
3315 (client->mpd_root_node->Periods, period_id));
3317 GST_MPD_ADAPTATION_SET_NODE (gst_mpd_client2_get_adaptation_set_with_id
3318 (period_node->AdaptationSets, adaptation_set_id));
3319 g_return_val_if_fail (adap_set_node != NULL, NULL);
3321 gst_mpd_client2_get_representation_with_id
3322 (adap_set_node->Representations, representation_id);
3324 rep_node = gst_mpd_representation_node_new ();
3325 if (representation_id)
3326 rep_node->id = g_strdup (representation_id);
3329 _generate_new_string_id (adap_set_node->Representations,
3330 "representation_%.2d",
3331 gst_mpd_client2_get_representation_with_id_filter);
3332 GST_DEBUG_OBJECT (client, "Add a new representation with id %s",
3334 adap_set_node->Representations =
3335 g_list_append (adap_set_node->Representations, rep_node);
3338 va_start (myargs, property_name);
3339 g_object_set_valist (G_OBJECT (rep_node), property_name, myargs);
3342 return rep_node->id;
3345 /* add/set a segment list node */
3347 gst_mpd_client2_set_segment_list (GstMPDClient2 * client,
3348 gchar * period_id, guint adap_set_id, gchar * rep_id,
3349 const gchar * property_name, ...)
3351 GstMPDRepresentationNode *representation = NULL;
3352 GstMPDAdaptationSetNode *adaptation_set = NULL;
3353 GstMPDPeriodNode *period = NULL;
3356 g_return_val_if_fail (client != NULL, FALSE);
3357 g_return_val_if_fail (client->mpd_root_node != NULL, FALSE);
3360 GST_MPD_PERIOD_NODE (gst_mpd_client2_get_period_with_id
3361 (client->mpd_root_node->Periods, period_id));
3363 GST_MPD_ADAPTATION_SET_NODE (gst_mpd_client2_get_adaptation_set_with_id
3364 (period->AdaptationSets, adap_set_id));
3365 g_return_val_if_fail (adaptation_set != NULL, FALSE);
3368 gst_mpd_client2_get_representation_with_id
3369 (adaptation_set->Representations, rep_id);
3370 if (!representation->SegmentList) {
3371 representation->SegmentList = gst_mpd_segment_list_node_new ();
3374 va_start (myargs, property_name);
3375 g_object_set_valist (G_OBJECT (representation->SegmentList), property_name,
3382 /* add/set a segment template node */
3384 gst_mpd_client2_set_segment_template (GstMPDClient2 * client,
3385 gchar * period_id, guint adap_set_id, gchar * rep_id,
3386 const gchar * property_name, ...)
3388 GstMPDRepresentationNode *representation = NULL;
3389 GstMPDAdaptationSetNode *adaptation_set = NULL;
3390 GstMPDPeriodNode *period = NULL;
3393 g_return_val_if_fail (client != NULL, FALSE);
3394 g_return_val_if_fail (client->mpd_root_node != NULL, FALSE);
3397 GST_MPD_PERIOD_NODE (gst_mpd_client2_get_period_with_id
3398 (client->mpd_root_node->Periods, period_id));
3400 GST_MPD_ADAPTATION_SET_NODE (gst_mpd_client2_get_adaptation_set_with_id
3401 (period->AdaptationSets, adap_set_id));
3402 g_return_val_if_fail (adaptation_set != NULL, FALSE);
3405 gst_mpd_client2_get_representation_with_id
3406 (adaptation_set->Representations, rep_id);
3407 if (!representation->SegmentTemplate) {
3408 representation->SegmentTemplate = gst_mpd_segment_template_node_new ();
3411 va_start (myargs, property_name);
3412 g_object_set_valist (G_OBJECT (representation->SegmentTemplate),
3413 property_name, myargs);
3419 /* add a segmentURL node with to a SegmentList node */
3421 gst_mpd_client2_add_segment_url (GstMPDClient2 * client,
3422 gchar * period_id, guint adap_set_id, gchar * rep_id,
3423 const gchar * property_name, ...)
3425 GstMPDRepresentationNode *representation = NULL;
3426 GstMPDAdaptationSetNode *adaptation_set = NULL;
3427 GstMPDPeriodNode *period = NULL;
3428 GstMPDSegmentURLNode *segment_url = NULL;
3429 guint64 media_presentation_duration = 0;
3432 g_return_val_if_fail (client != NULL, FALSE);
3433 g_return_val_if_fail (client->mpd_root_node != NULL, FALSE);
3436 GST_MPD_PERIOD_NODE (gst_mpd_client2_get_period_with_id
3437 (client->mpd_root_node->Periods, period_id));
3439 GST_MPD_ADAPTATION_SET_NODE (gst_mpd_client2_get_adaptation_set_with_id
3440 (period->AdaptationSets, adap_set_id));
3441 g_return_val_if_fail (adaptation_set != NULL, FALSE);
3444 gst_mpd_client2_get_representation_with_id
3445 (adaptation_set->Representations, rep_id);
3446 if (!representation->SegmentList) {
3447 representation->SegmentList = gst_mpd_segment_list_node_new ();
3450 segment_url = gst_mpd_segment_url_node_new ();
3452 va_start (myargs, property_name);
3453 g_object_set_valist (G_OBJECT (segment_url), property_name, myargs);
3456 gst_mpd_segment_list_node_add_segment (representation->SegmentList,
3459 /* Set the media presentation time according to the new segment duration added */
3460 g_object_get (client->mpd_root_node, "media-presentation-duration",
3461 &media_presentation_duration, NULL);
3462 media_presentation_duration +=
3463 GST_MPD_MULT_SEGMENT_BASE_NODE (representation->SegmentList)->duration;
3464 g_object_set (client->mpd_root_node, "media-presentation-duration",
3465 media_presentation_duration, NULL);