LCOV - code coverage report
Current view: top level - filters - dasher.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 3306 4411 74.9 %
Date: 2021-04-29 23:48:07 Functions: 69 73 94.5 %

          Line data    Source code
       1             : /*
       2             :  *                      GPAC - Multimedia Framework C SDK
       3             :  *
       4             :  *                      Authors: Jean Le Feuvre
       5             :  *                      Copyright (c) Telecom ParisTech 2018-2021
       6             :  *                                      All rights reserved
       7             :  *
       8             :  *  This file is part of GPAC / MPEG-DASH/HLS segmenter
       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/filters.h>
      27             : #include <gpac/constants.h>
      28             : #include <gpac/iso639.h>
      29             : #include <gpac/mpd.h>
      30             : #include <gpac/internal/media_dev.h>
      31             : #include <gpac/base_coding.h>
      32             : #include <gpac/network.h>
      33             : #include <gpac/crypt_tools.h>
      34             : 
      35             : #define DEFAULT_PERIOD_ID        "_gf_dash_def_period"
      36             : 
      37             : typedef struct
      38             : {
      39             :         GF_List *streams;
      40             : 
      41             :         //period element we will fill
      42             :         GF_MPD_Period *period;
      43             : } GF_DasherPeriod;
      44             : 
      45             : enum
      46             : {
      47             :         DASHER_BS_SWITCH_DEF=0,
      48             :         DASHER_BS_SWITCH_OFF,
      49             :         DASHER_BS_SWITCH_ON,
      50             :         DASHER_BS_SWITCH_INBAND,
      51             :         DASHER_BS_SWITCH_FORCE,
      52             :         DASHER_BS_SWITCH_MULTI,
      53             : };
      54             : 
      55             : typedef enum
      56             : {
      57             :         DASHER_UTCREF_NONE=0,
      58             :         DASHER_UTCREF_NTP,
      59             :         DASHER_UTCREF_HTTP_HEAD,
      60             :         DASHER_UTCREF_ISO,
      61             :         DASHER_UTCREF_XSDATE,
      62             :         DASHER_UTCREF_INBAND,
      63             : } DasherUTCTimingType;
      64             : 
      65             : enum
      66             : {
      67             :         DASHER_NTP_REM=0,
      68             :         DASHER_NTP_YES,
      69             :         DASHER_NTP_KEEP,
      70             : };
      71             : 
      72             : enum
      73             : {
      74             :         DASHER_SAP_OFF=0,
      75             :         DASHER_SAP_SIG,
      76             :         DASHER_SAP_ON,
      77             : };
      78             : 
      79             : enum
      80             : {
      81             :         DASHER_BOUNDS_OUT=0,
      82             :         DASHER_BOUNDS_CLOSEST,
      83             :         DASHER_BOUNDS_IN,
      84             : };
      85             : 
      86             : enum
      87             : {
      88             :         DASHER_MUX_ISOM=0,
      89             :         DASHER_MUX_TS,
      90             :         DASHER_MUX_MKV,
      91             :         DASHER_MUX_WEBM,
      92             :         DASHER_MUX_OGG,
      93             :         DASHER_MUX_RAW,
      94             :         DASHER_MUX_AUTO,
      95             : };
      96             : 
      97             : enum
      98             : {
      99             :         DASHER_MPHA_NO=0,
     100             :         DASHER_MPHA_COMP_ONLY,
     101             :         DASHER_MPHA_ALL
     102             : };
     103             : 
     104             : enum
     105             : {
     106             :         DASHER_FWD_NO = 0,
     107             :         DASHER_FWD_SEGS,
     108             :         DASHER_FWD_ALL,
     109             : };
     110             : 
     111             : enum
     112             : {
     113             :         //unknown samples sync state at startup
     114             :         DASHER_SYNC_UNKNOWN=0,
     115             :         //all samples sync
     116             :         DASHER_SYNC_NONE,
     117             :         //some samples sync
     118             :         DASHER_SYNC_PRESENT,
     119             : };
     120             : 
     121             : enum
     122             : {
     123             :         DASHER_CMAF_NONE=0,
     124             :         DASHER_CMAF_CMFC,
     125             :         DASHER_CMAF_CMF2
     126             : };
     127             : 
     128             : typedef struct
     129             : {
     130             :         u32 bs_switch, profile, cp, ntp;
     131             :         s32 subs_sidx;
     132             :         s32 buf, timescale;
     133             :         Bool sfile, sseg, no_sar, mix_codecs, stl, tpl, align, sap, no_frag_def, sidx, split, hlsc, strict_cues, force_flush, last_seg_merge;
     134             :         u32 mha_compat;
     135             :         u32 strict_sap;
     136             :         u32 pssh;
     137             :         u32 cmaf;
     138             :         GF_Fraction segdur;
     139             :         u32 dmode;
     140             :         char *template;
     141             :         char *segext;
     142             :         char *initext;
     143             :         u32 muxtype;
     144             :         char *profX;
     145             :         Double asto;
     146             :         char *ast;
     147             :         char *state;
     148             :         char *cues;
     149             :         char *title, *source, *info, *cprt, *lang;
     150             :         GF_PropStringList location, base;
     151             :         Bool check_dur, skip_seg, loop, reschedule, scope_deps;
     152             :         Double refresh, tsb, subdur;
     153             :         u64 *_p_gentime, *_p_mpdtime;
     154             :         Bool m2ts;
     155             :         Bool cmpd, dual, sreg, pswitch;
     156             :         char *styp;
     157             :         Bool sigfrag;
     158             :         u32 sbound;
     159             :         char *utcs;
     160             :         char *mname;
     161             :         char *hlsdrm;
     162             :         u32 llhls;
     163             :         //inherited from mp4mx
     164             :         GF_Fraction cdur;
     165             : 
     166             :         //internal
     167             :         Bool in_error;
     168             : 
     169             :         //Manifest output pid
     170             :         GF_FilterPid *opid;
     171             : 
     172             :         GF_FilterPid *opid_alt;
     173             :         GF_Filter *alt_dst;
     174             :         Bool opid_alt_m3u8;
     175             : 
     176             :         GF_MPD *mpd;
     177             : 
     178             :         GF_DasherPeriod *current_period, *next_period;
     179             :         GF_List *pids;
     180             :         Bool template_use_source;
     181             :         s32 period_idx;
     182             : 
     183             :         GF_List *tpl_records;
     184             :         Bool use_xlink, use_cenc, check_main_role;
     185             : 
     186             :         //options for muxers, constrained by profile
     187             :         Bool no_fragments_defaults;
     188             : 
     189             :         Bool is_eos;
     190             :         u32 nb_seg_url_pending;
     191             :         u64 last_evt_check_time;
     192             :         Bool on_demand_done;
     193             :         Bool subdur_done;
     194             :         char *out_path;
     195             : 
     196             :         GF_Err setup_failure;
     197             : 
     198             :         Double nb_secs_to_discard;
     199             :         Bool first_context_load, store_init_params;
     200             :         Bool do_m3u8, do_mpd;
     201             :         Bool is_period_restore, is_empty_period;
     202             : 
     203             :         Bool store_seg_states;
     204             : 
     205             :         GF_List *postponed_pids;
     206             :         u32 last_dyn_period_id;
     207             :         u32 next_pid_id_in_period;
     208             :         Bool post_play_events;
     209             : 
     210             :         Bool force_period_switch;
     211             :         Bool period_not_ready;
     212             :         Bool check_connections;
     213             : 
     214             :         //-1 forces report update, otherwise this is a packet count
     215             :         s32 update_report;
     216             : 
     217             :         Bool purge_segments;
     218             : 
     219             :         Bool is_playing;
     220             : 
     221             :         Bool no_seg_dur;
     222             : 
     223             :         Bool utc_initialized;
     224             :         DasherUTCTimingType utc_timing_type;
     225             :         s32 utc_diff;
     226             : 
     227             :         Bool is_route;
     228             : 
     229             :         Bool force_hls_ll_manifest;
     230             : 
     231             :         u32 forward_mode;
     232             :         
     233             :         u8 last_hls_signature[GF_SHA1_DIGEST_SIZE], last_mpd_signature[GF_SHA1_DIGEST_SIZE], last_hls2_signature[GF_SHA1_DIGEST_SIZE];
     234             : 
     235             :         GF_CryptInfo *cinfo;
     236             : 
     237             :         Bool use_cues;
     238             :         Bool dyn_rate;
     239             : } GF_DasherCtx;
     240             : 
     241             : typedef enum
     242             : {
     243             :         DASHER_HDR_NONE=0,
     244             :         DASHER_HDR_PQ10,
     245             :         DASHER_HDR_HLG,
     246             : } DasherHDRType;
     247             : 
     248             : typedef struct _dash_stream
     249             : {
     250             :         GF_FilterPid *ipid, *opid;
     251             : 
     252             :         //stream properties
     253             :         u32 codec_id, timescale, stream_type, dsi_crc, dsi_enh_crc, id, dep_id, src_id;
     254             :         GF_Fraction sar, fps;
     255             :         u32 width, height;
     256             :         u32 sr, nb_ch;
     257             :         char *lang;
     258             :         Bool interlaced;
     259             :         const GF_PropertyValue *p_role;
     260             :         const GF_PropertyValue *p_period_desc;
     261             :         const GF_PropertyValue *p_as_desc;
     262             :         const GF_PropertyValue *p_as_any_desc;
     263             :         const GF_PropertyValue *p_rep_desc;
     264             :         const GF_PropertyValue *p_base_url;
     265             :         char *template;
     266             :         char *xlink;
     267             :         char *hls_vp_name;
     268             :         u32 nb_surround, nb_lfe;
     269             :         u64 ch_layout;
     270             :         GF_PropVec4i srd;
     271             :         DasherHDRType hdr_type;
     272             :         Bool sscale;
     273             : 
     274             :         //TODO: get the values for all below
     275             :         u32 view_id;
     276             :         //end of TODO
     277             : 
     278             : 
     279             :         u32 bitrate;
     280             :         GF_DasherPeriod *period;
     281             :         GF_MPD_Period *last_period;
     282             : 
     283             :         GF_Fraction dash_dur;
     284             : 
     285             :         char *period_id;
     286             :         char *period_continuity_id;
     287             :         GF_Fraction64 period_start;
     288             :         GF_Fraction64 period_dur;
     289             :         //0: not done, 1: eos/abort, 2: subdur exceeded
     290             :         u32 done;
     291             :         Bool seg_done;
     292             : 
     293             :         u32 nb_comp, nb_comp_done;
     294             : 
     295             :         u32 nb_rep, nb_rep_done;
     296             :         Double set_seg_duration;
     297             : 
     298             :         //repID for this stream, generated if not found
     299             :         char *rep_id;
     300             :         //AS ID for this stream, may be 0
     301             :         u32 as_id;
     302             :         u32 sync_as_id;
     303             :         struct _dash_stream *muxed_base;
     304             :         GF_List *complementary_streams;
     305             :         GF_List *comp_pids;
     306             : 
     307             :         //the one and only representation element
     308             :         GF_MPD_Representation *rep;
     309             :         //the parent adaptation set
     310             :         GF_MPD_AdaptationSet *set;
     311             :         Bool owns_set;
     312             :         //set to true to use inband params
     313             :         Bool inband_params;
     314             :         GF_List *multi_pids;
     315             :         GF_List *multi_tracks;
     316             :         //in case we share the same init segment, we MUST use the same timescale
     317             :         u32 force_timescale;
     318             : 
     319             : 
     320             :         u32 startNumber, seg_number;
     321             :         Bool rep_init;
     322             :         u64 first_cts;
     323             :         u64 first_dts;
     324             :         s64 pts_minus_cts;
     325             :         Bool is_encrypted;
     326             : 
     327             :         //target MPD timescale
     328             :         u32 mpd_timescale;
     329             :         //segment start time in target MPD timescale
     330             :         u64 seg_start_time;
     331             :         Bool split_set_names;
     332             :         Bool skip_tpl_reuse;
     333             :         u64 max_period_dur;
     334             : 
     335             :         GF_Filter *dst_filter;
     336             : 
     337             :         char *src_url;
     338             : 
     339             :         char *init_seg, *seg_template, *idx_template;
     340             :         u32 nb_sap_3, nb_sap_4;
     341             :         //ID of output pid (renumbered), used for content component and making sure output muxers use the same IDs
     342             :         u32 pid_id;
     343             :         //dependency ID of output pid (renumbered)
     344             :         u32 dep_pid_id;
     345             :         u32 nb_samples_in_source;
     346             :         u32 sync_points_type;
     347             :         //seg urls not yet handled (waiting for size/index callbacks)
     348             :         GF_List *pending_segment_urls;
     349             :         //segment states not yet handled (waiting for size/index/etc callbacks), used for M3U8 and state mode
     350             :         GF_List *pending_segment_states;
     351             :         //next segment start time in this stream timescale (NOT MPD timescale)
     352             :         u64 next_seg_start;
     353             :         //adjusted next segment start time in this stream timescale (NOT MPD timescale)
     354             :         //the value is the same as next_seg_start until the end of segment is found (SAP)
     355             :         //in which case it is adjusted to the SAP time
     356             :         u64 adjusted_next_seg_start;
     357             : 
     358             :         //force representation time end in this stream timescale (NOT MPD timescale)
     359             :         u64 force_rep_end;
     360             : 
     361             :         Bool segment_started;
     362             :         u64 first_cts_in_seg;
     363             :         u64 first_cts_in_next_seg;
     364             :         //used for last segment computation of segmentTimeline
     365             :         u64 est_first_cts_in_next_seg;
     366             :         u64 last_cts, last_dts;
     367             :         u64 cumulated_dur;
     368             :         Double cumulated_subdur;
     369             :         Bool subdur_done;
     370             :         u64 subdur_forced_use_period_dur;
     371             :         u64 nb_pck;
     372             :         u64 est_next_dts;
     373             :         u64 seek_to_pck;
     374             :         u64 ts_offset;
     375             :         u32 nb_repeat;
     376             : 
     377             :         Bool splitable;
     378             :         u32 loop_state;
     379             :         u32 split_dur_next;
     380             : 
     381             :         u32 moof_sn_inc, moof_sn;
     382             :         GF_Fraction64 clamped_dur;
     383             : 
     384             :         u32 nb_segments_purged;
     385             :         Double dur_purged;
     386             :         Bool tile_base;
     387             :         Bool tile_dep_id_merged;
     388             :         struct _dash_stream *merged_tile_dep;
     389             : 
     390             :         u32 cues_timescale;
     391             :         u32 nb_cues;
     392             :         GF_DASHCueInfo *cues;
     393             :         Bool cues_use_edits;
     394             :         s32 cues_ts_offset;
     395             :         Bool inband_cues;
     396             :         
     397             :         Bool clamp_done;
     398             :         u32 dcd_not_ready;
     399             : 
     400             :         Bool reschedule;
     401             : 
     402             :         GF_Fraction64 duration;
     403             :         GF_List *packet_queue;
     404             :         u32 nb_sap_in_queue;
     405             :         u32 sbound;
     406             : 
     407             :         u32 request_period_switch;
     408             : 
     409             :         //gm_ for gen manifest
     410             :         Double gm_duration_total, gm_duration_min, gm_duration_max;
     411             :         u32 gm_nb_segments;
     412             : 
     413             :         Bool no_seg_dur;
     414             :         //for route
     415             :         u64 hls_ref_id;
     416             :         GF_DASH_SegmentContext *current_seg_state;
     417             : 
     418             :         Bool transcode_detected;
     419             : 
     420             :         //HLS full seg encryption
     421             :         GF_CryptInfo *cinfo;
     422             :         GF_TrackCryptInfo *tci;
     423             :         u64 iv_low, iv_high;
     424             :         u32 key_idx;
     425             :         u32 nb_crypt_seg;
     426             : 
     427             :         Bool dyn_bitrate;
     428             :         u64 rate_first_dts_plus_one, rate_last_dts;
     429             :         u64 rate_media_size;
     430             : 
     431             :         u64 period_continuity_next_cts;
     432             : } GF_DashStream;
     433             : 
     434             : static void dasher_flush_segment(GF_DasherCtx *ctx, GF_DashStream *ds, Bool is_last_in_period);
     435             : static void dasher_update_rep(GF_DasherCtx *ctx, GF_DashStream *ds);
     436             : static void dasher_reset_stream(GF_Filter *filter, GF_DashStream *ds, Bool is_destroy);
     437             : static void dasher_update_period_duration(GF_DasherCtx *ctx, Bool is_period_switch);
     438             : static GF_Err dasher_setup_period(GF_Filter *filter, GF_DasherCtx *ctx, GF_DashStream *for_ds);
     439             : 
     440         488 : static GF_DasherPeriod *dasher_new_period()
     441             : {
     442             :         GF_DasherPeriod *period;
     443         488 :         GF_SAFEALLOC(period, GF_DasherPeriod);
     444         488 :         if (period)
     445         488 :                 period->streams = gf_list_new();
     446         488 :         return period;
     447             : }
     448             : 
     449             : #ifndef GPAC_DISABLE_AV_PARSERS
     450         126 : static GF_Err dasher_get_audio_info_with_m4a_sbr_ps(GF_DashStream *ds, const GF_PropertyValue *dsi, u32 *SampleRate, u32 *Channels)
     451             : {
     452             :         GF_M4ADecSpecInfo a_cfg;
     453             :         GF_Err e;
     454         126 :         if (SampleRate) *SampleRate = ds->sr;
     455         126 :         if (Channels) *Channels = ds->nb_ch;
     456             : 
     457         126 :         if (!dsi) {
     458           0 :                 if (!ds->dcd_not_ready) {
     459           0 :                         GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] missing AAC config\n"));
     460             :                 }
     461             :                 return GF_OK;
     462             :         }
     463         126 :         e = gf_m4a_get_config(dsi->value.data.ptr, dsi->value.data.size, &a_cfg);
     464         126 :         if (e) {
     465           0 :                 GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] corrupted AAC Config, %s\n", gf_error_to_string(e)));
     466             :                 return GF_NOT_SUPPORTED;
     467             :         }
     468         126 :         if (SampleRate && a_cfg.has_sbr) {
     469           0 :                 *SampleRate = a_cfg.sbr_sr;
     470             :         }
     471         126 :         if (Channels) *Channels = a_cfg.nb_chan;
     472             :         return e;
     473             : }
     474             : #endif
     475             : 
     476             : 
     477         250 : static void dasher_check_outpath(GF_DasherCtx *ctx)
     478             : {
     479         250 :         if (!ctx->out_path) {
     480         248 :                 ctx->out_path = gf_filter_pid_get_destination(ctx->opid);
     481         248 :                 if (!ctx->out_path) return;
     482             : 
     483         244 :                 if (ctx->mname) {
     484           0 :                         char *sep = strstr(ctx->out_path, "://");
     485           0 :                         if (sep) {
     486           0 :                                 char *opath = gf_url_concatenate(ctx->out_path, ctx->mname);
     487           0 :                                 if (opath) {
     488           0 :                                         gf_free(ctx->out_path);
     489           0 :                                         ctx->out_path = opath;
     490             :                                 }
     491             :                         }
     492             :                 }
     493             :                 //check if we have a route/atsc output, in which we will case assign hls ref prop
     494         244 :                 if (!strncmp(ctx->out_path, "route://", 8) || !strncmp(ctx->out_path, "atsc://", 7))
     495           4 :                         ctx->is_route = GF_TRUE;
     496             :         }
     497             :         //for routeout
     498         246 :         if (ctx->opid)
     499         246 :                 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_URL, &PROP_STRING(ctx->out_path) );
     500         246 :         if (ctx->opid_alt)
     501           2 :                 gf_filter_pid_set_property(ctx->opid_alt, GF_PROP_PID_URL, &PROP_STRING(ctx->out_path) );
     502             : }
     503             : 
     504             : 
     505         577 : static GF_Err dasher_hls_setup_crypto(GF_DasherCtx *ctx, GF_DashStream *ds)
     506             : {
     507             :         GF_Err e;
     508             :         u32 pid_id=1;
     509             :         u32 i, count;
     510             :         const GF_PropertyValue *p;
     511             :         GF_CryptInfo *cinfo = NULL;
     512         577 :         const char *drm = ctx->hlsdrm;
     513         577 :         if (!ctx->do_m3u8) return GF_OK;
     514          60 :         if (ds->is_encrypted) return GF_OK;
     515          31 :         p = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_CRYPT_INFO);
     516          31 :         if (p)
     517           0 :                 drm = p->value.string;
     518             :         else
     519          31 :                 cinfo = ctx->cinfo;
     520          31 :         if (!drm && !cinfo) return GF_OK;
     521             : 
     522           0 :         if (ctx->sfile) {
     523           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[Dasher] Cannot use HLS segment encryption with single file output\n"));
     524             :                 return GF_BAD_PARAM;
     525             :         }
     526             : 
     527           0 :         if (!cinfo) {
     528           0 :                 cinfo = gf_crypt_info_load(drm, &e);
     529           0 :                 if (!cinfo) {
     530           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[Dasher] Cannot load HLS DRM file %s\n", drm ));
     531           0 :                         return e;
     532             :                 }
     533           0 :                 if (p) {
     534           0 :                         if (ds->cinfo) gf_crypt_info_del(ds->cinfo);
     535           0 :                         ds->cinfo = cinfo;
     536             :                 } else {
     537           0 :                         ctx->cinfo = cinfo;
     538             :                 }
     539             :         }
     540           0 :         ds->tci = NULL;
     541           0 :         p = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_ID);
     542           0 :         if (p) pid_id = p->value.uint;
     543           0 :         count = gf_list_count(cinfo->tcis);
     544           0 :         for (i=0; i<count; i++) {
     545           0 :                 GF_TrackCryptInfo *tci = gf_list_get(cinfo->tcis, i);
     546           0 :                 if (tci->trackID && (tci->trackID != pid_id)) continue;
     547             : 
     548           0 :                 ds->tci = tci;
     549           0 :                 break;
     550             :         }
     551           0 :         if (!ds->tci) return GF_OK;
     552             : 
     553           0 :         ds->key_idx = 0;
     554           0 :         ds->iv_low = ds->iv_high = 0;
     555           0 :         for (i=0; i<8; i++) {
     556           0 :                 ds->iv_high |= ds->tci->keys[ds->key_idx].IV[i];
     557           0 :                 ds->iv_low |= ds->tci->keys[ds->key_idx].IV[i + 8];
     558           0 :                 if (i<7) {
     559           0 :                         ds->iv_high <<= 8;
     560           0 :                         ds->iv_low <<= 8;
     561             :                 }
     562             :         }
     563             : 
     564             :         return GF_OK;
     565             : }
     566             : 
     567          78 : static u32 dasher_get_dep_bitrate(GF_DasherCtx *ctx, GF_DashStream *ds)
     568             : {
     569          78 :         u32 bitrate = ds->bitrate;
     570          78 :         if (ds->dep_id) {
     571          39 :                 u32 i, count = gf_list_count(ctx->current_period->streams);
     572         587 :                 for (i=0; i<count; i++) {
     573         548 :                         GF_DashStream *a_ds = gf_list_get(ctx->current_period->streams, i);
     574         548 :                         if (a_ds == ds) continue;
     575             : 
     576         509 :                         if (gf_list_find(a_ds->complementary_streams, ds)>=0) {
     577             : 
     578          39 :                                 bitrate += dasher_get_dep_bitrate(ctx, a_ds);
     579             :                         }
     580             :                 }
     581             :         }
     582          78 :         return bitrate;
     583             : }
     584             : 
     585         282 : static void dasher_update_bitrate(GF_DasherCtx *ctx, GF_DashStream *ds)
     586             : {
     587             :         u64 rate;
     588             :         u32 scaler;
     589         282 :         if (!ds->dyn_bitrate || ds->bitrate) {
     590             :                 return;
     591             :         }
     592             : 
     593         215 :         if (!ds->rate_first_dts_plus_one) {
     594           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Couldn't compute bitrate in time for manifest generation, please report to GPAC devs !\n"));
     595             :                 return;
     596             :         }
     597             : 
     598         215 :         rate = ds->rate_media_size;
     599         215 :         rate *= 8;
     600         215 :         if (ds->rate_last_dts > ds->rate_first_dts_plus_one - 1) {
     601         215 :                 rate *= ds->timescale;
     602         215 :                 rate /= (ds->rate_last_dts - ds->rate_first_dts_plus_one + 1);
     603             :         } else {
     604           0 :                 rate *= ds->dash_dur.den;
     605           0 :                 rate /= ds->dash_dur.num;
     606             :         }
     607             :         //express rates in 100kbps or 10kbps, and if ds is done, trust the average, otherwise add 10%
     608         215 :         scaler = (rate > 1000000) ? 100000 : 10000;
     609         215 :         if (rate > 10*scaler) {
     610         187 :                 rate /= scaler;
     611         187 :                 if (!ds->done) scaler = 11 * scaler / 10;
     612         187 :                 rate *= scaler;
     613             :         }
     614             : 
     615         215 :         ds->bitrate = (u32) rate;
     616             : 
     617         215 :         if (ds->rep)
     618         210 :                 ds->rep->bandwidth = ds->bitrate;
     619             : 
     620         215 :         if (ds->dep_id) {
     621           0 :                 ds->rep->bandwidth = dasher_get_dep_bitrate(ctx, ds);
     622         215 :         } else if (ds->nb_comp && !ds->muxed_base) {
     623         210 :                 u32 i, count = gf_list_count(ctx->current_period->streams);
     624         443 :                 for (i=0; i<count; i++) {
     625         233 :                         GF_DashStream *a_ds = gf_list_get(ctx->current_period->streams, i);
     626         233 :                         if (ds == a_ds) continue;;
     627          23 :                         if (a_ds->muxed_base != ds) continue;
     628           5 :                         if (a_ds->dyn_bitrate) {
     629           5 :                                 dasher_update_bitrate(ctx, a_ds);
     630             :                         }
     631           5 :                         ds->rep->bandwidth += a_ds->bitrate;
     632             :                 }
     633             :         }
     634             : 
     635             :         //keep refreshing our rate estimation
     636         215 :         if (!ds->done && (ds->dyn_bitrate==1))
     637         157 :                 ds->bitrate = 0;
     638             : }
     639             : 
     640             : 
     641         357 : static GF_Err dasher_stream_period_changed(GF_Filter *filter, GF_DasherCtx *ctx, GF_DashStream *ds, Bool is_new_period_request)
     642             : {
     643             :         //period switching, check the stream is still scheduled - if so and not done, flush it, update period duration
     644         357 :         s32 res = gf_list_find(ctx->current_period->streams, ds);
     645             :         //force end of segment if stream is not yet done and in current period
     646         357 :         if ((res>=0) && !ds->done && !ds->seg_done) {
     647             :                 GF_DashStream *base_ds;
     648             : 
     649           0 :                 base_ds = ds->muxed_base ? ds->muxed_base : ds;
     650           0 :                 if (is_new_period_request) {
     651           0 :                         GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[Dasher] New period requested during PID %s reconfiguration\n", gf_filter_pid_get_name(ds->ipid) ));
     652             :                 } else {
     653           0 :                         GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] PID %s config changed during active period, forcing period switch\n", gf_filter_pid_get_name(ds->ipid) ));
     654             :                 }
     655           0 :                 ds->seg_done = GF_TRUE;
     656           0 :                 if(base_ds->nb_comp_done >= base_ds->nb_comp) {
     657           0 :                         GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Invalid new period: %u components processed (max %u expected)\n", base_ds->nb_comp_done, base_ds->nb_comp));
     658             :                         return GF_BAD_PARAM;
     659             :                 }
     660           0 :                 base_ds->nb_comp_done ++;
     661           0 :                 ds->first_cts_in_next_seg = ds->est_first_cts_in_next_seg;;
     662             : 
     663           0 :                 if (base_ds->nb_comp_done == base_ds->nb_comp) {
     664           0 :                         dasher_flush_segment(ctx, base_ds, GF_TRUE);
     665             :                 }
     666             : 
     667           0 :                 ctx->force_period_switch = GF_TRUE;
     668           0 :                 dasher_update_period_duration(ctx, GF_TRUE);
     669             :         }
     670             :         //remove stream from period
     671         357 :         if (res>=0) {
     672             :                 //force an EOS on this stream for ondemand / side index generation flush
     673           0 :                 if (ds->opid)
     674           0 :                         gf_filter_pid_set_eos(ds->opid);
     675           0 :                 ds->rep_init = GF_FALSE;
     676           0 :                 gf_list_rem(ctx->current_period->streams, res);
     677             :         }
     678         357 :         ds->request_period_switch = 0;
     679             : 
     680             :         //this is tricky, when reassigning period IDs in the middle of a stream, we may have cases where some streams
     681             :         //are ready several packets before other streams due to processing delay, which results in period switch signal not
     682             :         //happening at the same time
     683         357 :         if (is_new_period_request && !ds->rep && ctx->current_period->period && gf_list_count(ctx->current_period->streams)) {
     684             :                 Bool inject_in_period = GF_FALSE;
     685           0 :                 if (ds->period_id && ctx->current_period->period->ID && !strcmp(ds->period_id, ctx->current_period->period->ID))
     686             :                         inject_in_period = GF_TRUE;
     687           0 :                 else if ((ctx->period_idx>0) && (ds->period_start.num<0) && ((s32) -ds->period_start.num == ctx->period_idx))
     688             :                         inject_in_period = GF_TRUE;
     689           0 :                 else if (ds->period_start.num * 1000 == ctx->current_period->period->start * ds->period_start.den)
     690             :                         inject_in_period = GF_TRUE;
     691             : 
     692             :                 if (inject_in_period) {
     693           0 :                         gf_list_add(ctx->current_period->streams, ds);
     694           0 :                         ds->period = ctx->current_period;
     695           0 :                         dasher_setup_period(filter, ctx, ds);
     696             :                         //force a MPD publish asap
     697           0 :                         if (ctx->dmode != GF_DASH_STATIC)
     698           0 :                                 ctx->mpd->publishTime = 0;
     699             :                         return GF_OK;
     700             :                 }
     701             :         }
     702         357 :         gf_list_add(ctx->next_period->streams, ds);
     703         357 :         ds->period = ctx->next_period;
     704         357 :         return GF_OK;
     705             : }
     706             : 
     707          32 : static void dasher_send_encode_hints(GF_DasherCtx *ctx, GF_DashStream *ds)
     708             : {
     709          32 :         if (!ctx->sfile && !ctx->stl && !ctx->use_cues) {
     710             :                 GF_FilterEvent evt;
     711          32 :                 GF_FEVT_INIT(evt, GF_FEVT_ENCODE_HINTS, ds->ipid)
     712          32 :                 evt.encode_hints.intra_period = ds->dash_dur;
     713          32 :                 gf_filter_pid_send_event(ds->ipid, &evt);
     714             :         }
     715          32 : }
     716             : 
     717         577 : static GF_Err dasher_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
     718             : {
     719             :         Bool period_switch = GF_FALSE;
     720             :         const GF_PropertyValue *p, *dsi=NULL;
     721             :         u32 dc_crc, dc_enh_crc;
     722             :         GF_Err e;
     723             :         GF_DashStream *ds;
     724             :         Bool old_period_switch;
     725             :         u32 prev_stream_type;
     726             :         Bool new_period_request = GF_FALSE;
     727             :         const char *cue_file=NULL;
     728             :         s64 old_clamp_dur = 0;
     729         577 :         GF_DasherCtx *ctx = gf_filter_get_udta(filter);
     730             : 
     731         577 :         if (is_remove) {
     732           0 :                 ds = gf_filter_pid_get_udta(pid);
     733           0 :                 if (ds) {
     734           0 :                         if (ds->dyn_bitrate) dasher_update_bitrate(ctx, ds);
     735           0 :                         gf_list_del_item(ctx->pids, ds);
     736           0 :                         gf_list_del_item(ctx->current_period->streams, ds);
     737           0 :                         if (ctx->next_period)
     738           0 :                                 gf_list_del_item(ctx->next_period->streams, ds);
     739           0 :                         dasher_reset_stream(filter, ds, GF_TRUE);
     740           0 :                         gf_free(ds);
     741             :                 }
     742             :                 return GF_OK;
     743             :         }
     744         577 :         ctx->check_connections = GF_TRUE;
     745         577 :         if (!ctx->opid) {
     746         244 :                 u32 i, nb_opids = ctx->dual ? 2 : 1;
     747         490 :                 for (i=0; i < nb_opids; i++) {
     748             :                         char *segext=NULL;
     749             :                         char *force_ext=NULL;
     750             :                         GF_FilterPid *opid;
     751         246 :                         if (i==0) {
     752         244 :                                 ctx->opid = gf_filter_pid_new(filter);
     753         244 :                                 gf_filter_pid_set_name(ctx->opid, "MANIFEST");
     754         244 :                                 opid = ctx->opid;
     755             :                         } else {
     756           2 :                                 if (!ctx->alt_dst && ctx->out_path) {
     757             :                                         char szSRC[100];
     758             :                                         GF_FileIO *gfio = NULL;
     759             :                                         char *mpath = ctx->out_path;
     760             :                                         u32 len;
     761           2 :                                         if (!strncmp(mpath, "gfio://", 7)) {
     762           0 :                                                 gfio = gf_fileio_from_url(mpath);
     763           0 :                                                 if (!gfio) return GF_BAD_PARAM;
     764             :                                                 //only use basename as we will create the new resource through factory
     765           0 :                                                 mpath = (char *) gf_file_basename(gf_fileio_resource_url(gfio));
     766           0 :                                                 if (!mpath) return GF_OUT_OF_MEM;
     767             :                                         }
     768             : 
     769           2 :                                         len = (u32) strlen(mpath);
     770           2 :                                         char *out_path = gf_malloc(len+10);
     771           2 :                                         if (!out_path) return GF_OUT_OF_MEM;
     772             :                                         memcpy(out_path, mpath, len);
     773           2 :                                         out_path[len]=0;
     774           2 :                                         char *sep = gf_file_ext_start(out_path);
     775           2 :                                         if (sep) sep[0] = 0;
     776           2 :                                         if (ctx->do_m3u8) {
     777             :                                                 strcat(out_path, ".mpd");
     778             :                                                 force_ext = "mpd";
     779             :                                         } else {
     780           0 :                                                 ctx->opid_alt_m3u8 = GF_TRUE;
     781           0 :                                                 ctx->do_m3u8 = GF_TRUE;
     782             :                                                 strcat(out_path, ".m3u8");
     783             :                                                 force_ext = "m3u8";
     784             :                                         }
     785           2 :                                         if (gfio) {
     786           0 :                                                 const char *rel = gf_fileio_factory(gfio, out_path);
     787           0 :                                                 gf_free(out_path);
     788           0 :                                                 out_path = gf_strdup(rel);
     789           0 :                                                 if (!out_path) return GF_OUT_OF_MEM;
     790             :                                         }
     791             : 
     792           2 :                                         ctx->alt_dst = gf_filter_connect_destination(filter, out_path, &e);
     793           2 :                                         if (e) {
     794           0 :                                                 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Couldn't create secondary manifest output %s: %s\n", out_path, gf_error_to_string(e) ));
     795           0 :                                                 gf_free(out_path);
     796           0 :                                                 break;
     797             :                                         }
     798           2 :                                         gf_free(out_path);
     799             : 
     800             :                                         //reset any sourceID given in the dst_arg and assign sourceID to be the dasher filter
     801           2 :                                         gf_filter_reset_source(ctx->alt_dst);
     802           2 :                                         snprintf(szSRC, 100, "MuxSrc%cdasher_%p", gf_filter_get_sep(filter, GF_FS_SEP_NAME), ctx->alt_dst);
     803           2 :                                         gf_filter_set_source(ctx->alt_dst, filter, szSRC);
     804             : 
     805           2 :                                         ctx->opid_alt = gf_filter_pid_new(filter);
     806           2 :                                         gf_filter_pid_set_name(ctx->opid_alt, "MANIFEST_ALT");
     807             : 
     808           2 :                                         snprintf(szSRC, 100, "dasher_%p", ctx->alt_dst);
     809           2 :                                         gf_filter_pid_set_property(ctx->opid_alt, GF_PROP_PID_MUX_SRC, &PROP_STRING(szSRC) );
     810             :                                         //we also need to set the property on main output just to avoid the connection
     811             :                                         snprintf(szSRC, 100, "dasher_%p", ctx);
     812           2 :                                         gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_MUX_SRC, &PROP_STRING(szSRC) );
     813             :                                 }
     814           2 :                                 opid = ctx->opid_alt;
     815             :                         }
     816         246 :                         if (!opid)
     817           0 :                                 continue;
     818             : 
     819             :                         //copy properties at init or reconfig
     820         246 :                         gf_filter_pid_copy_properties(opid, pid);
     821         246 :                         gf_filter_pid_set_property(opid, GF_PROP_PID_DECODER_CONFIG, NULL);
     822         246 :                         gf_filter_pid_set_property(opid, GF_PROP_PID_DECODER_CONFIG_ENHANCEMENT, NULL);
     823         246 :                         gf_filter_pid_set_property(opid, GF_PROP_PID_CODECID, NULL);
     824         246 :                         gf_filter_pid_set_property(opid, GF_PROP_PID_UNFRAMED, NULL);
     825         246 :                         gf_filter_pid_set_property(opid, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(GF_STREAM_FILE) );
     826             :                         //for routeout
     827         246 :                         gf_filter_pid_set_property(opid, GF_PROP_PID_ORIG_STREAM_TYPE, &PROP_UINT(GF_STREAM_FILE) );
     828             : 
     829         246 :                         dasher_check_outpath(ctx);
     830             : 
     831         246 :                         p = gf_filter_pid_caps_query(pid, GF_PROP_PID_FILE_EXT);
     832         246 :                         if (p) {
     833           0 :                                 gf_filter_pid_set_property(opid, GF_PROP_PID_FILE_EXT, p );
     834           0 :                                 segext = p->value.string;
     835             :                         } else {
     836             :                                 segext = NULL;
     837         246 :                                 if (ctx->out_path) {
     838         242 :                                         segext = gf_file_ext_start(ctx->out_path);
     839           4 :                                 } else if (ctx->mname) {
     840           0 :                                         segext = gf_file_ext_start(ctx->mname);
     841             :                                 }
     842         242 :                                 if (!segext) segext = "mpd";
     843         242 :                                 else segext++;
     844         246 :                                 if (force_ext)
     845             :                                         segext = force_ext;
     846         246 :                                 gf_filter_pid_set_property(opid, GF_PROP_PID_FILE_EXT, &PROP_STRING(segext) );
     847             : 
     848         246 :                                 if (!strcmp(segext, "m3u8")) {
     849             : 
     850          24 :                                         gf_filter_pid_set_property(opid, GF_PROP_PID_MIME, &PROP_STRING("video/mpegurl"));
     851             :                                 } else {
     852         222 :                                         gf_filter_pid_set_property(opid, GF_PROP_PID_MIME, &PROP_STRING("application/dash+xml"));
     853             :                                 }
     854             :                         }
     855             : 
     856         246 :                         if (!strcmp(segext, "m3u8")) {
     857          24 :                                 ctx->do_m3u8 = GF_TRUE;
     858          24 :                                 gf_filter_pid_set_name(opid, "manifest_m3u8" );
     859             :                         } else {
     860         222 :                                 ctx->do_mpd = GF_TRUE;
     861         222 :                                 gf_filter_pid_set_name(opid, "manifest_mpd" );
     862             :                         }
     863             :                 }
     864             : 
     865         244 :                 ctx->store_seg_states = GF_FALSE;
     866             :                 //in m3u8 mode, always store all seg states. In MPD only if state, not ondemand
     867         244 :                 if (((ctx->state || ctx->purge_segments) && !ctx->sseg) || ctx->do_m3u8) ctx->store_seg_states = GF_TRUE;
     868             :         }
     869             : 
     870         577 :         ds = gf_filter_pid_get_udta(pid);
     871         577 :         if (!ds) {
     872         357 :                 GF_SAFEALLOC(ds, GF_DashStream);
     873         357 :                 if (!ds) return GF_OUT_OF_MEM;
     874         357 :                 ds->ipid = pid;
     875         357 :                 gf_list_add(ctx->pids, ds);
     876         357 :                 ds->complementary_streams = gf_list_new();
     877             :                 period_switch = GF_TRUE;
     878         357 :                 gf_filter_pid_set_udta(pid, ds);
     879         357 :                 ds->sbound = ctx->sbound;
     880         357 :                 if (ctx->sbound!=DASHER_BOUNDS_OUT)
     881           6 :                         ds->packet_queue = gf_list_new();
     882             : 
     883             :                 /*initial connection and we already have sent play event, send a PLAY on this new PID
     884             :                 TODO: we need to send STOP/PLAY depending on period
     885             :                 */
     886         357 :                 if (ctx->is_playing) {
     887             :                         GF_FilterEvent evt;
     888             : 
     889           2 :                         dasher_send_encode_hints(ctx, ds);
     890             : 
     891           2 :                         GF_FEVT_INIT(evt, GF_FEVT_PLAY, ds->ipid);
     892           2 :                         evt.play.speed = 1.0;
     893           2 :                         gf_filter_pid_send_event(ds->ipid, &evt);
     894             :                 }
     895             :                 //don't create pid at this time
     896             :         }
     897             : 
     898         577 :         gf_filter_pid_set_framing_mode(pid, GF_TRUE);
     899             : 
     900             : #define CHECK_PROP(_type, _mem, _e) \
     901             :         p = gf_filter_pid_get_property(pid, _type); \
     902             :         if (!p && (_e<=0) ) return _e; \
     903             :         if (p && (p->value.uint != _mem) && _mem) period_switch = GF_TRUE; \
     904             :         if (p) _mem = p->value.uint; \
     905             : 
     906             : #define CHECK_PROPL(_type, _mem, _e) \
     907             :         p = gf_filter_pid_get_property(pid, _type); \
     908             :         if (!p && (_e<=0) ) return _e; \
     909             :         if (p && (p->value.longuint != _mem) && _mem) period_switch = GF_TRUE; \
     910             :         if (p) _mem = p->value.longuint; \
     911             : 
     912             : #define CHECK_PROP_BOOL(_type, _mem, _e) \
     913             :         p = gf_filter_pid_get_property(pid, _type); \
     914             :         if (!p && (_e<=0) ) return _e; \
     915             :         if (p && (p->value.boolean != _mem) && _mem) period_switch = GF_TRUE; \
     916             :         if (p) _mem = p->value.uint; \
     917             : 
     918             : #define CHECK_PROP_FRAC(_type, _mem, _e) \
     919             :         p = gf_filter_pid_get_property(pid, _type); \
     920             :         if (!p && (_e<=0) ) return _e; \
     921             :         if (p && (p->value.frac.num * _mem.den != p->value.frac.den * _mem.num) && _mem.den && _mem.num) period_switch = GF_TRUE; \
     922             :         if (p) _mem = p->value.frac; \
     923             : 
     924             : #define CHECK_PROP_FRAC64(_type, _mem, _e) \
     925             :         p = gf_filter_pid_get_property(pid, _type); \
     926             :         if (!p && (_e<=0) ) return _e; \
     927             :         if (p && (p->value.lfrac.num * _mem.den != p->value.lfrac.den * _mem.num) && _mem.den && _mem.num) period_switch = GF_TRUE; \
     928             :         if (p) _mem = p->value.lfrac; \
     929             : 
     930             : 
     931             : #define CHECK_PROP_STR(_type, _mem, _e) \
     932             :         p = gf_filter_pid_get_property(pid, _type); \
     933             :         if (!p && (_e<=0) ) return _e; \
     934             :         if (p && _mem && strcmp(_mem, p->value.string)) period_switch = GF_TRUE; \
     935             :         if (p) { \
     936             :                 if (_mem) gf_free(_mem); \
     937             :                 _mem = gf_strdup(p->value.string); \
     938             :         }\
     939             : 
     940             : 
     941             : #define CHECK_PROP_PROP(_type, _mem, _e) \
     942             :         p = gf_filter_pid_get_property(pid, _type); \
     943             :         if (!p && (_e<=0) ) return _e; \
     944             :         if (p != _mem) period_switch = GF_TRUE; \
     945             :         _mem = p; \
     946             : 
     947             : 
     948         577 :         prev_stream_type = ds->stream_type;
     949         577 :         CHECK_PROP(GF_PROP_PID_STREAM_TYPE, ds->stream_type, GF_NOT_SUPPORTED)
     950             : 
     951         577 :         ds->tile_base = GF_FALSE;
     952             : 
     953         577 :         if (ds->stream_type != GF_STREAM_FILE) {
     954         576 :                 u32 prev_bitrate = ds->bitrate;
     955         576 :                 if (ds->stream_type==GF_STREAM_ENCRYPTED) {
     956         310 :                         CHECK_PROP(GF_PROP_PID_ORIG_STREAM_TYPE, ds->stream_type, GF_EOS)
     957         310 :                         ds->is_encrypted = GF_TRUE;
     958             :                 }
     959         576 :                 if (prev_stream_type==ds->stream_type)
     960             :                         period_switch = GF_FALSE;
     961             : 
     962         576 :                 CHECK_PROP(GF_PROP_PID_BITRATE, ds->bitrate, GF_EOS)
     963         576 :                 if (!ds->bitrate && prev_bitrate) {
     964           0 :                         ds->bitrate = prev_bitrate;
     965             :                         period_switch = GF_FALSE;
     966             :                 }
     967         576 :                 if (ds->bitrate && period_switch) {
     968             :                         //allow 20% variation in bitrate, otherwise force period switch
     969         298 :                         if ((ds->bitrate <= 120 * prev_bitrate / 100) && (ds->bitrate >= 80 * prev_bitrate / 100)) {
     970             :                                 period_switch = GF_FALSE;
     971             :                         }
     972             :                 }
     973             : 
     974         576 :                 CHECK_PROP(GF_PROP_PID_CODECID, ds->codec_id, GF_NOT_SUPPORTED)
     975         576 :                 CHECK_PROP(GF_PROP_PID_TIMESCALE, ds->timescale, GF_NOT_SUPPORTED)
     976             : 
     977         576 :                 if (ds->stream_type==GF_STREAM_VISUAL) {
     978         439 :                         CHECK_PROP(GF_PROP_PID_WIDTH, ds->width, GF_EOS)
     979         439 :                         CHECK_PROP(GF_PROP_PID_HEIGHT, ds->height, GF_EOS)
     980             :                         //don't return if not defined
     981         439 :                         CHECK_PROP_FRAC(GF_PROP_PID_SAR, ds->sar, GF_EOS)
     982         439 :                         if (!ds->sar.num) ds->sar.num = ds->sar.den = 1;
     983         439 :                         CHECK_PROP_FRAC(GF_PROP_PID_FPS, ds->fps, GF_EOS)
     984             : 
     985             : 
     986         439 :                         p = gf_filter_pid_get_property(pid, GF_PROP_PID_CROP_POS);
     987         439 :                         if (p && ((p->value.vec2i.x != ds->srd.x) || (p->value.vec2i.y != ds->srd.y) ) ) period_switch = GF_TRUE;
     988         439 :                         if (p) {
     989          36 :                                 ds->srd.x = p->value.vec2i.x;
     990          36 :                                 ds->srd.y = p->value.vec2i.y;
     991          36 :                                 ds->srd.z = ds->width;
     992          36 :                                 ds->srd.w = ds->height;
     993             :                         } else {
     994         403 :                                 p = gf_filter_pid_get_property(pid, GF_PROP_PID_TILE_BASE);
     995         403 :                                 if (p) {
     996           4 :                                         ds->srd.x = ds->srd.y = 0;
     997           4 :                                         ds->srd.z = ds->width;
     998           4 :                                         ds->srd.w = ds->height;
     999           4 :                                         ds->tile_base = GF_TRUE;
    1000             :                                 }
    1001             :                         }
    1002         137 :                 } else if (ds->stream_type==GF_STREAM_AUDIO) {
    1003         135 :                         CHECK_PROP(GF_PROP_PID_SAMPLE_RATE, ds->sr, GF_EOS)
    1004         135 :                         CHECK_PROP(GF_PROP_PID_NUM_CHANNELS, ds->nb_ch, GF_EOS)
    1005         135 :                         CHECK_PROPL(GF_PROP_PID_CHANNEL_LAYOUT, ds->ch_layout, GF_EOS)
    1006             :                 }
    1007             : 
    1008             :                 old_period_switch = period_switch;
    1009             : 
    1010             :                 //these ones can change without triggering period switch
    1011         576 :                 CHECK_PROP(GF_PROP_PID_NB_FRAMES, ds->nb_samples_in_source, GF_EOS)
    1012         576 :                 CHECK_PROP_FRAC64(GF_PROP_PID_DURATION, ds->duration, GF_EOS)
    1013         576 :                 CHECK_PROP_STR(GF_PROP_PID_URL, ds->src_url, GF_EOS)
    1014             :                 period_switch = old_period_switch;
    1015             : 
    1016         576 :                 CHECK_PROP(GF_PROP_PID_ID, ds->id, GF_EOS)
    1017         576 :                 CHECK_PROP(GF_PROP_PID_DEPENDENCY_ID, ds->dep_id, GF_EOS)
    1018             : 
    1019         576 :                 p = gf_filter_pid_get_property(pid, GF_PROP_PID_HAS_SYNC);
    1020             :                 u32 sync_type = DASHER_SYNC_UNKNOWN;
    1021         576 :                 if (p) sync_type = p->value.boolean ? DASHER_SYNC_PRESENT : DASHER_SYNC_NONE;
    1022         576 :                 if (sync_type != ds->sync_points_type) period_switch = GF_TRUE;
    1023         576 :                 ds->sync_points_type = sync_type;
    1024             : 
    1025         576 :                 if (ds->inband_cues)
    1026             :                         period_switch = old_period_switch;
    1027             : 
    1028         576 :                 if (ctx->scope_deps) {
    1029         576 :                         const char *src_args = gf_filter_pid_orig_src_args(pid, GF_TRUE);
    1030         576 :                         if (src_args) {
    1031         576 :                                 ds->src_id = gf_crc_32(src_args, (u32) strlen(src_args));
    1032             :                         }
    1033             :                 }
    1034             :                 dc_crc = 0;
    1035         576 :                 dsi = p = gf_filter_pid_get_property(pid, GF_PROP_PID_DECODER_CONFIG);
    1036         576 :                 if (p && (p->type==GF_PROP_DATA))
    1037         526 :                         dc_crc = gf_crc_32(p->value.data.ptr, p->value.data.size);
    1038             : 
    1039             :                 dc_enh_crc = 0;
    1040         576 :                 p = gf_filter_pid_get_property(pid, GF_PROP_PID_DECODER_CONFIG_ENHANCEMENT);
    1041         576 :                 if (p && (p->type==GF_PROP_DATA)) dc_enh_crc = gf_crc_32(p->value.data.ptr, p->value.data.size);
    1042             : 
    1043         576 :                 if (((dc_crc != ds->dsi_crc) && ds->dsi_crc)
    1044         569 :                         || ((dc_enh_crc != ds->dsi_enh_crc) && ds->dsi_enh_crc)
    1045             :                 ) {
    1046             :                         //check which codecs can support inband param sets
    1047          11 :                         switch (ds->codec_id) {
    1048          11 :                         case GF_CODECID_AVC:
    1049             :                         case GF_CODECID_SVC:
    1050             :                         case GF_CODECID_MVC:
    1051             :                         case GF_CODECID_HEVC:
    1052             :                         case GF_CODECID_LHVC:
    1053          11 :                                 if (!ctx->bs_switch)
    1054             :                                         period_switch = GF_TRUE;
    1055             :                                 break;
    1056             :                         default:
    1057             :                                 period_switch = GF_TRUE;
    1058             :                                 break;
    1059             :                         }
    1060         565 :                 }
    1061             :                 //check if input is ready
    1062         576 :                 ds->dcd_not_ready = 0;
    1063         576 :                 if (!dc_crc && !dc_enh_crc) {
    1064          48 :                         switch (ds->codec_id) {
    1065          15 :                         case GF_CODECID_AVC:
    1066             :                         case GF_CODECID_SVC:
    1067             :                         case GF_CODECID_MVC:
    1068             :                         case GF_CODECID_HEVC:
    1069             :                         case GF_CODECID_LHVC:
    1070             :                         case GF_CODECID_AAC_MPEG4:
    1071             :                         case GF_CODECID_AAC_MPEG2_MP:
    1072             :                         case GF_CODECID_AAC_MPEG2_LCP:
    1073             :                         case GF_CODECID_AAC_MPEG2_SSRP:
    1074             :                         case GF_CODECID_USAC:
    1075             :                         case GF_CODECID_AC3:
    1076             :                         case GF_CODECID_EAC3:
    1077             :                         case GF_CODECID_AV1:
    1078             :                         case GF_CODECID_VP8:
    1079             :                         case GF_CODECID_VP9:
    1080          15 :                                 ds->dcd_not_ready = gf_sys_clock();
    1081          15 :                                 break;
    1082             :                         default:
    1083             :                                 break;
    1084             :                         }
    1085         528 :                 }
    1086         576 :                 ds->dsi_crc = dc_crc;
    1087             : 
    1088         576 :                 CHECK_PROP_STR(GF_PROP_PID_TEMPLATE, ds->template, GF_EOS)
    1089         576 :                 CHECK_PROP_STR(GF_PROP_PID_LANGUAGE, ds->lang, GF_EOS)
    1090         576 :                 CHECK_PROP_BOOL(GF_PROP_PID_INTERLACED, ds->interlaced, GF_EOS)
    1091         576 :                 CHECK_PROP_PROP(GF_PROP_PID_AS_COND_DESC, ds->p_as_desc, GF_EOS)
    1092         576 :                 CHECK_PROP_PROP(GF_PROP_PID_AS_ANY_DESC, ds->p_as_any_desc, GF_EOS)
    1093         576 :                 CHECK_PROP_PROP(GF_PROP_PID_REP_DESC, ds->p_rep_desc, GF_EOS)
    1094         576 :                 CHECK_PROP_PROP(GF_PROP_PID_BASE_URL, ds->p_base_url, GF_EOS)
    1095         576 :                 CHECK_PROP_PROP(GF_PROP_PID_ROLE, ds->p_role, GF_EOS)
    1096         576 :                 CHECK_PROP_STR(GF_PROP_PID_HLS_PLAYLIST, ds->hls_vp_name, GF_EOS)
    1097         576 :                 CHECK_PROP_BOOL(GF_PROP_PID_SINGLE_SCALE, ds->sscale, GF_EOS)
    1098             : 
    1099             : 
    1100         576 :                 if (ds->rate_first_dts_plus_one)
    1101           0 :                         dasher_update_bitrate(ctx, ds);
    1102             : 
    1103         576 :                 if (!ds->bitrate) {
    1104          65 :                         char *tpl = ds->template ? ds->template : ctx->template;
    1105          65 :                         if (tpl && strstr(tpl, "$Bandwidth$")) {
    1106           0 :                                 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] No bitrate property assigned to PID %s but template uses $Bandwidth$, cannot initialize !\n\tTry specifying bitrate property after your source, e.g. -i source.raw:#Bitrate=VAL\n", gf_filter_pid_get_name(ds->ipid)));
    1107           0 :                                 ctx->in_error = GF_TRUE;
    1108           0 :                                 return GF_BAD_PARAM;
    1109             :                         } else {
    1110          65 :                                 GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[Dasher] No bitrate property assigned to PID %s, computing from bitstream\n", gf_filter_pid_get_name(ds->ipid)));
    1111          65 :                                 ds->dyn_bitrate = GF_TRUE;
    1112          65 :                                 ds->rate_first_dts_plus_one = 0;
    1113          65 :                                 ds->rate_media_size = 0;
    1114             :                         }
    1115             :                 } else {
    1116         511 :                         ds->dyn_bitrate = GF_FALSE;
    1117             :                 }
    1118             : 
    1119         576 :                 if (!ds->src_url)
    1120           0 :                         ds->src_url = gf_strdup("file");
    1121         576 :                 ds->startNumber = 1;
    1122         576 :                 CHECK_PROP(GF_PROP_PID_START_NUMBER, ds->startNumber, GF_EOS)
    1123         576 :                 ds->dash_dur = ctx->segdur;
    1124         576 :                 ds->no_seg_dur = ctx->no_seg_dur;
    1125         576 :                 p = gf_filter_pid_get_property(pid, GF_PROP_PID_DASH_DUR);
    1126         576 :                 if (p) {
    1127          45 :                         ds->dash_dur = p->value.frac;
    1128          45 :                         ds->no_seg_dur = GF_FALSE;
    1129          45 :                         if (!ds->dash_dur.num || !ds->dash_dur.den) {
    1130           0 :                                 ds->dash_dur.num = 1;
    1131           0 :                                 ds->dash_dur.den = 1;
    1132             :                         }
    1133             :                 }
    1134             :                 //this avoids very weird cases where (u64) (dash_dur*timescale) is 0. we limit the max segment duration to 1M sec, a bit more than 11.5 days
    1135         576 :                 if ((u64) ds->dash_dur.num > (u64)ds->dash_dur.den * 1000000) {
    1136           0 :                         ds->dash_dur.num = 1000000;
    1137           0 :                         ds->dash_dur.den = 1;
    1138             :                 }
    1139             : 
    1140         576 :                 ds->splitable = GF_FALSE;
    1141         576 :                 switch (ds->stream_type) {
    1142           2 :                 case GF_STREAM_TEXT:
    1143             :                 case GF_STREAM_METADATA:
    1144             :                 case GF_STREAM_OD:
    1145             :                 case GF_STREAM_SCENE:
    1146           2 :                         ds->splitable = ctx->split;
    1147           2 :                         break;
    1148             :                 }
    1149             : 
    1150         576 :                 old_clamp_dur = ds->clamped_dur.num;
    1151         576 :                 ds->clamped_dur.num = 0;
    1152         576 :                 ds->clamped_dur.den = 1;
    1153         576 :                 p = gf_filter_pid_get_property(pid, GF_PROP_PID_CLAMP_DUR);
    1154         576 :                 if (p && p->value.lfrac.den) ds->clamped_dur = p->value.lfrac;
    1155             : 
    1156             :                 //HDR
    1157             : #if !defined(GPAC_DISABLE_AV_PARSERS)
    1158         576 :                 if (dsi) {
    1159             : #if !defined(GPAC_DISABLE_HEVC)
    1160         526 :                         if (ds->codec_id == GF_CODECID_LHVC || ds->codec_id == GF_CODECID_HEVC_TILES || ds->codec_id == GF_CODECID_HEVC) {
    1161          86 :                                 GF_HEVCConfig* hevccfg = gf_odf_hevc_cfg_read(dsi->value.data.ptr, dsi->value.data.size, GF_FALSE);
    1162          86 :                                 if (hevccfg) {
    1163             :                                         Bool is_interlaced;
    1164             :                                         HEVCState hevc;
    1165             :                                         HEVC_SPS* sps;
    1166             :                                         memset(&hevc, 0, sizeof(HEVCState));
    1167          86 :                                         gf_hevc_parse_ps(hevccfg, &hevc, GF_HEVC_NALU_VID_PARAM);
    1168          86 :                                         gf_hevc_parse_ps(hevccfg, &hevc, GF_HEVC_NALU_SEQ_PARAM);
    1169          86 :                                         sps = &hevc.sps[hevc.sps_active_idx];
    1170          86 :                                         if (sps && sps->colour_description_present_flag) {
    1171           0 :                                                 DasherHDRType old_hdr_type = ds->hdr_type;
    1172           0 :                                                 if (sps->colour_primaries == 9 && sps->matrix_coeffs == 9) {
    1173           0 :                                                         if (sps->transfer_characteristic == 14) ds->hdr_type = DASHER_HDR_HLG; //TODO: parse alternative_transfer_characteristics SEI
    1174           0 :                                                         if (sps->transfer_characteristic == 16) ds->hdr_type = DASHER_HDR_PQ10;
    1175             :                                                 }
    1176           0 :                                                 if (old_hdr_type != ds->hdr_type) period_switch = GF_TRUE;
    1177             :                                         }
    1178          86 :                                         is_interlaced = hevccfg->interlaced_source_flag ? GF_TRUE : GF_FALSE;
    1179          86 :                                         if (ds->interlaced != is_interlaced) period_switch = GF_TRUE;
    1180          86 :                                         ds->interlaced = is_interlaced;
    1181             : 
    1182          86 :                                         gf_odf_hevc_cfg_del(hevccfg);
    1183             :                                 }
    1184             :                         }
    1185             :                         else
    1186             : #endif
    1187         440 :                         if (ds->codec_id == GF_CODECID_AVC || ds->codec_id == GF_CODECID_SVC || ds->codec_id == GF_CODECID_MVC) {
    1188             :                                 AVCState avc;
    1189         213 :                                 GF_AVCConfig* avccfg = gf_odf_avc_cfg_read(dsi->value.data.ptr, dsi->value.data.size);
    1190         213 :                                 GF_NALUFFParam *sl = (GF_NALUFFParam *)gf_list_get(avccfg->sequenceParameterSets, 0);
    1191         213 :                                 if (sl) {
    1192             :                                         s32 idx;
    1193             :                                         memset(&avc, 0, sizeof(AVCState));
    1194         211 :                                         idx = gf_avc_read_sps(sl->data, sl->size, &avc, 0, NULL);
    1195         211 :                                         if (idx>=0) {
    1196         211 :                                                 Bool is_interlaced = avc.sps[idx].frame_mbs_only_flag ? GF_FALSE : GF_TRUE;
    1197         211 :                                                 if (ds->interlaced != is_interlaced) period_switch = GF_TRUE;
    1198         211 :                                                 ds->interlaced = is_interlaced;
    1199             :                                         }
    1200             :                                 }
    1201         213 :                                 gf_odf_avc_cfg_del(avccfg);
    1202             :                         }
    1203             :                 }
    1204             : #endif /*!GPAC_DISABLE_AV_PARSERS*/
    1205             : 
    1206         576 :                 if (ds->stream_type==GF_STREAM_AUDIO) {
    1207         135 :                         u32 _sr=0, _nb_ch=0;
    1208             : #ifndef GPAC_DISABLE_AV_PARSERS
    1209         135 :                         switch (ds->codec_id) {
    1210         130 :                         case GF_CODECID_AAC_MPEG4:
    1211             :                         case GF_CODECID_AAC_MPEG2_MP:
    1212             :                         case GF_CODECID_AAC_MPEG2_LCP:
    1213             :                         case GF_CODECID_AAC_MPEG2_SSRP:
    1214             :                         case GF_CODECID_USAC:
    1215             :                                 //DASH-IF and MPEG disagree here:
    1216         260 :                                 if ((ctx->profile == GF_DASH_PROFILE_AVC264_LIVE)
    1217             :                                         || (ctx->profile == GF_DASH_PROFILE_AVC264_ONDEMAND)
    1218         130 :                                         || (ctx->profile == GF_DASH_PROFILE_DASHIF_LL)
    1219             :                                 ) {
    1220           5 :                                         GF_Err res = dasher_get_audio_info_with_m4a_sbr_ps(ds, dsi, &_sr, &_nb_ch);
    1221           5 :                                         if (res) {
    1222             :                                                 //DASH-IF IOP 3.3 mandates the SBR/PS info
    1223           0 :                                                 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Could not get AAC info, %s\n", gf_error_to_string(res)));
    1224             :                                         }
    1225         125 :                                 } else if (dsi) {
    1226         121 :                                         dasher_get_audio_info_with_m4a_sbr_ps(ds, dsi, NULL, &_nb_ch);
    1227             :                                 }
    1228             :                                 break;
    1229           1 :                         case GF_CODECID_AC3:
    1230             :                         case GF_CODECID_EAC3:
    1231           1 :                                 if (dsi) {
    1232             :                                         u32 i;
    1233             :                                         GF_AC3Config ac3;
    1234           0 :                                         gf_odf_ac3_config_parse(dsi->value.data.ptr, dsi->value.data.size, (ds->codec_id==GF_CODECID_EAC3) ? GF_TRUE : GF_FALSE, &ac3);
    1235             : 
    1236           0 :                                         ds->nb_lfe = ac3.streams[0].lfon ? 1 : 0;
    1237           0 :                                         _nb_ch = gf_ac3_get_channels(ac3.streams[0].acmod);
    1238           0 :                                         for (i=0; i<ac3.streams[0].nb_dep_sub; ++i) {
    1239             :                                                 assert(ac3.streams[0].nb_dep_sub == 1);
    1240           0 :                                                 _nb_ch += gf_ac3_get_channels(ac3.streams[0].chan_loc);
    1241             :                                         }
    1242             :                                 }
    1243             :                                 break;
    1244             :                         }
    1245             : #endif
    1246         135 :                         if (_sr > ds->sr) ds->sr = _sr;
    1247         135 :                         if (_nb_ch > ds->nb_ch) ds->nb_ch = _nb_ch;
    1248             :                 }
    1249             : 
    1250             : 
    1251         576 :                 ds->pts_minus_cts = 0;
    1252         576 :                 p = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_DELAY);
    1253         576 :                 if (p && p->value.longsint) {
    1254          78 :                         ds->pts_minus_cts = p->value.longsint;
    1255             :                 }
    1256             : 
    1257             :                 //only reload queues if we detected a period switch
    1258         576 :                 if (period_switch) {
    1259         361 :                         cue_file = ctx->cues;
    1260         361 :                         p = gf_filter_pid_get_property(pid, GF_PROP_PID_DASH_CUE);
    1261         361 :                         if (p) cue_file = p->value.string;
    1262             : 
    1263         361 :                         if (ds->cues) gf_free(ds->cues);
    1264         361 :                         ds->cues = NULL;
    1265         361 :                         ds->nb_cues = 0;
    1266         361 :                         ds->inband_cues = GF_FALSE;
    1267         361 :                         if (cue_file) {
    1268          37 :                                 if (!strcmp(cue_file, "inband")) {
    1269          27 :                                         ds->inband_cues = GF_TRUE;
    1270          27 :                                         p = gf_filter_pid_get_property(pid, GF_PROP_PID_DASH_FWD);
    1271          27 :                                         if (p && p->value.uint)
    1272          27 :                                                 ctx->forward_mode = p->value.uint;
    1273             :                                 } else {
    1274          10 :                                         e = gf_mpd_load_cues(cue_file, ds->id, &ds->cues_timescale, &ds->cues_use_edits, &ds->cues_ts_offset, &ds->cues, &ds->nb_cues);
    1275          10 :                                         if (e) return e;
    1276          10 :                                         if (!ds->cues_timescale)
    1277           0 :                                                 ds->cues_timescale = ds->timescale;
    1278             :                                 }
    1279             :                         }
    1280             :                 }
    1281             :         } else {
    1282             : 
    1283           1 :                 p = gf_filter_pid_get_property(pid, GF_PROP_PID_URL);
    1284           1 :                 if (!p) p = gf_filter_pid_get_property(pid, GF_PROP_PID_FILEPATH);
    1285           1 :                 if (p) return GF_NOT_SUPPORTED;
    1286             : 
    1287           1 :                 CHECK_PROP_STR(GF_PROP_PID_XLINK, ds->xlink, GF_EOS)
    1288             :         }
    1289             : 
    1290             :         //stream representation was not yet setup but is scheduled for this period, do not trigger period switch
    1291             :         //this typically happens when we post-poned representation setup waiting for the decoder config
    1292         577 :         if (!ds->rep && (gf_list_find(ctx->current_period->streams, ds)>=0))
    1293             :                 period_switch = GF_FALSE;
    1294             : 
    1295             :         old_period_switch = period_switch;
    1296             :         period_switch = GF_FALSE;
    1297         577 :         CHECK_PROP_STR(GF_PROP_PID_PERIOD_ID, ds->period_id, GF_EOS)
    1298         577 :         CHECK_PROP_PROP(GF_PROP_PID_PERIOD_DESC, ds->p_period_desc, GF_EOS)
    1299         574 :         if (!period_switch && ctx->pswitch)
    1300             :                 period_switch = GF_TRUE;
    1301             : 
    1302         577 :         if (gf_filter_pid_get_property_str(pid, "period_switch"))
    1303             :                 period_switch = GF_TRUE;
    1304             : 
    1305         577 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_PERIOD_START);
    1306         577 :         if (p) {
    1307           3 :                 if (ds->period_start.num * p->value.lfrac.den != p->value.lfrac.num * ds->period_start.den) period_switch = GF_TRUE;
    1308           3 :                 ds->period_start = p->value.lfrac;
    1309             :         } else {
    1310         574 :                 if (ds->period_start.num) period_switch = GF_TRUE;
    1311         574 :                 ds->period_start.num = 0;
    1312         574 :                 ds->period_start.den = 1;
    1313             :         }
    1314             :         assert(ds->period_start.den);
    1315             : 
    1316         577 :         if (period_switch) {
    1317             :                 new_period_request = GF_TRUE;
    1318             :         } else {
    1319             :                 period_switch = old_period_switch;
    1320             :         }
    1321             : 
    1322         577 :         if (ds->period_continuity_id) gf_free(ds->period_continuity_id);
    1323         577 :         ds->period_continuity_id = NULL;
    1324         577 :         p = gf_filter_pid_get_property_str(ds->ipid, "period_resume");
    1325         577 :         if (!ctx->mpd || (gf_list_find(ctx->mpd->periods, ds->last_period)<0))
    1326         567 :                 ds->last_period = NULL;
    1327             : 
    1328         577 :         if (p && p->value.string && ds->last_period) {
    1329           0 :                 if (!ds->last_period->ID) {
    1330           0 :                         if (p->value.string[0]) {
    1331           0 :                                 ds->last_period->ID = p->value.string;
    1332             :                         } else {
    1333             :                                 char szPName[50];
    1334           0 :                                 sprintf(szPName, "P%d", 1 + gf_list_find(ctx->mpd->periods, ds->last_period));
    1335           0 :                                 ds->last_period->ID = gf_strdup(szPName);
    1336             :                         }
    1337             :                 }
    1338           0 :                 if (ds->set && (ds->set->id<0)) {
    1339             :                         //period may be NULL (no longer scheduled)
    1340           0 :                         if (!ds->as_id && ds->period && ds->period->period)
    1341           0 :                                 ds->as_id = gf_list_find(ds->period->period->adaptation_sets, ds->set) + 1;
    1342           0 :                         ds->set->id = ds->as_id;
    1343             :                 }
    1344           0 :                 ds->period_continuity_id = gf_strdup(ds->last_period->ID);
    1345             :         }
    1346         577 :         ds->last_period = NULL;
    1347             : 
    1348         577 :         ds->period_dur.num = 0;
    1349         577 :         ds->period_dur.den = 1;
    1350         577 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_PERIOD_DUR);
    1351         577 :         if (p) ds->period_dur = p->value.lfrac;
    1352             : 
    1353         577 :         if (ds->stream_type==GF_STREAM_FILE) {
    1354           1 :                 if (!ds->xlink && !ds->period_start.num && !ds->period_dur.num) {
    1355           0 :                         ds->done = 1;
    1356           0 :                         GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] null PID specified without any XLINK/start/duration, ignoring\n"));
    1357           1 :                 } else if (ds->xlink) {
    1358           1 :                         ctx->use_xlink = GF_TRUE;
    1359             :                 }
    1360             :         } else {
    1361         576 :                 if (ds->xlink) gf_free(ds->xlink);
    1362         576 :                 ds->xlink = NULL;
    1363         576 :                 CHECK_PROP_STR(GF_PROP_PID_XLINK, ds->xlink, GF_EOS)
    1364         576 :                 if (ds->xlink)
    1365           0 :                         ctx->use_xlink = GF_TRUE;
    1366             :         }
    1367             : 
    1368             :         //input was done due to clamp but forced to new period, reschedule
    1369         577 :         if (new_period_request && ds->done && old_clamp_dur) {
    1370           0 :                 gf_list_del_item(ctx->next_period->streams, ds);
    1371             :                 //reset discard, blocking mode on output (set by EOS) and reset dasher EOS state
    1372           0 :                 gf_filter_pid_set_discard(ds->ipid, GF_FALSE);
    1373           0 :                 if (ds->opid) {
    1374           0 :                         gf_filter_pid_discard_block(ds->opid);
    1375           0 :                         gf_filter_pid_remove(ds->opid);
    1376           0 :                         ds->opid = NULL;
    1377             :                 }
    1378           0 :                 if (ctx->is_eos) {
    1379           0 :                         ctx->is_eos = GF_FALSE;
    1380           0 :                         gf_filter_pid_discard_block(ctx->opid);
    1381           0 :                         if (ctx->opid_alt)
    1382           0 :                         gf_filter_pid_discard_block(ctx->opid_alt);
    1383             :                 }
    1384           0 :                 ds->rep_init = GF_FALSE;
    1385           0 :                 ds->rep = NULL;
    1386           0 :                 ds->set = NULL;
    1387           0 :                 ds->period = NULL;
    1388           0 :                 ds->done = 0;
    1389             : //              gf_filter_post_process_task(filter);
    1390             : //              dasher_reset_stream(filter, ds, GF_FALSE);
    1391             :         }
    1392             : 
    1393             :         //our stream is already scheduled for next period, don't do anything
    1394         577 :         if (gf_list_find(ctx->next_period->streams, ds)>=0)
    1395             :                 period_switch = GF_FALSE;
    1396             : 
    1397             :         //assign default ID
    1398         577 :         if (!ds->period_id)
    1399         350 :                 ds->period_id = gf_strdup(DEFAULT_PERIOD_ID);
    1400             : 
    1401         577 :         e = dasher_hls_setup_crypto(ctx, ds);
    1402         577 :         if (e) return e;
    1403             : 
    1404         577 :         if (!period_switch) {
    1405         220 :                 if (ds->opid) {
    1406         181 :                         gf_filter_pid_copy_properties(ds->opid, pid);
    1407             :                         //for route out
    1408         181 :                         if (ctx->is_route && ctx->do_m3u8)
    1409           0 :                                 gf_filter_pid_set_property(ds->opid, GF_PROP_PCK_HLS_REF, &PROP_LONGUINT( ds->hls_ref_id ) );
    1410         181 :                         if (ctx->llhls)
    1411           0 :                                 gf_filter_pid_set_property(ds->opid, GF_PROP_PID_LLHLS, &PROP_UINT(ctx->llhls) );
    1412             :                 }
    1413         220 :                 if (ds->rep)
    1414         181 :                         dasher_update_rep(ctx, ds);
    1415             :                 return GF_OK;
    1416             :         }
    1417             :         //period switch !
    1418             : 
    1419             :         //we have queued packets (sbound modes), we cannot switch period for this stream now, force queue flush
    1420         357 :         if (gf_list_count(ds->packet_queue)) {
    1421           0 :                 ds->request_period_switch = new_period_request ? 2 : 1;
    1422           0 :                 return GF_OK;
    1423             :         }
    1424             :         //done for this stream
    1425         357 :         return dasher_stream_period_changed(filter, ctx, ds, new_period_request);
    1426             : }
    1427             : 
    1428             : 
    1429         876 : static GF_Err dasher_update_mpd(GF_DasherCtx *ctx)
    1430             : {
    1431             :         char profiles_string[GF_MAX_PATH];
    1432             :         GF_XMLAttribute *cenc_att = NULL;
    1433             :         GF_XMLAttribute *xlink_att = NULL;
    1434             : 
    1435         876 :         u32 i, count=gf_list_count(ctx->mpd->x_attributes);
    1436         921 :         for (i=0; i<count; i++) {
    1437          45 :                 GF_XMLAttribute * att = gf_list_get(ctx->mpd->x_attributes, i);
    1438          45 :                 if (!strcmp(att->name, "xmlns:cenc")) cenc_att = att;
    1439          45 :                 if (!strcmp(att->name, "xmlns:xlink")) xlink_att = att;
    1440             : 
    1441             :         }
    1442         876 :         if (ctx->dmode==GF_MPD_TYPE_DYNAMIC) {
    1443         433 :                 ctx->mpd->type = GF_MPD_TYPE_DYNAMIC;
    1444             :         } else {
    1445         443 :                 ctx->mpd->type = GF_MPD_TYPE_STATIC;
    1446         443 :                 ctx->mpd->availabilityStartTime = 0;
    1447             :         }
    1448             : 
    1449         876 :         if (ctx->profile==GF_DASH_PROFILE_LIVE) {
    1450         328 :                 if (ctx->use_xlink && !ctx->m2ts) {
    1451             :                         strcpy(profiles_string, "urn:mpeg:dash:profile:isoff-segext-live:2014");
    1452             :                 } else {
    1453         324 :                         sprintf(profiles_string, "urn:mpeg:dash:profile:%s:2011", ctx->m2ts ? "mp2t-simple" : "isoff-live");
    1454             :                 }
    1455         548 :         } else if (ctx->profile==GF_DASH_PROFILE_ONDEMAND) {
    1456          30 :                 if (ctx->use_xlink) {
    1457             :                         strcpy(profiles_string, "urn:mpeg:dash:profile:isoff-segext-on-demand:2014");
    1458             :                 } else {
    1459             :                         strcpy(profiles_string, "urn:mpeg:dash:profile:isoff-on-demand:2011");
    1460             :                 }
    1461         518 :         } else if (ctx->profile==GF_DASH_PROFILE_MAIN) {
    1462          22 :                 sprintf(profiles_string, "urn:mpeg:dash:profile:%s:2011", ctx->m2ts ? "mp2t-main" : "isoff-main");
    1463         496 :         } else if (ctx->profile==GF_DASH_PROFILE_HBBTV_1_5_ISOBMF_LIVE) {
    1464             :                 strcpy(profiles_string, "urn:hbbtv:dash:profile:isoff-live:2012");
    1465         496 :         } else if (ctx->profile==GF_DASH_PROFILE_AVC264_LIVE) {
    1466             :                 strcpy(profiles_string, "urn:mpeg:dash:profile:isoff-live:2011,http://dashif.org/guidelines/dash264");
    1467         490 :         } else if (ctx->profile==GF_DASH_PROFILE_AVC264_ONDEMAND) {
    1468             :                 strcpy(profiles_string, "urn:mpeg:dash:profile:isoff-on-demand:2011,http://dashif.org/guidelines/dash264");
    1469         486 :         } else if (ctx->profile==GF_DASH_PROFILE_DASHIF_LL) {
    1470             :                 strcpy(profiles_string, "urn:mpeg:dash:profile:isoff-live:2011,http://www.dashif.org/guidelines/low-latency-live-v5");
    1471             :         } else {
    1472             :                 strcpy(profiles_string, "urn:mpeg:dash:profile:full:2011");
    1473             :         }
    1474             : 
    1475         876 :         if (ctx->cmaf) {
    1476           0 :                 const size_t offset = strlen(profiles_string);
    1477           0 :                 strncat(profiles_string+offset, ",urn:mpeg:dash:profile:cmaf:2019", GF_MAX_PATH-offset-1);
    1478             :         }
    1479             : 
    1480         876 :         if (ctx->profX) {
    1481             :                 char profiles_w_ext[GF_MAX_PATH+256];
    1482             :                 sprintf(profiles_w_ext, "%s,%s", profiles_string, ctx->profX);
    1483           0 :                 if (ctx->mpd->profiles) gf_free(ctx->mpd->profiles);
    1484           0 :                 ctx->mpd->profiles = gf_strdup(profiles_w_ext);
    1485             :         } else {
    1486         876 :                 if (ctx->mpd->profiles) gf_free(ctx->mpd->profiles);
    1487         876 :                 ctx->mpd->profiles = gf_strdup(profiles_string);
    1488             :         }
    1489             : 
    1490         876 :         if (ctx->use_cenc && !cenc_att) {
    1491          88 :                 cenc_att = gf_xml_dom_create_attribute("xmlns:cenc", "urn:mpeg:cenc:2013");
    1492          88 :                 gf_list_add(ctx->mpd->x_attributes, cenc_att);
    1493             :         }
    1494         876 :         if (ctx->use_xlink && !xlink_att) {
    1495           1 :                 xlink_att = gf_xml_dom_create_attribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
    1496           1 :                 gf_list_add(ctx->mpd->x_attributes, xlink_att);
    1497             :         }
    1498             : 
    1499         876 :         ctx->mpd->time_shift_buffer_depth = 0;
    1500         876 :         ctx->mpd->minimum_update_period = 0;
    1501             : 
    1502         876 :         if (ctx->dmode==GF_MPD_TYPE_DYNAMIC) {
    1503         433 :                 ctx->mpd->time_shift_buffer_depth = (u32) -1;
    1504         433 :                 if (ctx->tsb>=0) ctx->mpd->time_shift_buffer_depth = (u32) (1000*ctx->tsb);
    1505             : 
    1506         433 :                 if (ctx->refresh>=0) {
    1507         433 :                         if (ctx->refresh) {
    1508          39 :                                 ctx->mpd->minimum_update_period = (u32) (1000*ctx->refresh);
    1509             :                         } else {
    1510         394 :                                 ctx->mpd->minimum_update_period = ctx->segdur.num * 1000;
    1511         394 :                                 ctx->mpd->minimum_update_period /= ctx->segdur.den;
    1512             :                         }
    1513             :                 } else {
    1514           0 :                         ctx->mpd->minimum_update_period = 0;
    1515             :                 }
    1516             :         }
    1517         876 :         return GF_OK;
    1518             : }
    1519         241 : static GF_Err dasher_setup_mpd(GF_DasherCtx *ctx)
    1520             : {
    1521             :         u32 i, count;
    1522             :         GF_MPD_ProgramInfo *info;
    1523         241 :         ctx->mpd = gf_mpd_new();
    1524         241 :         ctx->mpd->xml_namespace = "urn:mpeg:dash:schema:mpd:2011";
    1525         241 :         ctx->mpd->base_URLs = gf_list_new();
    1526         241 :         ctx->mpd->locations = gf_list_new();
    1527         241 :         ctx->mpd->program_infos = gf_list_new();
    1528         241 :         ctx->mpd->periods = gf_list_new();
    1529             :         //created by default because we store xmlns in it
    1530         241 :         ctx->mpd->x_attributes = gf_list_new();
    1531         241 :         if (ctx->buf<0) {
    1532          68 :                 s32 buf = -ctx->buf;
    1533          68 :                 ctx->mpd->min_buffer_time = (u32) ( ctx->segdur.num * 10 * buf / ctx->segdur.den); //*1000 (ms) / 100 (percent)
    1534             :         } else
    1535         173 :                 ctx->mpd->min_buffer_time = ctx->buf;
    1536             : 
    1537         241 :         GF_SAFEALLOC(info, GF_MPD_ProgramInfo);
    1538         241 :         if (info) {
    1539         241 :                 gf_list_add(ctx->mpd->program_infos, info);
    1540         241 :                 if (ctx->title)
    1541           0 :                         info->title = gf_strdup(ctx->title);
    1542             :                 else {
    1543             :                         char tmp[256];
    1544             :                         const char *name = NULL;
    1545         241 :                         if (ctx->out_path) {
    1546             :                                 const char *url = ctx->out_path;
    1547         241 :                                 if (!strncmp(ctx->out_path, "gfio://", 7)) {
    1548           1 :                                         url = gf_fileio_translate_url(ctx->out_path);
    1549           1 :                                         if (!url) url = "";
    1550             :                                 }
    1551         241 :                                 name = strrchr(url, '/');
    1552         241 :                                 if (!name) name = strrchr(url, '\\');
    1553         241 :                                 if (!name) name = url;
    1554         239 :                                 else name++;
    1555             :                         }
    1556         241 :                         snprintf(tmp, 255, "%s generated by GPAC", name ? name : "");
    1557         241 :                         tmp[255]=0;
    1558         241 :                         info->title = gf_strdup(tmp);
    1559             :                 }
    1560         241 :                 if (ctx->cprt) info->copyright = gf_strdup(ctx->cprt);
    1561         241 :                 if (ctx->info) info->more_info_url = gf_strdup(ctx->info);
    1562         241 :                 else info->more_info_url = gf_strdup("http://gpac.io");
    1563         241 :                 if (ctx->source) info->source = gf_strdup(ctx->source);
    1564         241 :                 if (ctx->lang) info->lang = gf_strdup(ctx->lang);
    1565             :         }
    1566             : 
    1567         241 :         count = ctx->location.nb_items;
    1568         241 :         for (i=0; i<count; i++) {
    1569           0 :                 char *l = ctx->location.vals[i];
    1570           0 :                 gf_list_add(ctx->mpd->locations, gf_strdup(l));
    1571             :         }
    1572         241 :         count = ctx->base.nb_items;
    1573         242 :         for (i=0; i<count; i++) {
    1574             :                 GF_MPD_BaseURL *base;
    1575           1 :                 char *b = ctx->base.vals[i];
    1576           1 :                 GF_SAFEALLOC(base, GF_MPD_BaseURL);
    1577           1 :                 if (base) {
    1578           1 :                         base->URL = gf_strdup(b);
    1579           1 :                         gf_list_add(ctx->mpd->base_URLs, base);
    1580             :                 }
    1581             :         }
    1582         241 :         return dasher_update_mpd(ctx);
    1583             : }
    1584             : 
    1585             : 
    1586             : GF_Err rfc_6381_get_codec_aac(char *szCodec, u32 codec_id,  u8 *dsi, u32 dsi_size, Bool force_sbr);
    1587             : GF_Err rfc_6381_get_codec_m4v(char *szCodec, u32 codec_id, u8 *dsi, u32 dsi_size);
    1588             : GF_Err rfc_6381_get_codec_avc(char *szCodec, u32 subtype, GF_AVCConfig *avcc);
    1589             : GF_Err rfc_6381_get_codec_hevc(char *szCodec, u32 subtype, GF_HEVCConfig *hvcc);
    1590             : GF_Err rfc_6381_get_codec_av1(char *szCodec, u32 subtype, GF_AV1Config *av1c);
    1591             : GF_Err rfc_6381_get_codec_vpx(char *szCodec, u32 subtype, GF_VPConfig *vpcc);
    1592             : GF_Err rfc_6381_get_codec_dolby_vision(char *szCodec, u32 subtype, GF_DOVIDecoderConfigurationRecord *dovi);
    1593             : GF_Err rfc_6381_get_codec_vvc(char *szCodec, u32 subtype, GF_VVCConfig *vvcc);
    1594             : GF_Err rfc_6381_get_codec_mpegha(char *szCodec, u32 subtype, u8 *dsi, u32 dsi_size, s32 pl);
    1595             : GF_Err rfc6381_codec_name_default(char *szCodec, u32 subtype, u32 codec_id);
    1596             : 
    1597             : 
    1598         538 : static GF_Err dasher_get_rfc_6381_codec_name(GF_DasherCtx *ctx, GF_DashStream *ds, char *szCodec, Bool force_inband, Bool force_sbr)
    1599             : {
    1600             :         u32 subtype=0, subtype_src=0;
    1601             :         s32 mha_pl=-1;
    1602             :         const GF_PropertyValue *dcd, *dcd_enh, *dovi, *codec;
    1603             : 
    1604         538 :         dcd = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_ISOM_SUBTYPE);
    1605         538 :         if (dcd) subtype_src = dcd->value.uint;
    1606             : 
    1607         538 :         dcd = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_DECODER_CONFIG);
    1608         538 :         dcd_enh = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_DECODER_CONFIG_ENHANCEMENT);
    1609             : 
    1610         538 :         if (!force_inband) {
    1611         538 :                 force_inband = ds->inband_params;
    1612             :         }
    1613         538 :         if (!force_inband) {
    1614         530 :                 const GF_PropertyValue *p = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_ISOM_SUBTYPE);
    1615         530 :                 if (p) {
    1616             :                         //input uses inband parameters, force it on output regardless of bitstream switching mode
    1617         427 :                         switch (p->value.uint) {
    1618           3 :                         case GF_ISOM_SUBTYPE_AVC3_H264:
    1619             :                         case GF_ISOM_SUBTYPE_AVC4_H264:
    1620             :                         case GF_ISOM_SUBTYPE_LHE1:
    1621             :                         case GF_ISOM_SUBTYPE_HEV1:
    1622             :                                 force_inband = GF_TRUE;
    1623           3 :                                 ds->inband_params = GF_TRUE;
    1624             :                                 break;
    1625             :                         }
    1626             :                 }
    1627             :         }
    1628             : 
    1629         538 :         codec = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_CODEC);
    1630         538 :         if (codec && (codec->type==GF_PROP_STRING) && codec->value.string) {
    1631             :                 const char *codec_str = codec->value.string;
    1632          24 :                 if (codec_str[0] != '.') {
    1633             :                         snprintf(szCodec, RFC6381_CODEC_NAME_SIZE_MAX, "%s", codec_str);
    1634             :                         return GF_OK;
    1635             :                 }
    1636           0 :                 if (!subtype_src)
    1637           0 :                         subtype_src = gf_codecid_4cc_type(ds->codec_id);
    1638           0 :                 snprintf(szCodec, RFC6381_CODEC_NAME_SIZE_MAX, "%s%s", gf_4cc_to_str(subtype_src), codec_str);
    1639             :                 return GF_OK;
    1640             :         }
    1641             : 
    1642         514 :         dovi = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_DOLBY_VISION);
    1643         514 :         if (dovi) {
    1644           1 :                 GF_BitStream *bs = gf_bs_new(dovi->value.data.ptr, dovi->value.data.size, GF_BITSTREAM_READ);
    1645           1 :                 GF_DOVIDecoderConfigurationRecord *dvcc = gf_odf_dovi_cfg_read_bs(bs);
    1646           1 :                 gf_bs_del(bs);
    1647           1 :                 if (!dvcc) {
    1648           0 :                         GF_LOG(GF_LOG_DEBUG, GF_LOG_AUTHOR, ("[ISOM Tools] No config found for Dolby Vision file (\"%s\") when computing RFC6381.\n", gf_4cc_to_str(subtype)));
    1649             :                         return GF_BAD_PARAM;
    1650             :                 }
    1651           1 :                 GF_Err e = rfc_6381_get_codec_dolby_vision(szCodec, GF_ISOM_SUBTYPE_DVHE, dvcc);
    1652           1 :                 gf_odf_dovi_cfg_del(dvcc);
    1653             :                 return e;
    1654             :         }
    1655             : 
    1656         513 :         switch (ds->codec_id) {
    1657         121 :         case GF_CODECID_AAC_MPEG4:
    1658             :         case GF_CODECID_AAC_MPEG2_MP:
    1659             :         case GF_CODECID_AAC_MPEG2_LCP:
    1660             :         case GF_CODECID_AAC_MPEG2_SSRP:
    1661             :         case GF_CODECID_USAC:
    1662         121 :                 return rfc_6381_get_codec_aac(szCodec, ds->codec_id, dcd ? dcd->value.data.ptr : NULL, dcd ? dcd->value.data.size : 0, force_sbr);
    1663             : 
    1664           2 :         case GF_CODECID_MPEG4_PART2:
    1665           2 :                 return rfc_6381_get_codec_m4v(szCodec, ds->codec_id, dcd ? dcd->value.data.ptr : NULL, dcd ? dcd->value.data.size : 0);
    1666             :                 break;
    1667           2 :         case GF_CODECID_SVC:
    1668             :         case GF_CODECID_MVC:
    1669           2 :                 if (dcd_enh) dcd = dcd_enh;
    1670           2 :                 subtype = (ds->codec_id==GF_CODECID_SVC) ? GF_ISOM_SUBTYPE_SVC_H264 : GF_ISOM_SUBTYPE_MVC_H264;
    1671             :         case GF_CODECID_AVC:
    1672             :                 if (!subtype) {
    1673         172 :                         if (force_inband) {
    1674           7 :                                 subtype = dcd_enh ? GF_ISOM_SUBTYPE_AVC4_H264 : GF_ISOM_SUBTYPE_AVC3_H264;
    1675             :                         } else {
    1676         165 :                                 subtype = dcd_enh ? GF_ISOM_SUBTYPE_AVC2_H264 : GF_ISOM_SUBTYPE_AVC_H264;
    1677             :                         }
    1678             :                 }
    1679         174 :                 if (dcd) {
    1680         174 :                         GF_AVCConfig *avcc = gf_odf_avc_cfg_read(dcd->value.data.ptr, dcd->value.data.size);
    1681         174 :                         if (avcc) {
    1682         174 :                                 GF_Err e = rfc_6381_get_codec_avc(szCodec, subtype, avcc);
    1683         174 :                                 gf_odf_avc_cfg_del(avcc);
    1684             :                                 return e;
    1685             :                         }
    1686             :                 }
    1687           0 :                 snprintf(szCodec, RFC6381_CODEC_NAME_SIZE_MAX, "%s", gf_4cc_to_str(subtype));
    1688           0 :                 GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[Dasher] Cannot find AVC config, using default %s\n", szCodec));
    1689             :                 return GF_OK;
    1690             : 
    1691             : #ifndef GPAC_DISABLE_HEVC
    1692           0 :         case GF_CODECID_LHVC:
    1693           0 :                 subtype = force_inband ? GF_ISOM_SUBTYPE_LHE1 : GF_ISOM_SUBTYPE_LHV1;
    1694             :                 //fallthrough
    1695             :         case GF_CODECID_HEVC_TILES:
    1696             :                 if (!subtype) subtype = GF_ISOM_SUBTYPE_HVT1;
    1697          36 :                 if (!dcd && ds->dep_id) {
    1698          27 :                         u32 i, count = gf_list_count(ctx->current_period->streams);
    1699          27 :                         for (i=0; i<count; i++) {
    1700          27 :                                 GF_DashStream *a_ds = gf_list_get(ctx->current_period->streams, i);
    1701          27 :                                 if (a_ds->id != ds->dep_id) continue;
    1702          27 :                                 dcd = gf_filter_pid_get_property(a_ds->ipid, GF_PROP_PID_DECODER_CONFIG);
    1703             :                                 break;
    1704             :                         }
    1705             :                 }
    1706             :                 //fallthrough
    1707             :         case GF_CODECID_HEVC:
    1708         111 :                 if (!subtype) {
    1709          75 :                         if (ds->tile_base) {
    1710           4 :                                 subtype = force_inband ? GF_ISOM_SUBTYPE_HEV2 : GF_ISOM_SUBTYPE_HVC2;
    1711          71 :                         } else if (dcd_enh) {
    1712           2 :                                 if (dcd) {
    1713           2 :                                         subtype = force_inband ? GF_ISOM_SUBTYPE_HEV2 : GF_ISOM_SUBTYPE_HVC2;
    1714             :                                 } else {
    1715           0 :                                         subtype = force_inband ? GF_ISOM_SUBTYPE_LHE1 : GF_ISOM_SUBTYPE_LHV1;
    1716             :                                 }
    1717             :                         } else {
    1718          69 :                                 subtype = force_inband ? GF_ISOM_SUBTYPE_HEV1 : GF_ISOM_SUBTYPE_HVC1;
    1719             :                         }
    1720             :                 }
    1721         111 :                 if (dcd || dcd_enh) {
    1722         111 :                         GF_HEVCConfig *hvcc = dcd ? gf_odf_hevc_cfg_read(dcd->value.data.ptr, dcd->value.data.size, GF_FALSE) : NULL;
    1723         111 :                         if (hvcc) {
    1724         111 :                                 GF_Err e = rfc_6381_get_codec_hevc(szCodec, subtype, hvcc);
    1725         111 :                                 gf_odf_hevc_cfg_del(hvcc);
    1726             :                                 return e;
    1727             :                         }
    1728           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[Dasher] HEVC config not compliant !\n"));
    1729             :                         return GF_NON_COMPLIANT_BITSTREAM;
    1730             :                 }
    1731             : 
    1732           0 :                 snprintf(szCodec, RFC6381_CODEC_NAME_SIZE_MAX, "%s", gf_4cc_to_str(subtype));
    1733           0 :                 GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[Dasher] Cannot find HEVC config, using default %s\n", szCodec));
    1734             :                 return GF_OK;
    1735             : #endif
    1736             : 
    1737             : #ifndef GPAC_DISABLE_AV1
    1738             :         case GF_CODECID_AV1:
    1739             :                 if (!subtype) subtype = GF_ISOM_SUBTYPE_AV01;
    1740             : 
    1741          54 :                 if (dcd) {
    1742          54 :                         GF_AV1Config *av1c = gf_odf_av1_cfg_read(dcd->value.data.ptr, dcd->value.data.size);
    1743          54 :                         if (av1c) {
    1744          54 :                                 GF_Err e = rfc_6381_get_codec_av1(szCodec, subtype, av1c);
    1745          54 :                                 gf_odf_av1_cfg_del(av1c);
    1746             :                                 return e;
    1747             :                         }
    1748           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[DASHER] AV1 config not conformant\n"));
    1749             :                         return GF_NON_COMPLIANT_BITSTREAM;
    1750             :                 }
    1751           0 :                 snprintf(szCodec, RFC6381_CODEC_NAME_SIZE_MAX, "%s", gf_4cc_to_str(subtype));
    1752           0 :                 GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[Dasher] Cannot find AV1 config, using default %s\n", szCodec));
    1753             :                 return GF_OK;
    1754             : #endif /*GPAC_DISABLE_AV1*/
    1755             : 
    1756             : 
    1757             :         case GF_CODECID_VP8:
    1758             :                 if (!subtype) subtype = GF_ISOM_SUBTYPE_VP08;
    1759             :         case GF_CODECID_VP9:
    1760             :                 if (!subtype) subtype = GF_ISOM_SUBTYPE_VP09;
    1761             : 
    1762          43 :                 if (dcd) {
    1763          43 :                         GF_VPConfig *vpcc = gf_odf_vp_cfg_read(dcd->value.data.ptr, dcd->value.data.size);
    1764             : 
    1765          43 :                         if (vpcc) {
    1766          43 :                                 GF_Err e = rfc_6381_get_codec_vpx(szCodec, subtype, vpcc);
    1767          43 :                                 gf_odf_vp_cfg_del(vpcc);
    1768             :                                 return e;
    1769             :                         }
    1770           0 :                         GF_LOG(GF_LOG_DEBUG, GF_LOG_AUTHOR, ("[Dasher] No config found for VP file (\"%s\") when computing RFC6381.\n", gf_4cc_to_str(subtype)));
    1771             :                         return GF_NON_COMPLIANT_BITSTREAM;
    1772             :                 }
    1773           0 :                 snprintf(szCodec, RFC6381_CODEC_NAME_SIZE_MAX, "%s", gf_4cc_to_str(subtype));
    1774           0 :                 GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[Dasher] Cannot find VPX config, using default %s\n", szCodec));
    1775             :                 return GF_OK;
    1776             : 
    1777           0 :         case GF_CODECID_MHAS:
    1778           0 :                 subtype = subtype_src ? subtype_src : GF_ISOM_SUBTYPE_MH3D_MHM1;
    1779           0 :                 if (!dcd) {
    1780           0 :                         const GF_PropertyValue *pl = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_PROFILE_LEVEL);
    1781           0 :                         if (pl) mha_pl = (s32) pl->value.uint;
    1782             :                 }
    1783             :                 //fallthrough
    1784             :         case GF_CODECID_MPHA:
    1785           0 :                 if (!subtype)
    1786           0 :                         subtype = subtype_src ? subtype_src : GF_ISOM_SUBTYPE_MH3D_MHA1;
    1787             : 
    1788           0 :                 return rfc_6381_get_codec_mpegha(szCodec, subtype, dcd ? dcd->value.data.ptr : NULL, dcd ? dcd->value.data.size : 0, mha_pl);
    1789             : 
    1790             :         case GF_CODECID_VVC:
    1791             :                 if (!subtype) {
    1792           0 :                         subtype = force_inband ? GF_ISOM_SUBTYPE_VVI1 : GF_ISOM_SUBTYPE_VVC1;
    1793             :                 }
    1794           0 :                 if (dcd) {
    1795           0 :                         GF_VVCConfig *vvcc = gf_odf_vvc_cfg_read(dcd->value.data.ptr, dcd->value.data.size);
    1796             : 
    1797           0 :                         snprintf(szCodec, RFC6381_CODEC_NAME_SIZE_MAX, "%s.", gf_4cc_to_str(subtype));
    1798           0 :                         if (vvcc) {
    1799           0 :                                 GF_Err e = rfc_6381_get_codec_vvc(szCodec, subtype, vvcc);
    1800           0 :                                 gf_odf_vvc_cfg_del(vvcc);
    1801             :                                 return e;
    1802             :                         }
    1803           0 :                         GF_LOG(GF_LOG_DEBUG, GF_LOG_AUTHOR, ("[Dasher] No config found for VP file (\"%s\") when computing RFC6381.\n", gf_4cc_to_str(subtype)));
    1804             :                         return GF_NON_COMPLIANT_BITSTREAM;
    1805             :                 }
    1806           0 :                 snprintf(szCodec, RFC6381_CODEC_NAME_SIZE_MAX, "%s", gf_4cc_to_str(subtype));
    1807           0 :                 GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[Dasher] Cannot find VVC config, using default %s\n", szCodec));
    1808             :                 return GF_OK;
    1809             : 
    1810           8 :         default:
    1811           8 :                 subtype = gf_codecid_4cc_type(ds->codec_id);
    1812           8 :                 if (!subtype) {
    1813           0 :                         const GF_PropertyValue *p = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_ISOM_SUBTYPE);
    1814           0 :                         if (p) subtype = p->value.uint;
    1815             :                 }
    1816           8 :                 if (!subtype) {
    1817           0 :                         GF_LOG(GF_LOG_WARNING, GF_LOG_AUTHOR, ("[Dasher] codec parameters not known, cannot set codec string\n" ));
    1818             :                         strcpy(szCodec, "unkn");
    1819             :                         return GF_OK;
    1820             :                 }
    1821             : 
    1822           8 :                 return rfc6381_codec_name_default(szCodec, subtype, ds->codec_id);
    1823             :         }
    1824             :         return GF_OK;
    1825             : }
    1826             : 
    1827             : 
    1828          45 : static GF_DashStream *get_base_ds(GF_DasherCtx *ctx, GF_DashStream *for_ds)
    1829             : {
    1830             :         u32 i, count;
    1831          45 :         if (!for_ds->dep_id) return NULL;
    1832          45 :         count = gf_list_count(ctx->pids);
    1833           0 :         for (i=0; i<count; i++) {
    1834          45 :                 GF_DashStream *ds = gf_list_get(ctx->pids, i);
    1835          45 :                 if (ds->id == for_ds->dep_id)
    1836             :                         return ds;
    1837             :         }
    1838             :         return NULL;
    1839             : }
    1840             : 
    1841         109 : static void get_canon_urn(bin128 URN, char *res)
    1842             : {
    1843             :         char sres[4];
    1844             :         u32 i;
    1845             :         /* Output canonical UIID form */
    1846             :         strcpy(res, "");
    1847         545 :         for (i=0; i<4; i++) { sprintf(sres, "%02x", URN[i]); strcat(res, sres); }
    1848             :         strcat(res, "-");
    1849         327 :         for (i=4; i<6; i++) { sprintf(sres, "%02x", URN[i]); strcat(res, sres); }
    1850             :         strcat(res, "-");
    1851         327 :         for (i=6; i<8; i++) { sprintf(sres, "%02x", URN[i]); strcat(res, sres); }
    1852             :         strcat(res, "-");
    1853         327 :         for (i=8; i<10; i++) { sprintf(sres, "%02x", URN[i]); strcat(res, sres); }
    1854             :         strcat(res, "-");
    1855         763 :         for (i=10; i<16; i++) { sprintf(sres, "%02x", URN[i]); strcat(res, sres); }
    1856         109 : }
    1857             : 
    1858           0 : static const char *get_drm_kms_name(const char *canURN)
    1859             : {
    1860           0 :         if (!stricmp(canURN, "67706163-6365-6E63-6472-6D746F6F6C31")) return "GPAC1.0";
    1861           0 :         else if (!stricmp(canURN, "5E629AF5-38DA-4063-8977-97FFBD9902D4")) return "Marlin1.0";
    1862           0 :         else if (!strcmp(canURN, "adb41c24-2dbf-4a6d-958b-4457c0d27b95")) return "MediaAccess3.0";
    1863           0 :         else if (!strcmp(canURN, "A68129D3-575B-4F1A-9CBA-3223846CF7C3")) return "VideoGuard";
    1864           0 :         else if (!strcmp(canURN, "9a04f079-9840-4286-ab92-e65be0885f95")) return "PlayReady";
    1865           0 :         else if (!strcmp(canURN, "9a27dd82-fde2-4725-8cbc-4234aa06ec09")) return "VCAS";
    1866           0 :         else if (!strcmp(canURN, "F239E769-EFA3-4850-9C16-A903C6932EFB")) return "Adobe";
    1867           0 :         else if (!strcmp(canURN, "1f83e1e8-6ee9-4f0d-ba2f-5ec4e3ed1a66")) return "SecureMedia";
    1868           0 :         else if (!strcmp(canURN, "644FE7B5-260F-4FAD-949A-0762FFB054B4")) return "CMLA (OMA DRM)";
    1869           0 :         else if (!strcmp(canURN, "6a99532d-869f-5922-9a91-113ab7b1e2f3")) return "MobiTVDRM";
    1870           0 :         else if (!strcmp(canURN, "35BF197B-530E-42D7-8B65-1B4BF415070F")) return "DivX DRM";
    1871           0 :         else if (!strcmp(canURN, "B4413586-C58C-FFB0-94A5-D4896C1AF6C3")) return "VODRM";
    1872           0 :         else if (!strcmp(canURN, "edef8ba9-79d6-4ace-a3c8-27dcd51d21ed")) return "Widevine";
    1873           0 :         else if (!strcmp(canURN, "80a6be7e-1448-4c37-9e70-d5aebe04c8d2")) return "Irdeto";
    1874           0 :         else if (!strcmp(canURN, "dcf4e3e3-62f1-5818-7ba6-0a6fe33ff3dd")) return "CA 1.0, DRM+ 2.0";
    1875           0 :         else if (!strcmp(canURN, "45d481cb-8fe0-49c0-ada9-ab2d2455b2f2")) return "CoreCrypt";
    1876           0 :         else if (!strcmp(canURN, "616C7469-6361-7374-2D50-726F74656374")) return "altiProtect";
    1877           0 :         else if (!strcmp(canURN, "992c46e6-c437-4899-b6a0-50fa91ad0e39")) return "Arris SecureMedia SteelKnot version 1";
    1878           0 :         else if (!strcmp(canURN, "1077efec-c0b2-4d02-ace3-3c1e52e2fb4b")) return "cenc initData";
    1879           0 :         else if (!strcmp(canURN, "e2719d58-a985-b3c9-781a-b030af78d30e")) return "ClearKey1.0";
    1880           0 :         else if (!strcmp(canURN, "94CE86FB-07FF-4F43-ADB8-93D2FA968CA2")) return "FairPlay";
    1881           0 :         else if (!strcmp(canURN, "279fe473-512c-48fe-ade8-d176fee6b40f")) return "Arris Titanium";
    1882           0 :         else if (!strcmp(canURN, "aa11967f-cc01-4a4a-8e99-c5d3dddfea2d")) return "UDRM";
    1883           0 :         return "unknown";
    1884             : }
    1885             : 
    1886         323 : static GF_List *dasher_get_content_protection_desc(GF_DasherCtx *ctx, GF_DashStream *ds, GF_MPD_AdaptationSet *for_set)
    1887             : {
    1888             :         char sCan[40];
    1889             :         u32 prot_scheme=0;
    1890             :         u32 i, count;
    1891             :         const GF_PropertyValue *p;
    1892             :         GF_List *res = NULL;
    1893             :         GF_BitStream *bs_r;
    1894             : 
    1895         323 :         count = gf_list_count(ctx->current_period->streams);
    1896         323 :         bs_r = gf_bs_new((const char *) &count, 1, GF_BITSTREAM_READ);
    1897             : 
    1898        1167 :         for (i=0; i<count; i++) {
    1899             :                 GF_MPD_Descriptor *desc;
    1900         844 :                 GF_DashStream *a_ds = gf_list_get(ctx->current_period->streams, i);
    1901         844 :                 if (!a_ds->is_encrypted) continue;
    1902             : 
    1903         174 :                 if (for_set) {
    1904         174 :                         if (a_ds->set != for_set) continue;
    1905             :                         //for now only insert for the stream holding the set
    1906         125 :                         if (!a_ds->owns_set) continue;
    1907           0 :                 } else if ((a_ds != ds) && (a_ds->muxed_base != ds) ) {
    1908           0 :                         continue;
    1909             :                 }
    1910             : 
    1911         119 :                 p = gf_filter_pid_get_property(a_ds->ipid, GF_PROP_PID_PROTECTION_SCHEME_TYPE);
    1912         119 :                 if (p) prot_scheme = p->value.uint;
    1913             : 
    1914         119 :                 if ((prot_scheme==GF_ISOM_CENC_SCHEME) || (prot_scheme==GF_ISOM_CBC_SCHEME) || (prot_scheme==GF_ISOM_CENS_SCHEME) || (prot_scheme==GF_ISOM_CBCS_SCHEME)
    1915           0 :                 ) {
    1916             :                         const GF_PropertyValue *ki;
    1917             :                         u32 j, nb_pssh;
    1918             :                         GF_XMLAttribute *att;
    1919             :                         char szVal[GF_MAX_PATH];
    1920             : 
    1921         109 :                         ctx->use_cenc = GF_TRUE;
    1922             : 
    1923         109 :                         ki = gf_filter_pid_get_property(a_ds->ipid, GF_PROP_PID_CENC_KEY_INFO);
    1924         109 :                         if (!ki || !ki->value.data.ptr) {
    1925         109 :                                 continue;
    1926             :                         }
    1927             : 
    1928         109 :                         if (!res) res = gf_list_new();
    1929         109 :                         desc = gf_mpd_descriptor_new(NULL, "urn:mpeg:dash:mp4protection:2011", gf_4cc_to_str(prot_scheme));
    1930         109 :                         gf_list_add(res, desc);
    1931             : 
    1932         109 :                         get_canon_urn(ki->value.data.ptr + 4, sCan);
    1933         109 :                         att = gf_xml_dom_create_attribute("cenc:default_KID", sCan);
    1934         109 :                         if (!desc->x_attributes) desc->x_attributes = gf_list_new();
    1935         109 :                         gf_list_add(desc->x_attributes, att);
    1936             : 
    1937         109 :                         if (ctx->pssh <= GF_DASH_PSSH_MOOF) {
    1938         109 :                                 continue;
    1939             :                         }
    1940             :                         //(data) binary blob containing (u32)N [(bin128)SystemID(u32)version(u32)KID_count[(bin128)keyID](u32)priv_size(char*priv_size)priv_data]
    1941           0 :                         p = gf_filter_pid_get_property(a_ds->ipid, GF_PROP_PID_CENC_PSSH);
    1942           0 :                         if (!p) continue;
    1943             : 
    1944           0 :                         gf_bs_reassign_buffer(bs_r, p->value.data.ptr, p->value.data.size);
    1945           0 :                         nb_pssh = gf_bs_read_u32(bs_r);
    1946             : 
    1947             :                         //add pssh
    1948           0 :                         for (j=0; j<nb_pssh; j++) {
    1949             :                                 u32 pssh_idx;
    1950             :                                 bin128 sysID;
    1951             :                                 GF_XMLNode *node;
    1952             :                                 u32 version, k_count;
    1953           0 :                                 u8 *pssh_data=NULL;
    1954             :                                 u32 pssh_len, size_64;
    1955           0 :                                 GF_BitStream *bs_w = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
    1956             : 
    1957             :                                 //rewrite PSSH box
    1958           0 :                                 gf_bs_write_u32(bs_w, 0);
    1959           0 :                                 gf_bs_write_u32(bs_w, GF_ISOM_BOX_TYPE_PSSH);
    1960             : 
    1961           0 :                                 gf_bs_read_data(bs_r, sysID, 16);
    1962           0 :                                 version = gf_bs_read_u32(bs_r);
    1963             : 
    1964           0 :                                 k_count = gf_bs_read_u32(bs_r);
    1965           0 :                                 if (k_count) version = 1;
    1966           0 :                                 gf_bs_write_u8(bs_w, version);
    1967           0 :                                 gf_bs_write_u24(bs_w, 0);
    1968           0 :                                 gf_bs_write_data(bs_w, sysID, 16);
    1969           0 :                                 if (version) {
    1970           0 :                                         gf_bs_write_u32(bs_w, k_count);
    1971           0 :                                         for (j=0; j<k_count; j++) {
    1972             :                                                 bin128 keyID;
    1973           0 :                                                 gf_bs_read_data(bs_r, keyID, 16);
    1974           0 :                                                 gf_bs_write_data(bs_w, keyID, 16);
    1975             :                                         }
    1976             :                                 }
    1977           0 :                                 k_count = gf_bs_read_u32(bs_r);
    1978           0 :                                 gf_bs_write_u32(bs_w, k_count);
    1979           0 :                                 for (pssh_idx=0; pssh_idx<k_count; pssh_idx++) {
    1980           0 :                                         gf_bs_write_u8(bs_w, gf_bs_read_u8(bs_r) );
    1981             :                                 }
    1982           0 :                                 pssh_len = (u32) gf_bs_get_position(bs_w);
    1983           0 :                                 gf_bs_seek(bs_w, 0);
    1984           0 :                                 gf_bs_write_u32(bs_w, pssh_len);
    1985           0 :                                 gf_bs_seek(bs_w, pssh_len);
    1986           0 :                                 gf_bs_get_content(bs_w, &pssh_data, &pssh_len);
    1987           0 :                                 gf_bs_del(bs_w);
    1988             : 
    1989           0 :                                 get_canon_urn(sysID, sCan);
    1990           0 :                                 desc = gf_mpd_descriptor_new(NULL, NULL, NULL);
    1991           0 :                                 desc->x_children = gf_list_new();
    1992             :                                 sprintf(szVal, "urn:uuid:%s", sCan);
    1993           0 :                                 desc->scheme_id_uri = gf_strdup(szVal);
    1994           0 :                                 desc->value = gf_strdup(get_drm_kms_name(sCan));
    1995           0 :                                 gf_list_add(res, desc);
    1996             : 
    1997           0 :                                 GF_SAFEALLOC(node, GF_XMLNode);
    1998           0 :                                 if (node) {
    1999             :                                         GF_XMLNode *pnode;
    2000           0 :                                         node->type = GF_XML_NODE_TYPE;
    2001           0 :                                         node->name = gf_strdup("cenc:pssh");
    2002           0 :                                         node->content = gf_list_new();
    2003           0 :                                         gf_list_add(desc->x_children, node);
    2004             : 
    2005           0 :                                         GF_SAFEALLOC(pnode, GF_XMLNode);
    2006           0 :                                         if (pnode) {
    2007           0 :                                                 pnode->type = GF_XML_TEXT_TYPE;
    2008           0 :                                                 gf_list_add(node->content, pnode);
    2009             : 
    2010           0 :                                                 size_64 = 2*pssh_len;
    2011           0 :                                                 pnode->name = gf_malloc(size_64);
    2012           0 :                                                 if (pnode->name) {
    2013           0 :                                                         size_64 = gf_base64_encode((const char *)pssh_data, pssh_len, (char *)pnode->name, size_64);
    2014           0 :                                                         pnode->name[size_64] = 0;
    2015             :                                                 }
    2016             :                                         }
    2017             :                                 }
    2018           0 :                                 gf_free(pssh_data);
    2019             :                         }
    2020             :                 } else {
    2021          10 :                         GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Protection scheme %s has no official DASH mapping, using URI \"urn:gpac:dash:mp4protection:2018\"\n", gf_4cc_to_str(prot_scheme)));
    2022          10 :                         if (!res) res = gf_list_new();
    2023          10 :                         desc = gf_mpd_descriptor_new(NULL, "urn:gpac:dash:mp4protection:2018", gf_4cc_to_str(prot_scheme));
    2024          10 :                         gf_list_add(res, desc);
    2025             :                 }
    2026             :         }
    2027         323 :         gf_bs_del(bs_r);
    2028         323 :         return res;
    2029             : }
    2030             : 
    2031         357 : static void dasher_get_mime_and_ext(GF_DasherCtx *ctx, GF_DashStream *ds, const char **out_subtype, const char **out_ext)
    2032             : {
    2033             :         const char *subtype = NULL;
    2034             :         const char *mux_ext = NULL;
    2035             :         const char *cstr;
    2036             : 
    2037         357 :         if (ctx->m2ts) {
    2038             :                 subtype = "mp2t";
    2039         346 :         } else if (ctx->muxtype!=DASHER_MUX_AUTO) {
    2040         345 :                 switch (ctx->muxtype) {
    2041             :                 case DASHER_MUX_ISOM: subtype = "mp4"; mux_ext = "mp4"; break;
    2042           0 :                 case DASHER_MUX_TS: subtype = "mp2t"; mux_ext = "ts"; break;
    2043           1 :                 case DASHER_MUX_MKV: subtype = "x-matroska"; mux_ext = "mkv"; break;
    2044           0 :                 case DASHER_MUX_WEBM: subtype = "webm"; mux_ext = "webm"; break;
    2045           0 :                 case DASHER_MUX_OGG: subtype = "ogg"; mux_ext = "ogg"; break;
    2046           8 :                 case DASHER_MUX_RAW:
    2047           8 :                         cstr = gf_codecid_mime(ds->codec_id);
    2048           8 :                         if (cstr) {
    2049           8 :                                 subtype = strchr(cstr, '/');
    2050           8 :                                 if (subtype) subtype++;
    2051             :                                 else subtype = "raw";
    2052             :                         }
    2053           8 :                         if (out_ext) {
    2054           0 :                                 cstr = gf_codecid_file_ext(ds->codec_id);
    2055           0 :                                 if (cstr) *out_ext = cstr;
    2056             :                         }
    2057             :                         break;
    2058             :                 }
    2059           1 :         } else if (ctx->initext) {
    2060             :                 mux_ext = ctx->initext;
    2061           1 :                 if (!strcmp(ctx->initext, "ts") || !strcmp(ctx->initext, "m2ts")) {
    2062             :                         subtype = "mp2t";
    2063           0 :                         ctx->muxtype = DASHER_MUX_TS;
    2064           1 :                 } else if (!strcmp(ctx->initext, "mkv") || !strcmp(ctx->initext, "mka") || !strcmp(ctx->initext, "mks") || !strcmp(ctx->initext, "mk3d")) {
    2065             :                         subtype = "x-matroska";
    2066           1 :                         ctx->muxtype = DASHER_MUX_MKV;
    2067           0 :                 } else if (!strcmp(ctx->initext, "webm") || !strcmp(ctx->initext, "weba")) {
    2068             :                         subtype = "webm";
    2069           0 :                         ctx->muxtype = DASHER_MUX_WEBM;
    2070           0 :                 } else if (!strcmp(ctx->initext, "ogg") || !strcmp(ctx->initext, "oga") || !strcmp(ctx->initext, "ogv") || !strcmp(ctx->initext, "spx") || !strcmp(ctx->initext, "oggm") || !strcmp(ctx->initext, "opus")) {
    2071             :                         subtype = "ogg";
    2072           0 :                         ctx->muxtype = DASHER_MUX_OGG;
    2073             :                 }
    2074           0 :                 else if (!strcmp(ctx->initext, "null")) {
    2075             :                         mux_ext = "mp4";
    2076           0 :                         ctx->muxtype = DASHER_MUX_ISOM;
    2077             :                 }
    2078             :         }
    2079         357 :         if (!subtype) subtype = "mp4";
    2080         357 :         if (out_subtype) *out_subtype = subtype;
    2081         357 :         if (!mux_ext) mux_ext = "mp4";
    2082         357 :         if (out_ext) *out_ext = mux_ext;
    2083         357 : }
    2084             : 
    2085             : 
    2086         538 : static void dasher_update_rep(GF_DasherCtx *ctx, GF_DashStream *ds)
    2087             : {
    2088             :         char szCodec[RFC6381_CODEC_NAME_SIZE_MAX];
    2089             : 
    2090             :         //Outputs are not yet connected, derive mime from init segment extension
    2091         538 :         if (!ds->rep->mime_type) {
    2092         357 :                 const char *subtype = NULL;
    2093         357 :                 dasher_get_mime_and_ext(ctx, ds, &subtype, NULL);
    2094             : 
    2095         357 :                 if (ds->stream_type==GF_STREAM_VISUAL)
    2096         271 :                         gf_dynstrcat(&ds->rep->mime_type, "video/", NULL);
    2097          86 :                 else if (ds->stream_type==GF_STREAM_AUDIO)
    2098          84 :                         gf_dynstrcat(&ds->rep->mime_type, "audio/", NULL);
    2099             :                 else
    2100           2 :                         gf_dynstrcat(&ds->rep->mime_type, "application/", NULL);
    2101             : 
    2102         357 :                 gf_dynstrcat(&ds->rep->mime_type, subtype, NULL);
    2103             :         }
    2104             : 
    2105         538 :         ds->rep->bandwidth = ds->bitrate;
    2106         538 :         if (ds->stream_type==GF_STREAM_VISUAL) {
    2107         405 :                 ds->rep->width = ds->width;
    2108         405 :                 ds->rep->height = ds->height;
    2109             : 
    2110             : 
    2111         405 :                 if (!ds->rep->sar) {
    2112         271 :                         GF_SAFEALLOC(ds->rep->sar, GF_MPD_Fractional);
    2113             :                 }
    2114         405 :                 if (ds->rep->sar) {
    2115         405 :                         ds->rep->sar->num = ds->sar.num;
    2116         405 :                         ds->rep->sar->den = ds->sar.den;
    2117             :                 }
    2118         405 :                 if (ds->fps.num && ds->fps.den) {
    2119         376 :                         if (!ds->rep->framerate) {
    2120         242 :                                 GF_SAFEALLOC(ds->rep->framerate, GF_MPD_Fractional);
    2121             :                         }
    2122         376 :                         if (ds->rep->framerate) {
    2123         376 :                                 ds->rep->framerate->num = ds->fps.num;
    2124         376 :                                 ds->rep->framerate->den = ds->fps.den;
    2125         376 :                                 gf_media_get_reduced_frame_rate(&ds->rep->framerate->num, &ds->rep->framerate->den);
    2126             :                         }
    2127             :                 }
    2128             :         }
    2129         133 :         else if (ds->stream_type==GF_STREAM_AUDIO) {
    2130             :                 Bool use_cicp = GF_FALSE;
    2131             :                 Bool use_dolbyx = GF_FALSE;
    2132             :                 GF_MPD_Descriptor *desc;
    2133             :                 char value[256];
    2134         131 :                 ds->rep->samplerate = ds->sr;
    2135             : 
    2136         131 :                 if (ds->nb_surround || ds->nb_lfe) use_cicp = GF_TRUE;
    2137         131 :                 if ((ds->codec_id==GF_CODECID_MHAS) || (ds->codec_id==GF_CODECID_MPHA)) use_cicp = GF_TRUE;
    2138             : 
    2139         131 :                 if ((ds->codec_id==GF_CODECID_AC3) || (ds->codec_id==GF_CODECID_EAC3)) {
    2140             :                         //if regular MPEG-DASH, use CICP, otherwise use Dolby signaling
    2141           1 :                         if (ctx->profile > GF_DASH_PROFILE_FULL) {
    2142             :                                 use_dolbyx = GF_TRUE;
    2143             :                         }
    2144             :                 }
    2145             :                 if (use_dolbyx) {
    2146             :                         u32 cicp_layout = 0;
    2147           0 :                         if (ds->ch_layout)
    2148           0 :                                 cicp_layout = gf_audio_fmt_get_cicp_from_layout(ds->ch_layout);
    2149           0 :                         if (!cicp_layout)
    2150           0 :                                 cicp_layout = gf_audio_fmt_get_cicp_layout(ds->nb_ch, ds->nb_surround, ds->nb_lfe);
    2151             : 
    2152           0 :                         sprintf(value, "%X", gf_audio_fmt_get_dolby_chanmap(cicp_layout) );
    2153           0 :                         desc = gf_mpd_descriptor_new(NULL, "tag:dolby.com,2014:dash:audio_channel_configuration:2011", value);
    2154             :                 }
    2155         131 :                 else if (!use_cicp) {
    2156         131 :                         sprintf(value, "%d", ds->nb_ch);
    2157         131 :                         desc = gf_mpd_descriptor_new(NULL, "urn:mpeg:dash:23003:3:audio_channel_configuration:2011", value);
    2158             :                 } else {
    2159           0 :                         sprintf(value, "%d", gf_audio_fmt_get_cicp_layout(ds->nb_ch, ds->nb_surround, ds->nb_lfe));
    2160           0 :                         desc = gf_mpd_descriptor_new(NULL, "urn:mpeg:mpegB:cicp:ChannelConfiguration", value);
    2161             :                 }
    2162             : 
    2163         131 :                 gf_mpd_del_list(ds->rep->audio_channels, gf_mpd_descriptor_free, GF_TRUE);
    2164             : 
    2165         131 :                 gf_list_add(ds->rep->audio_channels, desc);
    2166             :         } else {
    2167             :         }
    2168             : 
    2169         538 :         dasher_get_rfc_6381_codec_name(ctx, ds, szCodec, (ctx->bs_switch==DASHER_BS_SWITCH_INBAND) ? GF_TRUE : GF_FALSE, GF_TRUE);
    2170         538 :         if (ds->rep->codecs) gf_free(ds->rep->codecs);
    2171         538 :         ds->rep->codecs = gf_strdup(szCodec);
    2172             : 
    2173         538 :         if (ds->interlaced) ds->rep->scan_type = GF_MPD_SCANTYPE_INTERLACED;
    2174             :         else {
    2175             :                 //profiles forcing scanType=progressive for progressive
    2176         538 :                 switch (ctx->profile) {
    2177           0 :                 case GF_DASH_PROFILE_HBBTV_1_5_ISOBMF_LIVE:
    2178           0 :                         ds->rep->scan_type = GF_MPD_SCANTYPE_PROGRESSIVE;
    2179           0 :                         break;
    2180             :                 }
    2181             :         }
    2182             : 
    2183         538 :         if (ctx->cp!=GF_DASH_CPMODE_ADAPTATION_SET) {
    2184           0 :                 gf_mpd_del_list(ds->rep->content_protection, gf_mpd_descriptor_free, 0);
    2185           0 :                 ds->rep->content_protection = dasher_get_content_protection_desc(ctx, ds, NULL);
    2186             :         }
    2187         538 : }
    2188             : 
    2189         357 : static void dasher_setup_rep(GF_DasherCtx *ctx, GF_DashStream *ds, u32 *srd_rep_idx)
    2190             : {
    2191             :         const GF_PropertyValue *p;
    2192             : 
    2193             :         assert(ds->rep==NULL);
    2194         357 :         ds->rep = gf_mpd_representation_new();
    2195         357 :         ds->rep->playback.udta = ds;
    2196         357 :         if (ds->tci)
    2197           0 :                 ds->rep->crypto_type = 1;
    2198             :         else
    2199         357 :                 ds->rep->crypto_type = ds->is_encrypted ? 2 : 0;
    2200             : 
    2201         357 :         dasher_update_rep(ctx, ds);
    2202             : 
    2203         357 :         p = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_AS_ID);
    2204             :         //do not reset as id in case of period continuity
    2205         357 :         if (p) {
    2206           0 :                 if (ds->as_id != p->value.uint) {
    2207           0 :                         if (ds->period_continuity_id) gf_free(ds->period_continuity_id);
    2208           0 :                         ds->period_continuity_id = NULL;
    2209             :                 }
    2210           0 :                 ds->as_id = p->value.uint;
    2211             :         }
    2212             : 
    2213         357 :         p = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_REP_ID);
    2214         357 :         if (p) {
    2215         279 :                 if (ds->rep_id) gf_free(ds->rep_id);
    2216             : 
    2217         306 :                 if (!ds->tile_base && (ds->srd.w || ds->srd.z) && !ctx->sseg && !ctx->sfile) {
    2218          27 :                         char *rep_name = gf_malloc(sizeof(char) * (strlen(p->value.string) + 15) );
    2219          27 :                         sprintf(rep_name, "%s_%d", p->value.string, *srd_rep_idx);
    2220          27 :                         ds->rep_id = rep_name;
    2221          27 :                         (*srd_rep_idx) ++;
    2222             :                 } else {
    2223         252 :                         ds->rep_id = gf_strdup(p->value.string);
    2224             :                 }
    2225             : 
    2226          78 :         } else if (!ds->rep_id) {
    2227             :                 char szRepID[20];
    2228          78 :                 sprintf(szRepID, "%d", 1 + gf_list_find(ctx->pids, ds));
    2229          78 :                 ds->rep_id = gf_strdup(szRepID);
    2230             :         }
    2231         357 :         ds->rep->id = gf_strdup(ds->rep_id);
    2232         357 : }
    2233             : 
    2234         258 : static Bool dasher_same_roles(GF_DashStream *ds1, GF_DashStream *ds2)
    2235             : {
    2236             :         const GF_PropStringList *slist;
    2237         258 :         if (ds1->p_role && ds2->p_role) {
    2238           0 :                 if (gf_props_equal(ds1->p_role, ds2->p_role)) return GF_TRUE;
    2239             :         }
    2240         258 :         if (!ds1->p_role && !ds2->p_role)
    2241             :                 return GF_TRUE;
    2242             : 
    2243             :         //special case, if one is set and the other is not, compare with "main" role
    2244           0 :         slist = ds2->p_role ?  &ds2->p_role->value.string_list : &ds1->p_role->value.string_list;
    2245           0 :         if (slist->nb_items==1) {
    2246           0 :                 if (!strcmp(slist->vals[0], "main")) return GF_TRUE;
    2247             :         }
    2248             :         return GF_FALSE;
    2249             : }
    2250             : 
    2251           0 : static u32 dasher_get_next_as_id(GF_DasherCtx *ctx)
    2252             : {
    2253             :         u32 check_id = 1;
    2254           0 :         u32 i, count = gf_list_count(ctx->current_period->streams);
    2255           0 :         for (i=0; i<count; i++) {
    2256           0 :                 GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
    2257           0 :                 if (ds->as_id == check_id) {
    2258           0 :                         check_id++;
    2259             :                         i = -1;
    2260           0 :                         continue;
    2261             :                 }
    2262             :         }
    2263           0 :         return check_id;
    2264             : }
    2265             : 
    2266         316 : static Bool dasher_same_adaptation_set(GF_DasherCtx *ctx, GF_DashStream *ds, GF_DashStream *ds_test)
    2267             : {
    2268             :         const char *lang1, *lang2;
    2269             :         const GF_PropertyValue *p1, *p2;
    2270             : 
    2271             :         //in all forward mode we don't rewrite the manifest, make each source file a single as
    2272         316 :         if (ctx->forward_mode==DASHER_FWD_ALL)
    2273             :                 return GF_FALSE;
    2274             :                 
    2275             :         //muxed representations
    2276         307 :         if (ds_test->muxed_base) {
    2277          10 :                 if (ds_test->muxed_base == ds)
    2278             :                         return GF_TRUE;
    2279             :                 //if muxed base rep has been registered with this AdaptationSet, also register this stream
    2280           0 :                 if (gf_list_find(ds->set->representations, ds_test->muxed_base->rep)>=0)
    2281             :                         return GF_TRUE;
    2282             :         }
    2283             : 
    2284             :         //otherwise we have to be of same type
    2285         297 :         if (ds->stream_type != ds_test->stream_type) return GF_FALSE;
    2286             : 
    2287             :         //not the same roles
    2288         258 :         if (!dasher_same_roles(ds, ds_test)) return GF_FALSE;
    2289             : 
    2290             :         //intra-only trick mode belongs to a separate AS
    2291         258 :         if ((ds->stream_type == GF_STREAM_VISUAL) && (ds->sync_points_type != ds_test->sync_points_type)) {
    2292             :                 //assign trickmode as id for dashif
    2293           0 :                 if (ds_test->sync_points_type == DASHER_SYNC_NONE) {
    2294           0 :                         if (!ds->as_id) ds->as_id = dasher_get_next_as_id(ctx);
    2295           0 :                         ds_test->sync_as_id = ds->as_id;
    2296             :                 }
    2297           0 :                 else if (ds->sync_points_type == DASHER_SYNC_NONE) {
    2298           0 :                         if (!ds_test->as_id) ds_test->as_id = dasher_get_next_as_id(ctx);
    2299           0 :                         ds->sync_as_id = ds_test->as_id;
    2300             :                 }
    2301             :                 return GF_FALSE;
    2302             :         }
    2303             : 
    2304             :         /* if two inputs don't have the same (number and value) as_desc they don't belong to the same AdaptationSet
    2305             :            (use c_as_desc for AdaptationSet descriptors common to all inputs in an AS) */
    2306         258 :         if (!ds->p_as_desc && ds_test->p_as_desc)
    2307             :                 return GF_FALSE;
    2308         258 :         if (ds->p_as_desc && !ds_test->p_as_desc)
    2309             :                 return GF_FALSE;
    2310         258 :         if (ds->p_as_desc && ! gf_props_equal(ds->p_as_desc, ds_test->p_as_desc))
    2311             :                 return GF_FALSE;
    2312             : 
    2313             :         //need same AS ID if specified
    2314         257 :         if (ds->as_id && ds_test->as_id &&(ds->as_id != ds_test->as_id) )
    2315             :                 return GF_FALSE;
    2316             : 
    2317             :         //need same dash duration if aligned
    2318         257 :         if (ctx->align) {
    2319         257 :                 if ((u64) ds->dash_dur.num * ds_test->dash_dur.den != (u64) ds_test->dash_dur.num * ds->dash_dur.den) return GF_FALSE;
    2320             :         }
    2321             : 
    2322             :         //if one of the pid is marked with period resume and the other is not, one is a spliced media the other no
    2323             :         //cf flist filter
    2324         257 :         p1 = gf_filter_pid_get_property_str(ds->ipid, "period_resume");
    2325         257 :         p2 = gf_filter_pid_get_property_str(ds_test->ipid, "period_resume");
    2326         257 :         if ((!p1 && p2) || (p1 && !p2) || (p1 && gf_props_equal(p1, p2)))
    2327             :                 return GF_FALSE;
    2328             : 
    2329         257 :         if (ds->srd.x != ds_test->srd.x) return GF_FALSE;
    2330          92 :         if (ds->srd.y != ds_test->srd.y) return GF_FALSE;
    2331          37 :         if (ds->srd.z != ds_test->srd.z) return GF_FALSE;
    2332          32 :         if (ds->srd.w != ds_test->srd.w) return GF_FALSE;
    2333             : 
    2334          32 :         if (ds->view_id != ds_test->view_id) return GF_FALSE;
    2335             :         //according to DASH spec mixing interlaced and progressive is OK
    2336             :         //if (ds->interlaced != ds_test->interlaced) return GF_FALSE;
    2337          32 :         if (ds->nb_ch != ds_test->nb_ch) return GF_FALSE;
    2338             : 
    2339          32 :         lang1 = ds->lang ? ds->lang : "und";
    2340          32 :         lang2 = ds_test->lang ? ds_test->lang : "und";
    2341          32 :         if (strcmp(lang1, lang2)) return GF_FALSE;
    2342             : 
    2343          30 :         if (ds->stream_type==GF_STREAM_VISUAL) {
    2344             :                 u32 w, h, tw, th;
    2345          30 :                 if (ctx->no_sar) {
    2346           0 :                         w = ds->width;
    2347           0 :                         h = ds->height;
    2348           0 :                         tw = ds_test->width;
    2349           0 :                         th = ds_test->height;
    2350             :                 } else {
    2351          30 :                         w = ds->width * ds->sar.num;
    2352          30 :                         h = ds->height * ds->sar.den;
    2353          30 :                         tw = ds_test->width * ds_test->sar.num;
    2354          30 :                         th = ds_test->height * ds_test->sar.den;
    2355             :                 }
    2356             : 
    2357             :                 //not the same aspect ratio
    2358          30 :                 if (w * th != h * tw)
    2359             :                         return GF_FALSE;
    2360           0 :         } else if (ds->stream_type==GF_STREAM_AUDIO) {
    2361           0 :                 if (!ctx->mix_codecs && (ds->codec_id != ds_test->codec_id) )
    2362             :                         return GF_FALSE;
    2363             :                 //we allow mix of channels config
    2364             :         } else {
    2365           0 :                 if (!ctx->mix_codecs && strcmp(ds->rep->codecs, ds_test->rep->codecs)) return GF_FALSE;
    2366           0 :                 return GF_TRUE;
    2367             :         }
    2368             :         //ok, we are video or audio with mixed codecs
    2369          30 :         if (ctx->mix_codecs) return GF_TRUE;
    2370             :         //we need dependencies, unless SRD case
    2371          30 :         if (!ds_test->srd.z && !ds_test->srd.w) {
    2372          20 :                 if (ds_test->dep_id && (ds_test->src_id==ds->src_id) && gf_list_find(ds->complementary_streams, ds_test) < 0) {
    2373             :                         return GF_FALSE;
    2374             :                 }
    2375             :         }
    2376             :         //we should be good
    2377             :         return GF_TRUE;
    2378             : }
    2379             : 
    2380        1388 : static void dasher_add_descriptors(GF_List **p_dst_list, const GF_PropertyValue *desc_val)
    2381             : {
    2382             :         u32 j, count;
    2383             :         GF_List *dst_list;
    2384        1388 :         if (!desc_val) return;
    2385           8 :         if (desc_val->type != GF_PROP_STRING_LIST) return;
    2386           8 :         count = desc_val->value.string_list.nb_items;
    2387           8 :         if (!count) return;
    2388           8 :         if ( ! (*p_dst_list)) *p_dst_list = gf_list_new();
    2389           8 :         dst_list = *p_dst_list;
    2390          16 :         for (j=0; j<count; j++) {
    2391           8 :                 char *desc = desc_val->value.string_list.vals[j];
    2392           8 :                 if (desc[0] == '<') {
    2393             :                         GF_XMLNode *d;
    2394           8 :                         GF_SAFEALLOC(d, GF_XMLNode);
    2395           8 :                         if (d) {
    2396           8 :                                 d->type = GF_XML_TEXT_TYPE;
    2397           8 :                                 d->name = gf_strdup(desc);
    2398           8 :                                 gf_list_add(dst_list, d);
    2399             :                         }
    2400             :                 } else {
    2401           0 :                         GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Invalid descriptor %s, expecting '<' as first character\n", desc));
    2402             :                 }
    2403             :         }
    2404             : }
    2405             : 
    2406         317 : static void dasher_setup_set_defaults(GF_DasherCtx *ctx, GF_MPD_AdaptationSet *set)
    2407             : {
    2408             :         u32 i, count;
    2409             :         Bool main_role_set = GF_FALSE;
    2410             :         //by default setup alignment
    2411         317 :         if (ctx->sseg) set->subsegment_alignment = ctx->align;
    2412         296 :         else set->segment_alignment = ctx->align;
    2413             : 
    2414             :         //startWithSAP is set when the first packet comes in
    2415             : 
    2416             :         //the rest depends on the various profiles/iop, to check
    2417         317 :         count = gf_list_count(set->representations);
    2418         356 :         for (i=0; i<count; i++) {
    2419         356 :                 GF_MPD_Representation *rep = gf_list_get(set->representations, i);
    2420         356 :                 GF_DashStream *ds = rep->playback.udta;
    2421             : 
    2422         356 :                 if (set->max_width < ds->width) set->max_width = ds->width;
    2423         356 :                 if (set->max_height < ds->height) set->max_height = ds->height;
    2424             : /*              if (set->max_bandwidth < ds->rep->bandwidth) set->max_bandwidth = ds->rep->bandwidth;
    2425             :                 if (set->max_framerate * ds->fps.den < ds->fps.num) set->max_framerate = (u32) (ds->fps.num / ds->fps.den);
    2426             : */
    2427             : 
    2428             :                 /*set trick mode*/
    2429         356 :                 if (set->intra_only && (ds->stream_type==GF_STREAM_VISUAL)) {
    2430             :                         char value[256];
    2431             :                         GF_MPD_Descriptor* desc;
    2432           1 :                         sprintf(value, "%d", ds->sync_as_id);
    2433           1 :                         desc = gf_mpd_descriptor_new(NULL, "http://dashif.org/guidelines/trickmode", value);
    2434           1 :                         gf_list_add(set->essential_properties, desc);
    2435             :                 }
    2436             :                 /*set role*/
    2437         356 :                 if (ds->p_role) {
    2438             :                         u32 j, role_count;
    2439           0 :                         role_count = ds->p_role->value.string_list.nb_items;
    2440           0 :                         for (j=0; j<role_count; j++) {
    2441           0 :                                 char *role = ds->p_role->value.string_list.vals[j];
    2442             :                                 GF_MPD_Descriptor *desc;
    2443             :                                 char *uri;
    2444           0 :                                 if (!strcmp(role, "caption") || !strcmp(role, "subtitle") || !strcmp(role, "main")
    2445           0 :                                 || !strcmp(role, "alternate") || !strcmp(role, "supplementary") || !strcmp(role, "commentary")
    2446           0 :                                 || !strcmp(role, "dub") || !strcmp(role, "description") || !strcmp(role, "sign")
    2447           0 :                                          || !strcmp(role, "metadata") || !strcmp(role, "enhanced-audio- intelligibility")
    2448             :                                 ) {
    2449             :                                         uri = "urn:mpeg:dash:role:2011";
    2450           0 :                                         if (!strcmp(role, "main")) main_role_set = GF_TRUE;
    2451             :                                 } else {
    2452           0 :                                         GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Unrecognized role %s - using GPAC urn for schemaID\n", role));
    2453             :                                         uri = "urn:gpac:dash:role:2013";
    2454             :                                 }
    2455           0 :                                 desc = gf_mpd_descriptor_new(NULL, uri, role);
    2456           0 :                                 gf_list_add(set->role, desc);
    2457             :                         }
    2458             :                 }
    2459             :                 //set SRD
    2460         356 :                 if (!i && ds->srd.z && ds->srd.w) {
    2461             :                         char value[256];
    2462             :                         GF_MPD_Descriptor *desc;
    2463          30 :                         if (ds->dep_id) {
    2464          27 :                                 sprintf(value, "1,%d,%d,%d,%d", ds->srd.x, ds->srd.y, ds->srd.z, ds->srd.w);
    2465          27 :                                 desc = gf_mpd_descriptor_new(NULL, "urn:mpeg:dash:srd:2014", value);
    2466          27 :                                 gf_list_add(set->supplemental_properties, desc);
    2467             :                         } else {
    2468             :                                 sprintf(value, "1,0,0,0,0,%d,%d", ds->srd.z, ds->srd.w);
    2469           3 :                                 desc = gf_mpd_descriptor_new(NULL, "urn:mpeg:dash:srd:2014", value);
    2470           3 :                                 gf_list_add(set->essential_properties, desc);
    2471             :                         }
    2472             :                 }
    2473             :                 //set HDR
    2474         356 :                 if (ds->hdr_type > DASHER_HDR_NONE) {
    2475             :                         char value[256];
    2476             :                         GF_MPD_Descriptor* desc;
    2477             :                         sprintf(value, "9");
    2478           0 :                         desc = gf_mpd_descriptor_new(NULL, "urn:mpeg:mpegB:cicp:ColourPrimaries", value);
    2479           0 :                         gf_list_add(set->essential_properties, desc);
    2480             :                         sprintf(value, "9");
    2481           0 :                         desc = gf_mpd_descriptor_new(NULL, "urn:mpeg:mpegB:cicp:MatrixCoefficients", value);
    2482           0 :                         gf_list_add(set->essential_properties, desc);
    2483             : 
    2484           0 :                         if (ds->hdr_type==DASHER_HDR_PQ10) {
    2485             :                                 sprintf(value, "16");
    2486           0 :                                 desc = gf_mpd_descriptor_new(NULL, "urn:mpeg:mpegB:cicp:TransferCharacteristics", value);
    2487           0 :                                 gf_list_add(set->essential_properties, desc);
    2488             :                         }
    2489             : 
    2490           0 :                         if (ds->hdr_type == DASHER_HDR_HLG) {
    2491             :                                 sprintf(value, "14");
    2492           0 :                                 desc = gf_mpd_descriptor_new(NULL, "urn:mpeg:mpegB:cicp:TransferCharacteristics", value);
    2493           0 :                                 gf_list_add(set->essential_properties, desc);
    2494             :                                 sprintf(value, "18");
    2495           0 :                                 desc = gf_mpd_descriptor_new(NULL, "urn:mpeg:mpegB:cicp:TransferCharacteristics", value);
    2496           0 :                                 gf_list_add(set->supplemental_properties, desc);
    2497             :                         }
    2498             :                 }
    2499             :         }
    2500         317 :         if (ctx->check_main_role && !main_role_set) {
    2501             :                 GF_MPD_Descriptor *desc;
    2502           0 :                 desc = gf_mpd_descriptor_new(NULL, "urn:mpeg:dash:role:2011", "main");
    2503           0 :                 gf_list_add(set->role, desc);
    2504             :         }
    2505         317 : }
    2506             : 
    2507           1 : static void rewrite_dep_ids(GF_DasherCtx *ctx, GF_DashStream *base_ds)
    2508             : {
    2509           1 :         u32 i, count = gf_list_count(ctx->pids);
    2510          20 :         for (i=0; i<count; i++) {
    2511          20 :                 GF_DashStream *ds = gf_list_get(ctx->pids, i);
    2512          20 :                 if (ds->src_id != base_ds->src_id) continue;
    2513          10 :                 if (!ds->dep_id || !ds->rep) continue;
    2514           9 :                 if (ds->dep_id != base_ds->id) continue;
    2515             : 
    2516           9 :                 ds->tile_dep_id_merged = GF_TRUE;
    2517           9 :                 if (ds->rep->dependency_id) gf_free(ds->rep->dependency_id);
    2518           9 :                 ds->rep->dependency_id = gf_strdup(base_ds->merged_tile_dep->rep->id);
    2519             :         }
    2520           1 : }
    2521             : 
    2522         317 : static void dasher_check_bitstream_swicthing(GF_DasherCtx *ctx, GF_MPD_AdaptationSet *set)
    2523             : {
    2524             :         u32 i, j, count;
    2525         317 :         Bool use_inband = (ctx->bs_switch==DASHER_BS_SWITCH_INBAND) ? GF_TRUE : GF_FALSE;
    2526         317 :         Bool use_multi = (ctx->bs_switch==DASHER_BS_SWITCH_MULTI) ? GF_TRUE : GF_FALSE;
    2527         317 :         GF_MPD_Representation *base_rep = gf_list_get(set->representations, 0);
    2528             :         GF_DashStream *base_ds;
    2529             : 
    2530         317 :         if (ctx->m2ts) {
    2531           8 :                 set->bitstream_switching = GF_TRUE;
    2532             :                 return;
    2533             :         }
    2534         309 :         if (ctx->bs_switch==DASHER_BS_SWITCH_OFF) return;
    2535         287 :         if (!base_rep) return;
    2536         287 :         base_ds = base_rep->playback.udta;
    2537             : 
    2538         287 :         count = gf_list_count(set->representations);
    2539         287 :         if (count==1) {
    2540         253 :                 if (ctx->bs_switch==DASHER_BS_SWITCH_FORCE) set->bitstream_switching=GF_TRUE;
    2541         251 :                 else if (ctx->bs_switch==DASHER_BS_SWITCH_INBAND) {
    2542           0 :                         base_ds->inband_params = GF_TRUE;
    2543             :                 }
    2544             :                 return;
    2545             :         }
    2546             : 
    2547          15 :         for (i=1; i<count; i++) {
    2548          34 :                 GF_MPD_Representation *rep = gf_list_get(set->representations, i);
    2549          34 :                 GF_DashStream *ds = rep->playback.udta;
    2550             :                 //same codec ID
    2551          34 :                 if (ds->codec_id == base_ds->codec_id) {
    2552             :                         //we will use inband params, so bs switching is OK
    2553          30 :                         if (use_inband || use_multi) continue;
    2554             :                         //we have deps, cannot use bitstream switching except for merged tile base
    2555          30 :                         if (ds->dep_id) {
    2556          10 :                                 if (ds->codec_id==GF_CODECID_HEVC_TILES) {
    2557             :                                         u32 id;
    2558           9 :                                         GF_DashStream *tile_base = get_base_ds(ctx, ds);
    2559           9 :                                         if (!tile_base) return;
    2560           9 :                                         id = tile_base->merged_tile_dep ? tile_base->merged_tile_dep->id : tile_base->id;
    2561           9 :                                         if (base_ds->dep_id==id) continue;
    2562             :                                 }
    2563             :                                 return;
    2564             :                         }
    2565             :                         //we consider we can switch in non-inband only if we have same CRC for the decoder config
    2566          20 :                         if (base_ds->dsi_crc == ds->dsi_crc) continue;
    2567             :                         //not the same config, no BS switching
    2568             :                         return;
    2569             :                 }
    2570             :                 //dependencies / different codec IDs, cannot use bitstream switching
    2571             :                 return;
    2572             :         }
    2573             :         //ok we can use BS switching, ensure we use the same timescale for every stream
    2574          15 :         set->bitstream_switching = GF_TRUE;
    2575             : 
    2576          45 :         for (i=0; i<count; i++) {
    2577          30 :                 GF_MPD_Representation *rep = gf_list_get(set->representations, i);
    2578          30 :                 GF_DashStream *ds = rep->playback.udta;
    2579          30 :                 if (base_ds->tile_base && ds->tile_base && (base_ds != ds) ) {
    2580           1 :                         ds->merged_tile_dep = base_ds;
    2581           1 :                         if (ds->rep) {
    2582           1 :                                 gf_list_rem(set->representations, i);
    2583           1 :                                 i--;
    2584           1 :                                 count--;
    2585           1 :                                 gf_mpd_representation_free(ds->rep);
    2586           1 :                                 ds->rep = NULL;
    2587             :                                 //switch dependencyID of all reps depending on this one to the new base
    2588           1 :                                 rewrite_dep_ids(ctx, ds);
    2589             :                         }
    2590             :                 }
    2591          45 :                 for (j=i+1; j<count; j++) {
    2592             :                         GF_DashStream *a_ds;
    2593          15 :                         rep = gf_list_get(set->representations, j);
    2594          15 :                         a_ds = rep->playback.udta;
    2595          15 :                         if (a_ds->stream_type != ds->stream_type) continue;
    2596          15 :                         if (a_ds->timescale != ds->timescale)
    2597           0 :                                 a_ds->force_timescale = ds->timescale;
    2598             :                 }
    2599             :         }
    2600             : }
    2601             : 
    2602             : GF_Err gf_cryptfout_push_key(GF_Filter *filter, bin128 *key, bin128 *IV);
    2603             : 
    2604         349 : static void dasher_open_destination(GF_Filter *filter, GF_DasherCtx *ctx, GF_MPD_Representation *rep, const char *szInitURL, Bool trash_init)
    2605             : {
    2606             :         GF_Err e;
    2607             :         Bool has_frag=GF_FALSE;
    2608             :         Bool has_subs=GF_FALSE;
    2609             :         Bool has_strun=GF_FALSE;
    2610             :         Bool has_vodcache=GF_FALSE;
    2611             :         Bool has_cmaf=GF_FALSE;
    2612         349 :         char sep_args = gf_filter_get_sep(filter, GF_FS_SEP_ARGS);
    2613         349 :         char sep_name = gf_filter_get_sep(filter, GF_FS_SEP_NAME);
    2614             :         const char *dst_args, *trailer_args=NULL;
    2615         349 :         char *szDST = NULL;
    2616             :         char szSRC[100];
    2617             : 
    2618         349 :         if (ctx->sigfrag)
    2619           6 :                 return;
    2620             : 
    2621         343 :         GF_DashStream *ds = rep->playback.udta;
    2622         343 :         if (ds->muxed_base) return;
    2623             : 
    2624             : 
    2625         343 :         ctx->check_connections = GF_TRUE;
    2626         343 :         gf_dynstrcat(&szDST, szInitURL, NULL);
    2627         343 :         if (ctx->out_path) {
    2628             :                 char *rel = NULL;
    2629         343 :                 if (ctx->do_m3u8 && ds->hls_vp_name) {
    2630           0 :                         char *tmp = gf_url_concatenate(ctx->out_path, ds->hls_vp_name);
    2631           0 :                         if (tmp) {
    2632           0 :                                 rel = gf_url_concatenate(tmp, szInitURL);
    2633           0 :                                 gf_free(tmp);
    2634             :                         }
    2635             :                 }
    2636           0 :                 if (!rel)
    2637         343 :                         rel = gf_url_concatenate(ctx->out_path, szInitURL);
    2638         343 :                 if (rel) {
    2639         343 :                         gf_free(szDST);
    2640         343 :                         szDST = rel;
    2641             :                 }
    2642             :         }
    2643         343 :         if (ds->tci) {
    2644           0 :                 char *tmp = szDST;
    2645           0 :                 szDST = NULL;
    2646           0 :                 gf_dynstrcat(&szDST, "gcryp://", NULL);
    2647           0 :                 gf_dynstrcat(&szDST, tmp, NULL);
    2648           0 :                 gf_free(tmp);
    2649             :         }
    2650             : 
    2651         343 :         sprintf(szSRC, "%cgfopt", sep_args);
    2652         343 :         gf_dynstrcat(&szDST, szSRC, NULL);
    2653             : 
    2654         343 :         dst_args = gf_filter_get_dst_args(filter);
    2655         343 :         if (dst_args) {
    2656             :                 char szKey[20], *sep;
    2657             :                 sprintf(szSRC, "%c", sep_args);
    2658         282 :                 gf_dynstrcat(&szDST, szSRC, NULL);
    2659             :                 
    2660         282 :                 gf_dynstrcat(&szDST, dst_args, NULL);
    2661             :                 sprintf(szKey, "%c%c", sep_args, sep_args);
    2662         282 :                 sep = strstr(szDST, szKey);
    2663         282 :                 if (sep) {
    2664           1 :                         sep[0] = 0;
    2665           1 :                         trailer_args = strstr(dst_args, szKey);
    2666             :                 }
    2667             :                 //look for frag arg
    2668             :                 sprintf(szKey, "%cfrag", sep_args);
    2669         282 :                 if (strstr(dst_args, szKey)) has_frag = GF_TRUE;
    2670             :                 else {
    2671             :                         sprintf(szKey, "%csfrag", sep_args);
    2672         282 :                         if (strstr(dst_args, szKey)) has_frag = GF_TRUE;
    2673             :                 }
    2674             :                 //look for subs_sidx arg
    2675             :                 sprintf(szKey, "%csubs_sidx", sep_args);
    2676         282 :                 if (strstr(dst_args, szKey)) has_subs = GF_TRUE;
    2677             : 
    2678             :                 sprintf(szKey, "%cstrun", sep_args);
    2679         282 :                 if (strstr(dst_args, szKey)) has_strun = GF_TRUE;
    2680             : 
    2681             :                 sprintf(szKey, "%cvodcache", sep_args);
    2682         282 :                 if (strstr(dst_args, szKey)) has_vodcache = GF_TRUE;
    2683             : 
    2684             :                 sprintf(szKey, "%ccmaf", sep_args);
    2685         282 :                 if (strstr(dst_args, szKey)) has_cmaf = GF_TRUE;
    2686             :         }
    2687             : 
    2688         343 :         if (trash_init) {
    2689             :                 sprintf(szSRC, "%cnoinit", sep_args);
    2690          56 :                 gf_dynstrcat(&szDST, szSRC, NULL);
    2691             :         }
    2692         343 :         if (!has_frag) {
    2693             :                 sprintf(szSRC, "%cfrag", sep_args);
    2694         343 :                 gf_dynstrcat(&szDST, szSRC, NULL);
    2695             :         }
    2696         343 :         if (!ctx->forward_mode) {
    2697         316 :                 if (!has_subs && ctx->sseg) {
    2698           4 :                         sprintf(szSRC, "%csubs_sidx%c0", sep_args, sep_name);
    2699           4 :                         gf_dynstrcat(&szDST, szSRC, NULL);
    2700             :                 }
    2701         316 :                 if (ctx->cues && !has_strun) {
    2702             :                         sprintf(szSRC, "%cstrun", sep_args);
    2703          10 :                         gf_dynstrcat(&szDST, szSRC, NULL);
    2704             :                 }
    2705         316 :                 if (ctx->styp) {
    2706             :                         sprintf(szSRC, "%cstyp=%s", sep_args, ctx->styp);
    2707           0 :                         gf_dynstrcat(&szDST, szSRC, NULL);
    2708             :                 }
    2709             :         }
    2710             : 
    2711             :         //override xps inband declaration in args
    2712         343 :         sprintf(szSRC, "%cxps_inband%c%s", sep_args, sep_name, ds->inband_params ? "all" : "no");
    2713         343 :         gf_dynstrcat(&szDST, szSRC, NULL);
    2714             : 
    2715         343 :         if (ctx->no_fragments_defaults) {
    2716             :                 sprintf(szSRC, "%cnofragdef", sep_args );
    2717          10 :                 gf_dynstrcat(&szDST, szSRC, NULL);
    2718             :         }
    2719         343 :         switch (ctx->pssh) {
    2720           0 :         case GF_DASH_PSSH_MPD:
    2721             :                 sprintf(szSRC, "%cpsshs%cnone", sep_args, sep_name);
    2722             :                 break;
    2723           0 :         case GF_DASH_PSSH_MOOF:
    2724             :         case GF_DASH_PSSH_MOOF_MPD:
    2725             :                 sprintf(szSRC, "%cpsshs%cmoof", sep_args, sep_name);
    2726             :                 break;
    2727         343 :         default:
    2728             :                 sprintf(szSRC, "%cpsshs%cmoov", sep_args, sep_name);
    2729             :                 break;
    2730             :         }
    2731         343 :         gf_dynstrcat(&szDST, szSRC, NULL);
    2732             : 
    2733             :         //patch for old arch: make sure we don't have any extra free box before the sidx
    2734             :         //we could also use vodcache=insert but this might break http outputs
    2735         343 :         if (gf_sys_old_arch_compat() && !has_vodcache && ctx->sseg) {
    2736             :                 sprintf(szSRC, "%cvodcache=on", sep_args );
    2737          19 :                 if (!strstr(szDST, szSRC))
    2738          19 :                         gf_dynstrcat(&szDST, szSRC, NULL);
    2739             :         }
    2740             : 
    2741             :         //we don't append mime in case of raw streams, raw format (writegen doesn't use mime types for raw media, only file ext)
    2742         343 :         if ((ctx->muxtype!=DASHER_MUX_RAW) || (ds->codec_id != GF_CODECID_RAW)) {
    2743         343 :                 sprintf(szSRC, "%cmime=%s", sep_args, rep->mime_type);
    2744         343 :                 gf_dynstrcat(&szDST, szSRC, NULL);
    2745             :         }
    2746             : 
    2747         343 :         if (ds->moof_sn>1) {
    2748             :                 sprintf(szSRC, "%cmsn%c%d", sep_args, sep_name, ds->moof_sn);
    2749          36 :                 gf_dynstrcat(&szDST, szSRC, NULL);
    2750             :         }
    2751         343 :         if (ds->moof_sn_inc>1) {
    2752             :                 sprintf(szSRC, "%cmsninc%c%d", sep_args, sep_name, ds->moof_sn_inc);
    2753          41 :                 gf_dynstrcat(&szDST, szSRC, NULL);
    2754             :         }
    2755         343 :         if (ds->sscale) {
    2756             :                 sprintf(szSRC, "%cmoovts%c-1", sep_args, sep_name);
    2757           2 :                 gf_dynstrcat(&szDST, szSRC, NULL);
    2758             :         }
    2759             : 
    2760         343 :         if (!has_cmaf && ctx->cmaf) {
    2761           0 :                 sprintf(szSRC, "%ccmaf%c%s", sep_args, sep_name, (ctx->cmaf==DASHER_CMAF_CMF2) ? "cmf2" : "cmfc");
    2762           0 :                 gf_dynstrcat(&szDST, szSRC, NULL);
    2763             :         }
    2764             : 
    2765             : 
    2766         343 :         if (trailer_args)
    2767           1 :                 gf_dynstrcat(&szDST, trailer_args, NULL);
    2768             :                 
    2769         343 :         ds->dst_filter = gf_filter_connect_destination(filter, szDST, &e);
    2770         343 :         gf_free(szDST);
    2771         343 :         szDST = NULL;
    2772         343 :         if (e) {
    2773           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Couldn't create output file %s: %s\n", szInitURL, gf_error_to_string(e) ));
    2774           0 :                 ctx->in_error = GF_TRUE;
    2775             :                 return;
    2776             :         }
    2777             :         //reset any sourceID given in the dst_arg and assign sourceID to be the dasher filter
    2778         343 :         sprintf(szSRC, "MuxSrc%cdasher_%p", sep_name, ds->dst_filter);
    2779         343 :         gf_filter_reset_source(ds->dst_filter);
    2780         343 :         gf_filter_set_source(ds->dst_filter, filter, szSRC);
    2781             : 
    2782         343 :         if (ds->tci && !trash_init) {
    2783             :                 //push NULL key, we are not encrypting the init segment
    2784           0 :                 gf_cryptfout_push_key(ds->dst_filter, NULL, NULL);
    2785             :         }
    2786             : }
    2787             : 
    2788           3 : static void dasher_gather_deps(GF_DasherCtx *ctx, u32 dependency_id, GF_List *multi_tracks)
    2789             : {
    2790           3 :         u32 i, count = gf_list_count(ctx->current_period->streams);
    2791          11 :         for (i=0; i<count; i++) {
    2792           8 :                 GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
    2793           8 :                 if (ds->id == dependency_id) {
    2794           3 :                         if (ds->tile_base) continue;
    2795             : 
    2796             :                         assert(ds->opid);
    2797           3 :                         gf_list_insert(multi_tracks, ds->opid, 0);
    2798           3 :                         if (ds->dep_id) dasher_gather_deps(ctx, ds->dep_id, multi_tracks);
    2799             :                 }
    2800             :         }
    2801           3 : }
    2802             : 
    2803          16 : static void dasher_update_dep_list(GF_DasherCtx *ctx, GF_DashStream *ds, const char *ref_type)
    2804             : {
    2805             :         u32 i, j, count, base_id;
    2806          16 :         GF_PropertyValue *p = (GF_PropertyValue *) gf_filter_pid_get_property_str(ds->opid, ref_type);
    2807          16 :         if (!p) return;
    2808           0 :         base_id = ds->dep_id ? ds->dep_id : ds->id;
    2809           0 :         count = gf_list_count(ctx->current_period->streams);
    2810           0 :         for (i=0; i<p->value.uint_list.nb_items; i++) {
    2811           0 :                 for (j=0; j<count; j++) {
    2812           0 :                         GF_DashStream *a_ds = gf_list_get(ctx->current_period->streams, j);
    2813           0 :                         if (a_ds->dep_id != base_id) continue;
    2814           0 :                         if ((a_ds->id == p->value.uint_list.vals[i]) && a_ds->pid_id) {
    2815           0 :                                 p->value.uint_list.vals[i] = a_ds->pid_id;
    2816             :                         }
    2817             :                 }
    2818             :         }
    2819             : }
    2820             : 
    2821         364 : static void dasher_open_pid(GF_Filter *filter, GF_DasherCtx *ctx, GF_DashStream *ds, GF_List *multi_pids, Bool init_trashed)
    2822             : {
    2823         364 :         GF_DashStream *base_ds = ds->muxed_base ? ds->muxed_base : ds;
    2824             :         char szSRC[1024];
    2825             : 
    2826         364 :         if (ctx->sigfrag || ctx->in_error)
    2827           9 :                 return;
    2828             : 
    2829             :         assert(!ds->opid);
    2830             :         assert(base_ds->dst_filter);
    2831             : 
    2832         361 :         if (ds->tile_base && !init_trashed) {
    2833           6 :                 s32 res = gf_list_find(ctx->postponed_pids, ds);
    2834           6 :                 if (res < 0) {
    2835           3 :                         gf_list_add(ctx->postponed_pids, ds);
    2836           3 :                         return;
    2837             :                 } else {
    2838           3 :                         gf_list_rem(ctx->postponed_pids, res);
    2839             :                 }
    2840         352 :         } else if (!ds->tile_base) {
    2841         352 :                 gf_list_del_item(ctx->postponed_pids, ds);
    2842             :         }
    2843             : 
    2844             :         //tile base not live profile, make sure all our deps are ready
    2845         355 :         if (ds->tile_base && !ctx->sseg) {
    2846           3 :                 u32 i, count = gf_list_count(ds->complementary_streams);
    2847          30 :                 for (i=0; i<count; i++) {
    2848          27 :                         GF_DashStream *a_ds = gf_list_get(ds->complementary_streams, i);
    2849             :                         //dep not ready
    2850          27 :                         if (!a_ds->opid) {
    2851           0 :                                 if (gf_list_find(ctx->postponed_pids, a_ds)<0) {
    2852           0 :                                         gf_list_add(ctx->postponed_pids, a_ds);
    2853             :                                 }
    2854           0 :                                 gf_list_del_item(ctx->postponed_pids, ds);
    2855           0 :                                 gf_list_add(ctx->postponed_pids, ds);
    2856           0 :                                 return;
    2857             :                         }
    2858             :                 }
    2859             :         }
    2860             : 
    2861         355 :         if (ctx->sigfrag)
    2862             :                 return;
    2863             : 
    2864         355 :         sprintf(szSRC, "dasher_%p", base_ds->dst_filter);
    2865         355 :         ds->opid = gf_filter_pid_new(filter);
    2866             : 
    2867             : #ifdef GPAC_64_BITS
    2868         355 :         ds->hls_ref_id = (u64) ds->opid;
    2869             : #else
    2870             :         ds->hls_ref_id = (u64) ((u32) ds->opid);
    2871             : #endif
    2872             : 
    2873         355 :         gf_filter_pid_copy_properties(ds->opid, ds->ipid);
    2874         355 :         if (!ds->muxed_base) {
    2875         343 :                 gf_filter_pid_set_property(ds->opid, GF_PROP_PID_FILE_EXT, &PROP_STRING("*"));
    2876         343 :                 gf_filter_pid_set_property(ds->opid, GF_PROP_PID_MIME, &PROP_STRING(ds->rep->mime_type));
    2877             :         }
    2878         355 :         if (ds->nb_cues) {
    2879             :                 u32 ncues = ds->nb_cues;
    2880          10 :                 if ((ds->cues[0].sample_num>0) || (ds->cues[0].cts>0) || (ds->cues[0].dts>0))
    2881          10 :                         ncues++;
    2882          10 :                 gf_filter_pid_set_property(ds->opid, GF_PROP_PID_DASH_SEGMENTS, &PROP_UINT(ncues) );
    2883             :         }
    2884             :         //for route out
    2885         355 :         if (ctx->is_route) {
    2886           4 :                 if (ctx->do_m3u8)
    2887           0 :                         gf_filter_pid_set_property(ds->opid, GF_PROP_PCK_HLS_REF, &PROP_LONGUINT( ds->hls_ref_id ) );
    2888             : 
    2889           4 :                 gf_filter_pid_set_property(ds->opid, GF_PROP_PID_REP_ID, &PROP_STRING( ds->rep->id ) );
    2890             : 
    2891             : 
    2892           4 :                 gf_filter_pid_set_property(ds->opid, GF_PROP_PID_DASH_DUR, &PROP_FRAC( ds->dash_dur ) );
    2893             :         }
    2894             : 
    2895         355 :         gf_filter_pid_require_source_id(ds->opid);
    2896             : 
    2897         355 :         if (ctx->pssh == GF_DASH_PSSH_MPD) {
    2898           0 :                 gf_filter_pid_set_property(ds->opid, GF_PROP_PID_CENC_PSSH, NULL);
    2899             :         }
    2900             : 
    2901             : 
    2902             :         //force PID ID
    2903         355 :         gf_filter_pid_set_property(ds->opid, GF_PROP_PID_ID, &PROP_UINT(ds->pid_id) );
    2904         355 :         if (ds->dep_pid_id)
    2905           0 :                 gf_filter_pid_set_property(ds->opid, GF_PROP_PID_DEPENDENCY_ID, &PROP_UINT(ds->dep_pid_id) );
    2906         355 :         gf_filter_pid_set_property(ds->opid, GF_PROP_PID_MUX_SRC, &PROP_STRING(szSRC) );
    2907         355 :         gf_filter_pid_set_property(ds->opid, GF_PROP_PID_DASH_MODE, &PROP_UINT(ctx->sseg ? 2 : 1) );
    2908         355 :         gf_filter_pid_set_property(ds->opid, GF_PROP_PID_DASH_DUR, &PROP_FRAC(ds->dash_dur) );
    2909             : 
    2910         355 :         if (ds->id != ds->pid_id) {
    2911           8 :                 dasher_update_dep_list(ctx, ds, "isom:scal");
    2912           8 :                 dasher_update_dep_list(ctx, ds, "isom:sabt");
    2913             :         }
    2914             : 
    2915             :         /*timescale forced (bitstream switching) */
    2916         355 :         if (ds->force_timescale)
    2917           0 :                 gf_filter_pid_set_property(ds->opid, GF_PROP_PID_TIMESCALE, &PROP_UINT(ds->force_timescale) );
    2918             : 
    2919         355 :         if (ds->rep && ds->rep->segment_template)
    2920          57 :                 gf_filter_pid_set_property(ds->opid, GF_PROP_PID_TEMPLATE, &PROP_STRING(ds->rep->segment_template->media));
    2921         298 :         else if (ds->set && ds->set->segment_template)
    2922         237 :                 gf_filter_pid_set_property(ds->opid, GF_PROP_PID_TEMPLATE, &PROP_STRING(ds->set->segment_template->media));
    2923             : 
    2924         355 :         gf_filter_pid_set_property(ds->opid, GF_PROP_PID_BITRATE, &PROP_UINT(ds->bitrate));
    2925         355 :         gf_filter_pid_set_property(ds->opid, GF_PROP_PCK_FILENAME, &PROP_STRING(ds->init_seg));
    2926             : 
    2927         355 :         if (ds->rep && ds->rep->codecs)
    2928         343 :                 gf_filter_pid_set_property(ds->opid, GF_PROP_PID_CODEC, &PROP_STRING(ds->rep->codecs));
    2929             : 
    2930             : 
    2931         355 :         if (multi_pids) {
    2932           0 :                 s32 idx = 1+gf_list_find(multi_pids, ds->ipid);
    2933             :                 assert(idx>0);
    2934           0 :                 gf_filter_pid_set_property(ds->opid, GF_PROP_PID_DASH_MULTI_PID, &PROP_POINTER(multi_pids) );
    2935           0 :                 gf_filter_pid_set_property(ds->opid, GF_PROP_PID_DASH_MULTI_PID_IDX, &PROP_UINT(idx) );
    2936             :         }
    2937             : 
    2938             : 
    2939         355 :         if (ds->tile_base && !ctx->sseg && !ctx->sfile) {
    2940           3 :                 u32 i, count = gf_list_count(ds->complementary_streams);
    2941           3 :                 if (!ds->multi_tracks) ds->multi_tracks = gf_list_new();
    2942           3 :                 gf_list_reset(ds->multi_tracks);
    2943             : 
    2944             :                 //gather all streams depending on our base
    2945          30 :                 for (i=0; i<count; i++) {
    2946          27 :                         GF_DashStream *a_ds = gf_list_get(ds->complementary_streams, i);
    2947             :                         assert(a_ds->opid);
    2948          27 :                         gf_list_add(ds->multi_tracks, a_ds->opid);
    2949             :                 }
    2950           3 :                 gf_filter_pid_set_property(ds->opid, GF_PROP_PID_DASH_MULTI_TRACK, &PROP_POINTER(ds->multi_tracks) );
    2951             :         }
    2952         355 :         if (ds->dep_id && !init_trashed) {
    2953           3 :                 if (!ds->multi_tracks) ds->multi_tracks = gf_list_new();
    2954           3 :                 gf_list_reset(ds->multi_tracks);
    2955           3 :                 dasher_gather_deps(ctx, ds->dep_id, ds->multi_tracks);
    2956           3 :                 if (gf_list_count(ds->multi_tracks)) {
    2957           3 :                         gf_filter_pid_set_property(ds->opid, GF_PROP_PID_DASH_MULTI_TRACK, &PROP_POINTER(ds->multi_tracks) );
    2958             :                 } else {
    2959           0 :                         gf_list_del(ds->multi_tracks);
    2960           0 :                         ds->multi_tracks = NULL;
    2961             :                 }
    2962             :         }
    2963             : 
    2964         355 :         if (ctx->llhls) {
    2965          12 :                 gf_filter_pid_set_property(ds->opid, GF_PROP_PID_LLHLS, &PROP_UINT(ctx->llhls) );
    2966             :         }
    2967             : }
    2968             : 
    2969         880 : static Bool dasher_template_use_source_url(const char *template)
    2970             : {
    2971         880 :         if (strstr(template, "$File$") != NULL) return GF_TRUE;
    2972         111 :         else if (strstr(template, "$FSRC$") != NULL) return GF_TRUE;
    2973         111 :         else if (strstr(template, "$SourcePath$") != NULL) return GF_TRUE;
    2974         111 :         else if (strstr(template, "$FURL$") != NULL) return GF_TRUE;
    2975         111 :         else if (strstr(template, "$URL$") != NULL) return GF_TRUE;
    2976         111 :         return GF_FALSE;
    2977             : }
    2978             : 
    2979          19 : static void dasher_set_content_components(GF_DashStream *ds)
    2980             : {
    2981             :         GF_MPD_ContentComponent *component;
    2982          19 :         GF_DashStream *base_ds = ds->muxed_base ? ds->muxed_base : ds;
    2983             : 
    2984          19 :         GF_SAFEALLOC(component, GF_MPD_ContentComponent);
    2985          19 :         if (!component) return;
    2986             : 
    2987          19 :         component->id = ds->pid_id;
    2988          19 :         switch (ds->stream_type) {
    2989           0 :         case GF_STREAM_TEXT:
    2990           0 :                 component->type = gf_strdup("text");
    2991           0 :                 break;
    2992          12 :         case GF_STREAM_VISUAL:
    2993          12 :                 component->type = gf_strdup("video");
    2994          12 :                 break;
    2995           7 :         case GF_STREAM_AUDIO:
    2996           7 :                 component->type = gf_strdup("audio");
    2997           7 :                 break;
    2998           0 :         case GF_STREAM_SCENE:
    2999             :         case GF_STREAM_OD:
    3000             :         default:
    3001           0 :                 component->type = gf_strdup("application");
    3002           0 :                 break;
    3003             :         }
    3004             :         /*if lang not specified at adaptationSet level, put it here*/
    3005          19 :         if (!base_ds->set->lang && ds->lang && strcmp(ds->lang, "und")) {
    3006           0 :                 component->lang = gf_strdup(ds->lang);
    3007             :         }
    3008          19 :         gf_list_add(base_ds->set->content_component, component);
    3009             : }
    3010             : 
    3011             : typedef struct
    3012             : {
    3013             :         char *tpl;
    3014             :         u32 nb_reused;
    3015             : } DashTemplateRecord;
    3016             : 
    3017         346 : static u32 dasher_check_template_reuse(GF_DasherCtx *ctx, const char *tpl)
    3018             : {
    3019             :         DashTemplateRecord *tr;
    3020         346 :         u32 i, count = gf_list_count(ctx->tpl_records);
    3021         362 :         for (i=0; i<count; i++) {
    3022         362 :                 tr = gf_list_get(ctx->tpl_records, i);
    3023         362 :                 if (!strcmp(tr->tpl, tpl)) {
    3024           0 :                         tr->nb_reused++;
    3025             :                         return tr->nb_reused;
    3026             :                 }
    3027             :         }
    3028         346 :         GF_SAFEALLOC(tr, DashTemplateRecord)
    3029         346 :         tr->tpl = gf_strdup(tpl);
    3030         346 :         gf_list_add(ctx->tpl_records, tr);
    3031             :         return 0;
    3032             : }
    3033             : 
    3034         317 : static void dasher_setup_sources(GF_Filter *filter, GF_DasherCtx *ctx, GF_MPD_AdaptationSet *set)
    3035             : {
    3036             :         char szDASHTemplate[GF_MAX_PATH];
    3037             :         char szTemplate[GF_MAX_PATH];
    3038             :         char szSegmentName[GF_MAX_PATH];
    3039             :         char szInitSegmentTemplate[GF_MAX_PATH];
    3040             :         char szInitSegmentFilename[GF_MAX_PATH];
    3041             :         char szIndexSegmentName[GF_MAX_PATH];
    3042             :         char szSetFileSuffix[200], szDASHSuffix[220];
    3043             :         const char *template = NULL;
    3044             :         u32 as_id = 0;
    3045             :         Bool single_template = GF_TRUE;
    3046             :         u32 i, j, count, nb_base, nb_streams;
    3047             :         GF_List *multi_pids = NULL;
    3048             :         u32 set_timescale = 0;
    3049             :         Bool init_template_done=GF_FALSE;
    3050         317 :         Bool use_inband = (ctx->bs_switch==DASHER_BS_SWITCH_INBAND) ? GF_TRUE : GF_FALSE;
    3051             :         Bool template_use_source = GF_FALSE;
    3052             :         Bool split_rep_names = GF_FALSE;
    3053             :         Bool split_set_names = GF_FALSE;
    3054             :         u32 force_ds_id;
    3055             :         GF_DashStream *ds;
    3056         317 :         GF_MPD_Representation *rep = gf_list_get(set->representations, 0);
    3057         317 :         if (!rep) {
    3058             :                 assert(0);
    3059           0 :                 return;
    3060             :         }
    3061         317 :         ds = rep->playback.udta;
    3062             : 
    3063         317 :         count = gf_list_count(set->representations);
    3064             : 
    3065         317 :         if (!ctx->sigfrag) {
    3066             :                 assert(ctx->template);
    3067         311 :                 template = ((GF_DashStream *)set->udta)->template;
    3068             :         }
    3069             : 
    3070         673 :         for (i=0; i<count; i++) {
    3071         356 :                 rep = gf_list_get(set->representations, i);
    3072         356 :                 ds = rep->playback.udta;
    3073         356 :                 if (!ds->template && !template) {}
    3074          27 :                 else if (ds->template && template && !strcmp(ds->template, template) ) {
    3075             :                 } else {
    3076             :                         single_template = GF_FALSE;
    3077             :                 }
    3078         356 :                 if (ds->template) template_use_source = dasher_template_use_source_url(ds->template);
    3079             : 
    3080         356 :                 if (template_use_source) {
    3081             :                         single_template = GF_FALSE;
    3082             :                 }
    3083             : 
    3084         356 :                 if (ds->as_id && !as_id)
    3085             :                         as_id = ds->as_id;
    3086             : 
    3087         356 :                 if (ds->fps.den && ( (ds->fps.num*set->max_framerate.den) >= (s32) (set->max_framerate.num*ds->fps.den) )) {
    3088         241 :                         set->max_framerate.num = ds->fps.num;
    3089         241 :                         set->max_framerate.den = ds->fps.den;
    3090         241 :                         gf_media_get_reduced_frame_rate(&set->max_framerate.num, &set->max_framerate.den);
    3091             :                 }
    3092         356 :                 if (ds->width && ds->height) {
    3093         265 :                         if (!set->par) {
    3094           0 :                                 GF_SAFEALLOC(set->par, GF_MPD_Fractional);
    3095             :                         }
    3096         265 :                         if (set->par) {
    3097         265 :                                 set->par->num = ds->width;
    3098         265 :                                 set->par->den = ds->height;
    3099         265 :                                 gf_media_reduce_aspect_ratio(&set->par->num, &set->par->den);
    3100             :                         }
    3101             :                 }
    3102             :         }
    3103         317 :         if (!template) template = ctx->template;
    3104             : 
    3105         317 :         if (as_id) {
    3106           0 :                 set->id = ds->as_id;
    3107             :         }
    3108         317 :         if (ctx->sseg) {
    3109          21 :                 set->segment_alignment = GF_TRUE;
    3110          21 :                 set->starts_with_sap = 1;
    3111             :         }
    3112             : 
    3113         317 :         if (count==1)
    3114             :                 single_template = GF_TRUE;
    3115          38 :         else if (single_template) {
    3116             :                 //for regular reps, if we depend on filename we cannot mutualize the template
    3117          32 :                 if (dasher_template_use_source_url(template) ) {
    3118             :                         single_template = GF_FALSE;
    3119             :                 }
    3120             :                 //and for scalable reps, if we don't have bandwidth /repID we cannot mutualize the template
    3121           3 :                 else if (gf_list_count(ds->complementary_streams) ) {
    3122           0 :                         if (strstr(template, "$Bandwidth$") != NULL) single_template = GF_FALSE;
    3123           0 :                         else if (strstr(template, "$RepresentationId$") != NULL) single_template = GF_FALSE;
    3124             :                 }
    3125             :         }
    3126             : 
    3127         317 :         if (set->lang) gf_free(set->lang);
    3128         317 :         set->lang = gf_strdup(ds->lang ? ds->lang : "und");
    3129             : 
    3130             :         //check all streams in active period not in this set
    3131         317 :         force_ds_id = ds->id;
    3132         317 :         nb_streams = gf_list_count(ctx->current_period->streams);
    3133         763 :         for (i=0; i<nb_streams; i++) {
    3134             :                 char *frag_uri;
    3135             :                 u32 len1, len2;
    3136         522 :                 GF_DashStream *ads = gf_list_get(ctx->current_period->streams, i);
    3137             : 
    3138         522 :                 if (force_ds_id && (ads != ds) && (ads->id == ds->id)) {
    3139             :                         force_ds_id = 0;
    3140             :                 }
    3141             : 
    3142         522 :                 if (ads->set == set) continue;
    3143         217 :                 frag_uri = strrchr(ds->src_url, '#');
    3144         217 :                 if (frag_uri) len1 = (u32) (frag_uri-1 - ds->src_url);
    3145         217 :                 else len1 = (u32) strlen(ds->src_url);
    3146         217 :                 frag_uri = strrchr(ads->src_url, '#');
    3147         217 :                 if (frag_uri) len2 = (u32) (frag_uri-1 - ads->src_url);
    3148         217 :                 else len2 = (u32) strlen(ads->src_url);
    3149             : 
    3150         217 :                 if ((len1==len2) && !strncmp(ds->src_url, ads->src_url, len1)) {
    3151             :                         split_set_names = GF_TRUE;
    3152             :                         break;
    3153             :                 }
    3154             :         }
    3155             : 
    3156         317 :         if (ctx->timescale>0) {
    3157           0 :                 set_timescale = ctx->timescale;
    3158             :         } else {
    3159             :                 u32 first_timescale;
    3160         317 :                 rep = gf_list_get(set->representations, 0);
    3161         317 :                 ds = rep->playback.udta;
    3162         317 :                 first_timescale = ds->timescale;
    3163         351 :                 for (i=1; i<count; i++) {
    3164          39 :                         rep = gf_list_get(set->representations, i);
    3165          39 :                         ds = rep->playback.udta;
    3166          39 :                         if (ds->timescale != first_timescale) {
    3167             :                                 //we cannot use a single template if enforcing timescales which are not identical
    3168             :                                 single_template = GF_FALSE;
    3169             :                                 break;
    3170             :                         }
    3171             :                 }
    3172             :         }
    3173             : 
    3174         655 :         for (i=0; i<count; i++) {
    3175         350 :                 if (ctx->sigfrag)
    3176             :                         break;
    3177             : 
    3178         344 :                 rep = gf_list_get(set->representations, i);
    3179         344 :                 ds = rep->playback.udta;
    3180             : 
    3181         344 :                 if (!dasher_template_use_source_url(template))
    3182          45 :                         continue;
    3183             : 
    3184         299 :                 if (ds->muxed_base)
    3185           8 :                         continue;
    3186             : 
    3187         315 :                 for (j=i+1; j<count; j++) {
    3188             :                         const GF_PropertyValue *p1, *p2;
    3189             :                         GF_DashStream *a_ds;
    3190          30 :                         rep = gf_list_get(set->representations, j);
    3191          30 :                         a_ds = rep->playback.udta;
    3192             : 
    3193          30 :                         if (a_ds->muxed_base == ds)
    3194           8 :                                 continue;
    3195             : 
    3196          22 :                         p1 = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_FILEPATH);
    3197          22 :                         p2 = gf_filter_pid_get_property(a_ds->ipid, GF_PROP_PID_FILEPATH);
    3198          22 :                         if (p1 && p2 && gf_props_equal(p1, p2)) split_rep_names = GF_TRUE;
    3199          16 :                         else if (!p1 && !p2) split_rep_names = GF_TRUE;
    3200          22 :                         p1 = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_URL);
    3201          22 :                         p2 = gf_filter_pid_get_property(a_ds->ipid, GF_PROP_PID_URL);
    3202          22 :                         if (p1 && p2 && gf_props_equal(p1, p2)) split_rep_names = GF_TRUE;
    3203          16 :                         else if (!p1 && !p2) split_rep_names = GF_TRUE;
    3204             : 
    3205          16 :                         if (split_rep_names) break;
    3206             :                 }
    3207         291 :                 if (split_rep_names) break;
    3208             :         }
    3209             : 
    3210         317 :         if (split_set_names) {
    3211          76 :                 if (!force_ds_id) {
    3212          17 :                         if (split_rep_names || !ds->split_set_names)
    3213          11 :                                 force_ds_id = ds->id;
    3214             :                         else
    3215           6 :                                 force_ds_id = gf_list_find(ctx->pids, ds) + 1;
    3216             :                 }
    3217             :                 sprintf(szSetFileSuffix, "_track%d_", force_ds_id);
    3218             :         }
    3219             : 
    3220             :         //assign PID IDs - we assume only one component of a given media type per adaptation set
    3221             :         //and assign the same PID ID for each component of the same type
    3222             :         //we could refine this using roles, but most HAS solutions don't use roles at the mulitplexed level
    3223         356 :         for (i=0; i<count; i++) {
    3224             :                 Bool is_bs_switching;
    3225         356 :                 rep = gf_list_get(set->representations, i);
    3226         356 :                 ds = rep->playback.udta;
    3227         356 :                 if (ds->pid_id) continue;
    3228             :                 //in bitstream switching mode, ensure each track in the set has the same ID
    3229             :                 //except when tile merge is used
    3230         351 :                 is_bs_switching = ds->tile_dep_id_merged ? GF_FALSE : set->bitstream_switching;
    3231         342 :                 if (is_bs_switching) {
    3232          28 :                         ctx->next_pid_id_in_period++;
    3233          28 :                         ds->pid_id = ctx->next_pid_id_in_period;
    3234             : 
    3235          45 :                         for (j=i+1; j<count; j++) {
    3236             :                                 GF_DashStream *a_ds;
    3237          17 :                                 rep = gf_list_get(set->representations, j);
    3238          17 :                                 a_ds = rep->playback.udta;
    3239          17 :                                 if (a_ds->pid_id) continue;
    3240          17 :                                 if (a_ds->dep_id) continue;
    3241           8 :                                 if (a_ds->stream_type == ds->stream_type) a_ds->pid_id = ds->pid_id;
    3242             :                         }
    3243             :                 }
    3244             :                 //otherwise copy over the source PID
    3245             :                 else {
    3246         323 :                         ds->pid_id = ds->id;
    3247             :                 }
    3248             :         }
    3249             : 
    3250         356 :         for (i=0; i<count; i++) {
    3251         356 :                 rep = gf_list_get(set->representations, i);
    3252         356 :                 ds = rep->playback.udta;
    3253         356 :                 if (!ds->dep_id) continue;
    3254             : 
    3255          49 :                 for (j=i+1; j<count; j++) {
    3256             :                         GF_DashStream *a_ds;
    3257          10 :                         rep = gf_list_get(set->representations, j);
    3258          10 :                         a_ds = rep->playback.udta;
    3259          10 :                         if (ds->dep_id == a_ds->id) {
    3260           0 :                                 ds->dep_pid_id = a_ds->pid_id;
    3261           0 :                                 break;
    3262             :                         }
    3263             :                 }
    3264             :         }
    3265             : 
    3266             :         //this is crude because we don't copy the properties, we just pass a list of pids to the destination muxer !!
    3267             :         //we should cleanup one of these days
    3268         317 :         if (set->bitstream_switching && (ctx->bs_switch==DASHER_BS_SWITCH_MULTI)) {
    3269           0 :                 multi_pids = gf_list_new();
    3270           0 :                 for (i=0; i<count; i++) {
    3271           0 :                         rep = gf_list_get(set->representations, i);
    3272           0 :                         ds = rep->playback.udta;
    3273           0 :                         if (ds->owns_set) ds->multi_pids = multi_pids;
    3274           0 :                         gf_list_add(multi_pids, ds->ipid);
    3275             :                 }
    3276             :         }
    3277             : 
    3278         317 :         if (ctx->cp!=GF_DASH_CPMODE_REPRESENTATION) {
    3279         317 :                 gf_mpd_del_list(set->content_protection, gf_mpd_descriptor_free, 0);
    3280         317 :                 set->content_protection = dasher_get_content_protection_desc(ctx, NULL, set);
    3281             :         }
    3282             : 
    3283         356 :         for (i=0; i<count; i++) {
    3284             :                 GF_Err e;
    3285             :                 char szRawExt[20];
    3286             :                 Bool use_dash_suffix = GF_FALSE;
    3287             :                 const char *seg_ext, *init_ext, *idx_ext, *force_init_seg_tpl;
    3288             : #if 0
    3289             :                 GF_MPD_URL *force_init_seg_sl;
    3290             : #endif
    3291             :                 Bool skip_init = GF_FALSE;
    3292             :                 GF_DashStream *tile_base_ds = NULL;
    3293             :                 Bool is_bs_switch;
    3294             :                 u32 reused_template_idx;
    3295             :                 u32 init_template_mode = GF_DASH_TEMPLATE_INITIALIZATION_TEMPLATE;
    3296         356 :                 rep = gf_list_get(set->representations, i);
    3297         356 :                 ds = rep->playback.udta;
    3298             : 
    3299             :                 //remove representations for streams muxed with others, but still open the output
    3300         356 :                 if (ds->muxed_base) {
    3301          10 :                         GF_DashStream *ds_set = set->udta;
    3302          10 :                         gf_list_rem(set->representations, i);
    3303          10 :                         i--;
    3304          10 :                         count--;
    3305             :                         assert(ds_set->nb_rep);
    3306          10 :                         ds_set->nb_rep--;
    3307             :                         assert(ds->muxed_base->dst_filter);
    3308          10 :                         gf_list_transfer(ds->muxed_base->rep->audio_channels, rep->audio_channels);
    3309          10 :                         gf_list_transfer(ds->muxed_base->rep->base_URLs, rep->base_URLs);
    3310          10 :                         gf_list_transfer(ds->muxed_base->rep->content_protection , rep->content_protection);
    3311          10 :                         gf_list_transfer(ds->muxed_base->rep->essential_properties , rep->essential_properties);
    3312          10 :                         gf_list_transfer(ds->muxed_base->rep->frame_packing , rep->frame_packing);
    3313          10 :                         if (rep->x_children) {
    3314           0 :                                 if (!ds->muxed_base->rep->x_children) ds->muxed_base->rep->x_children = gf_list_new();
    3315           0 :                                 gf_list_transfer(ds->muxed_base->rep->x_children, rep->x_children);
    3316             :                         }
    3317          10 :                         gf_list_transfer(ds->muxed_base->rep->supplemental_properties , rep->supplemental_properties);
    3318             : 
    3319          10 :                         gf_mpd_representation_free(ds->rep);
    3320          10 :                         ds->rep = NULL;
    3321             : 
    3322          10 :                         if (!gf_list_count(ds->set->content_component)) {
    3323           9 :                                 dasher_set_content_components(ds->muxed_base);
    3324             :                         }
    3325          10 :                         dasher_set_content_components(ds);
    3326             :                         assert(!multi_pids);
    3327             :                         //open PID
    3328          10 :                         dasher_open_pid(filter, ctx, ds, NULL, GF_FALSE);
    3329          20 :                         continue;
    3330             :                 }
    3331         346 :                 if (ds->template) strcpy(szTemplate, ds->template);
    3332         319 :                 else strcpy(szTemplate, ctx->template ? ctx->template : "");
    3333             : 
    3334         346 :                 if (use_inband)
    3335           0 :                         ds->inband_params = GF_TRUE;
    3336             : 
    3337             :                 //if bitstream switching and templating, only set for the first one
    3338         346 :                 if (i && set->bitstream_switching && ctx->stl && single_template) continue;
    3339             : 
    3340         346 :                 if (!set_timescale) set_timescale = ds->timescale;
    3341             : 
    3342         346 :                 if (ctx->timescale<0) ds->mpd_timescale = ds->timescale;
    3343         346 :                 else ds->mpd_timescale = set_timescale;
    3344             : 
    3345         346 :                 if (ds->nb_repeat && !ctx->loop) {
    3346           6 :                         if (split_set_names) {
    3347           6 :                                 sprintf(szDASHSuffix, "%sp%d_", szSetFileSuffix, ds->nb_repeat+1);
    3348             :                         } else {
    3349             :                                 sprintf(szDASHSuffix, "p%d_", ds->nb_repeat);
    3350             :                         }
    3351             :                         use_dash_suffix = GF_TRUE;
    3352         340 :                 } else if (split_set_names) {
    3353             :                         strcpy(szDASHSuffix, szSetFileSuffix);
    3354             :                         use_dash_suffix = GF_TRUE;
    3355             :                 }
    3356             : 
    3357             :                 //resolve segment template
    3358         346 :                 e = gf_filter_pid_resolve_file_template(ds->ipid, szTemplate, szDASHTemplate, 0, use_dash_suffix ? szDASHSuffix : NULL);
    3359         346 :                 if (e) {
    3360           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Cannot resolve template name %s, cannot derive output segment names, disabling rep %s\n", szTemplate, ds->src_url));
    3361           0 :                         gf_filter_pid_set_discard(ds->ipid, GF_TRUE);
    3362           0 :                         ds->done = 1;
    3363           0 :                         continue;
    3364             :                 }
    3365             : 
    3366             : 
    3367         346 :                 if (single_template && ds->split_set_names && !use_dash_suffix) {
    3368             :                         char szStrName[20];
    3369           0 :                         sprintf(szStrName, "_set%d", 1 + gf_list_find(ctx->current_period->period->adaptation_sets, set)  );
    3370             :                         strcat(szDASHTemplate, szStrName);
    3371             :                 }
    3372         346 :                 else if (split_rep_names) {
    3373             :                         char szStrName[20];
    3374          12 :                         sprintf(szStrName, "_rep%d", 1 + gf_list_find(set->representations, ds->rep)  );
    3375             :                         strcat(szDASHTemplate, szStrName);
    3376             :                 }
    3377             : 
    3378             :                 idx_ext = NULL;
    3379         346 :                 if (ctx->m2ts) {
    3380             :                         seg_ext = init_ext = "ts";
    3381           8 :                         if (!ctx->do_m3u8 && (ctx->subs_sidx>=0) )
    3382             :                                 idx_ext = "idx";
    3383             :                 } else {
    3384             :                         const char *def_ext = NULL;
    3385             : 
    3386             :                         seg_ext = init_ext = NULL;
    3387             : 
    3388         338 :                         if (ctx->muxtype==DASHER_MUX_TS) def_ext = "ts";
    3389         338 :                         else if (ctx->muxtype==DASHER_MUX_MKV) def_ext = "mkv";
    3390         336 :                         else if (ctx->muxtype==DASHER_MUX_WEBM) def_ext = "webm";
    3391         336 :                         else if (ctx->muxtype==DASHER_MUX_OGG) def_ext = "ogg";
    3392         336 :                         else if (ctx->muxtype==DASHER_MUX_RAW) {
    3393           8 :                                 char *ext = (char *) gf_codecid_file_ext(ds->codec_id);
    3394           8 :                                 if (ds->codec_id==GF_CODECID_RAW) {
    3395             :                                         const GF_PropertyValue *p;
    3396           0 :                                         if (ds->stream_type==GF_STREAM_VISUAL) {
    3397           0 :                                                 p = gf_filter_pid_get_property(ds->ipid, GF_PROP_PIXFMT);
    3398           0 :                                                 if (p) ext = (char *) gf_pixel_fmt_sname(p->value.uint);
    3399             :                                         }
    3400           0 :                                         else if (ds->stream_type==GF_STREAM_AUDIO) {
    3401           0 :                                                 p = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_AUDIO_FORMAT);
    3402           0 :                                                 if (p) ext = (char *) gf_audio_fmt_sname(p->value.uint);
    3403             :                                         }
    3404             :                                 }
    3405           8 :                                 strncpy(szRawExt, ext ? ext : "raw", 19);
    3406           8 :                                 szRawExt[19] = 0;
    3407           8 :                                 ext = strchr(szRawExt, '|');
    3408           8 :                                 if (ext) ext[0] = 0;
    3409             :                                 def_ext = szRawExt;
    3410             :                         }
    3411             : 
    3412         338 :                         if (ctx->segext && !stricmp(ctx->segext, "null")) {
    3413             :                                 seg_ext = NULL;
    3414             :                         } else {
    3415             :                                 seg_ext = ctx->segext;
    3416         338 :                                 if (!seg_ext) seg_ext = def_ext ? def_ext : "m4s";
    3417             :                         }
    3418         338 :                         if (ctx->initext && !stricmp(ctx->initext, "null")) {
    3419             :                                 init_ext = NULL;
    3420             :                         } else {
    3421             :                                 init_ext = ctx->initext;
    3422         338 :                                 if (!init_ext) init_ext = def_ext ? def_ext : "mp4";
    3423             :                         }
    3424             :                 }
    3425             : 
    3426         346 :                 is_bs_switch = set->bitstream_switching;
    3427             :                 //only used to force _init in default templates
    3428         346 :                 if (ds->tile_base) is_bs_switch = GF_FALSE;
    3429             : 
    3430             : 
    3431             :                 //check we are not reusing an existing template from previous periods, if so append a suffix
    3432             :                 //we check the final init name
    3433         346 :                 if (!ds->skip_tpl_reuse) {
    3434         346 :                         gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_INITIALIZATION, is_bs_switch, szInitSegmentFilename, ds->rep_id, NULL, szDASHTemplate, init_ext, 0, ds->bitrate, 0, ctx->stl);
    3435         346 :                         reused_template_idx = dasher_check_template_reuse(ctx, szInitSegmentFilename);
    3436         346 :                         if (reused_template_idx) {
    3437             :                                 char szExName[20];
    3438             :                                 sprintf(szExName, "_r%d_", reused_template_idx);
    3439             :                                 strcat(szDASHTemplate, szExName);
    3440             :                         }
    3441             :                 }
    3442             :                 
    3443             :                 //get final segment template with path resolution - output file name is NULL, we already have solved this
    3444         346 :                 gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_TEMPLATE_WITH_PATH, is_bs_switch, szSegmentName, ds->rep_id, NULL, szDASHTemplate, seg_ext, 0, 0, 0, ctx->stl);
    3445         346 :                 ds->seg_template = gf_strdup(szSegmentName);
    3446             : 
    3447             :                 //get final segment template - output file name is NULL, we already have solved this
    3448         346 :                 gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_TEMPLATE, is_bs_switch, szSegmentName, ds->rep_id, NULL, szDASHTemplate, seg_ext, 0, 0, 0, ctx->stl);
    3449             : 
    3450             :                 //get index templates
    3451         346 :                 if (idx_ext) {
    3452           1 :                         gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_REPINDEX_TEMPLATE_WITH_PATH, is_bs_switch, szIndexSegmentName, ds->rep_id, NULL, szDASHTemplate, idx_ext, 0, 0, 0, ctx->stl);
    3453           1 :                         ds->idx_template = gf_strdup(szIndexSegmentName);
    3454             : 
    3455           1 :                         gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_REPINDEX_TEMPLATE, is_bs_switch, szIndexSegmentName, ds->rep_id, NULL, szDASHTemplate, idx_ext, 0, 0, 0, ctx->stl);
    3456             :                 }
    3457             : 
    3458             :                 //get final init name - output file name is NULL, we already have solved this
    3459         346 :                 gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_INITIALIZATION, is_bs_switch, szInitSegmentFilename, ds->rep_id, NULL, szDASHTemplate, init_ext, 0, ds->bitrate, 0, ctx->stl);
    3460             : 
    3461             :                 //get final init template name - output file name is NULL, we already have solved this
    3462         346 :                 gf_media_mpd_format_segment_name(init_template_mode, is_bs_switch, szInitSegmentTemplate, ds->rep_id, NULL, szDASHTemplate, init_ext, 0, 0, 0, ctx->stl);
    3463             : 
    3464         346 :                 if (ctx->sigfrag) {
    3465           6 :                         if (ctx->template || ds->template) {
    3466             :                         } else {
    3467           5 :                                 strcpy(szInitSegmentFilename, gf_file_basename(ds->src_url));
    3468           5 :                                 strcpy(szSegmentName, gf_file_basename(ds->src_url));
    3469             :                         }
    3470             :                 }
    3471             : 
    3472         346 :                 if (ctx->store_seg_states) {
    3473             :                         assert(!ds->pending_segment_states);
    3474          74 :                         ds->pending_segment_states = gf_list_new();
    3475             :                 }
    3476             :                 /* baseURLs */
    3477         346 :                 nb_base = ds->p_base_url ? ds->p_base_url->value.string_list.nb_items : 0;
    3478         347 :                 for (j=0; j<nb_base; j++) {
    3479             :                         GF_MPD_BaseURL *base_url;
    3480           1 :                         char *url = ds->p_base_url->value.string_list.vals[j];
    3481           1 :                         GF_SAFEALLOC(base_url, GF_MPD_BaseURL);
    3482           1 :                         if (base_url) {
    3483           1 :                                 base_url->URL = gf_strdup(url);
    3484           1 :                                 gf_list_add(rep->base_URLs, base_url);
    3485             :                         }
    3486             :                 }
    3487             : 
    3488             :                 force_init_seg_tpl = NULL;
    3489             : #if 0
    3490             :                 force_init_seg_sl = NULL;
    3491             : #endif
    3492         346 :                 if (ds->codec_id==GF_CODECID_HEVC_TILES) {
    3493          36 :                         tile_base_ds = get_base_ds(ctx, ds);
    3494             :                         skip_init = GF_TRUE;
    3495          36 :                         if (tile_base_ds->rep->segment_template) force_init_seg_tpl = tile_base_ds->rep->segment_template->initialization;
    3496           0 :                         if (!force_init_seg_tpl && tile_base_ds->set->segment_template) force_init_seg_tpl = tile_base_ds->set->segment_template->initialization;
    3497             : 
    3498             : #if 0
    3499             :                         if (tile_base_ds->rep->segment_list) force_init_seg_sl = tile_base_ds->rep->segment_list->initialization_segment;
    3500             :                         if (!force_init_seg_sl && tile_base_ds->set->segment_list) force_init_seg_sl = tile_base_ds->set->segment_list->initialization_segment;
    3501             : #endif
    3502             :                 }
    3503         346 :                 if (ctx->m2ts) skip_init = GF_TRUE;
    3504         338 :                 else if (ctx->muxtype==DASHER_MUX_RAW) skip_init = GF_TRUE;
    3505         330 :                 else if (ctx->muxtype==DASHER_MUX_TS) skip_init = GF_TRUE;
    3506             : 
    3507             : 
    3508             :                 //forward mode, change segment names
    3509         346 :                 if (ctx->forward_mode) {
    3510          27 :                         u32 k, nb_pids = gf_list_count(ctx->pids);
    3511             :                         char *src = NULL;
    3512          27 :                         const GF_PropertyValue *p = gf_filter_pid_get_property(ds->ipid, GF_PROP_PCK_FILENAME);
    3513             : 
    3514          27 :                         if (!p || !p->value.string) {
    3515           0 :                                 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Couldn't fetch source URL in forward mode, cannot forward\n"));
    3516           0 :                                 ctx->in_error = GF_TRUE;
    3517           0 :                                 return;
    3518             :                         }
    3519             :                         src = p->value.string;
    3520             : 
    3521          81 :                         for (k=0; k<nb_pids; k++) {
    3522          81 :                                 GF_DashStream *a_ds = gf_list_get(ctx->pids, k);
    3523          81 :                                 if (ds == a_ds) continue;
    3524          54 :                                 if (!a_ds->dst_filter) continue;
    3525             : 
    3526          27 :                                 p = gf_filter_pid_get_property(a_ds->ipid, GF_PROP_PCK_FILENAME);
    3527          27 :                                 if (!p || !p->value.string) {
    3528           0 :                                         GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Couldn't fetch source URL in forward mode, cannot forward\n"));
    3529           0 :                                         ctx->in_error = GF_TRUE;
    3530           0 :                                         return;
    3531             :                                 }
    3532             :                                 //same init segment used (bs switching)
    3533          27 :                                 if (!strcmp(p->value.string, src))
    3534             :                                         skip_init = GF_TRUE;
    3535             :                         }
    3536             :                         strcpy(szInitSegmentFilename, src);
    3537             :                         strcpy(szInitSegmentTemplate, src);
    3538             : 
    3539          27 :                         if (ctx->tpl) {
    3540          27 :                                 p = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_TEMPLATE);
    3541          27 :                                 if (!p || !p->value.string) {
    3542           0 :                                         GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Couldn't fetch source template in forward mode, cannot forward\n"));
    3543           0 :                                         ctx->in_error = GF_TRUE;
    3544           0 :                                         return;
    3545             :                                 }
    3546             :                                 strcpy(szSegmentName, p->value.string);
    3547             :                         }
    3548             :                 }
    3549             : 
    3550         346 :                 ds->init_seg = gf_strdup(szInitSegmentFilename);
    3551             : 
    3552             :                 //we use segment template
    3553         346 :                 if (ctx->tpl) {
    3554             :                         GF_MPD_SegmentTemplate *seg_template;
    3555         289 :                         u32 start_number = ds->startNumber ? ds->startNumber : 1;
    3556         289 :                         u64 seg_duration = (u64)(ds->dash_dur.num) * ds->mpd_timescale / ds->dash_dur.den;
    3557             : 
    3558             :                         //first rep in set and bs switching or single template, create segment template at set level
    3559         289 :                         if (!i && (set->bitstream_switching || single_template) ) {
    3560             :                                 init_template_done = GF_TRUE;
    3561             :                                 seg_template = NULL;
    3562         247 :                                 if (!skip_init || force_init_seg_tpl || single_template) {
    3563         245 :                                         GF_SAFEALLOC(seg_template, GF_MPD_SegmentTemplate);
    3564         245 :                                         if (seg_template) {
    3565         245 :                                                 if (skip_init) {
    3566          35 :                                                         seg_template->initialization = force_init_seg_tpl ? gf_strdup(force_init_seg_tpl) : NULL;
    3567          35 :                                                         seg_template->hls_init_name = force_init_seg_tpl ? tile_base_ds->init_seg : NULL;
    3568             :                                                 } else {
    3569         210 :                                                         seg_template->initialization = gf_strdup(szInitSegmentTemplate);
    3570         210 :                                                         seg_template->hls_init_name = ds->init_seg;
    3571             :                                                 }
    3572             :                                         }
    3573             :                                 }
    3574         247 :                                 set->segment_template = seg_template;
    3575             : 
    3576         247 :                                 dasher_open_destination(filter, ctx, rep, szInitSegmentFilename, skip_init);
    3577             : 
    3578         247 :                                 if (single_template) {
    3579         233 :                                         seg_template->media = gf_strdup(szSegmentName);
    3580         233 :                                         if (ds->idx_template)
    3581           0 :                                                 seg_template->index = gf_strdup(szIndexSegmentName);
    3582             : 
    3583         233 :                                         seg_template->timescale = ds->mpd_timescale;
    3584         233 :                                         seg_template->start_number = start_number;
    3585         233 :                                         seg_template->duration = seg_duration;
    3586             : 
    3587         233 :                                         if (ctx->asto>0) {
    3588           2 :                                                 seg_template->availability_time_offset = ctx->asto;
    3589             :                                         }
    3590          14 :                                 } else if (seg_template) {
    3591          12 :                                         seg_template->start_number = (u32)-1;
    3592             :                                 }
    3593             :                         }
    3594             :                         //non-first rep in set and single template, only open destination
    3595         289 :                         if (i && single_template) {
    3596           1 :                                 dasher_open_destination(filter, ctx, rep, szInitSegmentFilename, (set->bitstream_switching || skip_init) ? GF_TRUE : GF_FALSE);
    3597             :                         }
    3598             :                         //first rep in set and no bs switching or mutliple templates, create segment template at rep level
    3599         288 :                         else if (i || !single_template) {
    3600          55 :                                 GF_SAFEALLOC(seg_template, GF_MPD_SegmentTemplate);
    3601          55 :                                 if (seg_template) {
    3602          55 :                                         rep->segment_template = seg_template;
    3603          55 :                                         if (!init_template_done) {
    3604          29 :                                                 if (skip_init) {
    3605           0 :                                                         seg_template->initialization = force_init_seg_tpl ? gf_strdup(force_init_seg_tpl) : NULL;
    3606           0 :                                                         seg_template->hls_init_name = force_init_seg_tpl ? tile_base_ds->init_seg : NULL;
    3607             :                                                 } else {
    3608          29 :                                                         seg_template->initialization = gf_strdup(szInitSegmentTemplate);
    3609          29 :                                                         seg_template->hls_init_name = ds->init_seg;
    3610             :                                                 }
    3611          29 :                                                 dasher_open_destination(filter, ctx, rep, szInitSegmentFilename, skip_init);
    3612          26 :                                         } else if (i) {
    3613          12 :                                                 dasher_open_destination(filter, ctx, rep, szInitSegmentFilename, (set->bitstream_switching || skip_init) ? GF_TRUE : GF_FALSE);
    3614             :                                         }
    3615          55 :                                         seg_template->media = gf_strdup(szSegmentName);
    3616          55 :                                         if (ds->idx_template)
    3617           1 :                                                 seg_template->index = gf_strdup(szIndexSegmentName);
    3618          55 :                                         seg_template->duration = seg_duration;
    3619          55 :                                         seg_template->timescale = ds->mpd_timescale;
    3620          55 :                                         seg_template->start_number = start_number;
    3621          55 :                                         if (ctx->asto > 0) {
    3622           0 :                                                 seg_template->availability_time_offset = ctx->asto;
    3623             :                                         }
    3624             :                                 }
    3625             :                         }
    3626             :                 }
    3627             :                 /*we are using a single file or segment, use base url*/
    3628          57 :                 else if (ctx->sseg || ctx->sfile) {
    3629             :                         GF_MPD_BaseURL *baseURL;
    3630             : /*                      char *segext = (ctx->segext && !stricmp(ctx->segext, "null")) ? NULL : "mp4";
    3631             :                         if (ctx->m2ts) segext = "ts";
    3632             : 
    3633             :                         //use GF_DASH_TEMPLATE_INITIALIZATION_SKIPINIT to get rid of default "init" added for init templates
    3634             :                         gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_INITIALIZATION, set->bitstream_switching, szInitSegmentName, ds->rep_id, NULL, szDASHTemplate, segext, 0, 0, 0, ctx->stl);
    3635             : */
    3636             : 
    3637          55 :                         if (ds->init_seg) gf_free(ds->init_seg);
    3638          55 :                         ds->init_seg = gf_strdup(szInitSegmentFilename);
    3639             : 
    3640          55 :                         GF_SAFEALLOC(baseURL, GF_MPD_BaseURL);
    3641          55 :                         if (!baseURL) continue;
    3642             : 
    3643          55 :                         if (!rep->base_URLs) rep->base_URLs = gf_list_new();
    3644          55 :                         gf_list_add(rep->base_URLs, baseURL);
    3645             : 
    3646          55 :                         if (ctx->sseg) {
    3647             :                                 GF_MPD_SegmentBase *segment_base;
    3648          21 :                                 baseURL->URL = gf_strdup(szInitSegmentFilename);
    3649          21 :                                 GF_SAFEALLOC(segment_base, GF_MPD_SegmentBase);
    3650          21 :                                 if (!segment_base) continue;
    3651          21 :                                 rep->segment_base = segment_base;
    3652          21 :                                 dasher_open_destination(filter, ctx, rep, szInitSegmentFilename, GF_FALSE);
    3653             :                         } else {
    3654             :                                 GF_MPD_SegmentList *seg_list;
    3655          34 :                                 GF_SAFEALLOC(seg_list, GF_MPD_SegmentList);
    3656          34 :                                 if (!seg_list) continue;
    3657          34 :                                 GF_SAFEALLOC(seg_list->initialization_segment, GF_MPD_URL);
    3658          34 :                                 if (!seg_list->initialization_segment) continue;
    3659          34 :                                 seg_list->start_number = (u32) -1;
    3660          34 :                                 baseURL->URL = gf_strdup(szInitSegmentFilename);
    3661          34 :                                 seg_list->dasher_segment_name = gf_strdup(szSegmentName);
    3662          34 :                                 seg_list->timescale = ds->mpd_timescale;
    3663          34 :                                 seg_list->duration = (u64) (ds->dash_dur.num) * ds->mpd_timescale / ds->dash_dur.den;
    3664          34 :                                 seg_list->segment_URLs = gf_list_new();
    3665          34 :                                 rep->segment_list = seg_list;
    3666          34 :                                 ds->pending_segment_urls = gf_list_new();
    3667             : 
    3668          34 :                                 dasher_open_destination(filter, ctx, rep, szInitSegmentFilename, skip_init);
    3669             :                         }
    3670             :                 }
    3671             :                 //no template, no single file, we need a file list
    3672             :                 else {
    3673             :                         GF_MPD_SegmentList *seg_list;
    3674           2 :                         GF_SAFEALLOC(seg_list, GF_MPD_SegmentList);
    3675           2 :                         if (!seg_list) continue;
    3676             : 
    3677           2 :                         if (!skip_init) {
    3678           1 :                                 GF_SAFEALLOC(seg_list->initialization_segment, GF_MPD_URL);
    3679           1 :                                 if (!seg_list->initialization_segment) continue;
    3680             : 
    3681           1 :                                 seg_list->initialization_segment->sourceURL = gf_strdup(szInitSegmentFilename);
    3682             :                         }
    3683           2 :                         seg_list->dasher_segment_name = gf_strdup(szSegmentName);
    3684           2 :                         seg_list->timescale = ds->mpd_timescale;
    3685           2 :                         seg_list->segment_URLs = gf_list_new();
    3686           2 :                         seg_list->start_number = (u32) -1;
    3687           2 :                         seg_list->duration = (u64) (ds->dash_dur.num) * ds->mpd_timescale / ds->dash_dur.den;
    3688           2 :                         rep->segment_list = seg_list;
    3689           2 :                         ds->pending_segment_urls = gf_list_new();
    3690             : 
    3691           2 :                         dasher_open_destination(filter, ctx, rep, szInitSegmentFilename, skip_init);
    3692             :                 }
    3693             : 
    3694             :                 //open PID
    3695         346 :                 dasher_open_pid(filter, ctx, ds, multi_pids, skip_init);
    3696             :         }
    3697             : }
    3698             : 
    3699           1 : static void dahser_purge_segment_timeline(GF_DashStream *ds, GF_MPD_SegmentTimeline *stl, GF_DASH_SegmentContext *sctx)
    3700             : {
    3701           1 :         GF_MPD_SegmentTimelineEntry *stl_e = gf_list_get(stl->entries, 0);
    3702           1 :         if (!stl_e) return;
    3703             : 
    3704           1 :         if (stl_e->repeat_count) {
    3705           1 :                 stl_e->repeat_count--;
    3706           1 :                 stl_e->start_time += stl_e->duration;
    3707             :         } else {
    3708           0 :                 u64 start_time = stl_e->start_time + stl_e->duration;
    3709           0 :                 gf_list_rem(stl->entries, 0);
    3710           0 :                 gf_free(stl_e);
    3711           0 :                 stl_e = gf_list_get(stl->entries, 0);
    3712           0 :                 if (!stl_e) {
    3713           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] No timeline entry after currently removed segment, cannot update start time\n" ));
    3714             :                         return;
    3715             :                 }
    3716             : 
    3717           0 :                 if (!stl_e->start_time) stl_e->start_time = start_time;
    3718           0 :                 else if (stl_e->start_time != start_time) {
    3719           0 :                         GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Mismatch in segment timeline while purging, new start time "LLU" but entry indicates "LLU", keeping original one\n", start_time, stl_e->start_time ));
    3720             :                 }
    3721             :         }
    3722             : }
    3723             : 
    3724         356 : static void dasher_purge_segments(GF_DasherCtx *ctx, u64 *period_dur)
    3725             : {
    3726             :         Double min_valid_mpd_time;
    3727             :         u64 max_rem_dur = 0;
    3728             :         u32 i, count;
    3729             : 
    3730             :         //non-static mode, purge segments
    3731         356 :         if (ctx->dmode == GF_MPD_TYPE_STATIC) return;
    3732         136 :         if (ctx->tsb<0) return;
    3733             : 
    3734             : 
    3735         114 :         min_valid_mpd_time = (Double) *period_dur;
    3736         114 :         min_valid_mpd_time /= 1000;
    3737         114 :         min_valid_mpd_time -= ctx->tsb;
    3738             :         //negative asto, we produce segments earlier but we don't want to delete them before the asto
    3739         114 :         if (ctx->asto<0) {
    3740           0 :                 min_valid_mpd_time += ctx->asto;
    3741             :         }
    3742         114 :         if (min_valid_mpd_time<=0) return;
    3743             : 
    3744          11 :         count = gf_list_count(ctx->current_period->streams);
    3745          26 :         for (i=0; i<count; i++) {
    3746          15 :                 GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
    3747          15 :                 if (ds->muxed_base) continue;
    3748          11 :                 if (!ds->rep) continue;
    3749          10 :                 if (!ds->rep->state_seg_list) continue;
    3750             : 
    3751           3 :                 while (1) {
    3752             :                         Double time, dur;
    3753             :                         Bool seg_url_found = GF_FALSE;
    3754             :                         Bool has_seg_list = GF_FALSE;
    3755          13 :                         GF_DASH_SegmentContext *sctx = gf_list_get(ds->rep->state_seg_list, 0);
    3756          13 :                         if (!sctx) break;
    3757             :                         /*not yet flushed*/
    3758          13 :                         if (gf_list_find(ds->pending_segment_states, sctx)>=0) break;
    3759           8 :                         time = (Double) sctx->time;
    3760           8 :                         time /= ds->mpd_timescale;
    3761           8 :                         dur = (Double) sctx->dur;
    3762           8 :                         dur/= ds->timescale;
    3763           8 :                         if (time + dur >= min_valid_mpd_time) break;
    3764           3 :                         if (sctx->filepath) {
    3765             :                                 GF_FilterEvent evt;
    3766           3 :                                 GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[Dasher] removing segment %s\n", sctx->filename ? sctx->filename : sctx->filepath));
    3767             : 
    3768           3 :                                 GF_FEVT_INIT(evt, GF_FEVT_FILE_DELETE, ds->opid);
    3769           3 :                                 evt.file_del.url = sctx->filepath;
    3770           3 :                                 gf_filter_pid_send_event(ds->opid, &evt);
    3771           3 :                                 gf_free(sctx->filepath);
    3772             :                         }
    3773             : 
    3774           3 :                         if (ds->rep->segment_list) {
    3775           0 :                                 GF_MPD_SegmentURL *surl = gf_list_pop_front(ds->rep->segment_list->segment_URLs);
    3776             :                                 has_seg_list = GF_TRUE;
    3777             :                                 //can be NULL if we mutualize everything at AdaptatioSet level
    3778           0 :                                 if (surl) {
    3779           0 :                                         gf_mpd_segment_url_free(surl);
    3780             :                                         seg_url_found = GF_TRUE;
    3781             :                                 }
    3782             :                         }
    3783             :                         //not an else due to inheritance
    3784           3 :                         if (ds->owns_set && ds->set->segment_list) {
    3785           0 :                                 GF_MPD_SegmentURL *surl = gf_list_pop_front(ds->set->segment_list->segment_URLs);
    3786             :                                 has_seg_list = GF_TRUE;
    3787             :                                 //can be NULL if we don't mutualize at AdaptatioSet level
    3788           0 :                                 if (surl) {
    3789           0 :                                         gf_mpd_segment_url_free(surl);
    3790             :                                         seg_url_found = GF_TRUE;
    3791             :                                 }
    3792             :                         }
    3793             :                         //but we must have at least one segment URL entry
    3794           3 :                         if (has_seg_list && !seg_url_found) {
    3795           0 :                                 GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] purging segment %s for AS %d rep %s but segment list is empty!\n",
    3796             :                                                 sctx->filename ? sctx->filename : "", ds->set->id, ds->rep->id));
    3797             :                         }
    3798             : 
    3799           3 :                         if (ds->rep->segment_template) {
    3800           1 :                                 if (ds->rep->segment_template->segment_timeline) {
    3801           0 :                                         dahser_purge_segment_timeline(ds, ds->rep->segment_template->segment_timeline, sctx);
    3802             :                                 }
    3803             :                         }
    3804             :                         //not an else due to inheritance
    3805           3 :                         if (ds->owns_set && ds->set->segment_template) {
    3806           3 :                                 if (ds->set->segment_template->segment_timeline) {
    3807           1 :                                         dahser_purge_segment_timeline(ds, ds->set->segment_template->segment_timeline, sctx);
    3808             :                                 }
    3809             :                         }
    3810             : 
    3811           3 :                         ds->nb_segments_purged ++;
    3812           3 :                         ds->dur_purged += dur;
    3813             :                         assert(gf_list_find(ds->pending_segment_states, sctx)<0);
    3814           3 :                         if (sctx->filename) gf_free(sctx->filename);
    3815           3 :                         if (sctx->hls_key_uri) gf_free(sctx->hls_key_uri);
    3816           3 :                         gf_free(sctx);
    3817           3 :                         gf_list_rem(ds->rep->state_seg_list, 0);
    3818             :                 }
    3819          10 :                 if (max_rem_dur < ds->dur_purged*1000) max_rem_dur = (u64) (ds->dur_purged * 1000);
    3820             :                 //final flush to static of live session: update start number
    3821          10 :                 if (ctx->dmode!=GF_MPD_TYPE_DYNAMIC) {
    3822           0 :                         if (ds->owns_set && ds->set && ds->set->segment_template) {
    3823           0 :                                 ds->set->segment_template->start_number += ds->nb_segments_purged;
    3824           0 :                         } else if (ds->rep && ds->rep->segment_template) {
    3825           0 :                                 ds->rep->segment_template->start_number += ds->nb_segments_purged;
    3826             :                         }
    3827           0 :                         ds->nb_segments_purged = 0;
    3828             :                 }
    3829             :         }
    3830             :         //final flush to static of live session: update period duration
    3831          11 :         if (ctx->dmode!=GF_MPD_TYPE_DYNAMIC) {
    3832           1 :                 if (max_rem_dur > *period_dur) *period_dur = 0;
    3833           1 :                 else *period_dur = *period_dur - max_rem_dur;
    3834             :         }
    3835             : }
    3836             : 
    3837         356 : static void dasher_update_period_duration(GF_DasherCtx *ctx, Bool is_period_switch)
    3838             : {
    3839             :         u32 i, count;
    3840         356 :         u64 pdur = 0;
    3841             :         u64 min_dur = 0;
    3842             :         u64 p_start=0;
    3843             :         GF_MPD_Period *prev_p = NULL;
    3844         356 :         count = gf_list_count(ctx->current_period->streams);
    3845         530 :         for (i=0; i<count; i++) {
    3846         530 :                 GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
    3847         530 :                 if (ds->muxed_base) continue;
    3848             : 
    3849         516 :                 if (ds->xlink && (ds->stream_type==GF_STREAM_FILE) ) {
    3850           0 :                         pdur = (u32) (1000*(s64)ds->period_dur.num / ds->period_dur.den);
    3851             :                 } else {
    3852         516 :                         u64 ds_dur = ds->max_period_dur;
    3853             : 
    3854             :                         //we had to generate one extra segment to unlock looping, but we don't want to advertise it in the manifest duration
    3855             :                         //because other sets may not be ready for this time interval
    3856         516 :                         if (ds->subdur_forced_use_period_dur)
    3857             :                                 ds_dur = ds->subdur_forced_use_period_dur;
    3858             : 
    3859         516 :                         if (ds->clamped_dur.num && !ctx->loop) {
    3860         177 :                                 u64 clamp_dur = (u64) (ds->clamped_dur.num * 1000);
    3861         177 :                                 if (clamp_dur < ds->clamped_dur.den * ds_dur) ds_dur = clamp_dur / ds->clamped_dur.den;
    3862             :                         }
    3863             : 
    3864         516 :                         if (ds->dur_purged && (ctx->mpd->type != GF_MPD_TYPE_DYNAMIC)) {
    3865           0 :                                 u64 rem_dur = (u64) (ds->dur_purged * 1000);
    3866           0 :                                 if (ds_dur>rem_dur) ds_dur -= rem_dur;
    3867             :                                 else ds_dur = 0;
    3868             :                         }
    3869             : 
    3870         516 :                         if (!min_dur || (min_dur > ds_dur)) min_dur = ds->max_period_dur;
    3871         516 :                         if (pdur < ds_dur) pdur = ds_dur;
    3872             :                 }
    3873             :         }
    3874             : 
    3875         356 :         if (!count) {
    3876           1 :                 if (ctx->current_period->period && ctx->current_period->period->duration)
    3877           1 :                         pdur = ctx->current_period->period->duration;
    3878             :         }
    3879             : 
    3880         356 :         if (!ctx->check_dur) {
    3881         202 :                 s32 diff = (s32) ((s64) pdur - (s64) min_dur);
    3882         202 :                 if (ABS(diff)>2000) {
    3883           0 :                         GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Adaptation sets in period are of unequal duration min %g max %g seconds\n", ((Double)min_dur)/1000, ((Double)pdur)/1000));
    3884             :                 }
    3885             :         }
    3886             : 
    3887         356 :         dasher_purge_segments(ctx, &pdur);
    3888             : 
    3889         356 :         if (ctx->current_period->period) {
    3890         356 :                 ctx->current_period->period->duration = pdur;
    3891             : 
    3892             :                 //update MPD duration in any case
    3893         356 :                 if (ctx->current_period->period->start) {
    3894           3 :                         ctx->mpd->media_presentation_duration = ctx->current_period->period->start + pdur;
    3895             :                 } else {
    3896         353 :                         u32 k, pcount = gf_list_count(ctx->mpd->periods);
    3897         353 :                         ctx->mpd->media_presentation_duration = 0;
    3898           8 :                         for (k=0; k<pcount; k++) {
    3899         361 :                                 GF_MPD_Period *p = gf_list_get(ctx->mpd->periods, k);
    3900         361 :                                 if (p->start)
    3901           1 :                                         ctx->mpd->media_presentation_duration = p->start + p->duration;
    3902             :                                 else
    3903         360 :                                         ctx->mpd->media_presentation_duration += p->duration;
    3904         361 :                                 if (p==ctx->current_period->period)
    3905             :                                         break;
    3906             :                         }
    3907             :                 }
    3908             :         }
    3909             : 
    3910         356 :         if (ctx->refresh<0)
    3911           0 :                 ctx->mpd->media_presentation_duration = (u32) ( (-ctx->refresh) * 1000 );
    3912             : 
    3913             :         //static mode, done
    3914         356 :         if (ctx->dmode != GF_MPD_TYPE_DYNAMIC) {
    3915         228 :                 return;
    3916             :         }
    3917             :         assert(ctx->current_period->period);
    3918             :         //dynamic mode only, reset durations
    3919             : 
    3920         128 :         ctx->mpd->gpac_mpd_time = ctx->mpd->media_presentation_duration;
    3921             : 
    3922             :         //not done yet for this period, keep duration to 0
    3923         128 :         if (ctx->subdur_done) {
    3924          22 :                 if (ctx->mpd->media_presentation_duration > ctx->current_period->period->duration)
    3925           3 :                         ctx->mpd->media_presentation_duration -= ctx->current_period->period->duration;
    3926             :                 else
    3927          19 :                         ctx->mpd->media_presentation_duration = 0;
    3928          22 :                 ctx->current_period->period->duration = 0;
    3929             :         }
    3930             : 
    3931         128 :         ctx->mpd->gpac_next_ntp_ms = ctx->mpd->gpac_init_ntp_ms + ctx->mpd->gpac_mpd_time;
    3932         128 :         if (ctx->asto<0)
    3933           0 :                 ctx->mpd->gpac_next_ntp_ms -= (u64) (-ctx->asto * 1000);
    3934         128 :         if (ctx->_p_gentime) (*ctx->_p_gentime) = ctx->mpd->gpac_next_ntp_ms;
    3935         128 :         if (ctx->_p_mpdtime) (*ctx->_p_mpdtime) = ctx->mpd->gpac_mpd_time;
    3936             : 
    3937         128 :         GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[Dasher] updated period %s duration "LLU" MPD time "LLU"\n", ctx->current_period->period->ID, pdur, ctx->mpd->gpac_mpd_time ));
    3938             : 
    3939         128 :         count = gf_list_count(ctx->mpd->periods);
    3940         133 :         for (i=0; i<count; i++) {
    3941         133 :                 GF_MPD_Period *p = gf_list_get(ctx->mpd->periods, i);
    3942         133 :                 if (p->start) {
    3943             :                         p_start = p->start;
    3944             :                 } else {
    3945         131 :                         p->start = p_start;
    3946             :                 }
    3947         133 :                 if (prev_p && (prev_p->start + prev_p->duration == p_start)) {
    3948           3 :                         prev_p->duration = 0;
    3949             :                 }
    3950         133 :                 p_start += p->duration;
    3951             :                 prev_p = p;
    3952             :         }
    3953             : }
    3954             : 
    3955        1014 : static void dasher_transfer_file(FILE *f, GF_FilterPid *opid, const char *name, GF_DashStream *ds)
    3956             : {
    3957             :         GF_FilterPacket *pck;
    3958             :         u32 size, nb_read;
    3959             :         u8 *output;
    3960             : 
    3961        1014 :         size = (u32) gf_fsize(f);
    3962             : 
    3963        1014 :         pck = gf_filter_pck_new_alloc(opid, size, &output);
    3964        1014 :         if (!pck) return;
    3965             : 
    3966        1014 :         nb_read = (u32) gf_fread(output, size, f);
    3967        1014 :         if (nb_read != size) {
    3968           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Error reading temp MPD file, read %d bytes but file size is %d\n", nb_read, size ));
    3969             :         }
    3970        1014 :         gf_filter_pck_set_framing(pck, GF_TRUE, GF_TRUE);
    3971        1014 :         gf_filter_pck_set_seek_flag(pck, GF_TRUE);
    3972        1014 :         if (name) {
    3973         612 :                 gf_filter_pck_set_property(pck, GF_PROP_PCK_FILENAME, &PROP_STRING(name) );
    3974             :         }
    3975        1014 :         if (ds) {
    3976         596 :                 gf_filter_pck_set_property(pck, GF_PROP_PCK_HLS_REF, &PROP_LONGUINT( ds->hls_ref_id ) );
    3977             :         }
    3978        1014 :         gf_filter_pck_send(pck);
    3979             : }
    3980             : 
    3981             : 
    3982             : static u64 dasher_get_utc(GF_DasherCtx *ctx)
    3983             : {
    3984         825 :         return gf_net_get_utc() - ctx->utc_diff;
    3985             : }
    3986             : 
    3987           6 : static void dasher_get_set_and_rep(GF_MPD_Period *period, const char *repid, GF_MPD_AdaptationSet **out_set, GF_MPD_Representation **out_rep)
    3988             : {
    3989           6 :         u32 i, nb_sets = gf_list_count(period->adaptation_sets);
    3990           2 :         for (i=0; i<nb_sets; i++) {
    3991             :                 u32 j, nb_reps;
    3992           8 :                 GF_MPD_AdaptationSet *set = gf_list_get(period->adaptation_sets, i);
    3993           8 :                 nb_reps = gf_list_count(set->representations);
    3994           6 :                 for (j=0; j<nb_reps; j++) {
    3995          12 :                         GF_MPD_Representation *rep = gf_list_get(set->representations, j);
    3996          12 :                         if (rep->id && !strcmp(rep->id, repid)) {
    3997           6 :                                 *out_set = set;
    3998           6 :                                 *out_rep = rep;
    3999             :                                 return;
    4000             :                         }
    4001             :                 }
    4002             :         }
    4003             : }
    4004             : 
    4005           6 : static Bool dasher_merge_rep(GF_DashStream *ds, GF_MPD_Representation *rep)
    4006             : {
    4007             :         Bool transcode_detected = GF_FALSE;
    4008             :         Bool recompute_set = GF_FALSE;
    4009           6 :         GF_MPD_Representation *n_rep = ds->rep;
    4010             : 
    4011             :         //TODO: copy other properties in case we transcode ?
    4012             : 
    4013             : #define CHECK_VAL(_name, _v) if (rep->_name != n_rep->_name) { rep->_name = n_rep->_name; transcode_detected = GF_TRUE; if (_v) recompute_set = GF_TRUE; }
    4014             : 
    4015             : #define CHECK_STR(_name) if (rep->_name && n_rep->_name && !strcmp(rep->_name, n_rep->_name)) {} \
    4016             :         else if (!rep->_name && !n_rep->_name) {}\
    4017             :         else { \
    4018             :                 if (rep->_name) gf_free(rep->_name); rep->_name = n_rep->_name ? gf_strdup(n_rep->_name) : NULL; \
    4019             :                 transcode_detected = GF_TRUE; \
    4020             :         }
    4021             : 
    4022             : //for frac, if not set on source PID, consider it unchanged
    4023             : #define CHECK_FRAC(_name) if (rep->_name && n_rep->_name && (rep->_name->num * n_rep->_name->den == rep->_name->den * n_rep->_name->num)) {} \
    4024             :         else if (!n_rep->_name) {}\
    4025             :         else { \
    4026             :                 if (rep->_name) gf_free(rep->_name); \
    4027             :                 if (n_rep->_name) { rep->_name = gf_malloc(sizeof(GF_MPD_Fractional)); memcpy(rep->_name, n_rep->_name, sizeof(GF_MPD_Fractional)); } \
    4028             :                 else rep->_name = NULL; \
    4029             :                 transcode_detected = GF_TRUE; \
    4030             :         }
    4031             : 
    4032           6 :         CHECK_STR(codecs)
    4033           6 :         CHECK_STR(profiles)
    4034           6 :         CHECK_STR(mime_type)
    4035           6 :         CHECK_STR(segmentProfiles)
    4036           6 :         CHECK_VAL(width, 1)
    4037           6 :         CHECK_VAL(height, 1)
    4038           6 :         CHECK_VAL(bandwidth, 0)
    4039           6 :         CHECK_VAL(samplerate, 0)
    4040           6 :         CHECK_VAL(scan_type, 0)
    4041           6 :         CHECK_FRAC(sar)
    4042           6 :         CHECK_FRAC(framerate)
    4043             : 
    4044           6 :         if (transcode_detected && !ds->transcode_detected) {
    4045           0 :                 ds->transcode_detected = GF_TRUE;
    4046           0 :                 GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Transcoded detected in forward mode, not fully tested !\n"));
    4047             :         }
    4048             : #undef CHECK_VAL
    4049             : #undef CHECK_STR
    4050             : #undef CHECK_FRAC
    4051             : 
    4052           6 :         return recompute_set;
    4053             : }
    4054           4 : static void dasher_forward_manifest_raw(GF_DasherCtx *ctx, GF_DashStream *ds, const char *manifest, const char *manifest_name)
    4055             : {
    4056             :         GF_FilterPacket *pck;
    4057             :         u32 size;
    4058             :         u8 *output;
    4059             : 
    4060           4 :         size = (u32) strlen(manifest);
    4061             : 
    4062           4 :         pck = gf_filter_pck_new_alloc(ctx->opid, size+1, &output);
    4063           4 :         if (!pck) return;
    4064             : 
    4065           4 :         memcpy(output, manifest, size);
    4066           4 :         output[size] = 0;
    4067           4 :         gf_filter_pck_set_framing(pck, GF_TRUE, GF_TRUE);
    4068           4 :         gf_filter_pck_set_seek_flag(pck, GF_TRUE);
    4069           4 :         if (manifest_name) {
    4070           3 :                 if (ctx->out_path) {
    4071           3 :                         char *url = gf_url_concatenate(ctx->out_path, manifest_name);
    4072           3 :                         gf_filter_pck_set_property(pck, GF_PROP_PCK_FILENAME, &PROP_STRING_NO_COPY(url) );
    4073             :                 } else {
    4074           0 :                         gf_filter_pck_set_property(pck, GF_PROP_PCK_FILENAME, &PROP_STRING(manifest_name) );
    4075             :                 }
    4076           3 :                 if (ds)
    4077           3 :                         gf_filter_pck_set_property(pck, GF_PROP_PCK_HLS_REF, &PROP_LONGUINT( ds->hls_ref_id ) );
    4078             :         }
    4079           4 :         gf_filter_pck_send(pck);
    4080             : 
    4081             : }
    4082           2 : static void dasher_forward_mpd(GF_DasherCtx *ctx, const char *manifest)
    4083             : {
    4084             :         u32 i, nb_periods, nb_streams;
    4085             :         FILE *tmp = NULL;
    4086           2 :         GF_MPD *mpd = gf_mpd_new();
    4087             :         GF_List *recompute_sets = NULL;
    4088           2 :         GF_DOMParser *dom = gf_xml_dom_new();
    4089           2 :         GF_Err e = gf_xml_dom_parse_string(dom, (char *)manifest);
    4090           2 :         if (e) goto err_exit;
    4091             : 
    4092           2 :         e = gf_mpd_init_from_dom(gf_xml_dom_get_root(dom), mpd, NULL);
    4093           2 :         if (e) goto err_exit;
    4094             : 
    4095           2 :         nb_streams = gf_list_count(ctx->pids);
    4096           2 :         nb_periods = gf_list_count(mpd->periods);
    4097           4 :         for (i=0; i<nb_periods; i++) {
    4098             :                 u32 j;
    4099           2 :                 GF_MPD_AdaptationSet *set = NULL;
    4100           2 :                 GF_MPD_Representation *rep = NULL;
    4101           2 :                 GF_MPD_Period *period = gf_list_get(mpd->periods, i);
    4102           8 :                 for (j=0; j<nb_streams; j++) {
    4103             :                         Bool invalidate_set;
    4104           6 :                         GF_DashStream *ds = gf_list_get(ctx->pids, j);
    4105           6 :                         if (ds->muxed_base) continue;
    4106           6 :                         const GF_PropertyValue *ps = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_DASH_PERIOD_START);
    4107           6 :                         const GF_PropertyValue *repid = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_REP_ID);
    4108           6 :                         if (!ps || !repid) {
    4109           0 :                                 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Couldn't fetch period start or rep ID in forward mode, cannot forward\n"));
    4110           0 :                                 goto err_exit;
    4111             :                         }
    4112           6 :                         if (period->start != ps->value.longuint) continue;
    4113           6 :                         dasher_get_set_and_rep(period, repid->value.string, &set, &rep);
    4114           6 :                         if (!set || !rep) {
    4115           0 :                                 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Couldn't locate adaptation set and period in source manifest in forward mode, cannot forward\n"));
    4116             :                                 goto err_exit;
    4117             :                         }
    4118             :                         //copy/reset common encryption
    4119           6 :                         if (set->content_protection) {
    4120           6 :                                 gf_mpd_del_list(set->content_protection, gf_mpd_descriptor_free, 1);
    4121             :                         }
    4122           6 :                         if (rep->content_protection) {
    4123           6 :                                 gf_mpd_del_list(rep->content_protection, gf_mpd_descriptor_free, 1);
    4124             :                         }
    4125           6 :                         if (gf_list_count(ds->rep->content_protection)) {
    4126           0 :                                 gf_list_del(rep->content_protection);
    4127           0 :                                 rep->content_protection = dasher_get_content_protection_desc(ctx, ds, NULL);
    4128             :                         }
    4129           6 :                         if (gf_list_count(ds->set->content_protection)) {
    4130           6 :                                 gf_list_del(set->content_protection);
    4131           6 :                                 set->content_protection = dasher_get_content_protection_desc(ctx, ds, ds->set);
    4132             :                         }
    4133           6 :                         invalidate_set = dasher_merge_rep(ds, rep);
    4134             :                         //wait until we are all done
    4135           6 :                         if (invalidate_set) {
    4136           0 :                                 if (!recompute_sets) recompute_sets = gf_list_new();
    4137           0 :                                 if (gf_list_find(recompute_sets, set)<0)
    4138           0 :                                         gf_list_add(recompute_sets, set);
    4139             : 
    4140             :                         }
    4141             :                 }
    4142             :         }
    4143             :         //update sets - TODO
    4144             : 
    4145             :         //and send
    4146           2 :         tmp = gf_file_temp(NULL);
    4147           2 :         mpd->xml_namespace = ctx->mpd->xml_namespace;
    4148           2 :         mpd->publishTime = dasher_get_utc(ctx);
    4149           2 :         e = gf_mpd_write(mpd, tmp, ctx->cmpd);
    4150           2 :         if (e) {
    4151           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Error serializing manifest in forward mode: %s\n", gf_error_to_string(e) ));
    4152             :                 e = GF_OK;
    4153             :                 goto err_exit;
    4154             :         }
    4155           2 :         dasher_transfer_file(tmp, ctx->opid, NULL, NULL);
    4156             : 
    4157           2 : err_exit:
    4158           2 :         if (tmp) gf_fclose(tmp);
    4159           2 :         gf_mpd_del(mpd);
    4160           2 :         gf_xml_dom_del(dom);
    4161           2 :         gf_list_del(recompute_sets);
    4162           2 :         if (e)
    4163           0 :                 ctx->in_error = GF_TRUE;
    4164           2 : }
    4165             : 
    4166         658 : static GF_Err dasher_write_and_send_manifest(GF_DasherCtx *ctx, u64 last_period_dur, Bool do_m3u8, Bool m3u8_second_pass, GF_FilterPid *opid, char *alt_name)
    4167             : {
    4168             :         void *last_signature;
    4169             :         u8 sig[GF_SHA1_DIGEST_SIZE];
    4170             :         GF_Err e;
    4171         658 :         FILE *tmp = gf_file_temp(NULL);
    4172         658 :         if (do_m3u8) {
    4173         386 :                 ctx->mpd->m3u8_time = ctx->hlsc;
    4174         386 :                 if (ctx->llhls==3)
    4175          42 :                         ctx->mpd->force_llhls_mode = m3u8_second_pass ? 2 : 1;
    4176             :                 else
    4177         344 :                         ctx->mpd->force_llhls_mode = 0;
    4178             : 
    4179         386 :                 if (m3u8_second_pass) {
    4180          21 :                         e = gf_mpd_write_m3u8_master_playlist(ctx->mpd, tmp, ctx->out_path, gf_list_get(ctx->mpd->periods, 0) );
    4181             :                 } else {
    4182         365 :                         e = gf_mpd_write_m3u8_master_playlist(ctx->mpd, tmp, ctx->out_path, gf_list_get(ctx->mpd->periods, 0) );
    4183             :                 }
    4184             :         } else {
    4185         272 :                 e = gf_mpd_write(ctx->mpd, tmp, ctx->cmpd);
    4186             :         }
    4187             : 
    4188         658 :         if (e) {
    4189           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] failed to write %s file: %s\n", do_m3u8 ? "M3U8" : "MPD", gf_error_to_string(e) ));
    4190           0 :                 gf_fclose(tmp);
    4191           0 :                 if (ctx->current_period->period)
    4192           0 :                         ctx->current_period->period->duration = last_period_dur;
    4193             :                 return e;
    4194             :         }
    4195             : 
    4196         658 :         if (ctx->profile == GF_DASH_PROFILE_HBBTV_1_5_ISOBMF_LIVE) {
    4197           0 :                 if (gf_ftell(tmp) > 100 * 1024)
    4198           0 :                         GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] manifest MPD is too big for HbbTV 1.5. Limit is 100kB, current size is "LLU"kB\n", gf_ftell(tmp) / 1024));
    4199             :         }
    4200             : 
    4201         658 :         gf_sha1_file_ptr(tmp, sig);
    4202         658 :         if (do_m3u8) {
    4203         386 :                 last_signature = (void *) m3u8_second_pass ? ctx->last_hls2_signature : ctx->last_hls_signature;
    4204             :         } else {
    4205         272 :                 last_signature = (void *) ctx->last_mpd_signature;
    4206             :         }
    4207             : 
    4208         658 :         if (memcmp(sig, last_signature, GF_SHA1_DIGEST_SIZE)) {
    4209             :                 memcpy(last_signature, sig, GF_SHA1_DIGEST_SIZE);
    4210             : 
    4211         416 :                 dasher_transfer_file(tmp, opid, alt_name, NULL);
    4212             :         }
    4213         658 :         gf_fclose(tmp);
    4214         658 :         return GF_OK;
    4215             : }
    4216             : 
    4217         253 : static void dasher_update_dyn_bitrates(GF_DasherCtx *ctx)
    4218             : {
    4219         253 :         u32 i, count = gf_list_count(ctx->current_period->streams);
    4220         530 :         for (i=0; i<count; i++) {
    4221         277 :                 GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
    4222         277 :                 if (ds->dyn_bitrate) dasher_update_bitrate(ctx, ds);
    4223             :         }
    4224         253 : }
    4225             : 
    4226         638 : GF_Err dasher_send_manifest(GF_Filter *filter, GF_DasherCtx *ctx, Bool for_mpd_only)
    4227             : {
    4228             :         GF_Err e;
    4229             :         u32 i, max_opid;
    4230             :         FILE *tmp;
    4231             :         u64 store_mpd_dur=0;
    4232             :         u64 max_seg_dur=0;
    4233             :         u64 last_period_dur;
    4234             : 
    4235             :         //manifest forwarding
    4236         638 :         if (ctx->forward_mode == DASHER_FWD_ALL)
    4237             :                 return GF_OK;
    4238             : 
    4239         635 :         if (ctx->dyn_rate)
    4240         209 :                 dasher_update_dyn_bitrates(ctx);
    4241             : 
    4242             :         //UGLY PATCH, to remove - we don't have the same algos in old arch and new arch, which result in slightly different max segment duration
    4243             :         //on audio for our test suite - patch it manually to avoid hash failures :(
    4244             :         //TODO, remove as soon as we switch archs
    4245         635 :         if (gf_sys_old_arch_compat() && (ctx->mpd->max_segment_duration==1022) && (ctx->mpd->media_presentation_duration==10160) ) {
    4246           1 :                 ctx->mpd->max_segment_duration = 1080;
    4247           1 :                 GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] patch for old regression tests hit, changing max seg dur from 1022 to 1080\nPlease notify GPAC devs to remove this, and do not use fot_test modes in dash filter\n"));
    4248             :         }
    4249             : 
    4250        1270 :         ctx->mpd->publishTime = dasher_get_utc(ctx);
    4251         635 :         if (ctx->utc_timing_type==DASHER_UTCREF_INBAND) {
    4252           0 :                 GF_MPD_Descriptor *d = gf_list_get(ctx->mpd->utc_timings, 0);
    4253           0 :                 if (d) {
    4254             :                         time_t gtime;
    4255             :                         struct tm *t;
    4256             :                         u32 sec;
    4257             :                         u32 ms;
    4258             :                         char szTime[100];
    4259           0 :                         if (d->value) gf_free(d->value);
    4260             : 
    4261           0 :                         gtime = ctx->mpd->publishTime / 1000;
    4262           0 :                         sec = (u32)(ctx->mpd->publishTime / 1000);
    4263           0 :                         ms = (u32)(ctx->mpd->publishTime - ((u64)sec) * 1000);
    4264             : 
    4265           0 :                         t = gf_gmtime(&gtime);
    4266           0 :                         sec = t->tm_sec;
    4267             :                         //see issue #859, no clue how this happened...
    4268           0 :                         if (sec > 60)
    4269             :                                 sec = 60;
    4270           0 :                         snprintf(szTime, 100, "%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);
    4271           0 :                         d->value = gf_strdup(szTime);
    4272             :                 }
    4273             :         }
    4274         635 :         dasher_update_mpd(ctx);
    4275         635 :         ctx->mpd->write_context = GF_FALSE;
    4276         635 :         ctx->mpd->was_dynamic = GF_FALSE;
    4277         635 :         if (ctx->dmode==GF_DASH_DYNAMIC_LAST)
    4278           8 :                 ctx->mpd->was_dynamic = GF_TRUE;
    4279             : 
    4280         635 :         if ((ctx->refresh>=0) && (ctx->dmode==GF_DASH_DYNAMIC)) {
    4281         408 :                 store_mpd_dur= ctx->mpd->media_presentation_duration;
    4282             :         }
    4283             : 
    4284         635 :         if (ctx->sseg && ctx->mpd->max_segment_duration) {
    4285          18 :                 max_seg_dur = ctx->mpd->max_subsegment_duration = ctx->mpd->max_segment_duration;
    4286          18 :                 ctx->mpd->max_segment_duration = 0;
    4287             :         }
    4288             : 
    4289             :         last_period_dur = 0;
    4290         635 :         if (ctx->current_period->period) {
    4291         635 :                 last_period_dur = ctx->current_period->period->duration;
    4292         635 :                 if (ctx->dmode==GF_DASH_DYNAMIC) {
    4293         408 :                         ctx->current_period->period->duration = 0;
    4294         408 :                         ctx->mpd->media_presentation_duration = 0;
    4295             :                 }
    4296             :         }
    4297             : 
    4298         635 :         max_opid = (ctx->dual && ctx->opid_alt) ? 2 : 1;
    4299        1272 :         for (i=0; i < max_opid; i++) {
    4300             :                 Bool do_m3u8 = GF_FALSE;
    4301             :                 GF_FilterPid *opid;
    4302             : 
    4303         637 :                 if (i==0) {
    4304         635 :                         if (max_opid>1) {
    4305           2 :                                 do_m3u8 = ctx->opid_alt_m3u8 ? GF_FALSE : GF_TRUE;
    4306             :                         } else {
    4307         633 :                                 do_m3u8 = ctx->do_m3u8;
    4308             :                         }
    4309         635 :                         opid = ctx->opid;
    4310             :                 } else {
    4311           2 :                         do_m3u8 = ctx->opid_alt_m3u8;
    4312           2 :                         opid = ctx->opid_alt;
    4313             :                 }
    4314         637 :                 if (do_m3u8 && for_mpd_only) {
    4315           0 :                         continue;
    4316             :                 }
    4317         637 :                 if ((ctx->llhls==3) && do_m3u8)
    4318          21 :                         ctx->mpd->force_llhls_mode = 1;
    4319         637 :                 e = dasher_write_and_send_manifest(ctx, last_period_dur, do_m3u8, GF_FALSE, opid, NULL);
    4320         637 :                 if (e) return e;
    4321             : 
    4322         637 :                 ctx->mpd->force_llhls_mode = 0;
    4323             :         }
    4324             : 
    4325         635 :         if (ctx->current_period->period)
    4326         635 :                 ctx->current_period->period->duration = last_period_dur;
    4327             : 
    4328         635 :         if (store_mpd_dur) {
    4329         303 :                 ctx->mpd->media_presentation_duration = store_mpd_dur;
    4330             :         }
    4331         635 :         if (max_seg_dur) {
    4332          18 :                 ctx->mpd->max_segment_duration = (u32) max_seg_dur;
    4333          18 :                 ctx->mpd->max_subsegment_duration = 0;
    4334             :         }
    4335             : 
    4336         635 :         if (ctx->do_m3u8) {
    4337             :                 Bool m3u8_second_pass = GF_FALSE;
    4338             :                 u32 j;
    4339         365 :                 GF_MPD_Period *period = gf_list_get(ctx->mpd->periods, 0);
    4340             :                 GF_MPD_AdaptationSet *as;
    4341             :                 GF_MPD_Representation *rep;
    4342             :                 GF_FilterPid *opid;
    4343             :                 assert(period);
    4344         365 :                 if (ctx->opid_alt_m3u8) opid = ctx->opid_alt;
    4345         365 :                 else opid = ctx->opid;
    4346             : 
    4347         365 : resend:
    4348         386 :                 i=0;
    4349        1168 :                 while ( (as = (GF_MPD_AdaptationSet *) gf_list_enum(period->adaptation_sets, &i))) {
    4350         396 :                         j=0;
    4351        1388 :                         while ( (rep = (GF_MPD_Representation *) gf_list_enum(as->representations, &j))) {
    4352         596 :                                 if (rep->m3u8_var_file) {
    4353             :                                         GF_DashStream *ds;
    4354         596 :                                         char *outfile = rep->m3u8_var_name;
    4355             :                                         Bool do_free = GF_FALSE;
    4356             : 
    4357         596 :                                         if (rep->m3u8_name) {
    4358             :                                                 outfile = (char *) rep->m3u8_name;
    4359           0 :                                                 if (ctx->out_path) {
    4360           0 :                                                         outfile = gf_url_concatenate(ctx->out_path, rep->m3u8_name);
    4361             :                                                         do_free = GF_TRUE;
    4362             :                                                 }
    4363             :                                         }
    4364         596 :                                         if (m3u8_second_pass) {
    4365             :                                                 char *sep;
    4366          21 :                                                 char *new_name = gf_strdup(outfile);
    4367             : 
    4368          21 :                                                 sep = gf_file_ext_start(new_name);
    4369          21 :                                                 if (sep) sep[0] = 0;
    4370          21 :                                                 gf_dynstrcat(&new_name, "_IF", NULL);
    4371          21 :                                                 sep = gf_file_ext_start(outfile);
    4372          21 :                                                 if (sep)
    4373          21 :                                                         gf_dynstrcat(&new_name, sep, NULL);
    4374             : 
    4375          21 :                                                 if (do_free) gf_free(outfile);
    4376          21 :                                                 outfile = new_name;
    4377             :                                                 do_free = GF_TRUE;
    4378             :                                         }
    4379         596 :                                         ds = rep->playback.udta;
    4380         596 :                                         dasher_transfer_file(rep->m3u8_var_file, opid, outfile, ds);
    4381         596 :                                         gf_fclose(rep->m3u8_var_file);
    4382         596 :                                         rep->m3u8_var_file = NULL;
    4383         596 :                                         if (do_free) gf_free(outfile);
    4384             :                                 }
    4385             :                         }
    4386             :                 }
    4387             : 
    4388         386 :                 if ((ctx->llhls==3) && !m3u8_second_pass && ctx->out_path) {
    4389             :                         char *sep;
    4390             :                         char szAltName[GF_MAX_PATH];
    4391             :                         strcpy(szAltName, ctx->out_path);
    4392          21 :                         sep = gf_file_ext_start(szAltName);
    4393          21 :                         if (sep) sep[0] = 0;
    4394             :                         strcat(szAltName, "_IF");
    4395          21 :                         sep = gf_file_ext_start(ctx->out_path);
    4396          21 :                         if (sep) strcat(szAltName, sep);
    4397             : 
    4398          21 :                         ctx->mpd->force_llhls_mode = 2;
    4399          21 :                         e = dasher_write_and_send_manifest(ctx, last_period_dur, GF_TRUE, GF_TRUE, ctx->opid, szAltName);
    4400          21 :                         if (e) return e;
    4401             : 
    4402             :                         m3u8_second_pass = GF_TRUE;
    4403          21 :                         goto resend;
    4404             : 
    4405             :                 }
    4406             :         }
    4407             : 
    4408             : 
    4409         635 :         if (ctx->state) {
    4410          49 :                 tmp = gf_fopen(ctx->state, "w");
    4411          49 :                 if (!tmp) {
    4412           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] failed to open context MPD %s for write\n", ctx->state ));
    4413             :                         return GF_IO_ERR;
    4414             :                 }
    4415          49 :                 ctx->mpd->write_context = GF_TRUE;
    4416          49 :                 e = gf_mpd_write(ctx->mpd, tmp, ctx->cmpd);
    4417          49 :                 gf_fclose(tmp);
    4418          49 :                 ctx->mpd->write_context = GF_FALSE;
    4419          49 :                 if (e) {
    4420           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] failed to write MPD file: %s\n", gf_error_to_string(e) ));
    4421             :                 }
    4422             :                 return e;
    4423             :         }
    4424             :         return GF_OK;
    4425             : }
    4426             : 
    4427         731 : static void dasher_reset_stream(GF_Filter *filter, GF_DashStream *ds, Bool is_destroy)
    4428             : {
    4429             :         //we do not remove the destination filter, it will be removed automatically once all remove_pids are called
    4430             :         //removing it explicetly will discard the upper chain and any packets not yet processed
    4431             : 
    4432         731 :         ds->dst_filter = NULL;
    4433         731 :         if (ds->seg_template) gf_free(ds->seg_template);
    4434         731 :         if (ds->idx_template) gf_free(ds->idx_template);
    4435         731 :         if (ds->init_seg) gf_free(ds->init_seg);
    4436         731 :         if (ds->multi_pids) gf_list_del(ds->multi_pids);
    4437         731 :         ds->multi_pids = NULL;
    4438         731 :         if (ds->multi_tracks) gf_list_del(ds->multi_tracks);
    4439         731 :         ds->multi_tracks = NULL;
    4440             : 
    4441         731 :         if (ds->pending_segment_urls) gf_list_del(ds->pending_segment_urls);
    4442         731 :         ds->pending_segment_urls = NULL;
    4443         731 :         if (ds->pending_segment_states) gf_list_del(ds->pending_segment_states);
    4444         731 :         ds->pending_segment_states = NULL;
    4445             : 
    4446         731 :         if (is_destroy) {
    4447         357 :                 if (ds->cues) gf_free(ds->cues);
    4448         357 :                 gf_list_del(ds->complementary_streams);
    4449         357 :                 gf_free(ds->rep_id);
    4450             :                 //string properties are locally copied
    4451             : #define RESET_PROP_STR(_prop) \
    4452             :                 if (_prop) gf_free(_prop);
    4453             : 
    4454         357 :                 RESET_PROP_STR(ds->src_url)
    4455         357 :                 RESET_PROP_STR(ds->template)
    4456         357 :                 RESET_PROP_STR(ds->lang)
    4457         357 :                 RESET_PROP_STR(ds->hls_vp_name)
    4458         357 :                 RESET_PROP_STR(ds->xlink)
    4459         357 :                 RESET_PROP_STR(ds->period_id)
    4460         357 :                 RESET_PROP_STR(ds->period_continuity_id)
    4461             : 
    4462             : #undef RESET_PROP_STR
    4463             :                 return;
    4464             :         }
    4465         374 :         ds->init_seg = ds->seg_template = ds->idx_template = NULL;
    4466         374 :         ds->split_set_names = GF_FALSE;
    4467         374 :         ds->nb_sap_3 = 0;
    4468         374 :         ds->nb_sap_4 = 0;
    4469         374 :         ds->pid_id = 0;
    4470         374 :         ds->force_timescale = 0;
    4471         374 :         ds->set = NULL;
    4472         374 :         ds->owns_set = GF_FALSE;
    4473         374 :         ds->rep = NULL;
    4474         374 :         ds->muxed_base = NULL;
    4475         374 :         ds->nb_comp = ds->nb_comp_done = 0;
    4476         374 :         gf_list_reset(ds->complementary_streams);
    4477         374 :         ds->inband_params = GF_FALSE;
    4478         374 :         ds->seg_start_time = 0;
    4479         374 :         ds->seg_number = ds->startNumber;
    4480         374 :         ds->nb_segments_purged = 0;
    4481         374 :         ds->dur_purged = 0;
    4482         374 :         ds->moof_sn_inc = 0;
    4483         374 :         ds->moof_sn = 0;
    4484         374 :         ds->seg_done = 0;
    4485         374 :         ds->subdur_done = 0;
    4486         374 :         if (ds->packet_queue) {
    4487           6 :                 while (gf_list_count(ds->packet_queue)) {
    4488           0 :                         GF_FilterPacket *pck = gf_list_pop_front(ds->packet_queue);
    4489           0 :                         gf_filter_pck_unref(pck);
    4490             :                 }
    4491           6 :                 ds->nb_sap_in_queue = 0;
    4492             :         }
    4493             : }
    4494             : 
    4495          32 : void dasher_context_update_period_end(GF_DasherCtx *ctx)
    4496             : {
    4497             :         u32 i, count;
    4498             : 
    4499          32 :         if (!ctx->mpd) return;
    4500             : 
    4501          32 :         count = gf_list_count(ctx->current_period->streams);
    4502          89 :         for (i=0; i<count; i++) {
    4503          57 :                 GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
    4504          57 :                 if (!ds->rep) continue;
    4505          37 :                 if (!ds->rep->dasher_ctx) continue;
    4506          37 :                 if (ds->done == 1) {
    4507           6 :                         ds->rep->dasher_ctx->done = 1;
    4508             :                 } else {
    4509             :                         //store all dynamic parameters of the rep
    4510          31 :                         ds->rep->dasher_ctx->last_pck_idx = ds->nb_pck;
    4511          31 :                         ds->seek_to_pck = ds->nb_pck;
    4512          31 :                         ds->rep->dasher_ctx->seg_number = ds->seg_number;
    4513          31 :                         ds->rep->dasher_ctx->next_seg_start = ds->next_seg_start;
    4514          31 :                         ds->rep->dasher_ctx->first_cts = ds->first_cts;
    4515          31 :                         ds->rep->dasher_ctx->first_dts = ds->first_dts;
    4516          31 :                         ds->rep->dasher_ctx->ts_offset = ds->ts_offset;
    4517          31 :                         ds->rep->dasher_ctx->segs_purged = ds->nb_segments_purged;
    4518          31 :                         ds->rep->dasher_ctx->dur_purged = ds->dur_purged;
    4519          31 :                         ds->rep->dasher_ctx->moof_sn = ds->moof_sn;
    4520          31 :                         ds->rep->dasher_ctx->moof_sn_inc = ds->moof_sn_inc;
    4521          31 :                         ds->rep->dasher_ctx->subdur_forced = ds->subdur_forced_use_period_dur ? GF_TRUE : GF_FALSE;
    4522             :                 }
    4523          37 :                 if (ctx->subdur) {
    4524          35 :                         ds->rep->dasher_ctx->cumulated_subdur = ds->cumulated_subdur + ctx->subdur;
    4525          35 :                         ds->rep->dasher_ctx->cumulated_dur = ((Double)ds->cumulated_dur) / ds->timescale;
    4526             : 
    4527             :                 }
    4528          37 :                 ds->rep->dasher_ctx->nb_repeat = ds->nb_repeat;
    4529          37 :                 ds->rep->dasher_ctx->est_next_dts = ds->est_next_dts;
    4530          37 :                 ds->rep->dasher_ctx->source_pid = ds->id;
    4531          37 :                 ds->rep->dasher_ctx->mpd_timescale = ds->mpd_timescale;
    4532          37 :                 ds->rep->dasher_ctx->last_dyn_period_id = ctx->last_dyn_period_id;
    4533             : 
    4534             :                 assert(ds->rep->dasher_ctx->init_seg);
    4535             :                 assert(ds->rep->dasher_ctx->src_url);
    4536             :                 assert(ds->rep->dasher_ctx->template_seg);
    4537             :         }
    4538             : }
    4539             : 
    4540          14 : void dasher_context_update_period_start(GF_DasherCtx *ctx)
    4541             : {
    4542             :         u32 i, j, count;
    4543             : 
    4544          14 :         if (!ctx->mpd) return;
    4545          14 :         count = gf_list_count(ctx->current_period->streams);
    4546          39 :         for (i=0; i<count; i++) {
    4547          25 :                 GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
    4548          25 :                 if (!ds->rep) continue;
    4549          24 :                 if (ds->rep->dasher_ctx) continue;
    4550             : 
    4551             :                 //store all static parameters of the rep
    4552          24 :                 GF_SAFEALLOC(ds->rep->dasher_ctx, GF_DASH_SegmenterContext);
    4553          24 :                 if (!ds->rep->dasher_ctx) return;
    4554             : 
    4555          24 :                 ds->rep->dasher_ctx->done = 0;
    4556             : 
    4557             :                 assert(ds->init_seg);
    4558          24 :                 ds->rep->dasher_ctx->init_seg = gf_strdup(ds->init_seg);
    4559             :                 assert(ds->src_url);
    4560          24 :                 ds->rep->dasher_ctx->src_url = gf_strdup(ds->src_url);
    4561             :                 assert(ds->seg_template);
    4562          24 :                 ds->rep->dasher_ctx->template_seg = gf_strdup(ds->seg_template);
    4563          24 :                 if (ds->idx_template)
    4564           0 :                         ds->rep->dasher_ctx->template_idx = gf_strdup(ds->idx_template);
    4565             : 
    4566          24 :                 ds->rep->dasher_ctx->pid_id = ds->pid_id;
    4567          24 :                 ds->rep->dasher_ctx->dep_pid_id = ds->dep_pid_id;
    4568          24 :                 ds->rep->dasher_ctx->period_start = ds->period_start;
    4569          24 :                 ds->rep->dasher_ctx->period_duration = ds->period_dur;
    4570          24 :                 ds->rep->dasher_ctx->multi_pids = ds->multi_pids ? GF_TRUE : GF_FALSE;
    4571          24 :                 ds->rep->dasher_ctx->dash_dur = ds->dash_dur;
    4572             : 
    4573          24 :                 if (strcmp(ds->period_id, DEFAULT_PERIOD_ID))
    4574           0 :                         ds->rep->dasher_ctx->period_id = ds->period_id;
    4575             : 
    4576          24 :                 ds->rep->dasher_ctx->owns_set = (ds->set->udta == ds) ? GF_TRUE : GF_FALSE;
    4577             : 
    4578          24 :                 if (ds->rep->dasher_ctx->mux_pids) gf_free(ds->rep->dasher_ctx->mux_pids);
    4579          24 :                 ds->rep->dasher_ctx->mux_pids = NULL;
    4580          69 :                 for (j=0; j<count; j++) {
    4581             :                         char szMuxPID[10];
    4582          45 :                         GF_DashStream *a_ds = gf_list_get(ctx->current_period->streams, j);
    4583          89 :                         if (a_ds==ds) continue;
    4584          21 :                         if (a_ds->muxed_base != ds) continue;
    4585             : 
    4586           1 :                         if (ds->rep->dasher_ctx->mux_pids)
    4587           0 :                                 sprintf(szMuxPID, " %d", a_ds->id);
    4588             :                         else
    4589           1 :                                 sprintf(szMuxPID, "%d", a_ds->id);
    4590             : 
    4591           1 :                         gf_dynstrcat(&ds->rep->dasher_ctx->mux_pids, szMuxPID, NULL);
    4592             :                 }
    4593             : 
    4594             :         }
    4595             : }
    4596             : 
    4597          34 : static GF_DashStream *dasher_get_stream(GF_DasherCtx *ctx, const char *src_url, u32 original_pid, u32 pid_id)
    4598             : {
    4599          34 :         u32 i, count = gf_list_count(ctx->pids);
    4600          14 :         for (i=0; i<count; i++) {
    4601          48 :                 GF_DashStream *ds = gf_list_get(ctx->pids, i);
    4602          48 :                 if (pid_id && (ds->pid_id==pid_id)) return ds;
    4603          48 :                 if (src_url && ds->src_url && !strcmp(ds->src_url, src_url) && (ds->id == original_pid) ) return ds;
    4604             :         }
    4605             :         return NULL;
    4606             : }
    4607             : 
    4608           4 : static GF_Err dasher_reload_muxed_comp(GF_DasherCtx *ctx, GF_DashStream *base_ds, char *mux_pids, Bool check_only)
    4609             : {
    4610             :         GF_Err e = GF_OK;
    4611           8 :         while (mux_pids) {
    4612             :                 u32 pid_id;
    4613             :                 GF_DashStream *ds;
    4614           4 :                 char *sep = strchr(mux_pids, ' ');
    4615           4 :                 if (sep) sep[0] = 0;
    4616             : 
    4617           4 :                 pid_id = atoi(mux_pids);
    4618           4 :                 ds = dasher_get_stream(ctx, base_ds->src_url, pid_id, 0);
    4619           4 :                 if (ds) {
    4620           4 :                         if (!check_only) {
    4621           2 :                                 if (ds->rep) gf_mpd_representation_free(ds->rep);
    4622           2 :                                 ds->rep = NULL;
    4623           2 :                                 ds->set = base_ds->set;
    4624           2 :                                 ds->muxed_base = base_ds;
    4625           2 :                                 base_ds->nb_comp ++;
    4626           2 :                                 ds->nb_comp = 1;
    4627           2 :                                 ds->done = base_ds->done;
    4628           2 :                                 ds->subdur_done = base_ds->subdur_done;
    4629           2 :                                 ds->period = ctx->current_period;
    4630             : 
    4631           2 :                                 gf_list_del_item(ctx->next_period->streams, ds);
    4632           2 :                                 gf_list_add(ctx->current_period->streams, ds);
    4633             :                         }
    4634             :                 } else {
    4635           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Couldn't find muxed PID %d in source %s, did you modify the source ?\n", pid_id, base_ds->src_url));
    4636             :                         e = GF_BAD_PARAM;
    4637             :                 }
    4638             : 
    4639           4 :                 if (!sep) break;
    4640           0 :                 sep[0] = ' ';
    4641           0 :                 mux_pids = sep+1;
    4642             : 
    4643           0 :                 if (e) return e;
    4644             :         }
    4645             :         return GF_OK;
    4646             : }
    4647             : 
    4648          21 : static GF_Err dasher_reload_context(GF_Filter *filter, GF_DasherCtx *ctx)
    4649             : {
    4650             :         GF_Err e;
    4651             :         Bool last_period_active = GF_FALSE;
    4652             :         u32 i, j, k, nb_p, nb_as, nb_rep, count;
    4653             :         GF_DOMParser *mpd_parser;
    4654             : 
    4655          21 :         ctx->first_context_load = GF_FALSE;
    4656             : 
    4657          21 :         if (!gf_file_exists(ctx->state)) return GF_OK;
    4658             : 
    4659             :         /* parse the MPD */
    4660          10 :         mpd_parser = gf_xml_dom_new();
    4661          10 :         e = gf_xml_dom_parse(mpd_parser, ctx->state, NULL, NULL);
    4662             : 
    4663          10 :         if (e != GF_OK) {
    4664           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Cannot parse MPD state %s: %s\n", ctx->state, gf_xml_dom_get_error(mpd_parser) ));
    4665           0 :                 gf_xml_dom_del(mpd_parser);
    4666           0 :                 return GF_URL_ERROR;
    4667             :         }
    4668          10 :         if (ctx->mpd) gf_mpd_del(ctx->mpd);
    4669          10 :         ctx->mpd = gf_mpd_new();
    4670          10 :         e = gf_mpd_init_from_dom(gf_xml_dom_get_root(mpd_parser), ctx->mpd, ctx->state);
    4671          10 :         gf_xml_dom_del(mpd_parser);
    4672             :         //test mode, strip URL path
    4673          10 :         if (gf_sys_is_test_mode()) {
    4674          10 :                 count = gf_list_count(ctx->mpd->program_infos);
    4675          20 :                 for (i=0; i<count; i++) {
    4676          10 :                         GF_MPD_ProgramInfo *info = gf_list_get(ctx->mpd->program_infos, i);
    4677          10 :                         if (info->title && strstr(info->title, "generated by GPAC")) {
    4678          10 :                                 gf_free(info->title);
    4679             :                                 char tmp[256];
    4680          10 :                                 char *name = strrchr(ctx->out_path, '/');
    4681          10 :                                 if (!name) name = strrchr(ctx->out_path, '\\');
    4682          10 :                                 if (!name) name = ctx->out_path;
    4683          10 :                                 else name++;
    4684             :                                 sprintf(tmp, "%s generated by GPAC", name);
    4685          10 :                                 info->title = gf_strdup(tmp);
    4686             :                         }
    4687             :                 }
    4688             :         }
    4689             : 
    4690          10 :         if (!ctx->mpd->xml_namespace)
    4691          10 :                 ctx->mpd->xml_namespace = "urn:mpeg:dash:schema:mpd:2011";
    4692             : 
    4693          10 :         if (e != GF_OK) {
    4694           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Cannot reload MPD state %s: %s\n", ctx->state, gf_error_to_string(e) ));
    4695           0 :                 gf_mpd_del(ctx->mpd);
    4696           0 :                 ctx->mpd = NULL;
    4697           0 :                 return GF_URL_ERROR;
    4698             :         }
    4699             : 
    4700             :         //do a first pass to detect any potential changes in input config, if so consider the period over.
    4701          10 :         nb_p = gf_list_count(ctx->mpd->periods);
    4702          20 :         for (i=0; i<nb_p; i++) {
    4703             :                 u32 nb_done_in_period = 0;
    4704             :                 u32 nb_remain_in_period = 0;
    4705          10 :                 GF_MPD_Period *p = gf_list_get(ctx->mpd->periods, i);
    4706          10 :                 nb_as = gf_list_count(p->adaptation_sets);
    4707          25 :                 for (j=0; j<nb_as; j++) {
    4708          15 :                         GF_MPD_AdaptationSet *set = gf_list_get(p->adaptation_sets, j);
    4709          15 :                         nb_rep = gf_list_count(set->representations);
    4710          30 :                         for (k=0; k<nb_rep; k++) {
    4711             :                                 GF_DashStream *ds;
    4712             :                                 char *p_id;
    4713          15 :                                 GF_MPD_Representation *rep = gf_list_get(set->representations, k);
    4714          15 :                                 if (! rep->dasher_ctx) continue;
    4715             : 
    4716             :                                 //ensure we have the same settings - if not consider the dash stream has been resetup for a new period
    4717          15 :                                 ds = dasher_get_stream(ctx, rep->dasher_ctx->src_url, rep->dasher_ctx->source_pid, 0);
    4718          15 :                                 if (!ds) {
    4719           0 :                                         rep->dasher_ctx->done = 1;
    4720           0 :                                         nb_done_in_period++;
    4721           0 :                                         if (rep->dasher_ctx->last_dyn_period_id >= ctx->last_dyn_period_id)
    4722           0 :                                                 ctx->last_dyn_period_id = 1 + rep->dasher_ctx->last_dyn_period_id;
    4723           0 :                                         continue;
    4724             :                                 }
    4725             : 
    4726          15 :                                 if (rep->dasher_ctx->done) {
    4727           0 :                                         nb_done_in_period++;
    4728           0 :                                         ds->nb_repeat = rep->dasher_ctx->nb_repeat + 1;
    4729           0 :                                         if (rep->dasher_ctx->last_dyn_period_id > ctx->last_dyn_period_id)
    4730           0 :                                                 ctx->last_dyn_period_id = rep->dasher_ctx->last_dyn_period_id;
    4731           0 :                                         continue;
    4732             :                                 }
    4733             : 
    4734             :                                 p_id = DEFAULT_PERIOD_ID;
    4735          15 :                                 if (rep->dasher_ctx->period_id) p_id = rep->dasher_ctx->period_id;
    4736             : 
    4737          15 :                                 if (ds->period_id && p_id && !strcmp(ds->period_id, p_id)) {
    4738           0 :                                 } else if (!ds->period_id && !rep->dasher_ctx->period_id) {
    4739             :                                 } else {
    4740           0 :                                         rep->dasher_ctx->done = 1;
    4741           0 :                                         nb_done_in_period++;
    4742           0 :                                         continue;
    4743             :                                 }
    4744          15 :                                 if (ds->period_start.num * rep->dasher_ctx->period_start.den != rep->dasher_ctx->period_start.num * ds->period_start.den) {
    4745           0 :                                         rep->dasher_ctx->done = 1;
    4746           0 :                                         nb_done_in_period++;
    4747           0 :                                         continue;
    4748             :                                 }
    4749          15 :                                 if (ds->period_dur.num * rep->dasher_ctx->period_duration.den != rep->dasher_ctx->period_duration.num * ds->period_dur.den) {
    4750           0 :                                         rep->dasher_ctx->done = 1;
    4751           0 :                                         nb_done_in_period++;
    4752           0 :                                         continue;
    4753             :                                 }
    4754             :                                 //check we can reload muxed components - if not consider this source as removed
    4755          15 :                                 if (rep->dasher_ctx->mux_pids) {
    4756           2 :                                         e = dasher_reload_muxed_comp(ctx, ds, rep->dasher_ctx->mux_pids, GF_TRUE);
    4757           2 :                                         if (e) {
    4758           0 :                                                 rep->dasher_ctx->done = 1;
    4759           0 :                                                 nb_done_in_period++;
    4760           0 :                                                 continue;
    4761             :                                         }
    4762             :                                 }
    4763          15 :                                 nb_remain_in_period++;
    4764             :                         }
    4765             :                 }
    4766          10 :                 if (nb_remain_in_period) {
    4767             :                         assert(i+1==nb_p);
    4768             :                         last_period_active = GF_TRUE;
    4769             :                 }
    4770           0 :                 else if (nb_done_in_period && ctx->subdur  ) {
    4771             :                         //we are done but we loop the entire streams
    4772           0 :                         for (j=0; j<gf_list_count(ctx->pids); j++) {
    4773           0 :                                 GF_DashStream *ds = gf_list_get(ctx->pids, j);
    4774           0 :                                 ds->done = 0;
    4775           0 :                                 ds->segment_started = GF_FALSE;
    4776           0 :                                 ds->seg_done = GF_FALSE;
    4777           0 :                                 ds->cumulated_dur = 0;
    4778           0 :                                 ds->cumulated_subdur = 0;
    4779             :                         }
    4780             :                 }
    4781             :         }
    4782             : 
    4783          10 :         if (!last_period_active) return GF_OK;
    4784          10 :         ctx->current_period->period = gf_list_last(ctx->mpd->periods);
    4785          10 :         gf_list_reset(ctx->current_period->streams);
    4786          10 :         gf_list_del(ctx->next_period->streams);
    4787          10 :         ctx->next_period->streams = gf_list_clone(ctx->pids);
    4788             : 
    4789          10 :         if (ctx->current_period->period->duration) {
    4790             :                 //reset last period duration and cumulated dur of MPD
    4791           1 :                 if (ctx->mpd->media_presentation_duration>ctx->current_period->period->duration)
    4792           0 :                         ctx->mpd->media_presentation_duration -= ctx->current_period->period->duration;
    4793             :                 else
    4794           1 :                         ctx->mpd->media_presentation_duration = 0;
    4795           1 :                 ctx->current_period->period->duration = 0;
    4796             :         }
    4797             : 
    4798          10 :         nb_as = gf_list_count(ctx->current_period->period->adaptation_sets);
    4799          25 :         for (j=0; j<nb_as; j++) {
    4800             :                 GF_DashStream *set_ds = NULL;
    4801             :                 GF_List *multi_pids = NULL;
    4802             :                 Bool use_multi_pid_init = GF_FALSE;
    4803          15 :                 GF_MPD_AdaptationSet *set = gf_list_get(ctx->current_period->period->adaptation_sets, j);
    4804          15 :                 nb_rep = gf_list_count(set->representations);
    4805          30 :                 for (k=0; k<nb_rep; k++) {
    4806             :                         GF_DashStream *ds;
    4807          15 :                         GF_MPD_Representation *rep = gf_list_get(set->representations, k);
    4808          15 :                         if (! rep->dasher_ctx) continue;
    4809             : 
    4810          15 :                         ds = dasher_get_stream(ctx, rep->dasher_ctx->src_url, rep->dasher_ctx->source_pid, 0);
    4811          15 :                         if (!ds) continue;
    4812             : 
    4813             :                         //restore everything
    4814          15 :                         ds->done = rep->dasher_ctx->done;
    4815          15 :                         ds->seg_number = rep->dasher_ctx->seg_number;
    4816             : 
    4817          15 :                         if (ds->init_seg) gf_free(ds->init_seg);
    4818          15 :                         ds->init_seg = gf_strdup(rep->dasher_ctx->init_seg);
    4819             : 
    4820          15 :                         if (ds->seg_template) gf_free(ds->seg_template);
    4821          15 :                         ds->seg_template = gf_strdup(rep->dasher_ctx->template_seg);
    4822             : 
    4823          15 :                         if (ds->idx_template) gf_free(ds->idx_template);
    4824          15 :                         ds->idx_template = rep->dasher_ctx->template_idx ? gf_strdup(rep->dasher_ctx->template_idx) : NULL;
    4825             : 
    4826          15 :                         if (rep->dasher_ctx->period_id) {
    4827           0 :                                 if (ds->period_id) gf_free(ds->period_id);
    4828           0 :                                 ds->period_id = gf_strdup(rep->dasher_ctx->period_id);
    4829             :                         }
    4830             : 
    4831          15 :                         ds->period_start = rep->dasher_ctx->period_start;
    4832          15 :                         if (!ds->period_start.den) {
    4833          15 :                                 ds->period_start.num = 0;
    4834          15 :                                 ds->period_start.den = 1;
    4835             :                         }
    4836          15 :                         ds->period_dur = rep->dasher_ctx->period_duration;
    4837          15 :                         if (!ds->period_dur.den) {
    4838          15 :                                 ds->period_dur.num = 0;
    4839          15 :                                 ds->period_dur.den = 1;
    4840             :                         }
    4841          15 :                         ds->pid_id = rep->dasher_ctx->pid_id;
    4842          15 :                         ds->dep_pid_id = rep->dasher_ctx->dep_pid_id;
    4843          15 :                         ds->seek_to_pck = rep->dasher_ctx->last_pck_idx;
    4844          15 :                         ds->dash_dur = rep->dasher_ctx->dash_dur;
    4845          15 :                         if (!ds->dash_dur.den) {
    4846           0 :                                 ds->dash_dur.num = 0;
    4847           0 :                                 ds->dash_dur.den = 1;
    4848             :                         }
    4849          15 :                         ds->next_seg_start = rep->dasher_ctx->next_seg_start;
    4850          15 :                         ds->adjusted_next_seg_start = ds->next_seg_start;
    4851          15 :                         ds->first_cts = rep->dasher_ctx->first_cts;
    4852          15 :                         ds->first_dts = rep->dasher_ctx->first_dts;
    4853          15 :                         ds->ts_offset = rep->dasher_ctx->ts_offset;
    4854          15 :                         ds->est_next_dts = rep->dasher_ctx->est_next_dts;
    4855          15 :                         ds->mpd_timescale = rep->dasher_ctx->mpd_timescale;
    4856          15 :                         ds->cumulated_dur = (u64) (rep->dasher_ctx->cumulated_dur * ds->timescale);
    4857          15 :                         ds->cumulated_subdur = rep->dasher_ctx->cumulated_subdur;
    4858          15 :                         ds->rep_init = GF_TRUE;
    4859          15 :                         ds->subdur_done = rep->dasher_ctx->subdur_forced ? GF_TRUE : GF_FALSE;
    4860          15 :                         ds->subdur_forced_use_period_dur = 0;
    4861          15 :                         ds->nb_pck = 0;
    4862          15 :                         if (!ctx->subdur) {
    4863           0 :                                 ds->nb_pck = ds->seek_to_pck;
    4864           0 :                                 ds->seek_to_pck = 0;
    4865             :                         }
    4866          15 :                         ds->nb_segments_purged = rep->dasher_ctx->segs_purged;
    4867          15 :                         ds->dur_purged = rep->dasher_ctx->dur_purged;
    4868          15 :                         ds->moof_sn = rep->dasher_ctx->moof_sn;
    4869          15 :                         ds->moof_sn_inc = rep->dasher_ctx->moof_sn_inc;
    4870          15 :                         ctx->last_dyn_period_id = rep->dasher_ctx->last_dyn_period_id;
    4871             : 
    4872          15 :                         if (ctx->store_seg_states && !ds->pending_segment_states)
    4873           3 :                                 ds->pending_segment_states = gf_list_new();
    4874             : 
    4875          15 :                         if (rep->segment_list && !ds->pending_segment_urls)
    4876           0 :                                 ds->pending_segment_urls = gf_list_new();
    4877             : 
    4878          15 :                         ds->owns_set = rep->dasher_ctx->owns_set;
    4879          15 :                         if (ds->owns_set) set_ds = ds;
    4880             : 
    4881          15 :                         if (rep->dasher_ctx->done) {
    4882           0 :                                 ds->done = 1;
    4883           0 :                                 if (ds->rep) gf_mpd_representation_free(ds->rep);
    4884           0 :                                 ds->rep = NULL;
    4885           0 :                                 continue;
    4886             :                         }
    4887             : 
    4888          15 :                         ds->nb_comp = 1;
    4889             : 
    4890          15 :                         if (ds->rep) gf_mpd_representation_free(ds->rep);
    4891          15 :                         ds->rep = rep;
    4892          15 :                         ds->set = set;
    4893          15 :                         rep->playback.udta = ds;
    4894          15 :                         if (ds->owns_set)
    4895          15 :                                 set->udta = ds;
    4896          15 :                         if (rep->dasher_ctx->multi_pids)
    4897             :                                 use_multi_pid_init = GF_TRUE;
    4898             : 
    4899          15 :                         ds->period = ctx->current_period;
    4900          15 :                         if ((ds->codec_id==GF_CODECID_MHAS) || (ds->codec_id==GF_CODECID_MPHA)) {
    4901           0 :                                 const GF_PropertyValue *prop = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_MHA_COMPATIBLE_PROFILES);
    4902           0 :                                 if (prop) {
    4903           0 :                                         ds->set->nb_alt_mha_profiles = prop->value.uint_list.nb_items;
    4904           0 :                                         ds->set->alt_mha_profiles = prop->value.uint_list.vals;
    4905             :                                 }
    4906             :                         }
    4907             : 
    4908             : 
    4909          15 :                         gf_list_del_item(ctx->next_period->streams, ds);
    4910             : 
    4911             :                         //non-muxed component or main comp of muxed goes first in the list
    4912          15 :                         if (ds->nb_comp>1) {
    4913           0 :                                 gf_list_insert(ctx->current_period->streams, ds, 0);
    4914             :                         } else {
    4915          15 :                                 gf_list_add(ctx->current_period->streams, ds);
    4916             :                         }
    4917             : 
    4918          15 :                         if (rep->dasher_ctx->mux_pids) {
    4919           2 :                                 e = dasher_reload_muxed_comp(ctx, ds, rep->dasher_ctx->mux_pids, GF_FALSE);
    4920           2 :                                 if (e) return e;
    4921             :                         }
    4922             :                 }
    4923             :                 assert(set_ds);
    4924          15 :                 set_ds->nb_rep = gf_list_count(set->representations);
    4925             : 
    4926             :                 //if multi PID init, gather pids
    4927          15 :                 if (use_multi_pid_init) {
    4928           0 :                         multi_pids = gf_list_new();
    4929           0 :                         for (i=0; i<nb_rep; i++) {
    4930           0 :                                 GF_MPD_Representation *rep = gf_list_get(set->representations, i);
    4931           0 :                                 GF_DashStream *ds = rep->playback.udta;
    4932           0 :                                 if (ds->owns_set) ds->multi_pids = multi_pids;
    4933           0 :                                 gf_list_add(multi_pids, ds->ipid);
    4934             :                         }
    4935             :                 }
    4936          15 :                 count = gf_list_count(ctx->current_period->streams);
    4937          30 :                 for (i=0; i<nb_rep; i++) {
    4938          15 :                         GF_MPD_Representation *rep = gf_list_get(set->representations, i);
    4939          15 :                         GF_DashStream *ds = rep->playback.udta;
    4940          15 :                         if (!ds || ds->done) continue;
    4941             :                         //happens when reloading context without closing the filter
    4942          15 :                         if (ds->dst_filter || ds->opid) continue;
    4943             : 
    4944             :                         //open destination, trashing init
    4945             :                         assert(!ds->muxed_base);
    4946           3 :                         dasher_open_destination(filter, ctx, rep, ds->init_seg, GF_TRUE);
    4947             : 
    4948           3 :                         dasher_open_pid(filter, ctx, ds, multi_pids, GF_TRUE);
    4949             : 
    4950           8 :                         for (j=0; j<count; j++) {
    4951           5 :                                 GF_DashStream *a_ds = gf_list_get(ctx->current_period->streams, j);
    4952           5 :                                 if (a_ds->muxed_base != ds) continue;
    4953             : 
    4954           2 :                                 dasher_open_pid(filter, ctx, a_ds, multi_pids, GF_TRUE);
    4955             :                         }
    4956             :                 }
    4957             :         }
    4958             : 
    4959             :         return GF_OK;
    4960             : }
    4961             : 
    4962         261 : static void dasher_udpate_periods_and_manifest(GF_Filter *filter, GF_DasherCtx *ctx)
    4963             : {
    4964         261 :         if (!ctx->subdur_done) {
    4965         241 :                 ctx->last_dyn_period_id++;
    4966         241 :                 ctx->next_pid_id_in_period = 0;
    4967             :         }
    4968             :         //update duration
    4969         261 :         dasher_update_period_duration(ctx, GF_TRUE);
    4970             : 
    4971         261 :         if (ctx->state)
    4972          32 :                 dasher_context_update_period_end(ctx);
    4973             : 
    4974             :         //we have a MPD ready, flush it
    4975         261 :         if (ctx->mpd)
    4976         261 :                 dasher_send_manifest(filter, ctx, GF_FALSE);
    4977         261 : }
    4978             : 
    4979             : typedef struct
    4980             : {
    4981             :         GF_Fraction64 period_start;
    4982             :         const char *period_id;
    4983             : } PeriodInfo;
    4984             : 
    4985           0 : static u32 dasher_period_count(GF_List *streams_in /*GF_DashStream*/)
    4986             : {
    4987             :         u32 nb_periods, i, j;
    4988             :         PeriodInfo *info;
    4989           0 :         GF_List *pinfos = gf_list_new();
    4990             : 
    4991           0 :         for (i=0; i < gf_list_count(streams_in); i++) {
    4992             :                 Bool same_period = GF_FALSE;
    4993           0 :                 GF_DashStream *ds = gf_list_get(streams_in, i);
    4994             :                 //check if we already have a period info with same ID or same start time
    4995           0 :                 nb_periods = gf_list_count(pinfos);
    4996           0 :                 for (j=0; j < nb_periods; j++) {
    4997           0 :                         info = gf_list_get(pinfos, j);
    4998           0 :                         if (info->period_start.num * ds->period_start.den == ds->period_start.num * info->period_start.den) {
    4999             :                                 same_period = GF_TRUE;
    5000             :                                 break;
    5001             :                         }
    5002           0 :                         if (info->period_id && ds->period_id && !strcmp(info->period_id, ds->period_id)) {
    5003             :                                 same_period = GF_TRUE;
    5004             :                                 break;
    5005             :                         }
    5006             :                 }
    5007             :                 //nope, register it
    5008           0 :                 if (!same_period) {
    5009           0 :                         GF_SAFEALLOC(info, PeriodInfo);
    5010           0 :                         if (info) {
    5011           0 :                                 info->period_start = ds->period_start;
    5012           0 :                                 info->period_id = ds->period_id;
    5013           0 :                                 gf_list_add(pinfos, info);
    5014             :                         }
    5015             :                 }
    5016             :         }
    5017           0 :         nb_periods = gf_list_count(pinfos);
    5018             :         while (1) {
    5019           0 :                 info = gf_list_pop_back(pinfos);
    5020           0 :                 if (!info) break;
    5021           0 :                 gf_free(info);
    5022             :         }
    5023           0 :         gf_list_del(pinfos);
    5024             : 
    5025           0 :         return nb_periods;
    5026             : }
    5027             : 
    5028          25 : static void dasher_init_utc(GF_Filter *filter, GF_DasherCtx *ctx)
    5029             : {
    5030             :         const char *cache_name;
    5031             :         u32 size;
    5032             :         u8 *data;
    5033             :         u64 remote_utc;
    5034             :         GF_Err e;
    5035             :         GF_DownloadSession *sess;
    5036             :         GF_DownloadManager *dm;
    5037             :         char *url;
    5038             :         DasherUTCTimingType def_type = DASHER_UTCREF_NONE;
    5039             : 
    5040          25 :         ctx->utc_initialized = GF_TRUE;
    5041          25 :         ctx->utc_timing_type = DASHER_UTCREF_NONE;
    5042          25 :         if (!ctx->utcs) {
    5043          25 :                 return;
    5044             :         }
    5045             :         url = ctx->utcs;
    5046           0 :         if (!strncmp(url, "xsd@", 4)) {
    5047             :                 def_type = DASHER_UTCREF_XSDATE;
    5048           0 :                 url += 4;
    5049             :         }
    5050             : 
    5051           0 :         dm  = gf_filter_get_download_manager(filter);
    5052           0 :         if (!dm) {
    5053           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Failed to get download manager, cannot sync to remote UTC clock\n"));
    5054             :                 return;
    5055             :         }
    5056           0 :         if (!strcmp(ctx->utcs, "inband")) {
    5057           0 :                 ctx->utc_timing_type = DASHER_UTCREF_INBAND;
    5058           0 :                 return;
    5059             :         }
    5060             : 
    5061           0 :         sess = gf_dm_sess_new(dm, url, GF_NETIO_SESSION_MEMORY_CACHE|GF_NETIO_SESSION_NOT_THREADED, NULL, NULL, &e);
    5062           0 :         if (e) {
    5063           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Failed to create session for remote UTC source %s: %s - local clock will be used instead\n", url, gf_error_to_string(e) ));
    5064             :                 return;
    5065             :         }
    5066           0 :         while (1) {
    5067             :                 GF_NetIOStatus status;
    5068           0 :                 e = gf_dm_sess_process(sess);
    5069           0 :                 if (e) break;
    5070           0 :                 gf_dm_sess_get_stats(sess, NULL, NULL, NULL, NULL, NULL, &status);
    5071           0 :                 if (status>=GF_NETIO_DATA_TRANSFERED) break;
    5072             :         }
    5073           0 :         if (e<0) {
    5074           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Failed to fetch remote UTC source %s: %s\n", url, gf_error_to_string(e) ));
    5075           0 :                 gf_dm_sess_del(sess);
    5076           0 :                 return;
    5077             :         }
    5078           0 :         cache_name = gf_dm_sess_get_cache_name(sess);
    5079           0 :         gf_blob_get(cache_name, &data, &size, NULL);
    5080           0 :         if (data) {
    5081             :                 //xsDate or isoDate - we always signal using iso
    5082           0 :                 if (strchr(data, 'T')) {
    5083           0 :                         remote_utc = gf_net_parse_date(data);
    5084           0 :                         if (remote_utc)
    5085           0 :                                 ctx->utc_timing_type = def_type ? def_type : DASHER_UTCREF_ISO;
    5086             :                 }
    5087             :                 //ntp
    5088           0 :                 else if (sscanf(data, LLU, &remote_utc) == 1) {
    5089             :                         //ntp value not counted since 1900, assume format is seconds till 1 jan 1970
    5090           0 :                         if (remote_utc<=GF_NTP_SEC_1900_TO_1970) {
    5091           0 :                                 remote_utc = remote_utc*1000;
    5092             :                         } else {
    5093           0 :                                 remote_utc = gf_net_ntp_to_utc(remote_utc);
    5094             :                         }
    5095           0 :                         if (remote_utc)
    5096           0 :                                 ctx->utc_timing_type = DASHER_UTCREF_NTP;
    5097             :                 }
    5098             :         }
    5099           0 :         gf_blob_release(cache_name);
    5100             : 
    5101             :         //not match, try http date
    5102           0 :         if (!ctx->utc_timing_type) {
    5103           0 :                 const char *hdr = gf_dm_sess_get_header(sess, "Date");
    5104           0 :                 if (hdr) {
    5105             :                         //http-head
    5106           0 :                         remote_utc = gf_net_parse_date(hdr);
    5107           0 :                         if (remote_utc)
    5108           0 :                                 ctx->utc_timing_type = DASHER_UTCREF_HTTP_HEAD;
    5109             :                 }
    5110             :         }
    5111             : 
    5112           0 :         if (!ctx->utc_timing_type) {
    5113           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Failed to parse response %s from remote UTC source %s\n", data, url ));
    5114             :         } else {
    5115           0 :                 ctx->utc_diff = (s32) ( (s64) gf_net_get_utc() - (s64) remote_utc );
    5116           0 :                 if (ABS(ctx->utc_diff) > 3600000) {
    5117           0 :                         GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Diff between local clock and remote %s is %d, way too large! Assuming 0 ms UTC diff\n", url, ctx->utc_diff));
    5118           0 :                         ctx->utc_diff = 0;
    5119             :                 } else {
    5120           0 :                         GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[Dasher] Synchronized clock to remote %s - UTC diff (local - remote) %d ms\n", url, ctx->utc_diff));
    5121             :                 }
    5122             : 
    5123           0 :                 if (!gf_list_count(ctx->mpd->utc_timings) ) {
    5124             :                         Bool dashif_ok = GF_FALSE;
    5125             :                         GF_MPD_Descriptor *utc_t;
    5126           0 :                         GF_SAFEALLOC(utc_t, GF_MPD_Descriptor);
    5127           0 :                         utc_t->value = gf_strdup(url);
    5128           0 :                         switch (ctx->utc_timing_type) {
    5129           0 :                         case DASHER_UTCREF_HTTP_HEAD:
    5130           0 :                                 utc_t->scheme_id_uri = gf_strdup("urn:mpeg:dash:utc:http-head:2014");
    5131             :                                 break;
    5132           0 :                         case DASHER_UTCREF_XSDATE:
    5133           0 :                                 utc_t->scheme_id_uri = gf_strdup("urn:mpeg:dash:utc:http-xsdate:2014");
    5134             :                                 dashif_ok = GF_TRUE;
    5135             :                                 break;
    5136           0 :                         case DASHER_UTCREF_ISO:
    5137           0 :                                 utc_t->scheme_id_uri = gf_strdup("urn:mpeg:dash:utc:http-iso:2014");
    5138             :                                 dashif_ok = GF_TRUE;
    5139             :                                 break;
    5140           0 :                         case DASHER_UTCREF_NTP:
    5141           0 :                                 utc_t->scheme_id_uri = gf_strdup("urn:mpeg:dash:utc:http-ntp:2014");
    5142             :                                 dashif_ok = GF_TRUE;
    5143             :                                 break;
    5144           0 :                         case DASHER_UTCREF_INBAND:
    5145           0 :                                 utc_t->scheme_id_uri = gf_strdup("urn:mpeg:dash:utc:direct:2014");
    5146             :                                 break;
    5147             :                         default:
    5148             :                                 break;
    5149             :                         }
    5150           0 :                         if (!dashif_ok && (ctx->profile==GF_DASH_PROFILE_DASHIF_LL)) {
    5151           0 :                                 GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] UTC reference %s allowed in DASH-IF Low Latency profile\n\tswitching to regular live profile\n", utc_t->scheme_id_uri));
    5152           0 :                                 ctx->profile = GF_DASH_PROFILE_LIVE;
    5153             :                         }
    5154           0 :                         if (!ctx->mpd->utc_timings)
    5155           0 :                                 ctx->mpd->utc_timings = gf_list_new();
    5156           0 :                         gf_list_add(ctx->mpd->utc_timings, utc_t);
    5157             :                 }
    5158             :         }
    5159           0 :         gf_dm_sess_del(sess);
    5160             : }
    5161             : 
    5162             : 
    5163         511 : static GF_Err dasher_switch_period(GF_Filter *filter, GF_DasherCtx *ctx)
    5164             : {
    5165             :         u32 i, count, nb_done;
    5166             :         char *period_id;
    5167             :         const char *remote_xlink = NULL;
    5168             :         const char *period_xlink = NULL;
    5169             :         u64 remote_dur = 0;
    5170             :         GF_DasherPeriod *p;
    5171             :         GF_Fraction64 period_start, next_period_start;
    5172             :         GF_DashStream *first_in_period=NULL;
    5173         511 :         p = ctx->current_period;
    5174             : 
    5175         511 :         if (!ctx->out_path) {
    5176           4 :                 dasher_check_outpath(ctx);
    5177             :         }
    5178         511 :         if (ctx->current_period->period) {
    5179         260 :                 if (ctx->dyn_rate)
    5180          44 :                         dasher_update_dyn_bitrates(ctx);
    5181             : 
    5182         260 :                 dasher_udpate_periods_and_manifest(filter, ctx);
    5183             :         }
    5184             : 
    5185         511 :         if (ctx->subdur_done || (ctx->current_period->period && (ctx->dmode == GF_MPD_TYPE_DYNAMIC_LAST)) )
    5186             :                 return GF_EOS;
    5187             : 
    5188         484 :         if (ctx->current_period->period) {
    5189         233 :                 GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[Dasher] End of Period %s\n", ctx->current_period->period->ID ? ctx->current_period->period->ID : ""));
    5190             :         }
    5191         484 :         ctx->is_period_restore = GF_FALSE;
    5192         484 :         ctx->is_empty_period = GF_FALSE;
    5193             : 
    5194             :         //safety check at period switch, probe each first packet in case we have a reconfigure pending
    5195         484 :         count = gf_list_count(ctx->pids);
    5196        1200 :         for (i=0; i<count;i++) {
    5197         716 :                 GF_DashStream *ds = gf_list_get(ctx->pids, i);
    5198         716 :                 gf_filter_pid_get_packet(ds->ipid);
    5199             :         }
    5200             : 
    5201             :         //reset - don't destroy, it is in the MPD
    5202         484 :         ctx->current_period->period = NULL;
    5203             :         //switch
    5204         484 :         ctx->current_period = ctx->next_period;
    5205         484 :         ctx->next_period = p;
    5206         484 :         ctx->on_demand_done = GF_FALSE;
    5207             : 
    5208             :         //reset input pids and detach output pids
    5209         484 :         count = gf_list_count(ctx->current_period->streams);
    5210         858 :         for (i=0; i<count;i++) {
    5211         374 :                 GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
    5212         374 :                 if (ds->opid) {
    5213           9 :                         gf_filter_pid_remove(ds->opid);
    5214           9 :                         ds->opid = NULL;
    5215             :                 }
    5216         374 :                 dasher_reset_stream(filter, ds, GF_FALSE);
    5217         374 :                 if (ds->reschedule) {
    5218           6 :                         ds->reschedule = GF_FALSE;
    5219           6 :                         ds->done = 0;
    5220             :                 }
    5221             :         }
    5222             : 
    5223             :         //figure out next period
    5224         484 :         count = gf_list_count(ctx->current_period->streams);
    5225         484 :         ctx->period_idx = 0;
    5226             :         period_start.num = -1;
    5227             :         period_start.den = 1;
    5228         858 :         for (i=0; i<count; i++) {
    5229         374 :                 GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
    5230             : 
    5231         374 :                 if (ds->done) continue;
    5232         368 :                 if (ds->period_start.num < 0) {
    5233           6 :                         s32 pstart = (s32) -ds->period_start.num;
    5234           6 :                         if (!ctx->period_idx || (pstart < ctx->period_idx)) ctx->period_idx = pstart;
    5235             :                 } else {
    5236         362 :                         if ((period_start.num<0) || (ds->period_start.num * period_start.den < period_start.num * ds->period_start.den)) {
    5237         247 :                                 period_start = ds->period_start;
    5238             :                                 assert(ds->period_start.den);
    5239             :                         }
    5240             :                 }
    5241             :         }
    5242             : 
    5243         484 :         if (period_start.num >= 0)
    5244         247 :                 ctx->period_idx = 0;
    5245             : 
    5246         484 :         if (ctx->first_context_load) {
    5247          21 :                 GF_Err e = dasher_reload_context(filter, ctx);
    5248          21 :                 if (e) {
    5249           0 :                         ctx->setup_failure = e;
    5250           0 :                         return e;
    5251             :                 }
    5252          21 :                 if (ctx->current_period->period) ctx->is_period_restore = GF_TRUE;
    5253             : 
    5254          21 :                 if (ctx->dmode==GF_DASH_DYNAMIC_LAST) {
    5255           1 :                         dasher_udpate_periods_and_manifest(filter, ctx);
    5256           1 :                         count = gf_list_count(ctx->pids);
    5257           3 :                         for (i=0; i<count; i++) {
    5258           2 :                                 GF_DashStream *ds = gf_list_get(ctx->pids, i);
    5259           2 :                                 gf_filter_pid_set_discard(ds->ipid, GF_TRUE);
    5260             :                         }
    5261             :                         return GF_EOS;
    5262             :                 }
    5263             :         }
    5264             : 
    5265             :         //filter out PIDs not for this period
    5266         483 :         count = gf_list_count(ctx->current_period->streams);
    5267             :         period_id = NULL;
    5268         867 :         for (i=0; i<count; i++) {
    5269             :                 Bool in_period=GF_TRUE;
    5270         384 :                 GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
    5271             : 
    5272         384 :                 if (ds->done) {
    5273             :                         in_period=GF_FALSE;
    5274         378 :                 } else if (!period_id) {
    5275         257 :                         period_id = ds->period_id;
    5276             :                         first_in_period = ds;
    5277         121 :                 } else if (strcmp(period_id, ds->period_id)) {
    5278             :                         in_period = GF_FALSE;
    5279             :                 }
    5280         384 :                 if (in_period) {
    5281         374 :                         if ((period_start.num>=0) && (ds->period_start.num * period_start.den != period_start.num * ds->period_start.den))
    5282             :                                 in_period = GF_FALSE;
    5283         374 :                         else if ((ctx->period_idx>0) && ((s32) -ds->period_start.num != ctx->period_idx))
    5284             :                                 in_period = GF_FALSE;
    5285             : 
    5286         374 :                         if (!in_period && (first_in_period == ds))
    5287             :                                 period_id = NULL;
    5288             :                 }
    5289             : 
    5290             :                 //if not in period, move to next period
    5291         384 :                 if (!in_period) {
    5292          11 :                         gf_list_rem(ctx->current_period->streams, i);
    5293          11 :                         i--;
    5294          11 :                         count--;
    5295          11 :                         ds->period = NULL;
    5296          11 :                         gf_list_add(ctx->next_period->streams, ds);
    5297          11 :                         continue;
    5298             :                 }
    5299         373 :                 if (ds->stream_type == GF_STREAM_FILE) {
    5300           1 :                         if (ds->xlink) remote_xlink = ds->xlink;
    5301           0 :                         else ctx->is_empty_period = GF_TRUE;
    5302             :                         remote_dur = 0;
    5303           1 :                         if (ds->period_dur.den)
    5304           1 :                                 remote_dur = (u64) (ds->period_dur.num * 1000) / ds->period_dur.den;
    5305         372 :                 } else if (!ctx->is_period_restore) {
    5306         357 :                         if (ds->xlink)
    5307             :                                 period_xlink = ds->xlink;
    5308             : 
    5309         357 :                         if (ctx->post_play_events) {
    5310             :                                 GF_FilterEvent evt;
    5311             : 
    5312           6 :                                 GF_FEVT_INIT(evt, GF_FEVT_STOP, ds->ipid);
    5313           6 :                                 gf_filter_pid_send_event(ds->ipid, &evt);
    5314             : 
    5315           6 :                                 gf_filter_pid_set_discard(ds->ipid, GF_FALSE);
    5316             : 
    5317           6 :                                 dasher_send_encode_hints(ctx, ds);
    5318             : 
    5319           6 :                                 GF_FEVT_INIT(evt, GF_FEVT_PLAY, ds->ipid);
    5320           6 :                                 evt.play.speed = 1.0;
    5321           6 :                                 gf_filter_pid_send_event(ds->ipid, &evt);
    5322             :                         }
    5323             :                 }
    5324             :         }
    5325         483 :         ctx->post_play_events = GF_FALSE;
    5326             : 
    5327         483 :         count = gf_list_count(ctx->current_period->streams);
    5328         483 :         if (!count) {
    5329         227 :                 count = gf_list_count(ctx->next_period->streams);
    5330             :                 nb_done = 0;
    5331         558 :                 for (i=0; i<count; i++)       {
    5332         331 :                         GF_DashStream *ds = gf_list_get(ctx->next_period->streams, i);
    5333         331 :                         if (ds->done) nb_done++;
    5334             :                 }
    5335         227 :                 if (nb_done == count) {
    5336         227 :                         GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[Dasher] End of MPD (no more active streams)\n"));
    5337         227 :                         ctx->on_demand_done = GF_TRUE;
    5338         227 :                         return GF_EOS;
    5339             :                 }
    5340             :         }
    5341             : 
    5342             :         //we need a new period unless created during reload, create it
    5343         256 :         if (!ctx->is_period_restore) {
    5344         247 :                 ctx->current_period->period = gf_mpd_period_new();
    5345         247 :                 if (!ctx->mpd) dasher_setup_mpd(ctx);
    5346         247 :                 gf_list_add(ctx->mpd->periods, ctx->current_period->period);
    5347             :         }
    5348             : 
    5349             : 
    5350         256 :         if (remote_xlink) {
    5351           1 :                 ctx->current_period->period->xlink_href = gf_strdup(remote_xlink);
    5352           1 :                 ctx->current_period->period->duration = remote_dur;
    5353             :         }
    5354         255 :         else if (period_xlink) {
    5355           0 :                 ctx->current_period->period->xlink_href = gf_strdup(period_xlink);
    5356             :         }
    5357             : 
    5358             : 
    5359             :         assert(period_id);
    5360             : 
    5361             :         next_period_start.num = -1;
    5362             :         next_period_start.den = 1;
    5363         256 :         if (period_start.num >= 0) {
    5364         246 :                 ctx->current_period->period->start = (u64)(period_start.num*1000 / period_start.den);
    5365             :                 //check next period start
    5366         246 :                 count = gf_list_count(ctx->next_period->streams);
    5367         256 :                 for (i=0; i<count; i++)       {
    5368          10 :                         GF_DashStream *ds = gf_list_get(ctx->next_period->streams, i);
    5369          10 :                         if (ds->done) continue;
    5370           8 :                         if (ds->period_start.num * period_start.den < period_start.num * ds->period_start.den) continue;
    5371           8 :                         if ((next_period_start.num<0) || (next_period_start.num * ds->period_start.den > ds->period_start.num * next_period_start.den)) {
    5372             :                                 next_period_start = ds->period_start;
    5373             :                         }
    5374             :                 }
    5375             :                 //check current period dur
    5376         246 :                 count = gf_list_count(ctx->current_period->streams);
    5377         604 :                 for (i=0; i<count; i++)       {
    5378             :                         GF_Fraction64 dur;
    5379         358 :                         GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
    5380         358 :                         if (!ds->period_dur.den) continue;
    5381             :                         dur = period_start;
    5382             :                         if (ds->period_dur.den) {
    5383         358 :                                 if (dur.den != ds->period_dur.den)
    5384           0 :                                         dur.num += ds->period_dur.num * dur.den / ds->period_dur.den;
    5385             :                                 else
    5386         358 :                                         dur.num += ds->period_dur.num;
    5387             :                         }
    5388             :                         
    5389         358 :                         if ((next_period_start.num < 0) || (next_period_start.num * dur.den > dur.num * next_period_start.den))
    5390             :                                 next_period_start = dur;
    5391             :                 }
    5392         246 :                 if (next_period_start.num > 0) {
    5393           0 :                         u64 next = next_period_start.num;
    5394           0 :                         if (next_period_start.den != period_start.den) {
    5395           0 :                                 next *= period_start.den;
    5396           0 :                                 next /= next_period_start.den;
    5397             :                         }
    5398           0 :                         ctx->current_period->period->duration = (u32) ( (next - period_start.num) * 1000 / period_start.den);
    5399             :                 }
    5400             :         }
    5401             : 
    5402             :         //assign period ID if none specified
    5403         256 :         if (strcmp(period_id, DEFAULT_PERIOD_ID))
    5404           5 :                 ctx->current_period->period->ID = gf_strdup(period_id);
    5405             :         //assign ID if dynamic - if dash_ctx also assign ID since we could have moved from dynamic to static
    5406         251 :         else if (!ctx->current_period->period->ID && ((ctx->dmode != GF_MPD_TYPE_STATIC) || ctx->state) ) {
    5407             :                 char szPName[50];
    5408          30 :                 sprintf(szPName, "DID%d", ctx->last_dyn_period_id + 1);
    5409          30 :                 ctx->current_period->period->ID = gf_strdup(szPName);
    5410             :         }
    5411             : 
    5412             :         //check all streams are ready
    5413         256 :         ctx->period_not_ready = GF_FALSE;
    5414         256 :         count = gf_list_count(ctx->current_period->streams);
    5415         629 :         for (i=0; i<count; i++) {
    5416         373 :                 GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
    5417             :                 //assign force_rep_end
    5418         373 :                 if (next_period_start.num > 0) {
    5419           0 :                         u64 next = next_period_start.num;
    5420           0 :                         if (next_period_start.den != period_start.den) {
    5421           0 :                                 next *= period_start.den;
    5422           0 :                                 next /= next_period_start.den;
    5423             :                         }
    5424             : 
    5425           0 :                         ds->force_rep_end = (u64) ((next - period_start.num) * ds->timescale / period_start.den);
    5426             :                 }
    5427         373 :                 if (ds->dcd_not_ready) {
    5428           9 :                         ctx->period_not_ready = GF_TRUE;
    5429             :                 }
    5430             :         }
    5431             :         //not all streams are ready, cannot setup period yet
    5432         256 :         if (ctx->period_not_ready)
    5433             :                 return GF_OK;
    5434             : 
    5435         251 :         return dasher_setup_period(filter, ctx, NULL);
    5436             : }
    5437             : 
    5438         256 : static GF_Err dasher_setup_period(GF_Filter *filter, GF_DasherCtx *ctx, GF_DashStream *inject_ds)
    5439             : {
    5440             :         u32 i, count, j, nb_sets;
    5441             :         Bool has_muxed_bases=GF_FALSE;
    5442             :         const char *remote_xlink = NULL;
    5443             :         Bool has_as_id = GF_FALSE;
    5444             :         Bool has_deps = GF_FALSE;
    5445             :         const GF_PropertyValue *prop;
    5446             :         GF_Fraction64 min_dur, min_adur, max_adur;
    5447             :         u32 srd_rep_idx;
    5448             : 
    5449         256 :         ctx->dyn_rate = GF_FALSE;
    5450         256 :         ctx->use_cues = GF_FALSE;
    5451             :         min_dur.num = min_adur.num = max_adur.num = 0;
    5452             :         min_dur.den = min_adur.den = max_adur.den = 1;
    5453         256 :         srd_rep_idx = 2; //2 for compat with old arch
    5454             : 
    5455         256 :         count = gf_list_count(ctx->current_period->streams);
    5456             :         //setup representations
    5457         629 :         for (i=0; i<count; i++) {
    5458         373 :                 GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
    5459         373 :                 if (inject_ds && (ds != inject_ds))
    5460           0 :                         continue;
    5461             : 
    5462         373 :                 if (ds->stream_type == GF_STREAM_FILE) {
    5463           1 :                         if (ds->xlink) remote_xlink = ds->xlink;
    5464         372 :                 } else if (!ctx->is_period_restore) {
    5465             :                         //setup representation - the representation is created independently from the period
    5466         357 :                         dasher_setup_rep(ctx, ds, &srd_rep_idx);
    5467             :                 }
    5468             :         }
    5469             : 
    5470             :         //setup representation dependency / components (muxed)
    5471         373 :         for (i=0; i<count; i++) {
    5472             :                 Bool remove = GF_FALSE;
    5473             :                 GF_DashStream *ds_video=NULL;
    5474         373 :                 GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
    5475         373 :                 if (inject_ds && (ds != inject_ds))
    5476           0 :                         continue;
    5477             : 
    5478         373 :                 ds->period = ctx->current_period;
    5479         373 :                 ds->last_period = ds->period->period;
    5480             : 
    5481         373 :                 if (ds->dyn_bitrate) ctx->dyn_rate = GF_TRUE;
    5482         373 :                 if (ds->inband_cues || ds->cues)
    5483          37 :                         ctx->use_cues = GF_TRUE;
    5484             : 
    5485         373 :                 if (ctx->loop) {
    5486          28 :                         prop = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_DURATION);
    5487          28 :                         if (prop && prop->value.lfrac.den) {
    5488             :                                 GF_Fraction64 d;
    5489          28 :                                 d.num = prop->value.lfrac.num;
    5490             :                                 d.den = prop->value.lfrac.den;
    5491          28 :                                 if (ds->clamped_dur.num && (ds->clamped_dur.num * d.den < d.num * ds->clamped_dur.den)) {
    5492             :                                         d = ds->clamped_dur;
    5493             :                                 }
    5494             : 
    5495          28 :                                 if (ds->stream_type == GF_STREAM_AUDIO) {
    5496          14 :                                         if (d.num * max_adur.den > max_adur.num * d.den) max_adur = d;
    5497          14 :                                         if (!min_adur.num || (d.num * min_adur.den < min_adur.num * d.den)) min_adur = d;
    5498             :                                 } else {
    5499          14 :                                         if (!min_dur.num || (d.num * min_dur.den < min_dur.num * d.den)) min_dur = d;
    5500             :                                 }
    5501             :                         }
    5502             :                 }
    5503             : 
    5504         373 :                 if (ds->stream_type == GF_STREAM_FILE) {
    5505             :                         remove = GF_TRUE;
    5506         372 :                 } else if (remote_xlink) {
    5507           0 :                         GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] period uses xlink but other media source %s, ignoring source\n", ds->src_url));
    5508             :                         remove = GF_TRUE;
    5509         372 :                 } else if (ctx->is_empty_period) {
    5510           0 :                         GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] empty period defined but other media source %s, ignoring source\n", ds->src_url));
    5511             :                         remove = GF_TRUE;
    5512             :                 }
    5513             : 
    5514             :                 if (remove) {
    5515           1 :                         ds->done = 1;
    5516           1 :                         ds->period = NULL;
    5517           1 :                         gf_list_rem(ctx->current_period->streams, i);
    5518           1 :                         gf_list_add(ctx->next_period->streams, ds);
    5519           1 :                         i--;
    5520           1 :                         count--;
    5521           1 :                         continue;
    5522             :                 }
    5523             : 
    5524         387 :                 if (ctx->is_period_restore) continue;
    5525             : 
    5526             :                 //add period descriptors
    5527         357 :                 dasher_add_descriptors(&ctx->current_period->period->x_children, ds->p_period_desc);
    5528             :                 //add representation descriptors
    5529         357 :                 dasher_add_descriptors(&ds->rep->x_children, ds->p_rep_desc);
    5530             : 
    5531         357 :                 if (ds->muxed_base) continue;
    5532             : 
    5533         347 :                 if (ds->stream_type==GF_STREAM_VISUAL)
    5534             :                         ds_video = ds;
    5535             : 
    5536         347 :                 ds->skip_tpl_reuse = GF_FALSE;
    5537             :                 // period resume (end of content replacement/splice/...): if using templates, check if period ID is used, if not force startNumber to resume
    5538         347 :                 prop = gf_filter_pid_get_property_str(ds->ipid, "period_resume");
    5539         347 :                 if (prop && prop->value.string && ctx->tpl && ds->mpd_timescale) {
    5540           0 :                         char *template = ds->template;
    5541           0 :                         if (!template) template = ctx->template;
    5542           0 :                         if (
    5543             :                                 //undefined period name
    5544           0 :                                 !prop->value.string[0]
    5545             :                                 //template dor not resolve against period name
    5546           0 :                                 || (template && !strstr(template, "$Period$"))
    5547             :                         ) {
    5548           0 :                                 u64 seg_duration = (u64)(ds->dash_dur.num) * ds->mpd_timescale / ds->dash_dur.den;
    5549           0 :                                 u64 period_start = ds->mpd_timescale * ctx->mpd->media_presentation_duration / 1000;
    5550           0 :                                 u64 num = period_start / seg_duration;
    5551           0 :                                 if (num * seg_duration < period_start)
    5552           0 :                                         num++;
    5553           0 :                                 ds->startNumber = (u32) (num+1);
    5554           0 :                                 ds->skip_tpl_reuse = GF_TRUE;
    5555             :                         }
    5556             :                 }
    5557             : 
    5558         347 :                 ds->nb_comp = 1;
    5559             : 
    5560        1424 :                 for (j=0; j<count; j++) {
    5561             :                         GF_DashStream *a_ds;
    5562        1077 :                         if (i==j) continue;
    5563         730 :                         a_ds = gf_list_get(ctx->current_period->streams, j);
    5564         730 :                         if (a_ds->dep_id && (a_ds->src_id==ds->src_id) && (a_ds->dep_id==ds->id) ) {
    5565          39 :                                 gf_list_add(ds->complementary_streams, a_ds);
    5566             :                                 has_deps = GF_TRUE;
    5567          39 :                                 if (!a_ds->rep->dependency_id) {
    5568          39 :                                         a_ds->rep->dependency_id = gf_strdup(ds->rep->id);
    5569             :                                 }
    5570             :                         }
    5571             :                         //check if this rep should be muxed: same rep ID, not raw format, not CMAF
    5572         730 :                         if (a_ds->muxed_base) continue;
    5573         730 :                         if (ctx->muxtype==DASHER_MUX_RAW) continue;
    5574         728 :                         if (ctx->cmaf) continue;
    5575         728 :                         if (strcmp(a_ds->rep_id, ds->rep_id)) continue;
    5576             : 
    5577             : 
    5578          10 :                         a_ds->muxed_base = ds;
    5579          10 :                         a_ds->dash_dur = ds->dash_dur;
    5580             :                         has_muxed_bases = GF_TRUE;
    5581          10 :                         ds->nb_comp++;
    5582             : 
    5583          10 :                         if (ctx->bs_switch==DASHER_BS_SWITCH_MULTI) {
    5584           0 :                                 GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Bitstream Swicthing mode \"multi\" is not supported with multiplexed representations, disabling bitstream switching\n"));
    5585           0 :                                 ctx->bs_switch = DASHER_BS_SWITCH_OFF;
    5586             :                         }
    5587          10 :                         if (!ds->rep->codecs || !strstr(ds->rep->codecs, a_ds->rep->codecs)) {
    5588          10 :                                 gf_dynstrcat(&ds->rep->codecs, a_ds->rep->codecs, ",");
    5589             :                         }
    5590             : 
    5591          10 :                         if (ctx->profile == GF_DASH_PROFILE_AVC264_LIVE) {
    5592           0 :                                 GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Muxed representations not allowed in DASH-IF AVC264 live profile\n\tswitching to regular live profile\n"));
    5593           0 :                                 ctx->profile = GF_DASH_PROFILE_LIVE;
    5594          10 :                         } else if (ctx->profile == GF_DASH_PROFILE_HBBTV_1_5_ISOBMF_LIVE) {
    5595           0 :                                 GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Muxed representations not allowed in HbbTV 1.5 ISOBMF live profile\n\tswitching to regular live profile\n"));
    5596           0 :                                 ctx->profile = GF_DASH_PROFILE_LIVE;
    5597          10 :                         } else if (ctx->profile == GF_DASH_PROFILE_AVC264_ONDEMAND) {
    5598           0 :                                 GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Muxed representations not allowed in DASH-IF AVC264 onDemand profile\n\tswitching to regular onDemand profile\n"));
    5599           0 :                                 ctx->profile = GF_DASH_PROFILE_ONDEMAND;
    5600          10 :                         } else if (ctx->profile == GF_DASH_PROFILE_DASHIF_LL) {
    5601           0 :                                 GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Muxed representations not allowed in DASH-IF Low Latency profile\n\tswitching to regular live profile\n"));
    5602           0 :                                 ctx->profile = GF_DASH_PROFILE_LIVE;
    5603             :                         }
    5604             :                 }
    5605             :                 //use video as main stream for segmentation of muxed sources
    5606         347 :                 if (ds_video != ds) {
    5607         137 :                         for (j=0; j<count; j++) {
    5608         137 :                                 GF_DashStream *a_ds = gf_list_get(ctx->current_period->streams, j);
    5609         137 :                                 if ((a_ds->muxed_base==ds) || (a_ds==ds)) {
    5610          79 :                                         if (a_ds == ds_video)
    5611           0 :                                                 a_ds->muxed_base = NULL;
    5612             :                                         else
    5613          79 :                                                 a_ds->muxed_base = ds_video;
    5614             :                                 }
    5615             :                         }
    5616             :                 }
    5617             :         }
    5618             : 
    5619             : 
    5620         256 :         if (ctx->loop && max_adur.num) {
    5621          14 :                 if (max_adur.num * min_adur.den != min_adur.num * max_adur.den) {
    5622           0 :                         GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Audio streams in the period have different durations (min "LLU"/"LLD", max "LLU"/"LLD"), may result in bad synchronization while looping\n", min_adur.num, min_adur.den, max_adur.num, max_adur.den));
    5623             :                 }
    5624          25 :                 for (i=0; i<count; i++) {
    5625          25 :                         GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
    5626          25 :                         if (ds->duration.num * max_adur.den > max_adur.num * ds->duration.den) {
    5627          15 :                                 GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Input %s: max audio duration "LLU"/"LLD" in the period is less than duration "LLU"/"LLD", clamping will happen\n", ds->src_url, max_adur.num, max_adur.den, ds->duration.num, ds->duration.den ));
    5628             :                         }
    5629          25 :                         ds->clamped_dur = max_adur;
    5630             :                 }
    5631             :         }
    5632             : 
    5633             : 
    5634         256 :         if (ctx->is_period_restore) return GF_OK;
    5635             : 
    5636         247 :         if (has_deps) {
    5637          45 :                 for (i=0; i<count; i++) {
    5638          45 :                         GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
    5639             :                         //assign rep bitrates
    5640          45 :                         if (ds->dep_id)
    5641          39 :                                 ds->rep->bandwidth = dasher_get_dep_bitrate(ctx, ds);
    5642             : 
    5643          45 :                         if (gf_list_count(ds->complementary_streams)) {
    5644           6 :                                 u32 nb_str = gf_list_count(ds->complementary_streams);
    5645           6 :                                 ds->moof_sn_inc = 1+nb_str;
    5646           6 :                                 ds->moof_sn = 1;
    5647          45 :                                 for (j=0; j<nb_str; j++) {
    5648          39 :                                         GF_DashStream *a_ds = gf_list_get(ds->complementary_streams, j);
    5649          39 :                                         a_ds->moof_sn_inc = ds->moof_sn_inc;
    5650          39 :                                         a_ds->moof_sn = ds->moof_sn + 1 + j;
    5651             :                                 }
    5652             :                         }
    5653             :                 }
    5654             :         }
    5655             : 
    5656             :         //moved all mux components after the base one, so that we do the segmentation on the main component
    5657         247 :         if (has_muxed_bases) {
    5658             :                 //setup reps in adaptation sets
    5659          19 :                 for (i=0; i<count; i++) {
    5660          19 :                         GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
    5661          19 :                         if (!ds->muxed_base) continue;
    5662          10 :                         gf_list_rem(ctx->current_period->streams, i);
    5663          10 :                         gf_list_add(ctx->current_period->streams, ds);
    5664             :                 }
    5665             :         }
    5666             : 
    5667             :         //setup reps in adaptation sets
    5668         357 :         for (i=0; i<count; i++) {
    5669         357 :                 GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
    5670         357 :                 if (ds->muxed_base) continue;
    5671             :                 //already setup
    5672         347 :                 if (ds->set) continue;
    5673             : 
    5674             :                 //not setup, create new AS
    5675         317 :                 ds->set = gf_mpd_adaptation_set_new();
    5676         317 :                 ds->owns_set = GF_TRUE;
    5677             :                 //only set hls intra for visual stream if we have know for sure
    5678         317 :                 if ((ds->stream_type==GF_STREAM_VISUAL) && (ds->sync_points_type==DASHER_SYNC_NONE)) {
    5679           1 :                         ds->set->intra_only = GF_TRUE;
    5680             :                 }
    5681         317 :                 if (ctx->llhls) {
    5682           9 :                         ds->set->use_hls_ll = GF_TRUE;
    5683           9 :                         if (ctx->cdur.den)
    5684           9 :                                 ds->set->hls_ll_frag_dur = ((Double)ctx->cdur.num) / ctx->cdur.den;
    5685             :                 }
    5686         317 :                 ds->set->udta = ds;
    5687         317 :                 if (ds->period_continuity_id) {
    5688           0 :                         GF_FilterPacket *pck = gf_filter_pid_get_packet(ds->ipid);
    5689           0 :                         if (pck && (gf_filter_pck_get_cts(pck) != ds->period_continuity_next_cts)) {
    5690           0 :                                 gf_free(ds->period_continuity_id);
    5691           0 :                                 ds->period_continuity_id = NULL;
    5692             :                         } else {
    5693           0 :                                 GF_MPD_Descriptor *desc = gf_mpd_descriptor_new(NULL, "urn:mpeg:dash:period-continuity:2015", ds->period_continuity_id);
    5694           0 :                                 gf_list_add(ds->set->supplemental_properties, desc);
    5695             :                         }
    5696             :                 }
    5697         317 :                 ds->period_continuity_next_cts = 0;
    5698             : 
    5699             : 
    5700         317 :                 if (ctx->mha_compat && ((ds->codec_id==GF_CODECID_MHAS) || (ds->codec_id==GF_CODECID_MPHA))) {
    5701           0 :                         const GF_PropertyValue *prop = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_MHA_COMPATIBLE_PROFILES);
    5702           0 :                         if (prop) {
    5703           0 :                                 ds->set->nb_alt_mha_profiles = prop->value.uint_list.nb_items;
    5704           0 :                                 ds->set->alt_mha_profiles = prop->value.uint_list.vals;
    5705           0 :                                 ds->set->alt_mha_profiles_only = (ctx->mha_compat==DASHER_MPHA_COMP_ONLY) ? GF_TRUE : GF_FALSE;
    5706             :                         }
    5707             :                 }
    5708             : 
    5709         317 :                 if (!ds->set->representations)
    5710           0 :                         ds->set->representations = gf_list_new();
    5711         317 :                 if (!ds->period->period->adaptation_sets)
    5712           0 :                         ds->period->period->adaptation_sets = gf_list_new();
    5713         317 :                 gf_list_add(ds->period->period->adaptation_sets, ds->set);
    5714             : 
    5715         317 :                 gf_list_add(ds->set->representations, ds->rep);
    5716         317 :                 ds->nb_rep = 1;
    5717             : 
    5718             :                 //add non-conditional adaptation set descriptors
    5719         317 :                 dasher_add_descriptors(&ds->set->x_children, ds->p_as_any_desc);
    5720             :                 //new AS, add conditional adaptation set descriptors
    5721         317 :                 dasher_add_descriptors(&ds->set->x_children, ds->p_as_desc);
    5722             : 
    5723         317 :                 if (ds->as_id) has_as_id = GF_TRUE;
    5724             :                 //for each following, check if same AS is possible
    5725         633 :                 for (j=i+1; j<count; j++) {
    5726             :                         GF_DashStream *a_ds;
    5727         316 :                         a_ds = gf_list_get(ctx->current_period->streams, j);
    5728             :                         //we add to the adaptation set even if shared rep, we will remove it when assigning templates and pids
    5729         316 :                         if (dasher_same_adaptation_set(ctx, ds, a_ds)) {
    5730          40 :                                 a_ds->set = ds->set;
    5731             : 
    5732          40 :                                 gf_list_add(ds->set->representations, a_ds->rep);
    5733          40 :                                 ds->nb_rep++;
    5734             :                                 //add non-conditional adaptation set descriptors
    5735          40 :                                 dasher_add_descriptors(&ds->set->x_children, a_ds->p_as_any_desc);
    5736             :                         }
    5737             :                 }
    5738             :         }
    5739         247 :         if (has_as_id) {
    5740           0 :                 for (i=0; i<count; i++) {
    5741           0 :                         GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
    5742           0 :                         if (!ds->owns_set) continue;
    5743           0 :                         for (j=i+1; j<count; j++) {
    5744           0 :                                 GF_DashStream *a_ds = gf_list_get(ctx->current_period->streams, j);
    5745             :                                 //avoid as id duplicates
    5746           0 :                                 if (ds->owns_set && a_ds->owns_set && (a_ds->as_id == ds->as_id)) {
    5747           0 :                                         a_ds->as_id = 0;
    5748             :                                 }
    5749             :                         }
    5750             :                 }
    5751             :         }
    5752             : 
    5753             :         //we need a pass on adaptation sets to figure out if they share the same source URL
    5754             :         //in case we use file name in templates
    5755         247 :         nb_sets = gf_list_count(ctx->current_period->period->adaptation_sets);
    5756         558 :         for (i=0; i<nb_sets; i++) {
    5757             :                 GF_DashStream *ds;
    5758             :                 GF_MPD_Representation *rep;
    5759             :                 GF_MPD_AdaptationSet *set;
    5760             : 
    5761         317 :                 if (ctx->sigfrag)
    5762             :                         break;
    5763             : 
    5764         311 :                 set = gf_list_get(ctx->current_period->period->adaptation_sets, i);
    5765             :                 assert(set);
    5766         311 :                 rep = gf_list_get(set->representations, 0);
    5767             :                 assert(rep);
    5768         311 :                 ds = rep->playback.udta;
    5769             : 
    5770         311 :                 if (inject_ds && (ds != inject_ds))
    5771           0 :                         continue;
    5772             : 
    5773         311 :                 if (!dasher_template_use_source_url(ds->template ? ds->template : ctx->template))
    5774          36 :                         continue;
    5775             : 
    5776         441 :                 for (j=i+1; j<nb_sets; j++) {
    5777             :                         Bool split_init = GF_FALSE;
    5778             :                         const GF_PropertyValue *p1, *p2;
    5779             :                         GF_DashStream *a_ds;
    5780             : 
    5781         166 :                         set = gf_list_get(ctx->current_period->period->adaptation_sets, j);
    5782         166 :                         rep = gf_list_get(set->representations, 0);
    5783             :                         assert(rep);
    5784         166 :                         a_ds = rep->playback.udta;
    5785             : 
    5786         166 :                         if (!dasher_template_use_source_url(a_ds->template ? a_ds->template : ctx->template))
    5787           0 :                                 continue;
    5788             : 
    5789         166 :                         p1 = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_URL);
    5790         166 :                         p2 = gf_filter_pid_get_property(a_ds->ipid, GF_PROP_PID_URL);
    5791         166 :                         if (p1 && p2) {
    5792         166 :                                 if (gf_props_equal(p1, p2)) split_init = GF_TRUE;
    5793             :                         } else {
    5794           0 :                                 p1 = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_FILEPATH);
    5795           0 :                                 p2 = gf_filter_pid_get_property(a_ds->ipid, GF_PROP_PID_FILEPATH);
    5796           0 :                                 if (p1 && p2 && gf_props_equal(p1, p2)) split_init = GF_TRUE;
    5797             :                         }
    5798             :                         
    5799             :                         if (split_init) {
    5800         156 :                                 ds->split_set_names = GF_TRUE;
    5801         156 :                                 a_ds->split_set_names = GF_TRUE;
    5802             :                         }
    5803             :                 }
    5804             :         }
    5805             : 
    5806             :         /*HbbTV 1.5 ISO live specific checks*/
    5807         247 :         if (ctx->profile == GF_DASH_PROFILE_HBBTV_1_5_ISOBMF_LIVE) {
    5808           0 :                 u32 nb_periods = dasher_period_count(ctx->current_period->streams);
    5809           0 :                 if (nb_sets > 16) {
    5810           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Max 16 adaptation sets in HbbTV 1.5 ISO live profile\n\tswitching to DASH AVC/264 live profile\n"));
    5811           0 :                         ctx->profile = GF_DASH_PROFILE_AVC264_LIVE;
    5812             :                 }
    5813           0 :                 if (nb_periods > 32) {
    5814           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Max 32 periods in HbbTV 1.5 ISO live profile\n\tswitching to regular DASH AVC/264 live profile\n"));
    5815           0 :                         ctx->profile = GF_DASH_PROFILE_AVC264_LIVE;
    5816             :                 }
    5817           0 :                 if (ctx->segdur.num < (s32) ctx->segdur.den) {
    5818           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Min segment duration 1s in HbbTV 1.5 ISO live profile\n\tcapping to 1s\n"));
    5819           0 :                         ctx->segdur.num = 1;
    5820           0 :                         ctx->segdur.den = 1;
    5821             :                 }
    5822           0 :                 if (ctx->segdur.num > 15 * (s32) ctx->segdur.den) {
    5823           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Max segment duration 15s in HbbTV 1.5 ISO live profile\n\tcapping to 15s\n"));
    5824           0 :                         ctx->segdur.num = 15;
    5825           0 :                         ctx->segdur.den = 1;
    5826             :                 }
    5827             :         }
    5828             : 
    5829             :         //init UTC reference time for dynamic
    5830         247 :         if (!ctx->mpd->availabilityStartTime && (ctx->dmode!=GF_MPD_TYPE_STATIC) && !inject_ds) {
    5831          25 :                 u64 dash_start_date = ctx->ast ? gf_net_parse_date(ctx->ast) : 0;
    5832             : 
    5833          25 :                 if (!ctx->utc_initialized) {
    5834          25 :                         dasher_init_utc(filter, ctx);
    5835             : 
    5836             :                         //setup service description
    5837          25 :                         if (ctx->profile == GF_DASH_PROFILE_DASHIF_LL) {
    5838           0 :                                 ctx->mpd->inject_service_desc = GF_TRUE;
    5839             :                         }
    5840             :                 }
    5841             : 
    5842          25 :                 ctx->mpd->gpac_init_ntp_ms = gf_net_get_ntp_ms();
    5843          50 :                 ctx->mpd->availabilityStartTime = dasher_get_utc(ctx);
    5844          25 :                 GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[Dasher] MPD Availability start time initialized to "LLU" ms\n", ctx->mpd->availabilityStartTime));
    5845             : 
    5846          25 :                 if (dash_start_date && (dash_start_date < ctx->mpd->availabilityStartTime)) {
    5847             :                         u64 start_date_sec_ntp, secs;
    5848             :                         Double ms;
    5849             :                         //recompute NTP init time matching the required ast
    5850           0 :                         secs = dash_start_date/1000;
    5851             :                         start_date_sec_ntp = (u32) secs;
    5852           0 :                         start_date_sec_ntp += GF_NTP_SEC_1900_TO_1970;
    5853           0 :                         ms = (Double) (dash_start_date - secs*1000);
    5854           0 :                         ms /= 1000.0;
    5855           0 :                         ctx->mpd->gpac_init_ntp_ms = (u64) (start_date_sec_ntp * 1000 + ms);
    5856             :                         //compute number of seconds to discard
    5857           0 :                         ctx->nb_secs_to_discard = (Double) (ctx->mpd->availabilityStartTime - dash_start_date);
    5858           0 :                         ctx->nb_secs_to_discard /= 1000;
    5859             :                         //don't discard TSB, this will be done automatically
    5860             : 
    5861           0 :                         ctx->mpd->availabilityStartTime = dash_start_date;
    5862             : 
    5863          25 :                 } else if (dash_start_date) {
    5864           0 :                         GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] specified AST %s seems in the future, ignoring it\n", ctx->ast));
    5865             :                 }
    5866             :         }
    5867             : 
    5868             :         //setup adaptation sets bitstream switching
    5869         357 :         for (i=0; i<count; i++) {
    5870         357 :                 GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
    5871         357 :                 if (!ds->owns_set) continue;
    5872         317 :                 if (inject_ds && (ds != inject_ds))
    5873           0 :                         continue;
    5874             :                 //check bitstream switching
    5875         317 :                 dasher_check_bitstream_swicthing(ctx, ds->set);
    5876             :                 //setup AS defaults, roles and co
    5877         317 :                 dasher_setup_set_defaults(ctx, ds->set);
    5878             :                 //setup sources, templates & co
    5879         317 :                 dasher_setup_sources(filter, ctx, ds->set);
    5880             :         }
    5881             : 
    5882         250 :         while (gf_list_count(ctx->postponed_pids)) {
    5883           3 :                 GF_DashStream *ds = gf_list_get(ctx->postponed_pids, 0);
    5884           3 :                 dasher_open_pid(filter, ctx, ds, ds->multi_pids, GF_FALSE);
    5885             :         }
    5886             : 
    5887             :         //good to go !
    5888         357 :         for (i=0; i<count; i++) {
    5889         357 :                 GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
    5890         357 :                 if (inject_ds && (ds != inject_ds))
    5891           0 :                         continue;
    5892             :                 //setup segmentation
    5893         357 :                 ds->rep_init = GF_FALSE;
    5894         357 :                 ds->seg_done = GF_FALSE;
    5895         357 :                 ds->next_seg_start = (u32) ( ((u64) ds->dash_dur.num * ds->timescale) / ds->dash_dur.den);
    5896         357 :                 ds->adjusted_next_seg_start = ds->next_seg_start;
    5897         357 :                 ds->segment_started = GF_FALSE;
    5898         357 :                 ds->seg_number = ds->startNumber;
    5899         357 :                 ds->first_cts = ds->first_dts = ds->max_period_dur = 0;
    5900             : 
    5901             :                 //simulate N loops of the source
    5902         357 :                 if (ctx->nb_secs_to_discard) {
    5903             :                         u64 period_dur, seg_dur;
    5904             :                         u32 nb_skip=0;
    5905             : 
    5906           0 :                         period_dur = (u64) (ctx->nb_secs_to_discard * ds->timescale);
    5907             :                         seg_dur = (u64) (ds->dash_dur.num) * ds->timescale / ds->dash_dur.den;
    5908             : 
    5909           0 :                         nb_skip = (u32) (period_dur / seg_dur);
    5910           0 :                         ds->ts_offset += nb_skip*seg_dur;
    5911           0 :                         ds->seg_number += nb_skip;
    5912             : 
    5913           0 :                         ds->max_period_dur = ds->cumulated_dur;
    5914           0 :                         ds->adjusted_next_seg_start += ds->ts_offset;
    5915           0 :                         ds->next_seg_start += ds->ts_offset;
    5916             :                 }
    5917             :         }
    5918             : 
    5919         247 :         ctx->nb_secs_to_discard = 0;
    5920             : 
    5921         247 :         if (ctx->state)
    5922          14 :                 dasher_context_update_period_start(ctx);
    5923             : 
    5924             :         return GF_OK;
    5925             : }
    5926             : 
    5927        3333 : static void dasher_insert_timeline_entry(GF_DasherCtx *ctx, GF_DashStream *ds)
    5928             : {
    5929             :         GF_MPD_SegmentTimelineEntry *s;
    5930             :         u64 duration;
    5931             :         Bool is_first = GF_FALSE;
    5932             :         Bool seg_align = GF_FALSE;
    5933             :         GF_MPD_SegmentTimeline *tl=NULL;
    5934             : 
    5935             :         //we only store segment timeline for the main component in the representation
    5936        3333 :         if (ds->muxed_base) return;
    5937             : 
    5938        3333 :         if (ds->rep && ds->rep->state_seg_list) {
    5939         528 :                 GF_DASH_SegmentContext *sctx = gf_list_last(ds->rep->state_seg_list);
    5940         528 :                 if (sctx)
    5941         528 :                         sctx->dur = ds->first_cts_in_next_seg - ds->first_cts_in_seg;
    5942             :         }
    5943             : 
    5944             :         //we only use segment timeline with templates
    5945        3333 :         if (!ctx->stl) return;
    5946             : 
    5947           4 :         if (gf_list_find(ds->set->representations, ds->rep)==0) is_first = GF_TRUE;
    5948             :         assert(ds->first_cts_in_next_seg > ds->first_cts_in_seg);
    5949           4 :         duration = ds->first_cts_in_next_seg - ds->first_cts_in_seg;
    5950           4 :         if (ds->timescale != ds->mpd_timescale) {
    5951           0 :                 duration *= ds->mpd_timescale;
    5952           0 :                 duration /= ds->timescale;
    5953             :         }
    5954           4 :         seg_align = (ds->set->segment_alignment || ds->set->subsegment_alignment) ? GF_TRUE : GF_FALSE;
    5955             :         //not first and segment alignment, ignore
    5956           4 :         if (!is_first && seg_align) {
    5957             :                 return;
    5958             :         }
    5959             : 
    5960             :         //no segment alignment store in each rep
    5961           4 :         if (!seg_align) {
    5962             :                 GF_MPD_SegmentTimeline **p_tl=NULL;
    5963           0 :                 if (!ds->rep) {
    5964           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] failed to store timeline entru, no representation assigned !\n"));
    5965             :                         return;
    5966             :                 }
    5967             : 
    5968           0 :                 if (ctx->tpl) {
    5969           0 :                         p_tl = &ds->rep->segment_template->segment_timeline;
    5970           0 :                         ds->rep->segment_template->duration = 0;
    5971             :                 } else {
    5972           0 :                         p_tl = &ds->rep->segment_list->segment_timeline;
    5973           0 :                         ds->rep->segment_list->duration = 0;
    5974             :                 }
    5975           0 :                 if (! (*p_tl)) {
    5976           0 :                         (*p_tl) = gf_mpd_segmentimeline_new();
    5977             :                 }
    5978           0 :                 tl = (*p_tl);
    5979             :         } else {
    5980             :                 Bool new_tl = GF_FALSE;
    5981             :                 GF_MPD_SegmentTimeline **p_tl=NULL;
    5982             : 
    5983             :                 if (!ds->set) {
    5984             :                         GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] failed to store timeline entru, no AdpatationSet assigned !\n"));
    5985             :                         return;
    5986             :                 }
    5987             :                 assert(ds->set);
    5988           4 :                 if (ctx->tpl) {
    5989             :                         //in case we had no template at set level
    5990           4 :                         if (!ds->set->segment_template) {
    5991           1 :                                 GF_SAFEALLOC(ds->set->segment_template, GF_MPD_SegmentTemplate);
    5992           1 :                                 if (ds->set->segment_template) {
    5993           1 :                                         ds->set->segment_template->start_number = (u32) -1;
    5994           1 :                                         ds->set->segment_template->timescale = ds->timescale;
    5995             :                                 }
    5996             :                                 new_tl = GF_TRUE;
    5997             :                         }
    5998           4 :                         p_tl = &ds->set->segment_template->segment_timeline;
    5999           4 :                         ds->set->segment_template->duration = 0;
    6000             :                 } else {
    6001             :                         //in case we had no template at set level
    6002           0 :                         if (!ds->set->segment_list) {
    6003           0 :                                 GF_SAFEALLOC(ds->set->segment_list, GF_MPD_SegmentList);
    6004           0 :                                 if (ds->set->segment_list) {
    6005           0 :                                         ds->set->segment_list->start_number = (u32) -1;
    6006           0 :                                         ds->set->segment_list->timescale = ds->timescale;
    6007             :                                 }
    6008             :                                 new_tl = GF_TRUE;
    6009             :                         }
    6010           0 :                         p_tl = &ds->set->segment_list->segment_timeline;
    6011           0 :                         ds->set->segment_list->duration = 0;
    6012             :                 }
    6013             : 
    6014           4 :                 if (! (*p_tl) ) {
    6015           2 :                         (*p_tl)  = gf_mpd_segmentimeline_new();
    6016             :                 }
    6017           4 :                 tl = (*p_tl);
    6018           4 :                 if (new_tl) {
    6019           1 :                         u32 i, count = gf_list_count(ds->set->representations);
    6020           1 :                         for (i=0; i<count; i++) {
    6021           1 :                                 GF_MPD_Representation *arep = gf_list_get(ds->set->representations, i);
    6022           1 :                                 if (arep && arep->segment_template) arep->segment_template->duration = 0;
    6023           1 :                                 if (arep && arep->segment_list) arep->segment_list->duration = 0;
    6024             :                         }
    6025             :                 }
    6026             :         }
    6027             : 
    6028             :         //append to previous entry if possible
    6029           4 :         s = gf_list_last(tl->entries);
    6030           4 :         if (s && (s->duration == duration) && (s->start_time + (s->repeat_count+1) * s->duration == ds->seg_start_time)) {
    6031           2 :                 s->repeat_count++;
    6032             :                 return;
    6033             :         }
    6034             :         //nope, allocate
    6035           2 :         GF_SAFEALLOC(s, GF_MPD_SegmentTimelineEntry);
    6036           2 :         if (!s) return;
    6037           2 :         s->start_time = ds->seg_start_time;
    6038           2 :         s->duration = (u32) duration;
    6039           2 :         gf_list_add(tl->entries, s);
    6040             : }
    6041             : 
    6042           0 : static void dasher_copy_segment_timelines(GF_DasherCtx *ctx, GF_MPD_AdaptationSet *set)
    6043             : {
    6044             :         GF_MPD_SegmentTimeline *src_tl = NULL;
    6045             :         u32 i, j, count, nb_s;
    6046           0 :         if (!ctx->stl) return;
    6047             :         //get as level segment timeline, set it to NULL, reassign it to first rep and clone for other reps
    6048           0 :         if (ctx->tpl) {
    6049             :                 assert(set->segment_template->segment_timeline);
    6050           0 :                 src_tl = set->segment_template->segment_timeline;
    6051           0 :                 set->segment_template->segment_timeline = NULL;
    6052             :         } else {
    6053             :                 assert(set->segment_list->segment_timeline);
    6054           0 :                 src_tl = set->segment_list->segment_timeline;
    6055           0 :                 set->segment_list->segment_timeline = NULL;
    6056             :         }
    6057           0 :         nb_s = gf_list_count(src_tl->entries);
    6058             : 
    6059           0 :         count = gf_list_count(set->representations);
    6060           0 :         for (i=0; i<count; i++) {
    6061             :                 GF_MPD_SegmentTimeline *tl = NULL;
    6062           0 :                 GF_MPD_Representation *rep = gf_list_get(set->representations, i);
    6063           0 :                 if (ctx->tpl) {
    6064           0 :                         if (!rep->segment_template) {
    6065           0 :                                 GF_SAFEALLOC(rep->segment_template, GF_MPD_SegmentTemplate);
    6066           0 :                                 if (!rep->segment_template) continue;
    6067             :                         }
    6068           0 :                         if (!i) {
    6069           0 :                                 rep->segment_template->segment_timeline = src_tl;
    6070           0 :                                 continue;
    6071             :                         }
    6072           0 :                         if (!rep->segment_template->segment_timeline) {
    6073           0 :                                 rep->segment_template->segment_timeline = gf_mpd_segmentimeline_new();
    6074             :                         }
    6075           0 :                         tl = rep->segment_template->segment_timeline;
    6076             :                 } else {
    6077           0 :                         if (!rep->segment_list) {
    6078           0 :                                 GF_SAFEALLOC(rep->segment_list, GF_MPD_SegmentList);
    6079           0 :                                 if (!rep->segment_list) continue;
    6080           0 :                                 rep->segment_list->start_number = (u32) -1;
    6081             :                         }
    6082           0 :                         if (!i) {
    6083           0 :                                 rep->segment_list->segment_timeline = src_tl;
    6084           0 :                                 continue;
    6085             :                         }
    6086           0 :                         if (!rep->segment_list->segment_timeline) {
    6087           0 :                                 rep->segment_list->segment_timeline = gf_mpd_segmentimeline_new();
    6088             :                         }
    6089           0 :                         tl = rep->segment_list->segment_timeline;
    6090             :                 }
    6091             :                 assert(tl);
    6092           0 :                 for (j=0; j<nb_s; j++) {
    6093             :                         GF_MPD_SegmentTimelineEntry *s;
    6094           0 :                         GF_MPD_SegmentTimelineEntry *src_s = gf_list_get(src_tl->entries, j);
    6095           0 :                         GF_SAFEALLOC(s, GF_MPD_SegmentTimelineEntry);
    6096           0 :                         if (!s) continue;
    6097             : 
    6098           0 :                         s->duration = src_s->duration;
    6099           0 :                         s->repeat_count = src_s->repeat_count;
    6100           0 :                         s->start_time = src_s->start_time;
    6101           0 :                         gf_list_add(tl->entries, s);
    6102             :                 }
    6103             :         }
    6104             : }
    6105             : 
    6106        3334 : static void dasher_flush_segment(GF_DasherCtx *ctx, GF_DashStream *ds, Bool is_last_in_period)
    6107             : {
    6108             :         u32 i, count;
    6109             :         GF_DashStream *ds_done = NULL, *ds_not_done = NULL;
    6110        3334 :         GF_DashStream *set_ds = ds->set->udta;
    6111        3334 :         GF_DashStream *base_ds = ds->muxed_base ? ds->muxed_base : ds;
    6112             :         Bool has_ds_done = GF_FALSE;
    6113             :         u32 seg_dur_ms=0;
    6114             :         GF_DashStream *ds_log = NULL;
    6115             :         u64 first_cts_in_cur_seg=0;
    6116             : 
    6117        3334 :         ctx->update_report = -1;
    6118             : 
    6119        3334 :         if (ds->segment_started) {
    6120             :                 Double seg_duration;
    6121        3333 :                 u64 seg_duration_unscale = base_ds->first_cts_in_next_seg - ds->first_cts_in_seg;
    6122             :                 //seg_duration /= base_ds->timescale;
    6123        3333 :                 if (!seg_duration_unscale) {
    6124           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Segment %d is empty - pid end of stream %d\n", ds->seg_number, gf_filter_pid_is_eos(ds->ipid) ));
    6125             :                 }
    6126        3333 :                 seg_dur_ms = (u32) (seg_duration_unscale*1000 / base_ds->timescale);
    6127        3333 :                 if (seg_dur_ms * base_ds->timescale < seg_duration_unscale* 1000) seg_dur_ms++;
    6128             : 
    6129        3333 :                 first_cts_in_cur_seg = ds->first_cts_in_seg;
    6130        3333 :                 if (ctx->mpd->max_segment_duration < seg_dur_ms)
    6131         291 :                         ctx->mpd->max_segment_duration = seg_dur_ms;
    6132             : 
    6133        3333 :                 seg_duration = (Double) base_ds->first_cts_in_next_seg - ds->first_cts_in_seg;
    6134        3333 :                 seg_duration /= base_ds->timescale;
    6135             : 
    6136        3333 :                 if (ctx->sigfrag) {
    6137          72 :                         if (ds->no_seg_dur) {
    6138          72 :                                 ds->gm_duration_total += seg_duration;
    6139          72 :                                 ds->gm_nb_segments++;
    6140          72 :                                 if (!ds->gm_duration_min || (ds->gm_duration_min>seg_duration) )
    6141           6 :                                         ds->gm_duration_min = seg_duration;
    6142          72 :                                 if (ds->gm_duration_max<seg_duration)
    6143           6 :                                         ds->gm_duration_max = seg_duration;
    6144          72 :                                 ds->dash_dur.num = (s32) (1000.0 * ds->gm_duration_total / ds->gm_nb_segments);
    6145          72 :                                 ds->dash_dur.den = 1000;
    6146          72 :                                 ds->rep->dash_dur = ds->dash_dur;
    6147             :                         }
    6148             : 
    6149          72 :                         if (ds->rep->segment_list && (ds->rep->segment_list->duration * ds->dash_dur.den != ds->dash_dur.num) ) {
    6150          48 :                                 ds->rep->segment_list->duration = (u64) (ds->dash_dur.num) * ds->rep->segment_list->timescale / ds->dash_dur.den;
    6151             :                         }
    6152          72 :                         if (ds->set->segment_list && (ds->set->segment_list->duration * ds->dash_dur.den != ds->dash_dur.num) ) {
    6153           0 :                                 ds->set->segment_list->duration = (u64) (ds->dash_dur.num) * ds->set->segment_list->timescale / ds->dash_dur.den;
    6154             :                         }
    6155          72 :                         if (ds->rep->segment_template && (ds->rep->segment_template->duration * ds->dash_dur.den != ds->dash_dur.num) ) {
    6156           0 :                                 ds->rep->segment_template->duration = (u64) (ds->dash_dur.num) * ds->rep->segment_template->timescale / ds->dash_dur.den;
    6157             :                         }
    6158          72 :                         if (ds->set && ds->set->segment_template && (ds->set->segment_template->duration * ds->dash_dur.den != ds->dash_dur.num) ) {
    6159           9 :                                 ds->set->segment_template->duration = (u64) (ds->dash_dur.num) * ds->set->segment_template->timescale / ds->dash_dur.den;
    6160             :                         }
    6161             :                 }
    6162        3333 :                 if (!base_ds->done && !ctx->stl && ctx->tpl && !ctx->cues && !ctx->forward_mode && !is_last_in_period) {
    6163             : 
    6164        2267 :                         if (2 * seg_duration * ds->dash_dur.den < ds->dash_dur.num) {
    6165             : 
    6166           0 :                                 GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Segment %d duration %g less than half DASH duration, consider reencoding or using segment timeline\n", ds->seg_number, seg_duration));
    6167        2267 :                         } else if (2 * seg_duration * ds->dash_dur.den > 3 * ds->dash_dur.num) {
    6168          24 :                                 GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Segment %d duration %g more than 3/2 DASH duration, consider reencoding or using segment timeline\n", ds->seg_number, seg_duration));
    6169             :                         }
    6170             :                 }
    6171        3333 :                 dasher_insert_timeline_entry(ctx, base_ds);
    6172             : 
    6173        3333 :                 if (ctx->align) {
    6174        3333 :                         if (!set_ds->nb_rep_done || !set_ds->set_seg_duration) {
    6175        2933 :                                 set_ds->set_seg_duration = seg_duration;
    6176             :                         } else {
    6177         400 :                                 Double diff = set_ds->set_seg_duration - seg_duration;
    6178             : 
    6179         400 :                                 if (ABS(diff) > 0.001) {
    6180           0 :                                         GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Segments are not aligned across representations: first rep segment duration %g but new segment duration %g for the same segment %d\n", set_ds->set_seg_duration, seg_duration, set_ds->seg_number));
    6181             : 
    6182           0 :                                         if (ctx->profile != GF_DASH_PROFILE_FULL) {
    6183           0 :                                                 set_ds->set->segment_alignment = GF_FALSE;
    6184           0 :                                                 set_ds->set->subsegment_alignment = GF_FALSE;
    6185           0 :                                                 ctx->profile = GF_DASH_PROFILE_FULL;
    6186           0 :                                                 GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] No segment alignment, switching to full profile\n"));
    6187           0 :                                                 dasher_copy_segment_timelines(ctx, set_ds->set);
    6188             :                                         }
    6189             :                                 }
    6190             :                         }
    6191        3333 :                         set_ds->nb_rep_done++;
    6192        3333 :                         if (set_ds->nb_rep_done < set_ds->nb_rep) {
    6193         401 :                                 if (ctx->subdur && (ds->cumulated_dur >= 0.8 * (ds->cumulated_subdur + ctx->subdur) * ds->timescale))
    6194           0 :                                         ds->subdur_done = GF_TRUE;
    6195             :                                 return;
    6196             :                         }
    6197        2932 :                         set_ds->set_seg_duration = 0;
    6198        2932 :                         set_ds->nb_rep_done = 0;
    6199             :                 }
    6200             : 
    6201             :                 ds_log = ds;
    6202             :         } else {
    6203           1 :                 if (ctx->align) {
    6204           1 :                         set_ds->nb_rep_done++;
    6205           1 :                         if (set_ds->nb_rep_done < set_ds->nb_rep) return;
    6206             : 
    6207           1 :                         set_ds->set_seg_duration = 0;
    6208           1 :                         set_ds->nb_rep_done = 0;
    6209             :                 }
    6210             :         }
    6211             : 
    6212        2933 :         if (ctx->subdur && (ds->cumulated_dur >= 0.8 * (ds->cumulated_subdur + ctx->subdur) * ds->timescale))
    6213          34 :                 ds->subdur_done = GF_TRUE;
    6214             : 
    6215        2933 :         count = gf_list_count(ctx->current_period->streams);
    6216             : 
    6217        2933 :         if (ctx->subdur) {
    6218             :                 u32 nb_sub_done=0;
    6219          38 :                 if (ctx->subdur_done) return;
    6220          70 :                 for (i=0; i<count; i++) {
    6221          70 :                         GF_DashStream *a_ds = gf_list_get(ctx->current_period->streams, i);
    6222          70 :                         if (a_ds->muxed_base) {
    6223           2 :                                 if (a_ds->muxed_base->subdur_done) a_ds->subdur_done = GF_TRUE;
    6224             :                         }
    6225             : 
    6226          70 :                         if (a_ds->subdur_done) {
    6227          48 :                                 nb_sub_done++;
    6228             :                         }
    6229             :                 }
    6230             :                 // if one of the AS is done and we are at 30% of target subdur, abort
    6231          38 :                 if (nb_sub_done && !ds->subdur_done
    6232           0 :                         && (ctx->subdur && (ds->cumulated_dur >= (0.7 * (ds->cumulated_subdur + ctx->subdur)) * ds->timescale))
    6233             :                 ) {
    6234           0 :                         ds->subdur_done = GF_TRUE;
    6235           0 :                         nb_sub_done++;
    6236             :                 }
    6237          38 :                 if (nb_sub_done==count)
    6238          20 :                         ctx->subdur_done = GF_TRUE;
    6239             :         }
    6240             : 
    6241             :         //reset all streams from our rep or our set
    6242       15387 :         for (i=0; i<count; i++) {
    6243       15387 :                 ds = gf_list_get(ctx->current_period->streams, i);
    6244             :                 //reset all in set if segment alignment
    6245       15387 :                 if (ctx->align) {
    6246       15387 :                         if (ds->set != set_ds->set) continue;
    6247             :                 } else {
    6248             :                         //otherwise reset only media components for this rep
    6249           0 :                         if ((ds->muxed_base != base_ds) && (ds != base_ds)) continue;
    6250             :                 }
    6251             : 
    6252        3520 :                 if (!ds->done) {
    6253        3151 :                         ds->first_cts_in_next_seg = ds->first_cts_in_seg = ds->est_first_cts_in_next_seg = 0;
    6254             :                 }
    6255             : 
    6256        3520 :                 if (ds->muxed_base) {
    6257         157 :                         if (!ds->done) {
    6258         148 :                                 ds->segment_started = GF_FALSE;
    6259         148 :                                 ds->seg_done = GF_FALSE;
    6260             :                         } else {
    6261             :                                 has_ds_done = GF_TRUE;
    6262             :                         }
    6263         157 :                         continue;
    6264             :                 }
    6265             :                 base_ds = ds;
    6266             : 
    6267        3363 :                 if (base_ds->done)
    6268             :                         ds_done = base_ds;
    6269        3003 :                 else if (base_ds->nb_comp_done==base_ds->nb_comp) ds_not_done = base_ds;
    6270             : 
    6271        3363 :                 if (!base_ds->done && base_ds->seg_done) {
    6272        3003 :                         base_ds->seg_done = GF_FALSE;
    6273        3003 :                         base_ds->nb_comp_done = 0;
    6274             : 
    6275             : #ifndef GPAC_DISABLE_LOG
    6276        3003 :                         if (ctx->dmode>=GF_DASH_DYNAMIC) {
    6277             :                                 u32 asid;
    6278             :                                 s64 ast_diff;
    6279         103 :                                 u64 seg_ast = ctx->mpd->availabilityStartTime;
    6280         103 :                                 seg_ast += ctx->current_period->period->start;
    6281         103 :                                 seg_ast += (base_ds->adjusted_next_seg_start*1000) / base_ds->timescale;
    6282             : 
    6283             :                                 //if theoretical AST of the segment is less than the current UTC, we are producing the segment too late.
    6284             :                                 ast_diff = (s64) dasher_get_utc(ctx);
    6285         103 :                                 ast_diff -= seg_ast;
    6286             : 
    6287         103 :                                 asid = base_ds->set->id;
    6288         103 :                                 if (!asid)
    6289           0 :                                         asid = gf_list_find(ctx->current_period->period->adaptation_sets, base_ds->set) + 1;
    6290             : 
    6291         103 :                                 if (ast_diff>10) {
    6292           5 :                                         GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] AS%d Rep %s segment %d done TOO LATE by %d ms\n", asid, base_ds->rep->id, base_ds->seg_number, (s32) ast_diff));
    6293             :                                 } else {
    6294          98 :                                         GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[Dasher] AS%d Rep %s segment %d done %d ms %s UTC due time\n", asid, base_ds->rep->id, base_ds->seg_number, ABS(ast_diff), (ast_diff<0) ? "before" : "after"));
    6295             :                                 }
    6296             :                         }
    6297             : #endif
    6298             : 
    6299             :                         assert(base_ds->segment_started);
    6300        3003 :                         base_ds->segment_started = GF_FALSE;
    6301             : 
    6302        3003 :                         base_ds->next_seg_start += (u64) (base_ds->dash_dur.num) * base_ds->timescale / base_ds->dash_dur.den;
    6303        6188 :                         while (base_ds->next_seg_start <= base_ds->adjusted_next_seg_start) {
    6304         182 :                                 base_ds->next_seg_start += (u64) (base_ds->dash_dur.num) * base_ds->timescale / base_ds->dash_dur.den;
    6305         182 :                                 if (ctx->skip_seg)
    6306           0 :                                         base_ds->seg_number++;
    6307             :                         }
    6308        3003 :                         base_ds->adjusted_next_seg_start = base_ds->next_seg_start;
    6309        3003 :                         base_ds->seg_number++;
    6310             :                 }
    6311             :         }
    6312             : 
    6313        2933 :         if (ds_log) {
    6314        2932 :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[Dasher] Rep#%s flush seg %d start %g duration %g next seg end time %g\n", ds_log->rep->id, ds_log->seg_number-1, ((Double)first_cts_in_cur_seg)/ds_log->timescale, ((Double)seg_dur_ms)/1000, ((Double)ds_log->adjusted_next_seg_start)/ds_log->timescale));
    6315             :         }
    6316             : 
    6317             :         //muxed representation with unaligned duration,
    6318        2933 :         if (has_ds_done) {
    6319          17 :                 for (i=0; i<count; i++) {
    6320          17 :                         ds = gf_list_get(ctx->current_period->streams, i);
    6321             :                         //otherwise reset only media components for this rep
    6322          17 :                         if ((ds->muxed_base != base_ds) && (ds != base_ds)) continue;
    6323             : 
    6324          17 :                         if (ds->done && (base_ds->nb_comp_done < base_ds->nb_comp)) {
    6325           0 :                                 base_ds->nb_comp_done++;
    6326             :                         }
    6327             :                 }
    6328             :         }
    6329             : 
    6330             :         //some reps are done, other not, force a max time on all AS in the period
    6331        2933 :         if (ds_done && ds_not_done) {
    6332         580 :                 for (i=0; i<count; i++) {
    6333         580 :                         ds = gf_list_get(ctx->current_period->streams, i);
    6334             : 
    6335         580 :                         if (ds->done) {
    6336         515 :                                 if (ds->set->udta == set_ds)
    6337          29 :                                         set_ds->nb_rep_done++;
    6338          65 :                         } else if (ctx->check_dur && !ds->force_rep_end) {
    6339           0 :                                 ds->force_rep_end = ds_done->first_cts_in_next_seg * ds->timescale / ds_done->timescale;
    6340             :                         }
    6341             :                 }
    6342             :         }
    6343             : }
    6344             : 
    6345          31 : static char *dasher_strip_base(GF_DasherCtx *ctx, char *url)
    6346             : {
    6347          31 :         char *manifest_path = ctx->out_path;
    6348             :         char *file_path = url;
    6349             :         char *res = url;
    6350             : 
    6351          31 :         if (!manifest_path || !url) return NULL;
    6352             :         
    6353          31 :         if (!strncmp(manifest_path, "./", 2)) manifest_path+=2;
    6354          31 :         if (!strncmp(file_path, "./", 2)) file_path+=2;
    6355             : 
    6356          31 :         const char *base_manifest = gf_file_basename(manifest_path);
    6357          31 :         u32 diff = (u32) (base_manifest - manifest_path);
    6358          31 :         if (!strncmp(file_path, manifest_path, diff)) {
    6359          31 :                 res = file_path + diff;
    6360             :         }
    6361             :         return res;
    6362             : }
    6363             : 
    6364             : static GFINLINE
    6365             : u64 dasher_translate_cts(GF_DashStream *ds, u64 cts)
    6366             : {
    6367      134282 :         if (ds->cues) {
    6368        6247 :                 cts -= ds->first_cts;
    6369      128035 :         } else if (cts < ds->first_dts) {
    6370             :                 cts = 0;
    6371      128035 :         } else if (ds->pts_minus_cts<0) {
    6372        9280 :                 if ((s64) (cts - ds->first_dts) >= -ds->pts_minus_cts) {
    6373        9278 :                         cts = cts - ds->first_dts + ds->pts_minus_cts;
    6374             :                 } else {
    6375             :                         cts = 0;
    6376             :                 }
    6377             :         } else {
    6378      118755 :                 cts -= ds->first_cts;
    6379             :         }
    6380             :         return cts;
    6381             : }
    6382             : 
    6383        3490 : static void dasher_mark_segment_start(GF_DasherCtx *ctx, GF_DashStream *ds, GF_FilterPacket *pck, GF_FilterPacket *in_pck)
    6384             : {
    6385             :         GF_DASH_SegmentContext *seg_state=NULL;
    6386             :         char szSegmentName[GF_MAX_PATH], szSegmentFullPath[GF_MAX_PATH], szIndexName[GF_MAX_PATH];
    6387        3490 :         GF_DashStream *base_ds = ds->muxed_base ? ds->muxed_base : ds;
    6388             : 
    6389        3490 :         if (ctx->forward_mode) {
    6390             :                 const GF_PropertyValue *p_fname, *p_manifest;
    6391             : 
    6392          54 :                 p_fname = gf_filter_pck_get_property(pck, GF_PROP_PCK_FILENAME);
    6393          54 :                 p_manifest = gf_filter_pck_get_property(pck, GF_PROP_PCK_FILENUM);
    6394          54 :                 if (!p_fname || !p_fname->value.string || !p_manifest) {
    6395           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Couldn't fetch source URL / idx of segment in forward mode, cannot forward\n"));
    6396           0 :                         ctx->in_error = GF_TRUE;
    6397         830 :                         return;
    6398             :                 }
    6399             :                 strcpy(szSegmentName, p_fname->value.string);
    6400             :                 //remove filename property
    6401          54 :                 gf_filter_pck_set_property(pck, GF_PROP_PCK_FILENAME, NULL);
    6402             : 
    6403             :                 //check for manifest update
    6404          54 :                 p_manifest = gf_filter_pck_get_property(in_pck, GF_PROP_PCK_DASH_MANIFEST);
    6405          54 :                 if (p_manifest) {
    6406           3 :                         if (p_manifest->value.string) {
    6407           3 :                                 if (strstr(p_manifest->value.string, "<MPD")) {
    6408           2 :                                         if (ctx->do_m3u8) {
    6409           0 :                                                 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Manifest forward mode got DASH MPD but output is HLS M3U8, cannot operate - change formats or dasher forward mode\n"));
    6410           0 :                                                 ctx->in_error = GF_TRUE;
    6411           0 :                                                 return;
    6412             :                                         } else {
    6413           2 :                                                 dasher_forward_mpd(ctx, p_manifest->value.string);
    6414             :                                         }
    6415             :                                 } else {
    6416           1 :                                         if (ctx->do_mpd) {
    6417           0 :                                                 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Manifest forward mode got M3U6 but output is DASH MPD, cannot operate - change formats or dasher forward mode\n"));
    6418           0 :                                                 ctx->in_error = GF_TRUE;
    6419           0 :                                                 return;
    6420             :                                         } else {
    6421           1 :                                                 dasher_forward_manifest_raw(ctx, ds, p_manifest->value.string, NULL);
    6422             :                                         }
    6423             :                                 }
    6424             :                         }
    6425           3 :                         gf_filter_pck_set_property(pck, GF_PROP_PCK_DASH_MANIFEST, NULL);
    6426             :                 }
    6427             : 
    6428             :                 //check for HLS child playlist update
    6429          54 :                 p_manifest = gf_filter_pck_get_property(in_pck, GF_PROP_PCK_HLS_VARIANT);
    6430          54 :                 p_fname = gf_filter_pck_get_property(in_pck, GF_PROP_PCK_HLS_VARIANT_NAME);
    6431          54 :                 if (p_manifest && p_fname && (p_fname->value.string_list.nb_items==p_manifest->value.string_list.nb_items)) {
    6432             :                         u32 i, count = p_fname->value.string_list.nb_items;
    6433           3 :                         for (i=0; i<count; i++)
    6434           3 :                                 dasher_forward_manifest_raw(ctx, ds, p_manifest->value.string_list.vals[i], p_fname->value.string_list.vals[i]);
    6435             :                 }
    6436          54 :                 if (p_manifest)
    6437           1 :                         gf_filter_pck_set_property(pck, GF_PROP_PCK_HLS_VARIANT, NULL);
    6438          54 :                 if (p_fname)
    6439           1 :                         gf_filter_pck_set_property(pck, GF_PROP_PCK_HLS_VARIANT_NAME, NULL);
    6440             : 
    6441             :                 //we need to move from segment name to output name
    6442          54 :                 if (ctx->forward_mode==DASHER_FWD_ALL)
    6443             :                         goto send_packet;
    6444             : 
    6445             :         }
    6446        3472 :         if (pck) {
    6447        3400 :                 if (ctx->ntp==DASHER_NTP_YES) {
    6448           0 :                         u64 ntpts = gf_net_get_ntp_ts();
    6449           0 :                         gf_filter_pck_set_property(pck, GF_PROP_PCK_SENDER_NTP, &PROP_LONGUINT(ntpts));
    6450        3400 :                 } else if (ctx->ntp==DASHER_NTP_REM) {
    6451        3400 :                         gf_filter_pck_set_property(pck, GF_PROP_PCK_SENDER_NTP, NULL);
    6452             :                 }
    6453             : 
    6454        3400 :                 gf_filter_pck_set_property(pck, GF_PROP_PCK_FILENUM, &PROP_UINT(base_ds->seg_number ) );
    6455             :         }
    6456             : 
    6457             :         //only signal file name & insert timelines on one stream for muxed representations
    6458        3472 :         if (ds->muxed_base) return;
    6459             : 
    6460        3315 :         ds->seg_start_time = ds->first_cts_in_seg;
    6461        3315 :         if (ds->timescale != ds->mpd_timescale) {
    6462           0 :                 ds->seg_start_time *= ds->mpd_timescale;
    6463           0 :                 ds->seg_start_time /= ds->timescale;
    6464             :         }
    6465             : 
    6466        3315 :         if (ctx->store_seg_states) {
    6467             :                 char *kms_uri;
    6468             :                 const GF_PropertyValue *p;
    6469         528 :                 if (!ds->rep->state_seg_list) {
    6470          71 :                         ds->rep->state_seg_list = gf_list_new();
    6471             :                 }
    6472         528 :                 if (!ds->rep->dash_dur.num) {
    6473          85 :                         ds->rep->timescale = ds->timescale;
    6474          85 :                         ds->rep->streamtype = ds->stream_type;
    6475          85 :                         ds->rep->timescale_mpd = ds->mpd_timescale;
    6476          85 :                         p = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_HLS_GROUPID);
    6477          85 :                         if (p)
    6478           2 :                                 ds->rep->groupID = p->value.string;
    6479             : 
    6480          85 :                         ds->rep->dash_dur = ds->dash_dur;
    6481          85 :                         if (ctx->sigfrag) {
    6482           2 :                                 const GF_PropertyValue *pid_url = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_URL);
    6483           2 :                                 if (pid_url && pid_url->value.string) {
    6484           2 :                                         ds->rep->hls_single_file_name = dasher_strip_base(ctx, pid_url->value.string);
    6485             :                                 }
    6486             :                         }
    6487             : 
    6488          85 :                         if (!ds->rep->hls_single_file_name && !ctx->m2ts) {
    6489          80 :                                 switch (ctx->muxtype) {
    6490             :                                 case DASHER_MUX_TS:
    6491             :                                 case DASHER_MUX_OGG:
    6492             :                                 case DASHER_MUX_RAW:
    6493             :                                         break;
    6494          80 :                                 default:
    6495          80 :                                         if (ds->set->bitstream_switching && ds->set->segment_template)
    6496           4 :                                                 ds->rep->hls_single_file_name = ds->set->segment_template->hls_init_name;
    6497             :                                         else
    6498          76 :                                                 ds->rep->hls_single_file_name = ds->init_seg;
    6499             :                                 }
    6500             :                         }
    6501          85 :                         ds->rep->nb_chan = ds->nb_ch;
    6502          85 :                         ds->rep->m3u8_name = ds->hls_vp_name;
    6503          85 :                         if (ds->fps.den) {
    6504          48 :                                 ds->rep->fps = ds->fps.num;
    6505          48 :                                 ds->rep->fps /= ds->fps.den;
    6506             :                         }
    6507             :                 }
    6508         528 :                 GF_SAFEALLOC(seg_state, GF_DASH_SegmentContext);
    6509         528 :                 if (!seg_state) return;
    6510         528 :                 seg_state->time = ds->seg_start_time;
    6511         528 :                 seg_state->seg_num = ds->seg_number;
    6512         528 :                 seg_state->llhls_mode = ctx->llhls;
    6513         528 :                 ds->current_seg_state = seg_state;
    6514         528 :                 seg_state->encrypted = GF_FALSE;
    6515             : 
    6516         528 :                 p = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_HLS_KMS);
    6517         528 :                 kms_uri = (p && p->value.string) ? p->value.string : NULL;
    6518         528 :                 if (ds->tci) {
    6519             :                         u32 s;
    6520           0 :                         ds->iv_low++;
    6521           0 :                         if (ds->iv_low == 0)
    6522           0 :                                 ds->iv_high++;
    6523           0 :                         for (s=0; s<8; s++)
    6524           0 :                                 seg_state->hls_iv[s] = (ds->iv_high >> 8*(7-s) ) & 0xFF;
    6525           0 :                         for (s=0; s<8; s++)
    6526           0 :                                 seg_state->hls_iv[s+8] = (ds->iv_low >> 8*(7-s) ) & 0xFF;
    6527             : 
    6528           0 :                         seg_state->encrypted = GF_TRUE;
    6529           0 :                         gf_cryptfout_push_key(ds->dst_filter, & ds->tci->keys[ds->key_idx].key, &seg_state->hls_iv);
    6530             : 
    6531           0 :                         if (ds->tci->keys[ds->key_idx].hls_info)
    6532             :                                 kms_uri = ds->tci->keys[ds->key_idx].hls_info;
    6533             : 
    6534           0 :                         ds->nb_crypt_seg++;
    6535           0 :                         if (ds->tci->keyRoll) {
    6536           0 :                                 if (ds->nb_crypt_seg == ds->tci->keyRoll) {
    6537           0 :                                         ds->nb_crypt_seg = 0;
    6538           0 :                                         ds->key_idx = (ds->key_idx+1) % ds->tci->nb_keys;
    6539             :                                 }
    6540             :                         }
    6541             :                 }
    6542             :                 //we need a hard copy as the pid may reconfigure before we flush the segment
    6543         528 :                 if (kms_uri)
    6544          24 :                         seg_state->hls_key_uri = gf_strdup(kms_uri);
    6545             : 
    6546         528 :                 gf_list_add(ds->rep->state_seg_list, seg_state);
    6547         528 :                 if (ctx->sigfrag) {
    6548          24 :                         const GF_PropertyValue *frag_range = gf_filter_pck_get_property(in_pck, GF_PROP_PCK_FRAG_RANGE);
    6549          24 :                         const GF_PropertyValue *frag_url = gf_filter_pck_get_property(in_pck, GF_PROP_PID_URL);
    6550          33 :                         if (frag_url && frag_url->value.string) {
    6551           9 :                                 char *f_url = dasher_strip_base(ctx, frag_url->value.string);
    6552           9 :                                 seg_state->filename = gf_strdup(f_url);
    6553             :                         }
    6554          15 :                         else if (frag_range) {
    6555          15 :                                 seg_state->file_offset = frag_range->value.lfrac.num;
    6556          15 :                                 seg_state->file_size = (u32) (frag_range->value.lfrac.den - seg_state->file_offset);
    6557             : 
    6558          15 :                                 if (ds->rep->segment_base && !ds->rep->segment_base->initialization_segment) {
    6559             :                                         GF_MPD_URL *url;
    6560           0 :                                         GF_SAFEALLOC(url, GF_MPD_URL);
    6561           0 :                                         if (url) {
    6562           0 :                                                 GF_SAFEALLOC(url->byte_range, GF_MPD_ByteRange);
    6563           0 :                                                 if (url->byte_range) {
    6564           0 :                                                         url->byte_range->start_range = 0;
    6565           0 :                                                         url->byte_range->end_range = seg_state->file_offset-1;
    6566             :                                                 }
    6567             :                                         }
    6568           0 :                                         ds->rep->segment_base->initialization_segment = url;
    6569             :                                 }
    6570             :                         } else {
    6571           0 :                                 gf_list_del_item(ds->rep->state_seg_list, seg_state);
    6572           0 :                                 gf_free(seg_state);
    6573           0 :                                 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Manifest generation only but not fragment information in packet, source demux not properly configured\n"));
    6574           0 :                                 ctx->in_error = GF_TRUE;
    6575             :                         }
    6576             :                 } else {
    6577         504 :                         gf_list_add(ds->pending_segment_states, seg_state);
    6578         504 :                         ctx->nb_seg_url_pending++;
    6579             :                 }
    6580             :         }
    6581             : 
    6582        3315 :         szIndexName[0] = 0;
    6583        3315 :         if (ds->idx_template) {
    6584             :                 //get final segment template - output file name is NULL, we already have solved this in source_setup
    6585           8 :                 gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_REPINDEX, ds->set->bitstream_switching, szIndexName, base_ds->rep_id, NULL, base_ds->idx_template, NULL, base_ds->seg_start_time, base_ds->rep->bandwidth, base_ds->seg_number, ctx->stl);
    6586             : 
    6587             :                 strcpy(szSegmentFullPath, szIndexName);
    6588           8 :                 if (ctx->out_path) {
    6589           8 :                         char *rel = gf_url_concatenate(ctx->out_path, szIndexName);
    6590           8 :                         if (rel) {
    6591             :                                 strcpy(szSegmentFullPath, rel);
    6592           8 :                                 gf_free(rel);
    6593             :                         }
    6594             :                 }
    6595           8 :                 if (pck)
    6596           8 :                         gf_filter_pck_set_property(pck, GF_PROP_PCK_IDXFILENAME, &PROP_STRING(szSegmentFullPath) );
    6597             :         }
    6598             : 
    6599        3315 :         if (ctx->sseg) {
    6600         324 :                 if (ctx->sigfrag) {
    6601          15 :                         const GF_PropertyValue *p = gf_filter_pck_get_property(in_pck, GF_PROP_PCK_SIDX_RANGE);
    6602          15 :                         if (p) {
    6603           1 :                                 if (ds->rep->segment_base && !ds->rep->segment_base->index_range) {
    6604           1 :                                         GF_SAFEALLOC(ds->rep->segment_base->index_range, GF_MPD_ByteRange);
    6605           1 :                                         if (ds->rep->segment_base->index_range) {
    6606           1 :                                                 ds->rep->segment_base->index_range->start_range = p->value.lfrac.num;
    6607           1 :                                                 ds->rep->segment_base->index_range->end_range = p->value.lfrac.den;
    6608           1 :                                                 ds->rep->segment_base->index_range_exact = GF_TRUE;
    6609             :                                         }
    6610             : 
    6611           1 :                                         if (!ds->rep->segment_base->initialization_segment) {
    6612           1 :                                                 GF_SAFEALLOC(ds->rep->segment_base->initialization_segment, GF_MPD_URL);
    6613             :                                         }
    6614           1 :                                         if (ds->rep->segment_base->initialization_segment && !ds->rep->segment_base->initialization_segment->byte_range) {
    6615           1 :                                                 GF_SAFEALLOC(ds->rep->segment_base->initialization_segment->byte_range, GF_MPD_ByteRange);
    6616           1 :                                                 if (ds->rep->segment_base->initialization_segment->byte_range) {
    6617           1 :                                                         ds->rep->segment_base->initialization_segment->byte_range->start_range = 0;
    6618           1 :                                                         ds->rep->segment_base->initialization_segment->byte_range->end_range = p->value.lfrac.num-1;
    6619             :                                                 }
    6620             :                                         }
    6621             :                                 } else {
    6622           0 :                                         ctx->in_error = GF_TRUE;
    6623           0 :                                         GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Several SIDX found but trying to regenerate an on-demand MPD, source file is not compatible. Try re-dashing the content or use main or full profiles\n"));
    6624             :                                 }
    6625             :                         }
    6626             :                 }
    6627             :                 return;
    6628             :         }
    6629             : 
    6630        2991 :         if (ctx->sfile) {
    6631             :                 GF_MPD_SegmentURL *seg_url;
    6632             :                 assert(ds->rep->segment_list);
    6633         349 :                 GF_SAFEALLOC(seg_url, GF_MPD_SegmentURL);
    6634         349 :                 if (!seg_url) return;
    6635             : 
    6636         349 :                 gf_list_add(ds->rep->segment_list->segment_URLs, seg_url);
    6637         349 :                 if (szIndexName[0])
    6638           0 :                         seg_url->index = gf_strdup(szIndexName);
    6639             : 
    6640         349 :                 if (ctx->sigfrag) {
    6641          48 :                         const GF_PropertyValue *frag_range = gf_filter_pck_get_property(in_pck, GF_PROP_PCK_FRAG_RANGE);
    6642          48 :                         const GF_PropertyValue *frag_url = gf_filter_pck_get_property(in_pck, GF_PROP_PID_URL);
    6643          48 :                         if (frag_url && frag_url->value.string) {
    6644          18 :                                 seg_url->media = gf_strdup(dasher_strip_base(ctx, frag_url->value.string));
    6645          18 :                                 if (ds->rep->segment_list && ds->rep->segment_list->initialization_segment && !ds->rep->segment_list->initialization_segment->sourceURL) {
    6646             : 
    6647           2 :                                         frag_url = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_URL);
    6648           2 :                                         if (frag_url && frag_url->value.string) {
    6649             :                                                 u32 j, nb_base;
    6650           2 :                                                 ds->rep->segment_list->initialization_segment->sourceURL = gf_strdup(dasher_strip_base(ctx, frag_url->value.string) );
    6651             : 
    6652           2 :                                                 nb_base = gf_list_count(ds->rep->base_URLs);
    6653           4 :                                                 for (j=0; j<nb_base; j++) {
    6654           2 :                                                         GF_MPD_BaseURL *burl = gf_list_get(ds->rep->base_URLs, j);
    6655           2 :                                                         if (! strcmp(burl->URL, frag_url->value.string)) {
    6656           0 :                                                                 gf_list_rem(ds->rep->base_URLs, j);
    6657           0 :                                                                 gf_mpd_base_url_free(burl);
    6658           0 :                                                                 break;
    6659             :                                                         }
    6660             :                                                 }
    6661             :                                         }
    6662             :                                 }
    6663             :                         }
    6664          30 :                         else if (frag_range) {
    6665          30 :                                 GF_SAFEALLOC(seg_url->media_range, GF_MPD_ByteRange);
    6666          30 :                                 if (seg_url->media_range) {
    6667          30 :                                         seg_url->media_range->start_range = frag_range->value.lfrac.num;
    6668          30 :                                         seg_url->media_range->end_range = frag_range->value.lfrac.den - 1;
    6669             :                                 }
    6670          30 :                                 if (ds->rep->segment_list && ds->rep->segment_list->initialization_segment && !ds->rep->segment_list->initialization_segment->byte_range) {
    6671           2 :                                         GF_SAFEALLOC(ds->rep->segment_list->initialization_segment->byte_range, GF_MPD_ByteRange);
    6672           2 :                                         if (ds->rep->segment_list->initialization_segment->byte_range) {
    6673           2 :                                                 ds->rep->segment_list->initialization_segment->byte_range->start_range = 0;
    6674           2 :                                                 ds->rep->segment_list->initialization_segment->byte_range->end_range = frag_range->value.lfrac.num-1;
    6675             :                                         }
    6676             :                                 }
    6677             :                         }
    6678             :                 } else {
    6679         301 :                         gf_list_add(ds->pending_segment_urls, seg_url);
    6680         301 :                         ctx->nb_seg_url_pending++;
    6681             :                 }
    6682             :                 return;
    6683             :         }
    6684             : 
    6685        2642 :         if (!ctx->stl && !ctx->cues && !ctx->forward_mode) {
    6686        2545 :                 Double drift, seg_start = (Double) ds->seg_start_time;
    6687        2545 :                 seg_start /= ds->mpd_timescale;
    6688        2545 :                 drift = seg_start - ((Double)(ds->seg_number - ds->startNumber)) * ds->dash_dur.num / ds->dash_dur.den;
    6689             : 
    6690        2545 :                 if ((ds->dash_dur.num>0) && (ABS(drift) * 2 * ds->dash_dur.den > ds->dash_dur.num)) {
    6691             :                         u64 cts = 0;
    6692          24 :                         if (pck) {
    6693          24 :                                 cts = dasher_translate_cts(ds, gf_filter_pck_get_cts(pck) );
    6694             :                         }
    6695          24 :                         GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] First CTS "LLU" in segment %d drifting by %g (more than half a segment duration) from segment time, consider reencoding or using segment timeline\n", cts, ds->seg_number,  drift));
    6696             :                 }
    6697             :         }
    6698             : 
    6699        2642 :         if (!ctx->forward_mode) {
    6700             :                 //get final segment template - output file name is NULL, we already have solved this in source_setup
    6701        2606 :                 gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_SEGMENT, ds->set->bitstream_switching, szSegmentName, base_ds->rep_id, NULL, base_ds->seg_template, NULL, base_ds->seg_start_time, base_ds->rep->bandwidth, base_ds->seg_number, ctx->stl);
    6702             :         }
    6703             : 
    6704             : 
    6705        2714 : send_packet:
    6706             :         strcpy(szSegmentFullPath, szSegmentName);
    6707             : 
    6708        2660 :         if (ctx->out_path) {
    6709             :                 char *rel = NULL;
    6710        2660 :                 if (ctx->do_m3u8 && ds->hls_vp_name && !ctx->forward_mode) {
    6711           0 :                         char *tmp = gf_url_concatenate(ctx->out_path, ds->hls_vp_name);
    6712           0 :                         if (tmp) {
    6713           0 :                                 rel = gf_url_concatenate(tmp, szSegmentName);
    6714           0 :                                 gf_free(tmp);
    6715             :                         }
    6716             :                 }
    6717           0 :                 if (!rel)
    6718        2660 :                         rel = gf_url_concatenate(ctx->out_path, szSegmentName);
    6719             : 
    6720        2660 :                 if (rel) {
    6721             :                         strcpy(szSegmentFullPath, rel);
    6722        2660 :                         gf_free(rel);
    6723             :                 }
    6724             :         }
    6725             : 
    6726        2660 :         if (seg_state) {
    6727         443 :                 seg_state->filepath = gf_strdup(szSegmentFullPath);
    6728         443 :                 seg_state->filename = gf_strdup(szSegmentName);
    6729             :         }
    6730             : 
    6731        2660 :         if (ds->rep->segment_list && (ctx->forward_mode!=DASHER_FWD_ALL) ) {
    6732             :                 GF_MPD_SegmentURL *seg_url;
    6733          41 :                 GF_SAFEALLOC(seg_url, GF_MPD_SegmentURL);
    6734          41 :                 if (seg_url) {
    6735          41 :                         gf_list_add(ds->rep->segment_list->segment_URLs, seg_url);
    6736          41 :                         seg_url->media = gf_strdup(szSegmentName);
    6737          41 :                         gf_list_add(ds->pending_segment_urls, seg_url);
    6738          41 :                         if (szIndexName[0])
    6739           0 :                                 seg_url->index = gf_strdup(szIndexName);
    6740             :                 }
    6741          41 :                 ctx->nb_seg_url_pending++;
    6742             :         }
    6743        2660 :         if (pck)
    6744        2651 :                 gf_filter_pck_set_property(pck, GF_PROP_PCK_FILENAME, &PROP_STRING(szSegmentFullPath) );
    6745             : }
    6746             : 
    6747         140 : static Bool dasher_check_loop(GF_DasherCtx *ctx, GF_DashStream *ds)
    6748             : {
    6749             :         u32 i, count;
    6750             :         u32 pmode = GF_PLAYBACK_MODE_NONE;
    6751             :         u64 ts_offset, max_ts_offset, max_ts_scale;
    6752             :         const GF_PropertyValue *p;
    6753         140 :         if (!ds->src_url) return GF_FALSE;
    6754             : 
    6755             :         //loop requested
    6756         140 :         if (ds->loop_state==2) return GF_TRUE;
    6757             : 
    6758         140 :         count = gf_list_count(ctx->current_period->streams);
    6759         140 :         if (!ds->loop_state) {
    6760          28 :                 for (i=0; i<count; i++) {
    6761          28 :                         GF_DashStream *a_ds = gf_list_get(ctx->current_period->streams, i);
    6762             : 
    6763          28 :                         p = gf_filter_pid_get_property(a_ds->ipid, GF_PROP_PID_PLAYBACK_MODE);
    6764          28 :                         if (p) pmode = p->value.uint;
    6765          28 :                         if (pmode == GF_PLAYBACK_MODE_NONE) {
    6766           0 :                                 GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Loop requested in subdur mode, but source cannot seek, defaulting to multi period for all streams\n"));
    6767           0 :                                 ctx->loop = GF_FALSE;
    6768           0 :                                 return GF_FALSE;
    6769             :                         }
    6770             :                 }
    6771          14 :                 ds->loop_state = 1;
    6772             :         }
    6773             : 
    6774             :         max_ts_offset = 0;
    6775             :         max_ts_scale = 1;
    6776             :         //check all input media duration
    6777         110 :         for (i=0; i<count; i++) {
    6778         243 :                 GF_DashStream *a_ds = gf_list_get(ctx->current_period->streams, i);
    6779             : 
    6780             :                 //one pid is waiting for loop while another has done its subdur and won't process any new segment until the next subdur call, which
    6781             :                 //will never happen since the first PID waits for loop. We must force early generation in this case
    6782         243 :                 if (a_ds->subdur_done) {
    6783           3 :                         a_ds->subdur_done = GF_FALSE;
    6784             :                         //remember the max period dur before this forced segment generation
    6785           3 :                         a_ds->subdur_forced_use_period_dur = a_ds->max_period_dur;
    6786             :                 }
    6787             : 
    6788             :                 //wait for each input to query loop
    6789         243 :                 if (!a_ds->loop_state) {
    6790         133 :                         a_ds->done = 0;
    6791         133 :                         return GF_TRUE;
    6792             :                 }
    6793             : 
    6794             :                 //get max duration
    6795         110 :                 ts_offset = a_ds->est_next_dts;
    6796             : 
    6797         110 :                 if (max_ts_offset * a_ds->timescale < ts_offset * max_ts_scale) {
    6798             :                         max_ts_offset = ts_offset;
    6799             :                         max_ts_scale = a_ds->timescale;
    6800             :                 }
    6801             :         }
    6802             : 
    6803             :         //assign ts offset and send stop/play
    6804          14 :         for (i=0; i<count; i++) {
    6805          14 :                 GF_DashStream *a_ds = gf_list_get(ctx->current_period->streams, i);
    6806             : 
    6807          14 :                 if (a_ds->subdur_done)
    6808           0 :                         continue;
    6809             : 
    6810             :                 ts_offset = max_ts_offset;
    6811          14 :                 ts_offset *= a_ds->timescale;
    6812          14 :                 ts_offset /= max_ts_scale;
    6813             : 
    6814          14 :                 a_ds->ts_offset = ts_offset;
    6815          14 :                 if (a_ds->done) continue;
    6816          13 :                 if (a_ds->ts_offset > a_ds->est_next_dts) {
    6817           0 :                         GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Looping streams of unequal duration, inserting "LLU" us of timestamp delay in pid %s from %s\n", ((a_ds->ts_offset - a_ds->est_next_dts) * 1000000) / a_ds->timescale, gf_filter_pid_get_name(a_ds->ipid), a_ds->src_url));
    6818             :                 }
    6819             : 
    6820          13 :                 a_ds->seek_to_pck = 0;
    6821          13 :                 a_ds->nb_pck = 0;
    6822          13 :                 a_ds->clamp_done = GF_FALSE;
    6823             : 
    6824          13 :                 a_ds->loop_state = 2;
    6825             : 
    6826          13 :                 if (ctx->subdur) {
    6827             :                         GF_FilterEvent evt;
    6828             : 
    6829          12 :                         GF_FEVT_INIT(evt, GF_FEVT_STOP, a_ds->ipid);
    6830          12 :                         gf_filter_pid_send_event(a_ds->ipid, &evt);
    6831             : 
    6832          12 :                         gf_filter_pid_set_discard(a_ds->ipid, GF_FALSE);
    6833             : 
    6834          12 :                         dasher_send_encode_hints(ctx, ds);
    6835             : 
    6836          12 :                         GF_FEVT_INIT(evt, GF_FEVT_PLAY, a_ds->ipid);
    6837          12 :                         evt.play.speed = 1.0;
    6838          12 :                         gf_filter_pid_send_event(a_ds->ipid, &evt);
    6839             :                 }
    6840             :         }
    6841             : 
    6842             :         return GF_TRUE;
    6843             : }
    6844             : 
    6845             : //depending on input formats, streams may be declared with or without DCD. For streams requiring the config, wait for it
    6846         100 : static Bool dasher_check_period_ready(GF_DasherCtx *ctx, Bool is_session_end)
    6847             : {
    6848         100 :         u32 i=0;
    6849             :         GF_DashStream *ds;
    6850         100 :         ctx->period_not_ready = GF_FALSE;
    6851         286 :         while ((ds = gf_list_enum(ctx->current_period->streams, &i))) {
    6852             : 
    6853         181 :                 if (is_session_end)
    6854           0 :                         gf_filter_pid_set_discard(ds->ipid, GF_TRUE);
    6855             : 
    6856         181 :                 if (ds->dcd_not_ready) {
    6857             :                         GF_FilterPacket *pck;
    6858             :                         u32 prev = ds->dcd_not_ready;
    6859         104 :                         ds->dcd_not_ready = 0;
    6860         104 :                         pck = gf_filter_pid_get_packet(ds->ipid);
    6861         104 :                         if (!pck) {
    6862          95 :                                 u32 diff = gf_sys_clock() - prev;
    6863          95 :                                 if (diff > 10000) {
    6864           0 :                                         GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Failed to initialize PID %s, no packets after %d ms, aborting\n", gf_filter_pid_get_name(ds->ipid), diff));
    6865           0 :                                         ctx->in_error = GF_TRUE;
    6866           0 :                                         return GF_FALSE;
    6867             :                                 }
    6868          95 :                                 ds->dcd_not_ready = prev;
    6869          95 :                                 ctx->period_not_ready = GF_TRUE;
    6870          95 :                                 return GF_FALSE;
    6871             :                         }
    6872             :                 }
    6873             :         }
    6874             :         return GF_TRUE;
    6875             : }
    6876             : 
    6877     5720281 : void dasher_format_report(GF_Filter *filter, GF_DasherCtx *ctx)
    6878             : {
    6879             :         u32 i, count;
    6880             :         Double max_ts=0;
    6881             :         u32 total_pc = 0;
    6882             :         char szDS[200];
    6883     5720281 :         char *szStatus = NULL;
    6884             : 
    6885     5720281 :         if (!gf_filter_reporting_enabled(filter))
    6886     5720281 :                 return;
    6887           0 :         if (!ctx->update_report)
    6888             :                 return;
    6889             :         //don't update at each packet, this would be too much
    6890           0 :         if ((ctx->update_report>0) && (ctx->update_report < 20))
    6891             :                 return;
    6892             : 
    6893           0 :         ctx->update_report = 0;
    6894             : 
    6895           0 :         sprintf(szDS, "P%s", ctx->current_period->period->ID ? ctx->current_period->period->ID : "1");
    6896           0 :         gf_dynstrcat(&szStatus, szDS, NULL);
    6897             : 
    6898           0 :         count = gf_list_count(ctx->current_period->streams);
    6899           0 :         for (i=0; i<count; i++) {
    6900             :                 s32 pc=-1;
    6901             :                 Double mpdtime;
    6902             :                 u32 set_idx;
    6903             :                 u32 rep_idx;
    6904             :                 u8 stype;
    6905           0 :                 GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
    6906           0 :                 if (ds->muxed_base) continue;
    6907             : 
    6908           0 :                 set_idx = 1 + gf_list_find(ctx->current_period->period->adaptation_sets, ds->set);
    6909           0 :                 rep_idx = 1 + gf_list_find(ds->set->representations, ds->rep);
    6910           0 :                 if (ds->stream_type==GF_STREAM_VISUAL) stype='V';
    6911           0 :                 else if (ds->stream_type==GF_STREAM_AUDIO) stype='A';
    6912           0 :                 else if (ds->stream_type==GF_STREAM_TEXT) stype='T';
    6913             :                 else stype='M';
    6914             : 
    6915           0 :                 if (ds->done || ds->subdur_done) {
    6916           0 :                         sprintf(szDS, "AS#%d.%d(%c) done (%d segs)", set_idx, rep_idx, stype, ds->seg_number);
    6917           0 :                         pc = 10000;
    6918             :                 } else {
    6919             :                         Double done;
    6920           0 :                         if (ctx->cues) {
    6921           0 :                                 done = (Double) (ds->last_dts);
    6922           0 :                                 done /= ds->timescale;
    6923           0 :                                 snprintf(szDS, 200, "AS#%d.%d(%c) seg #%d %.2fs", set_idx, rep_idx, stype, ds->seg_number, done);
    6924             :                         } else {
    6925             :                                 Double pcent, ddur;
    6926           0 :                                 done = (Double) ds->adjusted_next_seg_start;
    6927           0 :                                 done -= (Double) ds->last_dts;
    6928           0 :                                 if (done<0)
    6929             :                                         done=0;
    6930           0 :                                 done /= ds->timescale;
    6931           0 :                                 ddur = ((Double)ds->dash_dur.num) / ds->dash_dur.den;
    6932           0 :                                 done = ddur - done;
    6933             :                                 //this may happen since we don't print info at segment start
    6934           0 :                                 if (done<0)
    6935             :                                         done=0;
    6936           0 :                                 pcent = done / ddur;
    6937           0 :                                 pc = (s32) (done * 10000);
    6938           0 :                                 snprintf(szDS, 200, "AS#%d.%d(%c) seg #%d %.2fs (%.2f %%)", set_idx, rep_idx, stype, ds->seg_number, done, 100*pcent);
    6939             :                         }
    6940             : 
    6941           0 :                         mpdtime = (Double) ds->last_dts;
    6942           0 :                         mpdtime -= (Double) ds->first_dts;
    6943           0 :                         if (mpdtime<0) mpdtime=0;
    6944           0 :                         mpdtime /= ds->timescale;
    6945             : 
    6946           0 :                         if (ds->duration.den && ds->duration.num) {
    6947             :                                 done = mpdtime;
    6948             : 
    6949           0 :                                 done *= ds->duration.den;
    6950           0 :                                 done /= ds->duration.num;
    6951           0 :                                 pc = (u32) (10000*done);
    6952             :                         }
    6953           0 :                         if (max_ts<mpdtime)
    6954             :                                 max_ts = mpdtime;
    6955             :                 }
    6956           0 :                 if (pc > (s32) total_pc) total_pc = (u32) pc;
    6957           0 :                 gf_dynstrcat(&szStatus, szDS, " ");
    6958             :         }
    6959           0 :         if (total_pc!=10000) {
    6960           0 :                 sprintf(szDS, " / MPD %.2fs %d %%", max_ts, total_pc/100);
    6961           0 :                 gf_dynstrcat(&szStatus, szDS, NULL);
    6962             :         }
    6963           0 :         gf_filter_update_status(filter, total_pc, szStatus);
    6964           0 :         gf_free(szStatus);
    6965             : }
    6966             : 
    6967      123044 : static void dasher_drop_input(GF_DasherCtx *ctx, GF_DashStream *ds, Bool discard_all)
    6968             : {
    6969      123044 :         if (ds->sbound) {
    6970        1152 :                 while (gf_list_count(ds->packet_queue)) {
    6971        1143 :                         GF_FilterPacket *pck = gf_list_pop_front(ds->packet_queue);
    6972        1143 :                         if (gf_filter_pck_get_sap(pck)) {
    6973             :                                 assert(ds->nb_sap_in_queue);
    6974         699 :                                 ds->nb_sap_in_queue --;
    6975             :                         }
    6976        1143 :                         gf_filter_pck_unref(pck);
    6977        1143 :                         if (!discard_all) break;
    6978             :                 }
    6979             :         } else {
    6980      121978 :                 gf_filter_pid_drop_packet(ds->ipid);
    6981             :         }
    6982      123044 :         if (discard_all) {
    6983          82 :                 gf_filter_pid_set_discard(ds->ipid, GF_TRUE);
    6984             :         }
    6985      123044 : }
    6986             : 
    6987             : 
    6988             : 
    6989     5730092 : static GF_Err dasher_process(GF_Filter *filter)
    6990             : {
    6991             :         u32 i, count, nb_init, has_init, nb_reg_done;
    6992     5730092 :         GF_DasherCtx *ctx = gf_filter_get_udta(filter);
    6993             :         GF_Err e;
    6994             :         Bool seg_done = GF_FALSE;
    6995             : 
    6996     5730092 :         if (ctx->in_error) {
    6997           0 :                 gf_filter_abort(filter);
    6998           0 :                 return GF_SERVICE_ERROR;
    6999             :         }
    7000             : 
    7001             :         //session regulation is on and we have a an MPD (setup done) and a next time (first seg processed)
    7002             :         //check if we have reached the next time
    7003     5730092 :         if (ctx->sreg && !ctx->state && ctx->mpd && ctx->mpd->gpac_next_ntp_ms) {
    7004           0 :                 s64 diff = (s64) ctx->mpd->gpac_next_ntp_ms;
    7005           0 :                 diff -= (s64) gf_net_get_ntp_ms();
    7006           0 :                 if (diff>100) {
    7007           0 :                         GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[Dasher] Next generation scheduled in %d ms, nothing to do\n", diff));
    7008           0 :                         gf_filter_ask_rt_reschedule(filter, (u32) (diff*1000));
    7009           0 :                         return GF_OK;
    7010             :                 }
    7011             :         }
    7012             : 
    7013             :         //streams in period are not all ready, wait for them
    7014     5730092 :         if (ctx->period_not_ready) {
    7015         100 :                 Bool is_eos = gf_filter_end_of_session(filter);
    7016         100 :                 if (! dasher_check_period_ready(ctx, is_eos)) {
    7017          95 :                         return is_eos ? GF_SERVICE_ERROR : GF_OK;
    7018             :                 }
    7019           5 :                 e = dasher_setup_period(filter, ctx, NULL);
    7020           5 :                 if (e) return e;
    7021             :         }
    7022     5729997 :         if (ctx->check_connections) {
    7023         925 :                 if (gf_filter_connections_pending(filter))
    7024             :                         return GF_OK;
    7025         670 :                 ctx->check_connections = GF_FALSE;
    7026             :         }
    7027             : 
    7028     5729742 :         if (ctx->is_eos)
    7029             :                 return GF_EOS;
    7030     5720283 :         if (ctx->setup_failure) return ctx->setup_failure;
    7031             : 
    7032             :         nb_init = has_init = nb_reg_done = 0;
    7033             : 
    7034     5720283 :         count = gf_list_count(ctx->current_period->streams);
    7035    11538225 :         for (i=0; i<count; i++) {
    7036             :                 GF_DashStream *base_ds;
    7037     5817944 :                 GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
    7038             :                 assert(ds);
    7039     5817944 :                 if (ds->done) continue;
    7040     5726729 :                 base_ds = ds->muxed_base ? ds->muxed_base : ds;
    7041             :                 //subdur mode abort, don't process
    7042     5726729 :                 if (ds->subdur_done) {
    7043         250 :                         continue;
    7044             :                 }
    7045     5726479 :                 if (ds->seg_done) continue;
    7046             : 
    7047     5724793 :                 if (ctx->dmode == GF_MPD_TYPE_DYNAMIC_LAST) {
    7048           0 :                         ds->done = 1;
    7049           0 :                         continue;
    7050             :                 }
    7051             : 
    7052             :                 //flush as much as possible
    7053             :                 while (1) {
    7054             :                         u32 sap_type, dur, o_dur, split_dur;
    7055             :                         s32 check_dur;
    7056             :                         u64 cts, orig_cts, dts, split_dur_next, pcont_cts;
    7057             :                         Bool seg_over = GF_FALSE;
    7058             :                         Bool is_packet_split = GF_FALSE;
    7059             :                         Bool is_queue_flush = GF_FALSE;
    7060             :                         GF_FilterPacket *dst;
    7061     5851433 :                         GF_FilterPacket *pck = NULL;
    7062             : 
    7063     5851433 :                         if (!ds->request_period_switch) {
    7064             :                                 assert(ds->period == ctx->current_period);
    7065     5851433 :                                 pck = gf_filter_pid_get_packet(ds->ipid);
    7066             :                                 //we may change period after a packet fetch (reconfigure of input pid)
    7067     5851433 :                                 if ((ds->period != ctx->current_period) || ds->request_period_switch) {
    7068             :                                         //in closest mode, flush queue
    7069           0 :                                         if (!ds->sbound || !gf_list_count(ds->packet_queue)) {
    7070             :                                                 assert(gf_list_find(ctx->current_period->streams, ds)<0);
    7071           0 :                                                 count = gf_list_count(ctx->current_period->streams);
    7072           0 :                                                 i--;
    7073     5724791 :                                                 break;
    7074             :                                         }
    7075             :                                         is_queue_flush = GF_TRUE;
    7076             :                                 }
    7077             :                         } else {
    7078             :                                 is_queue_flush = GF_TRUE;
    7079             :                         }
    7080     5851433 :                         if (ds->sbound && pck && gf_filter_pck_is_blocking_ref(pck)) {
    7081           0 :                                 GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Cannot use `sbound` with blocking input packet references, disabling packet buffering for PID %s\n", gf_filter_pid_get_name(ds->ipid) ));
    7082           0 :                                 ds->sbound = DASHER_BOUNDS_OUT;
    7083             :                         }
    7084             : 
    7085             :                         //skipped merged tile base
    7086     5851433 :                         if (ds->merged_tile_dep) {
    7087         780 :                                 if (pck) gf_filter_pid_drop_packet(ds->ipid);
    7088         780 :                                 pck = NULL;
    7089             :                         }
    7090             :                         //queue mode
    7091     5850653 :                         else if (ds->sbound) {
    7092        2224 :                                 if (!is_queue_flush && pck) {
    7093        1143 :                                         gf_filter_pck_ref(&pck);
    7094        1143 :                                         gf_filter_pid_drop_packet(ds->ipid);
    7095        1143 :                                         gf_list_add(ds->packet_queue, pck);
    7096        1143 :                                         if (gf_filter_pck_get_sap(pck))
    7097         699 :                                                 ds->nb_sap_in_queue ++;
    7098             :                                 }
    7099        2224 :                                 if (
    7100             :                                         //we are flushing due to period switch
    7101             :                                         is_queue_flush
    7102             :                                         //we are flushing due to end of stream
    7103        2224 :                                         || gf_filter_pid_is_eos(ds->ipid) || ds->clamp_done
    7104             :                                 ) {
    7105         246 :                                         pck = gf_list_get(ds->packet_queue, 0);
    7106         246 :                                         is_queue_flush = GF_TRUE;
    7107        1978 :                                 } else if (
    7108             :                                         //if current segment is not started, always get packet from queue
    7109        1978 :                                         !ds->segment_started
    7110             :                                         //wait until we have more than 2 saps to get packet from queue, to check if next sap will be closer or not
    7111        1962 :                                         || (ds->nb_sap_in_queue>=2)
    7112             :                                 ) {
    7113         838 :                                         pck = gf_list_get(ds->packet_queue, 0);
    7114             :                                 } else {
    7115        1140 :                                         pck = NULL;
    7116             :                                 }
    7117             :                         }
    7118             : 
    7119             : 
    7120     5851433 :                         if (!pck) {
    7121     5717672 :                                 if (ds->request_period_switch) {
    7122           0 :                                         e = dasher_stream_period_changed(filter, ctx, ds, (ds->request_period_switch==2) ? GF_TRUE : GF_FALSE);
    7123           0 :                                         if (e < 0) {
    7124           0 :                                                 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Period switch request failed.\n"));
    7125           0 :                                                 i--;
    7126           0 :                                                 break;
    7127             :                                         }
    7128             :                                         assert(gf_list_find(ctx->current_period->streams, ds)<0);
    7129           0 :                                         count = gf_list_count(ctx->current_period->streams);
    7130           0 :                                         i--;
    7131           0 :                                         break;
    7132             :                                 }
    7133             : 
    7134             : 
    7135     5717672 :                                 if (gf_filter_pid_is_eos(ds->ipid) || ds->clamp_done) {
    7136             :                                         u32 ds_done = 1;
    7137         480 :                                         if (ctx->loop && dasher_check_loop(ctx, ds)) {
    7138         140 :                                                 if (ctx->subdur)
    7139             :                                                         break;
    7140             :                                                 //loop on the entire source, consider the stream not done for segment flush
    7141             :                                                 ds_done = 0;
    7142             :                                         }
    7143             : 
    7144         342 :                                         ds->clamp_done = GF_FALSE;
    7145             : 
    7146         342 :                                         ctx->update_report = -1;
    7147             :                                         //opid may be NULL for skipped tile rep
    7148         342 :                                         if (!ctx->sigfrag && ds->opid)
    7149         335 :                                                 gf_filter_pid_set_eos(ds->opid);
    7150             : 
    7151         342 :                                         if (!ds->done) ds->done = ds_done;
    7152         342 :                                         ds->seg_done = GF_TRUE;
    7153             :                                         seg_done = GF_TRUE;
    7154         342 :                                         ds->first_cts_in_next_seg = ds->est_first_cts_in_next_seg;
    7155         342 :                                         ds->est_first_cts_in_next_seg = 0;
    7156             :                                         assert(base_ds->nb_comp_done < base_ds->nb_comp);
    7157         342 :                                         base_ds->nb_comp_done ++;
    7158         342 :                                         if (base_ds->nb_comp_done == base_ds->nb_comp) {
    7159         333 :                                                 dasher_flush_segment(ctx, base_ds, GF_FALSE);
    7160         333 :                                                 base_ds->nb_comp_done = 0;
    7161             :                                         }
    7162             :                                         //loop on the entire source, mark as done for subdur and check if all other streams are done
    7163         342 :                                         if (!ds->done) {
    7164             :                                                 u32 j;
    7165           2 :                                                 ds->done = 2;
    7166           2 :                                                 ds->subdur_done = GF_TRUE;
    7167             :                                                 u32 nb_sub_done=0;
    7168           6 :                                                 for (j=0; j<count; j++) {
    7169           4 :                                                         GF_DashStream *a_ds = gf_list_get(ctx->current_period->streams, j);
    7170           4 :                                                         if (a_ds->muxed_base) a_ds = a_ds->muxed_base;
    7171           4 :                                                         if (a_ds->subdur_done) {
    7172           2 :                                                                 nb_sub_done++;
    7173             :                                                         }
    7174             :                                                 }
    7175           2 :                                                 if (nb_sub_done==count)
    7176           0 :                                                         ctx->subdur_done = GF_TRUE;
    7177         340 :                                         } else if (ctx->reschedule && !ctx->loop && (ctx->dmode==GF_MPD_TYPE_DYNAMIC) && !strcmp(ds->period_id, DEFAULT_PERIOD_ID) ) {
    7178           8 :                                                 if (gf_list_find(ctx->next_period->streams, ds)<0) {
    7179           4 :                                                         gf_list_add(ctx->next_period->streams, ds);
    7180             :                                                 }
    7181           8 :                                                 ctx->post_play_events = GF_TRUE;
    7182           8 :                                                 ds->nb_repeat++;
    7183           8 :                                                 ds->reschedule = GF_TRUE;
    7184           8 :                                                 gf_filter_pid_discard_block(ds->opid);
    7185             :                                         }
    7186             :                                 }
    7187             :                                 break;
    7188             :                         }
    7189      133761 :                         if (ds->seek_to_pck) {
    7190         200 :                                 u32 sn = gf_filter_pck_get_seq_num(pck);
    7191         200 :                                 if (sn) {
    7192         200 :                                         if (sn <= ds->seek_to_pck) {
    7193         186 :                                                 dasher_drop_input(ctx, ds, GF_FALSE);
    7194        4024 :                                                 continue;
    7195             :                                         }
    7196          14 :                                         ds->nb_pck = sn-1;
    7197             :                                 } else {
    7198             :                                         //no sn signaled, this implies we played from the beginning
    7199           0 :                                         if (ds->nb_pck < ds->seek_to_pck) {
    7200           0 :                                                 ds->nb_pck ++;
    7201           0 :                                                 dasher_drop_input(ctx, ds, GF_FALSE);
    7202           0 :                                                 continue;
    7203             :                                         }
    7204             :                                 }
    7205             :                         }
    7206      133575 :                         sap_type = gf_filter_pck_get_sap(pck);
    7207      133575 :                         ds->loop_state = 0;
    7208             : 
    7209      133575 :                         cts = gf_filter_pck_get_cts(pck);
    7210      133575 :                         dts = gf_filter_pck_get_dts(pck);
    7211      133575 :                         if (dts==GF_FILTER_NO_TS) dts = cts;
    7212             : 
    7213             :                         pcont_cts = cts;
    7214             : 
    7215      133575 :                         if (!ds->rep_init) {
    7216             :                                 u32 set_start_with_sap;
    7217         357 :                                 if (!sap_type) {
    7218           0 :                                         dasher_drop_input(ctx, ds, GF_FALSE);
    7219           0 :                                         break;
    7220             :                                 }
    7221             : 
    7222         357 :                                 set_start_with_sap = ctx->sseg ? base_ds->set->subsegment_starts_with_sap : base_ds->set->starts_with_sap;
    7223         357 :                                 if (!ds->muxed_base) {
    7224             :                                         //force sap type to 1 for non-visual streams if strict_sap is set to off
    7225         425 :                                         if ((ds->stream_type!=GF_STREAM_VISUAL) && (ctx->strict_sap==DASHER_SAP_OFF) ) {
    7226          79 :                                                 switch (ds->codec_id) {
    7227             :                                                 //MPEG-H requires saps
    7228             :                                                 case GF_CODECID_MPHA:
    7229             :                                                 case GF_CODECID_MHAS:
    7230             :                                                         break;
    7231          79 :                                                 default:
    7232             :                                                         sap_type = 1;
    7233          79 :                                                         break;
    7234             :                                                 }
    7235         267 :                                         }
    7236             :                                         //set AS sap type
    7237         346 :                                         if (!set_start_with_sap) {
    7238             :                                                 //don't set SAP type if not a base rep - could be further checked
    7239             :                                                 //if (!gf_list_count(ds->complementary_streams) )
    7240             :                                                 {
    7241         317 :                                                         if (ctx->sseg) {
    7242          21 :                                                                 ds->set->subsegment_starts_with_sap = sap_type;
    7243             :                                                         } else {
    7244         296 :                                                                 ds->set->starts_with_sap = sap_type;
    7245             :                                                         }
    7246             :                                                 }
    7247             :                                         }
    7248          29 :                                         else if (set_start_with_sap != sap_type) {
    7249           0 :                                                 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Segments do not start with the same SAP types: set initialized with %d but first packet got %d - bitstream will not be compliant\n", set_start_with_sap, sap_type));
    7250             :                                         }
    7251             :                                         //TODO setup proper PTO, the code below will break sync by realigning first AU of each stream
    7252         346 :                                         if ((s64) cts + ds->pts_minus_cts > 0) {
    7253           3 :                                                 u64 pto = cts + ds->pts_minus_cts;
    7254           3 :                                                 if (ds->rep->segment_list)
    7255           2 :                                                         ds->rep->segment_list->presentation_time_offset = pto;
    7256           1 :                                                 else if (ds->rep->segment_template)
    7257           0 :                                                         ds->rep->segment_template->presentation_time_offset = pto;
    7258           1 :                                                 else if (ds->set->segment_template)
    7259           1 :                                                         ds->set->segment_template->presentation_time_offset = pto;
    7260           0 :                                                 else if (ds->rep->segment_base)
    7261           0 :                                                         ds->rep->segment_base->presentation_time_offset = pto;
    7262             :                                         }
    7263             :                                         //period continuity, skip priming in new periods
    7264         346 :                                         if (ds->period_continuity_id)
    7265           0 :                                                 ds->pts_minus_cts = 0;
    7266             :                                 }
    7267             : 
    7268         357 :                                 ds->first_cts = cts;
    7269         357 :                                 ds->first_dts = dts;
    7270         357 :                                 ds->rep_init++;
    7271         357 :                                 has_init++;
    7272             :                         }
    7273             : 
    7274      133575 :                         nb_init++;
    7275             : 
    7276      133575 :                         if (ds->ts_offset) {
    7277         579 :                                 cts += ds->ts_offset;
    7278         579 :                                 dts += ds->ts_offset;
    7279             :                         }
    7280             : 
    7281             :                         //ready to write MPD for the first time in dynamic mode with template
    7282      133575 :                         if (has_init && (nb_init==count) && (ctx->dmode==GF_MPD_TYPE_DYNAMIC) && ctx->tpl && ctx->do_mpd && !ctx->dyn_rate) {
    7283          13 :                                 e = dasher_send_manifest(filter, ctx, GF_TRUE);
    7284          15 :                                 if (e) return e;
    7285             :                         }
    7286             : 
    7287             :                         cts = dasher_translate_cts(ds, cts);
    7288      133575 :                         dts -= ds->first_dts;
    7289             : 
    7290      133575 :                         if (ctx->sreg && ctx->mpd->gpac_mpd_time && (dts * 1000 > ctx->mpd->gpac_mpd_time * ds->timescale)) {
    7291           0 :                                 nb_reg_done++;
    7292           0 :                                 break;
    7293             :                         }
    7294             : 
    7295      133575 :                         dur = o_dur = gf_filter_pck_get_duration(pck);
    7296      133575 :                         pcont_cts += dur;
    7297      133575 :                         if (ds->period_continuity_next_cts < pcont_cts)
    7298      116120 :                                 ds->period_continuity_next_cts = pcont_cts;
    7299             : 
    7300             :                         split_dur = 0;
    7301             :                         split_dur_next = 0;
    7302             : 
    7303             :                         //patch to align old arch with new
    7304             :                         check_dur = 0;
    7305      133575 :                         if (ds->stream_type==GF_STREAM_AUDIO)
    7306       42376 :                                 check_dur = dur;
    7307             : 
    7308             :                         //adjust duration and cts
    7309             :                         orig_cts = cts;
    7310      133575 :                         if (ds->split_dur_next) {
    7311          52 :                                 cts += ds->split_dur_next;
    7312             :                                 assert(dur > ds->split_dur_next);
    7313          52 :                                 dur -= ds->split_dur_next;
    7314             :                                 split_dur_next = ds->split_dur_next;
    7315          52 :                                 ds->split_dur_next = 0;
    7316             :                                 is_packet_split = GF_TRUE;
    7317             :                         }
    7318             : 
    7319      133575 :                         if (ds->splitable && !ds->split_dur_next && !ds->cues && !ds->inband_cues) {
    7320             :                                 Bool do_split = GF_FALSE;
    7321             :                                 //adding this sample would exceed the segment duration
    7322          74 :                                 if (gf_sys_old_arch_compat()) {
    7323          74 :                                         if ( (cts + dur) * base_ds->timescale >= base_ds->adjusted_next_seg_start * ds->timescale )
    7324             :                                                 do_split = GF_TRUE;
    7325             :                                 } else {
    7326           0 :                                         if ( (cts + dur) * base_ds->timescale > base_ds->adjusted_next_seg_start * ds->timescale )
    7327             :                                                 do_split = GF_TRUE;
    7328             :                                 }
    7329             :                                 if (do_split) {
    7330             :                                         //this sample starts in the current segment - split it
    7331          52 :                                         if (cts * base_ds->timescale < base_ds->adjusted_next_seg_start * ds->timescale ) {
    7332          26 :                                                 split_dur = (u32) (base_ds->adjusted_next_seg_start * ds->timescale / base_ds->timescale - ds->last_cts);
    7333             : 
    7334          26 :                                                 if (gf_sys_old_arch_compat() && (split_dur==dur))
    7335             :                                                         split_dur=0;
    7336             : 
    7337          26 :                                                 if (split_dur>=dur)
    7338             :                                                         split_dur=0;
    7339             :                                         }
    7340             :                                 }
    7341             :                         }
    7342             : 
    7343             :                         //mux rep, wait for a CTS more than our base if base not yet over
    7344      133575 :                         if ((base_ds != ds) && !base_ds->seg_done && (cts * base_ds->timescale > base_ds->last_cts * ds->timescale ) )
    7345             :                                 break;
    7346             : 
    7347      129605 :                         if (ds->seek_to_pck) {
    7348          14 :                                 ds->seek_to_pck = 0;
    7349             :                         }
    7350             :                         //force flush mode, segment is done upon eos
    7351      129591 :                         else if (ctx->force_flush) {
    7352             :                         }
    7353             :                         //source-driven fragmentation check for segment start
    7354      129591 :                         else if (ctx->sigfrag) {
    7355        3666 :                                 const GF_PropertyValue *p = gf_filter_pck_get_property(pck, GF_PROP_PCK_FRAG_START);
    7356        3666 :                                 if (p && (p->value.uint>=1) && base_ds->segment_started) {
    7357             :                                         seg_over = GF_TRUE;
    7358          66 :                                         if (ds == base_ds) {
    7359          66 :                                                 base_ds->adjusted_next_seg_start = cts;
    7360             :                                         }
    7361             :                                 }
    7362             :                         }
    7363             :                         //inband-cue based segmentation
    7364      125925 :                         else if (ds->inband_cues) {
    7365        3375 :                                 const GF_PropertyValue *p = gf_filter_pck_get_property(pck, GF_PROP_PCK_CUE_START);
    7366        3375 :                                 if (p && p->value.boolean && base_ds->segment_started) {
    7367             :                                         seg_over = GF_TRUE;
    7368          27 :                                         if (ds == base_ds) {
    7369          27 :                                                 base_ds->adjusted_next_seg_start = cts;
    7370             :                                         }
    7371             :                                 }
    7372             :                         }
    7373             :                         //cue-list based segmentation
    7374      122550 :                         else if (ds->cues) {
    7375             :                                 u32 cidx;
    7376             :                                 GF_DASHCueInfo *cue=NULL;
    7377             :                                 Bool is_cue_split = GF_FALSE;
    7378             :                                 s32 has_mismatch = -1;
    7379             : 
    7380           2 :                                 for (cidx=0;cidx<ds->nb_cues; cidx++) {
    7381        5049 :                                         cue = &ds->cues[cidx];
    7382        5049 :                                         if (cue->sample_num) {
    7383        1212 :                                                 if (cue->sample_num == ds->nb_pck + 1) {
    7384             :                                                         is_cue_split = GF_TRUE;
    7385             :                                                         break;
    7386        1200 :                                                 } else if (cue->sample_num < ds->nb_pck) {
    7387           0 :                                                         has_mismatch = cidx;
    7388             :                                                 } else {
    7389             :                                                         break;
    7390             :                                                 }
    7391             :                                         }
    7392        3837 :                                         else if (cue->dts) {
    7393        1212 :                                                 u64 ts = (cue->dts - ds->cues_ts_offset) * ds->timescale;
    7394        1212 :                                                 u64 ts2 = dts * ds->cues_timescale;
    7395        1212 :                                                 if (ts == ts2) {
    7396             :                                                         is_cue_split = GF_TRUE;
    7397             :                                                         break;
    7398        1200 :                                                 } else if (ts < ts2) {
    7399           0 :                                                         has_mismatch = cidx;
    7400             :                                                 } else {
    7401             :                                                         break;
    7402             :                                                 }
    7403             :                                         }
    7404        2625 :                                         else if (cue->cts) {
    7405        2625 :                                                 s64 ts = (cue->cts - ds->cues_ts_offset) * ds->timescale;
    7406        2625 :                                                 s64 ts2 = (cts + ds->first_cts) * ds->cues_timescale;
    7407             : 
    7408             :                                                 //cues are given in track timeline (presentation time), subtract the media time to pres time offset
    7409        2625 :                                                 if (ds->cues_use_edits) {
    7410        1212 :                                                         ts2 += (s64) (ds->pts_minus_cts) * ds->cues_timescale;
    7411             :                                                 }
    7412        2625 :                                                 if (ts == ts2) {
    7413             :                                                         is_cue_split = GF_TRUE;
    7414             :                                                         break;
    7415        2602 :                                                 } else if (ts < ts2) {
    7416           2 :                                                         has_mismatch = cidx;
    7417             :                                                 } else {
    7418             :                                                         break;
    7419             :                                                 }
    7420             :                                         }
    7421             :                                 }
    7422             :                                 //start of first segment
    7423        6247 :                                 if (is_cue_split && !ds->segment_started) {
    7424           0 :                                         memmove(ds->cues, &ds->cues[cidx+1], (ds->nb_cues-cidx-1) * sizeof(GF_DASHCueInfo));
    7425           0 :                                         ds->nb_cues -= cidx+1;
    7426             :                                         is_cue_split = 0;
    7427             :                                 }
    7428             : 
    7429        6247 :                                 if (is_cue_split) {
    7430          47 :                                         if (!sap_type) {
    7431           0 :                                                 GF_LOG(ctx->strict_cues ?  GF_LOG_ERROR : GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] cue found (sn %d - dts "LLD" - cts "LLD") for PID %s but packet %d is not RAP !\n", cue->sample_num, cue->dts, cue->cts, gf_filter_pid_get_name(ds->ipid), ds->nb_pck));
    7432           0 :                                                 if (ctx->strict_cues) {
    7433           0 :                                                         gf_filter_pid_drop_packet(ds->ipid);
    7434           0 :                                                         gf_filter_pid_set_discard(ds->ipid, GF_TRUE);
    7435           0 :                                                         return GF_BAD_PARAM;
    7436             :                                                 }
    7437             :                                         }
    7438          47 :                                         memmove(ds->cues, &ds->cues[cidx+1], (ds->nb_cues-cidx-1) * sizeof(GF_DASHCueInfo));
    7439          47 :                                         ds->nb_cues -= cidx+1;
    7440             : 
    7441          47 :                                         if (sap_type==3)
    7442          47 :                                                 ds->nb_sap_3 ++;
    7443           0 :                                         else if (sap_type>3)
    7444           0 :                                                 ds->nb_sap_4 ++;
    7445             : 
    7446             :                                         /*check requested profiles can be generated, or adjust them*/
    7447          47 :                                         if (
    7448          94 :                                                 (ds->nb_sap_4 || (ds->nb_sap_3 > 1))
    7449          39 :                                                 && (ctx->profile != GF_DASH_PROFILE_FULL)
    7450             :                                                 /*TODO: store at DS level whether the usage of sap4 is ok or not (eg roll info for AAC is OK, not for xHEAAC-v2)
    7451             :                                                 for now we only complain for video*/
    7452           8 :                                                 && ((ds->stream_type==GF_STREAM_VISUAL) || (ctx->strict_sap==DASHER_SAP_ON) )
    7453             :                                         ) {
    7454           8 :                                                 GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] WARNING! Max SAP type %d detected - switching to FULL profile\n", ds->nb_sap_4 ? 4 : 3));
    7455           8 :                                                 ctx->profile = GF_DASH_PROFILE_FULL;
    7456           8 :                                                 if (ctx->sseg)
    7457           0 :                                                         ds->set->subsegment_starts_with_sap = sap_type;
    7458             :                                                 else
    7459           8 :                                                         ds->set->starts_with_sap = sap_type;
    7460             :                                         }
    7461             : 
    7462             : 
    7463             :                                         seg_over = GF_TRUE;
    7464          47 :                                         if (ds == base_ds) {
    7465          47 :                                                 base_ds->adjusted_next_seg_start = cts;
    7466             :                                         }
    7467             :                                 }
    7468             : 
    7469        6247 :                                 if (has_mismatch>=0) {
    7470           2 :                                         cue = &ds->cues[has_mismatch];
    7471           2 :                                         GF_LOG(ctx->strict_cues ?  GF_LOG_ERROR : GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] found cue (sn %d - dts "LLD" - cts "LLD") in stream %s before current packet (sn %d - dts "LLD" - cts "LLD") , buggy source cues ?\n", cue->sample_num, cue->dts, cue->cts, gf_filter_pid_get_name(ds->ipid), ds->nb_pck+1, dts + ds->first_cts, cts + ds->first_cts));
    7472           2 :                                         if (ctx->strict_cues) {
    7473           2 :                                                 gf_filter_pid_drop_packet(ds->ipid);
    7474           2 :                                                 gf_filter_pid_set_discard(ds->ipid, GF_TRUE);
    7475           2 :                                                 return GF_BAD_PARAM;
    7476             :                                         }
    7477             :                                 }
    7478             :                         }
    7479             :                         //forcing max time
    7480      116303 :                         else if (
    7481      116303 :                                 (base_ds->force_rep_end && (cts * base_ds->timescale >= base_ds->force_rep_end * ds->timescale) )
    7482      116303 :                                 || (base_ds->clamped_dur.num && (cts + o_dur > ds->ts_offset + base_ds->clamped_dur.num * ds->timescale / base_ds->clamped_dur.den))
    7483             :                         ) {
    7484          52 :                                 if (!base_ds->period->period->duration && base_ds->force_rep_end) {
    7485           0 :                                         GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Inputs duration do not match, %s truncated to %g duration\n", ds->src_url, ((Double)base_ds->force_rep_end)/base_ds->timescale ));
    7486             :                                 }
    7487          52 :                                 dasher_drop_input(ctx, ds, GF_TRUE);
    7488          52 :                                 ds->clamp_done = GF_TRUE;
    7489          52 :                                 continue;
    7490             :                         }
    7491             :                         //we have a SAP and we work in closest mode: check the next SAP in the queue, and decide if we
    7492             :                         //split the segment at this SAP or wait for the next one
    7493      116251 :                         else if (ds->segment_started && ds->sbound && sap_type) {
    7494         683 :                                 u32 idx, nb_queued, nb_pck = gf_list_count(ds->packet_queue);
    7495             :                                 nb_queued = nb_pck;
    7496         683 :                                 if (is_queue_flush) nb_queued += 1;
    7497             :                                 
    7498        1550 :                                 for (idx=1; idx<nb_queued; idx++) {
    7499             :                                         GF_FilterPacket *next;
    7500         883 :                                         if (idx==nb_pck) {
    7501           5 :                                                 next = gf_list_last(ds->packet_queue);
    7502             :                                         } else {
    7503         878 :                                                 next = gf_list_get(ds->packet_queue, idx);
    7504         878 :                                                 u32 sap_next = gf_filter_pck_get_sap(next);
    7505         878 :                                                 if (!sap_next) continue;
    7506             :                                         }
    7507         683 :                                         u32 next_dur = gf_filter_pck_get_duration(next);
    7508             :                                         //compute cts next
    7509         683 :                                         u64 cts_next = gf_filter_pck_get_cts(next);
    7510         683 :                                         if (ds->ts_offset) {
    7511          72 :                                                 cts_next += ds->ts_offset;
    7512             :                                         }
    7513             :                                         cts_next = dasher_translate_cts(ds, cts_next);
    7514             : 
    7515         683 :                                         if ((idx==nb_pck) && ctx->last_seg_merge) {
    7516           0 :                                                 u64 next_seg_dur = (cts_next + next_dur - cts);
    7517           0 :                                                 if (next_seg_dur * ds->dash_dur.den < (u64) ds->dash_dur.num * ds->timescale / 2)
    7518           0 :                                                         continue;
    7519             :                                         }
    7520             : 
    7521             :                                         //same rule as above
    7522         683 :                                         if ((cts_next + next_dur) * base_ds->timescale >= base_ds->adjusted_next_seg_start * ds->timescale ) {
    7523             :                                                 Bool force_seg_flush = GF_FALSE;
    7524          31 :                                                 s64 diff_next = cts_next * base_ds->timescale / ds->timescale;
    7525          31 :                                                 diff_next -= base_ds->adjusted_next_seg_start;
    7526             :                                                 //bounds at closest: if this SAP is closer to the target next segment start than the next SAP, split at this packet
    7527          31 :                                                 if (ds->sbound==DASHER_BOUNDS_CLOSEST) {
    7528          16 :                                                         s64 diff = cts * base_ds->timescale / ds->timescale;
    7529          16 :                                                         diff -= base_ds->adjusted_next_seg_start;
    7530             :                                                         //this one may be negative, but we always want diff_next positive (next SAP in next segment)
    7531          16 :                                                         if (diff<0)
    7532          12 :                                                                 diff = -diff;
    7533             :                                                         //old arch was only using closest for tracks with sync points
    7534          16 :                                                         if (gf_sys_old_arch_compat() && (base_ds->sync_points_type==DASHER_SYNC_NONE) ) {
    7535           8 :                                                                 if (diff_next > 0) {
    7536             :                                                                         force_seg_flush = GF_TRUE;
    7537             :                                                                 }
    7538             :                                                         }
    7539           8 :                                                         else if (diff<diff_next) {
    7540             :                                                                 force_seg_flush = GF_TRUE;
    7541             :                                                         }
    7542             :                                                 }
    7543             :                                                 //bounds always in: if the next SAP is strictly greater than the target next segment start, split at this packet
    7544             :                                                 else {
    7545          15 :                                                         if (diff_next > 0) {
    7546             :                                                                 force_seg_flush = GF_TRUE;
    7547             :                                                         }
    7548             :                                                 }
    7549             :                                                 if (force_seg_flush) {
    7550             :                                                         seg_over = GF_TRUE;
    7551          16 :                                                         if (ds == base_ds) {
    7552          16 :                                                                 base_ds->adjusted_next_seg_start = cts;
    7553             :                                                         }
    7554             :                                                         break;
    7555             :                                                 }
    7556             :                                         }
    7557             :                                 }
    7558             :                         }
    7559             :                         //we exceed segment duration - if segment was started, check if we need to stop segment
    7560             :                         //if segment was not started we insert the packet anyway
    7561      115568 :                         else if (!ds->sbound && ds->segment_started && ((cts + check_dur) * base_ds->timescale >= base_ds->adjusted_next_seg_start * ds->timescale ) ) {
    7562             :                                 //no sap, segment is over
    7563        6568 :                                 if (! ctx->sap) {
    7564             :                                         seg_over = GF_TRUE;
    7565             :                                 }
    7566        6501 :                                 else if ((ds->stream_type==GF_STREAM_AUDIO)
    7567         653 :                                         && ((cts + check_dur) * base_ds->timescale == base_ds->adjusted_next_seg_start * ds->timescale)
    7568             :                                 ) {
    7569             : 
    7570             :                                 }
    7571             :                                 // sap, segment is over
    7572        6500 :                                 else if (sap_type) {
    7573             : 
    7574        2939 :                                         if (sap_type==3)
    7575           8 :                                                 ds->nb_sap_3 ++;
    7576        2931 :                                         else if (sap_type>3)
    7577          14 :                                                 ds->nb_sap_4 ++;
    7578             : 
    7579             :                                         /*check requested profiles can be generated, or adjust them*/
    7580        2939 :                                         if ((ctx->profile != GF_DASH_PROFILE_FULL)
    7581        2621 :                                                 && (ds->nb_sap_4 || (ds->nb_sap_3 > 1))
    7582             :                                                 /*TODO: store at DS level whether the usage of sap4 is ok or not (eg roll info for AAC is OK, not for xHEAAC-v2)
    7583             :                                                 for now we only complain for video*/
    7584           2 :                                                 && ((ds->stream_type==GF_STREAM_VISUAL) || (ctx->strict_sap==DASHER_SAP_ON) )
    7585             :                                         ) {
    7586           2 :                                                 GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] WARNING! Max SAP type %d detected - switching to FULL profile\n", ds->nb_sap_4 ? 4 : 3));
    7587           2 :                                                 ctx->profile = GF_DASH_PROFILE_FULL;
    7588           2 :                                                 if (ctx->sseg)
    7589           2 :                                                         ds->set->subsegment_starts_with_sap = sap_type;
    7590             :                                                 else
    7591           0 :                                                         ds->set->starts_with_sap = sap_type;
    7592             :                                         }
    7593             : 
    7594             :                                         seg_over = GF_TRUE;
    7595        2939 :                                         if (ds == base_ds) {
    7596        2828 :                                                 base_ds->adjusted_next_seg_start = cts;
    7597             :                                         }
    7598             :                                 }
    7599             :                         }
    7600             : 
    7601             : 
    7602      129551 :                         if (ds->muxed_base && ds->muxed_base->done) {
    7603             :                                 seg_over = GF_FALSE;
    7604             :                         }
    7605             :                         //if flushing now will result in a one sample fragment afterwards
    7606             :                         //because this is the before-last sample, don't flush unless:
    7607             :                         //- we have an asto set (low latency)
    7608             :                         //- this is not an audio stream or all samples are SAPs
    7609             :                         //- we use cues
    7610      129502 :                         else if (seg_over && ds->nb_samples_in_source && !ctx->loop
    7611        2081 :                                 && (ds->nb_pck+1 == ds->nb_samples_in_source)
    7612          13 :                                 && !ds->inband_cues && !ds->cues
    7613          13 :                                 && !ctx->asto
    7614          13 :                                 && ! ((ds->sync_points_type==DASHER_SYNC_NONE) && (ds->stream_type!=GF_STREAM_AUDIO))
    7615             :                         ) {
    7616             :                                 seg_over = GF_FALSE;
    7617             :                         }
    7618             :                         //if dur=0 (some text streams), don't flush segment
    7619      129551 :                         if (seg_over && dur) {
    7620             :                                 assert(!ds->seg_done);
    7621             : 
    7622        3149 :                                 if (ds->request_period_switch && !gf_list_count(ds->packet_queue)) {
    7623           0 :                                         e = dasher_stream_period_changed(filter, ctx, ds, (ds->request_period_switch==2) ? GF_TRUE : GF_FALSE);
    7624           0 :                                         if (e < 0) {
    7625           0 :                                                 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Period switch request failed.\n"));
    7626             :                                                 break;
    7627             :                                         }
    7628             :                                         assert(gf_list_find(ctx->current_period->streams, ds)<0);
    7629           0 :                                         count = gf_list_count(ctx->current_period->streams);
    7630           0 :                                         i--;
    7631           0 :                                         break;
    7632             :                                 }
    7633             : 
    7634        3149 :                                 ds->seg_done = GF_TRUE;
    7635             : 
    7636             :                                 //in dynamic mode, send end of dash segment marker to flush segment right away, otherwise we will
    7637             :                                 //flush the segment at next segment start which could be after the segment AST => 404
    7638        3149 :                                 if (!ctx->subdur && (ctx->dmode>=GF_DASH_DYNAMIC)) {
    7639             :                                         GF_FilterPacket *eods_pck;
    7640          74 :                                         eods_pck = gf_filter_pck_new_alloc(ds->opid, 0, NULL);
    7641          74 :                                         if (eods_pck) {
    7642          74 :                                                 gf_filter_pck_set_property(eods_pck, GF_PROP_PCK_EODS, &PROP_BOOL(GF_TRUE) );
    7643          74 :                                                 gf_filter_pck_send(eods_pck);
    7644             :                                         }
    7645             :                                 }
    7646             : 
    7647        3149 :                                 ds->first_cts_in_next_seg = cts;
    7648             :                                 assert(base_ds->nb_comp_done < base_ds->nb_comp);
    7649        3149 :                                 base_ds->nb_comp_done ++;
    7650             : 
    7651        3149 :                                 if (split_dur_next)
    7652          26 :                                         ds->split_dur_next = (u32) split_dur_next;
    7653             : 
    7654        3149 :                                 if (base_ds->nb_comp_done == base_ds->nb_comp) {
    7655        3001 :                                         dasher_flush_segment(ctx, base_ds, GF_FALSE);
    7656             :                                         seg_done = GF_TRUE;
    7657             :                                 }
    7658             :                                 break;
    7659             :                         }
    7660             : 
    7661      126402 :                         if (cts==GF_FILTER_NO_TS) {
    7662           0 :                                 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] WARNING! Source packet has no timestamp !\n"));
    7663             : 
    7664           0 :                                 cts = ds->last_cts;
    7665           0 :                                 dts = ds->last_dts;
    7666             :                         } else {
    7667      126402 :                                 u64 ncts = cts + (split_dur ? split_dur : dur);
    7668      126402 :                                 if (ncts>ds->est_first_cts_in_next_seg)
    7669      116645 :                                         ds->est_first_cts_in_next_seg = ncts;
    7670             : 
    7671      126402 :                                 ncts *= 1000;
    7672      126402 :                                 ncts /= ds->timescale;
    7673      126402 :                                 if (ncts>base_ds->max_period_dur)
    7674      110988 :                                         base_ds->max_period_dur = ncts;
    7675             : 
    7676      126402 :                                 ds->last_cts = cts + (split_dur ? split_dur : dur);
    7677      126402 :                                 ds->last_dts = dts;
    7678      126402 :                                 ds->est_next_dts = dts + o_dur;
    7679             :                         }
    7680      126402 :                         ds->nb_pck ++;
    7681             : 
    7682      126402 :                         if (ctx->sigfrag) {
    7683        3600 :                                 if (!ds->segment_started) {
    7684          72 :                                         ds->first_cts_in_seg = cts;
    7685          72 :                                         dasher_mark_segment_start(ctx, ds, NULL, pck);
    7686          72 :                                         ds->segment_started = GF_TRUE;
    7687             :                                 }
    7688             : 
    7689        3600 :                                 ds->cumulated_dur += dur;
    7690             : 
    7691             :                                 //drop packet if not splitting
    7692        3600 :                                 if (!ds->split_dur_next)
    7693        3600 :                                         gf_filter_pid_drop_packet(ds->ipid);
    7694             : 
    7695        3600 :                                 if (ctx->in_error) {
    7696           0 :                                         gf_filter_pid_set_discard(ds->ipid, GF_TRUE);
    7697           0 :                                         gf_filter_pid_set_eos(ctx->opid);
    7698           0 :                                         return GF_BAD_PARAM;
    7699             :                                 }
    7700        3600 :                                 continue;
    7701             :                         }
    7702             :                         //create new ref to input
    7703      122802 :                         dst = gf_filter_pck_new_ref(ds->opid, 0, 0, pck);
    7704      122802 :                         if (!dst) return GF_OUT_OF_MEM;
    7705             : 
    7706             :                         //merge all props
    7707      122802 :                         gf_filter_pck_merge_properties(pck, dst);
    7708             :                         //we have ts offset, use computed cts and dts
    7709      122802 :                         if (ds->ts_offset) {
    7710         563 :                                 gf_filter_pck_set_cts(dst, gf_filter_pck_get_cts(pck) + ds->ts_offset);
    7711         563 :                                 gf_filter_pck_set_dts(dst, gf_filter_pck_get_dts(pck) + ds->ts_offset);
    7712             :                         }
    7713             : 
    7714      122802 :                         if (gf_sys_old_arch_compat() && ds->clamped_dur.num && ctx->loop
    7715        1917 :                                 && (cts + 2*o_dur >= ds->ts_offset + base_ds->clamped_dur.num * ds->timescale / base_ds->clamped_dur.den)
    7716             :                         ) {
    7717             :                                 u32 _dur = dur;
    7718             :                                 /* simple round with (int)+.5 to avoid trucating .99999 to 0 */
    7719          21 :                                 dur = (u32) (ds->clamped_dur.num * ds->timescale / ds->clamped_dur.den - (dts - ds->ts_offset) + 0.5);
    7720             :                                 //it may happen that the sample duration is 0 if the clamp duration is right after the sample DTS and timescale is not big enough to express it - force to 1
    7721          21 :                                 if (dur==0)
    7722             :                                         dur=1;
    7723             : 
    7724          21 :                                 gf_filter_pck_set_duration(dst, dur);
    7725          21 :                                 ds->est_next_dts += (s32) dur - (s32) _dur;;
    7726             :                         }
    7727             : 
    7728      122802 :                         if (!ds->segment_started) {
    7729        3418 :                                 ds->first_cts_in_seg = cts;
    7730        3418 :                                 dasher_mark_segment_start(ctx, ds, dst, pck);
    7731        3418 :                                 ds->segment_started = GF_TRUE;
    7732             :                         }
    7733             :                         //prev packet was split
    7734      122802 :                         if (is_packet_split) {
    7735             :                                 u64 diff=0;
    7736          26 :                                 u8 dep_flags = gf_filter_pck_get_dependency_flags(pck);
    7737          26 :                                 u64 ts = gf_filter_pck_get_cts(pck);
    7738          26 :                                 if (ts != GF_FILTER_NO_TS) {
    7739          26 :                                         cts += ds->first_cts;
    7740             :                                         assert(cts >= ts);
    7741          26 :                                         diff = cts - ts;
    7742             :                                 } else {
    7743           0 :                                         cts = ds->last_cts;
    7744             :                                 }
    7745          26 :                                 gf_filter_pck_set_cts(dst, cts + ds->ts_offset);
    7746             : 
    7747          26 :                                 ts = gf_filter_pck_get_dts(pck);
    7748          26 :                                 if (ts != GF_FILTER_NO_TS)
    7749          26 :                                         gf_filter_pck_set_dts(dst, ts + diff + ds->ts_offset);
    7750             : 
    7751             :                                 //add sample is redundant flag
    7752          26 :                                 dep_flags |= 0x1;
    7753          26 :                                 gf_filter_pck_set_dependency_flags(dst, dep_flags);
    7754             :                                 //this one might be incorrect of this split packet is also split, but we update the duration right below
    7755          26 :                                 gf_filter_pck_set_duration(dst, dur);
    7756             :                         }
    7757             : 
    7758             :                         //if split, adjust duration - this may happen on a split packet, if it covered 3 or more segments
    7759      122802 :                         if (split_dur) {
    7760             :                                 u32 cumulated_split_dur = split_dur;
    7761          26 :                                 gf_filter_pck_set_duration(dst, split_dur);
    7762             :                                 //adjust dur
    7763          26 :                                 cumulated_split_dur += (u32) (cts - orig_cts);
    7764             :                                 assert( dur > split_dur);
    7765          26 :                                 ds->split_dur_next = cumulated_split_dur;
    7766             :                                 dur = split_dur;
    7767             :                         }
    7768             : 
    7769             :                         //remove NTP
    7770      122802 :                         if (ctx->ntp != DASHER_NTP_KEEP)
    7771      122802 :                                 gf_filter_pck_set_property(dst, GF_PROP_PCK_SENDER_NTP, NULL);
    7772             : 
    7773             :                         //change packet times
    7774      122802 :                         if (ds->force_timescale) {
    7775             :                                 u64 ats;
    7776           0 :                                 ats = gf_filter_pck_get_dts(dst);
    7777           0 :                                 if (ats!=GF_FILTER_NO_TS) {
    7778           0 :                                         ats *= ds->force_timescale;
    7779           0 :                                         ats /= ds->timescale;
    7780           0 :                                         gf_filter_pck_set_dts(dst, ats);
    7781             :                                 }
    7782           0 :                                 ats = gf_filter_pck_get_cts(dst);
    7783           0 :                                 if (ats!=GF_FILTER_NO_TS) {
    7784           0 :                                         ats *= ds->force_timescale;
    7785           0 :                                         ats /= ds->timescale;
    7786           0 :                                         gf_filter_pck_set_cts(dst, ats);
    7787             :                                 }
    7788           0 :                                 ats = (u64) gf_filter_pck_get_duration(dst);
    7789           0 :                                 if (ats) {
    7790           0 :                                         ats *= ds->force_timescale;
    7791           0 :                                         ats /= ds->timescale;
    7792           0 :                                         gf_filter_pck_set_duration(dst, (u32) ats);
    7793             :                                 }
    7794             :                         }
    7795             : 
    7796      122802 :                         ds->cumulated_dur += dur;
    7797             : 
    7798      122802 :                         if (ds->current_seg_state && gf_filter_pck_get_crypt_flags(pck))
    7799        1416 :                                 ds->current_seg_state->encrypted = GF_TRUE;
    7800             :                         //TODO check drift between MPD start time and min CTS in segment (not just first CTS in segment)
    7801             : 
    7802             :                         //send packet
    7803      122802 :                         gf_filter_pck_send(dst);
    7804             : 
    7805      122802 :                         if (ctx->update_report>=0)
    7806       21539 :                                 ctx->update_report++;
    7807             : 
    7808      122802 :                         if (ds->dyn_bitrate) {
    7809             :                                 u32 dsize;
    7810       26262 :                                 u64 dts = gf_filter_pck_get_dts(pck);
    7811       26262 :                                 gf_filter_pck_get_data(pck, &dsize);
    7812       26262 :                                 if (!ds->rate_first_dts_plus_one)
    7813          60 :                                         ds->rate_first_dts_plus_one = 1 + dts;
    7814       26262 :                                 ds->rate_last_dts = dts;
    7815       26262 :                                 ds->rate_media_size += dsize;
    7816             :                         }
    7817             : 
    7818             :                         //drop packet if not splitting
    7819      122802 :                         if (!ds->split_dur_next)
    7820      122776 :                                 dasher_drop_input(ctx, ds, GF_FALSE);
    7821             : 
    7822             :                 }
    7823             :         }
    7824             :         nb_init  = 0;
    7825     5817942 :         for (i=0; i<count; i++) {
    7826     5817942 :                 GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
    7827             :                 //if (ds->muxed_base) ds = ds->muxed_base;
    7828     5817942 :                 if (ds->done || ds->subdur_done) nb_init++;
    7829     5726104 :                 else if (ds->seg_done && ctx->force_period_switch) nb_init++;
    7830     5726104 :                 else if (ds->seg_done && ds->muxed_base && ds->muxed_base->done) {
    7831           0 :                         nb_init++;
    7832           0 :                         ds->done = 1;
    7833             :                 }
    7834             :         }
    7835             : 
    7836     5720281 :         if (nb_reg_done && (nb_reg_done == count)) {
    7837           0 :                 ctx->mpd->gpac_mpd_time = 0;
    7838             :         }
    7839             : 
    7840     5720281 :         dasher_format_report(filter, ctx);
    7841             : 
    7842     5720281 :         if (seg_done) {
    7843             :                 Bool update_period = GF_FALSE;
    7844             :                 Bool update_manifest = GF_FALSE;
    7845        2142 :                 if (ctx->purge_segments) update_period = GF_TRUE;
    7846        2142 :                 if (ctx->mpd) {
    7847             :                         //segment timeline used, always update manifest
    7848        2142 :                         if (ctx->stl)
    7849             :                                 update_manifest = GF_TRUE;
    7850        2138 :                         else if (ctx->dmode==GF_DASH_DYNAMIC) {
    7851             :                                 //publish time not set, we never send the manifest, do it
    7852         128 :                                 if (!ctx->mpd->publishTime) {
    7853             :                                         update_manifest = GF_TRUE;
    7854             :                                 }
    7855             :                                 //whenever we have a new seg in HLS, push new manifest
    7856         122 :                                 else if (ctx->do_m3u8) {
    7857             :                                         update_manifest = GF_TRUE;
    7858             :                                 }
    7859             :                                 //we have a minimum ipdate period
    7860          60 :                                 else if (ctx->mpd->minimum_update_period) {
    7861          60 :                                         u64 diff = dasher_get_utc(ctx) - ctx->mpd->publishTime;
    7862          60 :                                         if (diff >= ctx->mpd->minimum_update_period)
    7863             :                                                 update_manifest = GF_TRUE;
    7864             :                                 }
    7865             :                         }
    7866        2142 :                         if (update_period)
    7867          95 :                                 dasher_update_period_duration(ctx, GF_FALSE);
    7868             : 
    7869        2142 :                         if (update_manifest)
    7870          85 :                                 dasher_send_manifest(filter, ctx, GF_FALSE);
    7871             :                 }
    7872     5718139 :         } else if (ctx->force_hls_ll_manifest) {
    7873         279 :                 ctx->force_hls_ll_manifest = GF_FALSE;
    7874         279 :                 dasher_send_manifest(filter, ctx, GF_FALSE);
    7875             :         }
    7876             : 
    7877             :         //still some running streams in period
    7878     5720281 :         if (count && (nb_init<count)) {
    7879     5662697 :                 gf_filter_post_process_task(filter);
    7880     5662697 :                 return GF_OK;
    7881             :         }
    7882             : 
    7883             :         //in subdur mode once we are done, flush output pids and discard all input packets
    7884             :         //this is done at the end to be able to resume dashing when loop is requested
    7885       57584 :         if (ctx->subdur) {
    7886         139 :                 for (i=0; i<count; i++) {
    7887             :                         GF_FilterPacket *eods_pck;
    7888         139 :                         GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
    7889         139 :                         if (ds->done) continue;
    7890          30 :                         eods_pck = gf_filter_pck_new_alloc(ds->opid, 0, NULL);
    7891          30 :                         if (!eods_pck) return GF_OUT_OF_MEM;
    7892          30 :                         ds->done = 2;
    7893          30 :                         ds->subdur_done = GF_TRUE;
    7894          30 :                         gf_filter_pck_set_property(eods_pck, GF_PROP_PCK_EODS, &PROP_BOOL(GF_TRUE) );
    7895          30 :                         gf_filter_pck_send(eods_pck);
    7896             : 
    7897          30 :                         dasher_drop_input(ctx, ds, GF_TRUE);
    7898             :                 }
    7899             :         }
    7900             : 
    7901             :         //we need to wait for full flush of packets before switching periods in order to get the
    7902             :         //proper segment size for segment_list+byte_range mode
    7903       57584 :         if (ctx->nb_seg_url_pending) {
    7904             :                 u64 diff;
    7905       57042 :                 if (!ctx->last_evt_check_time) ctx->last_evt_check_time = gf_sys_clock_high_res();
    7906             : 
    7907       57042 :                 diff = gf_sys_clock_high_res() - ctx->last_evt_check_time;
    7908       57042 :                 if (diff < 10000000) {
    7909       57042 :                         gf_filter_post_process_task(filter);
    7910       57042 :                         return GF_OK;
    7911             :                 }
    7912           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] timeout %d segment info still pending but no event from muxer after "LLU" us, aborting\n", ctx->nb_seg_url_pending, diff));
    7913           0 :                 ctx->nb_seg_url_pending = 0;
    7914           0 :                 return GF_SERVICE_ERROR;
    7915             :         }
    7916         542 :         if (ctx->sseg && !ctx->on_demand_done && !ctx->sigfrag) {
    7917             :                 return GF_OK;
    7918             :         }
    7919         511 :         ctx->force_period_switch = GF_FALSE;
    7920             :         //done with this period, do period switch - this will update the MPD if needed
    7921         511 :         e = dasher_switch_period(filter, ctx);
    7922             :         //no more periods
    7923         511 :         if (e==GF_EOS) {
    7924         255 :                 if (!ctx->is_eos) {
    7925         255 :                         ctx->is_eos = GF_TRUE;
    7926         255 :                         gf_filter_pid_set_eos(ctx->opid);
    7927             :                 }
    7928             :         }
    7929             :         return e;
    7930             : }
    7931             : 
    7932             : 
    7933             : 
    7934          17 : static void dasher_resume_subdur(GF_Filter *filter, GF_DasherCtx *ctx)
    7935             : {
    7936             :         GF_FilterEvent evt;
    7937             :         u32 i, count;
    7938          17 :         Bool is_last = (ctx->dmode == GF_MPD_TYPE_DYNAMIC_LAST) ? GF_TRUE : GF_FALSE;
    7939          17 :         if (!ctx->state) return;
    7940             : 
    7941          17 :         count = gf_list_count(ctx->pids);
    7942          48 :         for (i=0; i<count; i++) {
    7943          31 :                 GF_DashStream *ds = gf_list_get(ctx->pids, i);
    7944          31 :                 ds->rep = NULL;
    7945          31 :                 if ((ds->done==1) && !ctx->subdur && ctx->loop) {}
    7946          31 :                 else if (ds->reschedule) {
    7947             :                         //we possibly dispatched end of stream on all outputs, we need to force unblockink to get called again
    7948           6 :                         gf_filter_pid_discard_block(ds->opid);
    7949           6 :                         continue;
    7950             :                 }
    7951          25 :                 else if (ds->done != 2) continue;
    7952             : 
    7953          25 :                 if (is_last) continue;
    7954             : 
    7955          12 :                 gf_filter_pid_set_discard(ds->ipid, GF_FALSE);
    7956             : 
    7957             :                 //send stop and play
    7958          12 :                 GF_FEVT_INIT(evt, GF_FEVT_STOP, ds->ipid);
    7959          12 :                 gf_filter_pid_send_event(ds->ipid, &evt);
    7960             : 
    7961          12 :                 dasher_send_encode_hints(ctx, ds);
    7962          12 :                 GF_FEVT_INIT(evt, GF_FEVT_PLAY, ds->ipid);
    7963          12 :                 evt.play.speed = 1.0;
    7964          12 :                 if (!ctx->subdur || !ctx->loop) {
    7965           2 :                         ds->seek_to_pck = 0;
    7966             :                 } else {
    7967             :                         //request start after the last packet we processed
    7968          10 :                         evt.play.from_pck = (u32) ds->seek_to_pck+1;
    7969             :                 }
    7970          12 :                 gf_filter_pid_send_event(ds->ipid, &evt);
    7971             : 
    7972             :                 //full stream looping
    7973          12 :                 if (ds->subdur_done && !ctx->subdur) {
    7974           0 :                         ds->loop_state = 0;
    7975             :                         //mark as subdur done to force a context reload through period switch
    7976           0 :                         ds->done = 2;
    7977           0 :                         ds->seg_done = GF_FALSE;
    7978           0 :                         ds->subdur_done = GF_FALSE;
    7979             :                 }
    7980             :         }
    7981             : 
    7982          17 :         ctx->subdur_done = GF_FALSE;
    7983          17 :         ctx->is_eos = GF_FALSE;
    7984          17 :         if (!ctx->post_play_events && !is_last) {
    7985           7 :                 ctx->current_period->period = NULL;
    7986           7 :                 ctx->first_context_load = GF_TRUE;
    7987           7 :                 ctx->post_play_events = GF_TRUE;
    7988             :         }
    7989          17 :         gf_filter_post_process_task(filter);
    7990             : }
    7991             : 
    7992         330 : static void dasher_process_hls_ll(GF_DasherCtx *ctx, const GF_FilterEvent *evt)
    7993             : {
    7994         330 :         u32 i, count = gf_list_count(ctx->pids);
    7995             :         GF_DASH_SegmentContext *sctx;
    7996             :         GF_DashStream *ds = NULL;
    7997             : 
    7998         330 :         if (ctx->forward_mode)
    7999             :                 return;
    8000             : 
    8001         330 :         if (!ctx->store_seg_states) {
    8002           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Received fragment size info event but no associated segment state\n"));
    8003             :                 return;
    8004             :         }
    8005          90 :         for (i=0; i<count; i++) {
    8006         420 :                 ds = gf_list_get(ctx->pids, i);
    8007         420 :                 if (ds->opid == evt->base.on_pid) break;
    8008             :                 ds = NULL;
    8009             :         }
    8010         330 :         if (!ds) {
    8011           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Received fragment size info event but no associated pid\n"));
    8012             :                 return;
    8013             :         }
    8014         330 :         if (ds->muxed_base)
    8015             :                 ds = ds->muxed_base;
    8016             : 
    8017         330 :         sctx = gf_list_get(ds->pending_segment_states, 0);
    8018         330 :         if (!sctx || !ctx->nb_seg_url_pending) {
    8019           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Received segment size info event but no pending segments\n"));
    8020             :                 return;
    8021             :         }
    8022         330 :         sctx->frags = gf_realloc(sctx->frags, sizeof (GF_DASH_FragmentContext) * (sctx->nb_frags+1));
    8023         330 :         if (!sctx->frags) {
    8024           0 :                 sctx->nb_frags = 0;
    8025           0 :                 return;
    8026             :         }
    8027         330 :         sctx->frags[sctx->nb_frags].size = evt->frag_size.size;
    8028         330 :         sctx->frags[sctx->nb_frags].offset = evt->frag_size.offset;
    8029         330 :         if (evt->frag_size.duration.den) {
    8030         330 :                 sctx->frags[sctx->nb_frags].duration = (u32) ((u64) evt->frag_size.duration.num * ds->rep->timescale / evt->frag_size.duration.den);
    8031             :         } else {
    8032           0 :                 sctx->frags[sctx->nb_frags].duration = 0;
    8033             :         }
    8034         330 :         sctx->frags[sctx->nb_frags].independent = evt->frag_size.independent;
    8035         330 :         sctx->nb_frags++;
    8036         330 :         if (evt->frag_size.is_last) {
    8037          51 :                 sctx->llhls_done = GF_TRUE;
    8038             :         } else {
    8039         279 :                 ctx->force_hls_ll_manifest = GF_TRUE;
    8040             :         }
    8041             : }
    8042             : 
    8043       27868 : static Bool dasher_process_event(GF_Filter *filter, const GF_FilterEvent *evt)
    8044             : {
    8045             :         u32 i, count;
    8046             :         Bool flush_mpd = GF_FALSE;
    8047       27868 :         GF_DasherCtx *ctx = gf_filter_get_udta(filter);
    8048             : 
    8049       27868 :         ctx->last_evt_check_time = 0;
    8050             : 
    8051       27868 :         if (evt->base.type == GF_FEVT_RESUME) {
    8052             :                 //only process resume event when coming from main output PID, but always cancel it
    8053             :                 //this is needed in case the output filter where the resume event was initiated consumes both
    8054             :                 //manifest and segment PIDs, as is the case with httpout
    8055          20 :                 if (evt->base.on_pid == ctx->opid)
    8056          17 :                         dasher_resume_subdur(filter, ctx);
    8057             :                 return GF_TRUE;
    8058             :         }
    8059       27848 :         if (evt->base.type == GF_FEVT_CONNECT_FAIL) {
    8060           0 :                 ctx->in_error = GF_TRUE;
    8061           0 :                 gf_filter_pid_set_eos(ctx->opid);
    8062           0 :                 if (ctx->opid_alt)
    8063           0 :                         gf_filter_pid_set_eos(ctx->opid_alt);
    8064             :                 return GF_TRUE;
    8065             :         }
    8066             : 
    8067       27848 :         if (evt->base.type == GF_FEVT_PLAY) {
    8068         599 :                 ctx->is_playing = GF_TRUE;
    8069         599 :                 if (!ctx->sfile && !ctx->stl && !ctx->use_cues) {
    8070             :                         GF_FilterEvent anevt;
    8071         452 :                         GF_FEVT_INIT(anevt, GF_FEVT_ENCODE_HINTS, NULL)
    8072         452 :                         count = gf_list_count(ctx->pids);
    8073        1654 :                         for (i=0; i<count; i++) {
    8074        1202 :                                 GF_DashStream *ds = gf_list_get(ctx->pids, i);
    8075        1202 :                                 anevt.base.on_pid = ds->ipid;
    8076        1202 :                                 anevt.encode_hints.intra_period = ds->dash_dur;
    8077        1202 :                                 gf_filter_pid_send_event(ds->ipid, &anevt);
    8078             :                         }
    8079             :                 }
    8080             :                 return GF_FALSE;
    8081             :         }
    8082       27249 :         if (evt->base.type == GF_FEVT_STOP) {
    8083           0 :                 ctx->is_playing = GF_FALSE;
    8084           0 :                 return GF_FALSE;
    8085             :         }
    8086             : 
    8087       27249 :         if (evt->base.type == GF_FEVT_FRAGMENT_SIZE) {
    8088         330 :                 dasher_process_hls_ll(ctx, evt);
    8089         330 :                 return GF_TRUE;
    8090             :         }
    8091       26919 :         if (evt->base.type != GF_FEVT_SEGMENT_SIZE) return GF_FALSE;
    8092             : 
    8093        3567 :         if (ctx->forward_mode==DASHER_FWD_ALL)
    8094             :                 return GF_TRUE;
    8095             : 
    8096        3541 :         count = gf_list_count(ctx->pids);
    8097       25040 :         for (i=0; i<count; i++) {
    8098             :                 u64 r_start, r_end;
    8099       21499 :                 GF_DashStream *ds = gf_list_get(ctx->pids, i);
    8100       21499 :                 if (ds->opid != evt->base.on_pid) continue;
    8101             : 
    8102        3540 :                 if (ds->muxed_base)
    8103             :                         ds = ds->muxed_base;
    8104             : 
    8105        3540 :                 if (ctx->store_seg_states && !evt->seg_size.is_init) {
    8106         504 :                         GF_DASH_SegmentContext *sctx = gf_list_pop_front(ds->pending_segment_states);
    8107         504 :                         if (!sctx || !ctx->nb_seg_url_pending) {
    8108           0 :                                 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Broken muxer, received segment size info event but no pending segments\n"));
    8109             :                                 return GF_TRUE;
    8110             :                         }
    8111             :                         assert(sctx);
    8112             :                         assert(ctx->nb_seg_url_pending);
    8113         504 :                         ctx->nb_seg_url_pending--;
    8114         504 :                         sctx->file_size = 1 + (u32) (evt->seg_size.media_range_end - evt->seg_size.media_range_start);
    8115         504 :                         sctx->file_offset = evt->seg_size.media_range_start;
    8116         504 :                         sctx->index_size = 1 + (u32) (evt->seg_size.idx_range_end - evt->seg_size.idx_range_start);
    8117         504 :                         sctx->index_offset = evt->seg_size.idx_range_start;
    8118             : 
    8119         504 :                         if (sctx->llhls_mode) {
    8120          51 :                                 sctx->llhls_done = GF_TRUE;
    8121             :                                 //reset frags of past segments
    8122          51 :                                 s32 idx, reset_until = gf_list_find(ds->rep->state_seg_list, sctx);
    8123          63 :                                 for (idx=reset_until-4; idx>=0; idx--) {
    8124          18 :                                         GF_DASH_SegmentContext *prev_sctx = gf_list_get(ds->rep->state_seg_list, idx);
    8125          18 :                                         if (!prev_sctx->llhls_mode)
    8126             :                                                 break;
    8127             : 
    8128             :                                         //send file delete events
    8129          12 :                                         if (prev_sctx->llhls_mode>1) {
    8130             :                                                 u32 k;
    8131          24 :                                                 for (k=0; k<prev_sctx->nb_frags; k++) {
    8132             :                                                         GF_FilterEvent anevt;
    8133             :                                                         char szPath[GF_MAX_PATH];
    8134          20 :                                                         sprintf(szPath, "%s.%d", prev_sctx->filepath, k+1);
    8135          20 :                                                         GF_FEVT_INIT(anevt, GF_FEVT_FILE_DELETE, ds->opid);
    8136          20 :                                                         anevt.file_del.url = szPath;
    8137          20 :                                                         gf_filter_pid_send_event(ds->opid, &anevt);
    8138             :                                                 }
    8139             :                                         }
    8140          12 :                                         prev_sctx->llhls_mode = 0;
    8141             :                                 }
    8142             :                         }
    8143             :                 }
    8144             : 
    8145             :                 //in state mode we store everything
    8146             :                 //don't set segment sizes in template mode
    8147        3540 :                 if (ctx->tpl) continue;
    8148             :                 //only set size/index size for init segment when doing onDemand/single index
    8149         719 :                 if (ctx->sseg && !evt->seg_size.is_init) continue;
    8150             : 
    8151         410 :                 if (evt->seg_size.media_range_end) {
    8152         387 :                         r_start = evt->seg_size.media_range_start;
    8153             :                         r_end = evt->seg_size.media_range_end;
    8154             :                 } else {
    8155          23 :                         r_start = evt->seg_size.idx_range_start;
    8156          23 :                         r_end = evt->seg_size.idx_range_end;
    8157             :                 }
    8158             :                 //init segment or representation index, set it in on demand and main single source
    8159         454 :                 if ((ctx->sfile || ctx->sseg) && (evt->seg_size.is_init==1))  {
    8160             :                         GF_MPD_URL *url, **s_url;
    8161             : 
    8162          66 :                         if (ds->rep->segment_list) {
    8163          29 :                                 if (!evt->seg_size.media_range_start && !evt->seg_size.media_range_end) {
    8164           2 :                                         if (ds->rep->segment_list->initialization_segment) {
    8165           2 :                                                 gf_mpd_url_free(ds->rep->segment_list->initialization_segment);
    8166           2 :                                                 ds->rep->segment_list->initialization_segment = NULL;
    8167             :                                         }
    8168           2 :                                         continue;
    8169             :                                 }
    8170             :                         }
    8171             : 
    8172          64 :                         if (ds->rep->segment_base && !evt->seg_size.media_range_end) {
    8173          20 :                                 if (! ds->rep->segment_base->index_range) {
    8174          20 :                                         GF_SAFEALLOC(ds->rep->segment_base->index_range, GF_MPD_ByteRange);
    8175             :                                 }
    8176          20 :                                 if (ds->rep->segment_base->index_range) {
    8177          20 :                                         ds->rep->segment_base->index_range->start_range = r_start;
    8178          20 :                                         ds->rep->segment_base->index_range->end_range = r_end;
    8179          20 :                                         ds->rep->segment_base->index_range_exact = GF_TRUE;
    8180             :                                 }
    8181             :                                 flush_mpd = GF_TRUE;
    8182          20 :                                 continue;
    8183             :                         }
    8184             : 
    8185          44 :                         GF_SAFEALLOC(url, GF_MPD_URL);
    8186          44 :                         if (!url) return GF_TRUE;
    8187             : 
    8188          44 :                         GF_SAFEALLOC(url->byte_range, GF_MPD_ByteRange);
    8189          44 :                         if (!url->byte_range) return GF_TRUE;
    8190          44 :                         url->byte_range->start_range = r_start;
    8191          44 :                         url->byte_range->end_range = r_end;
    8192             : 
    8193             :                         s_url = NULL;
    8194          44 :                         if (ds->rep->segment_base) {
    8195          17 :                                 if (evt->seg_size.media_range_end) s_url = &ds->rep->segment_base->initialization_segment;
    8196             :                         } else {
    8197             :                                 assert(ds->rep->segment_list);
    8198          27 :                                 if (evt->seg_size.media_range_end) s_url = &ds->rep->segment_list->initialization_segment;
    8199           0 :                                 else s_url = &ds->rep->segment_list->representation_index;
    8200             :                         }
    8201             :                         assert(s_url);
    8202          44 :                         if (*s_url) gf_mpd_url_free(*s_url);
    8203          44 :                         *s_url = url;
    8204         344 :                 } else if (ds->rep->segment_list && !evt->seg_size.is_init) {
    8205         342 :                         GF_MPD_SegmentURL *url = gf_list_pop_front(ds->pending_segment_urls);
    8206         342 :                         if (!url || !ctx->nb_seg_url_pending) {
    8207           0 :                                 if (!ds->done) {
    8208           0 :                                         GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Broken muxer, received segment size info event but no pending segments\n"));
    8209             :                                 }
    8210             :                                 return GF_TRUE;
    8211             :                         }
    8212         342 :                         ctx->nb_seg_url_pending--;
    8213             : 
    8214         342 :                         if (!url->media && ctx->sfile) {
    8215         301 :                                 GF_SAFEALLOC(url->media_range, GF_MPD_ByteRange);
    8216         301 :                                 if (url->media_range) {
    8217         301 :                                         url->media_range->start_range = evt->seg_size.media_range_start;
    8218         301 :                                         url->media_range->end_range = evt->seg_size.media_range_end;
    8219             :                                 }
    8220             :                         }
    8221             :                         //patch in test mode, old arch was not generating the index size for segment lists
    8222         342 :                         if (evt->seg_size.idx_range_end && (!gf_sys_old_arch_compat() || ctx->sfile) ) {
    8223         263 :                                 GF_SAFEALLOC(url->index_range, GF_MPD_ByteRange);
    8224         263 :                                 if (url->index_range) {
    8225         263 :                                         url->index_range->start_range = evt->seg_size.idx_range_start;
    8226         263 :                                         url->index_range->end_range = evt->seg_size.idx_range_end;
    8227             :                                 }
    8228             :                         }
    8229             :                 }
    8230             :         }
    8231        3541 :         if (!ctx->sseg || !flush_mpd) return GF_TRUE;
    8232             : 
    8233             :         flush_mpd = GF_TRUE;
    8234          23 :         for (i=0; i<count; i++) {
    8235          26 :                 GF_DashStream *ds = gf_list_get(ctx->pids, i);
    8236          26 :                 if (!ds->rep) continue;
    8237          25 :                 if (! ds->rep->segment_base) continue;
    8238          25 :                 if (ds->rep->segment_base->index_range) continue;
    8239             :                 flush_mpd = GF_FALSE;
    8240             :                 break;
    8241             :         }
    8242          20 :         if (flush_mpd) {
    8243          17 :                 ctx->on_demand_done = GF_TRUE;
    8244          17 :                 gf_filter_post_process_task(filter);
    8245             :         }
    8246             :         return GF_TRUE;
    8247             : }
    8248             : 
    8249         247 : static GF_Err dasher_setup_profile(GF_DasherCtx *ctx)
    8250             : {
    8251         247 :         switch (ctx->profile) {
    8252           5 :         case GF_DASH_PROFILE_AVC264_LIVE:
    8253             :         case GF_DASH_PROFILE_AVC264_ONDEMAND:
    8254             :         case GF_DASH_PROFILE_DASHIF_LL:
    8255           5 :                 if (ctx->cp == GF_DASH_CPMODE_REPRESENTATION) {
    8256           0 :                         GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] ERROR! The selected DASH profile (DASH-IF IOP) requires the ContentProtection element to be present in the AdaptationSet element, updating.\n"));
    8257           0 :                         ctx->cp = GF_DASH_CPMODE_ADAPTATION_SET;
    8258             :                 }
    8259             :         default:
    8260             :                 break;
    8261             :         }
    8262         247 :         if (ctx->m2ts) {
    8263           5 :                 switch (ctx->profile) {
    8264           0 :                 case GF_DASH_PROFILE_HBBTV_1_5_ISOBMF_LIVE:
    8265             :                 case GF_DASH_PROFILE_AVC264_LIVE:
    8266             :                 case GF_DASH_PROFILE_DASHIF_LL:
    8267           0 :                         ctx->profile = GF_DASH_PROFILE_LIVE;
    8268           0 :                         break;
    8269           1 :                 case GF_DASH_PROFILE_ONDEMAND:
    8270             :                 case GF_DASH_PROFILE_AVC264_ONDEMAND:
    8271           1 :                         ctx->profile = GF_DASH_PROFILE_ONDEMAND;
    8272           1 :                         break;
    8273             :                 }
    8274             :         }
    8275             : 
    8276             :         /*adjust params based on profiles*/
    8277         247 :         switch (ctx->profile) {
    8278         146 :         case GF_DASH_PROFILE_LIVE:
    8279         146 :                 ctx->sseg = ctx->sfile = GF_FALSE;
    8280         146 :                 ctx->tpl = ctx->align = ctx->sap = GF_TRUE;
    8281         146 :                 break;
    8282           0 :         case GF_DASH_PROFILE_HBBTV_1_5_ISOBMF_LIVE:
    8283           0 :                 ctx->check_main_role = GF_TRUE;
    8284           0 :                 ctx->bs_switch = DASHER_BS_SWITCH_MULTI;
    8285             :                 //FALLTHROUGH
    8286           3 :         case GF_DASH_PROFILE_AVC264_LIVE:
    8287           3 :                 ctx->sseg = ctx->sfile = GF_FALSE;
    8288           3 :                 ctx->no_fragments_defaults = ctx->align = ctx->tpl = ctx->sap = GF_TRUE;
    8289           3 :                 break;
    8290           2 :         case GF_DASH_PROFILE_AVC264_ONDEMAND:
    8291           2 :                 ctx->tpl = GF_FALSE;
    8292           2 :                 ctx->no_fragments_defaults = ctx->align = ctx->sseg = ctx->sap = GF_TRUE;
    8293           2 :                 break;
    8294          17 :         case GF_DASH_PROFILE_ONDEMAND:
    8295          17 :                 ctx->sseg = ctx->align = ctx->sap = ctx->sfile = GF_TRUE;
    8296          17 :                 ctx->tpl = GF_FALSE;
    8297             : 
    8298          17 :                 if (ctx->m2ts) {
    8299           1 :                         ctx->sseg = GF_FALSE;
    8300           1 :                         ctx->tpl = GF_TRUE;
    8301           1 :                         ctx->profile = GF_DASH_PROFILE_MAIN;
    8302             :                 } else {
    8303          16 :                         if ((ctx->bs_switch != DASHER_BS_SWITCH_DEF) && (ctx->bs_switch != DASHER_BS_SWITCH_OFF)) {
    8304           0 :                                 GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] onDemand profile, bitstream switching mode cannot be used, defaulting to off.\n"));
    8305             :                         }
    8306             :                 }
    8307             :                 /*BS switching is meaningless in onDemand profile*/
    8308          17 :                 ctx->bs_switch = DASHER_BS_SWITCH_OFF;
    8309          17 :                 break;
    8310          10 :         case GF_DASH_PROFILE_MAIN:
    8311          10 :                 ctx->align = ctx->sap = GF_TRUE;
    8312          10 :                 ctx->sseg = ctx->tpl = GF_FALSE;
    8313          10 :                 break;
    8314           0 :         case GF_DASH_PROFILE_DASHIF_LL:
    8315           0 :                 ctx->sseg = ctx->sfile = GF_FALSE;
    8316           0 :                 ctx->no_fragments_defaults = ctx->align = ctx->tpl = ctx->sap = GF_TRUE;
    8317           0 :                 if (!ctx->utcs) {
    8318           0 :                         GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] DASH-IF LL requires UTCTiming but none specified, using http://time.akamai.com/?iso \n"));
    8319           0 :                         ctx->utcs = gf_strdup("http://time.akamai.com/?iso");
    8320             :                 }
    8321             :                 break;
    8322             :         default:
    8323             :                 break;
    8324             :         }
    8325             : 
    8326         247 :         if (ctx->sseg)
    8327          18 :                 ctx->tpl = GF_FALSE;
    8328             : 
    8329         247 :         if (ctx->bs_switch == DASHER_BS_SWITCH_DEF) {
    8330         224 :                 ctx->bs_switch = DASHER_BS_SWITCH_ON;
    8331             :         }
    8332             : 
    8333         247 :         if (ctx->cmaf) {
    8334           0 :                 ctx->align = GF_TRUE;
    8335           0 :                 ctx->sap = GF_TRUE;
    8336             :         }
    8337             : 
    8338         247 :         if (! ctx->align) {
    8339           0 :                 if (ctx->profile != GF_DASH_PROFILE_FULL) {
    8340           0 :                         GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Segments are not time-aligned in each representation of each period\n\tswitching to FULL profile\n"));
    8341           0 :                         ctx->profile = GF_DASH_PROFILE_FULL;
    8342             :                 }
    8343             :                 //commented out, this does not seem correct since BS switching is orthogonal to segment alignment
    8344             :                 //one could have inband params working even in non time-aligned setup
    8345             : #if 0
    8346             :                 if (ctx->bs_switch != DASHER_BS_SWITCH_OFF) {
    8347             :                         GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Segments are not time-aligned in each representation of each period\n\tdisabling bitstream switching\n"));
    8348             :                         ctx->bs_switch = DASHER_BS_SWITCH_OFF;
    8349             :                 }
    8350             : #endif
    8351             : 
    8352             :         }
    8353             : 
    8354             :         //check we have a segment template
    8355         247 :         if (!ctx->template) {
    8356         235 :                 if (!ctx->sigfrag) {
    8357         227 :                         ctx->template = gf_strdup( ctx->sfile ? "$File$$FS$_dash" : (ctx->stl ? "$File$_dash$FS$$Time$" : "$File$_dash$FS$$Number$") );
    8358         227 :                         GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[Dasher] No template assigned, using %s\n", ctx->template));
    8359             :                 }
    8360             : 
    8361         235 :                 if (ctx->profile == GF_DASH_PROFILE_FULL) {
    8362          19 :                         ctx->sfile = GF_TRUE;
    8363             :                 }
    8364             :         }
    8365             :         //backward compatibility with old arch using %s
    8366             :         else {
    8367          12 :                 char *sep = strstr(ctx->template, "%s");
    8368          12 :                 if (sep) {
    8369           0 :                         char *new_template = NULL;
    8370           0 :                         sep[0] = 0;
    8371           0 :                         gf_dynstrcat(&new_template, ctx->template, NULL);
    8372           0 :                         gf_dynstrcat(&new_template, "$File$", NULL);
    8373           0 :                         gf_dynstrcat(&new_template, sep+2, NULL);
    8374           0 :                         gf_free(ctx->template);
    8375           0 :                         ctx->template = new_template;
    8376             :                 }
    8377             : 
    8378             :         }
    8379         247 :         return GF_OK;
    8380             : }
    8381             : 
    8382         244 : static GF_Err dasher_initialize(GF_Filter *filter)
    8383             : {
    8384             :         GF_Err e;
    8385         244 :         GF_DasherCtx *ctx = gf_filter_get_udta(filter);
    8386         244 :         gf_filter_set_max_extra_input_pids(filter, -1);
    8387             : 
    8388         244 :         ctx->pids = gf_list_new();
    8389         244 :         ctx->postponed_pids = gf_list_new();
    8390         244 :         ctx->tpl_records = gf_list_new();
    8391             : 
    8392         244 :         if (!ctx->initext && (ctx->muxtype==DASHER_MUX_AUTO))
    8393         234 :                 ctx->muxtype = DASHER_MUX_ISOM;
    8394             : 
    8395         244 :         if ((ctx->segdur.num <= 0) || !ctx->segdur.den) {
    8396          54 :                 ctx->segdur.num = 1;
    8397          54 :                 ctx->segdur.den = 1;
    8398          54 :                 ctx->no_seg_dur = GF_TRUE;
    8399             :         }
    8400             : 
    8401         244 :         e = dasher_setup_profile(ctx);
    8402         244 :         if (e) return e;
    8403             : 
    8404         244 :         if (ctx->sfile && ctx->tpl)
    8405          21 :                 ctx->tpl = GF_FALSE;
    8406             : 
    8407         244 :         ctx->current_period = dasher_new_period();
    8408         244 :         ctx->next_period = dasher_new_period();
    8409         244 :         ctx->on_demand_done = GF_TRUE;
    8410             : 
    8411         244 :         if (ctx->state) {
    8412          14 :                 ctx->first_context_load = GF_TRUE;
    8413             :         }
    8414         244 :         if (ctx->subdur && !ctx->state) {
    8415           0 :                 GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] subdur mode specified but no context set, will only dash %g seconds of media\n", ctx->subdur));
    8416             :         }
    8417             :         //we build manifest from input frag/seg, always use single frag
    8418         244 :         if (ctx->sigfrag) {
    8419           6 :                 if (ctx->tpl) {
    8420           4 :                         if (!ctx->template) {
    8421           3 :                                 GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Warning, manifest generation only mode requested for live-based profile but no template provided, switching to main profile.\n"));
    8422           3 :                                 ctx->profile = GF_DASH_PROFILE_MAIN;
    8423           3 :                                 ctx->tpl = GF_FALSE;
    8424           3 :                                 dasher_setup_profile(ctx);
    8425             :                                 //we force single file in this mode, but we will replace byte ranges by source URL
    8426           3 :                                 ctx->sfile = GF_TRUE;
    8427             :                         } else {
    8428           1 :                                 ctx->sseg = GF_FALSE;
    8429           1 :                                 ctx->sfile = GF_FALSE;
    8430             :                         }
    8431             :                 } else {
    8432           2 :                         if (!ctx->sseg)
    8433           1 :                                 ctx->sfile = GF_TRUE;
    8434             :                 }
    8435             :         }
    8436             : 
    8437         244 :         if (!ctx->sap || ctx->sigfrag || ctx->cues)
    8438          19 :                 ctx->sbound = DASHER_BOUNDS_OUT;
    8439             : 
    8440         244 :         if ((ctx->tsb>=0) && (ctx->dmode!=GF_DASH_STATIC))
    8441          20 :                 ctx->purge_segments = GF_TRUE;
    8442             : 
    8443         244 :         if (ctx->state && ctx->sreg) {
    8444             :                 u32 diff;
    8445             :                 u64 next_gen_ntp;
    8446             :                 GF_Err dash_state_check_timing(const char *dash_state, u64 *next_gen_ntp_ms, u32 *next_time_ms);
    8447             : 
    8448           0 :                 e = dash_state_check_timing(ctx->state, &next_gen_ntp, &diff);
    8449           0 :                 if (e<0) return e;
    8450           0 :                 if (e==GF_EOS) {
    8451           0 :                         GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] generation called too early by %d ms\n", (s32) diff));
    8452             :                         return e;
    8453             :                 }
    8454             :         }
    8455             :         return GF_OK;
    8456             : }
    8457             : 
    8458             : 
    8459         244 : static void dasher_finalize(GF_Filter *filter)
    8460             : {
    8461         244 :         GF_DasherCtx *ctx = gf_filter_get_udta(filter);
    8462             : 
    8463         845 :         while (gf_list_count(ctx->pids)) {
    8464         357 :                 GF_DashStream *ds = gf_list_pop_back(ctx->pids);
    8465         357 :                 dasher_reset_stream(filter, ds, GF_TRUE);
    8466         357 :                 if (ds->packet_queue) gf_list_del(ds->packet_queue);
    8467         357 :                 if (ds->cinfo) gf_crypt_info_del(ds->cinfo);
    8468         357 :                 gf_free(ds);
    8469             :         }
    8470         244 :         gf_list_del(ctx->pids);
    8471         244 :         if (ctx->mpd) gf_mpd_del(ctx->mpd);
    8472             : 
    8473         590 :         while (gf_list_count(ctx->tpl_records)) {
    8474         346 :                 DashTemplateRecord *tr = gf_list_pop_back(ctx->tpl_records);
    8475         346 :                 gf_free(tr->tpl);
    8476         346 :                 gf_free(tr);
    8477             :         }
    8478         244 :         gf_list_del(ctx->tpl_records);
    8479             : 
    8480         244 :         if (ctx->next_period->period) gf_mpd_period_free(ctx->next_period->period);
    8481         244 :         gf_list_del(ctx->current_period->streams);
    8482         244 :         gf_free(ctx->current_period);
    8483         244 :         gf_list_del(ctx->next_period->streams);
    8484         244 :         gf_free(ctx->next_period);
    8485         244 :         if (ctx->out_path) gf_free(ctx->out_path);
    8486         244 :         gf_list_del(ctx->postponed_pids);
    8487         244 :         if (ctx->cinfo) gf_crypt_info_del(ctx->cinfo);
    8488         244 : }
    8489             : 
    8490             : #define MPD_EXTS "mpd|m3u8|3gm|ism"
    8491             : #define MPD_MIMES "application/dash+xml|video/vnd.3gpp.mpd|audio/vnd.3gpp.mpd|video/vnd.mpeg.dash.mpd|audio/vnd.mpeg.dash.mpd|audio/mpegurl|video/mpegurl|application/vnd.ms-sstr+xml"
    8492             : 
    8493             : static const GF_FilterCapability DasherCaps[] =
    8494             : {
    8495             :         //we accept files as input, but only for NULL file (no source)
    8496             :         CAP_UINT(GF_CAPS_INPUT,  GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
    8497             :         //only with no source
    8498             :         CAP_STRING(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_URL, "*"),
    8499             :         CAP_STRING(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_FILEPATH, "*"),
    8500             : 
    8501             :         CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
    8502             :         CAP_STRING(GF_CAPS_OUTPUT, GF_PROP_PID_FILE_EXT, MPD_EXTS),
    8503             :         CAP_STRING(GF_CAPS_OUTPUT, GF_PROP_PID_MIME, MPD_MIMES),
    8504             :         {0},
    8505             :         //anything AV pid framed result in manifest PID
    8506             :         CAP_UINT(GF_CAPS_INPUT,  GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
    8507             :         CAP_UINT(GF_CAPS_INPUT,  GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
    8508             :         CAP_UINT(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_CODECID, GF_CODECID_NONE),
    8509             :         CAP_BOOL(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
    8510             : 
    8511             :         CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
    8512             :         CAP_STRING(GF_CAPS_OUTPUT, GF_PROP_PID_FILE_EXT, MPD_EXTS),
    8513             :         CAP_STRING(GF_CAPS_OUTPUT, GF_PROP_PID_MIME, MPD_MIMES),
    8514             :         {0},
    8515             :         //anything else (not file, not AV and framed) in compressed format result in manifest PID
    8516             :         //we cannot handle RAW format for such streams as these are in-memory data (scene graph, decoded text, etc ..)
    8517             :         CAP_UINT(GF_CAPS_INPUT_EXCLUDED,  GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
    8518             :         CAP_UINT(GF_CAPS_INPUT_EXCLUDED,  GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
    8519             :         CAP_UINT(GF_CAPS_INPUT_EXCLUDED,  GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
    8520             :         CAP_UINT(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_CODECID, GF_CODECID_RAW),
    8521             :         CAP_BOOL(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
    8522             : 
    8523             :         CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
    8524             :         CAP_STRING(GF_CAPS_OUTPUT, GF_PROP_PID_FILE_EXT, MPD_EXTS),
    8525             :         CAP_STRING(GF_CAPS_OUTPUT, GF_PROP_PID_MIME, MPD_MIMES),
    8526             :         {0},
    8527             :         //anything else (not file and framed) result in media pids not file
    8528             :         CAP_UINT(GF_CAPS_INPUT_EXCLUDED | GF_CAPFLAG_LOADED_FILTER,  GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
    8529             :         CAP_UINT(GF_CAPS_INPUT_EXCLUDED | GF_CAPFLAG_LOADED_FILTER, GF_PROP_PID_CODECID, GF_CODECID_NONE),
    8530             :         CAP_BOOL(GF_CAPS_INPUT_EXCLUDED | GF_CAPFLAG_LOADED_FILTER, GF_PROP_PID_UNFRAMED, GF_TRUE),
    8531             :         CAP_UINT(GF_CAPS_OUTPUT_EXCLUDED | GF_CAPFLAG_LOADED_FILTER, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
    8532             : 
    8533             : };
    8534             : 
    8535             : 
    8536             : #define OFFS(_n)        #_n, offsetof(GF_DasherCtx, _n)
    8537             : static const GF_FilterArgs DasherArgs[] =
    8538             : {
    8539             :         { OFFS(segdur), "target segment duration in seconds. A value less than or equal to 0 means to 1.0 second", GF_PROP_FRACTION, "0/0", NULL, 0},
    8540             :         { OFFS(tpl), "use template mode (multiple segment, template URLs)", GF_PROP_BOOL, "true", NULL, 0},
    8541             :         { OFFS(stl), "use segment timeline (ignored in on_demand mode)", GF_PROP_BOOL, "false", NULL, 0},
    8542             :         { OFFS(dmode), "dash content mode\n"
    8543             :                 "- static: static content\n"
    8544             :                 "- dynamic: live generation\n"
    8545             :                 "- dynlast: last call for live, will turn the MPD into static"
    8546             :                 "", GF_PROP_UINT, "static", "static|dynamic|dynlast", GF_FS_ARG_UPDATE},
    8547             :         { OFFS(sseg), "single segment is used", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
    8548             :         { OFFS(sfile), "use a single file for all segments (default in on_demand)", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
    8549             :         { OFFS(align), "enable segment time alignment between representations", GF_PROP_BOOL, "true", NULL, GF_FS_ARG_HINT_ADVANCED},
    8550             :         { OFFS(sap), "enable splitting segments at SAP boundaries", GF_PROP_BOOL, "true", NULL, 0},
    8551             :         { OFFS(mix_codecs), "enable mixing different codecs in an adaptation set", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
    8552             :         { OFFS(ntp), "insert/override NTP clock at the beginning of each segment\n"
    8553             :         "- rem: removes NTP from all input packets\n"
    8554             :         "- yes: inserts NTP at each segment start\n"
    8555             :         "- keep: leaves input packet NTP untouched", GF_PROP_UINT, "rem", "rem|yes|keep", GF_FS_ARG_HINT_ADVANCED},
    8556             :         { OFFS(no_sar), "do not check for identical sample aspect ratio for adaptation sets", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
    8557             :         { OFFS(m2ts), "generate MPEG-2 TS output", GF_PROP_BOOL, "false", NULL, 0},
    8558             :         { OFFS(bs_switch), "bitstream switching mode (single init segment)\n"
    8559             :         "- def: resolves to off for onDemand and inband for live\n"
    8560             :         "- off: disables BS switching\n"
    8561             :         "- on: enables it if same decoder configuration is possible\n"
    8562             :         "- inband: moves decoder config inband if possible\n"
    8563             :         "- force: enables it even if only one representation\n"
    8564             :         "- multi: uses multiple stsd entries in ISOBMFF", GF_PROP_UINT, "def", "def|off|on|inband|force|multi", GF_FS_ARG_HINT_ADVANCED},
    8565             :         { OFFS(template), "template string to use to generate segment name - see filter help", GF_PROP_STRING, NULL, NULL, 0},
    8566             :         { OFFS(segext), "file extension to use for segments", GF_PROP_STRING, NULL, NULL, 0},
    8567             :         { OFFS(initext), "file extension to use for the init segment", GF_PROP_STRING, NULL, NULL, 0},
    8568             :         { OFFS(muxtype), "muxtype to use for the segments\n"
    8569             :                 "- mp4: uses ISOBMFF format\n"
    8570             :                 "- ts: uses MPEG-2 TS format\n"
    8571             :                 "- mkv: uses Matroska format\n"
    8572             :                 "- webm: uses WebM format\n"
    8573             :                 "- ogg: uses OGG format\n"
    8574             :                 "- raw: uses raw media format (disables muxed representations)\n"
    8575             :                 "- auto: guess format based on extension, default to mp4 if no extension", GF_PROP_UINT, "auto", "mp4|ts|mkv|webm|ogg|raw|auto", 0},
    8576             :         { OFFS(asto), "availabilityStartTimeOffset to use in seconds. A negative value simply increases the AST, a positive value sets the ASToffset to representations", GF_PROP_DOUBLE, "0", NULL, GF_FS_ARG_HINT_ADVANCED},
    8577             :         { OFFS(profile), "target DASH profile. This will set default option values to ensure conformance to the desired profile. For MPEG-2 TS, only main and live are used, others default to main\n"
    8578             :                 "- auto: turns profile to live for dynamic and full for non-dynamic\n"
    8579             :                 "- live: DASH live profile, using segment template\n"
    8580             :                 "- onDemand: MPEG-DASH live profile\n"
    8581             :                 "- main: MPEG-DASH main profile, using segment list\n"
    8582             :                 "- full: MPEG-DASH full profile\n"
    8583             :                 "- hbbtv1.5.live: HBBTV 1.5 DASH profile\n"
    8584             :                 "- dashavc264.live: DASH-IF live profile\n"
    8585             :                 "- dashavc264.onDemand: DASH-IF onDemand profile\n"
    8586             :                 "- dashif.ll: DASH IF low-latency profile (set UTC server to time.akamai.com if none set)"
    8587             :                 "", GF_PROP_UINT, "auto", "auto|live|onDemand|main|full|hbbtv1.5.live|dashavc264.live|dashavc264.onDemand|dashif.ll", 0 },
    8588             :         { OFFS(profX), "list of profile extensions, as used by DASH-IF and DVB. The string will be colon-concatenated with the profile used", GF_PROP_STRING, NULL, NULL, GF_FS_ARG_HINT_ADVANCED },
    8589             :         { OFFS(cp), "content protection element location\n"
    8590             :         "- set: in adaptation set element\n"
    8591             :         "- rep: in representation element\n"
    8592             :         "- both: in both adaptation set and representation elements"
    8593             :         "", GF_PROP_UINT, "set", "set|rep|both", GF_FS_ARG_HINT_ADVANCED },
    8594             :         { OFFS(pssh), "storage mode for PSSH box\n"
    8595             :         "- f: stores in movie fragment only\n"
    8596             :         "- v: stores in movie only\n"
    8597             :         "- m: stores in mpd only\n"
    8598             :         "- mf: stores in mpd and movie fragment\n"
    8599             :         "- mv: stores in mpd and movie\n"
    8600             :         "- n: discard pssh from mpd and segments", GF_PROP_UINT, "v", "v|f|mv|mf|m|n", GF_FS_ARG_HINT_ADVANCED},
    8601             :         { OFFS(buf), "min buffer duration in ms. negative value means percent of segment duration (eg -150 = 1.5*seg_dur)", GF_PROP_SINT, "-100", NULL, 0},
    8602             :         { OFFS(timescale), "set timescale for timeline and segment list/template. A value of 0 picks up the first timescale of the first stream in an adaptation set. A negative value forces using stream timescales for each timed element (multiplication of segment list/template/timelines). A positive value enforces the MPD timescale", GF_PROP_SINT, "0", NULL, GF_FS_ARG_HINT_ADVANCED},
    8603             :         { OFFS(check_dur), "check duration of sources in period, trying to have roughly equal duration. Enforced whenever period start times are used", GF_PROP_BOOL, "true", NULL, 0},
    8604             :         { OFFS(skip_seg), "increment segment number whenever an empty segment would be produced - NOT DASH COMPLIANT", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
    8605             :         { OFFS(title), "MPD title", GF_PROP_STRING, NULL, NULL, 0},
    8606             :         { OFFS(source), "MPD Source", GF_PROP_STRING, NULL, NULL, 0},
    8607             :         { OFFS(info), "MPD info url", GF_PROP_STRING, NULL, NULL, 0},
    8608             :         { OFFS(cprt), "MPD copyright string", GF_PROP_STRING, NULL, NULL, 0},
    8609             :         { OFFS(lang), "language of MPD Info", GF_PROP_STRING, NULL, NULL, 0},
    8610             :         { OFFS(location), "set MPD locations to given URL", GF_PROP_STRING_LIST, NULL, NULL, 0},
    8611             :         { OFFS(base), "set base URLs of MPD", GF_PROP_STRING_LIST, NULL, NULL, GF_FS_ARG_HINT_ADVANCED},
    8612             :         { OFFS(refresh), "refresh rate for dynamic manifests, in seconds. A negative value sets the MPD duration. If 0, uses dash duration", GF_PROP_DOUBLE, "0", NULL, GF_FS_ARG_HINT_ADVANCED},
    8613             :         { OFFS(tsb), "time-shift buffer depth in seconds. A negative value means infinity", GF_PROP_DOUBLE, "30", NULL, 0},
    8614             :         { OFFS(subdur), "maximum duration of the input file to be segmented. This does not change the segment duration, segmentation stops once segments produced exceeded the duration", GF_PROP_DOUBLE, "0", NULL, GF_FS_ARG_HINT_ADVANCED},
    8615             :         { OFFS(ast), "set start date (as xs:date, eg YYYY-MM-DDTHH:MM:SSZ) for live mode. Default is now. !! Do not use with multiple periods, nor when DASH duration is not a multiple of GOP size !!", GF_PROP_STRING, NULL, NULL, GF_FS_ARG_HINT_ADVANCED},
    8616             :         { OFFS(state), "path to file used to store/reload state info when simulating live. This is stored as a valid MPD with GPAC XML extensions", GF_PROP_STRING, NULL, NULL, GF_FS_ARG_HINT_EXPERT},
    8617             :         { OFFS(loop), "loop sources when dashing with subdur and state. If not set, a new period is created once the sources are over", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
    8618             :         { OFFS(split), "enable cloning samples for text/metadata/scene description streams, marking further clones as redundant", GF_PROP_BOOL, "true", NULL, GF_FS_ARG_HINT_ADVANCED},
    8619             :         { OFFS(hlsc), "insert clock reference in variant playlist in live HLS", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
    8620             :         { OFFS(cues), "set cue file - see filter help", GF_PROP_STRING, NULL, NULL, GF_FS_ARG_HINT_EXPERT},
    8621             :         { OFFS(strict_cues), "strict mode for cues, complains if splitting is not on SAP type 1/2/3 or if unused cue is found", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
    8622             :         { OFFS(strict_sap), "strict mode for sap\n"
    8623             :         "- off: ignore SAP types for PID other than video, enforcing _startsWithSAP=1_\n"
    8624             :         "- sig: same as [-off]() but keep _startsWithSAP_ to the true SAP value\n"
    8625             :         "- on: warn if any PID uses SAP 3 or 4 and switch to FULL profile", GF_PROP_UINT, "off", "off|sig|on", GF_FS_ARG_HINT_EXPERT},
    8626             : 
    8627             :         { OFFS(subs_sidx), "number of subsegments per sidx. negative value disables sidx. Only used to inherit sidx option of destination", GF_PROP_SINT, "-1", NULL, GF_FS_ARG_HINT_EXPERT},
    8628             :         { OFFS(cmpd), "skip line feed and spaces in MPD XML for compactness", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
    8629             :         { OFFS(styp), "indicate the 4CC to use for styp boxes when using ISOBMFF output", GF_PROP_STRING, NULL, NULL, GF_FS_ARG_HINT_EXPERT},
    8630             :         { OFFS(dual), "indicate to produce both MPD and M3U files", GF_PROP_BOOL, NULL, NULL, GF_FS_ARG_HINT_ADVANCED},
    8631             :         { OFFS(sigfrag), "use manifest generation only mode - see filter help", GF_PROP_BOOL, NULL, NULL, GF_FS_ARG_HINT_ADVANCED},
    8632             :         { OFFS(_p_gentime), "pointer to u64 holding the ntp clock in ms of next DASH generation in live mode", GF_PROP_POINTER, NULL, NULL, GF_FS_ARG_HINT_HIDE},
    8633             :         { OFFS(_p_mpdtime), "pointer to u64 holding the mpd time in ms of the last generated segment", GF_PROP_POINTER, NULL, NULL, GF_FS_ARG_HINT_HIDE},
    8634             :         { OFFS(sbound), "indicate how the theoretical segment start `TSS (= segment_number * duration)` should be handled\n"
    8635             :                                 "- out: segment split as soon as `TSS` is exceeded (`TSS` <= segment_start)\n"
    8636             :                                 "- closest: segment split at closest SAP to theoretical bound\n"
    8637             :                                 "- in: `TSS` is always in segment (`TSS` >= segment_start)", GF_PROP_UINT, "out", "out|closest|in", GF_FS_ARG_HINT_EXPERT},
    8638             :         { OFFS(reschedule), "reschedule sources with no period ID assigned once done (dynamic mode only)", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
    8639             :         { OFFS(sreg), "regulate the session\n"
    8640             :         "- when using subdur and context, only generate segments from the past up to live edge\n"
    8641             :         "- otherwise in dynamic mode without context, do not generate segments ahead of time", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
    8642             :         { OFFS(scope_deps), "scope PID dependencies to be within source. If disabled, PID dependencies will be checked across all input PIDs regardless of their sources", GF_PROP_BOOL, "true", NULL, GF_FS_ARG_HINT_EXPERT},
    8643             :         { OFFS(utcs), "URL to use as time server / UTCTiming source. Special value `inband` enables inband UTC (same as publishTime), special prefix `xsd@` uses xsDateTime schemeURI rather than ISO", GF_PROP_STRING, NULL, NULL, GF_FS_ARG_HINT_EXPERT},
    8644             :         { OFFS(force_flush), "force generating a single segment for each input. This can be useful in batch mode when average source duration is known and used as segment duration but actual duration may sometimes be greater", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
    8645             :         { OFFS(last_seg_merge), "force merging last segment if less than half the target duration", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
    8646             :         { OFFS(mha_compat), "adaptation set generation mode for compatible MPEG-H Audio profile\n"
    8647             :                 "- no: only generate the adaptation set for the main profile\n"
    8648             :                 "- comp: only generate the adaptation sets for all compatible profiles\n"
    8649             :                 "- all: generate the adaptation set for the main profile and all compatible profiles"
    8650             :                 , GF_PROP_UINT, "no", "no|comp|all", GF_FS_ARG_HINT_EXPERT},
    8651             :         { OFFS(mname), "output manifest name for ATSC3 muxing", GF_PROP_STRING, NULL, NULL, GF_FS_ARG_HINT_EXPERT},
    8652             :         { OFFS(llhls), "HLS low latency type\n"
    8653             :                 "- off: do not use LL-HLS\n"
    8654             :                 "- br: use LL-HLS with byte-range for segment parts, pointing to full segment (DASH-LL compatible)\n"
    8655             :                 "- sf: use separated files for segment parts\n"
    8656             :                 "- brsf: generate two sets of manifest, one for byte-range and one for files (`_IF` added before extension of manifest)", GF_PROP_UINT, "off", "off|br|sf|brsf", GF_FS_ARG_HINT_EXPERT},
    8657             :         { OFFS(cdur), "chunk duration for fragmentation modes", GF_PROP_FRACTION, "-1/1", NULL, GF_FS_ARG_HINT_HIDE},
    8658             :         { OFFS(hlsdrm), "cryp file info for HLS full segment encryption", GF_PROP_STRING, NULL, NULL, GF_FS_ARG_HINT_EXPERT},
    8659             :         { OFFS(cmaf), "use cmaf guidelines\n"
    8660             :                 "- no: CMAF not enforced\n"
    8661             :                 "- cmfc: use CMAF `cmfc` guidelines\n"
    8662             :                 "- cmf2: use CMAF `cmf2` guidelines"
    8663             :                 , GF_PROP_UINT, "no", "no|cmfc|cmf2", GF_FS_ARG_HINT_ADVANCED},
    8664             :         { OFFS(pswitch), "force period switch instead of absorbing PID reconfiguration (for splicing or add insertion not using periodID)", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_HIDE},
    8665             :         {0}
    8666             : };
    8667             : 
    8668             : 
    8669             : GF_FilterRegister DasherRegister = {
    8670             :         .name = "dasher",
    8671             :         GF_FS_SET_DESCRIPTION("DASH and HLS segmenter")
    8672             :         GF_FS_SET_HELP(
    8673             : "# GPAC DASH and HLS segmenter\n"
    8674             : "This filter provides segmentation and manifest generation for MPEG-DASH and HLS formats.\n"
    8675             : "The segmenter currently supports:\n"
    8676             : "- MPD and m3u8 generation (potentially in parallel)\n"
    8677             : "- ISOBMFF, MPEG-2 TS, MKV and raw bitstream segment formats\n"
    8678             : "- override of profiles and levels in manifest for codecs\n"
    8679             : "- most MPEG-DASH profiles\n"
    8680             : "- static and dynamic (live) manifest offering\n"
    8681             : "- context store and reload for batch processing of live/dynamic sessions\n"
    8682             : "\n"
    8683             : "The filter does perform per-segment real-time regulation using [-sreg]().\n"
    8684             : "If you need per-frame real-time regulation on non-real-time inputs, insert a [reframer](reframer) before to perform real-time regulation.\n"
    8685             : "EX src=file.mp4 reframer:rt=on @ -o live.mpd:dmode=dynamic\n"
    8686             : "## Template strings\n"
    8687             : "The segmenter uses templates to derive output file names, regardless of the DASH mode (even when templates are not used). "
    8688             : "The default one is `$File$_dash` for ondemand and single file modes, and `$File$_$Number$` for separate segment files\n"
    8689             : "EX template=Great_$File$_$Width$_$Number$\n"
    8690             : "If input is foo.mp4 with 640x360 video, this will resolve in Great_foo_640_$Number$ for the DASH template.\n"
    8691             : "EX template=Great_$File$_$Width$\n"
    8692             : "If input is foo.mp4 with 640x360 video, this will resolve in Great_foo_640.mp4 for onDemand case.\n"
    8693             : "\n"
    8694             : "Standard DASH replacement strings: \n"
    8695             : "- $Number[%%0Nd]$: replaced by the segment number, possibly prefixed with 0\n"
    8696             : "- $RepresentationID$: replaced by representation name\n"
    8697             : "- $Time$: replaced by segment start time\n"
    8698             : "- $Bandwidth$: replaced by representation bandwidth.\n"
    8699             : "Note: these strings are not replaced in the manifest templates elements.\n"
    8700             : "\n"
    8701             : "Additional replacement strings (not DASH, not generic GPAC replacements but may occur multiple times in template):\n"
    8702             : "- $Init=NAME$: replaced by NAME for init segment, ignored otherwise\n"
    8703             : "- $XInit=NAME$: complete replace by NAME for init segment, ignored otherwise\n"
    8704             : "- $Index=NAME$: replaced by NAME for index segments, ignored otherwise\n"
    8705             : "- $Path=PATH$: replaced by PATH when creating segments, ignored otherwise\n"
    8706             : "- $Segment=NAME$: replaced by NAME for media segments, ignored for init segments\n"
    8707             : "- $FS$ (FileSuffix): replaced by `_trackN` in case the input is an AV multiplex, or kept empty otherwise\n"
    8708             : "Note: these strings are replaced in the manifest templates elements.\n"
    8709             : "\n"
    8710             : "## PID assignment and configuration\n"
    8711             : "To assign PIDs into periods and adaptation sets and configure the session, the segmenter looks for the following properties on each input pid:\n"
    8712             : "- Representation: assigns representation ID to input pid. If not set, the default behavior is to have each media component in different adaptation sets. Setting the RepresentationID allows explicit multiplexing of the source(s)\n"
    8713             : "- Period: assigns period ID to input pid. If not set, the default behavior is to have all media in the same period with the same start time\n"
    8714             : "- PStart: assigns period start. If not set, 0 is assumed, and periods appear in the Period ID declaration order. If negative, this gives the period order (-1 first, then -2 ...). If positive, this gives the true start time and will abort DASHing at period end\n"
    8715             : "Note: When both positive and negative values are found, the by-order periods (negative) will be inserted AFTER the timed period (positive)\n"
    8716             : "- ASID: assigns parent adaptation set ID. If not 0, only sources with same AS ID will be in the same adaptation set\n"
    8717             : "Note: If multiple streams in source, only the first stream will have an AS ID assigned\n"
    8718             : "- xlink: for remote periods, only checked for null pid\n"
    8719             : "- Role, PDesc, ASDesc, ASCDesc, RDesc: various descriptors to set for period, AS or representation\n"
    8720             : "- BUrl: overrides segmenter [-base] with a set of BaseURLs to use for the pid (per representation)\n"
    8721             : "- Template: overrides segmenter [-template]() for this PID\n"
    8722             : "- DashDur: overrides segmenter segment duration for this PID\n"
    8723             : "- StartNumber: sets the start number for the first segment in the PID, default is 1\n"
    8724             : "- IntraOnly: indicates input pid follows HLS EXT-X-I-FRAMES-ONLY guidelines\n"
    8725             : "- Non-dash properties: Bitrate, SAR, Language, Width, Height, SampleRate, NumChannels, Language, ID, DependencyID, FPS, Interlaced, Codec. These properties are used to setup each representation and can be overridden on input PIDs using the general PID property settings (cf global help).\n"
    8726             : "  \n"
    8727             : "EX src=test.mp4:#Bitrate=1M dst=test.mpd\n"
    8728             : "This will force declaring a bitrate of 1M for the representation, regardless of actual input bitrate.\n"
    8729             : "EX src=muxav.mp4 dst=test.mpd\n"
    8730             : "This will create unmuxed DASH segments.\n"
    8731             : "EX src=muxav.mp4:#Representation=1 dst=test.mpd\n"
    8732             : "This will create muxed DASH segments.\n"
    8733             : "EX src=m1.mp4 src=m2.mp4:#Period=Yep dst=test.mpd\n"
    8734             : "This will put src m1.mp4 in first period, m2.mp4 in second period.\n"
    8735             : "EX src=m1.mp4:#BUrl=http://foo/bar dst=test.mpd\n"
    8736             : "This will assign a baseURL to src m1.mp4.\n"
    8737             : "EX src=m1.mp4:#ASCDesc=<ElemName val=\"attval\">text</ElemName> dst=test.mpd\n"
    8738             : "This will assign the specified XML descriptor to the adaptation set.\n"
    8739             : "Note:  this can be used to inject most DASH descriptors not natively handled by the segmenter.\n"
    8740             : "The segmenter handles the XML descriptor as a string and does not attempt to validate it. Descriptors, as well as some segmenter filter arguments, are string lists (comma-separated by default), so that multiple descriptors can be added:\n"
    8741             : "EX src=m1.mp4:#RDesc=<Elem attribute=\"1\"/>,<Elem2>text</Elem2> dst=test.mpd\n"
    8742             : "This will insert two descriptors in the representation(s) of m1.mp4.\n"
    8743             : "EX src=video.mp4:#Template=foo$Number$ src=audio.mp4:#Template=bar$Number$ dst=test.mpd\n"
    8744             : "This will assign different templates to the audio and video sources.\n"
    8745             : "EX src=null:#xlink=http://foo/bar.xml:#PDur=4 src=m.mp4:#PStart=-1\n"
    8746             : "This will insert an create an MPD with first a remote period then a regular one.\n"
    8747             : "EX src=null:#xlink=http://foo/bar.xml:#PStart=6 src=m.mp4\n"
    8748             : "This will create an MPD with first a regular period, dashing ony 6s of content, then a remote one.\n"
    8749             : "\n"
    8750             : "The segmenter will create muxing filter chains for each representation and will reassign PID IDs so that each media component (video, audio, ...) in an adaptation set has the same ID.\n"
    8751             : "\n"
    8752             : "For HLS, the output pid will deliver the master playlist **and** the variant playlists.\n"
    8753             : "The default variant playlist are $NAME_$N.m3u8, where $NAME is the radical of the output file name and $N is the 1-based index of the variant.\n"
    8754             : "\n"
    8755             : "## Segmentation\n"
    8756             : "The default behavior of the segmenter is to estimate the theoretical start time of each segment based on target segment duration, and start a new segment when a packet with SAP type 1,2,3 or 4 with time greater than the theoretical time is found.\n"
    8757             : "This behavior can be changed to find the best SAP packet around a segment theoretical boundary using [-sbound]():\n"
    8758             : "- closest mode: the segment will start at the closest SAP of the theoretical boundary\n"
    8759             : "- in mode: the segment will start at or before the theoretical boundary\n"
    8760             : "Warning: These modes will introduce delay in the segmenter (typically buffering of one GOP) and should not be used for low-latency modes.\n"
    8761             : "The segmenter can also be configured to:\n"
    8762             : "- completely ignore SAP when segmenting using [-sap]().\n"
    8763             : "- ignore SAP on non-video streams when segmenting using [-strict_sap]().\n"
    8764             : "\n"
    8765             : "## Dynamic (real-time live) Mode\n"
    8766             : "The dasher does not perform real-time regulation by default.\n"
    8767             : "For regular segmentation, you should enable segment regulation [-sreg]() if your sources are not real-time.\n"
    8768             : "EX gpac -i source.mp4 -o live.mpd:segdur=2:profile=live:dmode=dynamic:sreg\n"
    8769             : "\n"
    8770             : "For low latency segmentation with fMP4, you will need to specify the following options:\n"
    8771             : "- cdur: set the fMP4 fragment duration\n"
    8772             : "- asto: set the availability time offset for DASH. This value should be equal or slightly greater than segment duration minus cdur\n"
    8773             : "- llhls: enable low latency for HLS\n"
    8774             : "If your sources are not real-time, insert a reframer filter with real-time regulation\n"
    8775             : "EX gpac -i source.mp4 reframer:rt=on @ -o live.mpd:segdur=2:cdur=0.2:asto=1.8:profile=live:dmode=dynamic\n"
    8776             : "This will create DASH segments of 2 seconds made of fragments of 200 ms and indicate to the client that requests can be made 1.8 seconds earlier than segment complete availability on server.\n"
    8777             : "EX gpac -i source.mp4 reframer:rt=on @ -o live.m3u8:segdur=2:cdur=0.2:llhls=br:dmode=dynamic\n"
    8778             : "This will create DASH segments of 2 seconds made of fragments of 200 ms and produce HLS low latency parts using byte ranges in the final segment.\n"
    8779             : "EX gpac -i source.mp4 reframer:rt=on @ -o live.m3u8:segdur=2:cdur=0.2:llhls=sf:dmode=dynamic\n"
    8780             : "This will create DASH segments of 2 seconds made of fragments of 200 ms and produce HLS low latency parts using dedicated files.\n"
    8781             : "\n"
    8782             : "You can combine LL-HLS and DASH-LL generation:\n"
    8783             : "EX gpac -i source.mp4 reframer:rt=on @ -o live.mpd:dual:segdur=2:cdur=0.2:asto=1.8:llhls=br:profile=live:dmode=dynamic\n"
    8784             : "\n"
    8785             : "For DASH, the filter will use the local clock for UTC anchor points in DASH.\n"
    8786             : "The filter can fetch and signal clock in other ways using [-utcs]().\n"
    8787             : "EX [opts]:utcs=inband\n"
    8788             : "This will use the local clock and insert in the MPD a UTCTiming descriptor containing the local clock.\n"
    8789             : "EX [opts]::utcs=http://time.akamai.com[::opts]\n"
    8790             : "This will fetch time from `http://time.akamai.com`, use it as the UTC reference for segment generation and insert in the MPD a UTCTiming descriptor containing the time server URL.\n"
    8791             : "Note: if not set as a global option using `--utcs=`, you must escape the url using double `::` or use other separators.\n"
    8792             : "\n"
    8793             : "## Cue-driven segmentation\n"
    8794             : "The segmenter can take a list of instructions, or Cues, to use for the segmentation process, in which case only these are used to derive segment boundaries. Cues can be set through XML files or injected in input packets.\n"
    8795             : "\n"
    8796             : "Cue files can be specified for the entire segmenter, or per PID using `DashCue` property.\n"
    8797             : "Cues are given in an XML file with a root element called <DASHCues>, with currently no attribute specified. The children are one or more <Stream> elements, with attributes:\n"
    8798             : "- id: integer for stream/track/pid ID\n"
    8799             : "- timescale: integer giving the units of following timestamps\n"
    8800             : "- mode: if present and value is `edit`, the timestamp are in presentation time (edit list applied) otherwise they are in media time\n"
    8801             : "- ts_offset: integer giving a value (in timescale) to subtract to the DTS/CTS values listed\n"
    8802             : "\nThe children of <Stream> are one or more <Cue> elements, with attributes:\n"
    8803             : "- sample: integer giving the sample/frame number of a sample at which splitting shall happen\n"
    8804             : "- dts: long integer giving the decoding time stamp of a sample at which splitting shall happen\n"
    8805             : "- cts: long integer giving the composition / presentation time stamp of a sample at which splitting shall happen\n"
    8806             : "Warning: Cues shall be listed in decoding order.\n"
    8807             : "\n"
    8808             : "If the `DashCue` property of a PID equals `inband`, the PID will be segmented according to the `CueStart` property of input packets.\n"
    8809             : "This feature is typically combined with a list of files as input:\n"
    8810             : "EX -i list.m3u:sigcues -o res/live.mpd"
    8811             : "This will load the `flist` filter in cue mode, generating continuous timelines from the sources and injecting a `CueStart` property at each new file."
    8812             : "\n"
    8813             : "## Manifest Generation only mode\n"
    8814             : "The segmenter can be used to generate manifests from already fragmented ISOBMFF inputs using [-sigfrag]().\n"
    8815             : "In this case, segment boundaries are attached to each packet starting a segment and used to drive the segmentation.\n"
    8816             : "This can be used with single-track ISOBMFF sources, either single file or multi file.\n"
    8817             : "For single file source:\n"
    8818             : "- if onDemand [-profile]() is requested, sources have to be formatted as a DASH self-initializing media segment with the proper sidx.\n"
    8819             : "- templates are disabled.\n"
    8820             : "- [-sseg]() is forced for all profiles except onDemand ones.\n"
    8821             : "For multi files source:\n"
    8822             : "- input shall be a playlist containing the initial file followed by the ordered list of segments.\n"
    8823             : "- if no [-template]() is provided, the full or main [-profile]() will be used\n"
    8824             : "- if [-template]() is provided, it shall be correct: the filter will not try to guess one from the input file names and will not validate it either.\n"
    8825             : "\n"
    8826             : "The manifest generation-only mode supports both MPD and HLS generation.\n"
    8827             : "\n"
    8828             : "EX -i ondemand_src.mp4 -o dash.mpd:sigfrag:profile=onDemand\n"
    8829             : "This will generate a DASH manifest for onDemand Profile based on the input file.\n"
    8830             : "EX -i ondemand_src.mp4 -o dash.m3u8:sigfrag\n"
    8831             : "This will generate a HLS manifest based on the input file.\n"
    8832             : "EX -i seglist.txt -o dash.mpd:sigfrag\n"
    8833             : "This will generate a DASH manifest in Main Profile based on the input files.\n"
    8834             : "EX -i seglist.txt:Template=$XInit=init$$q1/$Number$ -o dash.mpd:sigfrag:profile=live\n"
    8835             : "This will generate a DASH manifest in live Profile based on the input files. The input file will contain `init.mp4`, `q1/1.m4s`, `q1/2.m4s`...\n"
    8836             : "\n"
    8837             : "## Muxer development considerations\n"
    8838             : "Output muxers allowing segmented output must obey the following:\n"
    8839             : "- inspect packet properties\n"
    8840             : " - FileNumber: if set, indicate the start of a new DASH segment\n"
    8841             : " - FileName: if set, indicate the file name. If not present, output shall be a single file. This is only set for packet carrying the `FileNumber` property, and only on one PID (usually the first) for multiplexed outputs\n"
    8842             : " - IDXName: gives the optional index name (if not present, index shall be in the same file as dash segment). Only used for MPEG-2 TS for now\n"
    8843             : " - EODS: property is set on packets with no payload and no timestamp to signal the end of a DASH segment. This is only used when stopping/resuming the segmentation process, in order to flush segments without dispatching an EOS (see [-subdur]() )\n"
    8844             : "- for each segment done, send a downstream event on the first connected PID signaling the size of the segment and the size of its index if any\n"
    8845             : "- for muxers with init data, send a downstream event signaling the size of the init and the size of the global index if any\n"
    8846             : "- the following filter options are passed to muxers, which should declare them as arguments:\n"
    8847             : " - noinit: disables output of init segment for the muxer (used to handle bitstream switching with single init in DASH)\n"
    8848             : " - frag: indicates muxer shall use fragmented format (used for ISOBMFF mostly)\n"
    8849             : " - subs_sidx=0: indicates an SIDX shall be generated - only added if not already specified by user\n"
    8850             : " - xps_inband=all|no: indicates AVC/HEVC/... parameter sets shall be sent inband or out of band\n"
    8851             : " - nofragdef: indicates fragment defaults should be set in each segment rather than in init segment\n"
    8852             : "\n"
    8853             : "The segmenter will add the following properties to the output PIDs:\n"
    8854             : "- DashMode: identifies VoD (single file with global index) or regular DASH mode used by segmenter\n"
    8855             : "- DashDur: identifies target DASH segment duration - this can be used to estimate the SIDX size for example\n"
    8856             :                         )
    8857             :         .private_size = sizeof(GF_DasherCtx),
    8858             :         .args = DasherArgs,
    8859             :         .initialize = dasher_initialize,
    8860             :         .finalize = dasher_finalize,
    8861             :         SETCAPS(DasherCaps),
    8862             :         .flags = GF_FS_REG_REQUIRES_RESOLVER,
    8863             :         .configure_pid = dasher_configure_pid,
    8864             :         .process = dasher_process,
    8865             :         .process_event = dasher_process_event,
    8866             : };
    8867             : 
    8868             : 
    8869        2877 : const GF_FilterRegister *dasher_register(GF_FilterSession *session)
    8870             : {
    8871        2877 :         return &DasherRegister;
    8872             : }

Generated by: LCOV version 1.13