LCOV - code coverage report
Current view: top level - media_tools - mpd.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 2386 2995 79.7 %
Date: 2021-04-29 23:48:07 Functions: 105 111 94.6 %

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

Generated by: LCOV version 1.13