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 (GstMPDClient, gst_mpd_client, GST_TYPE_OBJECT);
31 static GstMPDSegmentBaseNode *gst_mpd_client_get_segment_base (GstMPDPeriodNode
32 * Period, GstMPDAdaptationSetNode * AdaptationSet,
33 GstMPDRepresentationNode * Representation);
34 static GstMPDSegmentListNode *gst_mpd_client_get_segment_list (GstMPDClient *
35 client, GstMPDPeriodNode * Period, GstMPDAdaptationSetNode * AdaptationSet,
36 GstMPDRepresentationNode * Representation);
38 static guint gst_mpd_client_get_segments_counts (GstMPDClient * client,
39 GstActiveStream * stream);
41 static GList *gst_mpd_client_fetch_external_periods (GstMPDClient * client,
42 GstMPDPeriodNode * period_node);
43 static GList *gst_mpd_client_fetch_external_adaptation_set (GstMPDClient *
44 client, GstMPDPeriodNode * period, GstMPDAdaptationSetNode * adapt_set);
46 static GstMPDRepresentationNode *gst_mpd_client_get_lowest_representation (GList
48 static GstStreamPeriod *gst_mpd_client_get_stream_period (GstMPDClient *
51 typedef GstMPDNode *(*MpdClientStringIDFilter) (GList * list, gchar * data);
52 typedef GstMPDNode *(*MpdClientIDFilter) (GList * list, guint data);
55 gst_mpd_client_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_client_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);
83 gst_mpd_client_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_NODE (representation);
98 _generate_new_string_id (GList * list, const gchar * tuple,
99 MpdClientStringIDFilter filter)
106 id = g_strdup_printf (tuple, i);
107 node = filter (list, id);
115 _generate_new_id (GList * list, MpdClientIDFilter filter)
120 node = filter (list, id);
127 static GstMPDRepresentationNode *
128 gst_mpd_client_get_lowest_representation (GList * Representations)
131 GstMPDRepresentationNode *rep = NULL;
132 GstMPDRepresentationNode *lowest = NULL;
134 if (Representations == NULL)
137 for (list = g_list_first (Representations); list; list = g_list_next (list)) {
138 rep = (GstMPDRepresentationNode *) list->data;
139 if (rep && (!lowest || rep->bandwidth < lowest->bandwidth)) {
148 static GstMPDRepresentationNode *
149 gst_mpdparser_get_highest_representation (GList * Representations)
153 if (Representations == NULL)
156 list = g_list_last (Representations);
158 return list ? (GstMPDRepresentationNode *) list->data : NULL;
161 static GstMPDRepresentationNode *
162 gst_mpdparser_get_representation_with_max_bandwidth (GList * Representations,
166 GstMPDRepresentationNode *representation, *best_rep = NULL;
168 if (Representations == NULL)
171 if (max_bandwidth <= 0) /* 0 => get highest representation available */
172 return gst_mpdparser_get_highest_representation (Representations);
174 for (list = g_list_first (Representations); list; list = g_list_next (list)) {
175 representation = (GstMPDRepresentationNode *) list->data;
176 if (representation && representation->bandwidth <= max_bandwidth) {
177 best_rep = representation;
185 static GstMPDSegmentListNode *
186 gst_mpd_client_fetch_external_segment_list (GstMPDClient * client,
187 GstMPDPeriodNode * Period,
188 GstMPDAdaptationSetNode * AdaptationSet,
189 GstMPDRepresentationNode * Representation,
190 GstMPDSegmentListNode * parent, GstMPDSegmentListNode * segment_list)
192 GstFragment *download;
193 GstBuffer *segment_list_buffer = NULL;
197 GstUri *base_uri, *uri;
200 GstMPDSegmentListNode *new_segment_list = NULL;
202 /* ISO/IEC 23009-1:2014 5.5.3 4)
203 * Remove nodes that resolve to nothing when resolving
205 if (strcmp (segment_list->xlink_href,
206 "urn:mpeg:dash:resolve-to-zero:2013") == 0) {
210 if (!client->downloader) {
214 /* Build absolute URI */
216 /* Get base URI at the MPD level */
218 gst_uri_from_string (client->mpd_base_uri ? client->
219 mpd_base_uri : client->mpd_uri);
221 /* combine a BaseURL at the MPD level with the current base url */
223 gst_mpd_helper_combine_urls (base_uri, client->mpd_root_node->BaseURLs,
226 /* combine a BaseURL at the Period level with the current base url */
228 gst_mpd_helper_combine_urls (base_uri, Period->BaseURLs, &query, 0);
231 /* combine a BaseURL at the AdaptationSet level with the current base url */
233 gst_mpd_helper_combine_urls (base_uri, AdaptationSet->BaseURLs, &query,
236 if (Representation) {
237 /* combine a BaseURL at the Representation level with the current base url */
239 gst_mpd_helper_combine_urls (base_uri, Representation->BaseURLs,
244 uri = gst_uri_from_string_with_base (base_uri, segment_list->xlink_href);
246 gst_uri_set_query_string (uri, query);
248 uri_string = gst_uri_to_string (uri);
249 gst_uri_unref (base_uri);
253 gst_uri_downloader_fetch_uri (client->downloader,
254 uri_string, client->mpd_uri, TRUE, FALSE, TRUE, &err);
258 GST_ERROR ("Failed to download external SegmentList node at '%s': %s",
259 segment_list->xlink_href, err->message);
260 g_clear_error (&err);
264 segment_list_buffer = gst_fragment_get_buffer (download);
265 g_object_unref (download);
267 gst_buffer_map (segment_list_buffer, &map, GST_MAP_READ);
270 gst_mpdparser_get_external_segment_list ((const gchar *) map.data,
273 if (segment_list_buffer) {
274 gst_buffer_unmap (segment_list_buffer, &map);
275 gst_buffer_unref (segment_list_buffer);
278 return new_segment_list;
281 static GstMPDSegmentBaseNode *
282 gst_mpd_client_get_segment_base (GstMPDPeriodNode * Period,
283 GstMPDAdaptationSetNode * AdaptationSet,
284 GstMPDRepresentationNode * Representation)
286 GstMPDSegmentBaseNode *SegmentBase = NULL;
288 if (Representation && Representation->SegmentBase) {
289 SegmentBase = Representation->SegmentBase;
290 } else if (AdaptationSet && AdaptationSet->SegmentBase) {
291 SegmentBase = AdaptationSet->SegmentBase;
292 } else if (Period && Period->SegmentBase) {
293 SegmentBase = Period->SegmentBase;
295 /* the SegmentBase element could be encoded also inside a SegmentList element */
296 if (SegmentBase == NULL) {
297 if (Representation && Representation->SegmentList
298 && GST_MPD_MULT_SEGMENT_BASE_NODE (Representation->SegmentList)
299 && GST_MPD_MULT_SEGMENT_BASE_NODE (Representation->
300 SegmentList)->SegmentBase) {
302 GST_MPD_MULT_SEGMENT_BASE_NODE (Representation->
303 SegmentList)->SegmentBase;
304 } else if (AdaptationSet && AdaptationSet->SegmentList
305 && GST_MPD_MULT_SEGMENT_BASE_NODE (AdaptationSet->SegmentList)
306 && GST_MPD_MULT_SEGMENT_BASE_NODE (AdaptationSet->
307 SegmentList)->SegmentBase) {
309 GST_MPD_MULT_SEGMENT_BASE_NODE (AdaptationSet->
310 SegmentList)->SegmentBase;
311 } else if (Period && Period->SegmentList
312 && GST_MPD_MULT_SEGMENT_BASE_NODE (Period->SegmentList)
313 && GST_MPD_MULT_SEGMENT_BASE_NODE (Period->SegmentList)->SegmentBase) {
315 GST_MPD_MULT_SEGMENT_BASE_NODE (Period->SegmentList)->SegmentBase;
322 static GstMPDSegmentListNode *
323 gst_mpd_client_get_segment_list (GstMPDClient * client,
324 GstMPDPeriodNode * Period, GstMPDAdaptationSetNode * AdaptationSet,
325 GstMPDRepresentationNode * Representation)
327 GstMPDSegmentListNode **SegmentList;
328 GstMPDSegmentListNode *ParentSegmentList = NULL;
330 if (Representation && Representation->SegmentList) {
331 SegmentList = &Representation->SegmentList;
332 ParentSegmentList = AdaptationSet->SegmentList;
333 } else if (AdaptationSet && AdaptationSet->SegmentList) {
334 SegmentList = &AdaptationSet->SegmentList;
335 ParentSegmentList = Period->SegmentList;
336 Representation = NULL;
338 Representation = NULL;
339 AdaptationSet = NULL;
340 SegmentList = &Period->SegmentList;
343 /* Resolve external segment list here. */
344 if (*SegmentList && (*SegmentList)->xlink_href) {
345 GstMPDSegmentListNode *new_segment_list;
347 /* TODO: Use SegmentList of parent if
348 * - Parent has its own SegmentList
349 * - Fail to get SegmentList from external xml
352 gst_mpd_client_fetch_external_segment_list (client, Period,
353 AdaptationSet, Representation, ParentSegmentList, *SegmentList);
355 gst_mpd_segment_list_node_free (*SegmentList);
356 *SegmentList = new_segment_list;
363 gst_mpd_client_get_segment_duration (GstMPDClient * client,
364 GstActiveStream * stream, guint64 * scale_dur)
366 GstStreamPeriod *stream_period;
367 GstMPDMultSegmentBaseNode *base = NULL;
368 GstClockTime duration = 0;
370 g_return_val_if_fail (stream != NULL, GST_CLOCK_TIME_NONE);
371 stream_period = gst_mpd_client_get_stream_period (client);
372 g_return_val_if_fail (stream_period != NULL, GST_CLOCK_TIME_NONE);
374 if (stream->cur_segment_list) {
375 base = GST_MPD_MULT_SEGMENT_BASE_NODE (stream->cur_segment_list);
376 } else if (stream->cur_seg_template) {
377 base = GST_MPD_MULT_SEGMENT_BASE_NODE (stream->cur_seg_template);
380 if (base == NULL || base->SegmentBase == NULL) {
381 /* this may happen when we have a single segment */
382 duration = stream_period->duration;
384 *scale_dur = duration;
386 /* duration is guint so this cannot overflow */
387 duration = base->duration * GST_SECOND;
389 *scale_dur = duration;
390 duration /= base->SegmentBase->timescale;
397 gst_mpd_client_active_streams_free (GstMPDClient * client)
399 if (client->active_streams) {
400 g_list_foreach (client->active_streams,
401 (GFunc) gst_mpdparser_free_active_stream, NULL);
402 g_list_free (client->active_streams);
403 client->active_streams = NULL;
408 gst_mpd_client_finalize (GObject * object)
410 GstMPDClient *client = GST_MPD_CLIENT (object);
412 if (client->mpd_root_node)
413 gst_mpd_root_node_free (client->mpd_root_node);
415 if (client->periods) {
416 g_list_free_full (client->periods,
417 (GDestroyNotify) gst_mpdparser_free_stream_period);
420 gst_mpd_client_active_streams_free (client);
422 g_free (client->mpd_uri);
423 client->mpd_uri = NULL;
424 g_free (client->mpd_base_uri);
425 client->mpd_base_uri = NULL;
427 if (client->downloader)
428 gst_object_unref (client->downloader);
429 client->downloader = NULL;
431 G_OBJECT_CLASS (gst_mpd_client_parent_class)->finalize (object);
435 gst_mpd_client_class_init (GstMPDClientClass * klass)
437 GObjectClass *object_class = G_OBJECT_CLASS (klass);
438 object_class->finalize = gst_mpd_client_finalize;
442 gst_mpd_client_init (GstMPDClient * client)
447 gst_mpd_client_new (void)
449 GST_DEBUG_CATEGORY_INIT (gst_dash_mpd_client_debug, "dashmpdclient", 0,
451 return g_object_new (GST_TYPE_MPD_CLIENT, NULL);
455 gst_mpd_client_new_static (void)
457 GstMPDClient *client = gst_mpd_client_new ();
459 client->mpd_root_node = gst_mpd_root_node_new ();
460 client->mpd_root_node->default_namespace =
461 g_strdup ("urn:mpeg:dash:schema:mpd:2011");
462 client->mpd_root_node->profiles =
463 g_strdup ("urn:mpeg:dash:profile:isoff-main:2011");
464 client->mpd_root_node->type = GST_MPD_FILE_TYPE_STATIC;
465 client->mpd_root_node->minBufferTime = 1500;
471 gst_mpd_client_free (GstMPDClient * client)
474 gst_object_unref (client);
478 gst_mpd_client_parse (GstMPDClient * client, const gchar * data, gint size)
480 gboolean ret = FALSE;
483 ret = gst_mpdparser_get_mpd_root_node (&client->mpd_root_node, data, size);
486 gst_mpd_client_check_profiles (client);
487 gst_mpd_client_fetch_on_load_external_resources (client);
495 gst_mpd_client_get_xml_content (GstMPDClient * client, gchar ** data,
498 gboolean ret = FALSE;
500 g_return_val_if_fail (client != NULL, ret);
501 g_return_val_if_fail (client->mpd_root_node != NULL, ret);
503 ret = gst_mpd_node_get_xml_buffer (GST_MPD_NODE (client->mpd_root_node),
510 gst_mpd_client_get_availability_start_time (GstMPDClient * client)
512 GstDateTime *start_time;
515 return (GstDateTime *) NULL;
517 start_time = client->mpd_root_node->availabilityStartTime;
519 gst_date_time_ref (start_time);
524 gst_mpd_client_set_uri_downloader (GstMPDClient * client,
525 GstUriDownloader * downloader)
527 if (client->downloader)
528 gst_object_unref (client->downloader);
529 client->downloader = gst_object_ref (downloader);
533 gst_mpd_client_check_profiles (GstMPDClient * client)
535 GST_DEBUG ("Profiles: %s",
536 client->mpd_root_node->profiles ? client->mpd_root_node->
537 profiles : "<none>");
539 if (!client->mpd_root_node->profiles)
542 if (g_strstr_len (client->mpd_root_node->profiles, -1,
543 "urn:mpeg:dash:profile:isoff-on-demand:2011")) {
544 client->profile_isoff_ondemand = TRUE;
545 GST_DEBUG ("Found ISOFF on demand profile (2011)");
550 gst_mpd_client_fetch_on_load_external_resources (GstMPDClient * client)
554 for (l = client->mpd_root_node->Periods; l; /* explicitly advanced below */ ) {
555 GstMPDPeriodNode *period = l->data;
558 if (period->xlink_href && period->actuate == GST_MPD_XLINK_ACTUATE_ON_LOAD) {
559 GList *new_periods, *prev, *next;
561 new_periods = gst_mpd_client_fetch_external_periods (client, period);
564 client->mpd_root_node->Periods =
565 g_list_delete_link (client->mpd_root_node->Periods, l);
566 gst_mpd_period_node_free (period);
569 /* Get new next node, we will insert before this */
573 next = client->mpd_root_node->Periods;
575 while (new_periods) {
576 client->mpd_root_node->Periods =
577 g_list_insert_before (client->mpd_root_node->Periods, next,
579 new_periods = g_list_delete_link (new_periods, new_periods);
583 /* Update our iterator to the first new period if any, or the next */
587 l = client->mpd_root_node->Periods;
592 if (period->SegmentList && period->SegmentList->xlink_href
593 && period->SegmentList->actuate == GST_MPD_XLINK_ACTUATE_ON_LOAD) {
594 GstMPDSegmentListNode *new_segment_list;
597 gst_mpd_client_fetch_external_segment_list (client, period, NULL,
598 NULL, NULL, period->SegmentList);
600 gst_mpd_segment_list_node_free (period->SegmentList);
601 period->SegmentList = new_segment_list;
604 for (m = period->AdaptationSets; m; /* explicitly advanced below */ ) {
605 GstMPDAdaptationSetNode *adapt_set = m->data;
608 if (adapt_set->xlink_href
609 && adapt_set->actuate == GST_MPD_XLINK_ACTUATE_ON_LOAD) {
610 GList *new_adapt_sets, *prev, *next;
613 gst_mpd_client_fetch_external_adaptation_set (client, period,
617 period->AdaptationSets = g_list_delete_link (period->AdaptationSets, m);
618 gst_mpd_adaptation_set_node_free (adapt_set);
621 /* Get new next node, we will insert before this */
625 next = period->AdaptationSets;
627 while (new_adapt_sets) {
628 period->AdaptationSets =
629 g_list_insert_before (period->AdaptationSets, next,
630 new_adapt_sets->data);
631 new_adapt_sets = g_list_delete_link (new_adapt_sets, new_adapt_sets);
635 /* Update our iterator to the first new adapt_set if any, or the next */
639 m = period->AdaptationSets;
644 if (adapt_set->SegmentList && adapt_set->SegmentList->xlink_href
645 && adapt_set->SegmentList->actuate == GST_MPD_XLINK_ACTUATE_ON_LOAD) {
646 GstMPDSegmentListNode *new_segment_list;
649 gst_mpd_client_fetch_external_segment_list (client, period,
650 adapt_set, NULL, period->SegmentList, adapt_set->SegmentList);
652 gst_mpd_segment_list_node_free (adapt_set->SegmentList);
653 adapt_set->SegmentList = new_segment_list;
656 for (n = adapt_set->Representations; n; n = n->next) {
657 GstMPDRepresentationNode *representation = n->data;
659 if (representation->SegmentList
660 && representation->SegmentList->xlink_href
661 && representation->SegmentList->actuate ==
662 GST_MPD_XLINK_ACTUATE_ON_LOAD) {
664 GstMPDSegmentListNode *new_segment_list;
667 gst_mpd_client_fetch_external_segment_list (client, period,
668 adapt_set, representation, adapt_set->SegmentList,
669 representation->SegmentList);
671 gst_mpd_segment_list_node_free (representation->SegmentList);
672 representation->SegmentList = new_segment_list;
685 static GstStreamPeriod *
686 gst_mpd_client_get_stream_period (GstMPDClient * client)
688 g_return_val_if_fail (client != NULL, NULL);
689 g_return_val_if_fail (client->periods != NULL, NULL);
691 return g_list_nth_data (client->periods, client->period_idx);
695 gst_mpd_client_get_baseURL (GstMPDClient * client, guint indexStream)
697 GstActiveStream *stream;
699 g_return_val_if_fail (client != NULL, NULL);
700 g_return_val_if_fail (client->active_streams != NULL, NULL);
701 stream = g_list_nth_data (client->active_streams, indexStream);
702 g_return_val_if_fail (stream != NULL, NULL);
704 return stream->baseURL;
707 /* select a stream and extract the baseURL (if present) */
709 gst_mpd_client_parse_baseURL (GstMPDClient * client, GstActiveStream * stream,
712 GstStreamPeriod *stream_period;
713 static const gchar empty[] = "";
717 g_return_val_if_fail (stream != NULL, g_strdup (empty));
718 stream_period = gst_mpd_client_get_stream_period (client);
719 g_return_val_if_fail (stream_period != NULL, g_strdup (empty));
720 g_return_val_if_fail (stream_period->period != NULL, g_strdup (empty));
722 /* NULLify query return before we start */
726 /* initialise base url */
728 gst_uri_from_string (client->mpd_base_uri ? client->
729 mpd_base_uri : client->mpd_uri);
731 /* combine a BaseURL at the MPD level with the current base url */
733 gst_mpd_helper_combine_urls (abs_url, client->mpd_root_node->BaseURLs,
734 query, stream->baseURL_idx);
736 /* combine a BaseURL at the Period level with the current base url */
738 gst_mpd_helper_combine_urls (abs_url, stream_period->period->BaseURLs,
739 query, stream->baseURL_idx);
741 GST_DEBUG ("Current adaptation set id %i (%s)", stream->cur_adapt_set->id,
742 stream->cur_adapt_set->contentType);
743 /* combine a BaseURL at the AdaptationSet level with the current base url */
745 gst_mpd_helper_combine_urls (abs_url, stream->cur_adapt_set->BaseURLs,
746 query, stream->baseURL_idx);
748 /* combine a BaseURL at the Representation level with the current base url */
750 gst_mpd_helper_combine_urls (abs_url,
751 stream->cur_representation->BaseURLs, query, stream->baseURL_idx);
753 ret = gst_uri_to_string (abs_url);
754 gst_uri_unref (abs_url);
760 gst_mpd_client_get_segment_end_time (GstMPDClient * client,
761 GPtrArray * segments, const GstMediaSegment * segment, gint index)
763 const GstStreamPeriod *stream_period;
766 if (segment->repeat >= 0)
767 return segment->start + (segment->repeat + 1) * segment->duration;
769 if (index < segments->len - 1) {
770 const GstMediaSegment *next_segment =
771 g_ptr_array_index (segments, index + 1);
772 end = next_segment->start;
774 stream_period = gst_mpd_client_get_stream_period (client);
775 end = stream_period->start + stream_period->duration;
781 gst_mpd_client_add_media_segment (GstActiveStream * stream,
782 GstMPDSegmentURLNode * url_node, guint number, gint repeat,
783 guint64 scale_start, guint64 scale_duration,
784 GstClockTime start, GstClockTime duration)
786 GstMediaSegment *media_segment;
788 g_return_val_if_fail (stream->segments != NULL, FALSE);
790 media_segment = g_slice_new0 (GstMediaSegment);
792 media_segment->SegmentURL = url_node;
793 media_segment->number = number;
794 media_segment->scale_start = scale_start;
795 media_segment->scale_duration = scale_duration;
796 media_segment->start = start;
797 media_segment->duration = duration;
798 media_segment->repeat = repeat;
800 g_ptr_array_add (stream->segments, media_segment);
801 GST_LOG ("Added new segment: number %d, repeat %d, "
802 "ts: %" GST_TIME_FORMAT ", dur: %"
803 GST_TIME_FORMAT, number, repeat,
804 GST_TIME_ARGS (start), GST_TIME_ARGS (duration));
810 gst_mpd_client_stream_update_presentation_time_offset (GstMPDClient * client,
811 GstActiveStream * stream)
813 GstMPDSegmentBaseNode *segbase = NULL;
815 /* Find the used segbase */
816 if (stream->cur_segment_list) {
818 GST_MPD_MULT_SEGMENT_BASE_NODE (stream->cur_segment_list)->SegmentBase;
819 } else if (stream->cur_seg_template) {
821 GST_MPD_MULT_SEGMENT_BASE_NODE (stream->cur_seg_template)->SegmentBase;
822 } else if (stream->cur_segment_base) {
823 segbase = stream->cur_segment_base;
827 /* Avoid overflows */
828 stream->presentationTimeOffset =
829 gst_util_uint64_scale (segbase->presentationTimeOffset, GST_SECOND,
832 stream->presentationTimeOffset = 0;
835 GST_LOG ("Setting stream's presentation time offset to %" GST_TIME_FORMAT,
836 GST_TIME_ARGS (stream->presentationTimeOffset));
840 gst_mpd_client_setup_representation (GstMPDClient * client,
841 GstActiveStream * stream, GstMPDRepresentationNode * representation)
843 GstStreamPeriod *stream_period;
845 GstClockTime PeriodStart, PeriodEnd, start_time, duration;
849 if (stream->cur_adapt_set == NULL) {
850 GST_WARNING ("No valid AdaptationSet node in the MPD file, aborting...");
854 rep_list = stream->cur_adapt_set->Representations;
855 stream->cur_representation = representation;
856 stream->representation_idx = g_list_index (rep_list, representation);
858 /* clean the old segment list, if any */
859 if (stream->segments) {
860 g_ptr_array_unref (stream->segments);
861 stream->segments = NULL;
864 stream_period = gst_mpd_client_get_stream_period (client);
865 g_return_val_if_fail (stream_period != NULL, FALSE);
866 g_return_val_if_fail (stream_period->period != NULL, FALSE);
868 PeriodStart = stream_period->start;
869 if (GST_CLOCK_TIME_IS_VALID (stream_period->duration))
870 PeriodEnd = stream_period->start + stream_period->duration;
872 PeriodEnd = GST_CLOCK_TIME_NONE;
874 GST_LOG ("Building segment list for Period from %" GST_TIME_FORMAT " to %"
875 GST_TIME_FORMAT, GST_TIME_ARGS (PeriodStart), GST_TIME_ARGS (PeriodEnd));
877 if (representation->SegmentBase != NULL
878 || representation->SegmentList != NULL) {
881 /* We have a fixed list of segments for any of the cases here,
882 * init the segments list */
883 gst_mpdparser_init_active_stream_segments (stream);
885 /* get the first segment_base of the selected representation */
886 if ((stream->cur_segment_base =
887 gst_mpd_client_get_segment_base (stream_period->period,
888 stream->cur_adapt_set, representation)) == NULL) {
889 GST_DEBUG ("No useful SegmentBase node for the current Representation");
892 /* get the first segment_list of the selected representation */
893 if ((stream->cur_segment_list =
894 gst_mpd_client_get_segment_list (client, stream_period->period,
895 stream->cur_adapt_set, representation)) == NULL) {
896 GST_DEBUG ("No useful SegmentList node for the current Representation");
897 /* here we should have a single segment for each representation, whose URL is encoded in the baseURL element */
898 if (!gst_mpd_client_add_media_segment (stream, NULL, 1, 0, 0,
899 PeriodEnd - PeriodStart, PeriodStart, PeriodEnd - PeriodStart)) {
903 /* build the list of GstMediaSegment nodes from the SegmentList node */
904 SegmentURL = stream->cur_segment_list->SegmentURL;
905 if (SegmentURL == NULL) {
907 ("No valid list of SegmentURL nodes in the MPD file, aborting...");
911 /* build segment list */
912 i = GST_MPD_MULT_SEGMENT_BASE_NODE (stream->
913 cur_segment_list)->startNumber;
915 start_time = PeriodStart;
917 GST_LOG ("Building media segment list using a SegmentList node");
918 if (GST_MPD_MULT_SEGMENT_BASE_NODE (stream->
919 cur_segment_list)->SegmentTimeline) {
920 GstMPDSegmentTimelineNode *timeline;
923 GstClockTime presentationTimeOffset;
924 GstMPDSegmentBaseNode *segbase;
927 GST_MPD_MULT_SEGMENT_BASE_NODE (stream->
928 cur_segment_list)->SegmentBase;
929 presentationTimeOffset =
930 gst_util_uint64_scale (segbase->presentationTimeOffset, GST_SECOND,
932 GST_LOG ("presentationTimeOffset = %" G_GUINT64_FORMAT,
933 presentationTimeOffset);
936 GST_MPD_MULT_SEGMENT_BASE_NODE (stream->
937 cur_segment_list)->SegmentTimeline;
938 for (list = g_queue_peek_head_link (&timeline->S); list;
939 list = g_list_next (list)) {
942 S = (GstMPDSNode *) list->data;
943 GST_LOG ("Processing S node: d=%" G_GUINT64_FORMAT " r=%d t=%"
944 G_GUINT64_FORMAT, S->d, S->r, S->t);
946 GST_MPD_MULT_SEGMENT_BASE_NODE (stream->
947 cur_segment_list)->SegmentBase->timescale;
948 duration = gst_util_uint64_scale (S->d, GST_SECOND, timescale);
952 start_time = gst_util_uint64_scale (S->t, GST_SECOND, timescale)
953 + PeriodStart - presentationTimeOffset;
958 ("SegmentTimeline does not have a matching SegmentURL, aborting...");
962 if (!gst_mpd_client_add_media_segment (stream, SegmentURL->data, i,
963 S->r, start, S->d, start_time, duration)) {
967 start_time += duration * (S->r + 1);
968 start += S->d * (S->r + 1);
969 SegmentURL = g_list_next (SegmentURL);
975 gst_mpd_client_get_segment_duration (client, stream, &scale_dur);
976 if (!GST_CLOCK_TIME_IS_VALID (duration))
980 if (!gst_mpd_client_add_media_segment (stream, SegmentURL->data, i,
981 0, start, scale_dur, start_time, duration)) {
986 start_time += duration;
987 SegmentURL = g_list_next (SegmentURL);
992 if (representation->SegmentTemplate != NULL) {
993 stream->cur_seg_template = representation->SegmentTemplate;
994 } else if (stream->cur_adapt_set->SegmentTemplate != NULL) {
995 stream->cur_seg_template = stream->cur_adapt_set->SegmentTemplate;
996 } else if (stream_period->period->SegmentTemplate != NULL) {
997 stream->cur_seg_template = stream_period->period->SegmentTemplate;
1000 if (stream->cur_seg_template == NULL) {
1002 gst_mpdparser_init_active_stream_segments (stream);
1003 /* here we should have a single segment for each representation, whose URL is encoded in the baseURL element */
1004 if (!gst_mpd_client_add_media_segment (stream, NULL, 1, 0, 0,
1005 PeriodEnd - PeriodStart, 0, PeriodEnd - PeriodStart)) {
1009 GstClockTime presentationTimeOffset;
1010 GstMPDMultSegmentBaseNode *mult_seg =
1011 GST_MPD_MULT_SEGMENT_BASE_NODE (stream->cur_seg_template);
1012 presentationTimeOffset =
1013 gst_util_uint64_scale (mult_seg->SegmentBase->presentationTimeOffset,
1014 GST_SECOND, mult_seg->SegmentBase->timescale);
1015 GST_LOG ("presentationTimeOffset = %" GST_TIME_FORMAT,
1016 GST_TIME_ARGS (presentationTimeOffset));
1017 /* build segment list */
1018 i = mult_seg->startNumber;
1022 GST_LOG ("Building media segment list using this template: %s",
1023 stream->cur_seg_template->media);
1025 if (mult_seg->SegmentTimeline) {
1026 GstMPDSegmentTimelineNode *timeline;
1030 timeline = mult_seg->SegmentTimeline;
1031 gst_mpdparser_init_active_stream_segments (stream);
1032 for (list = g_queue_peek_head_link (&timeline->S); list;
1033 list = g_list_next (list)) {
1036 S = (GstMPDSNode *) list->data;
1037 GST_LOG ("Processing S node: d=%" G_GUINT64_FORMAT " r=%u t=%"
1038 G_GUINT64_FORMAT, S->d, S->r, S->t);
1039 timescale = mult_seg->SegmentBase->timescale;
1040 duration = gst_util_uint64_scale (S->d, GST_SECOND, timescale);
1043 start_time = gst_util_uint64_scale (S->t, GST_SECOND, timescale)
1044 + PeriodStart - presentationTimeOffset;
1047 if (!gst_mpd_client_add_media_segment (stream, NULL, i, S->r, start,
1048 S->d, start_time, duration)) {
1052 start += S->d * (S->r + 1);
1053 start_time += duration * (S->r + 1);
1056 /* NOP - The segment is created on demand with the template, no need
1057 * to build a list */
1062 /* clip duration of segments to stop at period end */
1063 if (stream->segments && stream->segments->len) {
1064 if (GST_CLOCK_TIME_IS_VALID (PeriodEnd)) {
1067 for (n = 0; n < stream->segments->len; ++n) {
1068 GstMediaSegment *media_segment =
1069 g_ptr_array_index (stream->segments, n);
1070 if (media_segment) {
1071 if (media_segment->start + media_segment->duration > PeriodEnd) {
1072 GstClockTime stop = PeriodEnd;
1073 if (n < stream->segments->len - 1) {
1074 GstMediaSegment *next_segment =
1075 g_ptr_array_index (stream->segments, n + 1);
1076 if (next_segment && next_segment->start < PeriodEnd)
1077 stop = next_segment->start;
1079 media_segment->duration =
1080 media_segment->start > stop ? 0 : stop - media_segment->start;
1081 GST_LOG ("Fixed duration of segment %u: %" GST_TIME_FORMAT, n,
1082 GST_TIME_ARGS (media_segment->duration));
1084 /* If the segment was clipped entirely, we discard it and all
1085 * subsequent ones */
1086 if (media_segment->duration == 0) {
1087 GST_WARNING ("Discarding %u segments outside period",
1088 stream->segments->len - n);
1089 /* _set_size should properly unref elements */
1090 g_ptr_array_set_size (stream->segments, n);
1097 #ifndef GST_DISABLE_GST_DEBUG
1098 if (stream->segments->len > 0) {
1099 GstMediaSegment *last_media_segment =
1100 g_ptr_array_index (stream->segments, stream->segments->len - 1);
1101 GST_LOG ("Built a list of %d segments", last_media_segment->number);
1103 GST_LOG ("All media segments were clipped");
1108 g_free (stream->baseURL);
1109 g_free (stream->queryURL);
1111 gst_mpd_client_parse_baseURL (client, stream, &stream->queryURL);
1113 gst_mpd_client_stream_update_presentation_time_offset (client, stream);
1118 #define CUSTOM_WRAPPER_START "<custom_wrapper>"
1119 #define CUSTOM_WRAPPER_END "</custom_wrapper>"
1122 gst_mpd_client_fetch_external_periods (GstMPDClient * client,
1123 GstMPDPeriodNode * period_node)
1125 GstFragment *download;
1126 GstAdapter *adapter;
1127 GstBuffer *period_buffer;
1130 GstUri *base_uri, *uri;
1131 gchar *query = NULL;
1132 gchar *uri_string, *wrapper;
1133 GList *new_periods = NULL;
1136 /* ISO/IEC 23009-1:2014 5.5.3 4)
1137 * Remove nodes that resolve to nothing when resolving
1139 if (strcmp (period_node->xlink_href,
1140 "urn:mpeg:dash:resolve-to-zero:2013") == 0) {
1144 if (!client->downloader) {
1148 /* Build absolute URI */
1150 /* Get base URI at the MPD level */
1152 gst_uri_from_string (client->mpd_base_uri ? client->
1153 mpd_base_uri : client->mpd_uri);
1155 /* combine a BaseURL at the MPD level with the current base url */
1157 gst_mpd_helper_combine_urls (base_uri, client->mpd_root_node->BaseURLs,
1159 uri = gst_uri_from_string_with_base (base_uri, period_node->xlink_href);
1161 gst_uri_set_query_string (uri, query);
1163 uri_string = gst_uri_to_string (uri);
1164 gst_uri_unref (base_uri);
1165 gst_uri_unref (uri);
1168 gst_uri_downloader_fetch_uri (client->downloader,
1169 uri_string, client->mpd_uri, TRUE, FALSE, TRUE, &err);
1170 g_free (uri_string);
1173 GST_ERROR ("Failed to download external Period node at '%s': %s",
1174 period_node->xlink_href, err->message);
1175 g_clear_error (&err);
1179 period_buffer = gst_fragment_get_buffer (download);
1180 g_object_unref (download);
1182 /* external xml could have multiple period without root xmlNode.
1183 * To avoid xml parsing error caused by no root node, wrapping it with
1184 * custom root node */
1185 adapter = gst_adapter_new ();
1187 wrapper = g_new (gchar, strlen (CUSTOM_WRAPPER_START));
1188 memcpy (wrapper, CUSTOM_WRAPPER_START, strlen (CUSTOM_WRAPPER_START));
1189 gst_adapter_push (adapter,
1190 gst_buffer_new_wrapped (wrapper, strlen (CUSTOM_WRAPPER_START)));
1192 gst_adapter_push (adapter, period_buffer);
1194 wrapper = g_strdup (CUSTOM_WRAPPER_END);
1195 gst_adapter_push (adapter,
1196 gst_buffer_new_wrapped (wrapper, strlen (CUSTOM_WRAPPER_END) + 1));
1198 data = gst_adapter_map (adapter, gst_adapter_available (adapter));
1201 gst_mpdparser_get_external_periods (data,
1202 gst_adapter_available (adapter));
1204 gst_adapter_unmap (adapter);
1205 gst_adapter_clear (adapter);
1206 gst_object_unref (adapter);
1212 gst_mpd_client_setup_media_presentation (GstMPDClient * client,
1213 GstClockTime time, gint period_idx, const gchar * period_id)
1215 GstStreamPeriod *stream_period;
1216 GstClockTime start, duration;
1219 gboolean ret = FALSE;
1221 g_return_val_if_fail (client != NULL, FALSE);
1222 g_return_val_if_fail (client->mpd_root_node != NULL, FALSE);
1224 /* Check if we set up the media presentation far enough already */
1225 for (list = client->periods; list; list = list->next) {
1226 GstStreamPeriod *stream_period = list->data;
1228 if ((time != GST_CLOCK_TIME_NONE
1229 && stream_period->duration != GST_CLOCK_TIME_NONE
1230 && stream_period->start + stream_period->duration >= time)
1231 || (time != GST_CLOCK_TIME_NONE && stream_period->start >= time))
1234 if (period_idx != -1 && stream_period->number >= period_idx)
1237 if (period_id != NULL && stream_period->period->id != NULL
1238 && strcmp (stream_period->period->id, period_id) == 0)
1243 GST_DEBUG ("Building the list of Periods in the Media Presentation");
1244 /* clean the old period list, if any */
1245 /* TODO: In theory we could reuse the ones we have so far but that
1246 * seems more complicated than the overhead caused here
1248 if (client->periods) {
1249 g_list_foreach (client->periods,
1250 (GFunc) gst_mpdparser_free_stream_period, NULL);
1251 g_list_free (client->periods);
1252 client->periods = NULL;
1257 duration = GST_CLOCK_TIME_NONE;
1259 if (client->mpd_root_node->mediaPresentationDuration <= 0 &&
1260 client->mpd_root_node->mediaPresentationDuration != -1) {
1261 /* Invalid MPD file: MPD duration is negative or zero */
1265 for (list = client->mpd_root_node->Periods; list;
1266 /* explicitly advanced below */ ) {
1267 GstMPDPeriodNode *period_node = list->data;
1268 GstMPDPeriodNode *next_period_node = NULL;
1270 /* Download external period */
1271 if (period_node->xlink_href) {
1275 new_periods = gst_mpd_client_fetch_external_periods (client, period_node);
1278 client->mpd_root_node->Periods =
1279 g_list_delete_link (client->mpd_root_node->Periods, list);
1280 gst_mpd_period_node_free (period_node);
1283 /* Get new next node, we will insert before this */
1287 next = client->mpd_root_node->Periods;
1289 while (new_periods) {
1290 client->mpd_root_node->Periods =
1291 g_list_insert_before (client->mpd_root_node->Periods, next,
1293 new_periods = g_list_delete_link (new_periods, new_periods);
1297 /* Update our iterator to the first new period if any, or the next */
1301 list = client->mpd_root_node->Periods;
1307 if (period_node->start != -1) {
1308 /* we have a regular period */
1309 /* start cannot be smaller than previous start */
1310 if (list != g_list_first (client->mpd_root_node->Periods)
1311 && start >= period_node->start * GST_MSECOND) {
1312 /* Invalid MPD file: duration would be negative or zero */
1315 start = period_node->start * GST_MSECOND;
1316 } else if (duration != GST_CLOCK_TIME_NONE) {
1317 /* start time inferred from previous period, this is still a regular period */
1320 && client->mpd_root_node->type == GST_MPD_FILE_TYPE_STATIC) {
1321 /* first period of a static MPD file, start time is 0 */
1323 } else if (client->mpd_root_node->type == GST_MPD_FILE_TYPE_DYNAMIC) {
1324 /* this should be a live stream, let this pass */
1326 /* this is an 'Early Available Period' */
1330 /* compute duration.
1331 If there is a start time for the next period, or this is the last period
1332 and mediaPresentationDuration was set, those values will take precedence
1333 over a configured period duration in computing this period's duration
1335 ISO/IEC 23009-1:2014(E), chapter 5.3.2.1
1336 "The Period extends until the PeriodStart of the next Period, or until
1337 the end of the Media Presentation in the case of the last Period."
1340 while ((next = g_list_next (list)) != NULL) {
1341 /* try to infer this period duration from the start time of the next period */
1342 next_period_node = next->data;
1344 if (next_period_node->xlink_href) {
1348 gst_mpd_client_fetch_external_periods (client, next_period_node);
1350 client->mpd_root_node->Periods =
1351 g_list_delete_link (client->mpd_root_node->Periods, next);
1352 gst_mpd_period_node_free (next_period_node);
1353 next_period_node = NULL;
1354 /* Get new next node, we will insert before this */
1355 next = g_list_next (list);
1356 while (new_periods) {
1357 client->mpd_root_node->Periods =
1358 g_list_insert_before (client->mpd_root_node->Periods, next,
1360 new_periods = g_list_delete_link (new_periods, new_periods);
1363 /* And try again, getting the next list element which is now our newly
1364 * inserted nodes. If any */
1366 /* Got the next period and it doesn't have to be downloaded first */
1371 if (next_period_node) {
1372 if (next_period_node->start != -1) {
1373 if (start >= next_period_node->start * GST_MSECOND) {
1374 /* Invalid MPD file: duration would be negative or zero */
1377 duration = next_period_node->start * GST_MSECOND - start;
1378 } else if (period_node->duration != -1) {
1379 if (period_node->duration <= 0) {
1380 /* Invalid MPD file: duration would be negative or zero */
1383 duration = period_node->duration * GST_MSECOND;
1384 } else if (client->mpd_root_node->type == GST_MPD_FILE_TYPE_DYNAMIC) {
1385 /* might be a live file, ignore unspecified duration */
1387 /* Invalid MPD file! */
1390 } else if (client->mpd_root_node->mediaPresentationDuration != -1) {
1391 /* last Period of the Media Presentation */
1392 if (client->mpd_root_node->mediaPresentationDuration * GST_MSECOND <=
1394 /* Invalid MPD file: duration would be negative or zero */
1398 client->mpd_root_node->mediaPresentationDuration * GST_MSECOND -
1400 } else if (period_node->duration != -1) {
1401 duration = period_node->duration * GST_MSECOND;
1402 } else if (client->mpd_root_node->type == GST_MPD_FILE_TYPE_DYNAMIC) {
1403 /* might be a live file, ignore unspecified duration */
1405 /* Invalid MPD file! */
1407 ("Invalid MPD file. The MPD is static without a valid duration");
1411 stream_period = g_slice_new0 (GstStreamPeriod);
1412 client->periods = g_list_append (client->periods, stream_period);
1413 stream_period->period = period_node;
1414 stream_period->number = idx++;
1415 stream_period->start = start;
1416 stream_period->duration = duration;
1418 GST_LOG (" - added Period %d start=%" GST_TIME_FORMAT " duration=%"
1419 GST_TIME_FORMAT, idx, GST_TIME_ARGS (start), GST_TIME_ARGS (duration));
1421 if ((time != GST_CLOCK_TIME_NONE
1422 && stream_period->duration != GST_CLOCK_TIME_NONE
1423 && stream_period->start + stream_period->duration >= time)
1424 || (time != GST_CLOCK_TIME_NONE && stream_period->start >= time))
1427 if (period_idx != -1 && stream_period->number >= period_idx)
1430 if (period_id != NULL && stream_period->period->id != NULL
1431 && strcmp (stream_period->period->id, period_id) == 0)
1438 ("Found a total of %d valid Periods in the Media Presentation up to this point",
1444 ("Found an Early Available Period, skipping the rest of the Media Presentation");
1449 ("Cannot get the duration of the Period %d, skipping the rest of the Media Presentation",
1455 gst_mpd_client_fetch_external_adaptation_set (GstMPDClient * client,
1456 GstMPDPeriodNode * period, GstMPDAdaptationSetNode * adapt_set)
1458 GstFragment *download;
1459 GstBuffer *adapt_set_buffer;
1462 GstUri *base_uri, *uri;
1463 gchar *query = NULL;
1465 GList *new_adapt_sets = NULL;
1467 /* ISO/IEC 23009-1:2014 5.5.3 4)
1468 * Remove nodes that resolve to nothing when resolving
1470 if (strcmp (adapt_set->xlink_href, "urn:mpeg:dash:resolve-to-zero:2013") == 0) {
1474 if (!client->downloader) {
1478 /* Build absolute URI */
1480 /* Get base URI at the MPD level */
1482 gst_uri_from_string (client->mpd_base_uri ? client->
1483 mpd_base_uri : client->mpd_uri);
1485 /* combine a BaseURL at the MPD level with the current base url */
1487 gst_mpd_helper_combine_urls (base_uri, client->mpd_root_node->BaseURLs,
1490 /* combine a BaseURL at the Period level with the current base url */
1492 gst_mpd_helper_combine_urls (base_uri, period->BaseURLs, &query, 0);
1494 uri = gst_uri_from_string_with_base (base_uri, adapt_set->xlink_href);
1496 gst_uri_set_query_string (uri, query);
1498 uri_string = gst_uri_to_string (uri);
1499 gst_uri_unref (base_uri);
1500 gst_uri_unref (uri);
1503 gst_uri_downloader_fetch_uri (client->downloader,
1504 uri_string, client->mpd_uri, TRUE, FALSE, TRUE, &err);
1505 g_free (uri_string);
1508 GST_ERROR ("Failed to download external AdaptationSet node at '%s': %s",
1509 adapt_set->xlink_href, err->message);
1510 g_clear_error (&err);
1514 adapt_set_buffer = gst_fragment_get_buffer (download);
1515 g_object_unref (download);
1517 gst_buffer_map (adapt_set_buffer, &map, GST_MAP_READ);
1520 gst_mpdparser_get_external_adaptation_sets ((const gchar *) map.data,
1523 gst_buffer_unmap (adapt_set_buffer, &map);
1524 gst_buffer_unref (adapt_set_buffer);
1526 return new_adapt_sets;
1530 gst_mpd_client_get_adaptation_sets_for_period (GstMPDClient * client,
1531 GstStreamPeriod * period)
1535 g_return_val_if_fail (period != NULL, NULL);
1537 /* Resolve all external adaptation sets of this period. Every user of
1538 * the adaptation sets would need to know the content of all adaptation sets
1539 * to decide which one to use, so we have to resolve them all here
1541 for (list = period->period->AdaptationSets; list;
1542 /* advanced explicitly below */ ) {
1543 GstMPDAdaptationSetNode *adapt_set = (GstMPDAdaptationSetNode *) list->data;
1544 GList *new_adapt_sets = NULL, *prev, *next;
1546 if (!adapt_set->xlink_href) {
1552 gst_mpd_client_fetch_external_adaptation_set (client, period->period,
1556 period->period->AdaptationSets =
1557 g_list_delete_link (period->period->AdaptationSets, list);
1558 gst_mpd_adaptation_set_node_free (adapt_set);
1561 /* Get new next node, we will insert before this */
1565 next = period->period->AdaptationSets;
1567 while (new_adapt_sets) {
1568 period->period->AdaptationSets =
1569 g_list_insert_before (period->period->AdaptationSets, next,
1570 new_adapt_sets->data);
1571 new_adapt_sets = g_list_delete_link (new_adapt_sets, new_adapt_sets);
1574 /* Update our iterator to the first new adaptation set if any, or the next */
1578 list = period->period->AdaptationSets;
1581 return period->period->AdaptationSets;
1585 gst_mpd_client_get_adaptation_sets (GstMPDClient * client)
1587 GstStreamPeriod *stream_period;
1589 stream_period = gst_mpd_client_get_stream_period (client);
1590 if (stream_period == NULL || stream_period->period == NULL) {
1591 GST_DEBUG ("No more Period nodes in the MPD file, terminating...");
1595 return gst_mpd_client_get_adaptation_sets_for_period (client, stream_period);
1599 gst_mpd_client_setup_streaming (GstMPDClient * client,
1600 GstMPDAdaptationSetNode * adapt_set)
1602 GstMPDRepresentationNode *representation;
1603 GList *rep_list = NULL;
1604 GstActiveStream *stream;
1606 rep_list = adapt_set->Representations;
1608 GST_WARNING ("Can not retrieve any representation, aborting...");
1612 stream = g_slice_new0 (GstActiveStream);
1613 gst_mpdparser_init_active_stream_segments (stream);
1615 stream->baseURL_idx = 0;
1616 stream->cur_adapt_set = adapt_set;
1618 GST_DEBUG ("0. Current stream %p", stream);
1623 gst_mpdparser_get_representation_with_max_bandwidth (rep_list,
1624 stream->max_bandwidth);
1626 if (!representation) {
1628 ("Can not retrieve a representation with the requested bandwidth");
1629 representation = gst_mpd_client_get_lowest_representation (rep_list);
1633 representation = gst_mpd_client_get_lowest_representation (rep_list);
1636 if (!representation) {
1637 GST_WARNING ("No valid representation in the MPD file, aborting...");
1638 gst_mpdparser_free_active_stream (stream);
1642 gst_mpdparser_representation_get_mimetype (adapt_set, representation);
1643 if (stream->mimeType == GST_STREAM_UNKNOWN) {
1644 GST_WARNING ("Unknown mime type in the representation, aborting...");
1645 gst_mpdparser_free_active_stream (stream);
1649 client->active_streams = g_list_append (client->active_streams, stream);
1650 if (!gst_mpd_client_setup_representation (client, stream, representation)) {
1651 GST_WARNING ("Failed to setup the representation, aborting...");
1655 GST_INFO ("Successfully setup the download pipeline for mimeType %d",
1662 gst_mpd_client_stream_seek (GstMPDClient * client, GstActiveStream * stream,
1663 gboolean forward, GstSeekFlags flags, GstClockTime ts,
1664 GstClockTime * final_ts)
1667 gint repeat_index = 0;
1668 GstMediaSegment *selectedChunk = NULL;
1670 g_return_val_if_fail (stream != NULL, 0);
1672 if (stream->segments) {
1673 for (index = 0; index < stream->segments->len; index++) {
1674 gboolean in_segment = FALSE;
1675 GstMediaSegment *segment = g_ptr_array_index (stream->segments, index);
1676 GstClockTime end_time;
1678 GST_DEBUG ("Looking at fragment sequence chunk %d / %d", index,
1679 stream->segments->len);
1682 gst_mpd_client_get_segment_end_time (client, stream->segments,
1685 /* avoid downloading another fragment just for 1ns in reverse mode */
1687 in_segment = ts < end_time;
1689 in_segment = ts <= end_time;
1692 GstClockTime chunk_time;
1694 selectedChunk = segment;
1695 repeat_index = (ts - segment->start) / segment->duration;
1697 chunk_time = segment->start + segment->duration * repeat_index;
1699 /* At the end of a segment in reverse mode, start from the previous fragment */
1700 if (!forward && repeat_index > 0
1701 && ((ts - segment->start) % segment->duration == 0))
1704 if ((flags & GST_SEEK_FLAG_SNAP_NEAREST) == GST_SEEK_FLAG_SNAP_NEAREST) {
1705 if (repeat_index + 1 < segment->repeat) {
1706 if (ts - chunk_time > chunk_time + segment->duration - ts)
1708 } else if (index + 1 < stream->segments->len) {
1709 GstMediaSegment *next_segment =
1710 g_ptr_array_index (stream->segments, index + 1);
1712 if (ts - chunk_time > next_segment->start - ts) {
1714 selectedChunk = next_segment;
1718 } else if (((forward && flags & GST_SEEK_FLAG_SNAP_AFTER) ||
1719 (!forward && flags & GST_SEEK_FLAG_SNAP_BEFORE)) &&
1722 if (repeat_index + 1 < segment->repeat) {
1726 if (index + 1 >= stream->segments->len) {
1727 selectedChunk = NULL;
1729 selectedChunk = g_ptr_array_index (stream->segments, ++index);
1737 if (selectedChunk == NULL) {
1738 stream->segment_index = stream->segments->len;
1739 stream->segment_repeat_index = 0;
1740 GST_DEBUG ("Seek to after last segment");
1745 *final_ts = selectedChunk->start + selectedChunk->duration * repeat_index;
1747 GstClockTime duration =
1748 gst_mpd_client_get_segment_duration (client, stream, NULL);
1749 GstStreamPeriod *stream_period = gst_mpd_client_get_stream_period (client);
1750 guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
1751 GstClockTime index_time;
1753 g_return_val_if_fail (GST_MPD_MULT_SEGMENT_BASE_NODE
1754 (stream->cur_seg_template)->SegmentTimeline == NULL, FALSE);
1755 if (!GST_CLOCK_TIME_IS_VALID (duration)) {
1759 if (ts > stream_period->start)
1760 ts -= stream_period->start;
1764 index = ts / duration;
1766 /* At the end of a segment in reverse mode, start from the previous fragment */
1767 if (!forward && index > 0 && ts % duration == 0)
1770 index_time = index * duration;
1772 if ((flags & GST_SEEK_FLAG_SNAP_NEAREST) == GST_SEEK_FLAG_SNAP_NEAREST) {
1773 if (ts - index_time > index_time + duration - ts)
1775 } else if (((forward && flags & GST_SEEK_FLAG_SNAP_AFTER) ||
1776 (!forward && flags & GST_SEEK_FLAG_SNAP_BEFORE))
1777 && ts != index_time) {
1781 if (segments_count > 0 && index >= segments_count) {
1782 stream->segment_index = segments_count;
1783 stream->segment_repeat_index = 0;
1784 GST_DEBUG ("Seek to after last segment");
1788 *final_ts = index * duration;
1791 stream->segment_repeat_index = repeat_index;
1792 stream->segment_index = index;
1798 gst_mpd_client_calculate_time_difference (const GstDateTime * t1,
1799 const GstDateTime * t2)
1801 GDateTime *gdt1, *gdt2;
1804 g_assert (t1 != NULL && t2 != NULL);
1805 gdt1 = gst_date_time_to_g_date_time ((GstDateTime *) t1);
1806 gdt2 = gst_date_time_to_g_date_time ((GstDateTime *) t2);
1807 diff = g_date_time_difference (gdt2, gdt1);
1808 g_date_time_unref (gdt1);
1809 g_date_time_unref (gdt2);
1810 return diff * GST_USECOND;
1814 gst_mpd_client_add_time_difference (GstDateTime * t1, gint64 usecs)
1820 g_assert (t1 != NULL);
1821 gdt = gst_date_time_to_g_date_time (t1);
1822 g_assert (gdt != NULL);
1823 gdt2 = g_date_time_add (gdt, usecs);
1824 g_assert (gdt2 != NULL);
1825 g_date_time_unref (gdt);
1826 rv = gst_date_time_new_from_g_date_time (gdt2);
1828 /* Don't g_date_time_unref(gdt2) because gst_date_time_new_from_g_date_time takes
1829 * ownership of the GDateTime pointer.
1836 gst_mpd_client_get_last_fragment_timestamp_end (GstMPDClient * client,
1837 guint stream_idx, GstClockTime * ts)
1839 GstActiveStream *stream;
1841 GstMediaSegment *currentChunk;
1842 GstStreamPeriod *stream_period;
1844 GST_DEBUG ("Stream index: %i", stream_idx);
1845 stream = g_list_nth_data (client->active_streams, stream_idx);
1846 g_return_val_if_fail (stream != NULL, 0);
1848 if (!stream->segments) {
1849 stream_period = gst_mpd_client_get_stream_period (client);
1850 *ts = stream_period->start + stream_period->duration;
1852 segment_idx = gst_mpd_client_get_segments_counts (client, stream) - 1;
1853 if (segment_idx >= stream->segments->len) {
1854 GST_WARNING ("Segment index %d is outside of segment list of length %d",
1855 segment_idx, stream->segments->len);
1858 currentChunk = g_ptr_array_index (stream->segments, segment_idx);
1860 if (currentChunk->repeat >= 0) {
1862 currentChunk->start + (currentChunk->duration * (1 +
1863 currentChunk->repeat));
1865 /* 5.3.9.6.1: negative repeat means repeat till the end of the
1866 * period, or the next update of the MPD (which I think is
1867 * implicit, as this will all get deleted/recreated), or the
1868 * start of the next segment, if any. */
1869 stream_period = gst_mpd_client_get_stream_period (client);
1870 *ts = stream_period->start + stream_period->duration;
1878 gst_mpd_client_get_next_fragment_timestamp (GstMPDClient * client,
1879 guint stream_idx, GstClockTime * ts)
1881 GstActiveStream *stream;
1882 GstMediaSegment *currentChunk;
1884 GST_DEBUG ("Stream index: %i", stream_idx);
1885 stream = g_list_nth_data (client->active_streams, stream_idx);
1886 g_return_val_if_fail (stream != NULL, 0);
1888 if (stream->segments) {
1889 GST_DEBUG ("Looking for fragment sequence chunk %d / %d",
1890 stream->segment_index, stream->segments->len);
1891 if (stream->segment_index >= stream->segments->len)
1893 currentChunk = g_ptr_array_index (stream->segments, stream->segment_index);
1896 currentChunk->start +
1897 (currentChunk->duration * stream->segment_repeat_index);
1899 GstClockTime duration =
1900 gst_mpd_client_get_segment_duration (client, stream, NULL);
1901 guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
1903 g_return_val_if_fail (GST_MPD_MULT_SEGMENT_BASE_NODE
1904 (stream->cur_seg_template)->SegmentTimeline == NULL, FALSE);
1905 if (!GST_CLOCK_TIME_IS_VALID (duration) || (segments_count > 0
1906 && stream->segment_index >= segments_count)) {
1909 *ts = stream->segment_index * duration;
1916 gst_mpd_client_get_stream_presentation_offset (GstMPDClient * client,
1919 GstActiveStream *stream = NULL;
1921 g_return_val_if_fail (client != NULL, 0);
1922 g_return_val_if_fail (client->active_streams != NULL, 0);
1923 stream = g_list_nth_data (client->active_streams, stream_idx);
1924 g_return_val_if_fail (stream != NULL, 0);
1926 return stream->presentationTimeOffset;
1930 gst_mpd_client_get_period_start_time (GstMPDClient * client)
1932 GstStreamPeriod *stream_period = NULL;
1934 g_return_val_if_fail (client != NULL, 0);
1935 stream_period = gst_mpd_client_get_stream_period (client);
1936 g_return_val_if_fail (stream_period != NULL, 0);
1938 return stream_period->start;
1942 * gst_mpd_client_get_utc_timing_sources:
1943 * @client: #GstMPDClient to check for UTCTiming elements
1944 * @methods: A bit mask of #GstMPDUTCTimingType that specifies the methods
1946 * @selected_method: (nullable): The selected method
1947 * Returns: (transfer none): A NULL terminated array of URLs of servers
1948 * that use @selected_method to provide a realtime clock.
1950 * Searches the UTCTiming elements found in the manifest for an element
1951 * that uses one of the UTC timing methods specified in @selected_method.
1952 * If multiple UTCTiming elements are present that support one of the
1953 * methods specified in @selected_method, the first one is returned.
1958 gst_mpd_client_get_utc_timing_sources (GstMPDClient * client,
1959 guint methods, GstMPDUTCTimingType * selected_method)
1963 g_return_val_if_fail (client != NULL, NULL);
1964 g_return_val_if_fail (client->mpd_root_node != NULL, NULL);
1965 for (list = g_list_first (client->mpd_root_node->UTCTimings); list;
1966 list = g_list_next (list)) {
1967 const GstMPDUTCTimingNode *node = (const GstMPDUTCTimingNode *) list->data;
1968 if (node->method & methods) {
1969 if (selected_method) {
1970 *selected_method = node->method;
1980 gst_mpd_client_get_next_fragment (GstMPDClient * client,
1981 guint indexStream, GstMediaFragmentInfo * fragment)
1983 GstActiveStream *stream = NULL;
1984 GstMediaSegment *currentChunk;
1985 gchar *mediaURL = NULL;
1986 gchar *indexURL = NULL;
1987 GstUri *base_url, *frag_url;
1990 g_return_val_if_fail (client != NULL, FALSE);
1991 g_return_val_if_fail (client->active_streams != NULL, FALSE);
1992 stream = g_list_nth_data (client->active_streams, indexStream);
1993 g_return_val_if_fail (stream != NULL, FALSE);
1994 g_return_val_if_fail (stream->cur_representation != NULL, FALSE);
1996 if (stream->segments) {
1997 GST_DEBUG ("Looking for fragment sequence chunk %d / %d",
1998 stream->segment_index, stream->segments->len);
1999 if (stream->segment_index >= stream->segments->len)
2002 GstClockTime duration = gst_mpd_client_get_segment_duration (client,
2004 guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
2006 g_return_val_if_fail (GST_MPD_MULT_SEGMENT_BASE_NODE
2007 (stream->cur_seg_template)->SegmentTimeline == NULL, FALSE);
2008 if (!GST_CLOCK_TIME_IS_VALID (duration) || (segments_count > 0
2009 && stream->segment_index >= segments_count)) {
2012 fragment->duration = duration;
2015 /* FIXME rework discont checking */
2016 /* fragment->discontinuity = segment_idx != currentChunk.number; */
2017 fragment->range_start = 0;
2018 fragment->range_end = -1;
2019 fragment->index_uri = NULL;
2020 fragment->index_range_start = 0;
2021 fragment->index_range_end = -1;
2023 if (stream->segments) {
2024 currentChunk = g_ptr_array_index (stream->segments, stream->segment_index);
2026 GST_DEBUG ("currentChunk->SegmentURL = %p", currentChunk->SegmentURL);
2027 if (currentChunk->SegmentURL != NULL) {
2029 g_strdup (gst_mpdparser_get_mediaURL (stream,
2030 currentChunk->SegmentURL));
2031 indexURL = g_strdup (currentChunk->SegmentURL->index);
2032 } else if (stream->cur_seg_template != NULL) {
2034 gst_mpdparser_build_URL_from_template (stream->cur_seg_template->
2035 media, stream->cur_representation->id,
2036 currentChunk->number + stream->segment_repeat_index,
2037 stream->cur_representation->bandwidth,
2038 currentChunk->scale_start +
2039 stream->segment_repeat_index * currentChunk->scale_duration);
2040 if (stream->cur_seg_template->index) {
2042 gst_mpdparser_build_URL_from_template (stream->cur_seg_template->
2043 index, stream->cur_representation->id,
2044 currentChunk->number + stream->segment_repeat_index,
2045 stream->cur_representation->bandwidth,
2046 currentChunk->scale_start +
2047 stream->segment_repeat_index * currentChunk->scale_duration);
2050 GST_DEBUG ("mediaURL = %s", mediaURL);
2051 GST_DEBUG ("indexURL = %s", indexURL);
2053 fragment->timestamp =
2054 currentChunk->start +
2055 stream->segment_repeat_index * currentChunk->duration;
2056 fragment->duration = currentChunk->duration;
2057 if (currentChunk->SegmentURL) {
2058 if (currentChunk->SegmentURL->mediaRange) {
2059 fragment->range_start =
2060 currentChunk->SegmentURL->mediaRange->first_byte_pos;
2061 fragment->range_end =
2062 currentChunk->SegmentURL->mediaRange->last_byte_pos;
2064 if (currentChunk->SegmentURL->indexRange) {
2065 fragment->index_range_start =
2066 currentChunk->SegmentURL->indexRange->first_byte_pos;
2067 fragment->index_range_end =
2068 currentChunk->SegmentURL->indexRange->last_byte_pos;
2072 if (stream->cur_seg_template != NULL) {
2074 gst_mpdparser_build_URL_from_template (stream->cur_seg_template->
2075 media, stream->cur_representation->id,
2076 stream->segment_index +
2077 GST_MPD_MULT_SEGMENT_BASE_NODE (stream->
2078 cur_seg_template)->startNumber,
2079 stream->cur_representation->bandwidth,
2080 stream->segment_index * fragment->duration);
2081 if (stream->cur_seg_template->index) {
2083 gst_mpdparser_build_URL_from_template (stream->cur_seg_template->
2084 index, stream->cur_representation->id,
2085 stream->segment_index +
2086 GST_MPD_MULT_SEGMENT_BASE_NODE (stream->
2087 cur_seg_template)->startNumber,
2088 stream->cur_representation->bandwidth,
2089 stream->segment_index * fragment->duration);
2095 GST_DEBUG ("mediaURL = %s", mediaURL);
2096 GST_DEBUG ("indexURL = %s", indexURL);
2098 fragment->timestamp = stream->segment_index * fragment->duration;
2101 base_url = gst_uri_from_string (stream->baseURL);
2102 frag_url = gst_uri_from_string_with_base (base_url, mediaURL);
2104 if (stream->queryURL) {
2105 frag_url = gst_uri_make_writable (frag_url);
2106 gst_uri_set_query_string (frag_url, stream->queryURL);
2108 fragment->uri = gst_uri_to_string (frag_url);
2109 gst_uri_unref (frag_url);
2111 if (indexURL != NULL) {
2112 frag_url = gst_uri_make_writable (gst_uri_from_string_with_base (base_url,
2114 gst_uri_set_query_string (frag_url, stream->queryURL);
2115 fragment->index_uri = gst_uri_to_string (frag_url);
2116 gst_uri_unref (frag_url);
2118 } else if (indexURL == NULL && (fragment->index_range_start
2119 || fragment->index_range_end != -1)) {
2120 /* index has no specific URL but has a range, we should only use this if
2121 * the media also has a range, otherwise we are serving some data twice
2122 * (in the media fragment and again in the index) */
2123 if (!(fragment->range_start || fragment->range_end != -1)) {
2124 GST_WARNING ("Ignoring index ranges because there isn't a media range "
2125 "and URIs would be the same");
2126 /* removing index information */
2127 fragment->index_range_start = 0;
2128 fragment->index_range_end = -1;
2132 gst_uri_unref (base_url);
2134 GST_DEBUG ("Loading chunk with URL %s", fragment->uri);
2140 gst_mpd_client_has_next_segment (GstMPDClient * client,
2141 GstActiveStream * stream, gboolean forward)
2144 guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
2146 if (segments_count > 0 && stream->segments
2147 && stream->segment_index + 1 == segments_count) {
2148 GstMediaSegment *segment;
2150 segment = g_ptr_array_index (stream->segments, stream->segment_index);
2151 if (segment->repeat >= 0
2152 && stream->segment_repeat_index >= segment->repeat)
2154 } else if (segments_count > 0
2155 && stream->segment_index + 1 >= segments_count) {
2159 if (stream->segment_index < 0)
2167 gst_mpd_client_advance_segment (GstMPDClient * client, GstActiveStream * stream,
2170 GstMediaSegment *segment;
2171 GstFlowReturn ret = GST_FLOW_OK;
2172 guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
2174 GST_DEBUG ("Advancing segment. Current: %d / %d r:%d", stream->segment_index,
2175 segments_count, stream->segment_repeat_index);
2177 /* handle special cases first */
2179 if (segments_count > 0 && stream->segment_index >= segments_count) {
2184 if (stream->segments == NULL) {
2185 if (stream->segment_index < 0) {
2186 stream->segment_index = 0;
2188 stream->segment_index++;
2189 if (segments_count > 0 && stream->segment_index >= segments_count) {
2196 /* special case for when playback direction is reverted right at *
2197 * the end of the segment list */
2198 if (stream->segment_index < 0) {
2199 stream->segment_index = 0;
2203 if (stream->segments == NULL)
2204 stream->segment_index--;
2205 if (stream->segment_index < 0) {
2206 stream->segment_index = -1;
2210 if (stream->segments == NULL)
2213 /* special case for when playback direction is reverted right at *
2214 * the end of the segment list */
2215 if (stream->segment_index >= segments_count) {
2216 stream->segment_index = segments_count - 1;
2217 segment = g_ptr_array_index (stream->segments, stream->segment_index);
2218 if (segment->repeat >= 0) {
2219 stream->segment_repeat_index = segment->repeat;
2221 GstClockTime start = segment->start;
2223 gst_mpd_client_get_segment_end_time (client, stream->segments,
2225 stream->segment_index);
2226 stream->segment_repeat_index =
2227 (guint) (end - start) / segment->duration;
2233 /* for the normal cases we can get the segment safely here */
2234 segment = g_ptr_array_index (stream->segments, stream->segment_index);
2236 if (segment->repeat >= 0 && stream->segment_repeat_index >= segment->repeat) {
2237 stream->segment_repeat_index = 0;
2238 stream->segment_index++;
2239 if (segments_count > 0 && stream->segment_index >= segments_count) {
2244 stream->segment_repeat_index++;
2247 if (stream->segment_repeat_index == 0) {
2248 stream->segment_index--;
2249 if (stream->segment_index < 0) {
2254 segment = g_ptr_array_index (stream->segments, stream->segment_index);
2255 /* negative repeats only seem to make sense at the end of a list,
2256 * so this one will probably not be. Needs some sanity checking
2257 * when loading the XML data. */
2258 if (segment->repeat >= 0) {
2259 stream->segment_repeat_index = segment->repeat;
2261 GstClockTime start = segment->start;
2263 gst_mpd_client_get_segment_end_time (client, stream->segments,
2265 stream->segment_index);
2266 stream->segment_repeat_index =
2267 (guint) (end - start) / segment->duration;
2270 stream->segment_repeat_index--;
2275 GST_DEBUG ("Advanced to segment: %d / %d r:%d (ret: %s)",
2276 stream->segment_index, segments_count,
2277 stream->segment_repeat_index, gst_flow_get_name (ret));
2282 gst_mpd_client_get_next_header (GstMPDClient * client, gchar ** uri,
2283 guint stream_idx, gint64 * range_start, gint64 * range_end)
2285 GstActiveStream *stream;
2286 GstStreamPeriod *stream_period;
2288 stream = gst_mpd_client_get_active_stream_by_index (client, stream_idx);
2289 g_return_val_if_fail (stream != NULL, FALSE);
2290 g_return_val_if_fail (stream->cur_representation != NULL, FALSE);
2291 stream_period = gst_mpd_client_get_stream_period (client);
2292 g_return_val_if_fail (stream_period != NULL, FALSE);
2293 g_return_val_if_fail (stream_period->period != NULL, FALSE);
2298 GST_DEBUG ("Looking for current representation header");
2300 if (stream->cur_segment_base) {
2301 if (stream->cur_segment_base->Initialization) {
2303 g_strdup (gst_mpdparser_get_initializationURL (stream,
2304 stream->cur_segment_base->Initialization));
2305 if (stream->cur_segment_base->Initialization->range) {
2307 stream->cur_segment_base->Initialization->range->first_byte_pos;
2309 stream->cur_segment_base->Initialization->range->last_byte_pos;
2311 } else if (stream->cur_segment_base->indexRange) {
2313 g_strdup (gst_mpdparser_get_initializationURL (stream,
2314 stream->cur_segment_base->Initialization));
2316 *range_end = stream->cur_segment_base->indexRange->first_byte_pos - 1;
2318 } else if (stream->cur_seg_template
2319 && stream->cur_seg_template->initialization) {
2321 gst_mpdparser_build_URL_from_template (stream->cur_seg_template->
2322 initialization, stream->cur_representation->id, 0,
2323 stream->cur_representation->bandwidth, 0);
2326 return *uri == NULL ? FALSE : TRUE;
2330 gst_mpd_client_get_next_header_index (GstMPDClient * client, gchar ** uri,
2331 guint stream_idx, gint64 * range_start, gint64 * range_end)
2333 GstActiveStream *stream;
2334 GstStreamPeriod *stream_period;
2336 stream = gst_mpd_client_get_active_stream_by_index (client, stream_idx);
2337 g_return_val_if_fail (stream != NULL, FALSE);
2338 g_return_val_if_fail (stream->cur_representation != NULL, FALSE);
2339 stream_period = gst_mpd_client_get_stream_period (client);
2340 g_return_val_if_fail (stream_period != NULL, FALSE);
2341 g_return_val_if_fail (stream_period->period != NULL, FALSE);
2346 GST_DEBUG ("Looking for current representation index");
2348 if (stream->cur_segment_base && stream->cur_segment_base->indexRange) {
2350 g_strdup (gst_mpdparser_get_initializationURL (stream,
2351 stream->cur_segment_base->RepresentationIndex));
2352 *range_start = stream->cur_segment_base->indexRange->first_byte_pos;
2353 *range_end = stream->cur_segment_base->indexRange->last_byte_pos;
2354 } else if (stream->cur_seg_template && stream->cur_seg_template->index) {
2356 gst_mpdparser_build_URL_from_template (stream->cur_seg_template->index,
2357 stream->cur_representation->id, 0,
2358 stream->cur_representation->bandwidth, 0);
2361 return *uri == NULL ? FALSE : TRUE;
2365 gst_mpd_client_get_next_fragment_duration (GstMPDClient * client,
2366 GstActiveStream * stream)
2368 GstMediaSegment *media_segment = NULL;
2371 g_return_val_if_fail (stream != NULL, 0);
2373 seg_idx = stream->segment_index;
2375 if (stream->segments) {
2376 if (seg_idx < stream->segments->len && seg_idx >= 0)
2377 media_segment = g_ptr_array_index (stream->segments, seg_idx);
2379 return media_segment == NULL ? 0 : media_segment->duration;
2381 GstClockTime duration =
2382 gst_mpd_client_get_segment_duration (client, stream, NULL);
2383 guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
2385 g_return_val_if_fail (GST_MPD_MULT_SEGMENT_BASE_NODE
2386 (stream->cur_seg_template)->SegmentTimeline == NULL, 0);
2388 if (!GST_CLOCK_TIME_IS_VALID (duration) || (segments_count > 0
2389 && seg_idx >= segments_count)) {
2397 gst_mpd_client_get_media_presentation_duration (GstMPDClient * client)
2399 GstClockTime duration;
2401 g_return_val_if_fail (client != NULL, GST_CLOCK_TIME_NONE);
2403 if (client->mpd_root_node->mediaPresentationDuration != -1) {
2404 duration = client->mpd_root_node->mediaPresentationDuration * GST_MSECOND;
2406 /* We can only get the duration for on-demand streams */
2407 duration = GST_CLOCK_TIME_NONE;
2414 gst_mpd_client_set_period_id (GstMPDClient * client, const gchar * period_id)
2416 GstStreamPeriod *next_stream_period;
2417 gboolean ret = FALSE;
2421 g_return_val_if_fail (client != NULL, FALSE);
2422 g_return_val_if_fail (client->periods != NULL, FALSE);
2423 g_return_val_if_fail (period_id != NULL, FALSE);
2425 if (!gst_mpd_client_setup_media_presentation (client, GST_CLOCK_TIME_NONE, -1,
2429 for (period_idx = 0, iter = client->periods; iter;
2430 period_idx++, iter = g_list_next (iter)) {
2431 next_stream_period = iter->data;
2433 if (next_stream_period->period->id
2434 && strcmp (next_stream_period->period->id, period_id) == 0) {
2436 client->period_idx = period_idx;
2445 gst_mpd_client_set_period_index (GstMPDClient * client, guint period_idx)
2447 GstStreamPeriod *next_stream_period;
2448 gboolean ret = FALSE;
2450 g_return_val_if_fail (client != NULL, FALSE);
2451 g_return_val_if_fail (client->periods != NULL, FALSE);
2453 if (!gst_mpd_client_setup_media_presentation (client, -1, period_idx, NULL))
2456 next_stream_period = g_list_nth_data (client->periods, period_idx);
2457 if (next_stream_period != NULL) {
2458 client->period_idx = period_idx;
2466 gst_mpd_client_get_period_index (GstMPDClient * client)
2470 g_return_val_if_fail (client != NULL, 0);
2471 period_idx = client->period_idx;
2477 gst_mpd_client_get_period_id (GstMPDClient * client)
2479 GstStreamPeriod *period;
2480 gchar *period_id = NULL;
2482 g_return_val_if_fail (client != NULL, 0);
2483 period = g_list_nth_data (client->periods, client->period_idx);
2484 if (period && period->period)
2485 period_id = period->period->id;
2491 gst_mpd_client_has_next_period (GstMPDClient * client)
2493 GList *next_stream_period;
2494 g_return_val_if_fail (client != NULL, FALSE);
2495 g_return_val_if_fail (client->periods != NULL, FALSE);
2497 if (!gst_mpd_client_setup_media_presentation (client, GST_CLOCK_TIME_NONE,
2498 client->period_idx + 1, NULL))
2501 next_stream_period =
2502 g_list_nth_data (client->periods, client->period_idx + 1);
2503 return next_stream_period != NULL;
2507 gst_mpd_client_has_previous_period (GstMPDClient * client)
2509 GList *next_stream_period;
2510 g_return_val_if_fail (client != NULL, FALSE);
2511 g_return_val_if_fail (client->periods != NULL, FALSE);
2513 if (!gst_mpd_client_setup_media_presentation (client, GST_CLOCK_TIME_NONE,
2514 client->period_idx - 1, NULL))
2517 next_stream_period =
2518 g_list_nth_data (client->periods, client->period_idx - 1);
2520 return next_stream_period != NULL;
2524 gst_mpd_client_get_rep_idx_with_min_bandwidth (GList * Representations)
2526 GList *list = NULL, *lowest = NULL;
2527 GstMPDRepresentationNode *rep = NULL;
2528 gint lowest_bandwidth = -1;
2530 if (Representations == NULL)
2533 for (list = g_list_first (Representations); list; list = g_list_next (list)) {
2534 rep = (GstMPDRepresentationNode *) list->data;
2535 if (rep && (!lowest || rep->bandwidth < lowest_bandwidth)) {
2537 lowest_bandwidth = rep->bandwidth;
2541 return lowest ? g_list_position (Representations, lowest) : -1;
2545 gst_mpd_client_get_rep_idx_with_max_bandwidth (GList * Representations,
2546 gint64 max_bandwidth, gint max_video_width, gint max_video_height, gint
2547 max_video_framerate_n, gint max_video_framerate_d)
2549 GList *list = NULL, *best = NULL;
2550 GstMPDRepresentationNode *representation;
2551 gint best_bandwidth = 0;
2553 GST_DEBUG ("max_bandwidth = %" G_GINT64_FORMAT, max_bandwidth);
2555 if (Representations == NULL)
2558 if (max_bandwidth <= 0) /* 0 => get lowest representation available */
2559 return gst_mpd_client_get_rep_idx_with_min_bandwidth (Representations);
2561 for (list = g_list_first (Representations); list; list = g_list_next (list)) {
2562 GstXMLFrameRate *framerate = NULL;
2564 representation = (GstMPDRepresentationNode *) list->data;
2566 /* FIXME: Really? */
2567 if (!representation)
2570 framerate = GST_MPD_REPRESENTATION_BASE_NODE (representation)->frameRate;
2573 GST_MPD_REPRESENTATION_BASE_NODE (representation)->maxFrameRate;
2575 if (framerate && max_video_framerate_n > 0) {
2576 if (gst_util_fraction_compare (framerate->num, framerate->den,
2577 max_video_framerate_n, max_video_framerate_d) > 0)
2581 if (max_video_width > 0
2582 && GST_MPD_REPRESENTATION_BASE_NODE (representation)->width >
2585 if (max_video_height > 0
2586 && GST_MPD_REPRESENTATION_BASE_NODE (representation)->height >
2590 if (representation->bandwidth <= max_bandwidth &&
2591 representation->bandwidth > best_bandwidth) {
2593 best_bandwidth = representation->bandwidth;
2597 return best ? g_list_position (Representations, best) : -1;
2601 gst_mpd_client_seek_to_first_segment (GstMPDClient * client)
2605 g_return_if_fail (client != NULL);
2606 g_return_if_fail (client->active_streams != NULL);
2608 for (list = g_list_first (client->active_streams); list;
2609 list = g_list_next (list)) {
2610 GstActiveStream *stream = (GstActiveStream *) list->data;
2612 stream->segment_index = 0;
2613 stream->segment_repeat_index = 0;
2619 gst_mpd_client_get_segments_counts (GstMPDClient * client,
2620 GstActiveStream * stream)
2622 GstStreamPeriod *stream_period;
2624 g_return_val_if_fail (stream != NULL, 0);
2626 if (stream->segments)
2627 return stream->segments->len;
2628 g_return_val_if_fail (GST_MPD_MULT_SEGMENT_BASE_NODE
2629 (stream->cur_seg_template)->SegmentTimeline == NULL, 0);
2631 stream_period = gst_mpd_client_get_stream_period (client);
2632 if (stream_period->duration != -1)
2633 return gst_util_uint64_scale_ceil (stream_period->duration, 1,
2634 gst_mpd_client_get_segment_duration (client, stream, NULL));
2640 gst_mpd_client_is_live (GstMPDClient * client)
2642 g_return_val_if_fail (client != NULL, FALSE);
2643 g_return_val_if_fail (client->mpd_root_node != NULL, FALSE);
2645 return client->mpd_root_node->type == GST_MPD_FILE_TYPE_DYNAMIC;
2649 gst_mpd_client_get_nb_active_stream (GstMPDClient * client)
2651 g_return_val_if_fail (client != NULL, 0);
2653 return g_list_length (client->active_streams);
2657 gst_mpd_client_get_nb_adaptationSet (GstMPDClient * client)
2659 GstStreamPeriod *stream_period;
2661 stream_period = gst_mpd_client_get_stream_period (client);
2662 g_return_val_if_fail (stream_period != NULL, 0);
2663 g_return_val_if_fail (stream_period->period != NULL, 0);
2665 return g_list_length (stream_period->period->AdaptationSets);
2669 gst_mpd_client_get_active_stream_by_index (GstMPDClient * client,
2672 g_return_val_if_fail (client != NULL, NULL);
2673 g_return_val_if_fail (client->active_streams != NULL, NULL);
2675 return g_list_nth_data (client->active_streams, stream_idx);
2679 gst_mpd_client_active_stream_contains_subtitles (GstActiveStream * stream)
2681 const gchar *mimeType;
2682 const gchar *adapt_set_codecs;
2683 const gchar *rep_codecs;
2686 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_representation)->mimeType;
2689 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->mimeType;
2691 if (g_strcmp0 (mimeType, "application/ttml+xml") == 0 ||
2692 g_strcmp0 (mimeType, "text/vtt") == 0)
2696 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->codecs;
2698 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_representation)->codecs;
2700 return (adapt_set_codecs && g_str_has_prefix (adapt_set_codecs, "stpp"))
2701 || (rep_codecs && g_str_has_prefix (rep_codecs, "stpp"));
2705 gst_mpd_client_get_stream_caps (GstActiveStream * stream)
2707 const gchar *mimeType, *caps_string;
2708 GstCaps *ret = NULL;
2710 if (stream == NULL || stream->cur_adapt_set == NULL
2711 || stream->cur_representation == NULL)
2715 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_representation)->mimeType;
2716 if (mimeType == NULL) {
2718 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->mimeType;
2721 caps_string = gst_mpd_helper_mimetype_to_caps (mimeType);
2723 if ((g_strcmp0 (caps_string, "application/mp4") == 0)
2724 && gst_mpd_client_active_stream_contains_subtitles (stream))
2725 caps_string = "video/quicktime";
2728 ret = gst_caps_from_string (caps_string);
2734 gst_mpd_client_get_bitstream_switching_flag (GstActiveStream * stream)
2736 if (stream == NULL || stream->cur_adapt_set == NULL)
2739 return stream->cur_adapt_set->bitstreamSwitching;
2743 gst_mpd_client_get_video_stream_width (GstActiveStream * stream)
2747 if (stream == NULL || stream->cur_adapt_set == NULL
2748 || stream->cur_representation == NULL)
2751 width = GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_representation)->width;
2753 width = GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->width;
2760 gst_mpd_client_get_video_stream_height (GstActiveStream * stream)
2764 if (stream == NULL || stream->cur_adapt_set == NULL
2765 || stream->cur_representation == NULL)
2769 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_representation)->height;
2771 height = GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->height;
2778 gst_mpd_client_get_video_stream_framerate (GstActiveStream * stream,
2779 gint * fps_num, gint * fps_den)
2784 if (stream->cur_adapt_set &&
2785 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->frameRate !=
2788 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->
2791 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->
2796 if (stream->cur_adapt_set &&
2797 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->maxFrameRate !=
2800 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->
2803 GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->
2808 if (stream->cur_representation &&
2809 GST_MPD_REPRESENTATION_BASE_NODE (stream->
2810 cur_representation)->frameRate != NULL) {
2812 GST_MPD_REPRESENTATION_BASE_NODE (stream->
2813 cur_representation)->frameRate->num;
2815 GST_MPD_REPRESENTATION_BASE_NODE (stream->
2816 cur_representation)->frameRate->den;
2820 if (stream->cur_representation &&
2821 GST_MPD_REPRESENTATION_BASE_NODE (stream->
2822 cur_representation)->maxFrameRate != NULL) {
2824 GST_MPD_REPRESENTATION_BASE_NODE (stream->
2825 cur_representation)->maxFrameRate->num;
2827 GST_MPD_REPRESENTATION_BASE_NODE (stream->
2828 cur_representation)->maxFrameRate->den;
2836 gst_mpd_client_get_audio_stream_rate (GstActiveStream * stream)
2840 if (stream == NULL || stream->cur_adapt_set == NULL
2841 || stream->cur_representation == NULL)
2845 GST_MPD_REPRESENTATION_BASE_NODE (stream->
2846 cur_representation)->audioSamplingRate;
2849 GST_MPD_REPRESENTATION_BASE_NODE (stream->
2850 cur_adapt_set)->audioSamplingRate;
2853 return rate ? atoi (rate) : 0;
2857 gst_mpd_client_get_audio_stream_num_channels (GstActiveStream * stream)
2859 if (stream == NULL || stream->cur_adapt_set == NULL
2860 || stream->cur_representation == NULL)
2862 /* TODO: here we have to parse the AudioChannelConfiguration descriptors */
2867 gst_mpd_client_get_list_and_nb_of_audio_language (GstMPDClient * client,
2870 GstStreamPeriod *stream_period;
2871 GstMPDAdaptationSetNode *adapt_set;
2872 GList *adaptation_sets, *list;
2873 const gchar *this_mimeType = "audio";
2874 gchar *mimeType = NULL;
2875 guint nb_adaptation_set = 0;
2877 stream_period = gst_mpd_client_get_stream_period (client);
2878 g_return_val_if_fail (stream_period != NULL, 0);
2879 g_return_val_if_fail (stream_period->period != NULL, 0);
2882 gst_mpd_client_get_adaptation_sets_for_period (client, stream_period);
2883 for (list = adaptation_sets; list; list = g_list_next (list)) {
2884 adapt_set = (GstMPDAdaptationSetNode *) list->data;
2885 if (adapt_set && adapt_set->lang) {
2886 gchar *this_lang = adapt_set->lang;
2887 GstMPDRepresentationNode *rep;
2889 gst_mpd_client_get_lowest_representation (adapt_set->Representations);
2891 if (GST_MPD_REPRESENTATION_BASE_NODE (rep))
2892 mimeType = GST_MPD_REPRESENTATION_BASE_NODE (rep)->mimeType;
2893 if (!mimeType && GST_MPD_REPRESENTATION_BASE_NODE (adapt_set)) {
2894 mimeType = GST_MPD_REPRESENTATION_BASE_NODE (adapt_set)->mimeType;
2897 if (gst_mpd_helper_strncmp_ext (mimeType, this_mimeType) == 0) {
2898 nb_adaptation_set++;
2899 *lang = g_list_append (*lang, this_lang);
2904 return nb_adaptation_set;
2909 gst_mpd_client_get_next_segment_availability_start_time (GstMPDClient * client,
2910 GstActiveStream * stream)
2912 GstDateTime *availability_start_time, *rv;
2914 GstMediaSegment *segment;
2915 GstClockTime segmentEndTime;
2916 const GstStreamPeriod *stream_period;
2917 GstClockTime period_start = 0;
2919 g_return_val_if_fail (client != NULL, NULL);
2920 g_return_val_if_fail (stream != NULL, NULL);
2922 stream_period = gst_mpd_client_get_stream_period (client);
2923 if (stream_period && stream_period->period) {
2924 period_start = stream_period->start;
2927 seg_idx = stream->segment_index;
2929 if (stream->segments) {
2930 segment = g_ptr_array_index (stream->segments, seg_idx);
2932 if (segment->repeat >= 0) {
2933 segmentEndTime = segment->start + (stream->segment_repeat_index + 1) *
2935 } else if (seg_idx < stream->segments->len - 1) {
2936 const GstMediaSegment *next_segment =
2937 g_ptr_array_index (stream->segments, seg_idx + 1);
2938 segmentEndTime = next_segment->start;
2940 g_return_val_if_fail (stream_period != NULL, NULL);
2941 segmentEndTime = period_start + stream_period->duration;
2944 GstClockTime seg_duration;
2945 seg_duration = gst_mpd_client_get_segment_duration (client, stream, NULL);
2946 if (seg_duration == 0)
2948 segmentEndTime = period_start + (1 + seg_idx) * seg_duration;
2951 availability_start_time = gst_mpd_client_get_availability_start_time (client);
2952 if (availability_start_time == NULL) {
2953 GST_WARNING_OBJECT (client, "Failed to get availability_start_time");
2957 rv = gst_mpd_client_add_time_difference (availability_start_time,
2958 segmentEndTime / GST_USECOND);
2959 gst_date_time_unref (availability_start_time);
2961 GST_WARNING_OBJECT (client, "Failed to offset availability_start_time");
2969 gst_mpd_client_seek_to_time (GstMPDClient * client, GDateTime * time)
2972 GTimeSpan ts_microseconds;
2974 gboolean ret = TRUE;
2977 g_return_val_if_fail (gst_mpd_client_is_live (client), FALSE);
2978 g_return_val_if_fail (client->mpd_root_node->availabilityStartTime != NULL,
2982 gst_date_time_to_g_date_time (client->mpd_root_node->
2983 availabilityStartTime);
2985 ts_microseconds = g_date_time_difference (time, start);
2986 g_date_time_unref (start);
2988 /* Clamp to availability start time, otherwise calculations wrap around */
2989 if (ts_microseconds < 0)
2990 ts_microseconds = 0;
2992 ts = ts_microseconds * GST_USECOND;
2993 for (stream = client->active_streams; stream; stream = g_list_next (stream)) {
2995 ret & gst_mpd_client_stream_seek (client, stream->data, TRUE, 0, ts,
3002 gst_mpd_client_has_isoff_ondemand_profile (GstMPDClient * client)
3004 return client->profile_isoff_ondemand;
3008 * gst_mpd_client_parse_default_presentation_delay:
3009 * @client: #GstMPDClient that has a parsed manifest
3010 * @default_presentation_delay: A string that specifies a time period
3011 * in fragments (e.g. "5 f"), seconds ("12 s") or milliseconds
3013 * Returns: the parsed string in milliseconds
3018 gst_mpd_client_parse_default_presentation_delay (GstMPDClient * client,
3019 const gchar * default_presentation_delay)
3022 char *endptr = NULL;
3024 g_return_val_if_fail (client != NULL, 0);
3025 g_return_val_if_fail (default_presentation_delay != NULL, 0);
3026 value = strtol (default_presentation_delay, &endptr, 10);
3027 if (endptr == default_presentation_delay || value == 0) {
3030 while (*endptr == ' ')
3032 if (*endptr == 's' || *endptr == 'S') {
3033 value *= 1000; /* convert to ms */
3034 } else if (*endptr == 'f' || *endptr == 'F') {
3035 gint64 segment_duration;
3036 g_assert (client->mpd_root_node != NULL);
3037 segment_duration = client->mpd_root_node->maxSegmentDuration;
3038 value *= segment_duration;
3039 } else if (*endptr != 'm' && *endptr != 'M') {
3040 GST_ERROR ("Unable to parse default presentation delay: %s",
3041 default_presentation_delay);
3048 gst_mpd_client_get_maximum_segment_duration (GstMPDClient * client)
3050 GstClockTime ret = GST_CLOCK_TIME_NONE, dur;
3053 g_return_val_if_fail (client != NULL, GST_CLOCK_TIME_NONE);
3054 g_return_val_if_fail (client->mpd_root_node != NULL, GST_CLOCK_TIME_NONE);
3056 if (client->mpd_root_node->maxSegmentDuration != GST_MPD_DURATION_NONE) {
3057 return client->mpd_root_node->maxSegmentDuration * GST_MSECOND;
3060 /* According to the DASH specification, if maxSegmentDuration is not present:
3061 "If not present, then the maximum Segment duration shall be the maximum
3062 duration of any Segment documented in this MPD"
3064 for (stream = client->active_streams; stream; stream = g_list_next (stream)) {
3065 dur = gst_mpd_client_get_segment_duration (client, stream->data, NULL);
3066 if (dur != GST_CLOCK_TIME_NONE && (dur > ret || ret == GST_CLOCK_TIME_NONE)) {
3074 gst_mpd_client_get_period_index_at_time (GstMPDClient * client,
3078 guint period_idx = G_MAXUINT;
3081 GstDateTime *avail_start =
3082 gst_mpd_client_get_availability_start_time (client);
3083 GstStreamPeriod *stream_period;
3085 if (avail_start == NULL)
3088 time_offset = gst_mpd_client_calculate_time_difference (avail_start, time);
3089 gst_date_time_unref (avail_start);
3091 if (time_offset < 0)
3094 if (!gst_mpd_client_setup_media_presentation (client, time_offset, -1, NULL))
3097 for (idx = 0, iter = client->periods; iter; idx++, iter = g_list_next (iter)) {
3098 stream_period = iter->data;
3099 if (stream_period->start <= time_offset
3100 && (!GST_CLOCK_TIME_IS_VALID (stream_period->duration)
3101 || stream_period->start + stream_period->duration > time_offset)) {
3110 /* add or set node methods */
3113 gst_mpd_client_set_root_node (GstMPDClient * client,
3114 const gchar * property_name, ...)
3117 g_return_val_if_fail (client != NULL, FALSE);
3119 if (!client->mpd_root_node)
3120 client->mpd_root_node = gst_mpd_root_node_new ();
3122 va_start (myargs, property_name);
3123 g_object_set_valist (G_OBJECT (client->mpd_root_node), property_name, myargs);
3130 gst_mpd_client_add_baseurl_node (GstMPDClient * client,
3131 const gchar * property_name, ...)
3133 GstMPDBaseURLNode *baseurl_node = NULL;
3136 g_return_val_if_fail (client != NULL, FALSE);
3137 g_return_val_if_fail (client->mpd_root_node != NULL, FALSE);
3139 va_start (myargs, property_name);
3141 baseurl_node = gst_mpd_baseurl_node_new ();
3142 g_object_set_valist (G_OBJECT (baseurl_node), property_name, myargs);
3143 client->mpd_root_node->BaseURLs =
3144 g_list_append (client->mpd_root_node->BaseURLs, baseurl_node);
3150 /* returns a period id */
3152 gst_mpd_client_set_period_node (GstMPDClient * client,
3153 gchar * period_id, const gchar * property_name, ...)
3155 GstMPDPeriodNode *period_node = NULL;
3158 g_return_val_if_fail (client != NULL, NULL);
3159 g_return_val_if_fail (client->mpd_root_node != NULL, NULL);
3162 GST_MPD_PERIOD_NODE (gst_mpd_client_get_period_with_id
3163 (client->mpd_root_node->Periods, period_id));
3165 period_node = gst_mpd_period_node_new ();
3167 period_node->id = g_strdup (period_id);
3170 _generate_new_string_id (client->mpd_root_node->Periods,
3171 "period_%.2d", gst_mpd_client_get_period_with_id);
3172 client->mpd_root_node->Periods =
3173 g_list_append (client->mpd_root_node->Periods, period_node);
3176 va_start (myargs, property_name);
3177 g_object_set_valist (G_OBJECT (period_node), property_name, myargs);
3180 return period_node->id;
3183 /* returns an adaptation set id */
3185 gst_mpd_client_set_adaptation_set_node (GstMPDClient * client,
3186 gchar * period_id, guint adaptation_set_id, const gchar * property_name,
3189 GstMPDAdaptationSetNode *adap_node = NULL;
3190 GstMPDPeriodNode *period_node = NULL;
3193 g_return_val_if_fail (client != NULL, 0);
3194 g_return_val_if_fail (client->mpd_root_node != NULL, 0);
3197 GST_MPD_PERIOD_NODE (gst_mpd_client_get_period_with_id
3198 (client->mpd_root_node->Periods, period_id));
3199 g_return_val_if_fail (period_node != NULL, 0);
3201 GST_MPD_ADAPTATION_SET_NODE (gst_mpd_client_get_adaptation_set_with_id
3202 (period_node->AdaptationSets, adaptation_set_id));
3204 adap_node = gst_mpd_adaptation_set_node_new ();
3205 if (adaptation_set_id)
3206 adap_node->id = adaptation_set_id;
3209 _generate_new_id (period_node->AdaptationSets,
3210 gst_mpd_client_get_adaptation_set_with_id);
3211 GST_DEBUG_OBJECT (client, "Add a new adaptation set with id %d",
3213 period_node->AdaptationSets =
3214 g_list_append (period_node->AdaptationSets, adap_node);
3217 va_start (myargs, property_name);
3218 g_object_set_valist (G_OBJECT (adap_node), property_name, myargs);
3221 return adap_node->id;
3224 /* returns a representation id */
3226 gst_mpd_client_set_representation_node (GstMPDClient * client,
3227 gchar * period_id, guint adaptation_set_id, gchar * representation_id,
3228 const gchar * property_name, ...)
3230 GstMPDRepresentationNode *rep_node = NULL;
3231 GstMPDAdaptationSetNode *adap_set_node = NULL;
3232 GstMPDPeriodNode *period_node = NULL;
3235 g_return_val_if_fail (client != NULL, NULL);
3236 g_return_val_if_fail (client->mpd_root_node != NULL, NULL);
3239 GST_MPD_PERIOD_NODE (gst_mpd_client_get_period_with_id
3240 (client->mpd_root_node->Periods, period_id));
3242 GST_MPD_ADAPTATION_SET_NODE (gst_mpd_client_get_adaptation_set_with_id
3243 (period_node->AdaptationSets, adaptation_set_id));
3244 g_return_val_if_fail (adap_set_node != NULL, NULL);
3246 GST_MPD_REPRESENTATION_NODE (gst_mpd_client_get_representation_with_id
3247 (adap_set_node->Representations, representation_id));
3249 rep_node = gst_mpd_representation_node_new ();
3250 if (representation_id)
3251 rep_node->id = g_strdup (representation_id);
3254 _generate_new_string_id (adap_set_node->Representations,
3255 "representation_%.2d", gst_mpd_client_get_representation_with_id);
3256 GST_DEBUG_OBJECT (client, "Add a new representation with id %s",
3258 adap_set_node->Representations =
3259 g_list_append (adap_set_node->Representations, rep_node);
3262 va_start (myargs, property_name);
3263 g_object_set_valist (G_OBJECT (rep_node), property_name, myargs);
3266 return rep_node->id;
3269 /* add/set a segment list node */
3271 gst_mpd_client_set_segment_list (GstMPDClient * client,
3272 gchar * period_id, guint adap_set_id, gchar * rep_id,
3273 const gchar * property_name, ...)
3275 GstMPDRepresentationNode *representation = NULL;
3276 GstMPDAdaptationSetNode *adaptation_set = NULL;
3277 GstMPDPeriodNode *period = NULL;
3280 g_return_val_if_fail (client != NULL, FALSE);
3281 g_return_val_if_fail (client->mpd_root_node != NULL, FALSE);
3284 GST_MPD_PERIOD_NODE (gst_mpd_client_get_period_with_id
3285 (client->mpd_root_node->Periods, period_id));
3287 GST_MPD_ADAPTATION_SET_NODE (gst_mpd_client_get_adaptation_set_with_id
3288 (period->AdaptationSets, adap_set_id));
3289 g_return_val_if_fail (adaptation_set != NULL, FALSE);
3292 GST_MPD_REPRESENTATION_NODE (gst_mpd_client_get_representation_with_id
3293 (adaptation_set->Representations, rep_id));
3294 if (!representation->SegmentList) {
3295 representation->SegmentList = gst_mpd_segment_list_node_new ();
3298 va_start (myargs, property_name);
3299 g_object_set_valist (G_OBJECT (representation->SegmentList), property_name,
3306 /* add/set a segment template node */
3308 gst_mpd_client_set_segment_template (GstMPDClient * client,
3309 gchar * period_id, guint adap_set_id, gchar * rep_id,
3310 const gchar * property_name, ...)
3312 GstMPDRepresentationNode *representation = NULL;
3313 GstMPDAdaptationSetNode *adaptation_set = NULL;
3314 GstMPDPeriodNode *period = NULL;
3317 g_return_val_if_fail (client != NULL, FALSE);
3318 g_return_val_if_fail (client->mpd_root_node != NULL, FALSE);
3321 GST_MPD_PERIOD_NODE (gst_mpd_client_get_period_with_id
3322 (client->mpd_root_node->Periods, period_id));
3324 GST_MPD_ADAPTATION_SET_NODE (gst_mpd_client_get_adaptation_set_with_id
3325 (period->AdaptationSets, adap_set_id));
3326 g_return_val_if_fail (adaptation_set != NULL, FALSE);
3329 GST_MPD_REPRESENTATION_NODE (gst_mpd_client_get_representation_with_id
3330 (adaptation_set->Representations, rep_id));
3331 if (!representation->SegmentTemplate) {
3332 representation->SegmentTemplate = gst_mpd_segment_template_node_new ();
3335 va_start (myargs, property_name);
3336 g_object_set_valist (G_OBJECT (representation->SegmentTemplate),
3337 property_name, myargs);
3343 /* add a segmentURL node with to a SegmentList node */
3345 gst_mpd_client_add_segment_url (GstMPDClient * client,
3346 gchar * period_id, guint adap_set_id, gchar * rep_id,
3347 const gchar * property_name, ...)
3349 GstMPDRepresentationNode *representation = NULL;
3350 GstMPDAdaptationSetNode *adaptation_set = NULL;
3351 GstMPDPeriodNode *period = NULL;
3352 GstMPDSegmentURLNode *segment_url = NULL;
3353 guint64 media_presentation_duration = 0;
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_client_get_period_with_id
3361 (client->mpd_root_node->Periods, period_id));
3363 GST_MPD_ADAPTATION_SET_NODE (gst_mpd_client_get_adaptation_set_with_id
3364 (period->AdaptationSets, adap_set_id));
3365 g_return_val_if_fail (adaptation_set != NULL, FALSE);
3368 GST_MPD_REPRESENTATION_NODE (gst_mpd_client_get_representation_with_id
3369 (adaptation_set->Representations, rep_id));
3371 if (!representation->SegmentList) {
3372 representation->SegmentList = gst_mpd_segment_list_node_new ();
3375 segment_url = gst_mpd_segment_url_node_new ();
3377 va_start (myargs, property_name);
3378 g_object_set_valist (G_OBJECT (segment_url), property_name, myargs);
3381 gst_mpd_segment_list_node_add_segment (representation->SegmentList,
3384 /* Set the media presentation time according to the new segment duration added */
3385 g_object_get (client->mpd_root_node, "media-presentation-duration",
3386 &media_presentation_duration, NULL);
3387 media_presentation_duration +=
3388 GST_MPD_MULT_SEGMENT_BASE_NODE (representation->SegmentList)->duration;
3389 g_object_set (client->mpd_root_node, "media-presentation-duration",
3390 media_presentation_duration, NULL);