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