1 /**
2  *			GPAC - Multimedia Framework C SDK
3  *
4  *			Authors: Jean Le Feuvre, Cyril Concolato
5  *			Copyright (c) Telecom ParisTech 2000-2020
6  *					All rights reserved
7  *
8  *  This file is part of GPAC / 3GPP/MPEG Media Presentation Description input module
9  *
10  *  GPAC is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU Lesser General Public License as published by
12  *  the Free Software Foundation; either version 2, or (at your option)
13  *  any later version.
14  *
15  *  GPAC is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU Lesser General Public License for more details.
19  *
20  *  You should have received a copy of the GNU Lesser General Public
21  *  License along with this library; see the file COPYING.  If not, write to
22  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23  *
24  */
25 
26 #include <gpac/mpd.h>
27 #include <gpac/download.h>
28 #include <gpac/internal/m3u8.h>
29 #include <gpac/network.h>
30 #include <gpac/maths.h>
31 
32 #ifndef GPAC_DISABLE_CORE_TOOLS
33 
gf_mpd_parse_bool(const char * const attr)34 static Bool gf_mpd_parse_bool(const char * const attr)
35 {
36 	if (!strcmp(attr, "true")) return 1;
37 	if (!strcmp(attr, "1")) return 1;
38 	return 0;
39 }
40 
gf_mpd_parse_string(char * attr)41 static char *gf_mpd_parse_string(char *attr)
42 {
43 	return gf_strdup(attr);
44 }
45 
46 
gf_mpd_valid_child(GF_MPD * mpd,GF_XMLNode * child)47 static Bool gf_mpd_valid_child(GF_MPD *mpd, GF_XMLNode *child)
48 {
49 	if (child->type != GF_XML_NODE_TYPE) return 0;
50 	if (!mpd->xml_namespace && !child->ns) return 1;
51 	if (mpd->xml_namespace && child->ns && !strcmp(mpd->xml_namespace, child->ns)) return 1;
52 	if (child->ns && !strcmp(child->ns, "gpac")) return 1;
53 	return 0;
54 }
55 
gf_mpd_is_known_descriptor(GF_XMLNode * child)56 static Bool gf_mpd_is_known_descriptor(GF_XMLNode *child)
57 {
58 	if (!strcmp(child->name, "FramePacking")  ||
59 	!strcmp(child->name, "AudioChannelConfiguration") ||
60 	!strcmp(child->name, "ContentProtection") ||
61 	!strcmp(child->name, "EssentialProperty") ||
62 	!strcmp(child->name, "SupplementalProperty")){
63 		return GF_TRUE;
64 	}
65 	else{
66 		return GF_FALSE;
67 	}
68 }
69 
gf_mpd_parse_other_descriptors(GF_XMLNode * child,GF_List * other_desc)70 static void gf_mpd_parse_other_descriptors(GF_XMLNode *child, GF_List *other_desc)
71 {
72 	if(!gf_mpd_is_known_descriptor(child)){
73 		char *descriptors=gf_xml_dom_serialize(child,GF_FALSE);
74 		GF_MPD_other_descriptors *Desc;
75 		GF_SAFEALLOC(Desc,GF_MPD_other_descriptors);
76 		if (!Desc) return;
77 
78 		Desc->xml_desc=descriptors;
79 		gf_list_add(other_desc, Desc);
80 	}
81 }
82 
gf_mpd_parse_text_content(GF_XMLNode * child)83 static char *gf_mpd_parse_text_content(GF_XMLNode *child)
84 {
85 	u32 child_index = 0;
86 	while (1) {
87 		child = gf_list_get(child->content, child_index);
88 		if (!child) {
89 			break;
90 		} else if (child->type == GF_XML_TEXT_TYPE) {
91 			return gf_mpd_parse_string(child->name);
92 		}
93 		child_index++;
94 	}
95 	return NULL;
96 }
97 
gf_mpd_parse_int(const char * const attr)98 static u32 gf_mpd_parse_int(const char * const attr)
99 {
100 	return atoi(attr);
101 }
102 
gf_mpd_parse_long_int(const char * const attr)103 static u64 gf_mpd_parse_long_int(const char * const attr)
104 {
105 	u64 longint;
106 	sscanf(attr, LLU, &longint);
107 	return longint;
108 }
109 
gf_mpd_parse_double(const char * const attr)110 static Double gf_mpd_parse_double(const char * const attr)
111 {
112 	return atof(attr);
113 }
114 
gf_mpd_parse_frac(const char * const attr,const char sep,GF_MPD_Fractional * res)115 static GF_MPD_Fractional *gf_mpd_parse_frac(const char * const attr, const char sep, GF_MPD_Fractional *res)
116 {
117 	char str[6];
118 	int ok;
119 	if (res==NULL) {
120 		GF_SAFEALLOC(res, GF_MPD_Fractional);
121 		if (!res) return NULL;
122 		res->den = 1;
123 	}
124 	snprintf(str, sizeof(str), "%%d%c%%d", sep);
125 	ok = sscanf(attr, str, &res->num, &res->den);
126 	if (ok!=2) {
127 		res->den = 1;
128 		res->num = atoi(attr);
129 	}
130 	return res;
131 }
132 
gf_mpd_parse_date(const char * const attr)133 static u64 gf_mpd_parse_date(const char * const attr)
134 {
135 	return gf_net_parse_date(attr);
136 }
137 
gf_mpd_parse_duration(const char * const duration)138 static u64 gf_mpd_parse_duration(const char * const duration)
139 {
140 	u32 i;
141 	char *sep1, *sep2;
142 	u32 h, m, s, ms;
143 	u32 year, month, day;
144 	Bool has_year, has_month, has_day;
145 	u64 y_dur;
146 	const char *startT;
147 	if (!duration) {
148 		GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[MPD] Error parsing duration: no value indicated\n"));
149 		return 0;
150 	}
151 	i = 0;
152 	while (1) {
153 		if (duration[i] == ' ') i++;
154 		else if (duration[i] == 0) return 0;
155 		else {
156 			break;
157 		}
158 	}
159 	if (duration[i] != 'P') {
160 		GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[MPD] Error parsing duration: no value indicated\n"));
161 		return 0;
162 	}
163 	startT = strchr(duration+1, 'T');
164 
165 	if (duration[i+1] == 0) {
166 		GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[MPD] Error parsing duration: no value indicated\n"));
167 		return 0;
168 	}
169 
170 	year = month = day = 0;
171 	has_year = strchr(duration+1, 'Y') ? GF_TRUE : GF_FALSE;
172 	has_month = strchr(duration+1, 'M') ? GF_TRUE : GF_FALSE;
173 	has_day = strchr(duration+1, 'D') ? GF_TRUE : GF_FALSE;
174 	if (has_year && has_month && has_day) sscanf(duration+1, "%dY%dM%dD", &year, &month, &day);
175 	else if (has_month && has_day) sscanf(duration+1, "%dM%dD", &month, &day);
176 	else if (has_year && has_month) sscanf(duration+1, "%dY%dM", &year, &month);
177 	else if (has_year && has_day) sscanf(duration+1, "%dY%dD", &year, &day);
178 	else if (has_year) sscanf(duration+1, "%dY", &year);
179 	else if (has_month) sscanf(duration+1, "%dM", &month);
180 	else if (has_day) sscanf(duration+1, "%dD", &day);
181 	y_dur = (year*365 + month*30 + day ) * 24;
182 	y_dur *= 3600;
183 	y_dur *= 1000;
184 
185 	if (! startT) return y_dur;
186 
187 	h = m = s = ms = 0;
188 	if (NULL != (sep1 = strchr(startT+1, 'H'))) {
189 		*sep1 = 0;
190 		h = atoi(duration+i+2);
191 		*sep1 = 'H';
192 		sep1++;
193 	} else {
194 		sep1 = (char *) startT+1;
195 	}
196 	if (NULL != (sep2 = strchr(sep1, 'M'))) {
197 		*sep2 = 0;
198 		m = atoi(sep1);
199 		*sep2 = 'M';
200 		sep2++;
201 	} else {
202 		sep2 = sep1;
203 	}
204 	if (NULL != (sep1 = strchr(sep2, 'S'))) {
205 		char *sep_dec = strchr(sep2, '.');
206 		*sep1 = 0;
207 		if (sep_dec) {
208 			sep_dec[0] = 0;
209 			s = atoi(sep2);
210 			ms = atoi(sep_dec+1);
211 			sep_dec[0] = '.';
212 		} else {
213 			s = atoi(sep2);
214 		}
215 		*sep1 = 'S';
216 	}
217 	return y_dur + (u64) ( ((h*3600+m*60+s)*(u64)1000) + ms );
218 }
219 
gf_mpd_parse_duration_u32(const char * const duration)220 static u32 gf_mpd_parse_duration_u32(const char * const duration)
221 {
222 	u64 dur = gf_mpd_parse_duration(duration);
223 	if (dur <= GF_UINT_MAX) {
224 		return (u32)dur;
225 	} else {
226 		GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[MPD] Parsed duration %s ("LLU") doesn't fit on 32 bits! Setting to the 32 bits max.\n", duration, dur));
227 		return GF_UINT_MAX;
228 	}
229 }
230 
gf_mpd_parse_byte_range(const char * const attr)231 static GF_MPD_ByteRange *gf_mpd_parse_byte_range(const char * const attr)
232 {
233 	GF_MPD_ByteRange *br;
234 	GF_SAFEALLOC(br, GF_MPD_ByteRange);
235 	if (!br) return NULL;
236 	sscanf(attr, LLD"-"LLD, &br->start_range, &br->end_range);
237 	return br;
238 }
239 
gf_mpd_parse_base_url(GF_List * container,GF_XMLNode * node)240 GF_Err gf_mpd_parse_base_url(GF_List *container, GF_XMLNode *node)
241 {
242 	u32 i;
243 	GF_Err e;
244 	GF_XMLAttribute *att;
245 	GF_MPD_BaseURL *url;
246 	GF_SAFEALLOC(url, GF_MPD_BaseURL);
247 	if (! url) return GF_OUT_OF_MEM;
248 	e = gf_list_add(container, url);
249 	if (e) return GF_OUT_OF_MEM;
250 
251 	i = 0;
252 	while ( (att = gf_list_enum(node->attributes, &i))) {
253 		if (!strcmp(att->name, "serviceLocation")) url->service_location = gf_mpd_parse_string(att->value);
254 		else if (!strcmp(att->name, "byteRange")) url->byte_range = gf_mpd_parse_byte_range(att->value);
255 	}
256 	url->URL = gf_mpd_parse_text_content(node);
257 	return GF_OK;
258 }
259 
gf_mpd_parse_program_info(GF_MPD * mpd,GF_XMLNode * root)260 static GF_Err gf_mpd_parse_program_info(GF_MPD *mpd, GF_XMLNode *root)
261 {
262 	GF_MPD_ProgramInfo *info;
263 	u32 att_index, child_index;
264 
265 	GF_SAFEALLOC(info, GF_MPD_ProgramInfo);
266 	if (!info) return GF_OUT_OF_MEM;
267 
268 	att_index = 0;
269 	while (1) {
270 		GF_XMLAttribute *att = gf_list_get(root->attributes, att_index);
271 		if (!att) {
272 			break;
273 		} else if (!strcmp(att->name, "moreInformationURL")) {
274 			info->more_info_url = gf_mpd_parse_string(att->value);
275 		} else if (!strcmp(att->name, "lang")) {
276 			info->lang = gf_mpd_parse_string(att->value);
277 		}
278 		att_index++;
279 	}
280 
281 	child_index = 0;
282 	while (1) {
283 		GF_XMLNode *child = gf_list_get(root->content, child_index);
284 		if (!child) {
285 			break;
286 		} else if (child->type == GF_XML_NODE_TYPE) {
287 			if (!strcmp(child->name, "Title")) {
288 				GF_XMLNode *data_node = gf_list_get(child->content, 0);
289 				if (data_node && data_node->type == GF_XML_TEXT_TYPE) {
290 					info->title = gf_strdup(data_node->name);
291 				}
292 			} else if (!strcmp(child->name, "Source")) {
293 				GF_XMLNode *data_node = gf_list_get(child->content, 0);
294 				if (data_node && data_node->type == GF_XML_TEXT_TYPE) {
295 					info->source = gf_strdup(data_node->name);
296 				}
297 			} else if (!strcmp(child->name, "Copyright")) {
298 				GF_XMLNode *data_node = gf_list_get(child->content, 0);
299 				if (data_node && data_node->type == GF_XML_TEXT_TYPE) {
300 					info->copyright = gf_strdup(data_node->name);
301 				}
302 			}
303 		}
304 		child_index++;
305 	}
306 	return gf_list_add(mpd->program_infos, info);
307 }
308 
gf_mpd_parse_url(GF_XMLNode * root)309 static GF_MPD_URL *gf_mpd_parse_url(GF_XMLNode *root)
310 {
311 	u32 i;
312 	GF_MPD_URL *url;
313 	GF_XMLAttribute *att;
314 
315 	GF_SAFEALLOC(url, GF_MPD_URL);
316 	if (!url) return NULL;
317 
318 	i = 0;
319 	while ( (att = gf_list_enum(root->attributes, &i)) ) {
320 		if (!strcmp(att->name, "sourceURL")) url->sourceURL = gf_mpd_parse_string(att->value);
321 		else if (!strcmp(att->name, "range")) url->byte_range = gf_mpd_parse_byte_range(att->value);
322 	}
323 	return url;
324 }
325 
gf_mpd_parse_segment_base_generic(GF_MPD * mpd,GF_MPD_SegmentBase * seg,GF_XMLNode * root)326 static void gf_mpd_parse_segment_base_generic(GF_MPD *mpd, GF_MPD_SegmentBase *seg, GF_XMLNode *root)
327 {
328 	GF_XMLAttribute *att;
329 	GF_XMLNode *child;
330 	u32 i = 0;
331 
332 	/*0 by default*/
333 	seg->time_shift_buffer_depth = 0;
334 
335 	while ( (att = gf_list_enum(root->attributes, &i)) ) {
336 		if (!strcmp(att->name, "timescale")) seg->timescale = gf_mpd_parse_int(att->value);
337 		else if (!strcmp(att->name, "presentationTimeOffset")) seg->presentation_time_offset = gf_mpd_parse_long_int(att->value);
338 		else if (!strcmp(att->name, "indexRange")) seg->index_range = gf_mpd_parse_byte_range(att->value);
339 		else if (!strcmp(att->name, "indexRangeExact")) seg->index_range_exact = gf_mpd_parse_bool(att->value);
340 		else if (!strcmp(att->name, "availabilityTimeOffset")) seg->availability_time_offset = gf_mpd_parse_double(att->value);
341 		else if (!strcmp(att->name, "timeShiftBufferDepth")) seg->time_shift_buffer_depth = gf_mpd_parse_duration_u32(att->value);
342 	}
343 
344 	i = 0;
345 	while ( (child = gf_list_enum(root->content, &i))) {
346 		if (!gf_mpd_valid_child(mpd, child)) continue;
347 		if (!strcmp(child->name, "Initialization")) seg->initialization_segment = gf_mpd_parse_url(child);
348 		else if (!strcmp(child->name, "RepresentationIndex")) seg->representation_index = gf_mpd_parse_url(child);
349 	}
350 }
351 
gf_mpd_parse_segment_timeline(GF_MPD * mpd,GF_XMLNode * root)352 static GF_MPD_SegmentTimeline *gf_mpd_parse_segment_timeline(GF_MPD *mpd, GF_XMLNode *root)
353 {
354 	u32 i, j;
355 	GF_XMLAttribute *att;
356 	GF_XMLNode *child;
357 	GF_MPD_SegmentTimeline *seg;
358 	GF_SAFEALLOC(seg, GF_MPD_SegmentTimeline);
359 	if (!seg) return NULL;
360 	seg->entries = gf_list_new();
361 
362 	i = 0;
363 	while ( (child = gf_list_enum(root->content, &i))) {
364 		if (!gf_mpd_valid_child(mpd, child)) continue;
365 		if (!strcmp(child->name, "S")) {
366 			GF_MPD_SegmentTimelineEntry *seg_tl_ent;
367 			GF_SAFEALLOC(seg_tl_ent, GF_MPD_SegmentTimelineEntry);
368 			if (!seg_tl_ent) continue;
369 			gf_list_add(seg->entries, seg_tl_ent);
370 
371 			j = 0;
372 			while ( (att = gf_list_enum(child->attributes, &j)) ) {
373 				if (!strcmp(att->name, "t"))
374 					seg_tl_ent->start_time = gf_mpd_parse_long_int(att->value);
375 				else if (!strcmp(att->name, "d"))
376 					seg_tl_ent->duration = gf_mpd_parse_int(att->value);
377 				else if (!strcmp(att->name, "r")) {
378 					seg_tl_ent->repeat_count = gf_mpd_parse_int(att->value);
379 					if (seg_tl_ent->repeat_count == (u32)-1)
380 						seg_tl_ent->repeat_count--;
381 				}
382 			}
383 		}
384 	}
385 	return seg;
386 }
387 
gf_mpd_parse_segment_base(GF_MPD * mpd,GF_XMLNode * root)388 static GF_MPD_SegmentBase *gf_mpd_parse_segment_base(GF_MPD *mpd, GF_XMLNode *root)
389 {
390 	GF_MPD_SegmentBase *seg;
391 	GF_SAFEALLOC(seg, GF_MPD_SegmentBase);
392 	if (!seg) return NULL;
393 	gf_mpd_parse_segment_base_generic(mpd, seg, root);
394 	return seg;
395 }
396 
gf_mpd_parse_multiple_segment_base(GF_MPD * mpd,GF_MPD_MultipleSegmentBase * seg,GF_XMLNode * root)397 void gf_mpd_parse_multiple_segment_base(GF_MPD *mpd, GF_MPD_MultipleSegmentBase *seg, GF_XMLNode *root)
398 {
399 	u32 i;
400 	GF_XMLAttribute *att;
401 	GF_XMLNode *child;
402 
403 	gf_mpd_parse_segment_base_generic(mpd, (GF_MPD_SegmentBase*)seg, root);
404 	seg->start_number = (u32) -1;
405 
406 	i = 0;
407 	while ( (att = gf_list_enum(root->attributes, &i)) ) {
408 		if (!strcmp(att->name, "duration")) seg->duration = gf_mpd_parse_int(att->value);
409 		else if (!strcmp(att->name, "startNumber")) seg->start_number = gf_mpd_parse_int(att->value);
410 	}
411 
412 	i = 0;
413 	while ( (child = gf_list_enum(root->content, &i))) {
414 		if (!gf_mpd_valid_child(mpd, child)) continue;
415 		if (!strcmp(child->name, "SegmentTimeline")) seg->segment_timeline = gf_mpd_parse_segment_timeline(mpd, child);
416 		else if (!strcmp(child->name, "BitstreamSwitching")) seg->bitstream_switching_url = gf_mpd_parse_url(child);
417 	}
418 }
419 
420 #if 0 //unused
421 GF_MPD_SegmentURL *gf_mpd_segmenturl_new(const char*media, u64 start_range, u64 end_range, const char *index, u64 idx_start_range, u64 idx_end_range)
422 {
423 	GF_MPD_SegmentURL *seg_url;
424 	GF_SAFEALLOC(seg_url, GF_MPD_SegmentURL);
425 	GF_SAFEALLOC(seg_url->media_range, GF_MPD_ByteRange);
426 	seg_url->media_range->start_range = start_range;
427 	seg_url->media_range->end_range = end_range;
428 	if (idx_start_range || idx_end_range) {
429 		GF_SAFEALLOC(seg_url->index_range, GF_MPD_ByteRange);
430 		seg_url->index_range->start_range = idx_start_range;
431 		seg_url->index_range->end_range = idx_end_range;
432 	}
433 	if(media)
434 		seg_url->media=gf_strdup(media);
435 	return seg_url;
436 }
437 #endif
438 
gf_mpd_parse_segment_url(GF_List * container,GF_XMLNode * root)439 void gf_mpd_parse_segment_url(GF_List *container, GF_XMLNode *root)
440 {
441 	u32 i;
442 	GF_MPD_SegmentURL *seg;
443 	GF_XMLAttribute *att;
444 
445 	GF_SAFEALLOC(seg, GF_MPD_SegmentURL);
446 	if (!seg) return;
447 	gf_list_add(container, seg);
448 
449 	i = 0;
450 	while ( (att = gf_list_enum(root->attributes, &i)) ) {
451 		if (!strcmp(att->name, "media")) seg->media = gf_mpd_parse_string(att->value);
452 		else if (!strcmp(att->name, "index")) seg->index = gf_mpd_parse_string(att->value);
453 		else if (!strcmp(att->name, "mediaRange")) seg->media_range = gf_mpd_parse_byte_range(att->value);
454 		else if (!strcmp(att->name, "indexRange")) seg->index_range = gf_mpd_parse_byte_range(att->value);
455 		//else if (!strcmp(att->name, "hls:keyMethod")) seg->key_url = gf_mpd_parse_string(att->value);
456 		else if (!strcmp(att->name, "hls:keyURL")) seg->key_url = gf_mpd_parse_string(att->value);
457 		else if (!strcmp(att->name, "hls:keyIV")) {
458 			GF_Err e = gf_bin128_parse(att->value, seg->key_iv);
459             if (e != GF_OK) {
460                 GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[MPD] Cannot parse hls:keyIV\n"));
461                 return;
462             }
463         }
464 		else if (!strcmp(att->name, "duration")) seg->duration=gf_mpd_parse_int(att->value);
465 	}
466 }
467 
gf_mpd_parse_segment_list(GF_MPD * mpd,GF_XMLNode * root)468 static GF_MPD_SegmentList *gf_mpd_parse_segment_list(GF_MPD *mpd, GF_XMLNode *root)
469 {
470 	u32 i;
471 	GF_MPD_SegmentList *seg;
472 	GF_XMLAttribute *att;
473 	GF_XMLNode *child;
474 
475 	GF_SAFEALLOC(seg, GF_MPD_SegmentList);
476 	if (!seg) return NULL;
477 	seg->segment_URLs = gf_list_new();
478 
479 	i = 0;
480 	while ( (att = gf_list_enum(root->attributes, &i)) ) {
481 		if (strstr(att->name, "href")) seg->xlink_href = gf_mpd_parse_string(att->value);
482 		else if (strstr(att->name, "actuate")) seg->xlink_actuate_on_load = !strcmp(att->value, "onLoad") ? 1 : 0;
483 	}
484 	gf_mpd_parse_multiple_segment_base(mpd, (GF_MPD_MultipleSegmentBase *)seg, root);
485 
486 	i = 0;
487 	while ( (child = gf_list_enum(root->content, &i))) {
488 		if (!gf_mpd_valid_child(mpd, child)) continue;
489 		if (!strcmp(child->name, "SegmentURL")) gf_mpd_parse_segment_url(seg->segment_URLs, child);
490 	}
491 	if (!gf_list_count(seg->segment_URLs)) {
492 		gf_list_del(seg->segment_URLs);
493 		seg->segment_URLs = NULL;
494 	}
495 	return seg;
496 }
497 
gf_mpd_parse_segment_template(GF_MPD * mpd,GF_XMLNode * root)498 static GF_MPD_SegmentTemplate *gf_mpd_parse_segment_template(GF_MPD *mpd, GF_XMLNode *root)
499 {
500 	u32 i;
501 	GF_MPD_SegmentTemplate *seg;
502 	GF_XMLAttribute *att;
503 
504 	GF_SAFEALLOC(seg, GF_MPD_SegmentTemplate);
505 	if (!seg) return NULL;
506 
507 	i = 0;
508 	while ( (att = gf_list_enum(root->attributes, &i)) ) {
509 		if (!strcmp(att->name, "media")) seg->media = gf_mpd_parse_string(att->value);
510 		else if (!strcmp(att->name, "index")) seg->index = gf_mpd_parse_string(att->value);
511 		else if (!strcmp(att->name, "initialization") ) seg->initialization = gf_mpd_parse_string(att->value);
512 		else if (!stricmp(att->name, "initialisation") || !stricmp(att->name, "initialization") ) {
513 			GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[MPD] Wrong spelling: got %s but expected \"initialization\" \n", att->name ));
514 			seg->initialization = gf_mpd_parse_string(att->value);
515 		}
516 		else if (!strcmp(att->name, "bitstreamSwitching")) seg->bitstream_switching = gf_mpd_parse_string(att->value);
517 	}
518 	gf_mpd_parse_multiple_segment_base(mpd, (GF_MPD_MultipleSegmentBase *)seg, root);
519 	return seg;
520 }
521 
gf_mpd_parse_content_component(GF_List * comps,GF_XMLNode * root)522 static GF_Err gf_mpd_parse_content_component(GF_List *comps, GF_XMLNode *root)
523 {
524 	u32 i;
525 	GF_XMLAttribute *att;
526 	GF_MPD_ContentComponent *comp;
527 	GF_SAFEALLOC(comp, GF_MPD_ContentComponent);
528 	if (!comp) return GF_OUT_OF_MEM;
529 	i = 0;
530 	while ((att = gf_list_enum(root->attributes, &i))) {
531 		if (!strcmp(att->name, "id")) comp->id = atoi(att->value);
532 		else if (!strcmp(att->name, "contentType")) comp->type = gf_strdup(att->value);
533 		else if (!strcmp(att->name, "lang")) comp->lang = gf_strdup(att->value);
534 	}
535 	gf_list_add(comps, comp);
536 	return GF_OK;
537 }
538 
539 #define MPD_STORE_EXTENSION_ATTR(_elem)	\
540 			if (!_elem->attributes) _elem->attributes = gf_list_new();	\
541 			i--;	\
542 			gf_list_rem(root->attributes, i);	\
543 			gf_list_add(_elem->attributes, att);	\
544 
545 #define MPD_STORE_EXTENSION_NODE(_elem)	\
546 		if (!_elem->children) _elem->children = gf_list_new();	\
547 		i--;	\
548 		gf_list_rem(root->content, i);	\
549 		gf_list_add(_elem->children, child);	\
550 
gf_mpd_parse_descriptor(GF_List * container,GF_XMLNode * root)551 static GF_Err gf_mpd_parse_descriptor(GF_List *container, GF_XMLNode *root)
552 {
553 	GF_XMLAttribute *att;
554 	GF_XMLNode *child;
555 	GF_MPD_Descriptor *mpd_desc;
556 	u32 i = 0;
557 
558 	GF_SAFEALLOC(mpd_desc, GF_MPD_Descriptor);
559 	if (!mpd_desc) return GF_OUT_OF_MEM;
560 
561 	while ( (att = gf_list_enum(root->attributes, &i)) ) {
562 		if (!strcmp(att->name, "schemeIdUri")) mpd_desc->scheme_id_uri = gf_mpd_parse_string(att->value);
563 		else if (!strcmp(att->name, "value")) mpd_desc->value = gf_mpd_parse_string(att->value);
564 		else if (!strcmp(att->name, "id")) mpd_desc->id = gf_mpd_parse_string(att->value);
565 		else {
566 			MPD_STORE_EXTENSION_ATTR(mpd_desc)
567 		}
568 	}
569 	gf_list_add(container, mpd_desc);
570 
571 	i = 0;
572 	while ( (child = gf_list_enum(root->content, &i))) {
573 		if (child->type != GF_XML_NODE_TYPE) continue;
574 
575 		MPD_STORE_EXTENSION_NODE(mpd_desc)
576 
577 	}
578 
579 	return GF_OK;
580 }
581 
gf_mpd_parse_common_representation(GF_MPD * mpd,GF_MPD_CommonAttributes * com,GF_XMLNode * root)582 static void gf_mpd_parse_common_representation(GF_MPD *mpd, GF_MPD_CommonAttributes *com, GF_XMLNode *root)
583 {
584 	GF_XMLAttribute *att;
585 	GF_XMLNode *child;
586 	u32 i = 0;
587 
588 	com->max_playout_rate = 1.0;
589 
590 	while ( (att = gf_list_enum(root->attributes, &i)) ) {
591 		if (!strcmp(att->name, "profiles")) com->profiles = gf_mpd_parse_string(att->value);
592 		else if (!strcmp(att->name, "width")) com->width = gf_mpd_parse_int(att->value);
593 		else if (!strcmp(att->name, "height")) com->height = gf_mpd_parse_int(att->value);
594 		else if (!strcmp(att->name, "sar")) {
595 			if (com->sar) gf_free(com->sar);
596 			com->sar = gf_mpd_parse_frac(att->value, ':', NULL);
597 		}
598 		else if (!strcmp(att->name, "frameRate")) {
599 			if (com->framerate) gf_free(com->framerate);
600 			com->framerate = gf_mpd_parse_frac(att->value, '/', NULL);
601 		}
602 		else if (!strcmp(att->name, "audioSamplingRate")) com->samplerate = gf_mpd_parse_int(att->value);
603 		else if (!strcmp(att->name, "mimeType")) com->mime_type = gf_mpd_parse_string(att->value);
604 		else if (!strcmp(att->name, "segmentProfiles")) com->segmentProfiles = gf_mpd_parse_string(att->value);
605 		else if (!strcmp(att->name, "codecs")) com->codecs = gf_mpd_parse_string(att->value);
606 		else if (!strcmp(att->name, "maximumSAPPeriod")) com->maximum_sap_period = gf_mpd_parse_int(att->value);
607 		else if (!strcmp(att->name, "startWithSAP")) {
608 			if (!strcmp(att->value, "false")) com->starts_with_sap = 0;
609 			else com->starts_with_sap = gf_mpd_parse_int(att->value);
610 		}
611 		else if (!strcmp(att->name, "maxPlayoutRate")) com->max_playout_rate = gf_mpd_parse_double(att->value);
612 		else if (!strcmp(att->name, "codingDependency")) com->coding_dependency = gf_mpd_parse_bool(att->value);
613 		else if (!strcmp(att->name, "scanType")) {
614 			if (!strcmp(att->value, "progressive")) com->scan_type = GF_MPD_SCANTYPE_PROGRESSIVE;
615 			else if (!strcmp(att->value, "interlaced")) com->scan_type = GF_MPD_SCANTYPE_INTERLACED;
616 		}
617 	}
618 
619 	i = 0;
620 	while ( (child = gf_list_enum(root->content, &i))) {
621 		if (!gf_mpd_valid_child(mpd, child)) continue;
622 		if (!strcmp(child->name, "FramePacking")) {
623 			gf_mpd_parse_descriptor(com->frame_packing, child);
624 		}
625 		else if (!strcmp(child->name, "AudioChannelConfiguration")) {
626 			gf_mpd_parse_descriptor(com->audio_channels, child);
627 		}
628 		else if (!strcmp(child->name, "ContentProtection")) {
629 			gf_mpd_parse_descriptor(com->content_protection, child);
630 		}
631 		else if (!strcmp(child->name, "EssentialProperty")) {
632 			gf_mpd_parse_descriptor(com->essential_properties, child);
633 		}
634 		else if (!strcmp(child->name, "SupplementalProperty")) {
635 			gf_mpd_parse_descriptor(com->supplemental_properties, child);
636 		}
637 	}
638 }
639 
gf_mpd_init_common_attributes(GF_MPD_CommonAttributes * com)640 static void gf_mpd_init_common_attributes(GF_MPD_CommonAttributes *com)
641 {
642 	com->audio_channels = gf_list_new();
643 	com->content_protection = gf_list_new();
644 	com->essential_properties = gf_list_new();
645 	com->supplemental_properties = gf_list_new();
646 	com->frame_packing = gf_list_new();
647 	com->max_playout_rate = 1.0;
648 }
649 
gf_mpd_representation_new()650 GF_MPD_Representation *gf_mpd_representation_new()
651 {
652 	GF_MPD_Representation *rep;
653 	GF_SAFEALLOC(rep, GF_MPD_Representation);
654 	if (!rep) return NULL;
655 	gf_mpd_init_common_attributes((GF_MPD_CommonAttributes *)rep);
656 	rep->base_URLs = gf_list_new();
657 	rep->sub_representations = gf_list_new();
658 	rep->other_descriptors = gf_list_new();
659 	return rep;
660 }
661 
gf_mpd_parse_dasher_context(GF_MPD * mpd,GF_XMLNode * root)662 static GF_DASH_SegmenterContext *gf_mpd_parse_dasher_context(GF_MPD *mpd, GF_XMLNode *root)
663 {
664 	u32 i;
665 	GF_DASH_SegmenterContext *dasher;
666 	GF_XMLAttribute *att;
667 	GF_SAFEALLOC(dasher, GF_DASH_SegmenterContext);
668 	if (!dasher) return NULL;
669 
670 	i = 0;
671 	while ( (att = gf_list_enum(root->attributes, &i)) ) {
672 		if (!strcmp(att->name, "done")) dasher->done = gf_mpd_parse_bool(att->value);
673 		else if (!strcmp(att->name, "init")) dasher->init_seg = gf_mpd_parse_string(att->value);
674 		else if (!strcmp(att->name, "template")) dasher->template_seg = gf_mpd_parse_string(att->value);
675 		else if (!strcmp(att->name, "index")) dasher->template_idx = gf_mpd_parse_string(att->value);
676 		else if (!strcmp(att->name, "url")) dasher->src_url = gf_mpd_parse_string(att->value);
677 		else if (!strcmp(att->name, "periodID")) dasher->period_id = gf_mpd_parse_string(att->value);
678 		else if (!strcmp(att->name, "segNumber")) dasher->seg_number = gf_mpd_parse_int(att->value);
679 		else if (!strcmp(att->name, "lastPacketIdx")) dasher->last_pck_idx = gf_mpd_parse_long_int(att->value);
680 		else if (!strcmp(att->name, "pidID")) dasher->pid_id = gf_mpd_parse_int(att->value);
681 		else if (!strcmp(att->name, "depID")) dasher->dep_pid_id = gf_mpd_parse_int(att->value);
682 		else if (!strcmp(att->name, "periodStart")) dasher->period_start = gf_mpd_parse_double(att->value);
683 		else if (!strcmp(att->name, "periodDuration")) dasher->period_duration = gf_mpd_parse_double(att->value);
684 		else if (!strcmp(att->name, "ownsSet")) dasher->owns_set = gf_mpd_parse_bool(att->value);
685 		else if (!strcmp(att->name, "multiPIDInit")) dasher->multi_pids = gf_mpd_parse_bool(att->value);
686 		else if (!strcmp(att->name, "dashDuration")) dasher->dash_dur = gf_mpd_parse_double(att->value);
687 		else if (!strcmp(att->name, "nextSegmentStart")) dasher->next_seg_start = gf_mpd_parse_long_int(att->value);
688 		else if (!strcmp(att->name, "firstCTS")) dasher->first_cts = gf_mpd_parse_long_int(att->value);
689 		else if (!strcmp(att->name, "firstDTS")) dasher->first_cts = gf_mpd_parse_long_int(att->value);
690 		else if (!strcmp(att->name, "estimatedNextDTS")) dasher->est_next_dts = gf_mpd_parse_long_int(att->value);
691 		else if (!strcmp(att->name, "nbRepeat")) dasher->nb_repeat = gf_mpd_parse_int(att->value);
692 		else if (!strcmp(att->name, "tsOffset")) dasher->ts_offset = gf_mpd_parse_long_int(att->value);
693 		else if (!strcmp(att->name, "mpdTimescale")) dasher->mpd_timescale = gf_mpd_parse_int(att->value);
694 		else if (!strcmp(att->name, "sourcePID")) dasher->source_pid = gf_mpd_parse_int(att->value);
695 		else if (!strcmp(att->name, "cumulatedDur")) dasher->cumulated_dur = gf_mpd_parse_double(att->value);
696 		else if (!strcmp(att->name, "cumulatedSubdur")) dasher->cumulated_subdur = gf_mpd_parse_double(att->value);
697 		else if (!strcmp(att->name, "muxPIDs")) dasher->mux_pids = gf_mpd_parse_string(att->value);
698 		else if (!strcmp(att->name, "segsPurged")) dasher->segs_purged = gf_mpd_parse_int(att->value);
699 		else if (!strcmp(att->name, "durPurged")) dasher->dur_purged = gf_mpd_parse_double(att->value);
700 		else if (!strcmp(att->name, "moofSN")) dasher->moof_sn = gf_mpd_parse_int(att->value);
701 		else if (!strcmp(att->name, "moofInc")) dasher->moof_sn_inc = gf_mpd_parse_int(att->value);
702 		else if (!strcmp(att->name, "lastDynPeriodID")) dasher->last_dyn_period_id = gf_mpd_parse_int(att->value);
703 
704 	}
705 	return dasher;
706 }
707 
gf_mpd_parse_segments_context(GF_MPD * mpd,GF_XMLNode * root)708 static GF_List *gf_mpd_parse_segments_context(GF_MPD *mpd, GF_XMLNode *root)
709 {
710 	GF_List *res = NULL;
711 	u32 i, j;
712 	GF_XMLAttribute *att;
713 	GF_XMLNode *child;
714 	i=0;
715 	while ((child = gf_list_enum(root->content, &i))) {
716 		GF_DASH_SegmentContext *sctx;
717 		if (!gf_mpd_valid_child(mpd, child)) continue;
718 
719 		if (strcmp(child->name, "segmentInfo")) continue;
720 		if (!res) res = gf_list_new();
721 
722 		GF_SAFEALLOC(sctx, GF_DASH_SegmentContext);
723 		if (!sctx) break;
724 
725 		gf_list_add(res, sctx);
726 
727 		j = 0;
728 		while ( (att = gf_list_enum(child->attributes, &j)) ) {
729 			if (!strcmp(att->name, "file")) sctx->filename = gf_mpd_parse_string(att->value);
730 			if (!strcmp(att->name, "path")) sctx->filepath = gf_mpd_parse_string(att->value);
731 			else if (!strcmp(att->name, "time")) sctx->time = gf_mpd_parse_long_int(att->value);
732 			else if (!strcmp(att->name, "dur")) sctx->dur = gf_mpd_parse_long_int(att->value);
733 			else if (!strcmp(att->name, "size")) sctx->file_size = gf_mpd_parse_int(att->value);
734 			else if (!strcmp(att->name, "offset")) sctx->file_offset = gf_mpd_parse_long_int(att->value);
735 			else if (!strcmp(att->name, "idx_size")) sctx->index_size = gf_mpd_parse_int(att->value);
736 			else if (!strcmp(att->name, "idx_offset")) sctx->index_offset = gf_mpd_parse_long_int(att->value);
737 			else if (!strcmp(att->name, "seg_num")) sctx->seg_num = gf_mpd_parse_int(att->value);
738 
739 		}
740 	}
741 	return res;
742 }
743 
gf_mpd_parse_representation(GF_MPD * mpd,GF_List * container,GF_XMLNode * root)744 static GF_Err gf_mpd_parse_representation(GF_MPD *mpd, GF_List *container, GF_XMLNode *root)
745 {
746 	u32 i;
747 	GF_MPD_Representation *rep;
748 	GF_XMLAttribute *att;
749 	GF_XMLNode *child;
750 	GF_Err e;
751 
752 	rep = gf_mpd_representation_new();
753 	e = gf_list_add(container, rep);
754 	if (e) return e;
755 
756 	i = 0;
757 	while ( (att = gf_list_enum(root->attributes, &i)) ) {
758 		if (!strcmp(att->name, "id")) rep->id = gf_mpd_parse_string(att->value);
759 		else if (!strcmp(att->name, "bandwidth")) rep->bandwidth = gf_mpd_parse_int(att->value);
760 		else if (!strcmp(att->name, "qualityRanking")) rep->quality_ranking = gf_mpd_parse_int(att->value);
761 		else if (!strcmp(att->name, "dependencyId")) rep->dependency_id = gf_mpd_parse_string(att->value);
762 		else if (!strcmp(att->name, "mediaStreamStructureId")) rep->media_stream_structure_id = gf_mpd_parse_string(att->value);
763 	}
764 	gf_mpd_parse_common_representation(mpd, (GF_MPD_CommonAttributes*)rep, root);
765 
766 	i = 0;
767 	while ( (child = gf_list_enum(root->content, &i))) {
768 		if (!gf_mpd_valid_child(mpd, child)) continue;
769 		if (!strcmp(child->name, "BaseURL")) {
770 			e = gf_mpd_parse_base_url(rep->base_URLs, child);
771 			if (e) return e;
772 		}
773 		else if (!strcmp(child->name, "SegmentBase")) {
774 			rep->segment_base = gf_mpd_parse_segment_base(mpd, child);
775 		}
776 		else if (!strcmp(child->name, "SegmentList")) {
777 			rep->segment_list = gf_mpd_parse_segment_list(mpd, child);
778 		}
779 		else if (!strcmp(child->name, "SegmentTemplate")) {
780 			rep->segment_template = gf_mpd_parse_segment_template(mpd, child);
781 		}
782 		else if (!strcmp(child->name, "SubRepresentation")) {
783 			/*TODO
784 						e = gf_mpd_parse_subrepresentation(rep->sub_representations, child);
785 						if (e) return e;
786 			*/
787 		}
788 		else if (!strcmp(child->name, "dasher")) {
789 			assert(!rep->dasher_ctx);
790 			rep->dasher_ctx = gf_mpd_parse_dasher_context(mpd, child);
791 		}
792 		else if (!strcmp(child->name, "segments")) {
793 			assert(!rep->state_seg_list);
794 			rep->state_seg_list = gf_mpd_parse_segments_context(mpd, child);
795 		}
796 		else{
797 			/*We'll be assuming here that any unrecognized element is a representation level
798 			  *descriptor*/
799 			gf_mpd_parse_other_descriptors(child,rep->other_descriptors);
800 
801 		}
802 	}
803 	return GF_OK;
804 }
805 
gf_mpd_adaptation_set_new()806 GF_MPD_AdaptationSet *gf_mpd_adaptation_set_new() {
807 	GF_MPD_AdaptationSet *set;
808 	GF_SAFEALLOC(set, GF_MPD_AdaptationSet);
809 	if (!set) return NULL;
810 	gf_mpd_init_common_attributes((GF_MPD_CommonAttributes *)set);
811 	set->accessibility = gf_list_new();
812 	set->role = gf_list_new();
813 	set->rating = gf_list_new();
814 	set->viewpoint = gf_list_new();
815 	set->content_component = gf_list_new();
816 	set->base_URLs = gf_list_new();
817 	set->representations = gf_list_new();
818 	set->other_descriptors = gf_list_new();
819 	GF_SAFEALLOC(set->par, GF_MPD_Fractional);
820 	/*assign default ID and group*/
821 	set->group = -1;
822 	return set;
823 }
824 
gf_mpd_parse_adaptation_set(GF_MPD * mpd,GF_List * container,GF_XMLNode * root)825 static GF_Err gf_mpd_parse_adaptation_set(GF_MPD *mpd, GF_List *container, GF_XMLNode *root)
826 {
827 	u32 i;
828 	GF_MPD_AdaptationSet *set;
829 	GF_XMLAttribute *att;
830 	GF_XMLNode *child;
831 	GF_Err e;
832 
833 	set = gf_mpd_adaptation_set_new();
834 	if (!set) return GF_OUT_OF_MEM;
835 
836 	e = gf_list_add(container, set);
837 	if (e) return e;
838 
839 	i = 0;
840 	while ( (att = gf_list_enum(root->attributes, &i)) ) {
841 		if (strstr(att->name, "href")) set->xlink_href = gf_mpd_parse_string(att->value);
842 		else if (strstr(att->name, "actuate")) set->xlink_actuate_on_load = !strcmp(att->value, "onLoad") ? GF_TRUE : GF_FALSE;
843 		else if (!strcmp(att->name, "id")) set->id = gf_mpd_parse_int(att->value);
844 		else if (!strcmp(att->name, "group")) set->group = gf_mpd_parse_int(att->value);
845 		else if (!strcmp(att->name, "lang")) set->lang = gf_mpd_parse_string(att->value);
846 		else if (!strcmp(att->name, "contentType")) set->content_type = gf_mpd_parse_string(att->value);
847 		else if (!strcmp(att->name, "par")) {
848 			if (set->par) gf_free(set->par);
849 			set->par = gf_mpd_parse_frac(att->value, ':', NULL);
850 		}
851 		else if (!strcmp(att->name, "minBandwidth")) set->min_bandwidth = gf_mpd_parse_int(att->value);
852 		else if (!strcmp(att->name, "maxBandwidth")) set->max_bandwidth = gf_mpd_parse_int(att->value);
853 		else if (!strcmp(att->name, "minWidth")) set->min_width = gf_mpd_parse_int(att->value);
854 		else if (!strcmp(att->name, "maxWidth")) set->max_width = gf_mpd_parse_int(att->value);
855 		else if (!strcmp(att->name, "minHeight")) set->min_height = gf_mpd_parse_int(att->value);
856 		else if (!strcmp(att->name, "maxHeight")) set->max_height = gf_mpd_parse_int(att->value);
857 		else if (!strcmp(att->name, "minFrameRate")) gf_mpd_parse_frac(att->value, '/', &set->min_framerate);
858 		else if (!strcmp(att->name, "maxFrameRate")) gf_mpd_parse_frac(att->value, '/', &set->max_framerate);
859 		else if (!strcmp(att->name, "segmentAlignment")) set->segment_alignment = gf_mpd_parse_bool(att->value);
860 		else if (!strcmp(att->name, "bitstreamSwitching")) set->bitstream_switching = gf_mpd_parse_bool(att->value);
861 		else if (!strcmp(att->name, "subsegmentAlignment")) set->subsegment_alignment = gf_mpd_parse_bool(att->value);
862 		else if (!strcmp(att->name, "subsegmentStartsWithSAP")) {
863 			if (!strcmp(att->value, "false")) set->subsegment_starts_with_sap  = 0;
864 			else set->subsegment_starts_with_sap = gf_mpd_parse_int(att->value);
865 		}
866 	}
867 	gf_mpd_parse_common_representation(mpd, (GF_MPD_CommonAttributes*)set, root);
868 
869 	i = 0;
870 	while ( (child = gf_list_enum(root->content, &i))) {
871 		if (!gf_mpd_valid_child(mpd, child)) continue;
872 		if (!strcmp(child->name, "Accessibility")) {
873 			e = gf_mpd_parse_descriptor(set->accessibility, child);
874 			if (e) return e;
875 		}
876 		else if (!strcmp(child->name, "Role")) {
877 			e = gf_mpd_parse_descriptor(set->role, child);
878 			if (e) return e;
879 		}
880 		else if (!strcmp(child->name, "Rating")) {
881 			e = gf_mpd_parse_descriptor(set->rating, child);
882 			if (e) return e;
883 		}
884 		else if (!strcmp(child->name, "Viewpoint")) {
885 			e = gf_mpd_parse_descriptor(set->viewpoint, child);
886 			if (e) return e;
887 		}
888 		else if (!strcmp(child->name, "BaseURL")) {
889 			e = gf_mpd_parse_base_url(set->base_URLs, child);
890 			if (e) return e;
891 		}
892 		else if (!strcmp(child->name, "ContentComponent")) {
893 			e = gf_mpd_parse_content_component(set->content_component, child);
894 			if (e) return e;
895 		}
896 		else if (!strcmp(child->name, "SegmentBase")) {
897 			set->segment_base = gf_mpd_parse_segment_base(mpd, child);
898 		}
899 		else if (!strcmp(child->name, "SegmentList")) {
900 			set->segment_list = gf_mpd_parse_segment_list(mpd, child);
901 		}
902 		else if (!strcmp(child->name, "SegmentTemplate")) {
903 			set->segment_template = gf_mpd_parse_segment_template(mpd, child);
904 		}
905 		else if (!strcmp(child->name, "Representation")) {
906 			e = gf_mpd_parse_representation(mpd, set->representations, child);
907 			if (e) return e;
908 		}
909 		else{
910 			/*We'll be assuming here that any unrecognized element is a adaptation level
911 			  *descriptor*/
912 			gf_mpd_parse_other_descriptors(child,set->other_descriptors);
913 		}
914 	}
915 	return GF_OK;
916 }
917 
gf_mpd_period_new()918 GF_MPD_Period *gf_mpd_period_new() {
919 	GF_MPD_Period *period;
920 	GF_SAFEALLOC(period, GF_MPD_Period);
921 	if (!period) return NULL;
922 	period->adaptation_sets = gf_list_new();
923 	period->base_URLs = gf_list_new();
924 	period->subsets = gf_list_new();
925 	period->other_descriptors = gf_list_new();
926 	return period;
927 }
928 
gf_mpd_parse_period(GF_MPD * mpd,GF_XMLNode * root)929 GF_Err gf_mpd_parse_period(GF_MPD *mpd, GF_XMLNode *root)
930 {
931 	u32 i;
932 	GF_MPD_Period *period;
933 	GF_XMLAttribute *att;
934 	GF_XMLNode *child;
935 	GF_Err e;
936 
937 	period = gf_mpd_period_new();
938 	if (!period) return GF_OUT_OF_MEM;
939 	e = gf_list_add(mpd->periods, period);
940 	if (e) return e;
941 
942 	i = 0;
943 	while ( (att = gf_list_enum(root->attributes, &i)) ) {
944 		if (strstr(att->name, "href")) period->xlink_href = gf_mpd_parse_string(att->value);
945 		else if (strstr(att->name, "actuate")) period->xlink_actuate_on_load = !strcmp(att->value, "onLoad") ? 1 : 0;
946 		else if (!strcmp(att->name, "id")) period->ID = gf_mpd_parse_string(att->value);
947 		else if (!strcmp(att->name, "start")) period->start = gf_mpd_parse_duration(att->value);
948 		else if (!strcmp(att->name, "duration")) period->duration = gf_mpd_parse_duration(att->value);
949 		else if (!strcmp(att->name, "bitstreamSwitching")) period->bitstream_switching = gf_mpd_parse_bool(att->value);
950 	}
951 
952 	i = 0;
953 	while ( (child = gf_list_enum(root->content, &i))) {
954 		if (!gf_mpd_valid_child(mpd, child)) continue;
955 		if (!strcmp(child->name, "BaseURL")) {
956 			e = gf_mpd_parse_base_url(period->base_URLs, child);
957 			if (e) return e;
958 		}
959 		else if (!strcmp(child->name, "SegmentBase")) {
960 			period->segment_base = gf_mpd_parse_segment_base(mpd, child);
961 		}
962 		else if (!strcmp(child->name, "SegmentList")) {
963 			period->segment_list = gf_mpd_parse_segment_list(mpd, child);
964 		}
965 		else if (!strcmp(child->name, "SegmentTemplate")) {
966 			period->segment_template = gf_mpd_parse_segment_template(mpd, child);
967 		}
968 		else if (!strcmp(child->name, "AdaptationSet")) {
969 			e = gf_mpd_parse_adaptation_set(mpd, period->adaptation_sets, child);
970 			if (e) return e;
971 		}
972 		else if (!strcmp(child->name, "SubSet")) {
973 		}
974 		else{
975 			/*We'll be assuming here that any unrecognized element is a period level
976 			  *descriptor*/
977 			gf_mpd_parse_other_descriptors(child, period->other_descriptors);
978 		}
979 
980 	}
981 	return GF_OK;
982 }
983 
984 GF_EXPORT
gf_mpd_new()985 GF_MPD *gf_mpd_new()
986 {
987 	GF_MPD *mpd;
988 	GF_SAFEALLOC(mpd, GF_MPD);
989 	return mpd;
990 }
991 
gf_mpd_del_list(GF_List * list,void (* __destructor)(void *),Bool reset_only)992 void gf_mpd_del_list(GF_List *list, void (*__destructor)(void *), Bool reset_only)
993 {
994 	if (!list) return;
995 	while (gf_list_count(list)) {
996 		void *item = gf_list_last(list);
997 		gf_list_rem_last(list);
998 		if (item && __destructor) __destructor(item);
999 	}
1000 	if (!reset_only) gf_list_del(list);
1001 }
1002 
gf_mpd_base_url_free(void * _item)1003 void gf_mpd_base_url_free(void *_item)
1004 {
1005 	GF_MPD_BaseURL *base_url = (GF_MPD_BaseURL *)_item;
1006 	if (base_url->service_location) gf_free(base_url->service_location);
1007 	if (base_url->URL) gf_free(base_url->URL);
1008 	if (base_url->redirection) gf_free(base_url->redirection);
1009 	gf_free(base_url);
1010 }
1011 
gf_mpd_url_free(void * _item)1012 void gf_mpd_url_free(void *_item)
1013 {
1014 	GF_MPD_URL *ptr = (GF_MPD_URL*)_item;
1015 	if (ptr->sourceURL) gf_free(ptr->sourceURL);
1016 	if (ptr->byte_range) gf_free(ptr->byte_range);
1017 	gf_free(ptr);
1018 }
gf_mpd_string_free(void * _item)1019 void gf_mpd_string_free(void *_item)
1020 {
1021 	if (_item) gf_free(_item);
1022 }
1023 
gf_mpd_prog_info_free(void * _item)1024 void gf_mpd_prog_info_free(void *_item)
1025 {
1026 	GF_MPD_ProgramInfo *ptr = (GF_MPD_ProgramInfo *)_item;
1027 	if (ptr->lang) gf_free(ptr->lang);
1028 	if (ptr->title) gf_free(ptr->title);
1029 	if (ptr->source) gf_free(ptr->source);
1030 	if (ptr->copyright) gf_free(ptr->copyright);
1031 	if (ptr->more_info_url) gf_free(ptr->more_info_url);
1032 	gf_free(ptr);
1033 }
gf_mpd_segment_url_free(void * _ptr)1034 void gf_mpd_segment_url_free(void *_ptr)
1035 {
1036 	GF_MPD_SegmentURL *ptr = (GF_MPD_SegmentURL *)_ptr;
1037 	if (ptr->index) gf_free(ptr->index);
1038 	if (ptr->index_range) gf_free(ptr->index_range);
1039 	if (ptr->media) gf_free(ptr->media);
1040 	if (ptr->media_range) gf_free(ptr->media_range);
1041 	if (ptr->key_url) gf_free(ptr->key_url);
1042 	gf_free(ptr);
1043 }
gf_mpd_segment_base_free(void * _item)1044 void gf_mpd_segment_base_free(void *_item)
1045 {
1046 	GF_MPD_SegmentBase *ptr = (GF_MPD_SegmentBase *)_item;
1047 	if (ptr->initialization_segment) gf_mpd_url_free(ptr->initialization_segment);
1048 	if (ptr->representation_index) gf_mpd_url_free(ptr->representation_index);
1049 	if (ptr->index_range) gf_free(ptr->index_range);
1050 	gf_free(ptr);
1051 }
1052 
gf_mpd_segment_entry_free(void * _item)1053 void gf_mpd_segment_entry_free(void *_item)
1054 {
1055 	gf_free(_item);
1056 }
gf_mpd_segment_timeline_free(void * _item)1057 void gf_mpd_segment_timeline_free(void *_item)
1058 {
1059 	GF_MPD_SegmentTimeline *ptr = (GF_MPD_SegmentTimeline *)_item;
1060 	gf_mpd_del_list(ptr->entries, gf_mpd_segment_entry_free, 0);
1061 	gf_free(ptr);
1062 }
1063 
1064 #if 0 //unused
1065 void gf_mpd_segment_url_list_free(GF_List *list)
1066 {
1067 	gf_mpd_del_list(list, gf_mpd_segment_url_free, 0);
1068 }
1069 #endif
1070 
gf_mpd_segment_list_free(void * _item)1071 void gf_mpd_segment_list_free(void *_item)
1072 {
1073 	GF_MPD_SegmentList *ptr = (GF_MPD_SegmentList *)_item;
1074 	if (ptr->xlink_href) gf_free(ptr->xlink_href);
1075 	if (ptr->initialization_segment) gf_mpd_url_free(ptr->initialization_segment);
1076 	if (ptr->bitstream_switching_url) gf_mpd_url_free(ptr->bitstream_switching_url);
1077 	if (ptr->representation_index) gf_mpd_url_free(ptr->representation_index);
1078 	if (ptr->segment_timeline) gf_mpd_segment_timeline_free(ptr->segment_timeline);
1079 	gf_mpd_del_list(ptr->segment_URLs, gf_mpd_segment_url_free, 0);
1080 	if (ptr->dasher_segment_name) gf_free(ptr->dasher_segment_name);
1081 	gf_free(ptr);
1082 }
1083 
gf_mpd_segment_template_free(void * _item)1084 void gf_mpd_segment_template_free(void *_item)
1085 {
1086 	GF_MPD_SegmentTemplate *ptr = (GF_MPD_SegmentTemplate *)_item;
1087 	if (ptr->initialization_segment) gf_mpd_url_free(ptr->initialization_segment);
1088 	if (ptr->bitstream_switching_url) gf_mpd_url_free(ptr->bitstream_switching_url);
1089 	if (ptr->representation_index) gf_mpd_url_free(ptr->representation_index);
1090 	if (ptr->segment_timeline) gf_mpd_segment_timeline_free(ptr->segment_timeline);
1091 	if (ptr->index) gf_free(ptr->index);
1092 	if (ptr->media) gf_free(ptr->media);
1093 	if (ptr->initialization) gf_free(ptr->initialization);
1094 	if (ptr->bitstream_switching) gf_free(ptr->bitstream_switching);
1095 	gf_free(ptr);
1096 }
1097 
gf_mpd_extensible_free(GF_MPD_ExtensibleVirtual * item)1098 void gf_mpd_extensible_free(GF_MPD_ExtensibleVirtual *item)
1099 {
1100 	if (item->attributes) {
1101 		while (gf_list_count(item->attributes)) {
1102 			GF_XMLAttribute *att = gf_list_last(item->attributes);
1103 			gf_list_rem_last(item->attributes);
1104 			if (att->name) gf_free(att->name);
1105 			if (att->value) gf_free(att->value);
1106 			gf_free(att);
1107 		}
1108 		gf_list_del(item->attributes);
1109 	}
1110 	if (item->children) {
1111 		while (gf_list_count(item->children)) {
1112 			GF_XMLNode *child = gf_list_last(item->children);
1113 			gf_list_rem_last(item->children);
1114 			gf_xml_dom_node_del(child);
1115 		}
1116 		gf_list_del(item->children);
1117 	}
1118 }
1119 
gf_mpd_descriptor_new(const char * id,const char * schemeIdUri,const char * value)1120 GF_MPD_Descriptor *gf_mpd_descriptor_new(const char *id, const char *schemeIdUri, const char *value) {
1121 	GF_MPD_Descriptor *mpd_desc;
1122 	GF_SAFEALLOC(mpd_desc, GF_MPD_Descriptor);
1123 	if (!mpd_desc) return NULL;
1124 	if (id) mpd_desc->id = gf_strdup(id);
1125 	if (schemeIdUri) mpd_desc->scheme_id_uri = gf_strdup(schemeIdUri);
1126 	if (value) mpd_desc->value = gf_strdup(value);
1127 	return mpd_desc;
1128 }
1129 
gf_mpd_descriptor_free(void * item)1130 void gf_mpd_descriptor_free(void *item)
1131 {
1132 	GF_MPD_Descriptor *mpd_desc = (GF_MPD_Descriptor*) item;
1133 	if (mpd_desc->id) gf_free(mpd_desc->id);
1134 	if (mpd_desc->scheme_id_uri) gf_free(mpd_desc->scheme_id_uri);
1135 	if (mpd_desc->value) gf_free(mpd_desc->value);
1136 	gf_mpd_extensible_free((GF_MPD_ExtensibleVirtual *)mpd_desc);
1137 
1138 	gf_free(mpd_desc);
1139 }
1140 
gf_mpd_other_descriptor_free(void * _item)1141 void gf_mpd_other_descriptor_free(void *_item)
1142 {
1143 	GF_MPD_other_descriptors *ptr = (GF_MPD_other_descriptors *)_item;
1144 	if(ptr->xml_desc)gf_free(ptr->xml_desc);
1145 	gf_free(ptr);
1146 }
1147 
gf_mpd_content_component_free(void * item)1148 void gf_mpd_content_component_free(void *item)
1149 {
1150 	GF_MPD_ContentComponent *component_descriptor=(GF_MPD_ContentComponent*) item;
1151 	if (component_descriptor->type) gf_free(component_descriptor->type);
1152 	if (component_descriptor->lang) gf_free(component_descriptor->lang);
1153 	gf_free(item);
1154 }
1155 
gf_mpd_common_attributes_free(GF_MPD_CommonAttributes * ptr)1156 void gf_mpd_common_attributes_free(GF_MPD_CommonAttributes *ptr)
1157 {
1158 	if (ptr->profiles) gf_free(ptr->profiles);
1159 	if (ptr->sar) gf_free(ptr->sar);
1160 	if (ptr->framerate) gf_free(ptr->framerate);
1161 	if (ptr->mime_type) gf_free(ptr->mime_type);
1162 	if (ptr->segmentProfiles) gf_free(ptr->segmentProfiles);
1163 	if (ptr->codecs) gf_free(ptr->codecs);
1164 	gf_mpd_del_list(ptr->frame_packing, gf_mpd_descriptor_free, 0);
1165 	gf_mpd_del_list(ptr->audio_channels, gf_mpd_descriptor_free, 0);
1166 	gf_mpd_del_list(ptr->content_protection, gf_mpd_descriptor_free, 0);
1167 	gf_mpd_del_list(ptr->essential_properties, gf_mpd_descriptor_free, 0);
1168 	gf_mpd_del_list(ptr->supplemental_properties, gf_mpd_descriptor_free, 0);
1169 }
1170 
gf_mpd_representation_free(void * _item)1171 void gf_mpd_representation_free(void *_item)
1172 {
1173 	GF_MPD_Representation *ptr = (GF_MPD_Representation *)_item;
1174 	gf_mpd_common_attributes_free((GF_MPD_CommonAttributes *)ptr);
1175 	if (ptr->id) gf_free(ptr->id);
1176 	if (ptr->dependency_id) gf_free(ptr->dependency_id);
1177 	if (ptr->media_stream_structure_id) gf_free(ptr->media_stream_structure_id);
1178 
1179 	if (ptr->playback.cached_init_segment_url) {
1180 		if (ptr->playback.owned_gmem && !strnicmp(ptr->playback.cached_init_segment_url, "gmem://", 7)) {
1181 			u32 size;
1182 			char *mem_address;
1183 			if (sscanf(ptr->playback.cached_init_segment_url, "gmem://%d@%p", &size, &mem_address) != 2) {
1184 				assert(0);
1185 			}
1186 			gf_free(mem_address);
1187 		}
1188 		gf_free(ptr->playback.cached_init_segment_url);
1189 	}
1190 	if (ptr->playback.init_segment.data) gf_free(ptr->playback.init_segment.data);
1191 	if (ptr->playback.key_url) gf_free(ptr->playback.key_url);
1192 
1193 	gf_mpd_del_list(ptr->base_URLs, gf_mpd_base_url_free, 0);
1194 	gf_mpd_del_list(ptr->sub_representations, NULL/*TODO*/, 0);
1195 	if (ptr->segment_base) gf_mpd_segment_base_free(ptr->segment_base);
1196 	if (ptr->segment_list) gf_mpd_segment_list_free(ptr->segment_list);
1197 	if (ptr->segment_template) gf_mpd_segment_template_free(ptr->segment_template);
1198 	if (ptr->other_descriptors)gf_mpd_del_list(ptr->other_descriptors, gf_mpd_other_descriptor_free, 0);
1199 
1200 	if (ptr->dasher_ctx) {
1201 		gf_free(ptr->dasher_ctx->init_seg);
1202 		if (ptr->dasher_ctx->period_id)
1203 			gf_free(ptr->dasher_ctx->period_id);
1204 		gf_free(ptr->dasher_ctx->src_url);
1205 		gf_free(ptr->dasher_ctx->template_seg);
1206 		if (ptr->dasher_ctx->template_idx) gf_free(ptr->dasher_ctx->template_idx);
1207 		if (ptr->dasher_ctx->mux_pids) gf_free(ptr->dasher_ctx->mux_pids);
1208 		gf_free(ptr->dasher_ctx);
1209 	}
1210 	if (ptr->state_seg_list) {
1211 		while (gf_list_count(ptr->state_seg_list)) {
1212 			GF_DASH_SegmentContext *s = gf_list_pop_back(ptr->state_seg_list);
1213 			if (s->filename) gf_free(s->filename);
1214 			if (s->filepath) gf_free(s->filepath);
1215 			gf_free(s);
1216 		}
1217 		gf_list_del(ptr->state_seg_list);
1218 	}
1219 	if (ptr->m3u8_var_name) gf_free(ptr->m3u8_var_name);
1220 	if (ptr->m3u8_var_file) gf_fclose(ptr->m3u8_var_file);
1221 
1222 	gf_free(ptr);
1223 }
1224 
gf_mpd_adaptation_set_free(void * _item)1225 void gf_mpd_adaptation_set_free(void *_item)
1226 {
1227 	GF_MPD_AdaptationSet *ptr = (GF_MPD_AdaptationSet *)_item;
1228 	gf_mpd_common_attributes_free((GF_MPD_CommonAttributes *)ptr);
1229 	if (ptr->lang) gf_free(ptr->lang);
1230 	if (ptr->content_type) gf_free(ptr->content_type);
1231 	if (ptr->par) gf_free(ptr->par);
1232 	if (ptr->xlink_href) gf_free(ptr->xlink_href);
1233 	gf_mpd_del_list(ptr->accessibility, gf_mpd_descriptor_free, 0);
1234 	gf_mpd_del_list(ptr->role, gf_mpd_descriptor_free, 0);
1235 	gf_mpd_del_list(ptr->rating, gf_mpd_descriptor_free, 0);
1236 	gf_mpd_del_list(ptr->viewpoint, gf_mpd_descriptor_free, 0);
1237 	gf_mpd_del_list(ptr->content_component, gf_mpd_content_component_free, 0);
1238 	if (ptr->segment_base) gf_mpd_segment_base_free(ptr->segment_base);
1239 	if (ptr->segment_list) gf_mpd_segment_list_free(ptr->segment_list);
1240 	if (ptr->segment_template) gf_mpd_segment_template_free(ptr->segment_template);
1241 	gf_mpd_del_list(ptr->base_URLs, gf_mpd_base_url_free, 0);
1242 	gf_mpd_del_list(ptr->representations, gf_mpd_representation_free, 0);
1243 	gf_mpd_del_list(ptr->other_descriptors, gf_mpd_other_descriptor_free, 0);
1244 	gf_free(ptr);
1245 }
1246 
gf_mpd_period_free(void * _item)1247 void gf_mpd_period_free(void *_item)
1248 {
1249 	GF_MPD_Period *ptr = (GF_MPD_Period *)_item;
1250 	if (ptr->ID) gf_free(ptr->ID);
1251 	if (ptr->origin_base_url) gf_free(ptr->origin_base_url);
1252 	if (ptr->xlink_href) gf_free(ptr->xlink_href);
1253 	if (ptr->segment_base) gf_mpd_segment_base_free(ptr->segment_base);
1254 	if (ptr->segment_list) gf_mpd_segment_list_free(ptr->segment_list);
1255 	if (ptr->segment_template) gf_mpd_segment_template_free(ptr->segment_template);
1256 
1257 	gf_mpd_del_list(ptr->base_URLs, gf_mpd_base_url_free, 0);
1258 	gf_mpd_del_list(ptr->adaptation_sets, gf_mpd_adaptation_set_free, 0);
1259 	gf_mpd_del_list(ptr->other_descriptors,gf_mpd_other_descriptor_free,0);
1260 	gf_mpd_del_list(ptr->subsets, NULL/*TODO*/, 0);
1261 	gf_free(ptr);
1262 }
1263 
1264 GF_EXPORT
gf_mpd_del(GF_MPD * mpd)1265 void gf_mpd_del(GF_MPD *mpd)
1266 {
1267 	if (!mpd) return;
1268 #ifdef GPAC_ENABLE_COVERAGE
1269 	if (gf_sys_is_cov_mode()) {
1270 		gf_mpd_string_free(NULL);
1271 	}
1272 #endif
1273 
1274 	gf_mpd_del_list(mpd->program_infos, gf_mpd_prog_info_free, 0);
1275 	gf_mpd_del_list(mpd->base_URLs, gf_mpd_base_url_free, 0);
1276 	gf_mpd_del_list(mpd->locations, gf_mpd_string_free, 0);
1277 	gf_mpd_del_list(mpd->metrics, NULL/*TODO*/, 0);
1278 	gf_mpd_del_list(mpd->periods, gf_mpd_period_free, 0);
1279 	if (mpd->profiles) gf_free(mpd->profiles);
1280 	if (mpd->ID) gf_free(mpd->ID);
1281 	gf_mpd_extensible_free((GF_MPD_ExtensibleVirtual*) mpd);
1282 	gf_free(mpd);
1283 }
1284 
1285 
1286 GF_EXPORT
gf_mpd_complete_from_dom(GF_XMLNode * root,GF_MPD * mpd,const char * default_base_url)1287 GF_Err gf_mpd_complete_from_dom(GF_XMLNode *root, GF_MPD *mpd, const char *default_base_url)
1288 {
1289 	GF_Err e;
1290 	u32 i;
1291 	Bool ns_ok = GF_FALSE;
1292 	GF_XMLAttribute *att;
1293 	GF_XMLNode *child;
1294 
1295 	if (!root || !mpd) return GF_BAD_PARAM;
1296 	i=0;
1297 	while ((att = gf_list_enum(root->attributes, &i))) {
1298 		if (!strcmp(att->name, "xmlns")) {
1299 			if (!root->ns && (!strcmp(att->value, "urn:mpeg:dash:schema:mpd:2011") || !strcmp(att->value, "urn:mpeg:DASH:schema:MPD:2011")) ) {
1300 				ns_ok = 1;
1301 				break;
1302 			}
1303 		}
1304 		else if (!strncmp(att->name, "xmlns:", 6)) {
1305 			if (root->ns && !strcmp(att->name+6, root->ns) && (!strcmp(att->value, "urn:mpeg:dash:schema:mpd:2011") || !strcmp(att->value, "urn:mpeg:DASH:schema:MPD:2011")) ) {
1306 				ns_ok = 1;
1307 				if (!mpd->xml_namespace) mpd->xml_namespace = root->ns;
1308 				break;
1309 			}
1310 		}
1311 	}
1312 
1313 	if (!ns_ok) {
1314 		GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[MPD] Wrong namespace found for DASH MPD - cannot parse\n"));
1315 	}
1316 
1317 	if (!strcmp(root->name, "Period")) {
1318 		return gf_mpd_parse_period(mpd, root);
1319 	}
1320 
1321 	i = 0;
1322 	while ((att = gf_list_enum(root->attributes, &i))) {
1323 		if (!strcmp(att->name, "id")) {
1324 			if (mpd->ID) gf_free(mpd->ID);
1325 			mpd->ID = gf_mpd_parse_string(att->value);
1326 		} else if (!strcmp(att->name, "profiles")) {
1327 			if (mpd->profiles) gf_free(mpd->profiles);
1328 			mpd->profiles = gf_mpd_parse_string(att->value);
1329 		} else if (!strcmp(att->name, "type")) {
1330 			if (!strcmp(att->value, "static")) mpd->type = GF_MPD_TYPE_STATIC;
1331 			else if (!strcmp(att->value, "dynamic")) mpd->type = GF_MPD_TYPE_DYNAMIC;
1332 		} else if (!strcmp(att->name, "availabilityStartTime")) {
1333 			mpd->availabilityStartTime = gf_mpd_parse_date(att->value);
1334 		} else if (!strcmp(att->name, "availabilityEndTime")) {
1335 			mpd->availabilityEndTime = gf_mpd_parse_date(att->value);
1336 		} else if (!strcmp(att->name, "publishTime")) {
1337 			mpd->publishTime = gf_mpd_parse_date(att->value);
1338 		} else if (!strcmp(att->name, "mediaPresentationDuration")) {
1339 			mpd->media_presentation_duration = gf_mpd_parse_duration(att->value);
1340 		} else if (!strcmp(att->name, "minimumUpdatePeriod")) {
1341 			mpd->minimum_update_period = gf_mpd_parse_duration_u32(att->value);
1342 		} else if (!strcmp(att->name, "minBufferTime")) {
1343 			mpd->min_buffer_time = gf_mpd_parse_duration_u32(att->value);
1344 		} else if (!strcmp(att->name, "timeShiftBufferDepth")) {
1345 			mpd->time_shift_buffer_depth = gf_mpd_parse_duration_u32(att->value);
1346 		} else if (!strcmp(att->name, "suggestedPresentationDelay")) {
1347 			mpd->suggested_presentation_delay = gf_mpd_parse_duration_u32(att->value);
1348 		} else if (!strcmp(att->name, "maxSegmentDuration")) {
1349 			mpd->max_segment_duration = gf_mpd_parse_duration_u32(att->value);
1350 		} else if (!strcmp(att->name, "maxSubsegmentDuration")) {
1351 			mpd->max_subsegment_duration = gf_mpd_parse_duration_u32(att->value);
1352 		} else if (!strcmp(att->name, "gpac:init_gen_time")) {
1353 			mpd->gpac_init_ntp_ms = gf_mpd_parse_long_int(att->value);
1354 		} else if (!strcmp(att->name, "gpac:next_gen_time")) {
1355 			mpd->gpac_next_ntp_ms = gf_mpd_parse_long_int(att->value);
1356 		} else if (!strcmp(att->name, "gpac:mpd_time")) {
1357 			mpd->gpac_mpd_time = gf_mpd_parse_long_int(att->value);
1358 		} else {
1359 			MPD_STORE_EXTENSION_ATTR(mpd)
1360 		}
1361 	}
1362 	if (mpd->type == GF_MPD_TYPE_STATIC)
1363 		mpd->minimum_update_period = mpd->time_shift_buffer_depth = 0;
1364 
1365 	i = 0;
1366 	while ( ( child = gf_list_enum(root->content, &i )) ) {
1367 		if (! gf_mpd_valid_child(mpd, child))
1368 			continue;
1369 
1370 		if (!strcmp(child->name, "ProgramInformation")) {
1371 			e = gf_mpd_parse_program_info(mpd, child);
1372 			if (e) return e;
1373 		} else if (!strcmp(child->name, "Location")) {
1374 			char *str = gf_mpd_parse_text_content(child);
1375 			if (str) gf_list_add(mpd->locations, str);
1376 		} else if (!strcmp(child->name, "Period")) {
1377 			e = gf_mpd_parse_period(mpd, child);
1378 			if (e) return e;
1379 		} else if (!strcmp(child->name, "Metrics")) {
1380 			GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[MPD] Metrics not implemented yet\n"));
1381 		} else if (!strcmp(child->name, "BaseURL")) {
1382 			e = gf_mpd_parse_base_url(mpd->base_URLs, child);
1383 			if (e) return e;
1384 		} else {
1385 			MPD_STORE_EXTENSION_NODE(mpd)
1386 		}
1387 	}
1388 
1389 	return GF_OK;
1390 }
1391 
1392 
1393 GF_EXPORT
gf_mpd_init_from_dom(GF_XMLNode * root,GF_MPD * mpd,const char * default_base_url)1394 GF_Err gf_mpd_init_from_dom(GF_XMLNode *root, GF_MPD *mpd, const char *default_base_url)
1395 {
1396 	if (!root || !mpd) return GF_BAD_PARAM;
1397 
1398 	assert(!mpd->periods);
1399 	mpd->periods = gf_list_new();
1400 	mpd->program_infos = gf_list_new();
1401 	mpd->base_URLs = gf_list_new();
1402 	mpd->locations = gf_list_new();
1403 	mpd->metrics = gf_list_new();
1404 
1405 	/*setup some defaults*/
1406 	mpd->type = GF_MPD_TYPE_STATIC;
1407 	mpd->time_shift_buffer_depth = (u32) -1; /*infinite by default*/
1408 	mpd->xml_namespace = NULL;
1409 
1410 	return gf_mpd_complete_from_dom(root, mpd, default_base_url);
1411 }
1412 
gf_m3u8_fill_mpd_struct(MasterPlaylist * pl,const char * m3u8_file,const char * src_base_url,const char * mpd_file,char * title,Double update_interval,char * mimeTypeForM3U8Segments,Bool do_import,Bool use_mpd_templates,Bool use_segment_timeline,Bool is_end,u32 max_dur,GF_MPD * mpd,Bool parse_sub_playlist)1413 static GF_Err gf_m3u8_fill_mpd_struct(MasterPlaylist *pl, const char *m3u8_file, const char *src_base_url, const char *mpd_file, char *title, Double update_interval,
1414                                       char *mimeTypeForM3U8Segments, Bool do_import, Bool use_mpd_templates, Bool use_segment_timeline, Bool is_end, u32 max_dur, GF_MPD *mpd, Bool parse_sub_playlist)
1415 {
1416 	char *sep, *template_base=NULL, *template_ext;
1417 	u32 nb_streams, i, j, k, template_width, template_idx_start;
1418 	Stream *stream;
1419 	PlaylistElement *pe, *elt;
1420 	GF_MPD_ProgramInfo *info;
1421 	GF_MPD_Period *period;
1422 	GF_Err e;
1423 	Bool all_template_used = use_mpd_templates;
1424 	char str[1024];
1425 
1426 	if (!mpd) return GF_BAD_PARAM;
1427 	assert(!mpd->periods);
1428 	mpd->periods = gf_list_new();
1429 	mpd->program_infos = gf_list_new();
1430 	mpd->base_URLs = gf_list_new();
1431 	mpd->locations = gf_list_new();
1432 	mpd->metrics = gf_list_new();
1433 	mpd->time_shift_buffer_depth = (u32) -1; /*infinite by default*/
1434 	mpd->xml_namespace = "urn:mpeg:dash:schema:mpd:2011";
1435 	mpd->type = is_end ? GF_MPD_TYPE_STATIC : GF_MPD_TYPE_DYNAMIC;
1436 
1437 
1438 	sep = strrchr(m3u8_file, '/');
1439 	if (!sep)
1440 		sep = strrchr(m3u8_file, '\\');
1441 	if (sep)
1442 		sep = sep + 1;
1443 	else
1444 		sep = (char *)m3u8_file;
1445 	mpd->ID = gf_strdup(sep);
1446 
1447 	if (update_interval) {
1448 		mpd->minimum_update_period = (u32) (update_interval*1000);
1449 	}
1450 	if (is_end) {
1451 		mpd->media_presentation_duration = (u64) (max_dur*1000);
1452 	}
1453 	if (mpd->type == GF_MPD_TYPE_STATIC)
1454 		mpd->minimum_update_period = mpd->time_shift_buffer_depth = 0;
1455 	mpd->min_buffer_time = 1500;
1456 
1457 	GF_SAFEALLOC(info, GF_MPD_ProgramInfo);
1458 	if (!info) return GF_OUT_OF_MEM;
1459 	info->more_info_url = gf_strdup("http://gpac.io");
1460 	info->title = gf_strdup(title);
1461 	sprintf(str, "Generated from URL %s", gf_file_basename(src_base_url));
1462 	info->source = gf_strdup(str);
1463 	if (!gf_sys_is_test_mode())
1464 		sprintf(str, "Generated by GPAC %s", gf_gpac_version());
1465 	else
1466 		sprintf(str, "Generated by GPAC");
1467 	info->copyright = gf_strdup(str);
1468 	gf_list_add(mpd->program_infos, info);
1469 
1470 	GF_SAFEALLOC(period, GF_MPD_Period);
1471 	if (!period) return GF_OUT_OF_MEM;
1472 	period->adaptation_sets = gf_list_new();
1473 	period->base_URLs = gf_list_new();
1474 	period->subsets = gf_list_new();
1475 	e = gf_list_add(mpd->periods, period);
1476 	if (e) return e;
1477 	if (is_end) {
1478 		period->duration = max_dur*1000;
1479 	}
1480 
1481 	/*check if we use templates*/
1482 	template_base = NULL;
1483 	template_ext = NULL;
1484 	template_width = 0;
1485 	template_idx_start = 0;
1486 	elt = NULL;
1487 	pe = NULL;
1488 
1489 
1490 	nb_streams = gf_list_count(pl->streams);
1491 	for (i=0; i<nb_streams; i++) {
1492 		u32 count_variants;
1493 		u32 width, height, samplerate, num_channels;
1494 		GF_MPD_AdaptationSet *set;
1495 		Bool use_template = use_mpd_templates;
1496 		GF_SAFEALLOC(set, GF_MPD_AdaptationSet);
1497 		if (!set) return GF_OUT_OF_MEM;
1498 		gf_mpd_init_common_attributes((GF_MPD_CommonAttributes *)set);
1499 		set->accessibility = gf_list_new();
1500 		set->role = gf_list_new();
1501 		set->rating = gf_list_new();
1502 		set->viewpoint = gf_list_new();
1503 		set->content_component = gf_list_new();
1504 		set->base_URLs = gf_list_new();
1505 		set->representations = gf_list_new();
1506 		/*assign default ID and group*/
1507 		set->group = -1;
1508 		set->segment_alignment = GF_TRUE;
1509 		e = gf_list_add(period->adaptation_sets, set);
1510 		if (e) return e;
1511 
1512 		/*check if we use templates*/
1513 		stream = gf_list_get(pl->streams, i);
1514 		count_variants = gf_list_count(stream->variants);
1515 
1516 		if (use_template) {
1517 			for (j=0; j<count_variants; j++) {
1518 				u32 count_elements;
1519 				pe = gf_list_get(stream->variants, j);
1520 				if (pe->element_type != TYPE_PLAYLIST)
1521 					continue;
1522 
1523 				count_elements = gf_list_count(pe->element.playlist.elements);
1524 				if (!count_elements)
1525 					continue;
1526 
1527 				if (!template_base && use_template) {
1528 					char *sub_url;
1529 					elt = gf_list_get(pe->element.playlist.elements, 0);
1530 					sub_url = strrchr(elt->url, '/');
1531 					if (!sub_url) {
1532 						sub_url = elt->url;
1533 					} else {
1534 						sub_url ++;
1535 					}
1536 					template_base = gf_strdup(sub_url);
1537 					template_ext = strrchr(template_base, '.');
1538 					k=0;
1539 					while (1) {
1540 						if (strchr("0123456789", template_base[k])) {
1541 							if (template_ext) {
1542 								template_ext[0] = 0;
1543 								template_width = (u32) strlen(template_base + k);
1544 								template_idx_start = atoi(template_base + k);
1545 								template_ext[0] = '.';
1546 							}
1547 							template_base[k] = 0;
1548 							break;
1549 						}
1550 						k++;
1551 						if (!template_base[k]) {
1552 							use_template = GF_FALSE;
1553 							break;
1554 						}
1555 					}
1556 				}
1557 				if (!template_ext) template_ext="";
1558 
1559 				if (use_template) {
1560 					for (k=0; k<count_elements; k++) {
1561 						char szURL[GF_MAX_PATH], *sub_url;
1562 						elt = gf_list_get(pe->element.playlist.elements, k);
1563 
1564 						if (template_width == 2)
1565 							sprintf(szURL, "%s%02d%s", template_base, template_idx_start + k, template_ext);
1566 						else if (template_width == 3)
1567 							sprintf(szURL, "%s%03d%s", template_base, template_idx_start + k, template_ext);
1568 						else if (template_width == 4)
1569 							sprintf(szURL, "%s%04d%s", template_base, template_idx_start + k, template_ext);
1570 						else if (template_width == 5)
1571 							sprintf(szURL, "%s%05d%s", template_base, template_idx_start + k, template_ext);
1572 						else if (template_width == 6)
1573 							sprintf(szURL, "%s%06d%s", template_base, template_idx_start + k, template_ext);
1574 						else
1575 							sprintf(szURL, "%s%d%s", template_base, template_idx_start + k, template_ext);
1576 
1577 						sub_url = strrchr(elt->url, '/');
1578 						if (!sub_url) sub_url = elt->url;
1579 						else sub_url ++;
1580 						if (strcmp(szURL, sub_url)) {
1581 							GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[MPD] Cannot remap M3U8 to segment template MPD, using segment list\n"));
1582 							use_template = GF_FALSE;
1583 							break;
1584 						}
1585 					}
1586 				}
1587 			}
1588 		}
1589 
1590 		/*if we use templates, put the SegmentTemplate element at the adaptationSet level*/
1591 		if (use_template) {
1592 			GF_SAFEALLOC(set->segment_template, GF_MPD_SegmentTemplate);
1593 			if (!set->segment_template)  return GF_OUT_OF_MEM;
1594 			if (pe)
1595 				set->segment_template->duration = (u32)pe->duration_info;
1596 			if (template_width > 1) {
1597 				sprintf(str, "%s$%%0%ddNumber$%s", template_base, template_width, template_ext);
1598 			} else {
1599 				sprintf(str, "%s$Number$%s", template_base, template_ext);
1600 			}
1601 			set->segment_template->media = gf_strdup(str);
1602 			set->segment_template->start_number = template_idx_start;
1603 		} else {
1604 			all_template_used = GF_FALSE;
1605 		}
1606 		for (j=0; j<count_variants; j++) {
1607 			char *base_url=NULL;
1608 			u32 count_elements;
1609 			char szName[20];
1610 #ifndef GPAC_DISABLE_MEDIA_IMPORT
1611 			Bool import_file = do_import;
1612 #endif
1613 			char *byte_range_media_file = NULL;
1614 			GF_MPD_Representation *rep;
1615 			pe = gf_list_get(stream->variants, j);
1616 
1617 			if (pe->element_type == TYPE_MEDIA) {
1618 				GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[M3U8] NOT SUPPORTED: M3U8 Media\n"));
1619 			} else if (pe->load_error) {
1620 				GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[M3U8] Error loading playlist element %s\n", pe->url));
1621 			} else if (pe->element_type != TYPE_PLAYLIST) {
1622 				GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[M3U8] NOT SUPPORTED: M3U8 unknown type for %s\n", pe->url));
1623 			}
1624 
1625 			count_elements = gf_list_count(pe->element.playlist.elements);
1626 			if (parse_sub_playlist && !count_elements)
1627 				continue;
1628 
1629 			if (pe->codecs && (pe->codecs[0] == '\"')) {
1630 				u32 len = (u32) strlen(pe->codecs);
1631 				memmove(pe->codecs, pe->codecs+1, len-1);
1632 				pe->codecs[len-2] = 0;
1633 			}
1634 #ifndef GPAC_DISABLE_MEDIA_IMPORT
1635 			if (pe->bandwidth && pe->codecs && pe->width && pe->height) {
1636 				import_file = GF_FALSE;
1637 			}
1638 #endif
1639 			if (pe->media_type==MEDIA_TYPE_SUBTITLES) {
1640 #ifndef GPAC_DISABLE_MEDIA_IMPORT
1641 				import_file = GF_FALSE;
1642 #endif
1643 				if (!pe->codecs) pe->codecs = gf_strdup("wvtt");
1644 			}
1645 			if (pe->media_type==MEDIA_TYPE_CLOSED_CAPTIONS) {
1646 #ifndef GPAC_DISABLE_MEDIA_IMPORT
1647 				import_file = GF_FALSE;
1648 #endif
1649 				if (!pe->codecs) pe->codecs = gf_strdup("wvtt");
1650 			}
1651 
1652 			k = 0;
1653 #ifndef GPAC_DISABLE_MEDIA_IMPORT
1654 try_next_segment:
1655 #endif
1656 			elt = gf_list_get(pe->element.playlist.elements, k);
1657 			if (parse_sub_playlist && !elt)
1658 				break;
1659 
1660 			if (elt) {
1661 				base_url = gf_url_get_absolute_path(elt->url, pe->url);
1662 			} else {
1663 				base_url = gf_strdup(pe->url);
1664 			}
1665 			sep = strrchr(base_url, '/');
1666 			if (!sep)
1667 				sep = strrchr(base_url, '\\');
1668 			/*keep final '/' */
1669 			if (sep)
1670 				sep[1] = 0;
1671 			/* if no path separator then base_url is just a filename */
1672 			else {
1673 				free(base_url);
1674 				base_url = gf_strdup("./");
1675 			}
1676 
1677 			width = pe->width;
1678 			height = pe->height;
1679 			samplerate = num_channels = 0;
1680 
1681 retry_import:
1682 
1683 #ifndef GPAC_DISABLE_MEDIA_IMPORT
1684 			if (elt && import_file) {
1685 				GF_MediaImporter import;
1686 				char *elt_url = elt->init_segment_url ? elt->init_segment_url : elt->url;
1687 				u64 br_start, br_end;
1688 				char *tmp_file = NULL;
1689 
1690 				br_start = elt->init_segment_url ? elt->init_byte_range_start : elt->byte_range_start;
1691 				br_end = elt->init_segment_url ? elt->init_byte_range_end : elt->byte_range_end;
1692 				elt_url = gf_url_get_absolute_path(elt_url, pe->url);
1693 
1694 				memset(&import, 0, sizeof(GF_MediaImporter));
1695 				import.trackID = 0;
1696 				import.flags = GF_IMPORT_PROBE_ONLY;
1697 
1698 				if (strstr(elt_url, "://") && !strstr(elt_url, "file://")) {
1699 					tmp_file = strrchr(elt_url, '/');
1700 					if (!tmp_file)
1701 						tmp_file = strrchr(elt_url, '\\');
1702 					if (tmp_file) {
1703 						tmp_file++;
1704 						e = gf_dm_wget(elt_url, tmp_file, br_start, br_end, NULL);
1705 						if (e == GF_OK) {
1706 							import.in_name = tmp_file;
1707 						}
1708 					}
1709 				} else {
1710 					import.in_name = elt_url;
1711 				}
1712 
1713 				if (!strstr(elt_url, "://") && !gf_file_exists(elt_url)) {
1714 					import_file = GF_FALSE;
1715 					goto retry_import;
1716 				}
1717 				e = gf_media_import(&import);
1718 
1719 				if (e != GF_OK) {
1720 //					GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[MPD] M3U8 missing Media Element %s< (Playlist %s) %s \n", import.in_name, base_url));
1721 					k++;
1722 					if (elt_url) gf_free(elt_url);
1723 					goto try_next_segment;
1724 				}
1725 
1726 				if (import.in_name && !pe->bandwidth && !elt->init_segment_url && pe->duration_info) {
1727 					u64 pos = 0;
1728 
1729 					Double bw;
1730 					FILE *t = gf_fopen(import.in_name, "rb");
1731 					if (t) {
1732 						pos = gf_fsize(t);
1733 						gf_fclose(t);
1734 					}
1735 					bw = (Double) pos;
1736 					bw *= 8;
1737 					bw /= pe->duration_info;
1738 					pe->bandwidth = (u32) bw;
1739 				} else if (!pe->bandwidth) {
1740 					//unknown bandwidth, default to 128k ...
1741 					pe->bandwidth = 128000;
1742 				}
1743 
1744 				if (tmp_file)
1745 					gf_file_delete(tmp_file);
1746 
1747 				if (!pe->codecs) {
1748 					char szCodecs[1024];
1749 					szCodecs[0] = 0;
1750 					for (k=0; k<import.nb_tracks; k++) {
1751 						if (strlen(import.tk_info[k].szCodecProfile)) {
1752 							if (strlen(szCodecs)) strcat(szCodecs, ",");
1753 							strcat(szCodecs, import.tk_info[k].szCodecProfile);
1754 						}
1755 					}
1756 					pe->codecs = gf_strdup(szCodecs);
1757 				}
1758 				for (k=0; k<import.nb_tracks; k++) {
1759 					switch (import.tk_info[k].stream_type) {
1760 					case GF_ISOM_MEDIA_VISUAL:
1761                     case GF_ISOM_MEDIA_AUXV:
1762                     case GF_ISOM_MEDIA_PICT:
1763 						width = import.tk_info[k].video_info.width;
1764 						height = import.tk_info[k].video_info.height;
1765 						break;
1766 					case GF_ISOM_MEDIA_AUDIO:
1767 						samplerate = import.tk_info[k].audio_info.sample_rate;
1768 						num_channels = import.tk_info[k].audio_info.nb_channels;
1769 						break;
1770 					}
1771 				}
1772 				if (elt_url) gf_free(elt_url);
1773 			}
1774 #endif
1775 			GF_SAFEALLOC(rep, GF_MPD_Representation);
1776 			if (!rep) return GF_OUT_OF_MEM;
1777 			gf_mpd_init_common_attributes((GF_MPD_CommonAttributes *)rep);
1778 			rep->base_URLs = gf_list_new();
1779 			rep->sub_representations = gf_list_new();
1780 
1781 			/*get rid of level 0 aac*/
1782 			if (elt && strstr(elt->url, ".aac"))
1783 				rep->playback.disabled = GF_TRUE;
1784 
1785 			e = gf_list_add(set->representations, rep);
1786 			if (e) return e;
1787 			sprintf(szName, "R%d_%d", i+1, j+1);
1788 			rep->id = gf_strdup(szName);
1789 			rep->bandwidth = pe->bandwidth;
1790 			/* TODO : if mime-type is still unknown, don't try to add codec information since it would be wrong */
1791 			if (!strcmp(M3U8_UNKNOWN_MIME_TYPE, mimeTypeForM3U8Segments)) {
1792 				GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[M3U8] Unknown mime-type when converting from M3U8 HLS playlist, setting %s\n", mimeTypeForM3U8Segments));
1793 			}
1794 			if (elt && elt->init_segment_url && (strstr(elt->init_segment_url, ".mp4") || strstr(elt->init_segment_url, ".MP4")) ) {
1795 				rep->mime_type = gf_strdup(samplerate ? "audio/mp4" : "video/mp4");
1796 			} else {
1797 				rep->mime_type = gf_strdup(mimeTypeForM3U8Segments);
1798 			}
1799 			if (pe->codecs) {
1800 				rep->codecs = gf_strdup(pe->codecs);
1801 			}
1802 			if (pe->language) {
1803 				//???
1804 			}
1805 			if (width && height) {
1806 				rep->width = width;
1807 				rep->height = height;
1808 			}
1809 			if (samplerate) {
1810 				rep->samplerate = samplerate;
1811 			}
1812 			if (num_channels) {
1813 				GF_MPD_Descriptor *desc;
1814 				GF_SAFEALLOC(desc, GF_MPD_Descriptor);
1815 				if (desc) {
1816 					char szChan[10];
1817 					desc->scheme_id_uri = gf_strdup("urn:mpeg:dash:23003:3:audio_channel_configuration:2011");
1818 					sprintf(szChan, "%d", num_channels);
1819 					desc->value = gf_strdup(szChan);
1820 					if (!rep->audio_channels) rep->audio_channels = gf_list_new();
1821 					gf_list_add(rep->audio_channels, desc);
1822 				}
1823 			}
1824 
1825 
1826 			if (use_template) {
1827 				GF_MPD_BaseURL *url;
1828 				GF_SAFEALLOC(url, GF_MPD_BaseURL);
1829 				if (! url) return GF_OUT_OF_MEM;
1830 				e = gf_list_add(rep->base_URLs, url);
1831 				if (e) return GF_OUT_OF_MEM;
1832 				url->URL = gf_strdup(base_url);
1833 
1834 
1835 				if (elt->init_segment_url) {
1836 					u32 len = (u32) strlen(base_url);
1837 					GF_SAFEALLOC(rep->segment_template, GF_MPD_SegmentTemplate);
1838 					if (!rep->segment_template)  return GF_OUT_OF_MEM;
1839 					rep->segment_template->start_number = (u32) -1;
1840 					if (!strncmp(base_url, elt->init_segment_url, len)) {
1841 						rep->segment_template->initialization = gf_strdup(elt->init_segment_url + len);
1842 					} else {
1843 						rep->segment_template->initialization = gf_strdup(elt->init_segment_url);
1844 					}
1845 				}
1846 
1847 				continue;
1848 			}
1849 
1850 			byte_range_media_file = NULL;
1851 			elt = gf_list_get(pe->element.playlist.elements, 0);
1852 			if (elt && (elt->byte_range_end || elt->byte_range_start)) {
1853 				GF_MPD_BaseURL *url;
1854 				GF_SAFEALLOC(url, GF_MPD_BaseURL);
1855 				if (! url) return GF_OUT_OF_MEM;
1856 				e = gf_list_add(rep->base_URLs, url);
1857 				if (e) return GF_OUT_OF_MEM;
1858 				byte_range_media_file = elt->url;
1859 				url->URL = gf_strdup(byte_range_media_file);
1860 			} else {
1861 				u32 url_len = (u32) strlen(base_url);
1862 				if (!strcmp(base_url, "./") || !strcmp(base_url, ".")) {
1863 
1864 				} else if (strncmp(base_url, mpd_file, url_len)) {
1865 					GF_MPD_BaseURL *url;
1866 					GF_SAFEALLOC(url, GF_MPD_BaseURL);
1867 					if (! url) return GF_OUT_OF_MEM;
1868 					e = gf_list_add(rep->base_URLs, url);
1869 					if (e) return GF_OUT_OF_MEM;
1870 					url->URL = gf_url_concatenate_parent(mpd_file, base_url);
1871 				}
1872 			}
1873 
1874 			GF_SAFEALLOC(rep->segment_list, GF_MPD_SegmentList);
1875 			if (!rep->segment_list) return GF_OUT_OF_MEM;
1876 			// doesn't parse sub-playlists, we need to save URL to these sub-playlist in xlink:href so that we can get the segment URL when we need
1877 			// note: for MPD type static, always parse all sub-playlist because we just do it once in a period
1878 			if (/*(mpd->type == GF_MPD_TYPE_DYNAMIC) && */ !parse_sub_playlist) {
1879 				rep->segment_list->xlink_href = pe->url;
1880 				pe->url=NULL;
1881 				gf_free(base_url);
1882 				base_url = NULL;
1883 				if (template_base) {
1884 					gf_free(template_base);
1885 					template_base = NULL;
1886 				}
1887 				continue;
1888 			}
1889 			rep->segment_list->segment_URLs = gf_list_new();
1890 			rep->segment_list->duration = (u64) (pe->duration_info * 1000);
1891 			rep->segment_list->timescale = 1000;
1892 			if (elt && elt->init_segment_url) {
1893 				u32 len = (u32) strlen(base_url);
1894 				GF_SAFEALLOC(rep->segment_list->initialization_segment, GF_MPD_URL);
1895 				if (!rep->segment_list->initialization_segment) return GF_OUT_OF_MEM;
1896 
1897 				if (!strncmp(base_url, elt->init_segment_url, len)) {
1898 					rep->segment_list->initialization_segment->sourceURL = gf_strdup(elt->init_segment_url + len);
1899 				} else {
1900 					rep->segment_list->initialization_segment->sourceURL = gf_strdup(elt->init_segment_url);
1901 				}
1902 				if (elt->init_byte_range_end) {
1903 					GF_SAFEALLOC(rep->segment_list->initialization_segment->byte_range, GF_MPD_ByteRange);
1904 					if (!rep->segment_list->initialization_segment->byte_range) return GF_OUT_OF_MEM;
1905 
1906 					rep->segment_list->initialization_segment->byte_range->start_range = elt->init_byte_range_start;
1907 					rep->segment_list->initialization_segment->byte_range->end_range = elt->init_byte_range_end;
1908 				}
1909 			}
1910 
1911 
1912 			Double avg_dur = 0;
1913 			Double cur_start=0;
1914 			Bool do_seg_timeline = use_segment_timeline;
1915 
1916 			for (k=0; k<count_elements; k++) {
1917 				Double diff, seg_start;
1918 				GF_MPD_SegmentURL *segment_url;
1919 				elt = gf_list_get(pe->element.playlist.elements, k);
1920 
1921 				if (!avg_dur) avg_dur = elt->duration_info;
1922 				else if (elt->duration_info) {
1923 					diff = elt->duration_info - avg_dur;
1924 					if (diff<0) diff = -diff;
1925 					if (diff > avg_dur/2) {
1926 						do_seg_timeline = GF_TRUE;
1927 					}
1928 				}
1929 				seg_start = ((Double)k*rep->segment_list->duration) / rep->segment_list->timescale;
1930 				if (k) {
1931 					diff = cur_start - seg_start;
1932 					if (diff<0) diff = -diff;
1933 					if (diff > avg_dur/2) {
1934 						do_seg_timeline = GF_TRUE;
1935 					}
1936 				}
1937 
1938 				cur_start += elt->duration_info;
1939 
1940 				GF_SAFEALLOC(segment_url, GF_MPD_SegmentURL);
1941 				if (!segment_url) return GF_OUT_OF_MEM;
1942 				gf_list_add(rep->segment_list->segment_URLs, segment_url);
1943 				if (byte_range_media_file) {
1944 					GF_SAFEALLOC(segment_url->media_range, GF_MPD_ByteRange);
1945 					if (!segment_url->media_range) return GF_OUT_OF_MEM;
1946 					segment_url->media_range->start_range = elt->byte_range_start;
1947 					segment_url->media_range->end_range = elt->byte_range_end;
1948 					if (strcmp(elt->url, byte_range_media_file)) {
1949 						segment_url->media = elt->url;
1950 						elt->url=NULL;
1951 					}
1952 				} else {
1953 					u32 len = (u32) strlen(base_url);
1954 					if (!strncmp(base_url, elt->url, len)) {
1955 						segment_url->media = gf_strdup(elt->url+len);
1956 					} else {
1957 						segment_url->media = elt->url;
1958 						elt->url=NULL;
1959 					}
1960 				}
1961 
1962 				if (elt->drm_method != DRM_NONE) {
1963 					//segment_url->key_url = "aes-128";
1964 					if (elt->key_uri) {
1965 						segment_url->key_url = elt->key_uri;
1966 						elt->key_uri=NULL;
1967 						memcpy(segment_url->key_iv, elt->key_iv, sizeof(bin128));
1968 					}
1969 				}
1970 			}
1971 			if (do_seg_timeline) {
1972 				u64 start_time = 0;
1973 				GF_SAFEALLOC(rep->segment_list->segment_timeline, GF_MPD_SegmentTimeline);
1974 				if (!rep->segment_list->segment_timeline) return GF_OUT_OF_MEM;
1975 
1976 				rep->segment_list->segment_timeline->entries = gf_list_new();
1977 				for (k=0; k<count_elements; k++) {
1978 					u64 dur;
1979 					GF_MPD_SegmentTimelineEntry *se;
1980 					elt = gf_list_get(pe->element.playlist.elements, k);
1981 					GF_SAFEALLOC(se, GF_MPD_SegmentTimelineEntry);
1982 					if (!se) return GF_OUT_OF_MEM;
1983 
1984 					dur = (u64) ( elt->duration_info * rep->segment_list->timescale);
1985 					se->duration = (u32) dur;
1986 					se->start_time = start_time;
1987 					start_time += dur;
1988 					gf_list_add(rep->segment_list->segment_timeline->entries, se);
1989 				}
1990 			}
1991 			gf_free(base_url);
1992 		}
1993 
1994 		if (template_base) {
1995 			gf_free(template_base);
1996 			template_base = NULL;
1997 		}
1998 
1999 	}
2000 	if (all_template_used) {
2001 		mpd->profiles = gf_strdup("urn:mpeg:dash:profile:isoff-live:2011");
2002 	} else {
2003 		mpd->profiles = gf_strdup("urn:mpeg:dash:profile:isoff-main:2011");
2004 	}
2005 
2006 	return GF_OK;
2007 }
2008 
2009 GF_EXPORT
gf_m3u8_to_mpd(const char * m3u8_file,const char * base_url,const char * mpd_file,u32 reload_count,char * mimeTypeForM3U8Segments,Bool do_import,Bool use_mpd_templates,Bool use_segment_timeline,GF_FileDownload * getter,GF_MPD * mpd,Bool parse_sub_playlist,Bool keep_files)2010 GF_Err gf_m3u8_to_mpd(const char *m3u8_file, const char *base_url,
2011                       const char *mpd_file,
2012                       u32 reload_count, char *mimeTypeForM3U8Segments, Bool do_import, Bool use_mpd_templates, Bool use_segment_timeline, GF_FileDownload *getter,
2013                       GF_MPD *mpd, Bool parse_sub_playlist, Bool keep_files)
2014 {
2015 	GF_Err e;
2016 	char *title;
2017 	u32 i, j, k;
2018 	Double update_interval;
2019 	MasterPlaylist *pl = NULL;
2020 	Stream *stream;
2021 	PlaylistElement *pe, *the_pe;
2022 	Bool is_end;
2023 	u32 max_dur = 0;
2024 
2025 	// first, we always need to parse the master playlist
2026 	e = gf_m3u8_parse_master_playlist(m3u8_file, &pl, base_url);
2027 	if (e) {
2028 		GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[M3U8] Failed to parse root playlist '%s', error = %s\n", m3u8_file, gf_error_to_string(e)));
2029 		gf_m3u8_master_playlist_del(&pl);
2030 		return e;
2031 	}
2032 	if (mpd_file == NULL) {
2033 		if (!keep_files) gf_file_delete(m3u8_file);
2034 		mpd_file = m3u8_file;
2035 	}
2036 
2037 	the_pe = NULL;
2038 	pe = NULL;
2039 	i = 0;
2040 	assert(pl);
2041 	assert(pl->streams);
2042 	while ((stream = gf_list_enum(pl->streams, &i))) {
2043 		j = 0;
2044 		while (NULL != (pe = gf_list_enum(stream->variants, &j))) {
2045 			Bool found = GF_FALSE;
2046 			char *suburl;
2047 			if (!pe->url)
2048 				continue;
2049 
2050 			/* filter out duplicated entries (seen on M6 m3u8) */
2051 			for (k=0; k<j-1; ++k) {
2052 				PlaylistElement *a_pe = gf_list_get(stream->variants, k);
2053 				if (a_pe->url && pe->url && !strcmp(a_pe->url, pe->url)) {
2054 					found = GF_TRUE;
2055 					break;
2056 				}
2057 			}
2058 			if (found)
2059 				continue;
2060 
2061 			the_pe = pe;
2062 			suburl = NULL;
2063 
2064 			if (!parse_sub_playlist)
2065 				continue;
2066 
2067 			if (strcmp(base_url, pe->url))
2068 				suburl = gf_url_concatenate(base_url, pe->url);
2069 
2070 			if (!suburl || !strcmp(base_url, suburl)) {
2071 				if (suburl)
2072 					gf_free(suburl);
2073 				GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[M3U8] Not downloading, programs are identical for %s...\n", pe->url));
2074 				continue;
2075 			}
2076 
2077 			if (getter && getter->new_session && getter->del_session && getter->get_cache_name) {
2078 				e = getter->new_session(getter, suburl);
2079 				if (e) {
2080 					gf_free(suburl);
2081 					pe->load_error = e;
2082 					continue;
2083 				}
2084 				if (e == GF_OK) {
2085 					pe->load_error = gf_m3u8_parse_sub_playlist(getter->get_cache_name(getter), &pl, suburl, stream, pe);
2086 				}
2087 				//getter->del_session(getter);
2088 			} else { /* for use in MP4Box */
2089 				if (strstr(suburl, "://")) {
2090 					GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[M3U8] Downloading %s...\n", suburl));
2091 					e = gf_dm_wget(suburl, "tmp.m3u8", 0, 0, NULL);
2092 					if (e == GF_OK) {
2093 						e = gf_m3u8_parse_sub_playlist("tmp.m3u8", &pl, suburl, stream, pe);
2094 					} else {
2095 						GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[M3U8] Download failed for %s\n", suburl));
2096 						e = GF_OK;
2097 					}
2098 					gf_file_delete("tmp.m3u8");
2099 				} else {
2100 					e = gf_m3u8_parse_sub_playlist(suburl, &pl, suburl, stream, pe);
2101 				}
2102 				if (e) {
2103 					GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[M3U8] Failed to parse subplaylist %s\n", suburl));
2104 				}
2105 
2106 			}
2107 			gf_free(suburl);
2108 		}
2109 		if (max_dur < (u32) stream->computed_duration) {
2110 			max_dur = (u32) stream->computed_duration;
2111 		}
2112 	}
2113 
2114 	is_end = !pl->playlist_needs_refresh;
2115 	if (!the_pe) {
2116 		GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[M3U8] The M3U8 playlist is not correct.\n"));
2117 		return GF_BAD_PARAM;
2118 	}
2119 
2120 	/*update interval is set to the duration of the last media file with rules defined in http live streaming RFC section 6.3.4*/
2121 	switch (reload_count) {
2122 	case 0:
2123 		update_interval = the_pe->duration_info;
2124 		break;
2125 	case 1:
2126 		update_interval = (Double)the_pe->duration_info / 2;
2127 		break;
2128 	case 2:
2129 		update_interval = 3 * ((Double)the_pe->duration_info / 2);
2130 		break;
2131 	default:
2132 		update_interval = 3 * the_pe->duration_info;
2133 		break;
2134 	}
2135 	if (is_end || ((the_pe->element_type == TYPE_PLAYLIST) && the_pe->element.playlist.is_ended)) {
2136 		update_interval = 0;
2137 		GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[M3U8] No need to refresh playlist!\n"));
2138 	} else {
2139 		GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[M3U8] Playlist will be refreshed every %g seconds, len=%d\n", update_interval, the_pe->duration_info));
2140 	}
2141 
2142 	title = the_pe->title;
2143 	if (!title || strlen(title) < 2)
2144 		title = the_pe->url;
2145 
2146 	assert(mpd_file);
2147 	assert(mpd);
2148 
2149 	e = gf_m3u8_fill_mpd_struct(pl, m3u8_file, base_url, mpd_file, title, update_interval, mimeTypeForM3U8Segments, do_import, use_mpd_templates, use_segment_timeline, is_end,  max_dur, mpd, parse_sub_playlist);
2150 
2151 	gf_m3u8_master_playlist_del(&pl);
2152 
2153 	//if local file force static
2154 	if (strstr(base_url, "://")==NULL)
2155 		mpd->type = GF_MPD_TYPE_STATIC;
2156 
2157 	return e;
2158 }
2159 
2160 GF_EXPORT
gf_m3u8_solve_representation_xlink(GF_MPD_Representation * rep,GF_FileDownload * getter,Bool * is_static,u64 * duration)2161 GF_Err gf_m3u8_solve_representation_xlink(GF_MPD_Representation *rep, GF_FileDownload *getter, Bool *is_static, u64 *duration)
2162 {
2163 	GF_Err e;
2164 	MasterPlaylist *pl = NULL;
2165 	Stream *stream;
2166 	PlaylistElement *pe;
2167 	u32 k, count_elements;
2168 	u64 start_time=0;
2169 	u32 base_url_len = 0;
2170 	char *base_url = NULL;
2171 
2172 	GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[M3U8] Solving m3u8 variant playlist %s\n", rep->segment_list->xlink_href));
2173 
2174 	if (!getter || !getter->new_session || !getter->del_session || !getter->get_cache_name) {
2175 		GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[M3U8] FileDownloader not found\n"));
2176 		return GF_BAD_PARAM;
2177 	}
2178 
2179 	if (gf_url_is_local(rep->segment_list->xlink_href)) {
2180 		e = gf_m3u8_parse_master_playlist(rep->segment_list->xlink_href, &pl, rep->segment_list->xlink_href);
2181 	} else {
2182 		e = getter->new_session(getter, rep->segment_list->xlink_href);
2183 		if (e) {
2184 			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[M3U8] Download failed for %s\n", rep->segment_list->xlink_href));
2185 			return e;
2186 		}
2187 		e = gf_m3u8_parse_master_playlist(getter->get_cache_name(getter), &pl, rep->segment_list->xlink_href);
2188 	}
2189 	if (e) {
2190 		GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[M3U8] Failed to parse playlist %s\n", rep->segment_list->xlink_href));
2191 		return GF_NON_COMPLIANT_BITSTREAM;
2192 	}
2193 
2194 	assert(pl);
2195 	assert(pl->streams);
2196 	assert(gf_list_count(pl->streams) == 1);
2197 
2198 	if (is_static) {
2199 		*is_static = pl->playlist_needs_refresh ? GF_FALSE : GF_TRUE;
2200 	}
2201 
2202 	stream = (Stream *)gf_list_get(pl->streams, 0);
2203 	assert(gf_list_count(stream->variants) == 1);
2204 	pe = (PlaylistElement *)gf_list_get(stream->variants, 0);
2205 
2206 	if (duration) {
2207 		*duration = (u32) (stream->computed_duration * 1000);
2208 	}
2209 
2210 	if (gf_list_count(rep->base_URLs)) {
2211 		GF_MPD_BaseURL *burl = gf_list_get(rep->base_URLs, 0);
2212 		if (burl->URL) {
2213 			base_url = burl->URL;
2214 			base_url_len = (u32) strlen(base_url);
2215 		}
2216 	}
2217 	if (!base_url) {
2218 		base_url = rep->segment_list->xlink_href;
2219 		if (base_url) {
2220 			char *sep = gf_file_basename(base_url);
2221 			if (sep)
2222 				base_url_len = (u32) (sep - base_url);
2223 		}
2224 	}
2225 
2226 	if (pe->init_segment_url) {
2227 		if (!rep->segment_list->initialization_segment) {
2228 			GF_SAFEALLOC(rep->segment_list->initialization_segment, GF_MPD_URL);
2229 			if (!rep->segment_list->initialization_segment) return GF_OUT_OF_MEM;
2230 
2231 			if (strstr(pe->init_segment_url, "mp4") || strstr(pe->init_segment_url, "MP4")) {
2232 				if (rep->mime_type) gf_free(rep->mime_type);
2233 				rep->mime_type = gf_strdup("video/mp4");
2234 			}
2235 			rep->segment_list->initialization_segment->sourceURL = pe->init_segment_url;
2236 			pe->init_segment_url=NULL;
2237 
2238 			if (pe->init_byte_range_end) {
2239 				GF_SAFEALLOC(rep->segment_list->initialization_segment->byte_range, GF_MPD_ByteRange);
2240 				if (!rep->segment_list->initialization_segment->byte_range) return GF_OUT_OF_MEM;
2241 
2242 				 rep->segment_list->initialization_segment->byte_range->start_range = pe->init_byte_range_start;
2243 				 rep->segment_list->initialization_segment->byte_range->end_range = pe->init_byte_range_end;
2244 			}
2245 		}
2246 	}
2247 	rep->starts_with_sap = pl->independent_segments ? 1: 3;
2248 
2249 	rep->segment_list->duration = (u64) (pe->duration_info * 1000);
2250 	rep->segment_list->timescale = 1000;
2251 	rep->m3u8_media_seq_min = pe->element.playlist.media_seq_min;
2252 	rep->m3u8_media_seq_max = pe->element.playlist.media_seq_max;
2253 	if (!rep->segment_list->segment_URLs)
2254 		rep->segment_list->segment_URLs = gf_list_new();
2255 	count_elements = gf_list_count(pe->element.playlist.elements);
2256 	for (k=0; k<count_elements; k++) {
2257 		GF_MPD_SegmentURL *segment_url;
2258 		char *seg_url;
2259 		PlaylistElement *elt = gf_list_get(pe->element.playlist.elements, k);
2260 		if (!elt)
2261 			continue;
2262 
2263 		//NOTE: for GPAC now, we disable stream AAC to avoid the problem when switching quality. It should be improved later !
2264 		if (strstr(elt->url, ".aac")) {
2265 			rep->playback.disabled = GF_TRUE;
2266 			return GF_OK;
2267 		}
2268 
2269 		GF_SAFEALLOC(segment_url, GF_MPD_SegmentURL);
2270 		if (!segment_url) {
2271 			return GF_OUT_OF_MEM;
2272 		}
2273 		gf_list_add(rep->segment_list->segment_URLs, segment_url);
2274 
2275 		//get absolute url, and remove base from it if we have a baseURL
2276 		if (base_url && !strncmp(elt->url, base_url, base_url_len)) {
2277 			segment_url->media = gf_strdup(elt->url + base_url_len);
2278 		} else {
2279 			seg_url = gf_url_concatenate(pe->url, elt->url);
2280 			if (base_url && !strncmp(seg_url, base_url, base_url_len)) {
2281 				segment_url->media = gf_strdup(seg_url + base_url_len);
2282 				gf_free(seg_url);
2283 			} else {
2284 				segment_url->media = seg_url;
2285 			}
2286 		}
2287 
2288 		if (! elt->utc_start_time) elt->utc_start_time = start_time;
2289 		segment_url->hls_utc_start_time = elt->utc_start_time;
2290 		start_time = elt->utc_start_time + (u64) (1000*elt->duration_info);
2291 
2292 		if (elt->drm_method != DRM_NONE) {
2293 			if (elt->key_uri) {
2294 				segment_url->key_url = elt->key_uri;
2295 				elt->key_uri=NULL;
2296 				memcpy(segment_url->key_iv, elt->key_iv, sizeof(bin128));
2297 			}
2298 		}
2299 		if (elt->byte_range_end) {
2300 			GF_SAFEALLOC(segment_url->media_range, GF_MPD_ByteRange);
2301 			if (!segment_url->media_range) return GF_OUT_OF_MEM;
2302 			segment_url->media_range->start_range = elt->byte_range_start;
2303 			segment_url->media_range->end_range = elt->byte_range_end;
2304 		}
2305 	}
2306 
2307 	if (!gf_list_count(rep->segment_list->segment_URLs)) {
2308 		gf_list_del(rep->segment_list->segment_URLs);
2309 		rep->segment_list->segment_URLs = NULL;
2310 	}
2311 
2312 	gf_free(rep->segment_list->xlink_href);
2313 	rep->segment_list->xlink_href = NULL;
2314 
2315 	gf_m3u8_master_playlist_del(&pl);
2316 
2317 	return GF_OK;
2318 }
2319 
2320 GF_EXPORT
gf_mpd_solve_segment_list_xlink(GF_MPD * mpd,GF_XMLNode * root)2321 GF_MPD_SegmentList *gf_mpd_solve_segment_list_xlink(GF_MPD *mpd, GF_XMLNode *root)
2322 {
2323 	return gf_mpd_parse_segment_list(mpd, root);
2324 }
2325 
2326 GF_EXPORT
gf_mpd_delete_segment_list(GF_MPD_SegmentList * segment_list)2327 void gf_mpd_delete_segment_list(GF_MPD_SegmentList *segment_list)
2328 {
2329 	gf_mpd_segment_list_free(segment_list);
2330 }
2331 
2332 
gf_mpd_lf(FILE * out,s32 indent)2333 static GFINLINE void gf_mpd_lf(FILE *out, s32 indent)
2334 {
2335 	if (indent>=0) gf_fprintf(out, "\n");
2336 }
gf_mpd_nl(FILE * out,s32 indent)2337 static GFINLINE void gf_mpd_nl(FILE *out, s32 indent)
2338 {
2339 	if (indent>=0) {
2340 		u32 i=(u32)indent;
2341 		while (i) {
2342 			gf_fprintf(out, " ");
2343 			i--;
2344 		}
2345 	}
2346 }
2347 
2348 /*time is given in ms*/
gf_mpd_print_date(FILE * out,char * name,u64 time)2349 void gf_mpd_print_date(FILE *out, char *name, u64 time)
2350 {
2351 	time_t gtime;
2352 	struct tm *t;
2353 	u32 sec;
2354 	u32 ms;
2355 	gtime = time / 1000;
2356 	sec = (u32)(time / 1000);
2357 	ms = (u32)(time - ((u64)sec) * 1000);
2358 
2359 	if (name) {
2360 		gf_fprintf(out, " %s=\"", name);
2361 	}
2362 	t = gf_gmtime(&gtime);
2363 	sec = t->tm_sec;
2364 	//see issue #859, no clue how this happened...
2365 	if (sec > 60)
2366 		sec = 60;
2367 	gf_fprintf(out, "%d-%02d-%02dT%02d:%02d:%02d.%03dZ", 1900 + t->tm_year, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, sec, ms);
2368 
2369 	if (name) {
2370 		gf_fprintf(out, "\"");
2371 	}
2372 }
2373 
gf_mpd_print_duration(FILE * out,char * name,u64 duration_in_ms,Bool UseHoursAndMinutes)2374 void gf_mpd_print_duration(FILE *out, char *name, u64 duration_in_ms, Bool UseHoursAndMinutes)
2375 {
2376 	u32 h, m, s, ms;
2377 
2378 	h = (u32) (duration_in_ms / 3600000);
2379 	m = (u32) (duration_in_ms/ 60000) - h*60;
2380 	s = (u32) (duration_in_ms/1000) - h*3600 - m*60;
2381 	ms = (u32) (duration_in_ms) - h*3600*1000 - m*60*1000 - s*1000;
2382 
2383 	gf_fprintf(out, " %s=\"PT", name);
2384 	if(UseHoursAndMinutes)
2385 		gf_fprintf(out, "%dH%dM", h, m);
2386 	gf_fprintf(out, "%d", s);
2387 	gf_fprintf(out, ".");
2388 	gf_fprintf(out, "%03dS\"", ms);
2389 }
2390 
gf_mpd_print_base_url(FILE * out,GF_MPD_BaseURL * base_URL,s32 indent)2391 static void gf_mpd_print_base_url(FILE *out, GF_MPD_BaseURL *base_URL, s32 indent)
2392 {
2393 	gf_mpd_nl(out, indent);
2394 	gf_fprintf(out, "<BaseURL");
2395 	if (base_URL->service_location)
2396 		gf_xml_dump_string(out, " serviceLocation=\"", base_URL->service_location, "\"");
2397 	if (base_URL->byte_range)
2398 		gf_fprintf(out, " byteRange=\""LLD"-"LLD"\"", base_URL->byte_range->start_range, base_URL->byte_range->end_range);
2399 
2400 	gf_xml_dump_string(out, ">", base_URL->URL, "</BaseURL>");
2401 	gf_mpd_lf(out, indent);
2402 }
2403 
gf_mpd_print_base_urls(FILE * out,GF_List * base_URLs,s32 indent)2404 static void gf_mpd_print_base_urls(FILE *out, GF_List *base_URLs, s32 indent)
2405 {
2406 	GF_MPD_BaseURL *url;
2407 	u32 i;
2408 	i=0;
2409 
2410 	while ((url = (GF_MPD_BaseURL *)gf_list_enum(base_URLs, &i))) {
2411 		gf_mpd_print_base_url(out, url, indent);
2412 	}
2413 }
2414 
gf_mpd_print_url(FILE * out,GF_MPD_URL * url,char * name,s32 indent)2415 static void gf_mpd_print_url(FILE *out, GF_MPD_URL *url, char *name, s32 indent)
2416 {
2417 	gf_mpd_nl(out, indent);
2418 	gf_fprintf(out, "<%s", name);
2419 	if (url->byte_range) gf_fprintf(out, " range=\""LLD"-"LLD"\"", url->byte_range->start_range, url->byte_range->end_range);
2420 	if (url->sourceURL) gf_fprintf(out, " sourceURL=\"%s\"", url->sourceURL);
2421 	gf_fprintf(out, "/>");
2422 	gf_mpd_lf(out, indent);
2423 }
2424 
gf_mpd_print_segment_base_attr(FILE * out,GF_MPD_SegmentBase * s)2425 static void gf_mpd_print_segment_base_attr(FILE *out, GF_MPD_SegmentBase *s)
2426 {
2427 	if (s->timescale) gf_fprintf(out, " timescale=\"%d\"", s->timescale);
2428 	if (s->presentation_time_offset) gf_fprintf(out, " presentationTimeOffset=\""LLU"\"", s->presentation_time_offset);
2429 	if (s->index_range_exact) gf_fprintf(out, " indexRangeExact=\"true\"");
2430 	if (s->index_range) gf_fprintf(out, " indexRange=\""LLD"-"LLD"\"", s->index_range->start_range, s->index_range->end_range);
2431 	if (s->availability_time_offset) gf_fprintf(out, " availabilityTimeOffset=\"%g\"", s->availability_time_offset);
2432 	if (s->time_shift_buffer_depth)
2433 		gf_mpd_print_duration(out, "timeShiftBufferDepth", s->time_shift_buffer_depth, GF_TRUE);
2434 }
2435 
gf_mpd_print_segment_base(FILE * out,GF_MPD_SegmentBase * s,s32 indent)2436 static void gf_mpd_print_segment_base(FILE *out, GF_MPD_SegmentBase *s, s32 indent)
2437 {
2438 	gf_mpd_nl(out, indent);
2439 	gf_fprintf(out, "<SegmentBase");
2440 	gf_mpd_print_segment_base_attr(out, s);
2441 	gf_fprintf(out, ">");
2442 	gf_mpd_lf(out, indent);
2443 
2444 	if (s->initialization_segment) gf_mpd_print_url(out, s->initialization_segment, "Initialization", indent+1);
2445 	if (s->representation_index) gf_mpd_print_url(out, s->representation_index, "RepresentationIndex", indent+1);
2446 
2447 	gf_mpd_nl(out, indent);
2448 	gf_fprintf(out, "</SegmentBase>");
2449 	gf_mpd_lf(out, indent);
2450 }
2451 
gf_mpd_print_segment_timeline(FILE * out,GF_MPD_SegmentTimeline * tl,s32 indent)2452 static void gf_mpd_print_segment_timeline(FILE *out, GF_MPD_SegmentTimeline *tl, s32 indent)
2453 {
2454 	u32 i;
2455 	u64 start_time=0;
2456 	GF_MPD_SegmentTimelineEntry *se;
2457 
2458 	gf_mpd_nl(out, indent);
2459 	gf_fprintf(out, "<SegmentTimeline>");
2460 	gf_mpd_lf(out, indent);
2461 
2462 	i = 0;
2463 	while ( (se = gf_list_enum(tl->entries, &i))) {
2464 		gf_mpd_nl(out, indent+1);
2465 		gf_fprintf(out, "<S");
2466 		if (!start_time || (se->start_time != start_time)) {
2467 			gf_fprintf(out, " t=\""LLD"\"", se->start_time);
2468 			start_time = se->start_time;
2469 		}
2470 		start_time += (se->repeat_count+1) * se->duration;
2471 
2472 		if (se->duration) gf_fprintf(out, " d=\"%d\"", se->duration);
2473 		if (se->repeat_count) gf_fprintf(out, " r=\"%d\"", se->repeat_count);
2474 		gf_fprintf(out, "/>");
2475 		gf_mpd_lf(out, indent);
2476 	}
2477 	gf_mpd_nl(out, indent);
2478 	gf_fprintf(out, "</SegmentTimeline>");
2479 	gf_mpd_lf(out, indent);
2480 }
2481 
gf_mpd_segmentimeline_new(void)2482 GF_MPD_SegmentTimeline *gf_mpd_segmentimeline_new(void)
2483 {
2484 	GF_MPD_SegmentTimeline *seg_tl;
2485 	GF_SAFEALLOC(seg_tl, GF_MPD_SegmentTimeline);
2486 	if (seg_tl && !seg_tl->entries) seg_tl->entries=gf_list_new();
2487 	return seg_tl;
2488 }
2489 
gf_mpd_print_multiple_segment_base(FILE * out,GF_MPD_MultipleSegmentBase * ms,s32 indent,Bool close_if_no_child)2490 static u32 gf_mpd_print_multiple_segment_base(FILE *out, GF_MPD_MultipleSegmentBase *ms, s32 indent, Bool close_if_no_child)
2491 {
2492 	gf_mpd_print_segment_base_attr(out, (GF_MPD_SegmentBase *)ms);
2493 
2494 	if (ms->start_number != (u32) -1) gf_fprintf(out, " startNumber=\"%d\"", ms->start_number);
2495 	if (ms->duration) gf_fprintf(out, " duration=\""LLD"\"", ms->duration);
2496 
2497 
2498 	if (!ms->bitstream_switching_url && !ms->segment_timeline && !ms->initialization_segment && !ms->representation_index) {
2499 		if (close_if_no_child) gf_fprintf(out, "/");
2500 		gf_fprintf(out, ">");
2501 		gf_mpd_lf(out, indent);
2502 		return 1;
2503 	}
2504 	gf_fprintf(out, ">");
2505 	gf_mpd_lf(out, indent);
2506 
2507 	if (ms->initialization_segment) gf_mpd_print_url(out, ms->initialization_segment, "Initialization", indent+1);
2508 	if (ms->representation_index) gf_mpd_print_url(out, ms->representation_index, "RepresentationIndex", indent+1);
2509 
2510 	if (ms->segment_timeline) gf_mpd_print_segment_timeline(out, ms->segment_timeline, indent+1);
2511 	if (ms->bitstream_switching_url) gf_mpd_print_url(out, ms->bitstream_switching_url, "BitstreamSwitching", indent+1);
2512 	return 0;
2513 }
2514 
gf_mpd_print_segment_list(FILE * out,GF_MPD_SegmentList * s,s32 indent)2515 static void gf_mpd_print_segment_list(FILE *out, GF_MPD_SegmentList *s, s32 indent)
2516 {
2517 	gf_mpd_nl(out, indent);
2518 	gf_fprintf(out, "<SegmentList");
2519 	if (s->xlink_href) {
2520 		gf_fprintf(out, " xlink:href=\"%s\"", s->xlink_href);
2521 		if (s->xlink_actuate_on_load)
2522 			gf_fprintf(out, " actuate=\"onLoad\"");
2523 	}
2524 	gf_mpd_print_multiple_segment_base(out, (GF_MPD_MultipleSegmentBase *)s, indent, GF_FALSE);
2525 
2526 	if (s->segment_URLs) {
2527 		u32 i;
2528 		GF_MPD_SegmentURL *url;
2529 		i = 0;
2530 		while ( (url = gf_list_enum(s->segment_URLs, &i))) {
2531 			gf_mpd_nl(out, indent+1);
2532 			gf_fprintf(out, "<SegmentURL");
2533                        if (url->media) gf_fprintf(out, " media=\"%s\"", url->media);
2534                        if (url->duration)gf_fprintf(out, " duration=\""LLU"\"", url->duration);
2535 			if (url->index) gf_fprintf(out, " index=\"%s\"", url->index);
2536 			if (url->media_range && url->media_range->end_range!=0) gf_fprintf(out, " mediaRange=\""LLD"-"LLD"\"", url->media_range->start_range, url->media_range->end_range);
2537 			if (url->index_range && url->index_range->end_range!=0) gf_fprintf(out, " indexRange=\""LLD"-"LLD"\"", url->index_range->start_range, url->index_range->end_range);
2538 			if (url->key_url) {
2539 				u32 idx;
2540 				gf_fprintf(out, " hls:keyMethod=\"aes-128\" hls:KeyURL=%s hls:KeyIV=\"", url->key_url);
2541 				for (idx=0; idx<16; idx++) {
2542 					gf_fprintf(out, "%02x", url->key_iv[idx]);
2543 				}
2544 				gf_fprintf(out, "\"");
2545 			}
2546 			gf_fprintf(out, "/>");
2547 			gf_mpd_lf(out, indent);
2548 		}
2549 	}
2550 	gf_mpd_nl(out, indent);
2551 	gf_fprintf(out, "</SegmentList>");
2552 	gf_mpd_lf(out, indent);
2553 }
2554 
gf_mpd_print_segment_template(FILE * out,GF_MPD_SegmentTemplate * s,s32 indent)2555 static void gf_mpd_print_segment_template(FILE *out, GF_MPD_SegmentTemplate *s, s32 indent)
2556 {
2557 	gf_mpd_nl(out, indent);
2558 	gf_fprintf(out, "<SegmentTemplate");
2559 
2560 	if (s->media) gf_fprintf(out, " media=\"%s\"", s->media);
2561 	if (s->index) gf_fprintf(out, " index=\"%s\"", s->index);
2562 	if (s->initialization) gf_fprintf(out, " initialization=\"%s\"", s->initialization);
2563 	if (s->bitstream_switching) gf_fprintf(out, " bitstreamSwitching=\"%s\"", s->bitstream_switching);
2564 
2565 	if (gf_mpd_print_multiple_segment_base(out, (GF_MPD_MultipleSegmentBase *)s, indent, GF_TRUE))
2566 		return;
2567 
2568 	gf_mpd_nl(out, indent);
2569 	gf_fprintf(out, "</SegmentTemplate>");
2570 	gf_mpd_lf(out, indent);
2571 }
2572 
gf_mpd_extensible_print_attr(FILE * out,GF_MPD_ExtensibleVirtual * item)2573 static void gf_mpd_extensible_print_attr(FILE *out, GF_MPD_ExtensibleVirtual *item)
2574 {
2575 	if (item->attributes) {
2576 		u32 j=0;
2577 		GF_XMLAttribute *att;
2578 		while ((att = (GF_XMLAttribute *)gf_list_enum(item->attributes, &j))) {
2579 			if (!strcmp(att->name, "xmlns")) continue;
2580 			else if (!strcmp(att->name, "xmlns:gpac")) continue;
2581 			gf_fprintf(out, " %s=\"", att->name);
2582 			gf_xml_dump_string(out, NULL, att->value, "\"");
2583 		}
2584 	}
2585 }
2586 
gf_mpd_extensible_print_nodes(FILE * out,GF_MPD_ExtensibleVirtual * item,s32 indent)2587 static void gf_mpd_extensible_print_nodes(FILE *out, GF_MPD_ExtensibleVirtual *item, s32 indent)
2588 {
2589 	if (item->children) {
2590 		u32 j=0;
2591 		GF_XMLNode *child;
2592 		gf_fprintf(out, ">");
2593 		gf_mpd_lf(out, indent);
2594 		while ((child = (GF_XMLNode *)gf_list_enum(item->children, &j))) {
2595 			char *txt = gf_xml_dom_serialize(child, 0);
2596 			gf_mpd_nl(out, indent+1);
2597 			gf_fprintf(out, "%s", txt);
2598 			gf_free(txt);
2599 			gf_mpd_lf(out, indent);
2600 		}
2601 	}
2602 }
2603 
gf_mpd_print_descriptors(FILE * out,GF_List * desc_list,char * desc_name,s32 indent)2604 static void gf_mpd_print_descriptors(FILE *out, GF_List *desc_list, char *desc_name, s32 indent)
2605 {
2606 	u32 i=0;
2607 	GF_MPD_Descriptor *desc;
2608 	while ((desc = (GF_MPD_Descriptor *)gf_list_enum(desc_list, &i))) {
2609 		gf_mpd_nl(out, indent);
2610 		gf_fprintf(out, "<%s", desc_name);
2611 		if (desc->id) gf_fprintf(out, " id=\"%s\"", desc->id);
2612 		if (desc->scheme_id_uri) gf_fprintf(out, " schemeIdUri=\"%s\"", desc->scheme_id_uri);
2613 		if (desc->value) gf_fprintf(out, " value=\"%s\"", desc->value);
2614 
2615 		if (desc->attributes) gf_mpd_extensible_print_attr(out, (GF_MPD_ExtensibleVirtual*)desc);
2616 
2617 		if (desc->children) {
2618 			gf_mpd_extensible_print_nodes(out, (GF_MPD_ExtensibleVirtual*)desc, indent);
2619 			gf_mpd_nl(out, indent);
2620 			gf_fprintf(out, "</%s>", desc_name);
2621 			gf_mpd_lf(out, indent);
2622 		} else {
2623 			gf_fprintf(out, "/>");
2624 			gf_mpd_lf(out, indent);
2625 		}
2626 	}
2627 }
2628 
gf_mpd_print_content_component(FILE * out,GF_List * content_component,s32 indent)2629 static void gf_mpd_print_content_component(FILE *out, GF_List *content_component , s32 indent)
2630 {
2631 	u32 i=0;
2632 	GF_MPD_ContentComponent *cc;
2633 	while ((cc = gf_list_enum(content_component, &i))) {
2634 		gf_mpd_nl(out, indent);
2635 		gf_fprintf(out, "<ContentComponent id=\"%d\" contentType=\"%s\"", cc->id, cc->type);
2636 		if (cc->lang)
2637 			gf_fprintf(out, " lang=\"%s\"", cc->lang);
2638 		gf_fprintf(out, "/>");
2639 		gf_mpd_lf(out, indent);
2640 	}
2641 }
2642 
gf_mpd_print_common_attributes(FILE * out,GF_MPD_CommonAttributes * ca)2643 static void gf_mpd_print_common_attributes(FILE *out, GF_MPD_CommonAttributes *ca)
2644 {
2645 	if (ca->profiles) {
2646 		gf_xml_dump_string(out, " profiles=\"", ca->profiles, "\"");
2647 	}
2648 	if (ca->mime_type) gf_fprintf(out, " mimeType=\"%s\"", ca->mime_type);
2649 	if (ca->codecs) gf_fprintf(out, " codecs=\"%s\"", ca->codecs);
2650 	if (ca->width) gf_fprintf(out, " width=\"%d\"", ca->width);
2651 	if (ca->height) gf_fprintf(out, " height=\"%d\"", ca->height);
2652 	if (ca->framerate){
2653 		gf_fprintf(out, " frameRate=\"%d",ca->framerate->num);
2654 		if(ca->framerate->den>1)gf_fprintf(out, "/%d",ca->framerate->den);
2655 		gf_fprintf(out, "\"");
2656 	}
2657 	if (ca->sar) gf_fprintf(out, " sar=\"%d:%d\"", ca->sar->num, ca->sar->den);
2658 	if (ca->samplerate) gf_fprintf(out, " audioSamplingRate=\"%d\"", ca->samplerate);
2659 	if (ca->segmentProfiles) {
2660 		gf_xml_dump_string(out, " segmentProfiles=\"", ca->segmentProfiles, "\"");
2661 	}
2662 	if (ca->maximum_sap_period) gf_fprintf(out, " maximumSAPPeriod=\"%d\"", ca->maximum_sap_period);
2663 	if (ca->starts_with_sap) gf_fprintf(out, " startWithSAP=\"%d\"", ca->starts_with_sap);
2664 	if ((ca->max_playout_rate!=1.0)) gf_fprintf(out, " maxPlayoutRate=\"%g\"", ca->max_playout_rate);
2665 	if (ca->coding_dependency) gf_fprintf(out, " codingDependency=\"true\"");
2666 	if (ca->scan_type != GF_MPD_SCANTYPE_UNKNOWN) gf_fprintf(out, " scanType=\"%s\"", ca->scan_type == GF_MPD_SCANTYPE_PROGRESSIVE ? "progressive" : "interlaced");
2667 }
2668 
gf_mpd_print_common_children(FILE * out,GF_MPD_CommonAttributes * ca,s32 indent)2669 static u32 gf_mpd_print_common_children(FILE *out, GF_MPD_CommonAttributes *ca, s32 indent)
2670 {
2671 	gf_mpd_print_descriptors(out, ca->frame_packing, "Framepacking", indent);
2672 	gf_mpd_print_descriptors(out, ca->audio_channels, "AudioChannelConfiguration", indent);
2673 	gf_mpd_print_descriptors(out, ca->content_protection, "ContentProtection", indent);
2674 	gf_mpd_print_descriptors(out, ca->essential_properties, "EssentialProperty", indent);
2675 	gf_mpd_print_descriptors(out, ca->supplemental_properties, "SupplementalProperty", indent);
2676 
2677 	if (ca->isobmf_tracks) {
2678 		u32 k=0;
2679 		GF_MPD_ISOBMFInfo *info;
2680 		gf_mpd_nl(out, indent);
2681 		gf_fprintf(out, "<ISOBMFInfo>");
2682 		gf_mpd_lf(out, indent);
2683 		while ((info = (GF_MPD_ISOBMFInfo *) gf_list_enum(ca->isobmf_tracks, &k))) {
2684 			gf_mpd_nl(out, indent+1);
2685 			gf_fprintf(out, "<ISOBMFTrack");
2686 			if (info->trackID) gf_fprintf(out, " ID=\"%d\"", info->trackID);
2687 			if (info->stsd) gf_fprintf(out, " stsd=\"%s\"", info->stsd);
2688 			if (info->mediaOffset) gf_fprintf(out, " offset=\""LLD"\"", info->mediaOffset);
2689 			gf_fprintf(out, "/>");
2690 			gf_mpd_lf(out, indent);
2691 		}
2692 		gf_mpd_nl(out, indent);
2693 		gf_fprintf(out, "</ISOBMFInfo>");
2694 		gf_mpd_lf(out, indent);
2695 	}
2696 	return 0;
2697 }
2698 
gf_mpd_print_dasher_context(FILE * out,GF_DASH_SegmenterContext * dasher,s32 indent)2699 static void gf_mpd_print_dasher_context(FILE *out, GF_DASH_SegmenterContext *dasher, s32 indent)
2700 {
2701 	gf_mpd_nl(out, indent);
2702 	gf_fprintf(out, "<gpac:dasher ");
2703 	gf_fprintf(out, "done=\"%s\" ", dasher->done ? "true" : "false");
2704 	gf_fprintf(out, "init=\"%s\" ", dasher->init_seg);
2705 	gf_fprintf(out, "template=\"%s\" ", dasher->template_seg);
2706 	if (dasher->template_idx)
2707 		gf_fprintf(out, "index=\"%s\" ", dasher->template_idx);
2708 	gf_fprintf(out, "segNumber=\"%d\" ", dasher->seg_number);
2709 	gf_fprintf(out, "url=\"%s\" ", dasher->src_url);
2710 	gf_fprintf(out, "lastPacketIdx=\""LLU"\" ", dasher->last_pck_idx);
2711 	gf_fprintf(out, "pidID=\"%d\" ", dasher->pid_id);
2712 
2713 	if (dasher->dep_pid_id)
2714 		gf_fprintf(out, "depID=\"%d\" ", dasher->dep_pid_id);
2715 
2716 	if (dasher->period_id)
2717 		gf_fprintf(out, "periodID=\"%s\" ", dasher->period_id);
2718 
2719 	if (dasher->period_duration)
2720 		gf_fprintf(out, "periodDuration=\"%g\" ", dasher->period_duration);
2721 	if (dasher->period_start)
2722 		gf_fprintf(out, "periodStart=\"%g\" ", dasher->period_start);
2723 
2724 	gf_fprintf(out, "multiPIDInit=\"%s\" ", dasher->multi_pids ? "true" : "false");
2725 	gf_fprintf(out, "dashDuration=\"%g\" ", dasher->dash_dur);
2726 	gf_fprintf(out, "nextSegmentStart=\""LLU"\" ", dasher->next_seg_start);
2727 	gf_fprintf(out, "firstCTS=\""LLU"\" ", dasher->first_cts);
2728 	gf_fprintf(out, "firstDTS=\""LLU"\" ", dasher->first_dts);
2729 	gf_fprintf(out, "mpdTimescale=\"%d\" ", dasher->mpd_timescale);
2730 	gf_fprintf(out, "sourcePID=\"%d\" ", dasher->source_pid);
2731 	gf_fprintf(out, "estimatedNextDTS=\""LLU"\" ", dasher->est_next_dts);
2732 	gf_fprintf(out, "cumulatedDur=\"%g\" ", dasher->cumulated_dur);
2733 	gf_fprintf(out, "cumulatedSubdur=\"%g\" ", dasher->cumulated_subdur);
2734 
2735 	gf_fprintf(out, "moofSN=\"%d\" ", dasher->moof_sn);
2736 	gf_fprintf(out, "moofInc=\"%d\" ", dasher->moof_sn_inc);
2737 
2738 	if (dasher->segs_purged)
2739 		gf_fprintf(out, "segsPurged=\"%d\" ", dasher->segs_purged);
2740 	if (dasher->dur_purged)
2741 		gf_fprintf(out, "durPurged=\"%g\" ", dasher->dur_purged);
2742 
2743 	if (dasher->nb_repeat)
2744 		gf_fprintf(out, "nbRepeat=\"%d\" ", dasher->nb_repeat);
2745 	if (dasher->ts_offset)
2746 		gf_fprintf(out, "tsOffset=\""LLU"\" ", dasher->ts_offset);
2747 	if (dasher->mux_pids)
2748 		gf_fprintf(out, "muxPIDs=\"%s\" ", dasher->mux_pids);
2749 
2750 	if (dasher->last_dyn_period_id) {
2751 		gf_fprintf(out, "lastDynPeriodID=\"%d\" ", dasher->last_dyn_period_id);
2752 	}
2753 
2754 	gf_fprintf(out, "ownsSet=\"%s\"/>", dasher->owns_set ? "true" : "false");
2755 	gf_mpd_lf(out, indent);
2756 }
2757 
gf_mpd_print_dasher_segments(FILE * out,GF_List * segments,s32 indent)2758 static void gf_mpd_print_dasher_segments(FILE *out, GF_List *segments, s32 indent)
2759 {
2760 	u32 i, count = gf_list_count(segments);
2761 	if (!count) return;
2762 
2763 	gf_mpd_nl(out, indent);
2764 	gf_fprintf(out, "<gpac:segments>\n");
2765 	for (i=0; i<count; i++) {
2766 		GF_DASH_SegmentContext *sctx = gf_list_get(segments, i);
2767 		gf_mpd_nl(out, indent+1);
2768 		gf_fprintf(out, "<segmentInfo ");
2769 		gf_fprintf(out, "time=\""LLU"\" ", sctx->time);
2770 		gf_fprintf(out, "dur=\""LLU"\" ", sctx->dur);
2771 		gf_fprintf(out, "seg_num=\"%d\" ", sctx->seg_num);
2772 		if (sctx->filename) gf_fprintf(out, "file=\"%s\" ", sctx->filename);
2773 		if (sctx->filepath) gf_fprintf(out, "path=\"%s\" ", sctx->filepath);
2774 		if (sctx->file_size) {
2775 			gf_fprintf(out, "size=\"%d\" ", sctx->file_size);
2776 			if (sctx->file_offset) gf_fprintf(out, "offset=\""LLU"\" ", sctx->file_offset);
2777 		}
2778 		if (sctx->index_size) {
2779 			gf_fprintf(out, "idx_size=\"%d\" ", sctx->index_size);
2780 			if (sctx->index_offset) gf_fprintf(out, "idx_offset=\""LLU"\" ", sctx->index_offset);
2781 		}
2782 		gf_fprintf(out, "/>");
2783 		gf_mpd_lf(out, indent);
2784 	}
2785 
2786 	gf_mpd_nl(out, indent);
2787 	gf_fprintf(out, "</gpac:segments>");
2788 	gf_mpd_lf(out, indent);
2789 }
2790 
gf_mpd_print_representation(GF_MPD_Representation const * const rep,FILE * out,Bool write_context,s32 indent)2791 static void gf_mpd_print_representation(GF_MPD_Representation const * const rep, FILE *out, Bool write_context, s32 indent)
2792 {
2793 	u32 i;
2794 
2795 	gf_mpd_nl(out, indent);
2796 	gf_fprintf(out, "<Representation");
2797 	if (rep->id) gf_fprintf(out, " id=\"%s\"", rep->id);
2798 
2799 /*	if (!gf_list_count(rep->base_URLs) && !rep->segment_base && !rep->segment_template && !rep->segment_list && !gf_list_count(rep->sub_representations)) {
2800 		can_close = 1;
2801 	}
2802 */
2803 
2804 	gf_mpd_print_common_attributes(out, (GF_MPD_CommonAttributes*)rep);
2805 
2806 	if (rep->bandwidth) gf_fprintf(out, " bandwidth=\"%d\"", rep->bandwidth);
2807 	if (rep->quality_ranking) gf_fprintf(out, " qualityRanking=\"%d\"", rep->quality_ranking);
2808 	if (rep->dependency_id) gf_fprintf(out, " dependencyId=\"%s\"", rep->dependency_id);
2809 	if (rep->media_stream_structure_id) gf_fprintf(out, " mediaStreamStructureId=\"%s\"", rep->media_stream_structure_id);
2810 
2811 
2812 	gf_fprintf(out, ">");
2813 	gf_mpd_lf(out, indent);
2814 
2815 	if (write_context) {
2816 		if (rep->dasher_ctx) {
2817 			gf_mpd_print_dasher_context(out, rep->dasher_ctx, indent+1);
2818 		}
2819 		if (rep->state_seg_list) {
2820 			gf_mpd_print_dasher_segments(out, rep->state_seg_list, indent+1);
2821 		}
2822 	}
2823 
2824 	if (rep->other_descriptors){
2825 		GF_MPD_other_descriptors *rsld;
2826 		i=0;
2827 		while ( (rsld = (GF_MPD_other_descriptors*) gf_list_enum(rep->other_descriptors, &i))) {
2828 			gf_mpd_nl(out, indent+1);
2829 			gf_fprintf(out, "%s",rsld->xml_desc);
2830 			gf_mpd_lf(out, indent);
2831 		}
2832 	}
2833 
2834 	gf_mpd_print_common_children(out, (GF_MPD_CommonAttributes*)rep, indent+1);
2835 
2836 	gf_mpd_print_base_urls(out, rep->base_URLs, indent+1);
2837 	if (rep->segment_base) {
2838 		gf_mpd_print_segment_base(out, rep->segment_base, indent+1);
2839 	}
2840 	if (rep->segment_list) {
2841 		gf_mpd_print_segment_list(out, rep->segment_list, indent+1);
2842 	}
2843 	if (rep->segment_template) {
2844 		gf_mpd_print_segment_template(out, rep->segment_template, indent+1);
2845 	}
2846 	/*TODO
2847 				e = gf_mpd_parse_subrepresentation(rep->sub_representations, child);
2848 				if (e) return e;
2849 	*/
2850 
2851 
2852 	gf_mpd_nl(out, indent);
2853 	gf_fprintf(out, "</Representation>");
2854 	gf_mpd_lf(out, indent);
2855 }
2856 
gf_mpd_print_adaptation_set(GF_MPD_AdaptationSet * as,FILE * out,Bool write_context,s32 indent)2857 static void gf_mpd_print_adaptation_set(GF_MPD_AdaptationSet *as, FILE *out, Bool write_context, s32 indent)
2858 {
2859 	u32 i;
2860 	GF_MPD_Representation *rep;
2861 	GF_MPD_other_descriptors *o_desc;
2862 
2863 	gf_mpd_nl(out, indent);
2864 	gf_fprintf(out, "<AdaptationSet");
2865 
2866 	if (as->id) gf_fprintf(out, " id=\"%d\"", as->id);
2867 	if (as->xlink_href) {
2868 		gf_fprintf(out, " xlink:href=\"%s\"", as->xlink_href);
2869 		if (as->xlink_actuate_on_load)
2870 			gf_fprintf(out, " actuate=\"onLoad\"");
2871 	}
2872 	if (as->segment_alignment) gf_fprintf(out, " segmentAlignment=\"true\"");
2873 	if (as->group !=  (u32) -1) gf_fprintf(out, " group=\"%d\"", as->group);
2874 	if (as->min_bandwidth) gf_fprintf(out, " minBandwidth=\"%d\"", as->min_bandwidth);
2875 	if (as->max_bandwidth) gf_fprintf(out, " maxBandwidth=\"%d\"", as->max_bandwidth);
2876 	if (as->min_width) gf_fprintf(out, " minWidth=\"%d\"", as->min_width);
2877 	if (as->max_width) gf_fprintf(out, " maxWidth=\"%d\"", as->max_width);
2878 	if (as->min_height) gf_fprintf(out, " minHeight=\"%d\"", as->min_height);
2879 	if (as->max_height) gf_fprintf(out, " maxHeight=\"%d\"", as->max_height);
2880 	if ((as->min_framerate.num != 0) && (as->min_framerate.den != 0)) {
2881 		if (as->min_framerate.den==1)
2882 			gf_fprintf(out, " minFrameRate=\"%d\"", as->min_framerate.num);
2883 		else
2884 			gf_fprintf(out, " minFrameRate=\"%d/%d\"", as->min_framerate.num, as->min_framerate.den);
2885 	}
2886 	if ((as->max_framerate.num != 0) && (as->max_framerate.den != 0)) {
2887 		if (as->max_framerate.den==1)
2888 			gf_fprintf(out, " maxFrameRate=\"%d\"", as->max_framerate.num);
2889 		else
2890 			gf_fprintf(out, " maxFrameRate=\"%d/%d\"", as->max_framerate.num, as->max_framerate.den);
2891 	}
2892 	if (as->par && (as->par->num != 0) && (as->par->den != 0))
2893 		gf_fprintf(out, " par=\"%d:%d\"", as->par->num, as->par->den);
2894 	if (as->lang) gf_fprintf(out, " lang=\"%s\"", as->lang);
2895 	if (as->bitstream_switching) gf_fprintf(out, " bitstreamSwitching=\"true\"");
2896 
2897 	gf_mpd_print_common_attributes(out, (GF_MPD_CommonAttributes*)as);
2898 	//backward compatibilty with old arch
2899 	if (as->subsegment_alignment) gf_fprintf(out, " subsegmentAlignment=\"true\"");
2900 	if (as->subsegment_starts_with_sap) gf_fprintf(out, " subsegmentStartsWithSAP=\"%d\"", as->subsegment_starts_with_sap);
2901 
2902 	gf_fprintf(out, ">");
2903 	gf_mpd_lf(out, indent);
2904 
2905 	i=0;
2906 	while ( (o_desc = (GF_MPD_other_descriptors*) gf_list_enum(as->other_descriptors, &i))) {
2907 		gf_mpd_nl(out, indent+1);
2908 		//do not use gf_xml_dump_string, this is already an XML escaped element
2909 		gf_fprintf(out, "%s\n", o_desc->xml_desc);
2910 		gf_mpd_lf(out, indent);
2911 	}
2912 
2913 	gf_mpd_print_common_children(out, (GF_MPD_CommonAttributes*)as, indent+1);
2914 
2915 	gf_mpd_print_base_urls(out, as->base_URLs, indent+1);
2916 
2917 	gf_mpd_print_descriptors(out, as->accessibility, "Accessibility", indent+1);
2918 	gf_mpd_print_descriptors(out, as->role, "Role", indent+1);
2919 	gf_mpd_print_descriptors(out, as->rating, "Rating", indent+1);
2920 	gf_mpd_print_descriptors(out, as->viewpoint, "Viewpoint", indent+1);
2921 	gf_mpd_print_content_component(out, as->content_component, indent+1);
2922 
2923 	if (as->segment_base) {
2924 		gf_mpd_print_segment_base(out, as->segment_base, indent+1);
2925 	}
2926 	if (as->segment_list) {
2927 		gf_mpd_print_segment_list(out, as->segment_list, indent+1);
2928 	}
2929 	if (as->segment_template) {
2930 		gf_mpd_print_segment_template(out, as->segment_template, indent+1);
2931 	}
2932 
2933 
2934 	i=0;
2935 	while ((rep = (GF_MPD_Representation *)gf_list_enum(as->representations, &i))) {
2936 		gf_mpd_print_representation(rep, out, write_context, indent+1);
2937 	}
2938 	gf_mpd_nl(out, indent);
2939 	gf_fprintf(out, "</AdaptationSet>");
2940 	gf_mpd_lf(out, indent);
2941 
2942 
2943 }
2944 
gf_mpd_print_period(GF_MPD_Period const * const period,Bool is_dynamic,FILE * out,Bool write_context,s32 indent)2945 static void gf_mpd_print_period(GF_MPD_Period const * const period, Bool is_dynamic, FILE *out, Bool write_context, s32 indent)
2946 {
2947 	GF_MPD_AdaptationSet *as;
2948 	GF_MPD_other_descriptors *o_desc;
2949 	u32 i;
2950 	gf_mpd_nl(out, indent);
2951 	gf_fprintf(out, "<Period");
2952 	if (period->xlink_href) {
2953 		gf_fprintf(out, " xlink:href=\"%s\"", period->xlink_href);
2954 		if (period->xlink_actuate_on_load)
2955 			gf_fprintf(out, " actuate=\"onLoad\"");
2956 	}
2957 	if (period->ID)
2958 		gf_fprintf(out, " id=\"%s\"", period->ID);
2959 	if (is_dynamic || period->start)
2960 		gf_mpd_print_duration(out, "start", period->start, GF_TRUE);
2961 	if (period->duration)
2962 		gf_mpd_print_duration(out, "duration", period->duration, GF_TRUE);
2963 	if (period->bitstream_switching)
2964 		gf_fprintf(out, " bitstreamSwitching=\"true\"");
2965 
2966 	gf_fprintf(out, ">");
2967 	gf_mpd_lf(out, indent);
2968 
2969 	i=0;
2970 	while ( (o_desc = (GF_MPD_other_descriptors*) gf_list_enum(period->other_descriptors, &i))) {
2971 		gf_mpd_nl(out, indent+1);
2972 		//do not use gf_xml_dump_stringn this is already an XML escape element
2973 		gf_fprintf(out, "%s", o_desc->xml_desc);
2974 		gf_mpd_lf(out, indent);
2975 	}
2976 
2977 
2978 	gf_mpd_print_base_urls(out, period->base_URLs, indent+1);
2979 
2980 	if (period->segment_base) {
2981 		gf_mpd_print_segment_base(out, period->segment_base, indent+1);
2982 	}
2983 	if (period->segment_list) {
2984 		gf_mpd_print_segment_list(out, period->segment_list, indent+1);
2985 	}
2986 	if (period->segment_template) {
2987 		gf_mpd_print_segment_template(out, period->segment_template, indent+1);
2988 	}
2989 
2990 	i=0;
2991 	while ( (as = (GF_MPD_AdaptationSet *) gf_list_enum(period->adaptation_sets, &i))) {
2992 		gf_mpd_print_adaptation_set(as, out, write_context, indent+1);
2993 	}
2994 	gf_mpd_nl(out, indent);
2995 	gf_fprintf(out, "</Period>");
2996 	gf_mpd_lf(out, indent);
2997 }
2998 
mpd_write_generation_comment(GF_MPD const * const mpd,FILE * out)2999 static GF_Err mpd_write_generation_comment(GF_MPD const * const mpd, FILE *out)
3000 {
3001 	u64 time_ms;
3002 	time_t gtime;
3003 	struct tm *t;
3004 	u32 sec;
3005 
3006 	time_ms = mpd->publishTime;
3007 	sec = (u32)(time_ms / 1000);
3008 	time_ms -= ((u64)sec) * 1000;
3009 	assert(time_ms<1000);
3010 
3011 	gtime = sec;
3012 	t = gf_gmtime(&gtime);
3013 	if (! gf_sys_is_test_mode() ){
3014 		gf_fprintf(out, "<!-- MPD file Generated with GPAC version %s at %d-%02d-%02dT%02d:%02d:%02d.%03dZ -->\n", gf_gpac_version(), 1900 + t->tm_year, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, (u32)time_ms);
3015 	}
3016 	if (!mpd->write_context) {
3017 		GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[MPD] Generating MPD at time %d-%02d-%02dT%02d:%02d:%02d.%03dZ\n", 1900 + t->tm_year, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, (u32)time_ms));
3018 	}
3019 	return GF_OK;
3020 }
3021 
gf_mpd_write_m3u8_playlist_tags_entry(FILE * out,const GF_MPD_Representation * rep,char * m3u8_name,const char * codec_ext,const char * g_type,const char * g_id_pref,u32 g_as_idx,const char * g2_type,const char * g2_id_pref,u32 g2_as_idx)3022 static void gf_mpd_write_m3u8_playlist_tags_entry(FILE *out, const GF_MPD_Representation *rep, char *m3u8_name, const char *codec_ext, const char *g_type, const char *g_id_pref, u32 g_as_idx, const char *g2_type, const char *g2_id_pref, u32 g2_as_idx)
3023 {
3024 	gf_fprintf(out, "#EXT-X-STREAM-INF:BANDWIDTH=%d,CODECS=\"%s", rep->bandwidth, rep->codecs);
3025 	if (codec_ext)
3026 		gf_fprintf(out, ",%s", codec_ext);
3027 	gf_fprintf(out, "\"");
3028 
3029 	if (rep->width && rep->height)
3030 		gf_fprintf(out, ",RESOLUTION=%dx%d", rep->width, rep->height);
3031 	if (rep->fps)
3032 		gf_fprintf(out,",FRAME-RATE=\"%.03g\"", rep->fps);
3033 
3034 	if (g_type && g_id_pref)
3035 		gf_fprintf(out,",%s=\"%s%d\"", g_type, g_id_pref, g_as_idx);
3036 	if (g2_type && g2_id_pref)
3037 		gf_fprintf(out,",%s=\"%s%d\"", g2_type, g2_id_pref, g2_as_idx);
3038 	gf_fprintf(out,"\n");
3039 
3040 	gf_fprintf(out, "%s\n",m3u8_name);
3041 
3042 }
3043 
gf_mpd_write_m3u8_playlist_tags(const GF_MPD_AdaptationSet * as,u32 as_idx,const GF_MPD_Representation * rep,FILE * out,char * m3u8_name,GF_MPD_Period * period,u32 nb_audio,u32 nb_subs,u32 nb_cc)3044 static void gf_mpd_write_m3u8_playlist_tags(const GF_MPD_AdaptationSet *as, u32 as_idx, const GF_MPD_Representation *rep, FILE *out, char *m3u8_name, GF_MPD_Period *period, u32 nb_audio, u32 nb_subs, u32 nb_cc)
3045 {
3046 	u32 i, j;
3047 	GF_MPD_AdaptationSet *r_as;
3048 	GF_MPD_Representation *r_rep;
3049 
3050 	if (!rep->mime_type) return;
3051 
3052 	//no period, this is a component description
3053 	if (!period) {
3054 		const char *g_type = NULL;
3055 		const char *g_id = NULL;
3056 
3057 		if (rep->streamtype==GF_STREAM_AUDIO) {
3058 			g_type = "AUDIO";
3059 			g_id = "audio";
3060 		}
3061 		else if (rep->streamtype==GF_STREAM_TEXT) {
3062 			g_type = "SUBTITLE";
3063 			g_id = "subs";
3064 		}
3065 		if (!g_type || !g_id)
3066 			return;
3067 
3068 		gf_fprintf(out, "#EXT-X-MEDIA:TYPE=%s,GROUP-ID=\"%s%d\",NAME=\"%s\",LANGUAGE=\"%s\",AUTOSELECT=YES,URI=\"%s\"", g_type, g_id, as_idx, rep->id, as->lang, m3u8_name);
3069 		if (rep->nb_chan)
3070 			gf_fprintf(out,",CHANNELS=\"%d\"", rep->nb_chan);
3071 		return;
3072 	}
3073 
3074 	//no other streams, directly write the entry
3075 	if (!nb_audio && !nb_subs && !nb_cc) {
3076 		gf_mpd_write_m3u8_playlist_tags_entry(out, rep, m3u8_name, NULL, NULL, NULL, 0, NULL, NULL, 0);
3077 		return;
3078 	}
3079 	//otherwise browse all adaptation sets
3080 	i=0;
3081 	while ( (r_as = (GF_MPD_AdaptationSet *) gf_list_enum(period->adaptation_sets, &i))) {
3082 		u32 g_as_idx;
3083 		Bool is_audio = GF_FALSE;
3084 		GF_MPD_AdaptationSet *r2_as;
3085 		GF_MPD_Representation *r2_rep;
3086 		const char *g_type = NULL;
3087 		const char *g_id = NULL;
3088 		const char *g_codec = NULL;
3089 		if (as==r_as) continue;
3090 
3091 		g_as_idx = (r_as->group>0) ? r_as->group : i;
3092 		r_rep = (GF_MPD_Representation *) gf_list_get(r_as->representations, 0);
3093 
3094 		//if audio streams are present, the first pass gather audio and we will need a second loop to get subs
3095 		if (nb_audio) {
3096 			if (r_rep->streamtype==GF_STREAM_AUDIO) {
3097 				g_type = "AUDIO";
3098 				g_id = "audio";
3099 				g_codec = r_rep->codecs;
3100 			}
3101 			is_audio = GF_TRUE;
3102 		} else {
3103 			if (r_rep->streamtype==GF_STREAM_TEXT) {
3104 				g_type = "SUBTITLE";
3105 				g_id = "subs";
3106 			}
3107 		}
3108 		//not our type
3109 		if (!g_type) continue;
3110 		//no audio, or no subs
3111 		if (!is_audio || !nb_subs) {
3112 			gf_mpd_write_m3u8_playlist_tags_entry(out, rep, m3u8_name, g_codec, g_type, g_id, g_as_idx, NULL, NULL, 0);
3113 			continue;
3114 		}
3115 		//audio and subs, we need a second loop on audio to get all subs
3116 		j=0;
3117 		while ( (r2_as = (GF_MPD_AdaptationSet *) gf_list_enum(period->adaptation_sets, &j))) {
3118 			u32 g2_as_idx;
3119 			const char *g2_type = NULL;
3120 			const char *g2_id = NULL;
3121 			if (r_as==r2_as) continue;
3122 			g2_as_idx = (r2_as->group>0) ? r2_as->group : j;
3123 
3124 			r2_rep = (GF_MPD_Representation *) gf_list_get(r2_as->representations, 0);
3125 			if (r2_rep->streamtype==GF_STREAM_TEXT) {
3126 				g2_type = "SUBTITLE";
3127 				g2_id = "subs";
3128 			}
3129 			if (!g2_type) continue;
3130 
3131 			gf_mpd_write_m3u8_playlist_tags_entry(out, rep, m3u8_name, g_codec, g_type, g_id, g_as_idx, g2_type, g2_id, g2_as_idx);
3132 		}
3133 	}
3134 }
3135 
gf_mpd_m3u8_get_init_seg(const GF_MPD_Period * period,const GF_MPD_AdaptationSet * as,const GF_MPD_Representation * rep)3136 static const char *gf_mpd_m3u8_get_init_seg(const GF_MPD_Period *period, const GF_MPD_AdaptationSet *as, const GF_MPD_Representation *rep)
3137 {
3138 	const char *url = NULL;
3139 	if (rep->segment_list) url = rep->segment_list->initialization_segment->sourceURL;
3140 	else if (rep->segment_template && rep->segment_template->initialization) url = rep->segment_template->initialization;
3141 	else if (rep->segment_template && rep->segment_template->initialization_segment) url = rep->segment_template->initialization_segment->sourceURL;
3142 
3143 	if (as->segment_list && as->segment_list->initialization_segment) url = as->segment_list->initialization_segment->sourceURL;
3144 	else if (as->segment_template && as->segment_template->initialization) url = as->segment_template->initialization;
3145 	else if (as->segment_template && as->segment_template->initialization_segment) url = as->segment_template->initialization_segment->sourceURL;
3146 
3147 	if (period->segment_list && period->segment_list->initialization_segment) url = period->segment_list->initialization_segment->sourceURL;
3148 	else if (period->segment_template && period->segment_template->initialization) url = period->segment_template->initialization;
3149 	else if (period->segment_template && period->segment_template->initialization_segment) url = period->segment_template->initialization_segment->sourceURL;
3150 	return url;
3151 }
3152 
gf_mpd_write_m3u8_playlist(const GF_MPD * mpd,const GF_MPD_Period * period,const GF_MPD_AdaptationSet * as,GF_MPD_Representation * rep,char * m3u8_name,u32 hls_version)3153 static GF_Err gf_mpd_write_m3u8_playlist(const GF_MPD *mpd, const GF_MPD_Period *period, const GF_MPD_AdaptationSet *as, GF_MPD_Representation *rep, char *m3u8_name, u32 hls_version)
3154 {
3155 	u32 i, count;
3156 	GF_DASH_SegmentContext *sctx;
3157 	FILE *out;
3158 	Bool close_file = GF_FALSE;
3159 
3160 	if (!strcmp(m3u8_name, "std")) out = stdout;
3161 	else if (mpd->create_m3u8_files) {
3162 		out = gf_fopen(m3u8_name, "wb");
3163 		if (!out) return GF_IO_ERR;
3164 		close_file = GF_TRUE;
3165 	} else {
3166 		out = gf_file_temp(NULL);
3167 		if (rep->m3u8_var_file) gf_fclose(rep->m3u8_var_file);
3168 		rep->m3u8_var_file = out;
3169 	}
3170 
3171 	count = gf_list_count(rep->state_seg_list);
3172 	sctx = gf_list_get(rep->state_seg_list, 0);
3173 
3174 	gf_fprintf(out,"#EXTM3U\n");
3175 	gf_fprintf(out,"#EXT-X-TARGETDURATION:%d\n",(u32) (rep->dash_dur) );
3176 	gf_fprintf(out,"#EXT-X-VERSION:%d\n", hls_version);
3177 	gf_fprintf(out,"#EXT-X-MEDIA-SEQUENCE:%d\n", sctx->seg_num);
3178 
3179 	if (as->starts_with_sap<SAP_TYPE_3)
3180 		gf_fprintf(out,"#EXT-X-INDEPENDENT-SEGMENTS\n");
3181 
3182 	if (mpd->m3u8_time && rep->timescale_mpd && (mpd->type == GF_MPD_TYPE_DYNAMIC)) {
3183 		u64 seg_ast = mpd->availabilityStartTime;
3184 		seg_ast += (sctx->time * 1000) / rep->timescale_mpd;
3185 		gf_fprintf(out, "#EXT-X-PROGRAM-DATE-TIME:");
3186 		gf_mpd_print_date(out, NULL, seg_ast);
3187 		gf_fprintf(out, "\n");
3188 	}
3189 
3190 	if (sctx->filename) {
3191 		if (rep->hls_single_file_name) {
3192 			gf_fprintf(out,"#EXT-X-MAP:URI=\"%s\"\n", rep->hls_single_file_name);
3193 		}
3194 		for (i=0; i<count; i++) {
3195 			Double dur;
3196 			sctx = gf_list_get(rep->state_seg_list, i);
3197 			assert(sctx->filename);
3198 
3199 			dur = (Double) sctx->dur;
3200 			dur /= rep->timescale;
3201 			gf_fprintf(out,"#EXTINF:%g,\n", dur);
3202 			gf_fprintf(out,"%s\n", sctx->filename);
3203 		}
3204 	} else {
3205 		GF_MPD_BaseURL *base_url=NULL;
3206 		GF_MPD_URL *init=NULL;
3207 
3208 		if (rep->segment_base && rep->segment_base->initialization_segment) init = rep->segment_base->initialization_segment;
3209 		if (as->segment_base && as->segment_base->initialization_segment) init = as->segment_base->initialization_segment;
3210 		if (period->segment_base && period->segment_base->initialization_segment) init = period->segment_base->initialization_segment;
3211 
3212 		if (rep->segment_list && rep->segment_list->initialization_segment) init = rep->segment_list->initialization_segment;
3213 		if (as->segment_list && as->segment_list->initialization_segment) init = as->segment_list->initialization_segment;
3214 		if (period->segment_list && period->segment_list->initialization_segment) init = period->segment_list->initialization_segment;
3215 
3216 		base_url = gf_list_get(rep->base_URLs, 0);
3217 		assert(base_url);
3218 
3219 		if (init) {
3220 			gf_fprintf(out,"#EXT-X-MAP:URI=\"%s\",BYTERANGE=\"%d@"LLU"\"\n", base_url->URL, (u32) (1+init->byte_range->end_range - init->byte_range->start_range), init->byte_range->start_range);
3221 		}
3222 
3223 		for (i=0; i<count; i++) {
3224 			Double dur;
3225 			sctx = gf_list_get(rep->state_seg_list, i);
3226 			assert(!sctx->filename);
3227 			assert(sctx->file_size);
3228 
3229 			dur = (Double) sctx->dur;
3230 			dur /= rep->timescale;
3231 			gf_fprintf(out,"#EXTINF:%g\n", dur);
3232 			gf_fprintf(out,"#EXT-X-BYTERANGE:%d@"LLU"\n", sctx->file_size, sctx->file_offset);
3233 			gf_fprintf(out,"%s\n", base_url->URL);
3234 		}
3235 	}
3236 
3237 	if (mpd->type != GF_MPD_TYPE_DYNAMIC)
3238 		gf_fprintf(out,"\n#EXT-X-ENDLIST\n");
3239 
3240 	if (close_file)
3241 		gf_fclose(out);
3242 
3243 	return GF_OK;
3244 }
3245 
3246 
gf_mpd_write_m3u8_master_playlist(GF_MPD const * const mpd,FILE * out,const char * m3u8_name,GF_MPD_Period * period)3247 GF_Err gf_mpd_write_m3u8_master_playlist(GF_MPD const * const mpd, FILE *out, const char* m3u8_name, GF_MPD_Period *period)
3248 {
3249 	u32 i, j, hls_version;
3250 	u32 var_idx;
3251 	GF_Err e;
3252 	GF_MPD_AdaptationSet *as;
3253 	GF_MPD_Representation *rep;
3254 	Bool use_range = GF_FALSE;
3255 	Bool use_init = GF_FALSE;
3256 	Bool use_ind_segments = GF_TRUE;
3257 	Bool is_fmp4 = GF_FALSE;
3258 	char *szVariantName;
3259 	char *m3u8_name_rad, *sep;
3260 	u32 nb_audio=0;
3261 	u32 nb_cc=0;
3262 	u32 nb_subs=0;
3263 	Bool has_muxed_comp = GF_FALSE;
3264 	Bool has_video = GF_FALSE;
3265 	Bool has_audio = GF_FALSE;
3266 
3267 	if (!m3u8_name || !period) return GF_BAD_PARAM;
3268 
3269 	i=0;
3270 	while ( (as = (GF_MPD_AdaptationSet *) gf_list_enum(period->adaptation_sets, &i))) {
3271 		if (gf_list_count(as->content_protection)) { /*use_crypt = GF_TRUE; */ }
3272 		if (as->starts_with_sap>2) use_ind_segments = GF_FALSE;
3273 
3274 		j=0;
3275 		while ( (rep = (GF_MPD_Representation *) gf_list_enum(as->representations, &j))) {
3276 			GF_DASH_SegmentContext *sctx;
3277 			const char *init_seg;
3278 			if (!rep->state_seg_list || !gf_list_count(rep->state_seg_list) ) {
3279 				GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[M3U8] No segment state in representation, MPD cannot be translated to M3U8\n"));
3280 				return GF_BAD_PARAM;
3281 			}
3282 
3283 			sctx = gf_list_get(rep->state_seg_list, 0);
3284 			if (!sctx->filename) use_range = GF_TRUE;
3285 			if (gf_list_count(rep->content_protection))  { /*use_crypt = GF_TRUE; */ }
3286 
3287 			init_seg = gf_mpd_m3u8_get_init_seg(period, as, rep);
3288 			if (init_seg) use_init = GF_TRUE;
3289 			if (rep->mime_type) {
3290 				if (strstr(rep->mime_type, "mp4")) is_fmp4 = GF_TRUE;
3291 			}
3292 
3293 			if (rep->streamtype==GF_STREAM_AUDIO) nb_audio++;
3294 			else if (rep->streamtype==GF_STREAM_TEXT) nb_subs++;
3295 		}
3296 	}
3297 	//we by default use floating point durations
3298 	hls_version = 3;
3299 	if (use_range) hls_version = 4;
3300 	if (is_fmp4 || use_init) hls_version = 6;
3301 
3302 
3303 	gf_fprintf(out, "#EXTM3U\n");
3304 	gf_fprintf(out, "#EXT-X-VERSION: %d\n", hls_version);
3305 	if (use_ind_segments)
3306 		gf_fprintf(out, "#EXT-X-INDEPENDENT-SEGMENTS\n");
3307 	gf_fprintf(out, "\n");
3308 
3309 	if (!strncmp(m3u8_name, "gfio://", 7)) {
3310 		const char *fpath = gf_fileio_translate_url(m3u8_name);
3311 		if (!fpath) return GF_BAD_PARAM;
3312 		//use basename since this file will be created throught GF_FileIO factory (relative to the original gfio://)
3313 		m3u8_name_rad = gf_strdup(gf_file_basename(fpath));
3314 	} else {
3315 		m3u8_name_rad = gf_strdup(m3u8_name);
3316 	}
3317 	sep = strrchr(m3u8_name_rad, '.');
3318 	if (sep) sep[0] = 0;
3319 	szVariantName = gf_malloc(sizeof(char) * (100 + strlen(m3u8_name_rad)) );
3320 
3321 
3322 	//first pass, generate all subplaylists, and check if we have muxed components, or video or audio
3323 	var_idx = 1;
3324 	i=0;
3325 	while ( (as = (GF_MPD_AdaptationSet *) gf_list_enum(period->adaptation_sets, &i))) {
3326 		if (as->content_component && gf_list_count(as->content_component)>1) {
3327 			has_muxed_comp = GF_TRUE;
3328 		}
3329 		if (as->max_width && as->max_height) {
3330 			has_video = GF_TRUE;
3331 		}
3332 		if (as->samplerate)
3333 			has_audio = GF_TRUE;
3334 
3335 		j=0;
3336 		while ( (rep = (GF_MPD_Representation *) gf_list_enum(as->representations, &j))) {
3337 			char *name = (char *) rep->m3u8_name;
3338 
3339 			if (!rep->state_seg_list || !gf_list_count(rep->state_seg_list) ) {
3340 				GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[M3U8] No segment state in representation, MPD cannot be translated to M3U8, ignoring representation\n"));
3341 				continue;
3342 			}
3343 			if (rep->mime_type) {
3344 				if (!strncmp(rep->mime_type, "video/", 6)) has_video = GF_TRUE;
3345 				else if (!strncmp(rep->mime_type, "audio/", 6)) has_audio = GF_TRUE;
3346 			}
3347 
3348 			if (!name) {
3349 				sprintf(szVariantName, "%s_%d.m3u8",m3u8_name_rad, var_idx);
3350 				if (rep->m3u8_var_name) gf_free(rep->m3u8_var_name);
3351 				rep->m3u8_var_name = gf_strdup(szVariantName);
3352 				name = gf_file_basename(rep->m3u8_var_name);
3353 			}
3354 			var_idx++;
3355 
3356 
3357 			e = gf_mpd_write_m3u8_playlist(mpd, period, as, rep, name, hls_version);
3358 			if (e) {
3359 				GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[M3U8] IO error while opening m3u8 files\n"));
3360 				return GF_IO_ERR;
3361 			}
3362 		}
3363 	}
3364 
3365 	//no muxed comp, no video, the audio is the main media we will list, force nb_audio=0 for gf_mpd_write_m3u8_playlist_tags
3366 	if (!has_video && !has_muxed_comp)
3367 		nb_audio = 0;
3368 
3369 	//second pass, generate master playlists with the right groups
3370 	i=0;
3371 	while ( (as = (GF_MPD_AdaptationSet *) gf_list_enum(period->adaptation_sets, &i))) {
3372 		Bool is_video = GF_FALSE;
3373 		Bool is_audio = GF_FALSE;
3374 		Bool is_muxed_comp = GF_FALSE;
3375 		Bool is_primary = GF_TRUE;
3376 		if (as->content_component && gf_list_count(as->content_component)>1) {
3377 			is_muxed_comp = GF_TRUE;
3378 		}
3379 		if (as->max_width && as->max_height) {
3380 			is_video = GF_TRUE;
3381 		}
3382 		if (as->samplerate) {
3383 			is_audio = GF_TRUE;
3384 		}
3385 
3386 		//check if we have audio or video
3387 		j=0;
3388 		while ( (rep = (GF_MPD_Representation *) gf_list_enum(as->representations, &j))) {
3389 			if (rep->mime_type) {
3390 				if (!strncmp(rep->mime_type, "video/", 6)) is_video = GF_TRUE;
3391 				else if (!strncmp(rep->mime_type, "audio/", 6)) is_audio = GF_TRUE;
3392 				break;
3393 			}
3394 		}
3395 		//figure out if this is the primary media or an associated media with the content
3396 		if (has_muxed_comp) {
3397 			if (!is_muxed_comp) is_primary = GF_FALSE;
3398 		} else if (has_video) {
3399 			if (!is_video) is_primary = GF_FALSE;
3400 		} else if (has_audio) {
3401 			if (!is_audio) is_primary = GF_FALSE;
3402 		}
3403 
3404 		j=0;
3405 		while ( (rep = (GF_MPD_Representation *) gf_list_enum(as->representations, &j))) {
3406 			char *name = (char *) rep->m3u8_name;
3407 			if (!rep->state_seg_list || !gf_list_count(rep->state_seg_list) ) {
3408 				continue;
3409 			}
3410 			if (rep->m3u8_var_name) {
3411 				name = gf_file_basename(rep->m3u8_var_name);
3412 			}
3413 
3414 			gf_mpd_write_m3u8_playlist_tags(as, i, rep, out, name, is_primary ? period : NULL, nb_audio, nb_subs, nb_cc);
3415 			gf_fprintf(out, "\n");
3416 		}
3417 	}
3418 
3419 	if (mpd->type != GF_MPD_TYPE_DYNAMIC)
3420 		gf_fprintf(out,"#EXT-X-ENDLIST\n");
3421 
3422 	gf_free(m3u8_name_rad);
3423 	gf_free(szVariantName);
3424 	return GF_OK;
3425 }
3426 
3427 
3428 #if 0 //unused
3429 GF_Err gf_mpd_write_m3u8_file(GF_MPD *mpd, const char *file_name, GF_MPD_Period *period)
3430 {
3431 	GF_Err e;
3432 	FILE *out;
3433 	if (!strcmp(file_name, "std")) out = stdout;
3434 	else {
3435 		out = gf_fopen(file_name, "wb");
3436 		if (!out) return GF_IO_ERR;
3437 
3438 		mpd->create_m3u8_files = GF_TRUE;
3439 	}
3440 
3441 	e = gf_mpd_write_m3u8_master_playlist(mpd, out, file_name, period);
3442 	gf_fclose(out);
3443 	mpd->create_m3u8_files = GF_FALSE;
3444 	return e;
3445 }
3446 #endif
3447 
gf_mpd_write(GF_MPD const * const mpd,FILE * out,Bool compact)3448 GF_Err gf_mpd_write(GF_MPD const * const mpd, FILE *out, Bool compact)
3449 {
3450 	u32 i, count;
3451 	s32 indent = compact ? GF_INT_MIN : 0;
3452 	GF_MPD_ProgramInfo *info;
3453 	char *text;
3454 
3455 	if (!mpd->xml_namespace) {
3456 		GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[MPD] No namespace found while writing. Setting to default.\n"));
3457 	}
3458 
3459 	gf_fprintf(out, "<?xml version=\"1.0\"?>");
3460 	gf_mpd_lf(out, indent);
3461 	mpd_write_generation_comment(mpd, out);
3462 	gf_fprintf(out, "<MPD xmlns=\"%s\"", (mpd->xml_namespace ? mpd->xml_namespace : "urn:mpeg:dash:schema:mpd:2011"));
3463 
3464 	if (mpd->write_context) {
3465 	 	gf_fprintf(out, " xmlns:gpac=\"urn:gpac:filters:dasher:2018\"" );
3466 	}
3467 
3468 	if (mpd->ID)
3469 		gf_fprintf(out, " id=\"%s\"", mpd->ID);
3470 
3471 	if (mpd->min_buffer_time)
3472 		gf_mpd_print_duration(out, "minBufferTime", mpd->min_buffer_time, GF_FALSE);
3473 
3474 	gf_fprintf(out," type=\"%s\"",(mpd->type == GF_MPD_TYPE_STATIC ? "static" : "dynamic"));
3475 
3476 
3477 	if (mpd->type == GF_MPD_TYPE_DYNAMIC)
3478 		gf_mpd_print_date(out, "availabilityStartTime", mpd->availabilityStartTime);
3479 	if (mpd->availabilityEndTime)
3480 		gf_mpd_print_date(out, "availabilityEndTime", mpd->availabilityEndTime);
3481 	if (mpd->publishTime && mpd->type != GF_MPD_TYPE_STATIC)
3482 		gf_mpd_print_date(out, "publishTime", mpd->publishTime);
3483 	if (mpd->media_presentation_duration)
3484 		gf_mpd_print_duration(out, "mediaPresentationDuration", mpd->media_presentation_duration, GF_TRUE);
3485 	if (mpd->minimum_update_period)
3486 		gf_mpd_print_duration(out, "minimumUpdatePeriod", mpd->minimum_update_period, GF_TRUE);
3487 	if ((s32) mpd->time_shift_buffer_depth > 0)
3488 		gf_mpd_print_duration(out, "timeShiftBufferDepth", mpd->time_shift_buffer_depth, GF_TRUE);
3489 	if (mpd->suggested_presentation_delay)
3490 		gf_mpd_print_duration(out, "suggestedPresentationDelay", mpd->suggested_presentation_delay, GF_TRUE);
3491 	if (mpd->max_segment_duration)
3492 		gf_mpd_print_duration(out, "maxSegmentDuration", mpd->max_segment_duration, GF_TRUE);
3493 	if (mpd->max_subsegment_duration)
3494 		gf_mpd_print_duration(out, "maxSubsegmentDuration", mpd->max_subsegment_duration, GF_TRUE);
3495 
3496 	if (mpd->profiles) {
3497 		gf_xml_dump_string(out, " profiles=\"", mpd->profiles, "\"");
3498 	}
3499 
3500 	if (mpd->attributes) gf_mpd_extensible_print_attr(out, (GF_MPD_ExtensibleVirtual*)mpd);
3501 
3502 	if (mpd->write_context) {
3503 		if (mpd->gpac_init_ntp_ms)
3504 			gf_fprintf(out," gpac:init_gen_time=\""LLU"\"", mpd->gpac_init_ntp_ms);
3505 		if (mpd->gpac_next_ntp_ms)
3506 			gf_fprintf(out," gpac:next_gen_time=\""LLU"\"", mpd->gpac_next_ntp_ms);
3507 		if (mpd->gpac_mpd_time)
3508 			gf_fprintf(out," gpac:mpd_time=\""LLU"\"", mpd->gpac_mpd_time);
3509 	}
3510 
3511 	gf_fprintf(out, ">");
3512 	gf_mpd_lf(out, indent);
3513 
3514 	if (mpd->children) {
3515 		gf_mpd_extensible_print_nodes(out, (GF_MPD_ExtensibleVirtual*)mpd, indent+1);
3516 	}
3517 
3518 	i=0;
3519 	while ((info = (GF_MPD_ProgramInfo *)gf_list_enum(mpd->program_infos, &i))) {
3520 		gf_mpd_nl(out, indent+1);
3521 		gf_fprintf(out, "<ProgramInformation");
3522 		if (info->lang) {
3523 			gf_fprintf(out, " lang=\"%s\"", info->lang);
3524 		}
3525 		if (info->more_info_url) {
3526 			gf_xml_dump_string(out, " moreInformationURL=\"", info->more_info_url, "\"");
3527 		}
3528 		gf_fprintf(out, ">");
3529 		gf_mpd_lf(out, indent);
3530 		if (info->title) {
3531 			gf_mpd_nl(out, indent+2);
3532 			gf_xml_dump_string(out, "<Title>", info->title, "</Title>");
3533 			gf_mpd_lf(out, indent);
3534 		}
3535 		if (info->source) {
3536 			gf_mpd_nl(out, indent+2);
3537 			gf_xml_dump_string(out, "<Source>", info->source, "</Source>");
3538 			gf_mpd_lf(out, indent);
3539 		}
3540 		if (info->copyright) {
3541 			gf_mpd_nl(out, indent+2);
3542 			gf_xml_dump_string(out, "<Copyright>", info->copyright, "</Copyright>");
3543 			gf_mpd_lf(out, indent);
3544 		}
3545 		gf_mpd_nl(out, indent+1);
3546 		gf_fprintf(out, "</ProgramInformation>");
3547 		gf_mpd_lf(out, indent);
3548 	}
3549 
3550 	gf_mpd_print_base_urls(out, mpd->base_URLs, indent+1);
3551 
3552 	gf_mpd_lf(out, indent);
3553 
3554 	i=0;
3555 	while ((text = (char *)gf_list_enum(mpd->locations, &i))) {
3556 		gf_mpd_nl(out, indent+1);
3557 		gf_xml_dump_string(out, "<Location>", text, "</Location>");
3558 		gf_mpd_lf(out, indent);
3559 	}
3560 
3561 	/*
3562 		i=0;
3563 		while ((text = (char *)gf_list_enum(mpd->metrics, &i))) {
3564 
3565 		}
3566 	*/
3567 
3568 	count = gf_list_count(mpd->periods);
3569 	for (i=0; i<count; i++) {
3570 		Bool is_dynamic;
3571 		GF_MPD_Period *period = (GF_MPD_Period *)gf_list_get(mpd->periods, i);
3572 		is_dynamic = (mpd->type==GF_MPD_TYPE_DYNAMIC) ? GF_TRUE : GF_FALSE;
3573 		//hack for backward compat with old arch, forces print period@start if 0
3574 		if (!i && count>1 && mpd->was_dynamic) is_dynamic = GF_TRUE;
3575 		gf_mpd_print_period(period, is_dynamic, out, mpd->write_context, indent+1);
3576 	}
3577 
3578 	gf_fprintf(out, "</MPD>");
3579 
3580 	return GF_OK;
3581 }
3582 
3583 GF_EXPORT
gf_mpd_write_file(GF_MPD const * const mpd,const char * file_name)3584 GF_Err gf_mpd_write_file(GF_MPD const * const mpd, const char *file_name)
3585 {
3586 	GF_Err e;
3587 	FILE *out;
3588 	if (!strcmp(file_name, "std")) out = stdout;
3589 	else {
3590 		out = gf_fopen(file_name, "wb");
3591 		if (!out) return GF_IO_ERR;
3592 	}
3593 
3594 	e = gf_mpd_write(mpd, out, GF_FALSE);
3595 	gf_fclose(out);
3596 	return e;
3597 }
3598 
3599 
3600 GF_EXPORT
gf_mpd_get_base_url_count(GF_MPD * mpd,GF_MPD_Period * period,GF_MPD_AdaptationSet * set,GF_MPD_Representation * rep)3601 u32 gf_mpd_get_base_url_count(GF_MPD *mpd, GF_MPD_Period *period, GF_MPD_AdaptationSet *set, GF_MPD_Representation *rep)
3602 {
3603 	u32 base_url_count, i;
3604 	base_url_count = 1;
3605 	i = gf_list_count(mpd->base_URLs);
3606 	if (i>1) base_url_count *= i;
3607 	i = gf_list_count(period->base_URLs);
3608 	if (i>1) base_url_count *= i;
3609 	i = gf_list_count(set->base_URLs);
3610 	if (i>1) base_url_count *= i;
3611 	i = gf_list_count(rep->base_URLs);
3612 	if (i>1) base_url_count *= i;
3613 
3614 	return base_url_count;
3615 }
3616 
gf_mpd_get_base_url(GF_List * baseURLs,char * parent_url,u32 * base_url_index)3617 static char *gf_mpd_get_base_url(GF_List *baseURLs, char *parent_url, u32 *base_url_index)
3618 {
3619 	GF_MPD_BaseURL *url_child;
3620 	u32 idx = 0;
3621 	u32 nb_base = gf_list_count(baseURLs);
3622 	if (nb_base>1) {
3623 		u32 nb_bits = gf_get_bit_size(nb_base-1);
3624 		u32 mask=0;
3625 		u32 i=0;
3626 		while (1) {
3627 			mask |= 1;
3628 			i++;
3629 			if (i>=nb_bits) break;
3630 			mask <<= 1;
3631 		}
3632 		idx = (*base_url_index) & mask;
3633 		(*base_url_index) = (*base_url_index) >> nb_bits;
3634 	} else {
3635 		idx = 0;
3636 	}
3637 
3638 	url_child = gf_list_get(baseURLs, idx);
3639 	if (url_child) {
3640 		char *t_url = gf_url_concatenate(parent_url, url_child->redirection ? url_child->redirection : url_child->URL);
3641 		gf_free(parent_url);
3642 		parent_url = t_url;
3643 	}
3644 	return parent_url;
3645 }
3646 
3647 GF_EXPORT
gf_mpd_resolve_url(GF_MPD * mpd,GF_MPD_Representation * rep,GF_MPD_AdaptationSet * set,GF_MPD_Period * period,const char * mpd_url,u32 base_url_index,GF_MPD_URLResolveType resolve_type,u32 item_index,u32 nb_segments_removed,char ** out_url,u64 * out_range_start,u64 * out_range_end,u64 * segment_duration_in_ms,Bool * is_in_base_url,char ** out_key_url,bin128 * out_key_iv)3648 GF_Err gf_mpd_resolve_url(GF_MPD *mpd, GF_MPD_Representation *rep, GF_MPD_AdaptationSet *set, GF_MPD_Period *period, const char *mpd_url, u32 base_url_index, GF_MPD_URLResolveType resolve_type, u32 item_index, u32 nb_segments_removed, char **out_url, u64 *out_range_start, u64 *out_range_end, u64 *segment_duration_in_ms, Bool *is_in_base_url, char **out_key_url, bin128 *out_key_iv)
3649 {
3650 	GF_MPD_SegmentTimeline *timeline = NULL;
3651 	u32 start_number = 1;
3652 	u32 timescale=0;
3653 	u64 duration=0;
3654 	char *url;
3655 	char *url_to_solve, *solved_template, *first_sep, *media_url;
3656 	char *init_template, *index_template;
3657 
3658 	if (!out_range_start || !out_range_end || !out_url || !mpd_url || !segment_duration_in_ms)
3659 		return GF_BAD_PARAM;
3660 	*out_range_start = *out_range_end = 0;
3661 	*out_url = NULL;
3662 	if (out_key_url) *out_key_url = NULL;
3663 	/*resolve base URLs from document base (download location) to representation (media)*/
3664 	url = gf_strdup(mpd_url);
3665 
3666 	url = gf_mpd_get_base_url(mpd->base_URLs, url, &base_url_index);
3667 	url = gf_mpd_get_base_url(period->base_URLs, url, &base_url_index);
3668 	url = gf_mpd_get_base_url(set->base_URLs, url, &base_url_index);
3669 	url = gf_mpd_get_base_url(rep->base_URLs, url, &base_url_index);
3670 	assert(url);
3671 
3672 	/*single URL*/
3673 	if (!rep->segment_list && !set->segment_list && !period->segment_list && !rep->segment_template && !set->segment_template && !period->segment_template) {
3674 		GF_MPD_URL *res_url;
3675 		GF_MPD_SegmentBase *base_seg = NULL;
3676 		if (item_index > 0)
3677 			return GF_EOS;
3678 		switch (resolve_type) {
3679 		case GF_MPD_RESOLVE_URL_MEDIA:
3680 		case GF_MPD_RESOLVE_URL_MEDIA_TEMPLATE:
3681 		case GF_MPD_RESOLVE_URL_MEDIA_NOSTART:
3682 			if (!url)
3683 				return GF_NON_COMPLIANT_BITSTREAM;
3684 			*out_url = url;
3685 			return GF_OK;
3686 		case GF_MPD_RESOLVE_URL_INIT:
3687 		case GF_MPD_RESOLVE_URL_INDEX:
3688 			res_url = NULL;
3689 			base_seg = rep->segment_base;
3690 			if (!base_seg) base_seg = set->segment_base;
3691 			if (!base_seg) base_seg = period->segment_base;
3692 
3693 			if (base_seg) {
3694 				if (resolve_type == GF_MPD_RESOLVE_URL_INDEX) {
3695 					res_url = base_seg->representation_index;
3696 				} else {
3697 					res_url = base_seg->initialization_segment;
3698 				}
3699 			}
3700 			if (is_in_base_url) *is_in_base_url = 0;
3701 			/*no initialization segment / index, use base URL*/
3702 			if (res_url && res_url->sourceURL) {
3703 				if (res_url->is_resolved) {
3704 					*out_url = gf_strdup(res_url->sourceURL);
3705 				} else {
3706 					*out_url = gf_url_concatenate(url, res_url->sourceURL);
3707 				}
3708 				gf_free(url);
3709 			} else {
3710 				*out_url = url;
3711 				if (is_in_base_url) *is_in_base_url = 1;
3712 			}
3713 			if (res_url && res_url->byte_range) {
3714 				*out_range_start = res_url->byte_range->start_range;
3715 				*out_range_end = res_url->byte_range->end_range;
3716 			} else if (base_seg && base_seg->index_range && (resolve_type == GF_MPD_RESOLVE_URL_INDEX)) {
3717 				*out_range_start = base_seg->index_range->start_range;
3718 				*out_range_end = base_seg->index_range->end_range;
3719 			}
3720 			return GF_OK;
3721 		default:
3722 			break;
3723 		}
3724 		gf_free(url);
3725 		return GF_BAD_PARAM;
3726 	}
3727 
3728 	/*segmentList*/
3729 	if (rep->segment_list || set->segment_list || period->segment_list) {
3730 		GF_MPD_URL *init_url, *index_url;
3731 		GF_MPD_SegmentURL *segment;
3732 		GF_List *segments = NULL;
3733 		u32 segment_count;
3734 
3735 		init_url = index_url = NULL;
3736 
3737 		/*apply inheritance of attributes, lowest level having preceedence*/
3738 		if (period->segment_list) {
3739 			if (period->segment_list->initialization_segment) init_url = period->segment_list->initialization_segment;
3740 			if (period->segment_list->segment_URLs) segments = period->segment_list->segment_URLs;
3741 			if (!timescale && period->segment_list->timescale) timescale = period->segment_list->timescale;
3742 		}
3743 		if (set->segment_list) {
3744 			if (set->segment_list->initialization_segment) init_url = set->segment_list->initialization_segment;
3745 			if (set->segment_list->segment_URLs) segments = set->segment_list->segment_URLs;
3746 			if (!timescale && set->segment_list->timescale) timescale = set->segment_list->timescale;
3747 		}
3748 		if (rep->segment_list) {
3749 			if (rep->segment_list->initialization_segment) init_url = rep->segment_list->initialization_segment;
3750 			if (rep->segment_list->segment_URLs) segments = rep->segment_list->segment_URLs;
3751 			if (!timescale && rep->segment_list->timescale) timescale = rep->segment_list->timescale;
3752 		}
3753 
3754 
3755 		segment_count = gf_list_count(segments);
3756 
3757 		switch (resolve_type) {
3758 		case GF_MPD_RESOLVE_URL_INIT:
3759 			if (init_url) {
3760 				if (init_url->sourceURL) {
3761 					if (init_url->is_resolved) {
3762 						*out_url = gf_strdup(init_url->sourceURL);
3763 					} else {
3764 						*out_url = gf_url_concatenate(url, init_url->sourceURL);
3765 					}
3766 					gf_free(url);
3767 				} else {
3768 					*out_url = url;
3769 				}
3770 				if (init_url->byte_range) {
3771 					*out_range_start = init_url->byte_range->start_range;
3772 					*out_range_end = init_url->byte_range->end_range;
3773 				}
3774 			} else {
3775 				gf_free(url);
3776 			}
3777 			return GF_OK;
3778 		case GF_MPD_RESOLVE_URL_MEDIA:
3779 		case GF_MPD_RESOLVE_URL_MEDIA_TEMPLATE:
3780 		case GF_MPD_RESOLVE_URL_MEDIA_NOSTART:
3781 			if (!url) {
3782 				GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[MPD] Media URL is not set in segment list\n"));
3783 				return GF_SERVICE_ERROR;
3784 			}
3785 			if ((item_index >= segment_count) || ((s32) item_index < 0)) {
3786 				gf_free(url);
3787 				return GF_EOS;
3788 			}
3789 			*out_url = url;
3790 			segment = gf_list_get(segments, item_index);
3791 			if (segment->media) {
3792 				*out_url = gf_url_concatenate(url, segment->media);
3793 				gf_free(url);
3794 			}
3795 			if (segment->media_range) {
3796 				*out_range_start = segment->media_range->start_range;
3797 				*out_range_end = segment->media_range->end_range;
3798 			}
3799 			if (segment->duration) {
3800 				*segment_duration_in_ms = (u32) ((Double) (segment->duration) * 1000.0 / timescale);
3801 			}
3802 			if (segment->key_url && out_key_url) {
3803 				*out_key_url = gf_strdup((const char *) segment->key_url);
3804 				if (out_key_iv)
3805 					memcpy((*out_key_iv), segment->key_iv, sizeof(bin128) );
3806 			}
3807 			return GF_OK;
3808 		case GF_MPD_RESOLVE_URL_INDEX:
3809 			if (item_index >= segment_count) {
3810 				gf_free(url);
3811 				return GF_EOS;
3812 			}
3813 			*out_url = url;
3814 			segment = gf_list_get(segments, item_index);
3815 			if (segment->index) {
3816 				*out_url = gf_url_concatenate(url, segment->index);
3817 				gf_free(url);
3818 			}
3819 			if (segment->index_range) {
3820 				*out_range_start = segment->index_range->start_range;
3821 				*out_range_end = segment->index_range->end_range;
3822 			}
3823 			return GF_OK;
3824 		default:
3825 			break;
3826 		}
3827 		gf_free(url);
3828 		return GF_BAD_PARAM;
3829 	}
3830 
3831 	/*segmentTemplate*/
3832 	media_url = init_template = index_template = NULL;
3833 
3834 	/*apply inheritance of attributes, lowest level having preceedence*/
3835 	if (period->segment_template) {
3836 		if (period->segment_template->initialization) init_template = period->segment_template->initialization;
3837 		if (period->segment_template->index) index_template = period->segment_template->index;
3838 		if (period->segment_template->media) media_url = period->segment_template->media;
3839 		if (period->segment_template->start_number != (u32) -1) start_number = period->segment_template->start_number;
3840 		if (period->segment_template->segment_timeline) timeline = period->segment_template->segment_timeline;
3841 		if (!timescale && period->segment_template->timescale) timescale = period->segment_template->timescale;
3842 		if (!duration && period->segment_template->duration) duration = period->segment_template->duration;
3843 	}
3844 	if (set->segment_template) {
3845 		if (set->segment_template->initialization) init_template = set->segment_template->initialization;
3846 		if (set->segment_template->index) index_template = set->segment_template->index;
3847 		if (set->segment_template->media) media_url = set->segment_template->media;
3848 		if (set->segment_template->start_number != (u32) -1) start_number = set->segment_template->start_number;
3849 		if (set->segment_template->segment_timeline) timeline = set->segment_template->segment_timeline;
3850 		if (!timescale && set->segment_template->timescale) timescale = set->segment_template->timescale;
3851 		if (!duration && set->segment_template->duration) duration = set->segment_template->duration;
3852 	}
3853 	if (rep->segment_template) {
3854 		if (rep->segment_template->initialization) init_template = rep->segment_template->initialization;
3855 		if (rep->segment_template->index) index_template = rep->segment_template->index;
3856 		if (rep->segment_template->media) media_url = rep->segment_template->media;
3857 		if (rep->segment_template->start_number != (u32) -1) start_number = rep->segment_template->start_number;
3858 		if (rep->segment_template->segment_timeline) timeline = rep->segment_template->segment_timeline;
3859 		if (!timescale && rep->segment_template->timescale) timescale = rep->segment_template->timescale;
3860 		if (!duration&& rep->segment_template->duration) duration = rep->segment_template->duration;
3861 	}
3862 
3863 	/*return segment duration in all cases*/
3864 	{
3865 		u64 out_duration;
3866 		u32 out_timescale;
3867 		gf_mpd_resolve_segment_duration(rep, set, period, &out_duration, &out_timescale, NULL, NULL);
3868 		*segment_duration_in_ms = (u64)((out_duration * 1000.0) / out_timescale);
3869 	}
3870 
3871 	/*offset the start_number with the number of discarded segments (no longer in our lists)*/
3872 	start_number += nb_segments_removed;
3873 
3874 	if (!media_url) {
3875 		GF_MPD_BaseURL *base = gf_list_get(rep->base_URLs, 0);
3876 		if (!base) return GF_BAD_PARAM;
3877 		media_url = base->URL;
3878 	}
3879 	url_to_solve = NULL;
3880 	switch (resolve_type) {
3881 	case GF_MPD_RESOLVE_URL_INIT:
3882 		url_to_solve = init_template;
3883 		break;
3884 	case GF_MPD_RESOLVE_URL_MEDIA:
3885 	case GF_MPD_RESOLVE_URL_MEDIA_TEMPLATE:
3886 	case GF_MPD_RESOLVE_URL_MEDIA_NOSTART:
3887 		url_to_solve = media_url;
3888 		break;
3889 	case GF_MPD_RESOLVE_URL_INDEX:
3890 		url_to_solve = index_template;
3891 		break;
3892 	default:
3893 		gf_free(url);
3894 		return GF_BAD_PARAM;
3895 	}
3896 	if (!url_to_solve) {
3897 		gf_free(url);
3898 		return GF_OK;
3899 	}
3900 	/*let's solve the template*/
3901 	solved_template = gf_malloc(sizeof(char)*(strlen(url_to_solve) + (rep->id ? strlen(rep->id) : 0)) * 2);
3902 	if (!solved_template) return GF_OUT_OF_MEM;
3903 
3904 	solved_template[0] = 0;
3905 	strcpy(solved_template, url_to_solve);
3906 	first_sep = strchr(solved_template, '$');
3907 	if (first_sep) first_sep[0] = 0;
3908 
3909 	first_sep = strchr(url_to_solve, '$');
3910 	while (first_sep) {
3911 		char szPrintFormat[50];
3912 		char szFormat[100];
3913 		char *format_tag;
3914 		char *second_sep = strchr(first_sep+1, '$');
3915 		if (!second_sep) {
3916 			gf_free(url);
3917 			gf_free(solved_template);
3918 			return GF_NON_COMPLIANT_BITSTREAM;
3919 		}
3920 		second_sep[0] = 0;
3921 		format_tag = strchr(first_sep+1, '%');
3922 
3923 		if (format_tag) {
3924 			strcpy(szPrintFormat, format_tag);
3925 			format_tag[0] = 0;
3926 			if (!strchr(szPrintFormat, 'd') && !strchr(szPrintFormat, 'i')  && !strchr(szPrintFormat, 'u'))
3927 				strcat(szPrintFormat, "d");
3928 		} else {
3929 			strcpy(szPrintFormat, "%d");
3930 		}
3931 		/* identifier is $$ -> replace by $*/
3932 		if (!strlen(first_sep+1)) {
3933 			strcat(solved_template, "$");
3934 		}
3935 		else if (!strcmp(first_sep+1, "RepresentationID")) {
3936 			if (rep->id) {
3937 				strcat(solved_template, rep->id);
3938 			} else {
3939 				GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[MPD] Missing ID on representation - cannot solve template\n\n"));
3940 				gf_free(url);
3941 				gf_free(solved_template);
3942 				second_sep[0] = '$';
3943 				return GF_NON_COMPLIANT_BITSTREAM;
3944 			}
3945 		}
3946 		else if (!strcmp(first_sep+1, "Number")) {
3947 			if (resolve_type==GF_MPD_RESOLVE_URL_MEDIA_TEMPLATE) {
3948 				strcat(solved_template, "$Number$");
3949 			} else if (resolve_type==GF_MPD_RESOLVE_URL_MEDIA_NOSTART) {
3950 				sprintf(szFormat, szPrintFormat, item_index);
3951 				strcat(solved_template, szFormat);
3952 			} else {
3953 				sprintf(szFormat, szPrintFormat, start_number + item_index);
3954 				strcat(solved_template, szFormat);
3955 			}
3956 
3957 			/*check start time is in period (start time is ~seg_duration * item_index, since startNumber seg has start time = 0 in the period*/
3958 			if (period->duration
3959 				&& (item_index * (*segment_duration_in_ms) > period->duration)) {
3960 				gf_free(url);
3961 				gf_free(solved_template);
3962 				second_sep[0] = '$';
3963 				return GF_EOS;
3964 			}
3965 		}
3966 		else if (!strcmp(first_sep+1, "Index")) {
3967 			GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[MPD] Wrong template identifier Index detected - using Number instead\n\n"));
3968 			sprintf(szFormat, szPrintFormat, start_number + item_index);
3969 			strcat(solved_template, szFormat);
3970 		}
3971 		else if (!strcmp(first_sep+1, "Bandwidth")) {
3972 			sprintf(szFormat, szPrintFormat, rep->bandwidth);
3973 			strcat(solved_template, szFormat);
3974 		}
3975 		else if (!strcmp(first_sep+1, "Time")) {
3976 			if (resolve_type==GF_MPD_RESOLVE_URL_MEDIA_TEMPLATE) {
3977 				strcat(solved_template, "$Time$");
3978 			} else if (timeline) {
3979 				/*uses segment timeline*/
3980 				u32 k, nb_seg, cur_idx, nb_repeat;
3981 				u64 time, start_time;
3982 				nb_seg = gf_list_count(timeline->entries);
3983 				cur_idx = 0;
3984 				start_time=0;
3985 				for (k=0; k<nb_seg; k++) {
3986 					GF_MPD_SegmentTimelineEntry *ent = gf_list_get(timeline->entries, k);
3987 					if (item_index>cur_idx+ent->repeat_count) {
3988 						cur_idx += 1 + ent->repeat_count;
3989 						if (ent->start_time) start_time = ent->start_time;
3990 						if (k<nb_seg-1) {
3991 							start_time += ent->duration * (1 + ent->repeat_count);
3992 							continue;
3993 						} else {
3994 							gf_free(url);
3995 							gf_free(solved_template);
3996 							second_sep[0] = '$';
3997 							return GF_EOS;
3998 						}
3999 					}
4000 					*segment_duration_in_ms = ent->duration;
4001 					*segment_duration_in_ms = (u32) ((Double) (*segment_duration_in_ms) * 1000.0 / timescale);
4002 					nb_repeat = item_index - cur_idx;
4003 					time = ent->start_time ? ent->start_time : start_time;
4004 					time += nb_repeat * ent->duration;
4005 
4006 					/*replace final 'd' with LLD (%lld or I64d)*/
4007 					szPrintFormat[strlen(szPrintFormat)-1] = 0;
4008 					strcat(szPrintFormat, &LLD[1]);
4009 					sprintf(szFormat, szPrintFormat, time);
4010 					strcat(solved_template, szFormat);
4011 					break;
4012 				}
4013 			} else if (duration) {
4014 				u64 time = item_index * duration;
4015 				szPrintFormat[strlen(szPrintFormat)-1] = 0;
4016 				strcat(szPrintFormat, &LLD[1]);
4017 				sprintf(szFormat, szPrintFormat, time);
4018 				strcat(solved_template, szFormat);
4019 			}
4020 		}
4021 		else {
4022 			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[MPD] Unknown template identifier %s - disabling rep\n\n", first_sep+1));
4023 			*out_url = NULL;
4024 			gf_free(url);
4025 			gf_free(solved_template);
4026 			second_sep[0] = '$';
4027 			return GF_NON_COMPLIANT_BITSTREAM;
4028 		}
4029 		if (format_tag) format_tag[0] = '%';
4030 		second_sep[0] = '$';
4031 		/*look for next keyword - copy over remaining text if any*/
4032 		first_sep = strchr(second_sep+1, '$');
4033 		if (first_sep) first_sep[0] = 0;
4034 		if (strlen(second_sep+1))
4035 			strcat(solved_template, second_sep+1);
4036 		if (first_sep) first_sep[0] = '$';
4037 	}
4038 	*out_url = gf_url_concatenate(url, solved_template);
4039 	gf_free(url);
4040 	gf_free(solved_template);
4041 	return GF_OK;
4042 }
4043 
4044 GF_EXPORT
gf_mpd_get_duration(GF_MPD * mpd)4045 Double gf_mpd_get_duration(GF_MPD *mpd)
4046 {
4047 	Double duration;
4048 	duration = (Double)mpd->media_presentation_duration;
4049 	if (!duration) {
4050 		u32 i;
4051 		for (i = 0; i<gf_list_count(mpd->periods); i++) {
4052 			GF_MPD_Period *period = gf_list_get(mpd->periods, i);
4053 			duration += (Double)period->duration;
4054 		}
4055 	}
4056 	return duration / 1000.0;
4057 }
4058 
4059 GF_EXPORT
gf_mpd_resolve_segment_duration(GF_MPD_Representation * rep,GF_MPD_AdaptationSet * set,GF_MPD_Period * period,u64 * out_duration,u32 * out_timescale,u64 * out_pts_offset,GF_MPD_SegmentTimeline ** out_segment_timeline)4060 void gf_mpd_resolve_segment_duration(GF_MPD_Representation *rep, GF_MPD_AdaptationSet *set, GF_MPD_Period *period, u64 *out_duration, u32 *out_timescale, u64 *out_pts_offset, GF_MPD_SegmentTimeline **out_segment_timeline)
4061 {
4062 	u32 timescale = 0;
4063 	u64 pts_offset = 0;
4064 	GF_MPD_SegmentTimeline *segment_timeline;
4065 	GF_MPD_MultipleSegmentBase *mbase_rep, *mbase_set, *mbase_period;
4066 
4067 	if (!period) return;
4068 
4069 	if (out_segment_timeline) *out_segment_timeline = NULL;
4070 	if (out_pts_offset) *out_pts_offset = 0;
4071 
4072 	/*single media segment - duration is not known unless indicated in period*/
4073 	if (rep->segment_base || set->segment_base || period->segment_base) {
4074 		if (period->duration) {
4075 			*out_duration = period->duration;
4076 			timescale = 1000;
4077 		} else {
4078 			*out_duration = 0;
4079 			timescale = 0;
4080 		}
4081 		if (rep->segment_base && rep->segment_base->presentation_time_offset) pts_offset = rep->segment_base->presentation_time_offset;
4082 		if (rep->segment_base && rep->segment_base->timescale) timescale = rep->segment_base->timescale;
4083 		if (!pts_offset && set->segment_base && set->segment_base->presentation_time_offset) pts_offset = set->segment_base->presentation_time_offset;
4084 		if (!timescale && set->segment_base && set->segment_base->timescale) timescale = set->segment_base->timescale;
4085 
4086 		if (!pts_offset && period->segment_base && period->segment_base->presentation_time_offset) pts_offset = period->segment_base->presentation_time_offset;
4087 		if (!timescale && period->segment_base && period->segment_base->timescale) timescale = period->segment_base->timescale;
4088 
4089 		if (out_pts_offset) *out_pts_offset = pts_offset;
4090 		*out_timescale = timescale ? timescale : 1;
4091 		return;
4092 	}
4093 	/*we have a segment template list or template*/
4094 	mbase_rep = rep->segment_list ? (GF_MPD_MultipleSegmentBase *) rep->segment_list : (GF_MPD_MultipleSegmentBase *) rep->segment_template;
4095 	mbase_set = set->segment_list ? (GF_MPD_MultipleSegmentBase *)set->segment_list : (GF_MPD_MultipleSegmentBase *)set->segment_template;
4096 	mbase_period = period->segment_list ? (GF_MPD_MultipleSegmentBase *)period->segment_list : (GF_MPD_MultipleSegmentBase *)period->segment_template;
4097 
4098 	segment_timeline = NULL;
4099 	if (mbase_period) segment_timeline =  mbase_period->segment_timeline;
4100 	if (mbase_set && mbase_set->segment_timeline) segment_timeline =  mbase_set->segment_timeline;
4101 	if (mbase_rep && mbase_rep->segment_timeline) segment_timeline =  mbase_rep->segment_timeline;
4102 
4103 	timescale = mbase_rep ? mbase_rep->timescale : 0;
4104 	if (!timescale && mbase_set && mbase_set->timescale) timescale = mbase_set->timescale;
4105 	if (!timescale && mbase_period && mbase_period->timescale) timescale  = mbase_period->timescale;
4106 	if (!timescale) timescale = 1;
4107 	*out_timescale = timescale;
4108 
4109 	if (out_pts_offset) {
4110 		pts_offset = mbase_rep ? mbase_rep->presentation_time_offset : 0;
4111 		if (!pts_offset && mbase_set && mbase_set->presentation_time_offset) pts_offset = mbase_set->presentation_time_offset;
4112 		if (!pts_offset && mbase_period && mbase_period->presentation_time_offset) pts_offset = mbase_period->presentation_time_offset;
4113 		*out_pts_offset = pts_offset;
4114 	}
4115 
4116 	if (mbase_rep && mbase_rep->duration) *out_duration = mbase_rep->duration;
4117 	else if (mbase_set && mbase_set->duration) *out_duration = mbase_set->duration;
4118 	else if (mbase_period && mbase_period->duration) *out_duration = mbase_period->duration;
4119 
4120 	if (out_segment_timeline) *out_segment_timeline = segment_timeline;
4121 
4122 	/*for SegmentTimeline, just pick the first one as an indication (exact timeline solving is not done here)*/
4123 	if (segment_timeline) {
4124 		GF_MPD_SegmentTimelineEntry *ent = gf_list_get(segment_timeline->entries, 0);
4125 		if (ent) *out_duration = ent->duration;
4126 	}
4127 	else if (rep->segment_list) {
4128 		GF_MPD_SegmentURL *url = gf_list_get(rep->segment_list->segment_URLs, 0);
4129 		if (url && url->duration) *out_duration = url->duration;
4130 	}
4131 }
4132 
gf_mpd_segment_timeline_start(GF_MPD_SegmentTimeline * timeline,u32 segment_index,u64 * segment_duration)4133 static u64 gf_mpd_segment_timeline_start(GF_MPD_SegmentTimeline *timeline, u32 segment_index, u64 *segment_duration)
4134 {
4135 	u64 start_time = 0;
4136 	u32 i, idx, k;
4137 	idx = 0;
4138 	for (i = 0; i<gf_list_count(timeline->entries); i++) {
4139 		GF_MPD_SegmentTimelineEntry *ent = gf_list_get(timeline->entries, i);
4140 		if (ent->start_time) start_time = ent->start_time;
4141 		for (k = 0; k<ent->repeat_count + 1; k++) {
4142 			if (idx == segment_index) {
4143 				if (segment_duration)
4144 					*segment_duration = ent->duration;
4145 				return start_time;
4146 			}
4147 			idx++;
4148 			start_time += ent->duration;
4149 		}
4150 	}
4151 	return start_time;
4152 }
4153 
4154 GF_EXPORT
gf_mpd_get_segment_start_time_with_timescale(s32 in_segment_index,GF_MPD_Period const * const period,GF_MPD_AdaptationSet const * const set,GF_MPD_Representation const * const rep,u64 * out_segment_start_time,u64 * out_opt_segment_duration,u32 * out_opt_scale)4155 GF_Err gf_mpd_get_segment_start_time_with_timescale(s32 in_segment_index,
4156 	GF_MPD_Period const * const period, GF_MPD_AdaptationSet const * const set, GF_MPD_Representation const * const rep,
4157 	u64 *out_segment_start_time, u64 *out_opt_segment_duration, u32 *out_opt_scale)
4158 {
4159 	u64 duration = 0, start_time = 0;
4160 	u32 timescale = 0;
4161 	GF_List *seglist = NULL;
4162 	GF_MPD_SegmentTimeline *timeline = NULL;
4163 
4164 	if (!out_segment_start_time || !period || !set || !rep) {
4165 		return GF_BAD_PARAM;
4166 	}
4167 
4168 	/*single segment: return nothing*/
4169 	if (rep->segment_base || set->segment_base || period->segment_base) {
4170 		*out_segment_start_time = start_time;
4171 		return GF_OK;
4172 	}
4173 	if (rep->segment_list || set->segment_list || period->segment_list) {
4174 		if (period->segment_list) {
4175 			if (period->segment_list->duration) duration = period->segment_list->duration;
4176 			if (period->segment_list->timescale) timescale = period->segment_list->timescale;
4177 			if (period->segment_list->segment_timeline) timeline = period->segment_list->segment_timeline;
4178 			if (gf_list_count(period->segment_list->segment_URLs)) seglist = period->segment_list->segment_URLs;
4179 		}
4180 		if (set->segment_list) {
4181 			if (set->segment_list->duration) duration = set->segment_list->duration;
4182 			if (set->segment_list->timescale) timescale = set->segment_list->timescale;
4183 			if (set->segment_list->segment_timeline) timeline = set->segment_list->segment_timeline;
4184 			if (gf_list_count(set->segment_list->segment_URLs)) seglist = set->segment_list->segment_URLs;
4185 		}
4186 		if (rep->segment_list) {
4187 			if (rep->segment_list->duration) duration = rep->segment_list->duration;
4188 			if (rep->segment_list->timescale) timescale = rep->segment_list->timescale;
4189 			if (gf_list_count(rep->segment_list->segment_URLs)) seglist = rep->segment_list->segment_URLs;
4190 		}
4191 		if (!timescale) timescale = 1;
4192 
4193 		if (timeline) {
4194 			start_time = gf_mpd_segment_timeline_start(timeline, in_segment_index, &duration);
4195 		}
4196 		else if (duration) {
4197 			start_time = in_segment_index * duration;
4198 		}
4199 		else if (seglist && (in_segment_index >= 0)) {
4200 			u32 i;
4201 			start_time = 0;
4202 			for (i = 0; i <= (u32)in_segment_index; i++) {
4203 				GF_MPD_SegmentURL *url = gf_list_get(seglist, i);
4204 				if (!url) break;
4205 				duration = url->duration;
4206 				if (i < (u32)in_segment_index)
4207 					start_time += url->duration;
4208 			}
4209 		}
4210 		if (out_opt_segment_duration) *out_opt_segment_duration = duration;
4211 		if (out_opt_scale) *out_opt_scale = timescale;
4212 
4213 		*out_segment_start_time = start_time;
4214 		return GF_OK;
4215 	}
4216 
4217 	if (!rep->segment_template && !set->segment_template && !period->segment_template) {
4218 		GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[MPD] Representation without any SegmentBase, SegmentList or SegmentTemplate (non compliant). Assuming default SegmentBase\n"));
4219 		*out_segment_start_time = start_time;
4220 		return GF_OK;
4221 	}
4222 
4223 	if (period->segment_template) {
4224 		if (period->segment_template->duration) duration = period->segment_template->duration;
4225 		if (period->segment_template->timescale) timescale = period->segment_template->timescale;
4226 		if (period->segment_template->segment_timeline) timeline = period->segment_template->segment_timeline;
4227 
4228 	}
4229 	if (set->segment_template) {
4230 		if (set->segment_template->duration) duration = set->segment_template->duration;
4231 		if (set->segment_template->timescale) timescale = set->segment_template->timescale;
4232 		if (set->segment_template->segment_timeline) timeline = set->segment_template->segment_timeline;
4233 	}
4234 	if (rep->segment_template) {
4235 		if (rep->segment_template->duration) duration = rep->segment_template->duration;
4236 		if (rep->segment_template->timescale) timescale = rep->segment_template->timescale;
4237 		if (rep->segment_template->segment_timeline) timeline = rep->segment_template->segment_timeline;
4238 	}
4239 	if (!timescale) timescale = 1;
4240 
4241 	if (timeline) {
4242 		start_time = gf_mpd_segment_timeline_start(timeline, in_segment_index, &duration);
4243 	}
4244 	else {
4245 		start_time = in_segment_index * duration;
4246 	}
4247 
4248 	if (out_opt_segment_duration) *out_opt_segment_duration = duration;
4249 	if (out_opt_scale) *out_opt_scale = timescale;
4250 	*out_segment_start_time = start_time;
4251 
4252 	return GF_OK;
4253 }
4254 
4255 #if 0 //unused
4256 static GF_Err mpd_seek_periods(Double seek_time, GF_MPD const * const in_mpd, GF_MPD_Period **out_period)
4257 {
4258 	Double start_time;
4259 	u32 i;
4260 
4261 	start_time = 0;
4262 	for (i=0; i<gf_list_count(in_mpd->periods); i++) {
4263 		GF_MPD_Period *period = gf_list_get(in_mpd->periods, i);
4264 		Double dur;
4265 
4266 		if (period->xlink_href) {
4267 			GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[MPD] Period contains XLINKs. Not supported.\n", period->xlink_href));
4268 			return GF_NOT_SUPPORTED;
4269 		}
4270 
4271 		dur = (Double)period->duration;
4272 		dur /= 1000;
4273 		if (seek_time >= start_time) {
4274 			if ((seek_time < start_time + dur)
4275 				|| (i+1 == gf_list_count(in_mpd->periods) && dur == 0.0)) {
4276 				*out_period = period;
4277 				break;
4278 			} else {
4279 				return GF_EOS;
4280 			}
4281 		}
4282 		start_time += dur;
4283 	}
4284 
4285 	return GF_OK;
4286 }
4287 #endif
4288 
4289 
4290 GF_EXPORT
gf_mpd_seek_in_period(Double seek_time,MPDSeekMode seek_mode,GF_MPD_Period const * const in_period,GF_MPD_AdaptationSet const * const in_set,GF_MPD_Representation const * const in_rep,u32 * out_segment_index,Double * out_opt_seek_time)4291 GF_Err gf_mpd_seek_in_period(Double seek_time, MPDSeekMode seek_mode,
4292 	GF_MPD_Period const * const in_period, GF_MPD_AdaptationSet const * const in_set, GF_MPD_Representation const * const in_rep,
4293 	u32 *out_segment_index, Double *out_opt_seek_time)
4294 {
4295 	Double seg_start = 0.0;
4296 	u32 segment_idx = 0;
4297 
4298 	if (!out_segment_index) {
4299 		return GF_BAD_PARAM;
4300 	}
4301 
4302 	while (1) {
4303 		Double segment_duration;
4304 		u32 timescale=1000;
4305 		u64 segment_duration_in_scale=0, seg_start_in_scale;
4306 
4307 		//TODO this could be further optimized by directly querying the index for this start time ...
4308 		GF_Err e = gf_mpd_get_segment_start_time_with_timescale(segment_idx, in_period, in_set, in_rep, &seg_start_in_scale, &segment_duration_in_scale, &timescale);
4309 		if (e<0)
4310 			return e;
4311 		segment_duration = segment_duration_in_scale / (Double)timescale;
4312 
4313 		if (seek_mode == MPD_SEEK_PREV) {
4314 			if ((seek_time >= seg_start) && (seek_time < seg_start + segment_duration)) {
4315 				if (out_opt_seek_time) *out_opt_seek_time = seg_start;
4316 				break;
4317 			}
4318 		} else if (seek_mode == MPD_SEEK_NEAREST) {
4319 			if ((seek_time >= seg_start) && (seek_time < seg_start + segment_duration)) {
4320 				Double dist_to_prev = seek_time - seg_start;
4321 				Double dist_to_next = seg_start + segment_duration - seek_time;
4322 				if (dist_to_next < dist_to_prev) {
4323 					if (out_opt_seek_time) *out_opt_seek_time = seg_start + segment_duration;
4324 					segment_idx++;
4325 				} else {
4326 					if (out_opt_seek_time) *out_opt_seek_time = seg_start;
4327 				}
4328 				break;
4329 			}
4330 		} else {
4331 			assert(0);
4332 			return GF_NOT_SUPPORTED;
4333 		}
4334 
4335 		seg_start += segment_duration;
4336 		segment_idx++;
4337 	}
4338 
4339 	*out_segment_index = segment_idx;
4340 	return GF_OK;
4341 }
4342 
4343 #if 0 //unused
4344 GF_Err gf_mpd_seek_to_time(Double seek_time, MPDSeekMode seek_mode,
4345 	GF_MPD const * const in_mpd, GF_MPD_AdaptationSet const * const in_set, GF_MPD_Representation const * const in_rep,
4346 	GF_MPD_Period **out_period, u32 *out_segment_index, Double *out_opt_seek_time)
4347 {
4348 	GF_Err e = GF_OK;
4349 
4350 	if (!out_period || !out_segment_index) {
4351 		return GF_BAD_PARAM;
4352 	}
4353 
4354 	e = mpd_seek_periods(seek_time, in_mpd, out_period);
4355 	if (e)
4356 		return e;
4357 
4358 	e = gf_mpd_seek_in_period(seek_time, seek_mode, *out_period, in_set, in_rep, out_segment_index, out_opt_seek_time);
4359 	if (e)
4360 		return e;
4361 
4362 	return GF_OK;
4363 }
4364 #endif
4365 
4366 /*
4367 	smooth streaming 2.1 support
4368 
4369 	this is still very basic - we miss support for:
4370 	* live streams
4371 	* sparse stream
4372 	* Custom Attributes in Url pattern
4373 
4374 	The smooth maifest is transformed into an MPD with
4375  StreamIndex <=> AdaptationSet
4376  Url Template <=> SegmentTemplate.media at adaptation set level
4377  QualityLevel <=> Representation
4378  Codecs info at quality level <=> SegmentTemplate.initialisation at representation level using internal "isobmff:// ..." scheme
4379  chunks <=> Segment Timeline at adaptation set level
4380  */
4381 
smooth_parse_chunk(GF_MPD * mpd,GF_List * container,GF_XMLNode * root)4382 static GF_Err smooth_parse_chunk(GF_MPD *mpd, GF_List *container, GF_XMLNode *root)
4383 {
4384     u32 i;
4385     GF_MPD_SegmentTimelineEntry *chunk;
4386     GF_XMLAttribute *att;
4387 
4388     GF_SAFEALLOC(chunk, GF_MPD_SegmentTimelineEntry);
4389     if (!chunk) return GF_OUT_OF_MEM;
4390     gf_list_add(container, chunk);
4391     i = 0;
4392     while ( (att = gf_list_enum(root->attributes, &i)) ) {
4393         if (!strcmp(att->name, "n")) {}
4394         else if (!strcmp(att->name, "d")) chunk->duration = atoi(att->value);
4395         else if (!strcmp(att->name, "t")) chunk->start_time = atoi(att->value);
4396     }
4397     return GF_OK;
4398 }
4399 
smooth_replace_string(char * src_str,char * str_match,char * str_replace,char ** output)4400 static GF_Err smooth_replace_string(char *src_str, char *str_match, char *str_replace, char **output)
4401 {
4402     u32 len;
4403     char c, *res, *sep = strstr(src_str, str_match);
4404     if (!sep) {
4405         res = gf_strdup(src_str);
4406         if (*output) gf_free(*output);
4407         *output = res;
4408         return GF_OK;
4409     }
4410 
4411     c = sep[0];
4412     sep[0] = 0;
4413     len = (u32) ( strlen(src_str) + strlen(str_replace) + strlen(sep+strlen(str_match)) + 1 );
4414     res = gf_malloc(sizeof(char) * len);
4415     strcpy(res, src_str);
4416     strcat(res, str_replace);
4417     strcat(res, sep+strlen(str_match));
4418     sep[0] = c;
4419 
4420     if (*output) gf_free(*output);
4421     *output = res;
4422     return GF_OK;
4423 }
4424 
smooth_parse_quality_level(GF_MPD * mpd,GF_List * container,GF_XMLNode * root,u32 timescale)4425 static GF_Err smooth_parse_quality_level(GF_MPD *mpd, GF_List *container, GF_XMLNode *root, u32 timescale)
4426 {
4427     u32 i;
4428     Bool is_audio = GF_FALSE;
4429     GF_MPD_Representation *rep;
4430     GF_XMLAttribute *att;
4431     GF_Err e;
4432     char *szISOBMFFInit = NULL;
4433 
4434     GF_SAFEALLOC(rep, GF_MPD_Representation);
4435     if (!rep) return GF_OUT_OF_MEM;
4436     gf_mpd_init_common_attributes((GF_MPD_CommonAttributes *)rep);
4437     rep->base_URLs = gf_list_new();
4438     rep->sub_representations = gf_list_new();
4439     e = gf_list_add(container, rep);
4440     if (e) return e;
4441 
4442 	gf_dynstrcat(&szISOBMFFInit, "isobmff://", NULL);
4443 
4444 
4445 #define ISBMFFI_ADD_KEYWORD(_name, _value) \
4446 	if (_value != NULL) {\
4447 		gf_dynstrcat(&szISOBMFFInit, _name, NULL);\
4448 		gf_dynstrcat(&szISOBMFFInit, "=", NULL);\
4449 		gf_dynstrcat(&szISOBMFFInit, _value, NULL);\
4450 		gf_dynstrcat(&szISOBMFFInit, " ", NULL);\
4451 	}
4452 
4453 #define ISBMFFI_ADD_KEYWORD_CONST(_name, _value) \
4454 	gf_dynstrcat(&szISOBMFFInit, _name, NULL);\
4455 	gf_dynstrcat(&szISOBMFFInit, "=", NULL);\
4456 	gf_dynstrcat(&szISOBMFFInit, _value, NULL);\
4457 	gf_dynstrcat(&szISOBMFFInit, " ", NULL);\
4458 
4459 
4460     i = 0;
4461     while ( (att = gf_list_enum(root->attributes, &i)) ) {
4462         if (!strcmp(att->name, "Index")) rep->id = gf_strdup(att->value);
4463         else if (!strcmp(att->name, "Bitrate")) rep->bandwidth = atoi(att->value);
4464         else if (!strcmp(att->name, "MaxWidth")) {
4465             rep->width = atoi(att->value);
4466             ISBMFFI_ADD_KEYWORD("w", att->value)
4467         }
4468         else if (!strcmp(att->name, "MaxHeight")) {
4469             rep->height = atoi(att->value);
4470             ISBMFFI_ADD_KEYWORD("h", att->value)
4471         }
4472         else if (!strcmp(att->name, "FourCC")) {
4473             ISBMFFI_ADD_KEYWORD("4cc", att->value)
4474         }
4475         else if (!strcmp(att->name, "CodecPrivateData")) {
4476             ISBMFFI_ADD_KEYWORD("init", att->value)
4477         }
4478         else if (!strcmp(att->name, "NALUnitLengthField")) {
4479             ISBMFFI_ADD_KEYWORD("nal", att->value)
4480         }
4481         else if (!strcmp(att->name, "BitsPerSample")) {
4482             ISBMFFI_ADD_KEYWORD("bps", att->value)
4483             is_audio = GF_TRUE;
4484         }
4485         else if (!strcmp(att->name, "AudioTag")) {
4486             ISBMFFI_ADD_KEYWORD("atag", att->value)
4487             is_audio = GF_TRUE;
4488         }
4489         else if (!strcmp(att->name, "Channels")) {
4490             ISBMFFI_ADD_KEYWORD("ch", att->value)
4491             is_audio = GF_TRUE;
4492         }
4493         else if (!strcmp(att->name, "SamplingRate")) {
4494             ISBMFFI_ADD_KEYWORD("srate", att->value)
4495             is_audio = GF_TRUE;
4496         }
4497     }
4498     if (timescale != 10000000) {
4499         char szTS[20], *v;
4500         sprintf(szTS, "%d", timescale);
4501 		//prevent gcc warning
4502 		v = (char *)szTS;
4503         ISBMFFI_ADD_KEYWORD("scale", v)
4504     }
4505     ISBMFFI_ADD_KEYWORD_CONST("tfdt", "0000000000000000000")
4506     //create a url for the IS to be reconstructed
4507     rep->mime_type = gf_strdup(is_audio ? "audio/mp4" : "video/mp4");
4508     GF_SAFEALLOC(rep->segment_template, GF_MPD_SegmentTemplate);
4509     if (!rep->segment_template) return GF_OUT_OF_MEM;
4510     rep->segment_template->initialization = szISOBMFFInit;
4511     return GF_OK;
4512 }
4513 
smooth_parse_stream_index(GF_MPD * mpd,GF_List * container,GF_XMLNode * root,u32 timescale)4514 static GF_Err smooth_parse_stream_index(GF_MPD *mpd, GF_List *container, GF_XMLNode *root, u32 timescale)
4515 {
4516     u32 i;
4517     GF_MPD_AdaptationSet *set;
4518     GF_XMLAttribute *att;
4519     GF_XMLNode *child;
4520 
4521     GF_SAFEALLOC(set, GF_MPD_AdaptationSet);
4522     if (!set) return GF_OUT_OF_MEM;
4523     gf_mpd_init_common_attributes((GF_MPD_CommonAttributes *)set);
4524 
4525     gf_list_add(container, set);
4526 
4527     set->accessibility = gf_list_new();
4528     set->role = gf_list_new();
4529     set->rating = gf_list_new();
4530     set->viewpoint = gf_list_new();
4531     set->content_component = gf_list_new();
4532     set->base_URLs = gf_list_new();
4533     set->representations = gf_list_new();
4534     set->segment_alignment = GF_TRUE;
4535     /*assign default ID and group*/
4536     set->group = -1;
4537 
4538     i=0;
4539     while ((att = gf_list_enum(root->attributes, &i))) {
4540         if (!strcmp(att->name, "Type")) {}
4541         else if (!strcmp(att->name, "Name")) {}
4542         else if (!strcmp(att->name, "Chunks")) {}
4543         else if (!strcmp(att->name, "MaxWidth")) set->max_width = atoi(att->value);
4544         else if (!strcmp(att->name, "MaxHeight")) set->max_height = atoi(att->value);
4545         else if (!strcmp(att->name, "Url")) {
4546             char *template_url=NULL;
4547             smooth_replace_string(att->value, "{bitrate}", "$Bandwidth$", &template_url);
4548             smooth_replace_string(template_url, "{Bitrate}", "$Bandwidth$", &template_url);
4549             smooth_replace_string(template_url, "{start time}", "$Time$", &template_url);
4550             smooth_replace_string(template_url, "{start_time}", "$Time$", &template_url);
4551             //TODO handle track substitution and custom attrib
4552 
4553             GF_SAFEALLOC(set->segment_template, GF_MPD_SegmentTemplate);
4554             if (!set->segment_template) return GF_OUT_OF_MEM;
4555             set->segment_template->media = template_url;
4556             set->segment_template->timescale = timescale;
4557             GF_SAFEALLOC(set->segment_template->segment_timeline, GF_MPD_SegmentTimeline);
4558             if (!set->segment_template->segment_timeline) return GF_OUT_OF_MEM;
4559 
4560             set->segment_template->segment_timeline->entries = gf_list_new();
4561         }
4562     }
4563 
4564     i = 0;
4565     while ( ( child = gf_list_enum(root->content, &i )) ) {
4566         if (!strcmp(child->name, "QualityLevel")) {
4567             smooth_parse_quality_level(mpd, set->representations, child, timescale);
4568         }
4569         if (!strcmp(child->name, "c")) {
4570             smooth_parse_chunk(mpd, set->segment_template->segment_timeline->entries, child);
4571         }
4572     }
4573 
4574     return GF_OK;
4575 }
4576 
4577 GF_EXPORT
gf_mpd_init_smooth_from_dom(GF_XMLNode * root,GF_MPD * mpd,const char * default_base_url)4578 GF_Err gf_mpd_init_smooth_from_dom(GF_XMLNode *root, GF_MPD *mpd, const char *default_base_url)
4579 {
4580     GF_Err e;
4581     u32 i, timescale;
4582     GF_XMLAttribute *att;
4583     GF_XMLNode *child;
4584     GF_MPD_Period *period;
4585 
4586     if (!root || !mpd) return GF_BAD_PARAM;
4587 
4588     assert(!mpd->periods);
4589     mpd->periods = gf_list_new();
4590     mpd->program_infos = gf_list_new();
4591     mpd->base_URLs = gf_list_new();
4592     mpd->locations = gf_list_new();
4593     mpd->metrics = gf_list_new();
4594 
4595     /*setup some defaults*/
4596     mpd->type = GF_MPD_TYPE_STATIC;
4597     mpd->time_shift_buffer_depth = (u32) -1; /*infinite by default*/
4598     mpd->xml_namespace = NULL;
4599 
4600 
4601     timescale = 10000000;
4602     i=0;
4603     while ((att = gf_list_enum(root->attributes, &i))) {
4604         if (!strcmp(att->name, "TimeScale"))
4605         timescale = atoi(att->value);
4606         else if (!strcmp(att->name, "Duration"))
4607         mpd->media_presentation_duration = atoi(att->value);
4608         else if (!strcmp(att->name, "IsLive") && stricmp(att->value, "true") )
4609         mpd->type = GF_MPD_TYPE_DYNAMIC;
4610         else if (!strcmp(att->name, "LookaheadCount"))
4611         {}
4612         else if (!strcmp(att->name, "DVRWindowLength"))
4613         mpd->time_shift_buffer_depth = atoi(att->value);
4614     }
4615     mpd->media_presentation_duration = mpd->media_presentation_duration * 1000 / timescale;
4616     mpd->time_shift_buffer_depth = mpd->time_shift_buffer_depth * 1000 / timescale;
4617 
4618 
4619     GF_SAFEALLOC(period, GF_MPD_Period);
4620     if (!period) return GF_OUT_OF_MEM;
4621     gf_list_add(mpd->periods, period);
4622     period->adaptation_sets = gf_list_new();
4623     if (!period->adaptation_sets) return GF_OUT_OF_MEM;
4624 
4625     i = 0;
4626     while ( ( child = gf_list_enum(root->content, &i )) ) {
4627         if (!strcmp(child->name, "StreamIndex")) {
4628             e = smooth_parse_stream_index(mpd, period->adaptation_sets, child, timescale);
4629             if (e) return e;
4630         }
4631     }
4632 
4633     return GF_OK;
4634 }
4635 
4636 GF_EXPORT
gf_mpd_smooth_to_mpd(char * smooth_file,GF_MPD * mpd,const char * default_base_url)4637 GF_Err gf_mpd_smooth_to_mpd(char * smooth_file, GF_MPD *mpd, const char *default_base_url)
4638 {
4639 	GF_DOMParser *dom = gf_xml_dom_new();
4640 	GF_Err e = gf_xml_dom_parse(dom, smooth_file, NULL, 0);
4641 	if (!e) {
4642 		e = gf_mpd_init_smooth_from_dom(gf_xml_dom_get_root(dom), mpd, default_base_url);
4643 		if (e) {
4644 			GF_LOG(GF_LOG_ERROR, GF_LOG_AUDIO, ("[MPD] Failed to convert smooth manifest to MPD\n"));
4645 		}
4646 	} else {
4647 		GF_LOG(GF_LOG_ERROR, GF_LOG_AUDIO, ("[MPD] Failed to load smooth manifest\n"));
4648 	}
4649 	gf_xml_dom_del(dom);
4650 	return e;
4651 }
4652 #define EXTRACT_FORMAT(_nb_chars)	\
4653 			strcpy(szFmt, "%d");	\
4654 			char_template+=_nb_chars;	\
4655 			if (seg_rad_name[char_template]=='%') {	\
4656 				char *sep = strchr(seg_rad_name+char_template, '$');	\
4657 				if (sep) {	\
4658 					sep[0] = 0;	\
4659 					strcpy(szFmt, seg_rad_name+char_template);	\
4660 					char_template += (u32) strlen(seg_rad_name+char_template);	\
4661 					sep[0] = '$';	\
4662 				}	\
4663 			}	\
4664 			char_template+=1;	\
4665 
4666 GF_EXPORT
gf_media_mpd_format_segment_name(GF_DashTemplateSegmentType seg_type,Bool is_bs_switching,char * segment_name,const char * rep_id,const char * base_url,const char * seg_rad_name,const char * seg_ext,u64 start_time,u32 bandwidth,u32 segment_number,Bool use_segment_timeline)4667 GF_Err gf_media_mpd_format_segment_name(GF_DashTemplateSegmentType seg_type, Bool is_bs_switching, char *segment_name, const char *rep_id, const char *base_url, const char *seg_rad_name, const char *seg_ext, u64 start_time, u32 bandwidth, u32 segment_number, Bool use_segment_timeline)
4668 {
4669 	Bool has_number= GF_FALSE;
4670 	Bool force_path = GF_FALSE;
4671 	if (seg_type==GF_DASH_TEMPLATE_TEMPLATE_WITH_PATH) {
4672 		seg_type = GF_DASH_TEMPLATE_TEMPLATE;
4673 		force_path = GF_TRUE;
4674 	}
4675 	if (seg_type==GF_DASH_TEMPLATE_REPINDEX_TEMPLATE_WITH_PATH) {
4676 		seg_type = GF_DASH_TEMPLATE_REPINDEX_TEMPLATE;
4677 		force_path = GF_TRUE;
4678 	}
4679 
4680 	Bool is_index = (seg_type==GF_DASH_TEMPLATE_REPINDEX) ? GF_TRUE : GF_FALSE;
4681 	Bool is_init = (seg_type==GF_DASH_TEMPLATE_INITIALIZATION) ? GF_TRUE : GF_FALSE;
4682 	Bool is_template = (seg_type==GF_DASH_TEMPLATE_TEMPLATE) ? GF_TRUE : GF_FALSE;
4683 	Bool is_init_template = (seg_type==GF_DASH_TEMPLATE_INITIALIZATION_TEMPLATE) ? GF_TRUE : GF_FALSE;
4684 	Bool is_index_template = (seg_type==GF_DASH_TEMPLATE_REPINDEX_TEMPLATE) ? GF_TRUE : GF_FALSE;
4685 	Bool needs_init=((is_init || is_init_template) && !is_bs_switching) ? GF_TRUE : GF_FALSE;
4686 	Bool has_init_keyword = GF_FALSE;
4687 	Bool needs_index = GF_FALSE;
4688 	u32 char_template = 0;
4689 	size_t seg_rad_name_len;
4690 
4691 	char tmp[100];
4692 	strcpy(segment_name, "");
4693 
4694 	if (is_index_template) is_template = GF_TRUE;
4695 
4696 	if (seg_type==GF_DASH_TEMPLATE_INITIALIZATION_SKIPINIT) {
4697 		seg_type = GF_DASH_TEMPLATE_INITIALIZATION;
4698 		needs_init = GF_FALSE;
4699 		is_init = GF_TRUE;
4700 	}
4701 	if (seg_type==GF_DASH_TEMPLATE_INITIALIZATION_TEMPLATE_SKIPINIT) {
4702 		seg_type = GF_DASH_TEMPLATE_INITIALIZATION_TEMPLATE;
4703 		is_init_template = GF_TRUE;
4704 		needs_init = GF_FALSE;
4705 	}
4706 
4707 	if (!seg_rad_name) return GF_BAD_PARAM;
4708 
4709 	seg_rad_name_len = strlen(seg_rad_name);
4710 
4711 	if ( (is_index || is_index_template) && !strstr(seg_rad_name, "$Index")) {
4712 		needs_index = GF_TRUE;
4713 	}
4714 	if (strstr(seg_rad_name, "$RepresentationID$") || strstr(seg_rad_name, "$Bandwidth$"))
4715 		needs_init = GF_FALSE;
4716 
4717 	if (strstr(seg_rad_name, "$Init="))
4718 		has_init_keyword = GF_TRUE;
4719 
4720 	while (char_template <= seg_rad_name_len) {
4721 		char szFmt[20];
4722 		char char_val = seg_rad_name[char_template];
4723 
4724 		if (!is_template && !is_init_template && !strnicmp(& seg_rad_name[char_template], "$RepresentationID$", 18) ) {
4725 			char_template += 18;
4726 			strcat(segment_name, rep_id);
4727 			needs_init = GF_FALSE;
4728 		}
4729 		else if (!is_template && !is_init_template && !strnicmp(& seg_rad_name[char_template], "$Bandwidth", 10)) {
4730 			EXTRACT_FORMAT(10);
4731 
4732 			sprintf(tmp, szFmt, bandwidth);
4733 			strcat(segment_name, tmp);
4734 			needs_init = GF_FALSE;
4735 		}
4736 		else if (!is_template && !strnicmp(& seg_rad_name[char_template], "$Time", 5)) {
4737 			EXTRACT_FORMAT(5);
4738 			if (is_init || is_init_template) {
4739 				if (!has_init_keyword && needs_init) {
4740 					strcat(segment_name, "init");
4741 					needs_init = GF_FALSE;
4742 				}
4743 				continue;
4744 			}
4745 			/*replace %d to LLD*/
4746 			szFmt[strlen(szFmt)-1]=0;
4747 			strcat(szFmt, &LLD[1]);
4748 			sprintf(tmp, szFmt, start_time);
4749 			strcat(segment_name, tmp);
4750 			has_number = GF_TRUE;
4751 		}
4752 		else if (!is_template && !strnicmp(& seg_rad_name[char_template], "$Number", 7)) {
4753 			EXTRACT_FORMAT(7);
4754 
4755 			if (is_init || is_init_template) {
4756 				if (!has_init_keyword && needs_init) {
4757 					strcat(segment_name, "init");
4758 					needs_init = GF_FALSE;
4759 				}
4760 				continue;
4761 			}
4762 			sprintf(tmp, szFmt, segment_number);
4763 			strcat(segment_name, tmp);
4764 			has_number = GF_TRUE;
4765 		}
4766 		else if (!strnicmp(& seg_rad_name[char_template], "$Init=", 6)) {
4767 			char *sep = strchr(seg_rad_name + char_template+6, '$');
4768 			if (sep) sep[0] = 0;
4769 			if (is_init || is_init_template) {
4770 				strcat(segment_name, seg_rad_name + char_template+6);
4771 				needs_init = GF_FALSE;
4772 			}
4773 			char_template += (u32) strlen(seg_rad_name + char_template)+1;
4774 			if (sep) sep[0] = '$';
4775 		}
4776 		else if (!strnicmp(& seg_rad_name[char_template], "$Index=", 7)) {
4777 			char *sep = strchr(seg_rad_name + char_template+7, '$');
4778 			if (sep) sep[0] = 0;
4779 			if (is_index) {
4780 				strcat(segment_name, seg_rad_name + char_template+6);
4781 				needs_index = GF_FALSE;
4782 			}
4783 			char_template += (u32) strlen(seg_rad_name + char_template)+1;
4784 			if (sep) sep[0] = '$';
4785 		}
4786 		else if (!strnicmp(& seg_rad_name[char_template], "$Path=", 6)) {
4787 			char *sep = strchr(seg_rad_name + char_template+6, '$');
4788 			if (sep) sep[0] = 0;
4789 			if (force_path || (!is_template && !is_init_template)) {
4790 				strcat(segment_name, seg_rad_name + char_template+6);
4791 			}
4792 			char_template += (u32) strlen(seg_rad_name + char_template)+1;
4793 			if (sep) sep[0] = '$';
4794 		}
4795 		else if (!strnicmp(& seg_rad_name[char_template], "$Segment=", 9)) {
4796 			char *sep = strchr(seg_rad_name + char_template+9, '$');
4797 			if (sep) sep[0] = 0;
4798 			if (!is_init && !is_init_template) {
4799 				strcat(segment_name, seg_rad_name + char_template+9);
4800 			}
4801 			char_template += (u32) strlen(seg_rad_name + char_template)+1;
4802 			if (sep) sep[0] = '$';
4803 		}
4804 
4805 		else {
4806 			char_template+=1;
4807 			if (char_val=='\\') char_val = '/';
4808 
4809 			sprintf(tmp, "%c", char_val);
4810 			strcat(segment_name, tmp);
4811 		}
4812 	}
4813 
4814 	if (is_template && !strstr(seg_rad_name, "$Number") && !strstr(seg_rad_name, "$Time")) {
4815 		if (use_segment_timeline) {
4816 			strcat(segment_name, "$Time$");
4817 		} else {
4818 			strcat(segment_name, "$Number$");
4819 		}
4820 	}
4821 
4822 	if (needs_init)
4823 		strcat(segment_name, "init");
4824 	if (needs_index)
4825 		strcat(segment_name, "idx");
4826 
4827 	if (!is_init && !is_template && !is_init_template && !is_index && !has_number) {
4828 		if (use_segment_timeline) {
4829 			sprintf(tmp, LLU, start_time);
4830 			strcat(segment_name, tmp);
4831 		}
4832 		else {
4833 			sprintf(tmp, "%d", segment_number);
4834 			strcat(segment_name, tmp);
4835 		}
4836 	}
4837 	if (seg_ext) {
4838 		strcat(segment_name, ".");
4839 		strcat(segment_name, seg_ext);
4840 	}
4841 
4842 	if ((seg_type != GF_DASH_TEMPLATE_TEMPLATE) && (seg_type != GF_DASH_TEMPLATE_INITIALIZATION_TEMPLATE)) {
4843 		char *sep = strrchr(segment_name, '/');
4844 		if (sep) {
4845 			char cv = sep[0];
4846 			sep[0] = 0;
4847 			if (!gf_dir_exists(segment_name)) {
4848 				gf_mkdir(segment_name);
4849 			}
4850 			sep[0] = cv;
4851 		}
4852 	}
4853 
4854 	return GF_OK;
4855 }
4856 
4857 
gf_mpd_load_cues(const char * cues_file,u32 stream_id,u32 * cues_timescale,Bool * use_edit_list,GF_DASHCueInfo ** out_cues,u32 * nb_cues)4858 GF_Err gf_mpd_load_cues(const char *cues_file, u32 stream_id, u32 *cues_timescale, Bool *use_edit_list, GF_DASHCueInfo **out_cues, u32 *nb_cues)
4859 {
4860 	GF_XMLNode *root, *stream, *cue;
4861 	GF_XMLAttribute *att;
4862 	u32 i, j, k;
4863 	GF_DOMParser *parser = gf_xml_dom_new();
4864 	GF_Err e = gf_xml_dom_parse(parser, cues_file, NULL, NULL);
4865 	if (e != GF_OK) {
4866 		gf_xml_dom_del(parser);
4867 		GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error loading cue file %s: %s\n", cues_file, gf_error_to_string(e)));
4868 		return GF_NON_COMPLIANT_BITSTREAM;
4869 	}
4870 	root = gf_xml_dom_get_root(parser);
4871 	if (e != GF_OK) {
4872 		gf_xml_dom_del(parser);
4873 		GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error loading cue file, no root element found\n"));
4874 		return GF_NON_COMPLIANT_BITSTREAM;
4875 	}
4876 	if (strcmp(root->name, "DASHCues")) {
4877 		GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Wrong cue file, expecting DASHCues got %s\n", root->name));
4878 		gf_xml_dom_del(parser);
4879 		return GF_NON_COMPLIANT_BITSTREAM;
4880 	}
4881 
4882 	i=0;
4883 	while ((stream = gf_list_enum(root->content, &i))) {
4884 		u32 id=0;
4885 		u32 cur_cue;
4886 		GF_DASHCueInfo *cues;
4887 		u32 timescale=1000;
4888 		if (stream->type != GF_XML_NODE_TYPE) continue;
4889 		if (strcmp(stream->name, "Stream")) continue;
4890 
4891 		*use_edit_list = GF_FALSE;
4892 		j=0;
4893 		while ((att = gf_list_enum(stream->attributes, &j))) {
4894 			if (!strcmp(att->name, "id")) id = atoi(att->value);
4895 			else if (!strcmp(att->name, "timescale")) timescale = atoi(att->value);
4896 			else if (!strcmp(att->name, "mode") && !strcmp(att->value, "edit") ) *use_edit_list = GF_TRUE;
4897 		}
4898 		if (id != stream_id) continue;
4899 
4900 		*cues_timescale = timescale;
4901 		*nb_cues = 0;
4902 
4903 		j=0;
4904 		while ((cue = gf_list_enum(stream->content, &j))) {
4905 			if (cue->type != GF_XML_NODE_TYPE) continue;
4906 			if (strcmp(cue->name, "Cue")) continue;
4907 			(*nb_cues)++;
4908 		}
4909 		cues = gf_malloc(sizeof(GF_DASHCueInfo)* (*nb_cues) );
4910 		if (!cues) {
4911 			gf_xml_dom_del(parser);
4912 			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Failed to allocate %d cues\n", (*nb_cues) ));
4913 			return GF_OUT_OF_MEM;
4914 		}
4915 		memset(cues, 0, sizeof(GF_DASHCueInfo)* (*nb_cues) );
4916 		*out_cues = cues;
4917 
4918 		j=0;
4919 		cur_cue = 0;
4920 		while ((cue = gf_list_enum(stream->content, &j))) {
4921 			if (cue->type != GF_XML_NODE_TYPE) continue;
4922 			if (strcmp(cue->name, "Cue")) continue;
4923 
4924 			k=0;
4925 			while ((att = gf_list_enum(cue->attributes, &k))) {
4926 				if (!strcmp(att->name, "sample")) cues[cur_cue].sample_num = atoi(att->value);
4927 				else if (!strcmp(att->name, "dts")) sscanf(att->value, LLD, &cues[cur_cue].dts);
4928 				else if (!strcmp(att->name, "cts")) sscanf(att->value, LLD, &cues[cur_cue].cts);
4929 			}
4930 			cur_cue++;
4931 		}
4932 
4933 
4934 		break;
4935 	}
4936 	gf_xml_dom_del(parser);
4937 
4938 	if (!stream) {
4939 		GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] No cues found for requested stream %d\n", stream_id));
4940 		return GF_BAD_PARAM;
4941 	}
4942 	return GF_OK;
4943 }
4944 
gf_mpd_split_adaptation_sets(GF_MPD * mpd)4945 GF_Err gf_mpd_split_adaptation_sets(GF_MPD *mpd)
4946 {
4947 	u32 i, nb_periods, next_as_id=0;;
4948 	if (!mpd) return GF_BAD_PARAM;
4949 
4950 	nb_periods = gf_list_count(mpd->periods);
4951 	for (i=0; i<nb_periods; i++) {
4952 		u32 j, nb_as;
4953 		GF_MPD_Period *period = gf_list_get(mpd->periods, i);
4954 		nb_as = gf_list_count(period->adaptation_sets);
4955 		for (j=0; j<nb_as; j++) {
4956 			GF_MPD_AdaptationSet *set = gf_list_get(period->adaptation_sets, j);
4957 			if (set->id > next_as_id)
4958 				next_as_id = set->id;
4959 		}
4960 	}
4961 	next_as_id++;
4962 
4963 	for (i=0; i<nb_periods; i++) {
4964 		u32 j, nb_as;
4965 		GF_MPD_Period *period = gf_list_get(mpd->periods, i);
4966 		GF_List *new_as = gf_list_new();
4967 
4968 		nb_as = gf_list_count(period->adaptation_sets);
4969 		for (j=0; j<nb_as; j++) {
4970 			GF_MPD_AdaptationSet *set = gf_list_get(period->adaptation_sets, j);
4971 			GF_List *reps = set->representations;
4972 			u32 nb_reps = gf_list_count(set->representations);
4973 
4974 			gf_list_add(new_as, set);
4975 			if (nb_reps<=1) {
4976 				continue;
4977 			}
4978 			while (gf_list_count(set->representations)>1) {
4979 				FILE *f = gf_file_temp(NULL);
4980 				u32 size;
4981 				char *data, szAdd[100];
4982 				GF_Blob blob;
4983 				GF_DOMParser *dom;
4984 				GF_XMLNode *root;
4985 				GF_MPD_Representation *rep = gf_list_get(reps, 1);
4986 				gf_list_rem(reps, 1);
4987 				set->representations = gf_list_new();
4988 				gf_list_add(set->representations, rep);
4989 
4990 				if (set->id) {
4991 					set->id = next_as_id;
4992 					next_as_id++;
4993 				}
4994 
4995 				//serialize
4996 				gf_mpd_print_adaptation_set(set, f, GF_FALSE, 0);
4997 				size = (u32) gf_ftell(f);
4998 				data = gf_malloc(size+1);
4999 				gf_fseek(f, 0, SEEK_SET);
5000 				size = (u32) gf_fread(data, size, f);
5001 				data[size]=0;
5002 				blob.data = data;
5003 				blob.size = size;
5004 				sprintf(szAdd, "gmem://%p", &blob);
5005 
5006 				//parse
5007 				dom = gf_xml_dom_new();
5008 				gf_xml_dom_parse(dom, szAdd, NULL, NULL);
5009 				root = gf_xml_dom_get_root(dom);
5010 				gf_mpd_parse_adaptation_set(mpd, new_as, root);
5011 				gf_xml_dom_del(dom);
5012 				gf_free(data);
5013 				gf_fclose(f);
5014 
5015 
5016 				gf_mpd_representation_free(rep);
5017 				gf_list_del(set->representations);
5018 				set->representations = reps;
5019 			}
5020 		}
5021 		gf_list_del(period->adaptation_sets);
5022 		period->adaptation_sets = new_as;
5023 	}
5024 	return GF_OK;
5025 }
5026 
5027 
5028 #endif /*GPAC_DISABLE_CORE_TOOLS*/
5029