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