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 :
|