LCOV - code coverage report
Current view: top level - filters - filelist.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 607 1395 43.5 %
Date: 2021-04-29 23:48:07 Functions: 16 23 69.6 %

          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 / file concatenator filter
       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/thread.h>
      29             : #include <gpac/list.h>
      30             : #include <gpac/bitstream.h>
      31             : #include <gpac/isomedia.h>
      32             : #include <gpac/network.h>
      33             : 
      34             : #ifndef GPAC_DISABLE_AV_PARSERS
      35             : #include <gpac/avparse.h>
      36             : #endif
      37             : 
      38             : enum
      39             : {
      40             :         FLIST_STATE_STOP=0,
      41             :         FLIST_STATE_PLAY,
      42             :         FLIST_STATE_WAIT_PLAY,
      43             : };
      44             : 
      45             : typedef struct
      46             : {
      47             :         Bool is_raw, planar;
      48             :         u32 nb_ch, abps, sample_rate;
      49             : } RawAudioInfo;
      50             : 
      51             : 
      52             : typedef struct
      53             : {
      54             :         GF_FilterPid *ipid;
      55             :         GF_FilterPid *opid;
      56             :         GF_FilterPid *opid_aux;
      57             :         u32 stream_type;
      58             :         //in current timescale
      59             :         u64 max_cts, max_dts;
      60             :         u32 o_timescale, timescale;
      61             :         //in output timescale
      62             :         u64 cts_o, dts_o;
      63             :         Bool single_frame;
      64             :         Bool is_eos;
      65             :         u64 dts_sub;
      66             :         u64 first_dts_plus_one;
      67             : 
      68             :         Bool skip_dts_init;
      69             :         u32 play_state;
      70             : 
      71             :         Bool send_cue;
      72             :         s32 delay, initial_delay;
      73             : 
      74             :         RawAudioInfo ra_info, splice_ra_info;
      75             : 
      76             :         s32 audio_samples_to_keep;
      77             : 
      78             :         GF_FilterPid *splice_ipid;
      79             :         Bool splice_ready;
      80             : 
      81             :         u64 cts_o_splice, dts_o_splice;
      82             :         u64 dts_sub_splice;
      83             :         u32 timescale_splice;
      84             :         s32 splice_delay;
      85             :         Bool wait_rap;
      86             : } FileListPid;
      87             : 
      88             : typedef struct
      89             : {
      90             :         char *file_name;
      91             :         u64 last_mod_time;
      92             :         u64 file_size;
      93             : } FileListEntry;
      94             : 
      95             : enum
      96             : {
      97             :         FL_SORT_NONE=0,
      98             :         FL_SORT_NAME,
      99             :         FL_SORT_SIZE,
     100             :         FL_SORT_DATE,
     101             :         FL_SORT_DATEX,
     102             : };
     103             : 
     104             : enum
     105             : {
     106             :         //no splicing
     107             :         FL_SPLICE_NONE=0,
     108             :         //waiting for splice to be active, content is main content
     109             :         FL_SPLICE_BEFORE,
     110             :         //splice is active, content is spliced content
     111             :         FL_SPLICE_ACTIVE,
     112             :         //splice no longer active, content is main content
     113             :         FL_SPLICE_AFTER,
     114             : };
     115             : 
     116             : enum
     117             : {
     118             :         FL_RAW_AV=0,
     119             :         FL_RAW_AUDIO,
     120             :         FL_RAW_VIDEO,
     121             :         FL_RAW_NO
     122             : };
     123             : 
     124             : 
     125             : enum
     126             : {
     127             :         FL_SPLICE_DELTA = 1,
     128             :         FL_SPLICE_FRAME = 1<<1,
     129             : };
     130             : 
     131             : typedef struct
     132             : {
     133             :         //opts
     134             :         Bool revert, sigcues, fdel;
     135             :         u32 raw;
     136             :         s32 floop;
     137             :         u32 fsort;
     138             :         u32 ka;
     139             :         GF_PropStringList srcs;
     140             :         GF_Fraction fdur;
     141             :         u32 timescale;
     142             : 
     143             :         GF_FilterPid *file_pid;
     144             :         char *file_path;
     145             :         u32 last_url_crc;
     146             :         u32 last_url_lineno;
     147             :         u32 last_splice_crc;
     148             :         u32 last_splice_lineno;
     149             :         Bool load_next;
     150             : 
     151             :         GF_List *filter_srcs;
     152             :         GF_List *io_pids;
     153             :         Bool is_eos;
     154             : 
     155             :         GF_Fraction64 cts_offset, dts_offset, wait_dts_plus_one, dts_sub_plus_one;
     156             : 
     157             :         u32 nb_repeat;
     158             :         Double start, stop;
     159             :         Bool do_cat, do_del, src_error;
     160             :         u64 start_range, end_range;
     161             :         GF_List *file_list;
     162             :         s32 file_list_idx;
     163             : 
     164             :         u64 current_file_dur;
     165             :         Bool last_is_isom;
     166             : 
     167             :         u32 wait_update_start;
     168             :         u64 last_file_modif_time;
     169             : 
     170             :         Bool skip_sync;
     171             :         Bool first_loaded;
     172             :         char *frag_url;
     173             : 
     174             :         char *unknown_params;
     175             :         char *pid_props;
     176             : 
     177             :         GF_Fraction64 splice_start, splice_end;
     178             :         u32 flags_splice_start, flags_splice_end;
     179             :         u32 splice_state;
     180             :         FileListPid *splice_ctrl;
     181             : 
     182             :         //splice out, in and current time in **output** timeline
     183             :         u64 splice_end_cts, splice_start_cts, spliced_current_cts;
     184             :         Bool wait_splice_start;
     185             :         Bool wait_splice_end;
     186             :         Bool wait_source;
     187             :         GF_Fraction64 cts_offset_at_splice, dts_offset_at_splice, dts_sub_plus_one_at_splice;
     188             : 
     189             :         GF_List *splice_srcs;
     190             :         char *dyn_period_id;
     191             :         u32 cur_splice_index;
     192             :         u32 splice_nb_repeat;
     193             :         Bool keep_splice, mark_only, was_kept;
     194             :         char *splice_props;
     195             :         char *splice_pid_props;
     196             : 
     197             :         GF_Fraction64 init_splice_start, init_splice_end;
     198             :         u32 init_flags_splice_start, init_flags_splice_end;
     199             :         Double init_start, init_stop;
     200             :         Bool force_splice_resume;
     201             : } GF_FileListCtx;
     202             : 
     203             : static const GF_FilterCapability FileListCapsSrc[] =
     204             : {
     205             :         CAP_UINT(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
     206             :         CAP_UINT(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_CODECID, GF_CODECID_NONE),
     207             : };
     208             : 
     209             : static const GF_FilterCapability FileListCapsSrc_RAW_AV[] =
     210             : {
     211             :         //raw audio and video only
     212             :         CAP_UINT(GF_CAPS_INPUT_OUTPUT,  GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
     213             :         CAP_UINT(GF_CAPS_INPUT_OUTPUT,  GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
     214             :         CAP_UINT(GF_CAPS_INPUT_OUTPUT,  GF_PROP_PID_CODECID, GF_CODECID_RAW),
     215             :         {0},
     216             :         //no restriction for media other than audio and video - cf regular caps for comments
     217             :         CAP_UINT(GF_CAPS_INPUT_EXCLUDED,  GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
     218             :         CAP_UINT(GF_CAPS_INPUT_EXCLUDED,  GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
     219             :         CAP_UINT(GF_CAPS_INPUT_EXCLUDED,  GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
     220             :         CAP_UINT(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_CODECID, GF_CODECID_NONE),
     221             :         CAP_UINT(GF_CAPS_INPUT_EXCLUDED,  GF_PROP_PID_UNFRAMED, GF_TRUE),
     222             : };
     223             : 
     224             : static const GF_FilterCapability FileListCapsSrc_RAW_A[] =
     225             : {
     226             :         //raw audio only
     227             :         CAP_UINT(GF_CAPS_INPUT_OUTPUT,  GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
     228             :         CAP_UINT(GF_CAPS_INPUT_OUTPUT,  GF_PROP_PID_CODECID, GF_CODECID_RAW),
     229             :         {0},
     230             :         //no restriction for media other than audio - cf regular caps for comments
     231             :         CAP_UINT(GF_CAPS_INPUT_EXCLUDED,  GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
     232             :         CAP_UINT(GF_CAPS_INPUT_EXCLUDED,  GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
     233             :         CAP_UINT(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_CODECID, GF_CODECID_NONE),
     234             :         CAP_UINT(GF_CAPS_INPUT_EXCLUDED,  GF_PROP_PID_UNFRAMED, GF_TRUE),
     235             : };
     236             : 
     237             : static const GF_FilterCapability FileListCapsSrc_RAW_V[] =
     238             : {
     239             :         //raw video only
     240             :         CAP_UINT(GF_CAPS_INPUT_OUTPUT,  GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
     241             :         CAP_UINT(GF_CAPS_INPUT_OUTPUT,  GF_PROP_PID_CODECID, GF_CODECID_RAW),
     242             :         {0},
     243             :         //no restriction for media other than video - cf regular caps for comments
     244             :         CAP_UINT(GF_CAPS_INPUT_EXCLUDED,  GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
     245             :         CAP_UINT(GF_CAPS_INPUT_EXCLUDED,  GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
     246             :         CAP_UINT(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_CODECID, GF_CODECID_NONE),
     247             :         CAP_UINT(GF_CAPS_INPUT_EXCLUDED,  GF_PROP_PID_UNFRAMED, GF_TRUE),
     248             : };
     249             : 
     250          61 : static void filelist_start_ipid(GF_FileListCtx *ctx, FileListPid *iopid, u32 prev_timescale)
     251             : {
     252          61 :         iopid->is_eos = GF_FALSE;
     253             : 
     254          61 :         if (!ctx->do_cat) {
     255             :                 GF_FilterEvent evt;
     256             :                 //if we reattached the input, we must send a play request
     257          34 :                 gf_filter_pid_init_play_event(iopid->ipid, &evt, ctx->start, 1.0, "FileList");
     258          34 :                 gf_filter_pid_send_event(iopid->ipid, &evt);
     259          34 :         iopid->skip_dts_init = GF_FALSE;
     260          34 :         if (iopid->play_state==FLIST_STATE_STOP) {
     261           0 :             GF_FEVT_INIT(evt, GF_FEVT_STOP, iopid->ipid)
     262           0 :             gf_filter_pid_send_event(iopid->ipid, &evt);
     263           0 :             iopid->skip_dts_init = GF_TRUE;
     264             :         }
     265             :         }
     266             : 
     267             :         //and convert back cts/dts offsets to output timescale
     268          61 :         if (ctx->dts_offset.num && ctx->dts_offset.den) {
     269          58 :                 iopid->dts_o = ctx->dts_offset.num;
     270          58 :                 iopid->dts_o *= iopid->o_timescale;
     271          58 :                 iopid->dts_o /= ctx->dts_offset.den;
     272             :         } else {
     273           3 :                 iopid->dts_o = 0;
     274             :         }
     275          61 :         if (ctx->cts_offset.num && ctx->cts_offset.den) {
     276          58 :                 iopid->cts_o = ctx->cts_offset.num;
     277          58 :                 iopid->cts_o *= iopid->o_timescale;
     278          58 :                 iopid->cts_o /= ctx->cts_offset.den;
     279             :         } else {
     280           3 :                 iopid->cts_o = 0;
     281             :         }
     282             :         
     283          61 :         if (prev_timescale) {
     284             :                 u64 dts, cts;
     285             : 
     286             :                 //in input timescale
     287          61 :                 dts = iopid->max_dts - iopid->dts_sub;
     288          61 :                 cts = iopid->max_cts - iopid->dts_sub;
     289             :                 //convert to output timescale
     290          61 :                 if (prev_timescale != iopid->o_timescale) {
     291           4 :                         dts *= iopid->o_timescale;
     292           4 :                         dts /= prev_timescale;
     293             : 
     294           4 :                         cts *= iopid->o_timescale;
     295           4 :                         cts /= prev_timescale;
     296             :                 }
     297          61 :                 if (
     298             :                         //skip sync mode, do not adjust timestamps
     299          61 :                         ctx->skip_sync
     300             :                         //in case of rounding
     301          61 :                         || ((dts > iopid->dts_o) && (cts > iopid->cts_o))
     302             :                 ) {
     303           0 :                         iopid->dts_o = dts;
     304           0 :                         iopid->cts_o = cts;
     305             :                 }
     306             :         }
     307          61 :         iopid->max_cts = iopid->max_dts = 0;
     308             : 
     309          61 :         iopid->first_dts_plus_one = 0;
     310          61 : }
     311             : 
     312         119 : static Bool filelist_merge_prop(void *cbk, u32 prop_4cc, const char *prop_name, const GF_PropertyValue *src_prop)
     313             : {
     314             :         const GF_PropertyValue *p;
     315             :         GF_FilterPid *pid = (GF_FilterPid *) cbk;
     316             : 
     317         119 :         if (prop_4cc) p = gf_filter_pid_get_property(pid, prop_4cc);
     318           0 :         else p = gf_filter_pid_get_property_str(pid, prop_name);
     319         119 :         if (p) return GF_FALSE;
     320           2 :         return GF_TRUE;
     321             : }
     322             : 
     323           0 : static void filelist_push_period_id(GF_FileListCtx *ctx, GF_FilterPid *opid)
     324             : {
     325             :         char szID[200];
     326           0 :         char *dyn_period = NULL;
     327             : 
     328           0 :         gf_dynstrcat(&dyn_period, ctx->dyn_period_id, NULL);
     329           0 :         sprintf(szID, "_%d", ctx->cur_splice_index+1);
     330           0 :         gf_dynstrcat(&dyn_period, szID, NULL);
     331           0 :         gf_filter_pid_set_property(opid, GF_PROP_PID_PERIOD_ID, &PROP_STRING_NO_COPY(dyn_period));
     332           0 : }
     333             : 
     334          10 : static void filelist_override_caps(GF_Filter *filter, GF_FileListCtx *ctx)
     335             : {
     336          10 :         switch (ctx->raw) {
     337           0 :         case FL_RAW_AV:
     338           0 :                 gf_filter_override_caps(filter, FileListCapsSrc_RAW_AV, GF_ARRAY_LENGTH(FileListCapsSrc_RAW_AV) );
     339             :                 break;
     340           0 :         case FL_RAW_AUDIO:
     341           0 :                 gf_filter_override_caps(filter, FileListCapsSrc_RAW_A, GF_ARRAY_LENGTH(FileListCapsSrc_RAW_A) );
     342             :                 break;
     343           0 :         case FL_RAW_VIDEO:
     344           0 :                 gf_filter_override_caps(filter, FileListCapsSrc_RAW_V, GF_ARRAY_LENGTH(FileListCapsSrc_RAW_V) );
     345             :                 break;
     346          10 :         default:
     347          10 :                 gf_filter_override_caps(filter, FileListCapsSrc, 2);
     348             :         }
     349          10 : }
     350             : 
     351          47 : static GF_Err filelist_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
     352             : {
     353             :         FileListPid *iopid;
     354             :         GF_FilterPid *opid = NULL;
     355             :         const GF_PropertyValue *p;
     356             :         u32 i, count;
     357             :         Bool reassign = GF_FALSE;
     358             :         Bool first_config = GF_FALSE;
     359             :         u32 force_bitrate = 0;
     360             :         u32 prev_timescale = 0;
     361             :         char *src_url = NULL;
     362          47 :         GF_FileListCtx *ctx = gf_filter_get_udta(filter);
     363             : 
     364          47 :         if (is_remove) {
     365          10 :                 if (pid==ctx->file_pid)
     366           0 :                         ctx->file_pid = NULL;
     367             :                 else {
     368          10 :                         iopid = gf_filter_pid_get_udta(pid);
     369          10 :                         if (iopid)
     370          10 :                                 iopid->ipid = NULL;
     371             :                 }
     372             :                 return GF_OK;
     373             :         }
     374             : 
     375          37 :         if (!ctx->file_pid && !ctx->file_list) {
     376           7 :                 if (! gf_filter_pid_check_caps(pid))
     377             :                         return GF_NOT_SUPPORTED;
     378           7 :                 ctx->file_pid = pid;
     379             : 
     380             :                 //we will declare pids later
     381             : 
     382             :                 //from now on we only accept the above caps
     383           7 :                 filelist_override_caps(filter, ctx);
     384             :         }
     385          37 :         if (ctx->file_pid == pid) return GF_OK;
     386             : 
     387             :         iopid = NULL;
     388          23 :         count = gf_list_count(ctx->io_pids);
     389          23 :         for (i=0; i<count; i++) {
     390          13 :                 iopid = gf_list_get(ctx->io_pids, i);
     391          13 :                 if (iopid->ipid==pid) break;
     392             :                 //check matching stream types if out pit not connected, and reuse if matching
     393          10 :                 if (!iopid->ipid) {
     394          10 :                         p = gf_filter_pid_get_property(pid, GF_PROP_PID_STREAM_TYPE);
     395             :                         assert(p);
     396          10 :                         if (p->value.uint == iopid->stream_type) {
     397          10 :                                 iopid->ipid = pid;
     398          10 :                                 prev_timescale = iopid->timescale;
     399          10 :                                 iopid->timescale = gf_filter_pid_get_timescale(pid);
     400             :                                 reassign = GF_TRUE;
     401          10 :                                 break;
     402             :                         }
     403             :                 }
     404             :                 iopid = NULL;
     405             :         }
     406             : 
     407          23 :         if (!iopid) {
     408          10 :                 GF_SAFEALLOC(iopid, FileListPid);
     409          10 :                 if (!iopid) return GF_OUT_OF_MEM;
     410          10 :                 iopid->ipid = pid;
     411          10 :                 if (ctx->timescale) iopid->o_timescale = ctx->timescale;
     412             :                 else {
     413          10 :                         iopid->o_timescale = gf_filter_pid_get_timescale(pid);
     414          10 :                         if (!iopid->o_timescale) iopid->o_timescale = 1000;
     415             :                 }
     416          10 :                 gf_list_add(ctx->io_pids, iopid);
     417          10 :                 iopid->send_cue = ctx->sigcues;
     418          10 :                 iopid->play_state = FLIST_STATE_WAIT_PLAY;
     419             :                 first_config = GF_TRUE;
     420             :         }
     421          23 :         gf_filter_pid_set_udta(pid, iopid);
     422             : 
     423          23 :         if (!iopid->opid) {
     424          10 :                 iopid->opid = gf_filter_pid_new(filter);
     425          10 :                 p = gf_filter_pid_get_property(pid, GF_PROP_PID_STREAM_TYPE);
     426             :                 assert(p);
     427          10 :                 iopid->stream_type = p->value.uint;
     428             :         }
     429          23 :         gf_filter_pid_set_framing_mode(pid, GF_TRUE);
     430             : 
     431          23 :         if (ctx->sigcues) {
     432           0 :                 p = gf_filter_pid_get_property(iopid->opid, GF_PROP_PID_BITRATE);
     433           0 :                 if (!p) p = gf_filter_pid_get_property(iopid->ipid, GF_PROP_PID_BITRATE);
     434           0 :                 if (p) force_bitrate = p->value.uint;
     435             : 
     436           0 :                 p = gf_filter_pid_get_property(iopid->ipid, GF_PROP_PID_URL);
     437           0 :                 if (p && p->value.string) src_url = gf_strdup(p->value.string);
     438             : 
     439             :         }
     440          23 :         opid = iopid->opid;
     441             : 
     442          23 :         if (ctx->keep_splice && (ctx->splice_state==FL_SPLICE_ACTIVE) && iopid->splice_ipid) {
     443             :                 assert(!iopid->opid_aux);
     444           0 :                 iopid->opid_aux = gf_filter_pid_new(filter);
     445             :                 opid = iopid->opid_aux;
     446             : 
     447           0 :                 gf_filter_pid_set_property_str(iopid->opid, "period_switch", &PROP_BOOL(GF_TRUE));
     448           0 :                 gf_filter_pid_set_property_str(iopid->opid, "period_resume", &PROP_STRING(ctx->dyn_period_id ? ctx->dyn_period_id : "") );
     449             : 
     450           0 :                 if (ctx->splice_props)
     451           0 :                         gf_filter_pid_push_properties(iopid->opid, ctx->splice_props, GF_TRUE, GF_TRUE);
     452             : 
     453             :         } else {
     454          23 :                 gf_filter_pid_set_property_str(iopid->opid, "period_switch", NULL);
     455             :         }
     456             : 
     457             :         //copy properties at init or reconfig:
     458             :         //reset (no more props)
     459          23 :         gf_filter_pid_reset_properties(opid);
     460             : 
     461          23 :         gf_filter_pid_copy_properties(opid, iopid->ipid);
     462             : 
     463             :         //if file pid is defined, merge its properties. This will allow forwarding user-defined properties,
     464             :         // eg -i list.txt:#MyProp=toto to all PIDs coming from the sources
     465          23 :         if (ctx->file_pid) {
     466          17 :                 gf_filter_pid_merge_properties(opid, ctx->file_pid, filelist_merge_prop, iopid->ipid);
     467             : 
     468          17 :                 p = gf_filter_pid_get_property(opid, GF_PROP_PID_MIME);
     469          17 :                 if (p && p->value.string && !strcmp(p->value.string, "application/x-gpac-playlist"))
     470           2 :                         gf_filter_pid_set_property(opid, GF_PROP_PID_MIME, NULL);
     471             :         }
     472             : 
     473             :         //we could further optimize by querying the duration of all sources in the list
     474          23 :         gf_filter_pid_set_property(opid, GF_PROP_PID_PLAYBACK_MODE, &PROP_UINT(GF_PLAYBACK_MODE_NONE) );
     475          23 :         gf_filter_pid_set_property(opid, GF_PROP_PID_TIMESCALE, &PROP_UINT(iopid->o_timescale) );
     476          23 :         gf_filter_pid_set_property(opid, GF_PROP_PID_NB_FRAMES, NULL );
     477             : 
     478          23 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_NB_FRAMES);
     479          23 :         iopid->single_frame = (p && (p->value.uint==1)) ? GF_TRUE : GF_FALSE ;
     480          23 :         iopid->timescale = gf_filter_pid_get_timescale(pid);
     481          23 :         if (!iopid->timescale) iopid->timescale = 1000;
     482             : 
     483          23 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_CODECID);
     484          23 :         if (p && (p->value.uint==GF_CODECID_RAW) && (iopid->stream_type==GF_STREAM_AUDIO)) {
     485           0 :                 p = gf_filter_pid_get_property(pid, GF_PROP_PID_NUM_CHANNELS);
     486           0 :                 iopid->ra_info.nb_ch = p ? p->value.uint : 1;
     487           0 :                 p = gf_filter_pid_get_property(pid, GF_PROP_PID_SAMPLE_RATE);
     488           0 :                 iopid->ra_info.sample_rate = p ? p->value.uint : 0;
     489           0 :                 iopid->ra_info.abps = 0;
     490           0 :                 p = gf_filter_pid_get_property(pid, GF_PROP_PID_AUDIO_FORMAT);
     491           0 :                 if (p) {
     492           0 :                         iopid->ra_info.abps = gf_audio_fmt_bit_depth(p->value.uint) / 8;
     493           0 :                         iopid->ra_info.planar = (p->value.uint > GF_AUDIO_FMT_LAST_PACKED) ? 1 : 0;
     494             :                 }
     495           0 :                 iopid->ra_info.abps *= iopid->ra_info.nb_ch;
     496           0 :                 iopid->ra_info.is_raw = GF_TRUE;
     497             :         } else {
     498          23 :                 iopid->ra_info.abps = iopid->ra_info.nb_ch = iopid->ra_info.sample_rate = 0;
     499          23 :                 iopid->ra_info.is_raw = GF_FALSE;
     500             :         }
     501             : 
     502          23 :         if (ctx->frag_url)
     503           0 :                 gf_filter_pid_set_property(opid, GF_PROP_PID_ORIG_FRAG_URL, &PROP_NAME(ctx->frag_url) );
     504             : 
     505          23 :         if (ctx->sigcues) {
     506           0 :                 if (!src_url) {
     507           0 :                         gf_filter_pid_set_property(opid, GF_PROP_PID_URL, &PROP_STRING("mysource") );
     508           0 :                         gf_filter_pid_set_property(opid, GF_PROP_PID_FILEPATH, &PROP_STRING("mysource") );
     509             :                 } else {
     510           0 :                         gf_filter_pid_set_property(opid, GF_PROP_PID_URL, &PROP_STRING(src_url) );
     511           0 :                         gf_filter_pid_set_property(opid, GF_PROP_PID_FILEPATH, &PROP_STRING_NO_COPY(src_url));
     512             :                 }
     513           0 :                 if (force_bitrate)
     514           0 :                         gf_filter_pid_set_property(opid, GF_PROP_PID_BITRATE, &PROP_UINT(force_bitrate) );
     515             :                 else
     516           0 :                         gf_filter_pid_set_property(opid, GF_PROP_PID_BITRATE, NULL );
     517             : 
     518           0 :                 gf_filter_pid_set_property(opid, GF_PROP_PID_DASH_CUE, &PROP_STRING("inband") );
     519             :         } else {
     520          23 :                 p = gf_filter_pid_get_property(pid, GF_PROP_PID_URL);
     521          23 :                 if (p)
     522          23 :                         gf_filter_pid_set_property(opid, GF_PROP_PID_URL, p);
     523             :         }
     524             : 
     525          23 :         if (ctx->pid_props) {
     526           0 :                 gf_filter_pid_push_properties(opid, ctx->pid_props, GF_TRUE, GF_TRUE);
     527             :         }
     528             : 
     529          23 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_DELAY);
     530          23 :         iopid->delay = p ? (s32) p->value.longsint : 0;
     531          23 :         if (first_config) {
     532          10 :                 iopid->initial_delay = iopid->delay;
     533             :         } else {
     534          13 :                 gf_filter_pid_set_property(opid, GF_PROP_PID_DELAY, iopid->initial_delay ? &PROP_LONGSINT(iopid->initial_delay) : NULL);
     535             :         }
     536             : 
     537          23 :         if (ctx->splice_state==FL_SPLICE_BEFORE) {
     538           0 :                 if (!ctx->splice_ctrl) ctx->splice_ctrl = iopid;
     539           0 :                 else if (iopid->stream_type==GF_STREAM_VISUAL) ctx->splice_ctrl = iopid;
     540           0 :                 iopid->timescale_splice = iopid->timescale;
     541           0 :                 iopid->splice_delay = iopid->delay;
     542             : 
     543           0 :                 if (ctx->dyn_period_id) gf_free(ctx->dyn_period_id);
     544           0 :                 p = gf_filter_pid_get_property(opid, GF_PROP_PID_PERIOD_ID);
     545           0 :                 if (p && p->value.string) {
     546           0 :                         ctx->dyn_period_id = gf_strdup(p->value.string);
     547             :                 } else {
     548           0 :                         ctx->dyn_period_id = NULL;
     549             :                 }
     550             :         }
     551          23 :         if (ctx->dyn_period_id) {
     552           0 :                 if ((ctx->splice_state==FL_SPLICE_BEFORE) || (ctx->splice_state==FL_SPLICE_AFTER)) {
     553           0 :                         filelist_push_period_id(ctx, opid);
     554             :                 }
     555             :                 //reset period ID if we keep the splice, and copy it from spliced content if any
     556           0 :                 else if ((ctx->splice_state==FL_SPLICE_ACTIVE) && ctx->keep_splice) {
     557           0 :                         gf_filter_pid_set_property(iopid->opid, GF_PROP_PID_PERIOD_ID, NULL);
     558           0 :                         p = gf_filter_pid_get_property(opid, GF_PROP_PID_PERIOD_ID);
     559           0 :                         if (p && p->value.string)
     560           0 :                                 gf_filter_pid_set_property(iopid->opid, GF_PROP_PID_PERIOD_ID, p);
     561             :                 }
     562             :         }
     563          23 :         gf_filter_pid_set_property_str(opid, "period_resume", NULL);
     564             : 
     565             :         //if we reattached the input, we must send a play request
     566          23 :         if (reassign) {
     567          10 :                 filelist_start_ipid(ctx, iopid, prev_timescale);
     568             :         }
     569             : 
     570             :         return GF_OK;
     571             : }
     572             : 
     573         405 : static Bool filelist_process_event(GF_Filter *filter, const GF_FilterEvent *evt)
     574             : {
     575             :         u32 i, count;
     576             :         FileListPid *iopid;
     577             :         GF_FilterEvent fevt;
     578         405 :         GF_FileListCtx *ctx = gf_filter_get_udta(filter);
     579             : 
     580             :         //manually forward event to all input, except our file in pid
     581             :         memcpy(&fevt, evt, sizeof(GF_FilterEvent));
     582         405 :         count = gf_list_count(ctx->io_pids);
     583         803 :         for (i=0; i<count; i++) {
     584         398 :                 iopid = gf_list_get(ctx->io_pids, i);
     585         398 :                 if (!iopid->ipid) continue;
     586             : 
     587             :         //only send on non connected inputs or on the one matching the pid event
     588         397 :         if (iopid->opid && (iopid->opid != evt->base.on_pid))
     589         386 :             continue;
     590             : 
     591          11 :                 fevt.base.on_pid = iopid->ipid;
     592          11 :                 if (evt->base.type==GF_FEVT_PLAY) {
     593          10 :                         gf_filter_pid_init_play_event(iopid->ipid, &fevt, ctx->start, 1.0, "FileList");
     594          10 :                         iopid->play_state = FLIST_STATE_PLAY;
     595          10 :                         iopid->is_eos = GF_FALSE;
     596           1 :                 } else if (evt->base.type==GF_FEVT_STOP) {
     597           0 :                         iopid->play_state = FLIST_STATE_STOP;
     598           0 :                         iopid->is_eos = GF_TRUE;
     599             :                 }
     600          11 :                 gf_filter_pid_send_event(iopid->ipid, &fevt);
     601             :         }
     602             :         //and cancel
     603         405 :         return GF_TRUE;
     604             : }
     605             : 
     606             : 
     607          47 : static void filelist_check_implicit_cat(GF_FileListCtx *ctx, char *szURL)
     608             : {
     609             :         char *res_url = NULL;
     610          47 :         if (ctx->file_path) {
     611          41 :                 res_url = gf_url_concatenate(ctx->file_path, szURL);
     612             :                 szURL = res_url;
     613             :         }
     614             : 
     615          47 :         switch (gf_isom_probe_file(szURL)) {
     616             :         //this is a fragment
     617          27 :         case 3:
     618          27 :                 if (ctx->last_is_isom) {
     619          27 :                         ctx->do_cat = GF_TRUE;
     620             :                 }
     621             :                 break;
     622             :         //this is a movie, reload
     623           3 :         case 2:
     624             :         case 1:
     625           3 :                 ctx->do_cat = GF_FALSE;
     626           3 :                 ctx->last_is_isom = GF_TRUE;
     627           3 :                 break;
     628          17 :         default:
     629          17 :                 ctx->do_cat = GF_FALSE;
     630          17 :                 ctx->last_is_isom = GF_FALSE;
     631             :         }
     632          47 :         if (res_url)
     633          41 :                 gf_free(res_url);
     634          47 : }
     635             : 
     636           0 : static void filelist_parse_splice_time(char *aval, GF_Fraction64 *frac, u32 *flags)
     637             : {
     638           0 :         *flags = 0;
     639           0 :         if (!aval || !strlen(aval)) {
     640           0 :                 frac->num = -1;
     641           0 :                 frac->den = 1;
     642           0 :                 return;
     643             :         }
     644           0 :         frac->num = 0;
     645           0 :         frac->den = 0;
     646             : 
     647           0 :         if (!stricmp(aval, "now")) {
     648           0 :                 frac->num = -2;
     649           0 :                 frac->den = 1;
     650           0 :                 return;
     651             :         }
     652           0 :         if (strchr(aval, ':')) {
     653           0 :                 frac->den = gf_net_parse_date(aval);
     654           0 :                 frac->num = -3;
     655             :         }
     656             : 
     657           0 :         if (aval[0]=='+') {
     658           0 :                 aval ++;
     659           0 :                 *flags |= FL_SPLICE_DELTA;
     660             :         }
     661           0 :         if (aval[0]=='F') {
     662           0 :                 aval ++;
     663           0 :                 *flags |= FL_SPLICE_FRAME;
     664             :         }
     665             : 
     666           0 :         gf_parse_lfrac(aval, frac);
     667             : }
     668             : 
     669          57 : static Bool filelist_next_url(GF_Filter *filter, GF_FileListCtx *ctx, char szURL[GF_MAX_PATH], Bool is_splice_update)
     670             : {
     671             :         u32 len;
     672             :         u32 url_crc = 0;
     673             :         Bool last_found = GF_FALSE;
     674             :         FILE *f;
     675             :         u32 nb_repeat=0, lineno=0;
     676          57 :         u64 start_range=0, end_range=0;
     677             :         Double start=0, stop=0;
     678             :         GF_Fraction64 splice_start, splice_end;
     679             :         Bool do_cat=0;
     680             :         Bool do_del=0;
     681             :         Bool is_end=0;
     682             :         Bool keep_splice=0;
     683             :         Bool mark_only=0;
     684             :         Bool no_sync=0;
     685          57 :         u32 start_flags=0;
     686          57 :         u32 end_flags=0;
     687             : 
     688          57 :         if (ctx->file_list) {
     689             :                 FileListEntry *fentry, *next;
     690             : 
     691           9 :                 if (ctx->revert) {
     692           0 :                         if (!ctx->file_list_idx) {
     693           0 :                                 if (!ctx->floop) return GF_FALSE;
     694           0 :                                 if (ctx->floop>0) ctx->floop--;
     695           0 :                                 ctx->file_list_idx = gf_list_count(ctx->file_list);
     696             :                         }
     697           0 :                         ctx->file_list_idx --;
     698             :                 } else {
     699           9 :                         ctx->file_list_idx ++;
     700           9 :                         if (ctx->file_list_idx >= (s32) gf_list_count(ctx->file_list)) {
     701           3 :                                 if (!ctx->floop) return GF_FALSE;
     702           0 :                                 if (ctx->floop>0) ctx->floop--;
     703           0 :                                 ctx->file_list_idx = 0;
     704             :                         }
     705             :                 }
     706           6 :                 fentry = gf_list_get(ctx->file_list, ctx->file_list_idx);
     707           6 :                 strncpy(szURL, fentry->file_name, sizeof(char)*(GF_MAX_PATH-1) );
     708           6 :                 szURL[GF_MAX_PATH-1] = 0;
     709           6 :                 filelist_check_implicit_cat(ctx, szURL);
     710           6 :                 next = gf_list_get(ctx->file_list, ctx->file_list_idx + 1);
     711           6 :                 if (next)
     712           3 :                         ctx->current_file_dur = next->last_mod_time - fentry->last_mod_time;
     713             :                 return GF_TRUE;
     714             :         }
     715             : 
     716          48 :         if (ctx->wait_update_start || is_splice_update) {
     717           0 :                 u64 last_modif_time = gf_file_modification_time(ctx->file_path);
     718           0 :                 if (ctx->last_file_modif_time >= last_modif_time) {
     719           0 :                         if (!is_splice_update) {
     720           0 :                                 u32 diff = gf_sys_clock() - ctx->wait_update_start;
     721           0 :                                 if (diff > 60 * ctx->ka) {
     722           0 :                                         GF_LOG(GF_LOG_WARNING, GF_LOG_AUTHOR, ("[FileList] Timeout refreshing playlist after %d ms, triggering eos\n", diff));
     723           0 :                                         ctx->ka = 0;
     724             :                                 }
     725             :                         }
     726             :                         return GF_FALSE;
     727             :                 }
     728           0 :                 ctx->wait_update_start = 0;
     729           0 :                 ctx->last_file_modif_time = last_modif_time;
     730             :         }
     731             : 
     732          48 :         splice_start.num = splice_end.num = 0;
     733          48 :         splice_start.den = splice_end.den = 0;
     734             : 
     735          48 :         f = gf_fopen(ctx->file_path, "rt");
     736         302 :         while (f) {
     737         254 :                 char *l = gf_fgets(szURL, GF_MAX_PATH, f);
     738             :                 url_crc = 0;
     739         254 :                 if (!l || (gf_feof(f) && !szURL[0]) ) {
     740           7 :                         if (ctx->floop != 0) {
     741           0 :                                 gf_fseek(f, 0, SEEK_SET);
     742             :                                 //load first line
     743             :                                 last_found = GF_TRUE;
     744           0 :                                 continue;
     745             :                         }
     746           7 :                         gf_fclose(f);
     747           7 :                         if (is_end) {
     748           0 :                                 ctx->ka = 0;
     749           7 :                         } else if (ctx->ka) {
     750           0 :                                 ctx->wait_update_start = gf_sys_clock();
     751           0 :                                 ctx->last_file_modif_time = gf_file_modification_time(ctx->file_path);
     752             :                         }
     753             :                         return GF_FALSE;
     754             :                 }
     755             : 
     756         247 :                 len = (u32) strlen(szURL);
     757             :                 //in keep-alive mode, each line shall end with \n, of not consider the file is not yet ready
     758         247 :                 if (ctx->ka && !is_end && len && (szURL[len-1]!='\n')) {
     759           0 :                         gf_fclose(f);
     760           0 :                         ctx->wait_update_start = gf_sys_clock();
     761           0 :                         ctx->last_file_modif_time = gf_file_modification_time(ctx->file_path);
     762           0 :                         return GF_FALSE;
     763             :                 }
     764             :                 //strip all \r, \n, \t at the end
     765         494 :                 while (len && strchr("\n\r\t ", szURL[len-1])) {
     766         247 :                         szURL[len-1] = 0;
     767             :                         len--;
     768             :                 }
     769         247 :                 if (!len) continue;
     770             : 
     771             :                 //comment
     772         232 :                 if (szURL[0] == '#') {
     773             :                         //ignored line
     774           2 :                         if (szURL[1] == '#')
     775           0 :                                 continue;
     776             : 
     777           2 :                         char *args = szURL+1;
     778             :                         nb_repeat=0;
     779             :                         start=stop=0;
     780             :                         do_cat = 0;
     781           2 :                         start_range=end_range=0;
     782           2 :                         if (ctx->pid_props) {
     783           0 :                                 gf_free(ctx->pid_props);
     784           0 :                                 ctx->pid_props = NULL;
     785             :                         }
     786             : 
     787           2 :                         while (args) {
     788             :                                 char c;
     789             :                                 char *sep, *aval = NULL;
     790           0 :                                 while (args[0]==' ') args++;
     791             : 
     792           2 :                                 sep = strchr(args, ' ');
     793           2 :                                 if (strncmp(args, "props", 5))
     794           2 :                                         aval = strchr(args, ',');
     795             : 
     796           2 :                                 if (sep && aval && (aval < sep))
     797             :                                         sep = aval;
     798           2 :                                 else if (aval)
     799             :                                         sep = aval;
     800             : 
     801           2 :                                 if (sep) {
     802           0 :                                         c = sep[0];
     803           0 :                                         sep[0] = 0;
     804             :                                 }
     805           2 :                                 aval = strchr(args, '=');
     806           2 :                                 if (aval) {
     807           2 :                                         aval[0] = 0;
     808           2 :                                         aval++;
     809             :                                 }
     810             : 
     811           2 :                                 if (!strcmp(args, "repeat")) {
     812           2 :                                         if (aval)
     813           2 :                                                 nb_repeat = atoi(aval);
     814           0 :                                 } else if (!strcmp(args, "start")) {
     815           0 :                                         if (aval)
     816             :                                                 start = atof(aval);
     817           0 :                                 } else if (!strcmp(args, "stop")) {
     818           0 :                                         if (aval)
     819             :                                                 stop = atof(aval);
     820           0 :                                 } else if (!strcmp(args, "cat")) {
     821             :                                         do_cat = GF_TRUE;
     822           0 :                                 } else if (!strcmp(args, "del")) {
     823             :                                         do_del = GF_TRUE;
     824           0 :                                 } else if (do_cat && !strcmp(args, "srange")) {
     825           0 :                                         if (aval)
     826           0 :                                                 sscanf(aval, LLU, &start_range);
     827           0 :                                 } else if (do_cat && !strcmp(args, "send")) {
     828           0 :                                         if (aval)
     829           0 :                                                 sscanf(aval, LLU, &end_range);
     830           0 :                                 } else if (ctx->ka && !strcmp(args, "end")) {
     831             :                                         is_end = GF_TRUE;
     832           0 :                                 } else if (!strcmp(args, "ka")) {
     833           0 :                                         sscanf(aval, "%u", &ctx->ka);
     834           0 :                                 } else if (!strcmp(args, "raw")) {
     835             :                                         u32 raw_type = 0;
     836           0 :                                         if (aval) {
     837           0 :                                                 if (!stricmp(aval, "av")) raw_type = FL_RAW_AV;
     838           0 :                                                 else if (!stricmp(aval, "a")) raw_type = FL_RAW_AUDIO;
     839           0 :                                                 else if (!stricmp(aval, "v")) raw_type = FL_RAW_VIDEO;
     840             :                                         }
     841           0 :                                         if (filter && (ctx->raw != raw_type) ) {
     842           0 :                                                 ctx->raw = raw_type;
     843           0 :                                                 filelist_override_caps(filter, ctx);
     844             :                                         }
     845           0 :                                 } else if (!strcmp(args, "floop")) {
     846           0 :                                         ctx->floop = atoi(aval);
     847           0 :                                 } else if (!strcmp(args, "props")) {
     848           0 :                                         if (ctx->pid_props) gf_free(ctx->pid_props);
     849           0 :                                         ctx->pid_props = aval ? gf_strdup(aval) : NULL;
     850           0 :                                 } else if (!strcmp(args, "out")) {
     851           0 :                                         filelist_parse_splice_time(aval, &splice_start, &start_flags);
     852           0 :                                 } else if (!strcmp(args, "in")) {
     853           0 :                                         filelist_parse_splice_time(aval, &splice_end, &end_flags);
     854           0 :                                 } else if (!strcmp(args, "keep")) {
     855             :                                         keep_splice = GF_TRUE;
     856           0 :                                 } else if (!strcmp(args, "mark")) {
     857             :                                         mark_only = GF_TRUE;
     858           0 :                                 } else if (!strcmp(args, "nosync")) {
     859             :                                         no_sync = GF_TRUE;
     860           0 :                                 } else if (!strcmp(args, "sprops")) {
     861           0 :                                         if (ctx->splice_props) gf_free(ctx->splice_props);
     862           0 :                                         ctx->splice_props = aval ? gf_strdup(aval) : NULL;
     863             :                                 } else {
     864           0 :                                         if (!ctx->unknown_params || !strstr(ctx->unknown_params, args)) {
     865           0 :                                                 GF_LOG(GF_LOG_WARNING, GF_LOG_AUTHOR, ("[FileList] Unrecognized directive %s, ignoring\n", args));
     866           0 :                                                 gf_dynstrcat(&ctx->unknown_params, args, ",");
     867             :                                         }
     868             :                                 }
     869             : 
     870           2 :                                 if (aval) aval[0] = '=';
     871             : 
     872           2 :                                 if (!sep) break;
     873           0 :                                 sep[0] = c;
     874           0 :                                 args = sep+1;
     875             :                         }
     876           2 :                         continue;
     877             :                 }
     878             :                 //increase line num only for non-comment lines
     879         230 :                 lineno++;
     880             : 
     881         230 :                 url_crc = gf_crc_32(szURL, (u32) strlen(szURL) );
     882         230 :                 if (!ctx->last_url_crc) {
     883           7 :                         ctx->last_url_crc = url_crc;
     884           7 :                         ctx->last_url_lineno = lineno;
     885           7 :                         break;
     886             :                 }
     887         223 :                 if ( ((ctx->last_url_crc == url_crc) && (ctx->last_url_lineno == lineno))
     888         182 :                         || (is_splice_update && (ctx->last_splice_crc==url_crc))
     889             :                 ) {
     890             :                         last_found = GF_TRUE;
     891             :                         nb_repeat=0;
     892             :                         start=stop=0;
     893             :                         do_cat=0;
     894          41 :                         start_range=end_range=0;
     895          41 :                         if (is_splice_update)
     896             :                                 break;
     897          41 :                         splice_start.den = splice_end.den = 0;
     898             :                         keep_splice = 0;
     899             :                         mark_only = 0;
     900          41 :                         start_flags=0;
     901          41 :                         end_flags=0;
     902             :                         no_sync=0;
     903          41 :                         continue;
     904             :                 }
     905         182 :                 if (last_found) {
     906          34 :                         ctx->last_url_crc = url_crc;
     907          34 :                         ctx->last_url_lineno = lineno;
     908          34 :                         break;
     909             :                 }
     910             :                 nb_repeat=0;
     911             :                 start=stop=0;
     912             :                 do_cat=0;
     913             :                 do_del=0;
     914         148 :                 start_range=end_range=0;
     915         148 :                 splice_start.den = splice_end.den = 0;
     916             :                 keep_splice = 0;
     917             :                 mark_only = 0;
     918         148 :                 start_flags=0;
     919         148 :                 end_flags=0;
     920             :                 no_sync=0;
     921             :         }
     922          41 :         gf_fclose(f);
     923             : 
     924          41 :         if (is_splice_update && !last_found)
     925             :                 return GF_FALSE;
     926             : 
     927             :         //not in active splicing and we had a splice start set, trigger a new splice
     928          41 :         if ((ctx->splice_state != FL_SPLICE_ACTIVE) && splice_start.den) {
     929             :                 //activate if:
     930           0 :                 if (
     931             :                         //new new splice start is undefined (prepare for new splice)
     932           0 :                         (splice_start.num<0)
     933             :                         // or new splice start is defined and prev splice start was undefined
     934           0 :                         || ((splice_start.num>=0) && ((ctx->splice_start.num < 0) || !ctx->splice_start.den) )
     935             :                         // or new splice time is greater than previous one
     936           0 :                         || (ctx->splice_start.num * splice_start.den < splice_start.num * ctx->splice_start.den)
     937             :                 ) {
     938             : 
     939           0 :                         ctx->init_splice_start = ctx->splice_start = splice_start;
     940           0 :                         if (!splice_end.den) {
     941           0 :                                 ctx->splice_end.num = -1;
     942           0 :                                 ctx->splice_end.den = 1;
     943             :                         } else {
     944           0 :                                 ctx->splice_end = splice_end;
     945             :                         }
     946           0 :                         ctx->init_splice_end = ctx->splice_end;
     947             : 
     948           0 :                         ctx->splice_state = FL_SPLICE_BEFORE;
     949             :                         //items to be spliced are the ones after the spliced content
     950           0 :                         ctx->last_splice_crc = ctx->last_url_crc = url_crc;
     951           0 :                         ctx->last_url_lineno = lineno;
     952           0 :                         ctx->last_splice_lineno = lineno;
     953           0 :                         ctx->last_file_modif_time = gf_file_modification_time(ctx->file_path);
     954           0 :                         ctx->keep_splice = keep_splice;
     955           0 :                         ctx->mark_only = mark_only;
     956           0 :                         ctx->init_flags_splice_start = ctx->flags_splice_start = start_flags;
     957           0 :                         ctx->init_flags_splice_end = ctx->flags_splice_end = end_flags;
     958             : 
     959           0 :                         if (!ctx->first_loaded) {
     960           0 :                                 if (ctx->splice_start.num == -2)
     961           0 :                                         ctx->splice_start.num = -1;
     962           0 :                                 if (ctx->splice_end.num == -2)
     963           0 :                                         ctx->splice_end.num = -1;
     964             :                         }
     965             :                 }
     966             :         }
     967             :         //in active splicing and we hava a splice end set, update splice value if previous one was undefined or not set
     968          41 :         else if ((ctx->splice_state == FL_SPLICE_ACTIVE)  && splice_end.den && (!ctx->splice_end.den || (ctx->splice_end.num<0))) {
     969           0 :                 ctx->init_splice_end = ctx->splice_end = splice_end;
     970           0 :                 ctx->init_flags_splice_end = ctx->flags_splice_end = end_flags;
     971             :         }
     972             : 
     973          41 :         if (is_splice_update)
     974             :                 return GF_FALSE;
     975             : 
     976          41 :         if ((ctx->splice_state == FL_SPLICE_ACTIVE) && (splice_start.den || splice_end.den)) {
     977           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[FileList] URL %s is a main splice content but expecting a regular content in active splice\n", szURL));
     978             :                 return GF_FALSE;
     979             :         }
     980             : 
     981             : 
     982          41 :         len = (u32) strlen(szURL);
     983          82 :         while (len && strchr("\r\n", szURL[len-1])) {
     984           0 :                 szURL[len-1] = 0;
     985             :                 len--;
     986             :         }
     987          41 :         ctx->nb_repeat = nb_repeat;
     988          41 :         ctx->start = start;
     989          41 :         ctx->stop = stop;
     990          41 :         ctx->do_cat = do_cat;
     991          41 :         ctx->skip_sync = no_sync;
     992             : 
     993          41 :         if (!ctx->floop)
     994          41 :                 ctx->do_del = (do_del || ctx->fdel) ? GF_TRUE : GF_FALSE;
     995          41 :         ctx->start_range = start_range;
     996          41 :         ctx->end_range = end_range;
     997          41 :         filelist_check_implicit_cat(ctx, szURL);
     998          41 :         return GF_TRUE;
     999             : }
    1000             : 
    1001           0 : static void filelist_on_filter_setup_error(GF_Filter *failed_filter, void *udta, GF_Err err)
    1002             : {
    1003             :         u32 i, count;
    1004             :         GF_Filter *filter = (GF_Filter *)udta;
    1005           0 :         GF_FileListCtx *ctx = gf_filter_get_udta(filter);
    1006             : 
    1007           0 :         GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[FileList] Failed to load URL %s: %s\n", gf_filter_get_src_args(failed_filter), gf_error_to_string(err) ));
    1008             : 
    1009           0 :         count = gf_list_count(ctx->io_pids);
    1010           0 :         for (i=0; i<count; i++) {
    1011           0 :                 FileListPid *iopid = gf_list_get(ctx->io_pids, i);
    1012           0 :                 if (!iopid->ipid) continue;
    1013             : 
    1014           0 :                 if (gf_filter_pid_is_filter_in_parents(iopid->ipid, failed_filter))
    1015           0 :                         iopid->ipid = NULL;
    1016             :         }
    1017           0 :         ctx->src_error = GF_TRUE;
    1018           0 :         gf_list_del_item(ctx->filter_srcs, failed_filter);
    1019           0 :         gf_filter_post_process_task(filter);
    1020           0 : }
    1021             : 
    1022             : 
    1023          57 : static GF_Err filelist_load_next(GF_Filter *filter, GF_FileListCtx *ctx)
    1024             : {
    1025             :         GF_Filter *fsrc;
    1026             :         FileListPid *iopid;
    1027             :         u32 i, count;
    1028             :         GF_Filter *prev_filter;
    1029             :         GF_List *filters = NULL;
    1030             :         char *link_args = NULL;
    1031             :         s32 link_idx = -1;
    1032             :         Bool is_filter_chain = GF_FALSE;
    1033          57 :         Bool do_del = ctx->do_del;
    1034             :         GF_Err e;
    1035             :         u32 s_idx;
    1036             :         char *url;
    1037             :         char szURL[GF_MAX_PATH];
    1038             :         Bool next_url_ok;
    1039             : 
    1040          57 :         next_url_ok = filelist_next_url(filter, ctx, szURL, GF_FALSE);
    1041             : 
    1042          57 :         if (!next_url_ok && ctx->ka) {
    1043           0 :                 gf_filter_ask_rt_reschedule(filter, ctx->ka*1000);
    1044           0 :                 return GF_OK;
    1045             :         }
    1046          57 :         count = gf_list_count(ctx->io_pids);
    1047             : 
    1048          57 :         if (ctx->wait_splice_start) {
    1049           0 :                 if (!next_url_ok) {
    1050           0 :                         ctx->wait_splice_start = GF_FALSE;
    1051           0 :                         ctx->wait_source = GF_FALSE;
    1052           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[FileList] No URL for splice, ignoring splice\n"));
    1053           0 :                         ctx->splice_state = FL_SPLICE_AFTER;
    1054           0 :                         ctx->splice_end_cts = ctx->splice_start_cts;
    1055           0 :                         ctx->cts_offset = ctx->cts_offset_at_splice;
    1056           0 :                         ctx->dts_offset = ctx->dts_offset_at_splice;
    1057           0 :                         ctx->dts_sub_plus_one = ctx->dts_sub_plus_one_at_splice;
    1058           0 :                         return GF_OK;
    1059             :                 }
    1060             :                 do_del = GF_FALSE;
    1061             :                 assert (!ctx->splice_srcs);
    1062           0 :                 ctx->splice_srcs = ctx->filter_srcs;
    1063           0 :                 ctx->filter_srcs = gf_list_new();
    1064             :         }
    1065             : 
    1066          57 :         if (do_del) {
    1067             :                 GF_FilterEvent evt;
    1068           0 :                 GF_FEVT_INIT(evt, GF_FEVT_FILE_DELETE, NULL);
    1069           0 :                 evt.file_del.url = "__gpac_self__";
    1070           0 :                 for (i=0; i<gf_list_count(ctx->filter_srcs); i++) {
    1071           0 :                         fsrc = gf_list_get(ctx->filter_srcs, i);
    1072           0 :                         gf_filter_send_event(fsrc, &evt, GF_FALSE);
    1073             :                 }
    1074             :         }
    1075          57 :         if (!ctx->do_cat
    1076             :                 //only reset if not last entry, so that we keep the last graph setup at the end
    1077          27 :                 && next_url_ok
    1078             :         ) {
    1079          30 :                 while (gf_list_count(ctx->filter_srcs)) {
    1080          10 :                         fsrc = gf_list_pop_back(ctx->filter_srcs);
    1081          10 :                         gf_filter_remove_src(filter, fsrc);
    1082             :                 }
    1083             :         }
    1084             : 
    1085             : 
    1086          57 :         ctx->load_next = GF_FALSE;
    1087             : 
    1088          57 :         if (! next_url_ok) {
    1089          10 :                 if (ctx->splice_state==FL_SPLICE_ACTIVE) {
    1090           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[FileList] No next URL for splice but splice period still active, resuming splice with possible broken coding dependencies!\n"));
    1091           0 :                         ctx->wait_splice_end = GF_TRUE;
    1092           0 :                         ctx->splice_state = FL_SPLICE_AFTER;
    1093           0 :                         ctx->dts_sub_plus_one.num = 1;
    1094           0 :                         ctx->dts_sub_plus_one.den = 1;
    1095           0 :                         for (i=0; i<count; i++) {
    1096           0 :                                 FileListPid *iopid = gf_list_get(ctx->io_pids, i);
    1097           0 :                                 iopid->splice_ready = GF_TRUE;
    1098             :                         }
    1099             :                         return GF_OK;
    1100             :                 }
    1101             : 
    1102          10 :                 for (i=0; i<count; i++) {
    1103          10 :                         iopid = gf_list_get(ctx->io_pids, i);
    1104          10 :                         gf_filter_pid_set_eos(iopid->opid);
    1105          10 :                         if (iopid->opid_aux)
    1106           0 :                                 gf_filter_pid_set_eos(iopid->opid_aux);
    1107          10 :                         iopid->ipid = NULL;
    1108             :                 }
    1109          10 :                 ctx->is_eos = GF_TRUE;
    1110          10 :                 return GF_EOS;
    1111             :         }
    1112          37 :         for (i=0; i<gf_list_count(ctx->io_pids); i++) {
    1113          37 :                 FileListPid *an_iopid = gf_list_get(ctx->io_pids, i);
    1114          37 :                 if (ctx->do_cat) {
    1115          27 :                         gf_filter_pid_clear_eos(an_iopid->ipid, GF_TRUE);
    1116          27 :                         filelist_start_ipid(ctx, an_iopid, an_iopid->timescale);
    1117             :                 } else {
    1118          10 :                         if (ctx->wait_splice_start && !an_iopid->splice_ipid) {
    1119           0 :                                 an_iopid->splice_ipid = an_iopid->ipid;
    1120           0 :                                 an_iopid->dts_o_splice = an_iopid->dts_o;
    1121           0 :                                 an_iopid->cts_o_splice = an_iopid->cts_o;
    1122           0 :                                 an_iopid->dts_sub_splice = an_iopid->dts_sub;
    1123           0 :                                 an_iopid->splice_ra_info = an_iopid->ra_info;
    1124             :                         }
    1125          10 :                         an_iopid->ipid = NULL;
    1126          10 :                         an_iopid->ra_info.is_raw = GF_FALSE;
    1127             :                 }
    1128             :         }
    1129             : 
    1130             :         fsrc = NULL;
    1131             :         prev_filter = NULL;
    1132             :         s_idx = 0;
    1133             :         url = szURL;
    1134          47 :         while (url) {
    1135             :                 char c = 0;
    1136             :                 char *sep, *lsep;
    1137          47 :                 char *sep_f = strstr(url, " @");
    1138             : 
    1139          47 :                 sep = strstr(url, " && ");
    1140          47 :                 if (!sep && ctx->srcs.nb_items)
    1141           6 :                         sep = strstr(url, "&&");
    1142             : 
    1143          47 :                 if (sep_f && ctx->do_cat) {
    1144           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[FileList] Cannot use filter directives in cat mode\n"));
    1145             :                         return GF_BAD_PARAM;
    1146             :                 }
    1147             : 
    1148          47 :                 if (sep && sep_f) {
    1149           0 :                         if (sep_f < sep)
    1150             :                                 sep = NULL;
    1151             :                         else
    1152             :                                 sep_f = NULL;
    1153             :                 }
    1154             : 
    1155          47 :                 if (sep) {
    1156           0 :                         c = sep[0];
    1157           0 :                         sep[0] = 0;
    1158             :                 }
    1159          47 :                 else if (sep_f) {
    1160           0 :                         sep_f[0] = 0;
    1161             :                 }
    1162             :                 //skip empty
    1163          47 :                 while (url[0] == ' ')
    1164           0 :                         url++;
    1165             : 
    1166             : 
    1167             : #define SET_SOURCE(_filter, _from) \
    1168             :                         if (link_idx>0) { \
    1169             :                                 prev_filter = gf_list_get(filters, (u32) link_idx); \
    1170             :                                 if (!prev_filter) { \
    1171             :                                         if (filters) gf_list_del(filters); \
    1172             :                                         GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[FileList] Invalid link directive, filter index %d does not point to a valid filter\n")); \
    1173             :                                         return GF_SERVICE_ERROR; \
    1174             :                                 } \
    1175             :                         }\
    1176             :                         lsep = link_args ? strchr(link_args, ' ') : NULL; \
    1177             :                         if (lsep) lsep[0] = 0; \
    1178             :                         gf_filter_set_source(_filter, _from, link_args); \
    1179             :                         if (lsep) lsep[0] = ' '; \
    1180             :                         link_args = NULL;\
    1181             :                         link_idx=-1;
    1182             : 
    1183             : 
    1184          47 :                 if (!url[0]) {
    1185             :                         //last link directive before new source specifier
    1186           0 :                         if (is_filter_chain && prev_filter && (link_idx>=0)) {
    1187           0 :                                 SET_SOURCE(filter, prev_filter);
    1188             :                                 prev_filter = NULL;
    1189           0 :                                 gf_list_reset(filters);
    1190             :                         }
    1191          47 :                 } else if (ctx->do_cat) {
    1192             :                         char *f_url;
    1193             :                         GF_FilterEvent evt;
    1194          27 :                         fsrc = gf_list_get(ctx->filter_srcs, s_idx);
    1195          27 :                         if (!fsrc) {
    1196           0 :                                 if (sep) sep[0] = c;
    1197           0 :                                 else if (sep_f) sep_f[0] = ' ';
    1198             : 
    1199           0 :                                 GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[FileList] More URL to cat than opened service!\n"));
    1200           0 :                                 return GF_BAD_PARAM;
    1201             :                         }
    1202          27 :                         f_url = gf_url_concatenate(ctx->file_path, url);
    1203             :                         memset(&evt, 0, sizeof(GF_FilterEvent));
    1204          27 :                         evt.base.type = GF_FEVT_SOURCE_SWITCH;
    1205          27 :                         evt.seek.source_switch = f_url ? f_url : url;
    1206          27 :                         evt.seek.start_offset = ctx->start_range;
    1207          27 :                         evt.seek.end_offset = ctx->end_range;
    1208          27 :                         gf_filter_send_event(fsrc, &evt, GF_FALSE);
    1209          27 :                         if (f_url)
    1210          27 :                                 gf_free(f_url);
    1211             :                 } else {
    1212             :                         GF_Filter *f = NULL;
    1213          20 :                         if (is_filter_chain) {
    1214           0 :                                 f = gf_filter_load_filter(filter, url, &e);
    1215             :                         } else {
    1216          20 :                                 fsrc = gf_filter_connect_source(filter, url, ctx->file_path, GF_TRUE, &e);
    1217             : 
    1218          20 :                                 if (fsrc)
    1219          20 :                                         gf_filter_set_setup_failure_callback(filter, fsrc, filelist_on_filter_setup_error, filter);
    1220             :                         }
    1221             : 
    1222          20 :                         if (e) {
    1223           0 :                                 if (filters) gf_list_del(filters);
    1224           0 :                                 GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[FileList] Failed to open file %s: %s\n", szURL, gf_error_to_string(e) ));
    1225             : 
    1226           0 :                                 if (sep) sep[0] = c;
    1227           0 :                                 else if (sep_f) sep_f[0] = ' ';
    1228           0 :                                 if (!ctx->splice_srcs)
    1229           0 :                                         ctx->splice_state = FL_SPLICE_NONE;
    1230             :                                 return GF_SERVICE_ERROR;
    1231             :                         }
    1232          20 :                         if (is_filter_chain) {
    1233             : 
    1234           0 :                                 if (!filters) filters = gf_list_new();
    1235           0 :                                 if (!gf_list_count(filters)) gf_list_add(filters, fsrc);
    1236             : 
    1237           0 :                                 SET_SOURCE(f, prev_filter);
    1238             : 
    1239             :                                 //insert at beginning, so that link_idx=0 <=> last declared filter
    1240           0 :                                 gf_list_insert(filters, f, 0);
    1241             :                                 prev_filter = f;
    1242             :                         } else {
    1243          20 :                                 gf_list_add(ctx->filter_srcs, fsrc);
    1244             :                         }
    1245             :                 }
    1246          47 :                 if (!sep && !sep_f) break;
    1247           0 :                 if (sep) {
    1248           0 :                         sep[0] = c;
    1249           0 :                         url = (sep[0]==' ') ? sep+4 : sep+2;
    1250             :                         is_filter_chain = GF_FALSE;
    1251           0 :                         if (prev_filter) {
    1252           0 :                                 gf_filter_set_source(filter, prev_filter, NULL);
    1253             :                                 prev_filter = NULL;
    1254             :                         }
    1255           0 :                         if (filters) gf_list_reset(filters);
    1256             :                         link_idx = -1;
    1257             :                 } else {
    1258           0 :                         sep_f[0] = ' ';
    1259             : 
    1260           0 :                         if (sep_f[2] != ' ') link_args = sep_f+2;
    1261             :                         else link_args = NULL;
    1262             : 
    1263             :                         link_idx = 0;
    1264           0 :                         if (link_args) {
    1265           0 :                                 if (link_args[0] != '#') {
    1266           0 :                                         sep = strchr(link_args, '#');
    1267           0 :                                         if (sep) {
    1268           0 :                                                 sep[0] = 0;
    1269             :                                                 link_idx = atoi(link_args);
    1270           0 :                                                 sep[0] = '#';
    1271           0 :                                                 link_args = sep+1;
    1272             :                                         } else {
    1273             :                                                 link_idx = atoi(link_args);
    1274             :                                                 link_args = NULL;
    1275             :                                         }
    1276             :                                         // " @-1" directive restart chain from source
    1277           0 :                                         if (link_idx<0) {
    1278           0 :                                                 if (prev_filter) {
    1279           0 :                                                         gf_filter_set_source(filter, prev_filter, NULL);
    1280             :                                                         prev_filter = NULL;
    1281             :                                                 }
    1282           0 :                                                 if (filters) gf_list_reset(filters);
    1283             :                                         }
    1284             : 
    1285             :                                 } else {
    1286           0 :                                         link_args++;
    1287             :                                 }
    1288             :                         }
    1289             : 
    1290           0 :                         url = strchr(sep_f+2, ' ');
    1291           0 :                         if (url && (url[1]!='&'))
    1292           0 :                                 url += 1;
    1293             : 
    1294             :                         is_filter_chain = GF_TRUE;
    1295           0 :                         if (!prev_filter) {
    1296           0 :                                 if (!fsrc) {
    1297           0 :                                         if (filters) gf_list_del(filters);
    1298           0 :                                         GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[FileList] Missing source declaration before filter directive\n"));
    1299             :                                         return GF_BAD_PARAM;
    1300             :                                 }
    1301             :                                 prev_filter = fsrc;
    1302             :                         }
    1303             : 
    1304             :                         //last empty link
    1305           0 :                         if (!url && prev_filter) {
    1306           0 :                                 SET_SOURCE(filter, prev_filter);
    1307             :                                 prev_filter = NULL;
    1308             :                         }
    1309             :                 }
    1310             :         }
    1311          47 :         if (prev_filter) {
    1312           0 :                 gf_filter_set_source(filter, prev_filter, NULL);
    1313             :                 prev_filter = NULL;
    1314             :         }
    1315          47 :         if (filters) gf_list_del(filters);
    1316             :         //wait for PIDs to connect
    1317          47 :         GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("[FileList] Switching to file %s\n", szURL));
    1318             : 
    1319          47 :         ctx->wait_splice_start = GF_FALSE;
    1320          47 :         return GF_OK;
    1321             : }
    1322             : 
    1323             : static s64 filelist_translate_splice_cts(FileListPid *iopid, u64 cts)
    1324             : {
    1325           0 :         if (iopid->timescale_splice != iopid->o_timescale) {
    1326           0 :                 cts *= iopid->o_timescale;
    1327           0 :                 cts /= iopid->timescale_splice;
    1328             :         }
    1329           0 :         return iopid->cts_o_splice + cts - iopid->dts_sub_splice;
    1330             : }
    1331             : 
    1332           0 : static Bool filelist_check_splice(GF_FileListCtx *ctx)
    1333             : {
    1334             :         u64 cts;
    1335             :         u32 i, count;
    1336             :         GF_FilterPacket *pck;
    1337             :         GF_FilterSAPType sap;
    1338             :         GF_FilterPid *ipid;
    1339             :         Bool is_raw_audio;
    1340             :         assert(ctx->splice_ctrl);
    1341             :         assert(ctx->splice_state);
    1342             : 
    1343           0 :         ipid = ctx->splice_ctrl->splice_ipid ? ctx->splice_ctrl->splice_ipid : ctx->splice_ctrl->ipid;
    1344           0 :         is_raw_audio = ctx->splice_ctrl->splice_ipid ? ctx->splice_ctrl->splice_ra_info.is_raw : ctx->splice_ctrl->ra_info.is_raw;
    1345             : 
    1346           0 :         pck = gf_filter_pid_get_packet(ipid);
    1347           0 :         if (!pck) {
    1348           0 :                 if (!gf_filter_pid_is_eos(ipid))
    1349             :                         return GF_FALSE;
    1350             : 
    1351           0 :                 if (ctx->splice_state==FL_SPLICE_BEFORE)
    1352           0 :                         ctx->splice_state = FL_SPLICE_AFTER;
    1353             : 
    1354             :                 //if we are in end of stream, abort
    1355           0 :                 if (ctx->splice_state==FL_SPLICE_ACTIVE) {
    1356           0 :                         ctx->splice_state = FL_SPLICE_AFTER;
    1357           0 :                         ctx->splice_end_cts = ctx->spliced_current_cts;
    1358             : 
    1359           0 :                         count = gf_list_count(ctx->io_pids);
    1360           0 :                         for (i=0; i<count; i++) {
    1361           0 :                                 FileListPid *iopid = gf_list_get(ctx->io_pids, i);
    1362           0 :                                 iopid->splice_ready = GF_FALSE;
    1363             :                         }
    1364           0 :                         ctx->wait_splice_end = GF_TRUE;
    1365             :                 }
    1366             :                 return GF_TRUE;
    1367             :         }
    1368           0 :         cts = gf_filter_pck_get_cts(pck);
    1369           0 :         if (cts==GF_FILTER_NO_TS)
    1370             :                 return GF_TRUE;
    1371             : 
    1372           0 :         if (ctx->splice_ctrl->splice_delay>=0) {
    1373           0 :                 cts += ctx->splice_ctrl->splice_delay;
    1374           0 :         } else if (cts > - ctx->splice_ctrl->splice_delay) {
    1375           0 :                 cts += ctx->splice_ctrl->splice_delay;
    1376             :         } else {
    1377             :                 cts = 0;
    1378             :         }
    1379             : 
    1380           0 :         sap = is_raw_audio ? GF_FILTER_SAP_1 : gf_filter_pck_get_sap(pck);
    1381           0 :         if (ctx->splice_state==FL_SPLICE_BEFORE) {
    1382           0 :                 ctx->spliced_current_cts = filelist_translate_splice_cts(ctx->splice_ctrl, cts);
    1383           0 :                 if (sap && (sap <= GF_FILTER_SAP_3)) {
    1384             :                         u64 check_ts = cts;
    1385           0 :                         if (is_raw_audio) {
    1386           0 :                                 check_ts += gf_filter_pck_get_duration(pck);
    1387           0 :                                 check_ts -= 1;
    1388             :                         }
    1389             : 
    1390           0 :                         if (ctx->splice_start.num==-1) {
    1391             :                                 char szURL[GF_MAX_PATH];
    1392           0 :                                 filelist_next_url(NULL, ctx, szURL, GF_TRUE);
    1393           0 :                         } else if (ctx->splice_start.num==-2) {
    1394           0 :                                 ctx->splice_start.num = cts;
    1395           0 :                                 ctx->splice_start.den = ctx->splice_ctrl->timescale_splice;
    1396           0 :                         } else if (ctx->splice_start.num==-3) {
    1397           0 :                                 u64 now = gf_net_get_utc();
    1398           0 :                                 if (now >= ctx->splice_start.den) {
    1399           0 :                                         ctx->splice_start.num = cts;
    1400           0 :                                         ctx->splice_start.den = ctx->splice_ctrl->timescale_splice;
    1401             :                                 }
    1402             :                         }
    1403             : 
    1404             :                         //we're entering the splice period
    1405           0 :                         if ((ctx->splice_start.num >= 0)
    1406           0 :                                 && ( (s64) check_ts * (s64) ctx->splice_start.den >= ctx->splice_start.num * (s64) ctx->splice_ctrl->timescale_splice)
    1407             :                         ) {
    1408             :                                 //cts larger than splice end, move directly to splice_in state
    1409           0 :                                 if ((ctx->splice_end.num >= 0)
    1410           0 :                                         && !ctx->flags_splice_end
    1411           0 :                                         && ((s64) cts * (s64) ctx->splice_end.den >= ctx->splice_end.num * ctx->splice_ctrl->timescale_splice)
    1412             :                                 ) {
    1413           0 :                                         ctx->splice_state = FL_SPLICE_AFTER;
    1414           0 :                                         ctx->splice_end_cts = filelist_translate_splice_cts(ctx->splice_ctrl, cts);
    1415           0 :                                         return GF_TRUE;
    1416             :                                 }
    1417           0 :                                 if (is_raw_audio) {
    1418           0 :                                         u64 ts_diff = ctx->splice_start.num * ctx->splice_ctrl->timescale_splice;
    1419           0 :                                         ts_diff /= ctx->splice_start.den;
    1420           0 :                                         if (ts_diff >= cts) {
    1421             :                                                 ts_diff -= cts;
    1422             :                                                 cts += ts_diff;
    1423             :                                         }
    1424             :                                 }
    1425           0 :                                 ctx->splice_state = FL_SPLICE_ACTIVE;
    1426           0 :                                 ctx->splice_start_cts = filelist_translate_splice_cts(ctx->splice_ctrl, cts);
    1427             :                                 //we just activate splice, iopid->dts_sub_splice is not set yet !!
    1428             :                                 assert(!ctx->splice_ctrl->dts_sub_splice);
    1429           0 :                                 ctx->splice_start_cts -= ctx->splice_ctrl->dts_sub;
    1430             : 
    1431           0 :                                 if (ctx->flags_splice_end & FL_SPLICE_DELTA) {
    1432           0 :                                         ctx->splice_start.num = cts;
    1433           0 :                                         ctx->splice_start.den = ctx->splice_ctrl->timescale_splice;
    1434             :                                 }
    1435             : 
    1436           0 :                                 count = gf_list_count(ctx->io_pids);
    1437           0 :                                 for (i=0; i<count; i++) {
    1438           0 :                                         FileListPid *iopid = gf_list_get(ctx->io_pids, i);
    1439           0 :                                         iopid->splice_ready = GF_FALSE;
    1440             :                                 }
    1441             :                                 //signal on controler that we are ready for splicing
    1442           0 :                                 ctx->splice_ctrl->splice_ready = GF_TRUE;
    1443             :                                 //but wait for all other streams to be ready too
    1444           0 :                                 ctx->wait_splice_start = GF_TRUE;
    1445             :                         }
    1446             :                 }
    1447             :         }
    1448             : 
    1449           0 :         if (ctx->splice_state==FL_SPLICE_ACTIVE) {
    1450             :                 u64 check_ts = cts;
    1451           0 :                 if (is_raw_audio) {
    1452           0 :                         check_ts += gf_filter_pck_get_duration(pck);
    1453           0 :                         check_ts -= 1;
    1454             :                 }
    1455             : 
    1456           0 :                 if (ctx->flags_splice_end & FL_SPLICE_DELTA) {
    1457           0 :                         GF_Fraction64 s_end = ctx->splice_start;
    1458           0 :                         u64 diff = ctx->splice_end.num;
    1459           0 :                         diff *= ctx->splice_start.den;
    1460           0 :                         diff /= ctx->splice_end.den;
    1461           0 :                         s_end.num += diff;
    1462           0 :                         ctx->splice_end = s_end;
    1463           0 :                         ctx->flags_splice_end = 0;
    1464             :                 }
    1465           0 :                 else if (ctx->splice_end.num==-1) {
    1466             :                         char szURL[GF_MAX_PATH];
    1467           0 :                         filelist_next_url(NULL, ctx, szURL, GF_TRUE);
    1468           0 :                 } else if (sap && (sap <= GF_FILTER_SAP_3)) {
    1469           0 :                         if (ctx->splice_end.num==-2) {
    1470           0 :                                 ctx->splice_end.num = cts;
    1471           0 :                                 ctx->splice_end.den = ctx->splice_ctrl->timescale_splice;
    1472           0 :                         } else if (ctx->splice_end.num==-3) {
    1473           0 :                                 u64 now = gf_net_get_utc();
    1474           0 :                                 if (now >= ctx->splice_start.den) {
    1475           0 :                                         ctx->splice_end.num = cts;
    1476           0 :                                         ctx->splice_end.den = ctx->splice_ctrl->timescale_splice;
    1477             :                                 }
    1478             :                         }
    1479             :                 }
    1480             : 
    1481           0 :                 if (sap && (sap <= GF_FILTER_SAP_3)
    1482           0 :                         && (ctx->splice_end.num >= 0)
    1483           0 :                         && ((s64) check_ts * (s64) ctx->splice_end.den >= ctx->splice_end.num * ctx->splice_ctrl->timescale_splice)
    1484             :                 ) {
    1485           0 :                         ctx->splice_state = FL_SPLICE_AFTER;
    1486             : 
    1487           0 :                         if (is_raw_audio) {
    1488           0 :                                 u64 ts_diff = ctx->splice_end.num * ctx->splice_ctrl->timescale_splice;
    1489           0 :                                 ts_diff /= ctx->splice_end.den;
    1490           0 :                                 if (ts_diff >= cts) {
    1491             :                                         ts_diff -= cts;
    1492             :                                         cts += ts_diff;
    1493             :                                 }
    1494             :                         }
    1495             : 
    1496             : 
    1497           0 :                         ctx->splice_end_cts = filelist_translate_splice_cts(ctx->splice_ctrl, cts);
    1498             : 
    1499           0 :                         count = gf_list_count(ctx->io_pids);
    1500           0 :                         for (i=0; i<count; i++) {
    1501           0 :                                 FileListPid *iopid = gf_list_get(ctx->io_pids, i);
    1502           0 :                                 iopid->splice_ready = GF_FALSE;
    1503             :                         }
    1504             :                         //do not signal splice ready on controler yet, we need to wait for the first frame in the splice content to have cts >= splice end
    1505           0 :                         ctx->wait_splice_end = GF_TRUE;
    1506           0 :                         return GF_TRUE;
    1507             :                 }
    1508             : 
    1509           0 :                 ctx->spliced_current_cts = filelist_translate_splice_cts(ctx->splice_ctrl, cts);
    1510             :         }
    1511             : 
    1512             :         return GF_TRUE;
    1513             : }
    1514             : 
    1515           0 : void filelist_copy_raw_audio(FileListPid *iopid, const u8 *src, u32 src_size, u32 offset, u8 *dst, u32 nb_samp, RawAudioInfo *ra)
    1516             : {
    1517           0 :         if (iopid->ra_info.planar) {
    1518             :                 u32 i, bps, stride;
    1519           0 :                 stride = src_size / ra->nb_ch;
    1520           0 :                 bps = ra->abps / ra->nb_ch;
    1521           0 :                 for (i=0; i<ra->nb_ch; i++) {
    1522           0 :                         memcpy(dst + i*bps*nb_samp, src + i*stride + offset * bps, nb_samp * bps);
    1523             :                 }
    1524             :         } else {
    1525           0 :                 memcpy(dst, src + offset * ra->abps, nb_samp * ra->abps);
    1526             :         }
    1527           0 : }
    1528             : 
    1529           0 : static void filelist_forward_splice_pck(FileListPid *iopid, GF_FilterPacket *pck)
    1530             : {
    1531           0 :         if (iopid->audio_samples_to_keep) {
    1532             :                 u32 nb_samp, dur;
    1533             :                 u64 cts;
    1534             :                 u8 *output;
    1535             :                 const u8 *data;
    1536             :                 GF_FilterPacket *dst_pck;
    1537             :                 u32 pck_size, osize, offset=0;
    1538             : 
    1539           0 :                 cts = gf_filter_pck_get_cts(pck);
    1540           0 :                 dur = gf_filter_pck_get_duration(pck);
    1541             : 
    1542           0 :                 data = gf_filter_pck_get_data(pck, &pck_size);
    1543           0 :                 if (iopid->audio_samples_to_keep>0) {
    1544           0 :                         nb_samp = iopid->audio_samples_to_keep;
    1545           0 :                         iopid->audio_samples_to_keep = -iopid->audio_samples_to_keep;
    1546             :                 } else {
    1547           0 :                         nb_samp = pck_size / iopid->splice_ra_info.abps + iopid->audio_samples_to_keep;
    1548           0 :                         offset = -iopid->audio_samples_to_keep;
    1549           0 :                         iopid->audio_samples_to_keep = 0;
    1550             :                 }
    1551           0 :                 osize = nb_samp * iopid->splice_ra_info.abps;
    1552           0 :                 dst_pck = gf_filter_pck_new_alloc(iopid->opid, osize, &output);
    1553           0 :                 if (!dst_pck) return;
    1554             : 
    1555           0 :                 filelist_copy_raw_audio(iopid, data, pck_size, offset, output, nb_samp, &iopid->splice_ra_info);
    1556             : 
    1557             :                 dur = nb_samp;
    1558           0 :                 if (iopid->timescale_splice != iopid->splice_ra_info.sample_rate) {
    1559           0 :                         dur *= iopid->timescale_splice;
    1560           0 :                         dur /= iopid->splice_ra_info.sample_rate;
    1561           0 :                         offset *= iopid->timescale_splice;
    1562           0 :                         offset /= iopid->splice_ra_info.sample_rate;
    1563             :                 }
    1564           0 :                 cts += offset;
    1565           0 :                 gf_filter_pck_set_cts(dst_pck, cts);
    1566           0 :                 gf_filter_pck_set_dts(dst_pck, cts);
    1567           0 :                 gf_filter_pck_set_duration(dst_pck, dur);
    1568             : 
    1569           0 :                 gf_filter_pck_send(dst_pck);
    1570             :         } else {
    1571           0 :                 gf_filter_pck_forward(pck, iopid->opid);
    1572             :         }
    1573             : }
    1574             : 
    1575           0 : static void filelist_purge_slice(GF_FileListCtx *ctx)
    1576             : {
    1577           0 :         u32 i, count = gf_list_count(ctx->io_pids);
    1578             :         assert(ctx->splice_ctrl);
    1579             : 
    1580           0 :         if (ctx->mark_only)
    1581             :                 return;
    1582             :         assert(ctx->splice_ctrl->splice_ipid);
    1583             : 
    1584           0 :         for (i=0; i<count; i++) {
    1585           0 :                 FileListPid *iopid = gf_list_get(ctx->io_pids, i);
    1586           0 :                 if (iopid == ctx->splice_ctrl) continue;
    1587           0 :                 if (!iopid->splice_ipid) continue;
    1588             : 
    1589           0 :                 while (1) {
    1590             :                         u64 cts;
    1591           0 :                         GF_FilterPacket *pck = gf_filter_pid_get_packet(iopid->splice_ipid);
    1592           0 :                         if (!pck) break;
    1593             : 
    1594           0 :                         cts = gf_filter_pck_get_cts(pck);
    1595           0 :                         if (cts==GF_FILTER_NO_TS) cts=0;
    1596             : 
    1597             :                         cts = filelist_translate_splice_cts(iopid, cts);
    1598           0 :                         if (cts * ctx->splice_ctrl->o_timescale > ctx->spliced_current_cts * iopid->o_timescale)
    1599             :                                 break;
    1600             : 
    1601           0 :                         if (ctx->keep_splice) {
    1602           0 :                                 GF_FilterPacket *pck = gf_filter_pid_get_packet(iopid->splice_ipid);
    1603           0 :                                 filelist_forward_splice_pck(iopid, pck);
    1604             :                         }
    1605           0 :                         gf_filter_pid_drop_packet(iopid->splice_ipid);
    1606             :                 }
    1607             :         }
    1608           0 :         if (ctx->keep_splice) {
    1609           0 :                 GF_FilterPacket *pck = gf_filter_pid_get_packet(ctx->splice_ctrl->splice_ipid);
    1610           0 :                 filelist_forward_splice_pck(ctx->splice_ctrl, pck);
    1611             :         }
    1612             : 
    1613           0 :         gf_filter_pid_drop_packet(ctx->splice_ctrl->splice_ipid);
    1614             : }
    1615             : 
    1616        2310 : void filein_send_packet(GF_FileListCtx *ctx, FileListPid *iopid, GF_FilterPacket *pck, Bool is_splice_forced)
    1617             : {
    1618             :         GF_FilterPacket *dst_pck;
    1619             :         u32 dur;
    1620             :         u64 dts, cts;
    1621        2310 :         if (iopid->audio_samples_to_keep) {
    1622             :                 u32 nb_samp;
    1623             :                 u8 *output;
    1624             :                 const u8 *data;
    1625             :                 u32 pck_size, osize, offset=0;
    1626           0 :                 RawAudioInfo *ra = is_splice_forced ? &iopid->splice_ra_info : &iopid->ra_info;
    1627             : 
    1628           0 :                 data = gf_filter_pck_get_data(pck, &pck_size);
    1629           0 :                 if (iopid->audio_samples_to_keep>0) {
    1630           0 :                         nb_samp = iopid->audio_samples_to_keep;
    1631             :                 } else {
    1632           0 :                         nb_samp = pck_size / ra->abps + iopid->audio_samples_to_keep;
    1633           0 :                         offset = -iopid->audio_samples_to_keep;
    1634             :                 }
    1635             : 
    1636           0 :                 osize = ABS(iopid->audio_samples_to_keep) * ra->abps;
    1637           0 :                 dst_pck = gf_filter_pck_new_alloc((!is_splice_forced && iopid->opid_aux) ? iopid->opid_aux : iopid->opid, osize, &output);
    1638           0 :                 if (!dst_pck) return;
    1639             : 
    1640           0 :                 filelist_copy_raw_audio(iopid, data, pck_size, offset, output, nb_samp, ra);
    1641             :         } else {
    1642        2310 :                 dst_pck = gf_filter_pck_new_ref(iopid->opid_aux ? iopid->opid_aux : iopid->opid, 0, 0, pck);
    1643        2310 :                 if (!dst_pck) return;
    1644             :         }
    1645        2310 :         gf_filter_pck_merge_properties(pck, dst_pck);
    1646             : 
    1647        2310 :         dts = gf_filter_pck_get_dts(pck);
    1648        2310 :         if (dts==GF_FILTER_NO_TS) dts=0;
    1649             : 
    1650        2310 :         cts = gf_filter_pck_get_cts(pck);
    1651        2310 :         if (cts==GF_FILTER_NO_TS) cts=0;
    1652             : 
    1653        2310 :         if (iopid->single_frame && (ctx->fsort==FL_SORT_DATEX) ) {
    1654           0 :                 dur = (u32) ctx->current_file_dur;
    1655             :                 //move from second to input pid timescale
    1656           0 :                 dur *= iopid->timescale;
    1657        2345 :         } else if (iopid->single_frame && ctx->fdur.num && ctx->fdur.den) {
    1658          35 :                 s64 pdur = ctx->fdur.num;
    1659          35 :                 pdur *= iopid->timescale;
    1660          35 :                 pdur /= ctx->fdur.den;
    1661          35 :                 dur = (u32) pdur;
    1662        2275 :         } else if (iopid->audio_samples_to_keep) {
    1663           0 :                 RawAudioInfo *ra = is_splice_forced ? &iopid->splice_ra_info : &iopid->ra_info;
    1664           0 :                 if (iopid->audio_samples_to_keep>0) {
    1665           0 :                         dur = iopid->audio_samples_to_keep;
    1666           0 :                         if ( (iopid->splice_ipid && (iopid->splice_ipid != iopid->ipid)) || (!ctx->keep_splice && !ctx->mark_only))
    1667           0 :                                 iopid->audio_samples_to_keep = 0;
    1668             :                         else
    1669           0 :                                 iopid->audio_samples_to_keep = -iopid->audio_samples_to_keep;
    1670             :                 } else {
    1671             :                         u32 pck_size;
    1672           0 :                         gf_filter_pck_get_data(pck, &pck_size);
    1673           0 :                         dur = pck_size/ra->abps + iopid->audio_samples_to_keep;
    1674           0 :                         cts += -iopid->audio_samples_to_keep;
    1675           0 :                         dts += -iopid->audio_samples_to_keep;
    1676           0 :                         iopid->audio_samples_to_keep = 0;
    1677             :                 }
    1678           0 :                 if (iopid->timescale != ra->sample_rate) {
    1679           0 :                         dur *= iopid->timescale;
    1680           0 :                         dur /= ra->sample_rate;
    1681             :                 }
    1682             :         } else {
    1683        2275 :                 dur = gf_filter_pck_get_duration(pck);
    1684             :         }
    1685             : 
    1686        2310 :         if (iopid->timescale == iopid->o_timescale) {
    1687        1635 :                 gf_filter_pck_set_dts(dst_pck, iopid->dts_o + dts - iopid->dts_sub);
    1688        1635 :                 gf_filter_pck_set_cts(dst_pck, iopid->cts_o + cts - iopid->dts_sub);
    1689        1635 :                 gf_filter_pck_set_duration(dst_pck, dur);
    1690             :         } else {
    1691             :                 u64 ts = dts;
    1692         675 :                 ts *= iopid->o_timescale;
    1693         675 :                 ts /= iopid->timescale;
    1694         675 :                 gf_filter_pck_set_dts(dst_pck, iopid->dts_o + ts - iopid->dts_sub);
    1695             :                 ts = cts;
    1696         675 :                 ts *= iopid->o_timescale;
    1697         675 :                 ts /= iopid->timescale;
    1698         675 :                 gf_filter_pck_set_cts(dst_pck, iopid->cts_o + ts - iopid->dts_sub);
    1699             : 
    1700         675 :                 ts = dur;
    1701         675 :                 ts *= iopid->o_timescale;
    1702         675 :                 ts /= iopid->timescale;
    1703         675 :                 gf_filter_pck_set_duration(dst_pck, (u32) ts);
    1704             :         }
    1705        2310 :         dts += dur;
    1706        2310 :         cts += dur;
    1707             : 
    1708        2310 :         if (iopid->delay>=0) {
    1709        2310 :                 cts += iopid->delay;
    1710           0 :         } else if (cts > - iopid->delay) {
    1711           0 :                 cts += iopid->delay;
    1712             :         } else {
    1713             :                 cts = 0;
    1714             :         }
    1715             : 
    1716        2310 :         if (!is_splice_forced) {
    1717        2310 :                 if (dts > iopid->max_dts)
    1718        2310 :                         iopid->max_dts = dts;
    1719        2310 :                 if (cts > iopid->max_cts)
    1720        1755 :                         iopid->max_cts = cts;
    1721             : 
    1722             :                 //remember our first DTS
    1723        2310 :                 if (!iopid->first_dts_plus_one) {
    1724          68 :                         iopid->first_dts_plus_one = dts + 1;
    1725             :                 }
    1726             : 
    1727        2310 :                 if (iopid->send_cue) {
    1728           0 :                         iopid->send_cue = GF_FALSE;
    1729           0 :                         gf_filter_pck_set_property(dst_pck, GF_PROP_PCK_CUE_START, &PROP_BOOL(GF_TRUE));
    1730             :                 }
    1731             :         }
    1732             : 
    1733        2310 :         gf_filter_pck_send(dst_pck);
    1734             : 
    1735        2310 :         if (!iopid->audio_samples_to_keep) {
    1736        2310 :                 gf_filter_pid_drop_packet(iopid->ipid);
    1737             :         }
    1738             : }
    1739             : 
    1740        2913 : static GF_Err filelist_process(GF_Filter *filter)
    1741             : {
    1742             :         Bool start, end, purge_splice = GF_FALSE;
    1743             :         u32 i, count, nb_done, nb_inactive, nb_stop, nb_ready;
    1744             :         FileListPid *iopid;
    1745        2913 :         GF_FileListCtx *ctx = gf_filter_get_udta(filter);
    1746             : 
    1747             : 
    1748        2913 :         if (!ctx->file_list) {
    1749             :                 GF_FilterPacket *pck;
    1750        2439 :                 if (!ctx->file_pid) {
    1751             :                         return GF_EOS;
    1752             :                 }
    1753        2439 :                 pck = gf_filter_pid_get_packet(ctx->file_pid);
    1754        2439 :                 if (pck) {
    1755           7 :                         gf_filter_pck_get_framing(pck, &start, &end);
    1756           7 :                         gf_filter_pid_drop_packet(ctx->file_pid);
    1757             : 
    1758           7 :                         if (end) {
    1759             :                                 const GF_PropertyValue *p;
    1760             :                                 Bool is_first = GF_TRUE;
    1761             :                                 FILE *f=NULL;
    1762           7 :                                 p = gf_filter_pid_get_property(ctx->file_pid, GF_PROP_PID_FILEPATH);
    1763           7 :                                 if (p) {
    1764             :                                         char *frag;
    1765           7 :                                         if (ctx->file_path) {
    1766           0 :                                                 gf_free(ctx->file_path);
    1767             :                                                 is_first = GF_FALSE;
    1768             :                                         }
    1769           7 :                                         ctx->file_path = gf_strdup(p->value.string);
    1770           7 :                                         frag = strchr(ctx->file_path, '#');
    1771           7 :                                         if (frag) {
    1772           0 :                                                 frag[0] = 0;
    1773           0 :                                                 ctx->frag_url = gf_strdup(frag+1);
    1774             :                                         }
    1775           7 :                                         f = gf_fopen(ctx->file_path, "rt");
    1776             :                                 }
    1777           7 :                                 if (!f) {
    1778           0 :                                         GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[FileList] Unable to open file %s\n", ctx->file_path ? ctx->file_path : "no source path"));
    1779             :                                         return GF_SERVICE_ERROR;
    1780             :                                 } else {
    1781           7 :                                         gf_fclose(f);
    1782           7 :                                         ctx->load_next = is_first;
    1783             :                                 }
    1784             :                         }
    1785             :                 }
    1786             :         }
    1787        2913 :         if (ctx->is_eos)
    1788             :                 return GF_EOS;
    1789             : 
    1790        2909 :         if (ctx->load_next) {
    1791          57 :                 return filelist_load_next(filter, ctx);
    1792             :         }
    1793             : 
    1794             : 
    1795        2852 :         count = gf_list_count(ctx->io_pids);
    1796             :         //init first timestamp
    1797        2852 :         if (!ctx->dts_sub_plus_one.num) {
    1798             :                 u32 nb_eos = 0;
    1799             : 
    1800         124 :                 for (i=0; i<gf_list_count(ctx->filter_srcs); i++) {
    1801         590 :                         GF_Filter *fsrc = gf_list_get(ctx->filter_srcs, i);
    1802         590 :                         if (gf_filter_has_pid_connection_pending(fsrc, filter)) {
    1803             :                                 return GF_OK;
    1804             :                         }
    1805             :                 }
    1806             : 
    1807          71 :                 for (i=0; i<count; i++) {
    1808             :                         GF_FilterPacket *pck;
    1809             :                         u64 dts;
    1810         123 :                         iopid = gf_list_get(ctx->io_pids, i);
    1811         123 :             if (!iopid->ipid) {
    1812           0 :                                 if (ctx->src_error) {
    1813           0 :                                         nb_eos++;
    1814           0 :                                         continue;
    1815             :                                 }
    1816             : 
    1817           0 :                                 if (iopid->opid) gf_filter_pid_set_eos(iopid->opid);
    1818           0 :                                 if (iopid->opid_aux) gf_filter_pid_set_eos(iopid->opid_aux);
    1819             :                                 return GF_OK;
    1820             :                         }
    1821         123 :             if (iopid->skip_dts_init) continue;
    1822         123 :             pck = gf_filter_pid_get_packet(iopid->ipid);
    1823             : 
    1824         123 :                         if (!pck) {
    1825          55 :                                 if (gf_filter_pid_is_eos(iopid->ipid) || (iopid->play_state==FLIST_STATE_STOP)) {
    1826           3 :                                         nb_eos++;
    1827           3 :                                         continue;
    1828             :                                 }
    1829          52 :                                 ctx->dts_sub_plus_one.num = 0;
    1830          52 :                                 return GF_OK;
    1831             :                         }
    1832             : 
    1833             : 
    1834          68 :                         dts = gf_filter_pck_get_dts(pck);
    1835          68 :                         if (dts==GF_FILTER_NO_TS)
    1836           0 :                                 dts = gf_filter_pck_get_cts(pck);
    1837          68 :                         if (dts==GF_FILTER_NO_TS)
    1838             :                                 dts = 0;
    1839             : 
    1840             :                         //make sure we start all streams on a SAP
    1841          68 :                         if (!iopid->wait_rap && gf_filter_pck_get_seek_flag(pck)) {
    1842           0 :                                 gf_filter_pid_drop_packet(iopid->ipid);
    1843             :                                 pck = NULL;
    1844             :                         }
    1845          68 :                         else if (iopid->wait_rap && !iopid->ra_info.is_raw && !gf_filter_pck_get_sap(pck)) {
    1846           0 :                                 gf_filter_pid_drop_packet(iopid->ipid);
    1847             :                                 pck = NULL;
    1848             :                         }
    1849             :                         if (!pck) {
    1850           0 :                                 iopid->wait_rap = GF_TRUE;
    1851           0 :                                 if (!ctx->wait_dts_plus_one.num
    1852           0 :                                         || ((dts + 1) * ctx->wait_dts_plus_one.den > ctx->wait_dts_plus_one.num * (u64) iopid->timescale)
    1853             :                                 ) {
    1854           0 :                                         ctx->wait_dts_plus_one.num = dts + 1;
    1855           0 :                                         ctx->wait_dts_plus_one.den = iopid->timescale;
    1856             :                                 }
    1857           0 :                                 ctx->dts_sub_plus_one.num = 0;
    1858           0 :                                 return GF_OK;
    1859             :                         }
    1860          68 :                         if (ctx->wait_dts_plus_one.num && (dts * ctx->wait_dts_plus_one.den < (ctx->wait_dts_plus_one.num - 1) * (u64) iopid->timescale) ) {
    1861           0 :                                 gf_filter_pid_drop_packet(iopid->ipid);
    1862           0 :                                 iopid->wait_rap = GF_TRUE;
    1863           0 :                                 ctx->dts_sub_plus_one.num = 0;
    1864           0 :                                 return GF_OK;
    1865             :                         }
    1866          68 :                         if (iopid->wait_rap) {
    1867           0 :                                 iopid->wait_rap = GF_FALSE;
    1868           0 :                                 ctx->dts_sub_plus_one.num = 0;
    1869           0 :                                 return GF_OK;
    1870             :                         }
    1871          68 :                         if (!ctx->dts_sub_plus_one.num
    1872           0 :                                 || (dts * ctx->dts_sub_plus_one.den < (ctx->dts_sub_plus_one.num - 1) *(u64)  iopid->timescale)
    1873             :                         ) {
    1874          68 :                                 ctx->dts_sub_plus_one.num = dts + 1;
    1875          68 :                                 ctx->dts_sub_plus_one.den = iopid->timescale;
    1876             :                         }
    1877             :                 }
    1878          72 :                 ctx->src_error = GF_FALSE;
    1879          72 :                 if (nb_eos) {
    1880           3 :                         if (nb_eos==count) {
    1881             :                                 //force load
    1882           3 :                                 ctx->load_next = GF_TRUE;
    1883           3 :                                 return filelist_process(filter);
    1884             :                         }
    1885             :                         return GF_OK;
    1886             :                 }
    1887             : 
    1888             :                 //if we start with a splice, set first dst_sub to 0 to keep timestamps untouched
    1889          69 :                 if (!ctx->splice_state) {
    1890          69 :                         ctx->first_loaded = GF_TRUE;
    1891          69 :                         ctx->skip_sync = GF_FALSE;
    1892           0 :                 } else if (!ctx->first_loaded) {
    1893           0 :                         ctx->dts_sub_plus_one.num = 1;
    1894           0 :                         ctx->dts_sub_plus_one.den = 1;
    1895           0 :                         ctx->first_loaded = 1;
    1896           0 :                         ctx->skip_sync = GF_FALSE;
    1897             :                 }
    1898             : 
    1899          68 :                 for (i=0; i<count; i++) {
    1900          68 :                         iopid = gf_list_get(ctx->io_pids, i);
    1901          68 :                         iopid->dts_sub = ctx->dts_sub_plus_one.num - 1;
    1902          68 :                         iopid->dts_sub *= iopid->o_timescale;
    1903          68 :                         iopid->dts_sub /= ctx->dts_sub_plus_one.den;
    1904             :                 }
    1905          69 :                 ctx->wait_dts_plus_one.num = 0;
    1906             :         }
    1907             : 
    1908        2331 :         if (ctx->splice_state) {
    1909           0 :                 if (!filelist_check_splice(ctx))
    1910             :                         return GF_OK;
    1911             :         }
    1912             : 
    1913             :         nb_done = nb_inactive = nb_stop = nb_ready = 0;
    1914        2330 :         for (i=0; i<count; i++) {
    1915        2330 :                 iopid = gf_list_get(ctx->io_pids, i);
    1916        2330 :                 if (!iopid->ipid) {
    1917           0 :             iopid->splice_ready = GF_TRUE;
    1918           0 :                         nb_inactive++;
    1919           0 :                         continue;
    1920             :                 }
    1921        2330 :                 if (iopid->play_state==FLIST_STATE_WAIT_PLAY)
    1922          12 :                         continue;
    1923        2318 :         if (iopid->play_state==FLIST_STATE_STOP) {
    1924           0 :                         nb_stop++;
    1925             :             //in case the input still dispatch packets, drop them
    1926           0 :             while (1) {
    1927           0 :                 GF_FilterPacket *pck = gf_filter_pid_get_packet(iopid->ipid);
    1928           0 :                 if (!pck) break;
    1929           0 :                 gf_filter_pid_drop_packet(iopid->ipid);
    1930             :             }
    1931           0 :             while (iopid->splice_ipid) {
    1932           0 :                 GF_FilterPacket *pck = gf_filter_pid_get_packet(iopid->splice_ipid);
    1933           0 :                 if (!pck) break;
    1934           0 :                 gf_filter_pid_drop_packet(iopid->splice_ipid);
    1935             :             }
    1936           0 :             nb_done++;
    1937           0 :             iopid->splice_ready = GF_TRUE;
    1938           0 :             continue;
    1939             :         }
    1940        2318 :                 if (iopid->is_eos) {
    1941           0 :             iopid->splice_ready = GF_TRUE;
    1942           0 :                         nb_done++;
    1943           0 :                         continue;
    1944             :                 }
    1945             : 
    1946             :                 while (1) {
    1947             :                         u64 cts;
    1948             :                         GF_FilterPacket *pck;
    1949             : 
    1950        4628 :                         pck = gf_filter_pid_get_packet(iopid->ipid);
    1951        4628 :                         if (!pck) {
    1952         469 :                                 if (gf_filter_pid_is_eos(iopid->ipid)) {
    1953          68 :                                         if (ctx->wait_splice_end) {
    1954           0 :                                                 iopid->splice_ready = GF_TRUE;
    1955          68 :                                         } else if (ctx->wait_splice_start) {
    1956           0 :                                                 iopid->splice_ready = GF_TRUE;
    1957             :                                         } else {
    1958          68 :                                                 iopid->is_eos = GF_TRUE;
    1959          68 :                                                 if (ctx->splice_state==FL_SPLICE_ACTIVE)
    1960             :                                                         purge_splice = GF_TRUE;
    1961             :                                         }
    1962             :                                 }
    1963             : 
    1964         469 :                                 if (iopid->is_eos)
    1965          68 :                                         nb_done++;
    1966             :                                 break;
    1967             :                         }
    1968             : 
    1969        4159 :                         if (gf_filter_pid_would_block(iopid->opid) && (!iopid->opid_aux || gf_filter_pid_would_block(iopid->opid_aux)))
    1970             :                                 break;
    1971             : 
    1972        2310 :                         cts = gf_filter_pck_get_cts(pck);
    1973        2310 :                         if (ctx->splice_state && (cts != GF_FILTER_NO_TS)) {
    1974           0 :                                 u32 dur = gf_filter_pck_get_duration(pck);
    1975             : 
    1976           0 :                                 if (iopid->delay>=0) {
    1977           0 :                                         cts += iopid->delay;
    1978           0 :                                 } else if (cts > - iopid->delay) {
    1979           0 :                                         cts += iopid->delay;
    1980             :                                 } else {
    1981             :                                         cts = 0;
    1982             :                                 }
    1983             : 
    1984             :                                 //translate cts in output timescale, and compare with splice
    1985           0 :                                 if (iopid->timescale != iopid->o_timescale) {
    1986           0 :                                         cts *= iopid->o_timescale;
    1987           0 :                                         cts /= iopid->timescale;
    1988           0 :                                         dur *= iopid->o_timescale;
    1989           0 :                                         dur /= iopid->timescale;
    1990             :                                 }
    1991           0 :                                 if (iopid->cts_o + cts >= iopid->dts_sub)
    1992           0 :                                         cts = iopid->cts_o + cts - iopid->dts_sub;
    1993             :                                 else
    1994             :                                         cts = 0;
    1995             : 
    1996             :                                 //about to enter the splice period
    1997           0 :                                 if (ctx->splice_state==FL_SPLICE_BEFORE) {
    1998             :                                         //do not dispatch yet if cts is greater than last CTS seen on splice control pid
    1999           0 :                                         if (cts * ctx->splice_ctrl->o_timescale > ctx->spliced_current_cts * iopid->o_timescale)
    2000             :                                                 break;
    2001             :                                 }
    2002             :                                 //in the splice period
    2003           0 :                                 else if (ctx->splice_state==FL_SPLICE_ACTIVE) {
    2004             :                                         u64 check_ts = cts;
    2005             : 
    2006           0 :                                         if (iopid->ra_info.is_raw) {
    2007           0 :                                                 check_ts += dur;
    2008           0 :                                                 check_ts -= 1;
    2009             :                                         }
    2010             : 
    2011             :                                         //packet in splice range
    2012           0 :                                         if (check_ts * ctx->splice_ctrl->o_timescale >= ctx->splice_start_cts * iopid->o_timescale) {
    2013             :                                                 Bool keep_pck = GF_FALSE;
    2014             :                                                 //waiting for all streams to reach splice out point (packet is from main content)
    2015             :                                                 //don't drop packet yet in case splice content is not ready
    2016           0 :                                                 if (ctx->wait_splice_start) {
    2017           0 :                                                         if (iopid->ra_info.is_raw && iopid->ra_info.sample_rate && !iopid->audio_samples_to_keep) {
    2018             :                                                                 u64 ts_diff = ctx->splice_start_cts * iopid->o_timescale;
    2019           0 :                                                                 ts_diff /= ctx->splice_ctrl->o_timescale;
    2020           0 :                                                                 if (ts_diff >= cts) {
    2021           0 :                                                                         ts_diff -= cts;
    2022           0 :                                                                         if (iopid->ra_info.sample_rate != iopid->o_timescale) {
    2023           0 :                                                                                 ts_diff *= iopid->ra_info.sample_rate;
    2024           0 :                                                                                 ts_diff /= iopid->o_timescale;
    2025             :                                                                         }
    2026           0 :                                                                         iopid->audio_samples_to_keep = (s32) ts_diff;
    2027           0 :                                                                         if (ts_diff) keep_pck = GF_TRUE;
    2028             :                                                                 }
    2029             :                                                         }
    2030             :                                                         if (!keep_pck) {
    2031           0 :                                                                 iopid->splice_ready = GF_TRUE;
    2032           0 :                                                                 break;
    2033             :                                                         }
    2034             :                                                 }
    2035           0 :                                                 if (!keep_pck && (iopid == ctx->splice_ctrl))
    2036             :                                                         purge_splice = GF_TRUE;
    2037             : 
    2038             :                                                 //do not dispatch yet if cts is greater than last CTS seen on splice control pid
    2039           0 :                                                 if (cts * ctx->splice_ctrl->o_timescale > ctx->spliced_current_cts * iopid->o_timescale)
    2040             :                                                         break;
    2041             : 
    2042             :                                         }
    2043             :                                         //should not happen
    2044           0 :                                         else if (!ctx->wait_splice_start) {
    2045           0 :                                                 gf_filter_pid_drop_packet(iopid->ipid);
    2046           0 :                                                 continue;
    2047             :                                         }
    2048             :                                 }
    2049             :                                 //leaving the splice period
    2050           0 :                                 else if (ctx->splice_state==FL_SPLICE_AFTER) {
    2051             :                                         //still in spliced content
    2052           0 :                                         if (ctx->wait_splice_end) {
    2053             :                                                 u64 check_ts = cts;
    2054           0 :                                                 if (iopid->ra_info.is_raw) {
    2055           0 :                                                         check_ts += dur;
    2056           0 :                                                         check_ts -= 1;
    2057             :                                                 }
    2058             : 
    2059           0 :                                                 if (
    2060             :                                                         //packet is after splice end, drop
    2061           0 :                                                         (check_ts * ctx->splice_ctrl->o_timescale >= ctx->splice_end_cts * iopid->o_timescale)
    2062             :                                                         //packet is before splice end but a previous packet was dropped because after splice end (i.e. we dropped a ref), drop
    2063           0 :                                                         || iopid->splice_ready
    2064             :                                                 ) {
    2065             :                                                         Bool do_break = GF_TRUE;
    2066           0 :                                                         if (iopid->ra_info.is_raw && !iopid->audio_samples_to_keep) {
    2067             :                                                                 u64 ts_diff = ctx->splice_end_cts * iopid->o_timescale;
    2068           0 :                                                                 ts_diff /= ctx->splice_ctrl->o_timescale;
    2069           0 :                                                                 if (ts_diff >= cts) {
    2070           0 :                                                                         ts_diff -= cts;
    2071           0 :                                                                         if (iopid->ra_info.sample_rate != iopid->o_timescale) {
    2072           0 :                                                                                 ts_diff *= iopid->ra_info.sample_rate;
    2073           0 :                                                                                 ts_diff /= iopid->o_timescale;
    2074             :                                                                         }
    2075           0 :                                                                         iopid->audio_samples_to_keep = (s32) ts_diff;
    2076           0 :                                                                         if (ts_diff)
    2077             :                                                                                 do_break = GF_FALSE;
    2078             :                                                                 }
    2079             :                                                         }
    2080             :                                                         if (do_break) {
    2081           0 :                                                                 iopid->splice_ready = GF_TRUE;
    2082           0 :                                                                 if (!ctx->mark_only)
    2083           0 :                                                                         gf_filter_pid_drop_packet(iopid->ipid);
    2084             :                                                                 break;
    2085             :                                                         }
    2086             :                                                 }
    2087             :                                         } else {
    2088             :                                                 //out of spliced content, packet is from main:
    2089             :                                                 //drop packet if before CTS of splice end point (open gop or packets from other PIDs not yet discarded during splice)
    2090             :                                                 u64 check_ts = cts;
    2091           0 :                                                 if (iopid->ra_info.is_raw) {
    2092           0 :                                                         check_ts += dur;
    2093           0 :                                                         check_ts -= 1;
    2094             :                                                 }
    2095           0 :                                                 if (!iopid->audio_samples_to_keep && (check_ts * ctx->splice_ctrl->o_timescale < ctx->splice_end_cts * iopid->o_timescale)) {
    2096             :                                                         //do not drop if not raw audio and we were in keep/mark mode
    2097           0 :                                                         if (iopid->ra_info.is_raw || !ctx->was_kept) {
    2098           0 :                                                                 gf_filter_pid_drop_packet(iopid->ipid);
    2099           0 :                                                                 break;
    2100             :                                                         }
    2101             :                                                 }
    2102           0 :                                                 if (ctx->wait_splice_start) {
    2103           0 :                                                         iopid->splice_ready = GF_FALSE;
    2104             :                                                 }
    2105             :                                         }
    2106             :                                 }
    2107             :                         }
    2108             : 
    2109        2310 :                         if (ctx->wait_source) {
    2110           0 :                                 nb_ready++;
    2111           0 :                                 break;
    2112             :                         }
    2113             : 
    2114        2310 :                         filein_send_packet(ctx, iopid, pck, GF_FALSE);
    2115             : 
    2116             :                         //if we have an end range, compute max_dts (includes dur) - first_dts
    2117        2310 :                         if (ctx->stop > ctx->start) {
    2118           0 :                                 if ( (ctx->stop-ctx->start) * iopid->timescale <= (iopid->max_dts - iopid->first_dts_plus_one + 1)) {
    2119             :                                         GF_FilterEvent evt;
    2120           0 :                                         GF_FEVT_INIT(evt, GF_FEVT_STOP, iopid->ipid)
    2121           0 :                                         gf_filter_pid_send_event(iopid->ipid, &evt);
    2122           0 :                                         gf_filter_pid_set_discard(iopid->ipid, GF_TRUE);
    2123           0 :                                         iopid->is_eos = GF_TRUE;
    2124           0 :                                         nb_done++;
    2125             :                                         break;
    2126             :                                 }
    2127             :                         }
    2128             :                 }
    2129             :         }
    2130             : 
    2131        2331 :         if (purge_splice) {
    2132           0 :                 filelist_purge_slice(ctx);
    2133             :         }
    2134        2331 :         if (ctx->wait_source) {
    2135           0 :                 if (nb_ready + nb_inactive + nb_done == count) {
    2136           0 :                         ctx->wait_source = GF_FALSE;
    2137             : 
    2138           0 :                         if (ctx->splice_state==FL_SPLICE_AFTER)
    2139           0 :                                 ctx->splice_state = FL_SPLICE_BEFORE;
    2140             :                 }
    2141             :         }
    2142             : 
    2143             : 
    2144        2331 :         if (ctx->wait_splice_end) {
    2145             :                 Bool ready = GF_TRUE;
    2146           0 :                 for (i=0; i<count; i++) {
    2147           0 :                         iopid = gf_list_get(ctx->io_pids, i);
    2148           0 :                         if (!iopid->ipid) continue;
    2149           0 :                         if (!iopid->splice_ready) {
    2150             :                                 ready = GF_FALSE;
    2151             :                                 break;
    2152             :                         }
    2153             :                 }
    2154           0 :                 if (!ready)
    2155             :                         return GF_OK;
    2156             : 
    2157           0 :                 GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("[FileList] Splice end reached, resuming main content\n"));
    2158           0 :                 ctx->wait_splice_end = GF_FALSE;
    2159           0 :                 ctx->nb_repeat = ctx->splice_nb_repeat;
    2160           0 :                 ctx->splice_nb_repeat = 0;
    2161             : 
    2162             :                 //reset props pushed by splice
    2163           0 :                 if (ctx->pid_props) gf_free(ctx->pid_props);
    2164           0 :                 ctx->pid_props = ctx->splice_pid_props;
    2165           0 :                 ctx->splice_pid_props = NULL;
    2166           0 :                 ctx->skip_sync = GF_FALSE;
    2167             : 
    2168           0 :                 for (i=0; i<count; i++) {
    2169           0 :                         iopid = gf_list_get(ctx->io_pids, i);
    2170           0 :                         iopid->splice_ready = GF_FALSE;
    2171             :                         //detach
    2172           0 :                         if (!ctx->mark_only && iopid->ipid) {
    2173           0 :                                 gf_filter_pid_set_udta(iopid->ipid, NULL);
    2174           0 :                                 gf_filter_pid_set_discard(iopid->ipid, GF_TRUE);
    2175             :                         }
    2176             : 
    2177           0 :                         if (iopid->splice_ipid) {
    2178             :                                 GF_FilterPacket *pck;
    2179           0 :                                 iopid->ipid = iopid->splice_ipid;
    2180           0 :                                 iopid->dts_o = iopid->dts_o_splice;
    2181           0 :                                 iopid->cts_o = iopid->cts_o_splice;
    2182           0 :                                 iopid->dts_sub = iopid->dts_sub_splice;
    2183           0 :                                 iopid->dts_sub_splice = 0;
    2184           0 :                                 iopid->dts_o_splice = 0;
    2185           0 :                                 iopid->cts_o_splice = 0;
    2186           0 :                                 iopid->splice_ipid = NULL;
    2187           0 :                                 iopid->ra_info = iopid->splice_ra_info;
    2188           0 :                                 iopid->splice_ra_info.is_raw = GF_FALSE;
    2189           0 :                                 iopid->timescale = iopid->timescale_splice;
    2190             : 
    2191             :                                 //if spliced media was raw audio, we may need to forward or truncate part of the packet before switching properties
    2192           0 :                                 pck = iopid->ra_info.is_raw ? gf_filter_pid_get_packet(iopid->ipid) : NULL;
    2193           0 :                                 if (pck) {
    2194           0 :                                         u64 cts = gf_filter_pck_get_cts(pck);
    2195           0 :                                         u64 check_ts = cts + gf_filter_pck_get_duration(pck) - 1;
    2196             : 
    2197           0 :                                         if (cts * ctx->splice_ctrl->timescale < ctx->splice_end_cts * iopid->timescale) {
    2198           0 :                                                 if (check_ts * ctx->splice_ctrl->timescale > ctx->splice_end_cts * iopid->timescale) {
    2199           0 :                                                         u64 diff_ts = ctx->splice_end_cts * iopid->timescale / ctx->splice_ctrl->timescale;
    2200           0 :                                                         diff_ts -= cts;
    2201             : 
    2202           0 :                                                         if (iopid->timescale != iopid->ra_info.sample_rate) {
    2203           0 :                                                                 diff_ts *= iopid->ra_info.sample_rate;
    2204           0 :                                                                 diff_ts /= iopid->timescale;
    2205             :                                                         }
    2206           0 :                                                         iopid->audio_samples_to_keep = (s32) diff_ts;
    2207           0 :                                                         if (ctx->keep_splice) {
    2208           0 :                                                                 filein_send_packet(ctx, iopid, pck, GF_TRUE);
    2209             :                                                         } else {
    2210           0 :                                                                 iopid->audio_samples_to_keep = -iopid->audio_samples_to_keep;
    2211             :                                                         }
    2212             :                                                 }
    2213             :                                         }
    2214             : 
    2215             :                                 }
    2216           0 :                         } else if (!ctx->mark_only) {
    2217           0 :                                 iopid->ipid = NULL;
    2218             :                         }
    2219           0 :                         iopid->is_eos = GF_FALSE;
    2220             :                 }
    2221           0 :                 if (!ctx->mark_only) {
    2222           0 :                         while (gf_list_count(ctx->filter_srcs)) {
    2223           0 :                                 GF_Filter *fsrc = gf_list_pop_back(ctx->filter_srcs);
    2224           0 :                                 gf_filter_remove_src(filter, fsrc);
    2225             :                         }
    2226           0 :                         gf_list_del(ctx->filter_srcs);
    2227           0 :                         ctx->filter_srcs = ctx->splice_srcs;
    2228           0 :                         ctx->splice_srcs = NULL;
    2229             :                 } else {
    2230           0 :                         for (i=0; i<count; i++) {
    2231           0 :                                 iopid = gf_list_get(ctx->io_pids, i);
    2232           0 :                                 if (!iopid->ipid) continue;
    2233           0 :                                 gf_filter_pid_set_property_str(iopid->opid, "period_resume", NULL);
    2234           0 :                                 gf_filter_pid_set_property_str(iopid->opid, "period_resume", &PROP_STRING(ctx->dyn_period_id ? ctx->dyn_period_id : "") );
    2235           0 :                                 gf_filter_pid_set_property_str(iopid->opid, "period_switch", NULL);
    2236           0 :                                 gf_filter_pid_set_property_str(iopid->opid, "period_switch", &PROP_BOOL(GF_TRUE) );
    2237             :                         }
    2238             :                 }
    2239           0 :                 ctx->was_kept = (ctx->mark_only || ctx->keep_splice) ? GF_TRUE : GF_FALSE;
    2240             : 
    2241           0 :                 ctx->mark_only = GF_FALSE;
    2242             : 
    2243             : 
    2244           0 :                 ctx->splice_state = FL_SPLICE_AFTER;
    2245           0 :                 if (ctx->keep_splice) {
    2246           0 :                         for (i=0; i<count; i++) {
    2247           0 :                                 iopid = gf_list_get(ctx->io_pids, i);
    2248           0 :                                 if (iopid->opid_aux) {
    2249           0 :                                         gf_filter_pid_set_eos(iopid->opid_aux);
    2250           0 :                                         gf_filter_pid_remove(iopid->opid_aux);
    2251           0 :                                         iopid->opid_aux = NULL;
    2252             :                                 }
    2253             :                         }
    2254           0 :                         ctx->keep_splice = GF_FALSE;
    2255             : 
    2256           0 :                         if (ctx->splice_props) {
    2257           0 :                                 gf_free(ctx->splice_props);
    2258           0 :                                 ctx->splice_props = NULL;
    2259             :                         }
    2260             :                 }
    2261             : 
    2262             :                 //spliced media is still active, restore our timeline as it was before splicing
    2263           0 :                 if (! gf_filter_pid_is_eos(ctx->splice_ctrl->ipid)) {
    2264             : 
    2265           0 :                         ctx->cur_splice_index ++;
    2266             :                         //force a reconfig
    2267           0 :                         for (i=0; i<count; i++) {
    2268           0 :                                 iopid = gf_list_get(ctx->io_pids, i);
    2269           0 :                                 if (iopid->ipid) {
    2270           0 :                                         filelist_configure_pid(filter, iopid->ipid, GF_FALSE);
    2271           0 :                                         gf_filter_pid_set_property_str(iopid->opid, "period_resume", &PROP_STRING(ctx->dyn_period_id ? ctx->dyn_period_id : "") );
    2272             :                                 }
    2273             :                         }
    2274             : 
    2275           0 :                         ctx->cts_offset = ctx->cts_offset_at_splice;
    2276           0 :                         ctx->dts_offset = ctx->dts_offset_at_splice;
    2277           0 :                         ctx->dts_sub_plus_one = ctx->dts_sub_plus_one_at_splice;
    2278             :                         //set splice start as undefined to probe for new splice time
    2279           0 :                         ctx->splice_start.num = -1;
    2280           0 :                         ctx->splice_start.den = 1;
    2281           0 :                         ctx->splice_end.num = -1;
    2282           0 :                         ctx->splice_end.den = 1;
    2283             :                         //make sure we have a packet ready on each input pid before dispatching packets from the main content
    2284             :                         //so that we trigger synchronized reconfig on all pids
    2285           0 :                         ctx->wait_source = GF_TRUE;
    2286           0 :                         return GF_OK;
    2287             :                 }
    2288             : 
    2289           0 :                 for (i=0; i<count; i++) {
    2290           0 :                         iopid = gf_list_get(ctx->io_pids, i);
    2291           0 :                         if (!iopid->ipid) continue;
    2292           0 :                         gf_filter_pid_set_udta(iopid->ipid, NULL);
    2293           0 :                         gf_filter_pid_set_discard(iopid->ipid, GF_TRUE);
    2294             :                 }
    2295             :                 //spliced media is done, load next
    2296           0 :                 GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("[FileList] Spliced media is over, switching to next item in playlist\n"));
    2297             :                 nb_inactive = 0;
    2298             :                 nb_done = count;
    2299           0 :                 ctx->cur_splice_index = 0;
    2300           0 :                 ctx->splice_state = ctx->nb_repeat ? FL_SPLICE_BEFORE : FL_SPLICE_NONE;
    2301             :         }
    2302             : 
    2303        2331 :         if (ctx->wait_splice_start) {
    2304             :                 Bool ready = GF_TRUE;
    2305           0 :                 for (i=0; i<count; i++) {
    2306           0 :                         iopid = gf_list_get(ctx->io_pids, i);
    2307           0 :                         if (!iopid->ipid) continue;
    2308           0 :                         if (!iopid->splice_ready) {
    2309             :                                 ready = GF_FALSE;
    2310             :                                 break;
    2311             :                         }
    2312             :                 }
    2313           0 :                 if (!ready)
    2314             :                         return GF_OK;
    2315             : 
    2316           0 :                 GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("[FileList] Splice start reached, loading splice content\n"));
    2317           0 :                 ctx->splice_nb_repeat = ctx->nb_repeat;
    2318           0 :                 ctx->nb_repeat = 0;
    2319           0 :                 ctx->init_start = ctx->start;
    2320           0 :                 ctx->init_stop = ctx->stop;
    2321           0 :                 ctx->was_kept = GF_FALSE;
    2322           0 :                 ctx->stop = ctx->start = 0;
    2323             :                 nb_inactive = 0;
    2324             :                 nb_done = count;
    2325             : 
    2326             :                 assert(!ctx->splice_pid_props);
    2327           0 :                 ctx->splice_pid_props = ctx->pid_props;
    2328           0 :                 ctx->pid_props = NULL;
    2329             : 
    2330           0 :                 ctx->cts_offset_at_splice = ctx->cts_offset;
    2331           0 :                 ctx->dts_offset_at_splice = ctx->dts_offset;
    2332           0 :                 ctx->dts_sub_plus_one_at_splice = ctx->dts_sub_plus_one;
    2333             : 
    2334           0 :                 if (ctx->mark_only) {
    2335           0 :                         ctx->cur_splice_index ++;
    2336           0 :                         for (i=0; i<count; i++) {
    2337           0 :                                 iopid = gf_list_get(ctx->io_pids, i);
    2338           0 :                                 if (iopid->ipid) {
    2339           0 :                                         gf_filter_pid_set_property_str(iopid->opid, "period_resume", NULL);
    2340           0 :                                         gf_filter_pid_set_property_str(iopid->opid, "period_resume", &PROP_STRING(ctx->dyn_period_id ? ctx->dyn_period_id : "") );
    2341           0 :                                         gf_filter_pid_set_property_str(iopid->opid, "period_switch", NULL);
    2342           0 :                                         gf_filter_pid_set_property_str(iopid->opid, "period_switch", &PROP_BOOL(GF_TRUE) );
    2343             :                                 }
    2344           0 :                                 if (ctx->splice_props)
    2345           0 :                                         gf_filter_pid_push_properties(iopid->opid, ctx->splice_props, GF_TRUE, GF_TRUE);
    2346             : 
    2347           0 :                                 if (ctx->dyn_period_id)
    2348           0 :                                         filelist_push_period_id(ctx, iopid->opid);
    2349             :                         }
    2350           0 :                         if (ctx->splice_props) {
    2351           0 :                                 gf_free(ctx->splice_props);
    2352           0 :                                 ctx->splice_props = NULL;
    2353             :                         }
    2354           0 :                         ctx->wait_splice_start = GF_FALSE;
    2355           0 :                         ctx->splice_ctrl->splice_ipid = ctx->splice_ctrl->ipid;
    2356           0 :                         ctx->splice_ctrl->splice_ra_info = ctx->splice_ctrl->ra_info;
    2357           0 :                         return GF_OK;
    2358             :                 }
    2359             :                 //make sure we have a packet ready on each input pid before dispatching packets from the splice
    2360             :                 //so that we trigger synchronized reconfig on all pids
    2361           0 :                 ctx->wait_source = GF_TRUE;
    2362             :         }
    2363             : 
    2364             : 
    2365             : 
    2366        2331 :         if ((nb_inactive!=count) && (nb_done+nb_inactive==count)) {
    2367             :                 //compute max cts and dts
    2368             :                 GF_Fraction64 max_cts, max_dts;
    2369             :                 max_cts.num = max_dts.num = 0;
    2370             :                 max_cts.den = max_dts.den = 1;
    2371             : 
    2372          68 :                 if (gf_filter_end_of_session(filter) || (nb_stop + nb_inactive == count) ) {
    2373           0 :                         for (i=0; i<count; i++) {
    2374           0 :                                 iopid = gf_list_get(ctx->io_pids, i);
    2375           0 :                                 gf_filter_pid_set_eos(iopid->opid);
    2376             :                         }
    2377           0 :                         ctx->is_eos = GF_TRUE;
    2378           0 :                         return GF_EOS;
    2379             :                 }
    2380          68 :                 ctx->dts_sub_plus_one.num = 0;
    2381         136 :                 for (i=0; i<count; i++) {
    2382             :                         u64 ts;
    2383          68 :                         iopid = gf_list_get(ctx->io_pids, i);
    2384          68 :                         iopid->send_cue = ctx->sigcues;
    2385          68 :                         if (!iopid->ipid) continue;
    2386             : 
    2387          68 :                         ts = iopid->max_cts - iopid->dts_sub;
    2388          68 :                         if (max_cts.num * (u64) iopid->timescale < ts * max_cts.den) {
    2389             :                                 max_cts.num = ts;
    2390             :                                 max_cts.den = iopid->timescale;
    2391             :                         }
    2392             : 
    2393          68 :                         ts = iopid->max_dts - iopid->dts_sub;
    2394          68 :                         if (max_dts.num * (u64) iopid->timescale < ts * max_dts.den) {
    2395          68 :                                 max_dts.num = ts;
    2396             :                                 max_dts.den = iopid->timescale;
    2397             :                         }
    2398             :                 }
    2399          68 :                 if (!ctx->cts_offset.num) {
    2400          10 :                         ctx->cts_offset = max_dts;
    2401          58 :                 } else if (ctx->cts_offset.den == max_dts.den) {
    2402          51 :                         ctx->cts_offset.num += max_dts.num;
    2403           7 :                 } else if (max_dts.den>ctx->cts_offset.den) {
    2404           3 :                         ctx->cts_offset.num *= max_dts.den;
    2405           3 :                         ctx->cts_offset.num /= ctx->cts_offset.den;
    2406           3 :                         ctx->cts_offset.num += max_dts.num;
    2407           3 :                         ctx->cts_offset.den = max_dts.den;
    2408             :                 } else {
    2409           4 :                         ctx->cts_offset.num += max_dts.num * ctx->cts_offset.den / max_dts.den;
    2410             :                 }
    2411             : 
    2412          68 :                 if (!ctx->dts_offset.num) {
    2413          10 :                         ctx->dts_offset = max_dts;
    2414          58 :                 } else if (ctx->dts_offset.den == max_dts.den) {
    2415          51 :                         ctx->dts_offset.num += max_dts.num;
    2416           7 :                 } else if (max_dts.den>ctx->dts_offset.den) {
    2417           3 :                         ctx->dts_offset.num *= max_dts.den;
    2418           3 :                         ctx->dts_offset.num /= ctx->dts_offset.den;
    2419           3 :                         ctx->dts_offset.num += max_dts.num;
    2420           3 :                         ctx->dts_offset.den = max_dts.den;
    2421             :                 } else {
    2422           4 :                         ctx->dts_offset.num += max_dts.num * ctx->dts_offset.den / max_dts.den;
    2423             :                 }
    2424             : 
    2425          68 :                 if (ctx->nb_repeat) {
    2426             :                         Bool is_splice_resume = GF_FALSE;
    2427          24 :                         if ((s32) ctx->nb_repeat>0)
    2428          24 :                                 ctx->nb_repeat--;
    2429             : 
    2430             :                         //reload of main content
    2431          24 :                         if (ctx->splice_state==FL_SPLICE_AFTER) {
    2432           0 :                                 ctx->splice_start = ctx->init_splice_start;
    2433           0 :                                 ctx->splice_end = ctx->init_splice_end;
    2434           0 :                                 ctx->flags_splice_start = ctx->init_flags_splice_start;
    2435           0 :                                 ctx->flags_splice_end = ctx->init_flags_splice_end;
    2436           0 :                                 ctx->splice_state = FL_SPLICE_BEFORE;
    2437           0 :                                 ctx->splice_end_cts = 0;
    2438           0 :                                 ctx->splice_start_cts = 0;
    2439           0 :                                 ctx->spliced_current_cts = 0;
    2440           0 :                                 ctx->dts_sub_plus_one_at_splice.num = 0;
    2441           0 :                                 ctx->last_url_crc = ctx->last_splice_crc;
    2442           0 :                                 ctx->last_url_lineno = ctx->last_splice_lineno;
    2443           0 :                                 ctx->start = ctx->init_start;
    2444           0 :                                 ctx->stop = ctx->init_stop;
    2445             :                                 is_splice_resume = GF_TRUE;
    2446             :                         }
    2447             : 
    2448          48 :                         for (i=0; i<count; i++) {
    2449             :                                 GF_FilterEvent evt;
    2450          24 :                                 iopid = gf_list_get(ctx->io_pids, i);
    2451          24 :                                 if (!iopid->ipid) continue;
    2452             : 
    2453          24 :                                 gf_filter_pid_set_discard(iopid->ipid, GF_FALSE);
    2454             : 
    2455          24 :                                 GF_FEVT_INIT(evt, GF_FEVT_STOP, iopid->ipid);
    2456          24 :                                 gf_filter_pid_send_event(iopid->ipid, &evt);
    2457             : 
    2458          24 :                                 iopid->is_eos = GF_FALSE;
    2459          24 :                                 filelist_start_ipid(ctx, iopid, iopid->timescale);
    2460          24 :                                 if (is_splice_resume) {
    2461           0 :                                         iopid->dts_o_splice = iopid->cts_o;
    2462           0 :                                         iopid->cts_o_splice = iopid->dts_o;
    2463           0 :                                         iopid->splice_ra_info = iopid->ra_info;
    2464           0 :                                         iopid->dts_sub_splice = 0;
    2465             :                                 }
    2466             :                         }
    2467             :                 } else {
    2468          44 :                         if (ctx->splice_state!=FL_SPLICE_ACTIVE) {
    2469          44 :                                 ctx->splice_state = FL_SPLICE_NONE;
    2470          44 :                                 ctx->last_splice_crc = 0;
    2471             :                         }
    2472             :                         //force load
    2473          44 :                         ctx->load_next = GF_TRUE;
    2474          44 :                         return filelist_process(filter);
    2475             :                 }
    2476             :         }
    2477             :         return GF_OK;
    2478             : }
    2479             : 
    2480           6 : static void filelist_add_entry(GF_FileListCtx *ctx, FileListEntry *fentry)
    2481             : {
    2482             :         u32 i, count;
    2483           6 :         GF_LOG(GF_LOG_DEBUG, GF_LOG_AUTHOR, ("[FileList] Adding file %s to list\n", fentry->file_name));
    2484           6 :         if (ctx->fsort==FL_SORT_NONE) {
    2485           4 :                 gf_list_add(ctx->file_list, fentry);
    2486             :                 return;
    2487             :         }
    2488           2 :         count = gf_list_count(ctx->file_list);
    2489           1 :         for (i=0; i<count; i++) {
    2490             :                 Bool insert=GF_FALSE;
    2491           1 :                 FileListEntry *cur = gf_list_get(ctx->file_list, i);
    2492           1 :                 switch (ctx->fsort) {
    2493           0 :                 case FL_SORT_SIZE:
    2494           0 :                         if (cur->file_size>fentry->file_size) insert = GF_TRUE;
    2495             :                         break;
    2496           0 :                 case FL_SORT_DATE:
    2497             :                 case FL_SORT_DATEX:
    2498           0 :                         if (cur->last_mod_time>fentry->last_mod_time) insert = GF_TRUE;
    2499             :                         break;
    2500           1 :                 case FL_SORT_NAME:
    2501           1 :                         if (strcmp(cur->file_name, fentry->file_name) > 0) insert = GF_TRUE;
    2502             :                         break;
    2503             :                 }
    2504             :                 if (insert) {
    2505           0 :                         gf_list_insert(ctx->file_list, fentry, i);
    2506             :                         return;
    2507             :                 }
    2508             :         }
    2509           2 :         gf_list_add(ctx->file_list, fentry);
    2510             : }
    2511             : 
    2512           2 : static Bool filelist_enum(void *cbck, char *item_name, char *item_path, GF_FileEnumInfo *file_info)
    2513             : {
    2514             :         FileListEntry *fentry;
    2515             :         GF_FileListCtx *ctx = cbck;
    2516           2 :         if (file_info->hidden) return GF_FALSE;
    2517           2 :         if (file_info->directory) return GF_FALSE;
    2518           2 :         if (file_info->drive) return GF_FALSE;
    2519           2 :         if (file_info->system) return GF_FALSE;
    2520             : 
    2521           2 :         GF_SAFEALLOC(fentry, FileListEntry);
    2522           2 :         if (!fentry) return GF_TRUE;
    2523             : 
    2524           2 :         fentry->file_name = gf_strdup(item_path);
    2525           2 :         fentry->file_size = file_info->size;
    2526           2 :         fentry->last_mod_time = file_info->last_modified;
    2527           2 :         filelist_add_entry(ctx, fentry);
    2528             : 
    2529           2 :         return GF_FALSE;
    2530             : }
    2531             : 
    2532          10 : static GF_Err filelist_initialize(GF_Filter *filter)
    2533             : {
    2534             :         u32 i, count;
    2535             :         char *sep_dir, c=0, *dir, *pattern;
    2536          10 :         GF_FileListCtx *ctx = gf_filter_get_udta(filter);
    2537          10 :         ctx->io_pids = gf_list_new();
    2538             : 
    2539          10 :         ctx->filter_srcs = gf_list_new();
    2540          10 :         if (ctx->ka)
    2541           0 :                 ctx->floop = 0;
    2542             : 
    2543             : 
    2544          10 :         if (! ctx->srcs.nb_items ) {
    2545           7 :                 if (! gf_filter_is_dynamic(filter)) {
    2546           0 :                         GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("[FileList] No inputs\n"));
    2547             :                 }
    2548             :                 return GF_OK;
    2549             :         }
    2550             : 
    2551           3 :         ctx->file_list = gf_list_new();
    2552           3 :         count = ctx->srcs.nb_items;
    2553           8 :         for (i=0; i<count; i++) {
    2554           5 :                 char *list = ctx->srcs.vals[i];
    2555             : 
    2556           5 :                 if (strchr(list, '*') ) {
    2557           1 :                         sep_dir = strrchr(list, '/');
    2558           1 :                         if (!sep_dir) sep_dir = strrchr(list, '\\');
    2559           1 :                         if (sep_dir) {
    2560           1 :                                 c = sep_dir[0];
    2561           1 :                                 sep_dir[0] = 0;
    2562             :                                 dir = list;
    2563           1 :                                 pattern = sep_dir+2;
    2564             :                         } else {
    2565             :                                 dir = ".";
    2566             :                                 pattern = list;
    2567             :                         }
    2568           1 :                         gf_enum_directory(dir, GF_FALSE, filelist_enum, ctx, pattern);
    2569           1 :                         if (c && sep_dir) sep_dir[0] = c;
    2570             :                 } else {
    2571             :                         u32 type = 0;
    2572           4 :                         if (strstr(list, " && ") || strstr(list, "&&"))
    2573             :                                 type = 1;
    2574           4 :                         else if (gf_file_exists(list))
    2575             :                                 type = 2;
    2576             : 
    2577             :                         if (type) {
    2578             :                                 FileListEntry *fentry;
    2579           4 :                                 GF_SAFEALLOC(fentry, FileListEntry);
    2580           4 :                                 if (fentry) {
    2581           4 :                                         fentry->file_name = gf_strdup(list);
    2582           4 :                                         if (type==2) {
    2583             :                                                 FILE *fo;
    2584           4 :                                                 fentry->last_mod_time = gf_file_modification_time(list);
    2585           4 :                                                 fo = gf_fopen(list, "rb");
    2586           4 :                                                 if (fo) {
    2587           4 :                                                         fentry->file_size = gf_fsize(fo);
    2588           4 :                                                         gf_fclose(fo);
    2589             :                                                 }
    2590             :                                         }
    2591           4 :                                         filelist_add_entry(ctx, fentry);
    2592             :                                 }
    2593             :                         } else {
    2594           0 :                                 GF_LOG(GF_LOG_WARNING, GF_LOG_AUTHOR, ("[FileList] File %s not found, ignoring\n", list));
    2595             :                         }
    2596             :                 }
    2597             :         }
    2598             : 
    2599           3 :         if (!gf_list_count(ctx->file_list)) {
    2600           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[FileList] No files found in list %s\n", ctx->srcs));
    2601             :                 return GF_BAD_PARAM;
    2602             :         }
    2603           3 :         if (ctx->fsort==FL_SORT_DATEX) {
    2604           0 :                 ctx->revert = GF_FALSE;
    2605           0 :                 ctx->floop = 0;
    2606             :         }
    2607           3 :         ctx->file_list_idx = ctx->revert ? gf_list_count(ctx->file_list) : -1;
    2608           3 :         ctx->load_next = GF_TRUE;
    2609             :         //from now on we only accept the above caps
    2610           3 :         filelist_override_caps(filter, ctx);
    2611             :         //and we act as a source, request processing
    2612           3 :         gf_filter_post_process_task(filter);
    2613             :         //prevent deconnection of filter when no input
    2614           3 :         gf_filter_make_sticky(filter);
    2615           3 :         return GF_OK;
    2616             : }
    2617             : 
    2618          10 : static void filelist_finalize(GF_Filter *filter)
    2619             : {
    2620          10 :         GF_FileListCtx *ctx = gf_filter_get_udta(filter);
    2621          30 :         while (gf_list_count(ctx->io_pids)) {
    2622          10 :                 FileListPid *iopid = gf_list_pop_back(ctx->io_pids);
    2623          10 :                 gf_free(iopid);
    2624             :         }
    2625          10 :         if (ctx->file_list) {
    2626           9 :                 while (gf_list_count(ctx->file_list)) {
    2627           6 :                         FileListEntry *fentry = gf_list_pop_back(ctx->file_list);
    2628           6 :                         gf_free(fentry->file_name);
    2629           6 :                         gf_free(fentry);
    2630             :                 }
    2631           3 :                 gf_list_del(ctx->file_list);
    2632             :         }
    2633          10 :         gf_list_del(ctx->io_pids);
    2634          10 :         gf_list_del(ctx->filter_srcs);
    2635          10 :         if (ctx->file_path) gf_free(ctx->file_path);
    2636          10 :         if (ctx->frag_url) gf_free(ctx->frag_url);
    2637          10 :         if (ctx->unknown_params) gf_free(ctx->unknown_params);
    2638          10 :         if (ctx->pid_props) gf_free(ctx->pid_props);
    2639          10 :         if (ctx->dyn_period_id) gf_free(ctx->dyn_period_id);
    2640          10 :         if (ctx->splice_props) gf_free(ctx->splice_props);
    2641          10 :         if (ctx->splice_pid_props) gf_free(ctx->splice_pid_props);
    2642          10 : }
    2643             : 
    2644        3065 : static const char *filelist_probe_data(const u8 *data, u32 size, GF_FilterProbeScore *score)
    2645             : {
    2646             :         u32 nb_lines = 0;
    2647        3065 :         if (!gf_utf8_is_legal(data, size)) {
    2648             :                 return NULL;
    2649             :         }
    2650        3218 :         while (data && size) {
    2651             :                 u32 i, line_size;
    2652             :                 Bool is_cr = GF_FALSE;
    2653             :                 char *nl;
    2654        3088 :                 nl = memchr(data, '\r', size);
    2655        3088 :                 if (!nl)
    2656        3063 :                         nl = memchr(data, '\n', size);
    2657             :                 else
    2658             :                         is_cr = GF_TRUE;
    2659             : 
    2660        3088 :                 if (nl)
    2661        2987 :                         line_size = (u32) (nl - (char *) data);
    2662             :                 else
    2663         101 :                         line_size = size-1;
    2664             : 
    2665             :                 //line is comment
    2666        3088 :                 if (data[0] != '#') {
    2667             :                         Bool line_empty = GF_TRUE;
    2668       21699 :                         for (i=0;i<line_size; i++) {
    2669       22478 :                                 char c = (char) data[i];
    2670       22478 :                                 if (!c) return NULL;
    2671       22416 :                                 if ( isalnum(c)) continue;
    2672             :                                 //valid URL chars plus backslash for win path
    2673        2586 :                                 if (strchr("-._~:/?#[]@!$&'()*+,;%=\\", c)) {
    2674             :                                         line_empty = GF_FALSE;
    2675        1869 :                                         continue;
    2676             :                                 }
    2677             :                                 //not a valid URL
    2678             :                                 return NULL;
    2679             :                         }
    2680         506 :                         if (!line_empty)
    2681         331 :                                 nb_lines++;
    2682             :                 }
    2683        2309 :                 if (!nl) break;
    2684        2306 :                 size -= (u32) (nl+1 - (char *) data);
    2685             :                 data = nl+1;
    2686        2306 :                 if (is_cr && (data[0]=='\n')) {
    2687           4 :                         size --;
    2688           4 :                         data++;
    2689             :                 }
    2690             :         }
    2691         133 :         if (!nb_lines) return NULL;
    2692         129 :         *score = GF_FPROBE_MAYBE_SUPPORTED;
    2693         129 :         return "application/x-gpac-playlist";
    2694             : }
    2695             : 
    2696             : #define OFFS(_n)        #_n, offsetof(GF_FileListCtx, _n)
    2697             : static const GF_FilterArgs GF_FileListArgs[] =
    2698             : {
    2699             :         { OFFS(floop), "loop playlist/list of files, `0` for one time, `n` for n+1 times, `-1` for indefinitely", GF_PROP_SINT, "0", NULL, 0},
    2700             :         { OFFS(srcs), "list of files to play - see filter help", GF_PROP_STRING_LIST, NULL, NULL, 0},
    2701             :         { OFFS(fdur), "for source files with a single frame, sets frame duration. 0/NaN fraction means reuse source timing which is usually not set!", GF_PROP_FRACTION, "1/25", NULL, 0},
    2702             :         { OFFS(revert), "revert list of files (not playlist)", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
    2703             :         { OFFS(timescale), "force output timescale on all pids. 0 uses the timescale of the first pid found", GF_PROP_UINT, "0", NULL, GF_FS_ARG_HINT_ADVANCED},
    2704             :         { OFFS(ka), "keep playlist alive (disable loop), waiting the for a new input to be added or `#end` to end playlist. The value specify the refresh rate in ms", GF_PROP_UINT, "0", NULL, GF_FS_ARG_HINT_ADVANCED},
    2705             : 
    2706             :         { OFFS(fsort), "sort list of files\n"
    2707             :                 "- no: no sorting, use default directory enumeration of OS\n"
    2708             :                 "- name: sort by alphabetical name\n"
    2709             :                 "- size: sort by increasing size\n"
    2710             :                 "- date: sort by increasing modification time\n"
    2711             :                 "- datex: sort by increasing modification time - see filter help"
    2712             :                 , GF_PROP_UINT, "no", "no|name|size|date|datex", 0},
    2713             : 
    2714             :         { OFFS(sigcues), "inject CueStart property at each source begin (new or repeated) for DASHing", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
    2715             :         { OFFS(fdel), "delete source files after processing in playlist mode (does not delete the playlist)", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
    2716             :         { OFFS(raw), "force input AV streams to be in raw format\n"
    2717             :         "- no: do not force decoding of inputs\n"
    2718             :         "- av: force decoding of audio and video inputs\n"
    2719             :         "- a: force decoding of audio inputs\n"
    2720             :         "- v: force decoding of video inputs", GF_PROP_UINT, "no", "av|a|v|no", GF_FS_ARG_HINT_NORMAL},
    2721             : 
    2722             :         {0}
    2723             : };
    2724             : 
    2725             : 
    2726             : static const GF_FilterCapability FileListCaps[] =
    2727             : {
    2728             :         CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
    2729             :         CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_FILE_EXT, "txt|m3u|pl"),
    2730             :         CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_MIME, "application/x-gpac-playlist"),
    2731             :         CAP_UINT(GF_CAPS_OUTPUT_EXCLUDED, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
    2732             : };
    2733             : 
    2734             : 
    2735             : GF_FilterRegister FileListRegister = {
    2736             :         .name = "flist",
    2737             :         GF_FS_SET_DESCRIPTION("Sources concatenator")
    2738             :         GF_FS_SET_HELP("This filter can be used to play playlist files or a list of sources.\n"
    2739             :                 "\n"
    2740             :                 "The filter loads any source supported by GPAC: remote or local files or streaming sessions (TS, RTP, DASH or other).\n"
    2741             :                 "The filter forces input demultiplex and recomputes the input timestamps into a continuous timeline.\n"
    2742             :                 "At each new source, the filter tries to remap input PIDs to already declared output PIDs of the same type, if any, or declares new output PIDs otherwise. If no input PID matches the type of an output, no packets are send for that PID.\n"
    2743             :                 "\n"
    2744             :                 "# Source list mode\n"
    2745             :                 "The source list mode is activated by using `flist:srcs=f1[,f2]`, where f1 can be a file or a directory to enumerate.\n"
    2746             :                 "The syntax for directory enumeration is:\n"
    2747             :                 "- dir/*: enumerates everything in dir\n"
    2748             :                 "- foo/*.png: enumerates all files with extension png in foo\n"
    2749             :                 "- foo/*.png;*.jpg: enumerates all files with extension png or jpg in foo\n"
    2750             :                 "\n"
    2751             :                 "The resulting file list can be sorted using [-fsort]().\n"
    2752             :                 "If the sort mode is `datex` and source files are images or single frame files, the following applies:\n"
    2753             :                 "- options [-floop](), [-revert]() and [-dur]() are ignored\n"
    2754             :                 "- the files are sorted by modification time\n"
    2755             :                 "- the first frame is assigned a timestamp of 0\n"
    2756             :                 "- each frame (coming from each file) is assigned a duration equal to the difference of modification time between the file and the next file\n"
    2757             :                 "- the last frame is assigned the same duration as the previous one\n"
    2758             :                 "# Playlist mode\n"
    2759             :                 "The playlist mode is activated when opening a playlist file (m3u format, utf-8 encoding, default extensions `m3u`, `txt` or `pl`).\n"
    2760             :                 "In this mode, directives can be given in a comment line, i.e. a line starting with '#' before the line with the file name.\n"
    2761             :                 "Lines stating with `##` are ignored.\n"
    2762             :                 "\n"
    2763             :                 "The playlist file is refreshed whenever the next source has to be reloaded in order to allow for dynamic pushing of sources in the playlist.\n"\
    2764             :                 "If the last URL played cannot be found in the playlist, the first URL in the playlist file will be loaded.\n"
    2765             :                 "\n"
    2766             :                 "When [-ka]() is used to keep refreshing the playlist on regular basis, the playlist must end with a new line.\n"
    2767             :                 "Playlist refreshing will abort:\n"
    2768             :                 "- if the input playlist has a line not ending with a LF `(\\n)` character, in order to avoid asynchronous issues when reading the playlist.\n"
    2769             :                 "- if the input playlist has not been modified for 60 times the refresh rate (based on file system modification time info).\n"
    2770             :                 "## Playlist directives\n"
    2771             :                 "A playlist directive line can contain zero or more directives, separated with space. The following directives are supported:\n"
    2772             :                 "- repeat=N: repeats N times the content (hence played N+1).\n"
    2773             :                 "- start=T: tries to play the file from start time T seconds (double format only). This may not work with some files/formats not supporting seeking.\n"
    2774             :                 "- stop=T: stops source playback after T seconds (double format only). This works on any source (implemented independently from seek support).\n"
    2775             :                 "- cat: specifies that the following entry should be concatenated to the previous source rather than opening a new source. This can optionally specify a byte range if desired, otherwise the full file is concatenated.\n"
    2776             :                 "Note: When sources are ISOBMFF files or segments on local storage or GF_FileIO objects, the concatenation will be automatically detected.\n"
    2777             :                 "- srange=T: when cat is set, indicates the start T (64 bit decimal, default 0) of the byte range from the next entry to concatenate.\n"
    2778             :                 "- send=T: when cat is set, indicates the end T (64 bit decimal, default 0) of the byte range from the next entry to concatenate.\n"
    2779             :                 "- props=STR: assigns properties described in `STR` to all pids coming from the listed sources on next line. `STR` is formatted according to `gpac -h doc` using the default parameter set.\n"
    2780             :                 "- del: specifies that the source file(s) must be deleted once processed, true by default is [-fdel]() is set.\n"
    2781             :                 "- out=V: specifies splicing start time (cf below).\n"
    2782             :                 "- in=V: specifies splicing end time (cf below).\n"
    2783             :                 "- nosync: prevents timestamp adjustments when joining sources (implied if `cat` is set).\n"
    2784             :                 "- keep: keeps spliced period in output (cf below).\n"
    2785             :                 "- mark: only inject marker for the splice period and do not load any replacement content (cf below).\n"
    2786             :                 "- sprops=STR: assigns properties described in `STR` to all pids of the main content during a splice (cf below). `STR` is formatted according to `gpac -h doc` using the default parameter set.\n"
    2787             :                 "\n"
    2788             :                 "The following global options (applying to the filter, not the sources) may also be set in the playlist:\n"
    2789             :                 "- ka=N: force [-ka]() option to `N` millisecond refresh.\n"
    2790             :                 "- floop=N: set [-floop]() option from within playlist.\n"
    2791             :                 "- raw: set [-raw]() option from within playlist.\n"
    2792             :                 "\n"
    2793             :                 "The default behavior when joining sources is to realign the timeline origin of the new source to the maximum time in all pids of the previous sources.\n"
    2794             :                 "This may create gaps in the timeline in case each pid are not of equal duration (quite common with most audio codecs).\n"
    2795             :                 "Using `nosync` directive will disable this realignment and provide a continuous timeline but may introduce synchronization errors depending in the source encoding (use with caution).\n"
    2796             :                 "## Source syntax\n"
    2797             :                 "The source lines follow the usual source syntax, see `gpac -h`.\n"
    2798             :                 "Additional pid properties can be added per source (see `gpac -h doc`), but are valid only for the current source, and reset at next source.\n"
    2799             :                 "\n"
    2800             :                 "The URL given can either be a single URL, or a list of URLs separated by \" && \" to load several sources for the active entry.\n"
    2801             :                 "Warning: There shall not be any other space/tab characters between sources.\n"
    2802             :                 "EX audio.mp4 && video.mp4\n"
    2803             :                 "## Source with filter chains\n"
    2804             :                 "Each URL can be followed by a chain of one or more filters, using the `@` link directive as used in gpac (see `gpac -h doc`).\n"
    2805             :                 "A negative link index (e.g. `@-1`) can be used to setup a new filter chain starting from the last specified source in the line.\n"
    2806             :                 "Warning: There shall be a single character, with value space (' '), before and after each link directive.\n"
    2807             :                 "\n"
    2808             :                 "EX src.mp4 @ reframer:rt=on\n"
    2809             :                 "This will inject a reframer with real-time regulation between source and `flist` filter.\n"
    2810             :                 "EX src.mp4 @ reframer:saps=1 @1 reframer:saps=0,2,3\n"
    2811             :                 "EX src.mp4 @ reframer:saps=1 @-1 reframer:saps=0,2,3\n"
    2812             :                 "This will inject a reframer filtering only SAP1 frames and a reframer filtering only non-SAP1 frames between source and `flist` filter\n"
    2813             :                 "\n"
    2814             :                 "Link options can be specified (see `gpac -h doc`).\n"
    2815             :                 "EX src.mp4 @#video reframer:rt=on\n"
    2816             :                 "This will inject a reframer with real-time regulation between video pid of source and `flist` filter.\n"
    2817             :                 "\n"
    2818             :                 "When using filter chains, the `flist` filter will only accept PIDs from the last declared filter in the chain.\n"
    2819             :                 "In order to accept other PIDs from the source, you must specify a final link directive with no following filter.\n"
    2820             :                 "EX src.mp4 @#video reframer:rt=on @-1#audio\n"
    2821             :                 "This will inject a reframer with real-time regulation between video pid of source and `flist` filter, and will also allow audio pids from source to connect to `flist` filter.\n"
    2822             :                 "\n"
    2823             :                 "The empty link directive can also be used on the last declared filter\n"
    2824             :                 "EX src.mp4 @ reframer:rt=on @#audio\n"
    2825             :                 "This will inject a reframer with real-time regulation between source and `flist` filter and only connect audio pids to `flist` filter.\n"
    2826             :                 "## Splicing\n"
    2827             :                 "The playlist can be used to splice content with other content following a media in the playlist.\n"
    2828             :                 "A source item is declared as main media in a splice operation if and only if it has an `out` directive set (possibly empty).\n"
    2829             :                 "Directive can be used for the main media except concatenation directives.\n"
    2830             :                 "\n"
    2831             :                 "The splicing operations do not alter media frames and do not perform uncompressed domain operations such as cross-fade or mixing.\n"
    2832             :                 "\n"
    2833             :                 "The `out` (resp. `in`) directive specifies the media splice start (resp. end) time. The value can be formatted as follows:\n"
    2834             :                 "- empty: the time is not yet assigned\n"
    2835             :                 "- `now`: the time is resolved to the next SAP point in the media\n"
    2836             :                 "- integer, float or fraction: set time in seconds\n"
    2837             :                 "- `+VAL`: used for `in` only, specify the end point as delta in seconds from the start point (`VAL` can be integer, float or fraction)\n"
    2838             :                 "- DATE: set splice time according to wall clock `DATE`, formatted as an `XSD dateTime`\n"
    2839             :                 "The splice times (except wall clock) are expressed in the source (main media) timing, not the reconstructed output timeline.\n"
    2840             :                 "\n"
    2841             :                 "When a splice begins (`out` time reached), the source items following the main media are played until the end of the splice or the end of the main media.\n"
    2842             :                 "Sources used during the splice period can use directives such as `start`, `dur` or `repeat`.\n"
    2843             :                 "\n"
    2844             :                 "Once a splice is done (`in` time reached), the main media `out` splice time is reset to undefined.\n"
    2845             :                 "\n"
    2846             :                 "When the main media has undefined `out` or `in` splice times, the playlist is reloaded at each new main media packet to check for resolved values.\n"
    2847             :                 "- `out` can only be modified when no splice is active, otherwise it is ignored. If modified, it resets the next source to play to be the one following the modified main media.\n"
    2848             :                 "- `in` can only be modified when a splice is active with an undefined end time, otherwise it is ignored.\n"
    2849             :                 "\n"
    2850             :                 "When the main media is over:\n"
    2851             :                 "- if `repeat` directive is set, the main media is repeated, `in` and `out` set to their initial values and the next splicing content is the one following the main content,\n"
    2852             :                 "- otherwise, the next source queued is the one following the last source played during the last splice period.\n"
    2853             :                 "\n"
    2854             :                 "It is allowed to defined several main media in the playlist, but a main media is not allowed as media for a splice period.\n"
    2855             :                 "\n"
    2856             :                 "The filter will look for the property `Period` on the output PIDs of the main media for multi-period DASH.\n"
    2857             :                 "If found, `_N` is appended to the period ID, with `N` starting from 1 and increased at each main media resume.\n"
    2858             :                 "If no `Period` property is set on main or spliced media, period switch can still be forced using [-pswitch](dasher) DASH option.\n"
    2859             :                 "\n"
    2860             :                 "If `mark` directive is set for a main media, no content replacement is done and the splice boundaries will be signaled in the main media.\n"
    2861             :                 "If `keep` directive is set for a main media, the main media is forwarded along with the replacement content.\n"
    2862             :                 "When `mark` or `keep` directives are set, it is possible to alter the PID properties of the main media using `sprops` directive.\n"
    2863             :                 "\n"
    2864             :                 "EX #out=2 in=4 mark sprops=#xlink=http://foo.bar/\nEX src:#Period=main\n"
    2865             :                 "This will inject property xlink on the output pids in the splice zone (corresponding to period `main_2`) but not in the rest of the main media.\n"
    2866             :                 "\n"
    2867             :                 "Directives `mark`, `keep` and `sprops` are reset at the end of the splice period.\n"
    2868             :                 )
    2869             :         .private_size = sizeof(GF_FileListCtx),
    2870             :         .max_extra_pids = -1,
    2871             :         .flags = GF_FS_REG_ACT_AS_SOURCE | GF_FS_REG_REQUIRES_RESOLVER,
    2872             :         .args = GF_FileListArgs,
    2873             :         .initialize = filelist_initialize,
    2874             :         .finalize = filelist_finalize,
    2875             :         SETCAPS(FileListCaps),
    2876             :         .configure_pid = filelist_configure_pid,
    2877             :         .process = filelist_process,
    2878             :         .process_event = filelist_process_event,
    2879             :         .probe_data = filelist_probe_data
    2880             : };
    2881             : 
    2882        2877 : const GF_FilterRegister *filelist_register(GF_FilterSession *session)
    2883             : {
    2884        2877 :         return &FileListRegister;
    2885             : }
    2886             : 

Generated by: LCOV version 1.13