1 /*
2  *			GPAC - Multimedia Framework C SDK
3  *
4  *			Authors: Jean Le Feuvre
5  *			Copyright (c) Telecom ParisTech 2017-2020
6  *					All rights reserved
7  *
8  *  This file is part of GPAC / force reframer 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/avparse.h>
27 #include <gpac/constants.h>
28 #include <gpac/filters.h>
29 
30 enum
31 {
32 	REFRAME_RT_OFF = 0,
33 	REFRAME_RT_ON,
34 	REFRAME_RT_SYNC,
35 };
36 
37 enum
38 {
39 	REFRAME_ROUND_BEFORE=0,
40 	REFRAME_ROUND_AFTER,
41 	REFRAME_ROUND_CLOSEST,
42 };
43 
44 enum
45 {
46 	RANGE_NONE=0,
47 	RANGE_CLOSED,
48 	RANGE_OPEN,
49 	RANGE_DONE
50 };
51 
52 enum
53 {
54 	EXTRACT_NONE=0,
55 	EXTRACT_RANGE,
56 	EXTRACT_SAP,
57 	EXTRACT_SIZE,
58 	EXTRACT_DUR,
59 };
60 
61 typedef struct
62 {
63 	GF_FilterPid *ipid, *opid;
64 	u32 timescale;
65 	u64 cts_us_at_init;
66 	u64 sys_clock_at_init;
67 	u32 nb_frames;
68 	Bool can_split;
69 	Bool all_saps;
70 	Bool needs_adjust;
71 
72 	u64 ts_at_range_start_plus_one;
73 	u64 ts_at_range_end;
74 	s64 cts_init;
75 
76 	GF_List *pck_queue;
77 	//0: not comuted, 1: computed and valid TS, 2: end of stream on pid
78 	u32 range_start_computed;
79 	u64 range_end_reached_ts;
80 	u64 prev_sap_ts;
81 	u32 prev_sap_frame_idx;
82 	u32 nb_frames_range;
83 	u64 sap_ts_plus_one;
84 	Bool first_pck_sent;
85 
86 	u32 tk_delay;
87 	Bool in_eos;
88 	u32 split_start;
89 	u32 split_end;
90 
91 	GF_FilterPacket *split_pck;
92 	GF_FilterPacket *resinsert_single_pck;
93 	Bool is_playing;
94 } RTStream;
95 
96 typedef struct
97 {
98 	//args
99 	Bool exporter;
100 	GF_PropUIntList saps;
101 	GF_PropUIntList frames;
102 	Bool refs;
103 	u32 rt;
104 	Double speed;
105 	Bool raw;
106 	GF_List *xs, *xe;
107 	Bool nosap, splitrange, xadjust;
108 	u32 xround;
109 	Double seeksafe;
110 
111 	//internal
112 	Bool filter_sap1;
113 	Bool filter_sap2;
114 	Bool filter_sap3;
115 	Bool filter_sap4;
116 	Bool filter_sap_none;
117 
118 	GF_List *streams;
119 	RTStream *clock;
120 
121 	u64 reschedule_in;
122 	u64 clock_val;
123 
124 	u32 range_type;
125 	u32 cur_range_idx;
126 	GF_Fraction64 cur_start, cur_end;
127 	u64 start_frame_idx_plus_one, end_frame_idx_plus_one;
128 
129 	Bool timing_initialized;
130 	Bool in_range;
131 
132 	Bool seekable;
133 
134 	GF_Fraction64 extract_dur;
135 	u32 extract_mode;
136 	Bool is_range_extraction;
137 	u32 file_idx;
138 
139 	u64 min_ts_computed;
140 	u32 min_ts_scale;
141 	u64 split_size;
142 	u64 est_file_size;
143 	u64 prev_min_ts_computed;
144 	u32 prev_min_ts_scale;
145 	u32 gop_depth;
146 
147 	u32 wait_video_range_adjust;
148 	Bool is_eos, has_seen_eos;
149 	u32 nb_non_saps;
150 } GF_ReframerCtx;
151 
reframer_reset_stream(GF_ReframerCtx * ctx,RTStream * st)152 static void reframer_reset_stream(GF_ReframerCtx *ctx, RTStream *st)
153 {
154 	if (st->pck_queue) {
155 		while (gf_list_count(st->pck_queue)) {
156 			GF_FilterPacket *pck = gf_list_pop_front(st->pck_queue);
157 			gf_filter_pck_unref(pck);
158 		}
159 		gf_list_del(st->pck_queue);
160 	}
161 	if (st->split_pck) gf_filter_pck_unref(st->split_pck);
162 	if (st->resinsert_single_pck) gf_filter_pck_unref(st->resinsert_single_pck);
163 	gf_free(st);
164 }
reframer_configure_pid(GF_Filter * filter,GF_FilterPid * pid,Bool is_remove)165 GF_Err reframer_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
166 {
167 	u32 i;
168 	const GF_PropertyValue *p;
169 	GF_ReframerCtx *ctx = gf_filter_get_udta(filter);
170 	RTStream *st = gf_filter_pid_get_udta(pid);
171 
172 	if (is_remove) {
173 		if (st) {
174 			gf_filter_pid_remove(st->opid);
175 			gf_list_del_item(ctx->streams, st);
176 			reframer_reset_stream(ctx, st);
177 		}
178 		return GF_OK;
179 	}
180 	if (! gf_filter_pid_check_caps(pid))
181 		return GF_NOT_SUPPORTED;
182 
183 	if (st) {
184 		gf_filter_pid_reset_properties(st->opid);
185 	} else {
186 		GF_SAFEALLOC(st, RTStream);
187 		if (!st) return GF_OUT_OF_MEM;
188 
189 		gf_list_add(ctx->streams, st);
190 		st->opid = gf_filter_pid_new(filter);
191 		gf_filter_pid_set_udta(pid, st);
192 		gf_filter_pid_set_udta(st->opid, st);
193 		st->ipid = pid;
194 		st->pck_queue = gf_list_new();
195 		st->all_saps = GF_TRUE;
196 	}
197 	//copy properties at init or reconfig
198 	gf_filter_pid_copy_properties(st->opid, pid);
199 
200 	p = gf_filter_pid_get_property(pid, GF_PROP_PID_TIMESCALE);
201 	if (p) st->timescale = p->value.uint;
202 	else st->timescale = 1000;
203 
204 	if (!st->all_saps) {
205 		ctx->nb_non_saps--;
206 		st->all_saps = GF_TRUE;
207 	}
208 	st->can_split = GF_FALSE;
209 	p = gf_filter_pid_get_property(pid, GF_PROP_PID_STREAM_TYPE);
210 	if (p) {
211 		switch (p->value.uint) {
212 		case GF_STREAM_TEXT:
213 			st->can_split = GF_TRUE;
214 			break;
215 		}
216 	}
217 	st->needs_adjust = ctx->xadjust ? GF_TRUE : GF_FALSE;
218 
219 	st->tk_delay = 0;
220 	p = gf_filter_pid_get_property(pid, GF_PROP_PID_DELAY);
221 	if (p) {
222 		//delay negative is skip: this is CTS adjustment for B-frames: we keep that notif in the stream
223 		if (p->value.sint<0) {
224 			st->tk_delay = 0;
225 		}
226 		//delay positive is delay, we keep the value for RT regulation and range
227 		else {
228 			st->tk_delay = (u32) p->value.sint;
229 			//if range processing, we drop frames not in the target playback range so do not forward delay
230 			if (ctx->range_type) {
231 				gf_filter_pid_set_property(st->opid, GF_PROP_PID_DELAY, NULL);
232 			}
233 		}
234 	}
235 	p = gf_filter_pid_get_property(pid, GF_PROP_PID_PLAYBACK_MODE);
236 	if (!p || (p->value.uint < GF_PLAYBACK_MODE_SEEK))
237 		ctx->seekable = GF_FALSE;
238 
239 
240 	ctx->filter_sap1 = ctx->filter_sap2 = ctx->filter_sap3 = ctx->filter_sap4 = ctx->filter_sap_none = GF_FALSE;
241 	for (i=0; i<ctx->saps.nb_items; i++) {
242 		switch (ctx->saps.vals[i]) {
243 		case 1: ctx->filter_sap1 = GF_TRUE; break;
244 		case 2: ctx->filter_sap2 = GF_TRUE; break;
245 		case 3: ctx->filter_sap3 = GF_TRUE; break;
246 		case 4: ctx->filter_sap4 = GF_TRUE; break;
247 		default: ctx->filter_sap_none = GF_TRUE; break;
248 		}
249 	}
250 	gf_filter_pid_set_framing_mode(pid, GF_TRUE);
251 
252 	return GF_OK;
253 }
254 
reframer_parse_date(char * date,GF_Fraction64 * value,u64 * frame_idx_plus_one,u32 * extract_mode)255 static Bool reframer_parse_date(char *date, GF_Fraction64 *value, u64 *frame_idx_plus_one, u32 *extract_mode)
256 {
257 	u64 v;
258 	value->num  =0;
259 	value->den = 0;
260 
261 	if (extract_mode)
262 		*extract_mode = EXTRACT_RANGE;
263 
264 	if (date[0] == 'T') {
265 		u32 h=0, m=0, s=0, ms=0;
266 		if (strchr(date, '.')) {
267 			if (sscanf(date, "T%u:%u:%u.%u", &h, &m, &s, &ms) != 4) {
268 				if (sscanf(date, "T%u:%u.%u", &m, &s, &ms) != 3) {
269 					goto exit;
270 				}
271 			}
272 			if (ms>=1000) ms=0;
273 		} else {
274 			if (sscanf(date, "T%u:%u:%u", &h, &m, &s) != 3) {
275 				goto exit;
276 			}
277 		}
278 		v = h*3600 + m*60 + s;
279 		v *= 1000;
280 		v += ms;
281 		value->num = v;
282 		value->den = 1000;
283 		return GF_TRUE;
284 	}
285 	if ((date[0]=='F') || (date[0]=='f')) {
286 		*frame_idx_plus_one = 1 + atoi(date+1);
287 		return GF_TRUE;
288 	}
289 	if (sscanf(date, LLD"/"LLU, &value->num, &value->den)==2) {
290 		return GF_TRUE;
291 	}
292 	if (sscanf(date, LLU, &v)==1) {
293 		value->num = v;
294 		value->den = 1000;
295 		return GF_TRUE;
296 	}
297 	if (!strcmp(date, "RAP") || !strcmp(date, "SAP")) {
298 		if (extract_mode)
299 			*extract_mode = EXTRACT_SAP;
300 		value->num = 0;
301 		value->den = 1000;
302 		return GF_TRUE;
303 	}
304 	if ((date[0]=='D') || (date[0]=='d')) {
305 		if (extract_mode)
306 			*extract_mode = EXTRACT_DUR;
307 		if (sscanf(date+1, LLD"/"LLU, &value->num, &value->den)==2) {
308 			return GF_TRUE;
309 		}
310 		if (sscanf(date+1, LLU, &v)==1) {
311 			value->num = v;
312 			value->den = 1000;
313 			return GF_TRUE;
314 		}
315 	}
316 	if ((date[0]=='S') || (date[0]=='s')) {
317 		GF_PropertyValue p;
318 		if (extract_mode)
319 			*extract_mode = EXTRACT_SIZE;
320 		p = gf_props_parse_value(GF_PROP_LUINT, "size", date+1, NULL, ',');
321 		if (p.type==GF_PROP_LUINT) {
322 			value->den = p.value.longuint;
323 			return GF_TRUE;
324 		}
325 	}
326 
327 exit:
328 	GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[Reframer] Unrecognized date format %s, expecting TXX:XX:XX[.XX], INT or FRAC\n", date));
329 	if (extract_mode)
330 		*extract_mode = EXTRACT_NONE;
331 	return GF_FALSE;
332 }
333 
reframer_load_range(GF_ReframerCtx * ctx)334 static void reframer_load_range(GF_ReframerCtx *ctx)
335 {
336 	u32 i, count;
337 	Bool do_seek = ctx->seekable;
338 	u64 prev_frame = ctx->start_frame_idx_plus_one;
339 	GF_Fraction64 prev_end;
340 	char *start_date=NULL, *end_date=NULL;
341 
342 	if (ctx->extract_mode==EXTRACT_DUR) {
343 		ctx->cur_start.num += (ctx->extract_dur.num * ctx->cur_start.den) / ctx->extract_dur.den;
344 		ctx->cur_end.num += (ctx->extract_dur.num * ctx->cur_end.den) / ctx->extract_dur.den;
345 		ctx->file_idx++;
346 		return;
347 	}
348 	if ((ctx->extract_mode==EXTRACT_SAP) || (ctx->extract_mode==EXTRACT_SIZE)) {
349 		ctx->cur_start = ctx->cur_end;
350 		ctx->min_ts_computed = 0;
351 		ctx->min_ts_scale = 0;
352 		ctx->file_idx++;
353 		return;
354 	}
355 	prev_end = ctx->cur_end;
356 	ctx->start_frame_idx_plus_one = 0;
357 	ctx->end_frame_idx_plus_one = 0;
358 	ctx->cur_start.num = 0;
359 	ctx->cur_start.den = 0;
360 	ctx->cur_end.num = 0;
361 	ctx->cur_end.den = 0;
362 
363 	count = gf_list_count(ctx->xs);
364 	if (!count) {
365 		if (ctx->range_type) goto range_done;
366 		return;
367 	}
368 	if (ctx->cur_range_idx>=count) {
369 		goto range_done;
370 	} else {
371 		start_date = gf_list_get(ctx->xs, ctx->cur_range_idx);
372 		end_date = gf_list_get(ctx->xe, ctx->cur_range_idx);
373 	}
374 	if (!start_date)
375 		goto range_done;
376 
377 	ctx->cur_range_idx++;
378 	if (!end_date) ctx->range_type = RANGE_OPEN;
379 	else ctx->range_type = RANGE_CLOSED;
380 
381 	if (!reframer_parse_date(start_date, &ctx->cur_start, &ctx->start_frame_idx_plus_one, &ctx->extract_mode)) {
382 		GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[Reframer] cannot parse start date, assuming end of ranges\n"));
383 		//done
384 		ctx->range_type = RANGE_DONE;
385 		return;
386 	}
387 
388 	//range in frame
389 	if (ctx->start_frame_idx_plus_one) {
390 		//either range is before or prev range was not frame-based
391 		if (ctx->start_frame_idx_plus_one > prev_frame)
392 			do_seek = GF_TRUE;
393 	}
394 	//range is time based, prev was frame-based, seek
395 	else if (!prev_end.den) {
396 		do_seek = GF_TRUE;
397 	} else {
398 		//cur start is before previous end, need to seek
399 		if (ctx->cur_start.num * prev_end.den < prev_end.num * ctx->cur_start.den) {
400 			do_seek = GF_TRUE;
401 		}
402 		//cur start is less than our seek safety from previous end, do not seek
403 		if (ctx->cur_start.num * prev_end.den < (prev_end.num + ctx->seeksafe*prev_end.den) * ctx->cur_start.den)
404 			do_seek = GF_FALSE;
405 	}
406 	//do not issue seek on first range, done when catching play requests
407 	if (ctx->cur_range_idx==1) {
408 		do_seek = GF_FALSE;
409 	}
410 
411 	if (!ctx->seekable && do_seek) {
412 		GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[Reframer] ranges not in order and input not seekable, aborting extraction\n"));
413 		goto range_done;
414 	}
415 
416 	ctx->is_range_extraction = ((ctx->extract_mode==EXTRACT_RANGE) || (ctx->extract_mode==EXTRACT_DUR)) ? GF_TRUE : GF_FALSE;
417 
418 	if (ctx->extract_mode != EXTRACT_RANGE) {
419 		end_date = NULL;
420 		if (ctx->extract_mode==EXTRACT_DUR) {
421 			ctx->extract_dur = ctx->cur_start;
422 			ctx->cur_start.num = 0;
423 			ctx->cur_start.den = ctx->extract_dur.den;
424 			ctx->cur_end = ctx->extract_dur;
425 			ctx->range_type = RANGE_CLOSED;
426 			ctx->file_idx = 1;
427 			ctx->splitrange = GF_TRUE;
428 			ctx->xadjust = GF_TRUE;
429 		}
430 		else if (ctx->extract_mode==EXTRACT_SIZE) {
431 			ctx->splitrange = GF_TRUE;
432 			ctx->split_size = ctx->cur_start.den;
433 			if (!ctx->split_size) {
434 				GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[Reframer] invalid split size %d\n", ctx->split_size));
435 				goto range_done;
436 			}
437 			ctx->file_idx = 1;
438 		}
439 		else if (ctx->extract_mode==EXTRACT_SAP) {
440 			ctx->splitrange = GF_TRUE;
441 		}
442 	}
443 	if (end_date) {
444 		if (!reframer_parse_date(end_date, &ctx->cur_end, &ctx->end_frame_idx_plus_one, NULL)) {
445 			GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[Reframer] cannot parse end date, assuming open range\n"));
446 			ctx->range_type = RANGE_OPEN;
447 		}
448 	}
449 
450 	//reset realtime range and issue seek requests
451 	if (ctx->rt || do_seek) {
452 		Double start_range = 0;
453 		if (do_seek) {
454 			start_range = (Double) ctx->cur_start.num;
455 			start_range /= ctx->cur_start.den;
456 			if (start_range > ctx->seeksafe)
457 				start_range -= ctx->seeksafe;
458 			else
459 				start_range = 0;
460 			ctx->has_seen_eos = GF_FALSE;
461 		}
462 		count = gf_list_count(ctx->streams);
463 		for (i=0; i<count; i++) {
464 			RTStream *st = gf_list_get(ctx->streams, i);
465 			if (ctx->rt) {
466 				st->cts_us_at_init = 0;
467 				st->sys_clock_at_init = 0;
468 			}
469 			if (do_seek) {
470 				GF_FilterEvent evt;
471 				GF_FEVT_INIT(evt, GF_FEVT_STOP, st->ipid);
472 				gf_filter_pid_send_event(st->ipid, &evt);
473 				GF_FEVT_INIT(evt, GF_FEVT_PLAY, st->ipid);
474 				evt.play.start_range = start_range;
475 				evt.play.speed = 1;
476 				gf_filter_pid_send_event(st->ipid, &evt);
477 			}
478 		}
479 	}
480 	return;
481 
482 range_done:
483 	//done
484 	ctx->range_type = RANGE_DONE;
485 	count = gf_list_count(ctx->streams);
486 	for (i=0; i<count; i++) {
487 		GF_FilterEvent evt;
488 		RTStream *st = gf_list_get(ctx->streams, i);
489 		gf_filter_pid_set_discard(st->ipid, GF_TRUE);
490 		GF_FEVT_INIT(evt, GF_FEVT_STOP, st->ipid);
491 		gf_filter_pid_send_event(st->ipid, &evt);
492 		gf_filter_pid_set_eos(st->opid);
493 	}
494 
495 }
496 
reframer_update_ts_shift(GF_ReframerCtx * ctx,s64 diff_ts,u32 timescale)497 static void reframer_update_ts_shift(GF_ReframerCtx *ctx, s64 diff_ts, u32 timescale)
498 {
499 	u32 i, count = gf_list_count(ctx->streams);
500 	for (i=0; i<count; i++) {
501 		s64 ts_shift;
502 		RTStream *st = gf_list_get(ctx->streams, i);
503 		ts_shift = diff_ts;
504 		ts_shift *= st->timescale;
505 		ts_shift /= timescale;
506 		st->cts_init -= ts_shift;
507 	}
508 }
reframer_drop_packet(GF_ReframerCtx * ctx,RTStream * st,GF_FilterPacket * pck,Bool pck_is_ref)509 void reframer_drop_packet(GF_ReframerCtx *ctx, RTStream *st, GF_FilterPacket *pck, Bool pck_is_ref)
510 {
511 	if (pck_is_ref) {
512 		gf_list_rem(st->pck_queue, 0);
513 		gf_filter_pck_unref(pck);
514 	} else {
515 		gf_filter_pid_drop_packet(st->ipid);
516 	}
517 }
518 
reframer_send_packet(GF_Filter * filter,GF_ReframerCtx * ctx,RTStream * st,GF_FilterPacket * pck,Bool pck_is_ref)519 Bool reframer_send_packet(GF_Filter *filter, GF_ReframerCtx *ctx, RTStream *st, GF_FilterPacket *pck, Bool pck_is_ref)
520 {
521 	Bool do_send = GF_FALSE;
522 
523 
524 	if (!ctx->rt) {
525 		do_send = GF_TRUE;
526 	} else {
527 		u64 cts_us = gf_filter_pck_get_dts(pck);
528 		if (cts_us==GF_FILTER_NO_TS)
529 			cts_us = gf_filter_pck_get_cts(pck);
530 
531 		if (cts_us==GF_FILTER_NO_TS) {
532 			do_send = GF_TRUE;
533 		} else {
534 			u64 clock = ctx->clock_val;
535 			cts_us += st->tk_delay;
536 
537 			cts_us *= 1000000;
538 			cts_us /= st->timescale;
539 			if (ctx->rt==REFRAME_RT_SYNC) {
540 				if (!ctx->clock) ctx->clock = st;
541 
542 				st = ctx->clock;
543 			}
544 			if (!st->sys_clock_at_init) {
545 				st->cts_us_at_init = cts_us;
546 				st->sys_clock_at_init = clock;
547 				do_send = GF_TRUE;
548 			} else if (cts_us < st->cts_us_at_init) {
549 				GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[Reframer] CTS less than CTS used to initialize clock, not delaying\n"));
550 				do_send = GF_TRUE;
551 			} else {
552 				u64 diff = cts_us - st->cts_us_at_init;
553 				if (ctx->speed>0) diff = (u64) ( diff / ctx->speed);
554 				else if (ctx->speed<0) diff = (u64) ( diff / -ctx->speed);
555 
556 				clock -= st->sys_clock_at_init;
557 				if (clock + 1000 >= diff) {
558 					do_send = GF_TRUE;
559 					GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Reframer] Sending packet "LLU" us too late (clock diff "LLU" - CTS diff "LLU")\n", 1000+clock - diff, clock, diff));
560 				} else {
561 					diff -= clock;
562 					if (!ctx->reschedule_in)
563 						ctx->reschedule_in = diff;
564 					else if (ctx->reschedule_in > diff)
565 						ctx->reschedule_in = diff;
566 				}
567 			}
568 		}
569 	}
570 
571 	if (!ctx->range_type && ctx->frames.nb_items) {
572 		u32 i;
573 		Bool found=GF_FALSE;
574 		for (i=0; i<ctx->frames.nb_items; i++) {
575 			if (ctx->frames.vals[i] == st->nb_frames + 1) {
576 				found=GF_TRUE;
577 				break;
578 			}
579 		}
580 		if (!found) {
581 			//drop
582 			gf_filter_pid_drop_packet(st->ipid);
583 			st->nb_frames++;
584 			return GF_TRUE;
585 		}
586 	}
587 
588 	if (!do_send)
589 		return GF_FALSE;
590 
591 	//range processing
592 	if (st->ts_at_range_start_plus_one) {
593 		s64 ts;
594 		GF_FilterPacket *new_pck = gf_filter_pck_new_ref(st->opid, NULL, 0, pck);
595 		gf_filter_pck_merge_properties(pck, new_pck);
596 
597 		//signal chunk start boundary
598 		if (!st->first_pck_sent) {
599 			u32 i, len;
600 			char *file_suf_name = NULL;
601 			char *start = gf_list_get(ctx->xs, ctx->cur_range_idx-1);
602 			char *end = NULL;
603 			if (ctx->range_type==1) end = gf_list_get(ctx->xe, ctx->cur_range_idx-1);
604 			st->first_pck_sent = GF_TRUE;
605 
606 			if (ctx->extract_mode==EXTRACT_RANGE) {
607 				gf_filter_pck_set_property(new_pck, GF_PROP_PCK_FILENUM, &PROP_UINT(ctx->cur_range_idx) );
608 				gf_dynstrcat(&file_suf_name, start, NULL);
609 				if (end)
610 					gf_dynstrcat(&file_suf_name, end, "_");
611 
612 				len = (u32) strlen(file_suf_name);
613 				//replace : and / characters
614 				for (i=0; i<len; i++) {
615 					switch (file_suf_name[i]) {
616 					case ':':
617 					case '/':
618 						file_suf_name[i] = '.';
619 						break;
620 					}
621 				}
622 				gf_filter_pck_set_property(new_pck, GF_PROP_PCK_FILESUF, &PROP_STRING_NO_COPY(file_suf_name) );
623 			} else {
624 				u64 start_t, end_t;
625 				char szFileSuf[1000];
626 				start_t = ctx->cur_start.num * 1000;
627 				start_t /= ctx->cur_start.den;
628 				end_t = ctx->cur_end.num * 1000;
629 				end_t /= ctx->cur_end.den;
630 
631 				gf_filter_pck_set_property(new_pck, GF_PROP_PCK_FILENUM, &PROP_UINT(ctx->file_idx) );
632 				sprintf(szFileSuf, LLU"-"LLU, start_t, end_t);
633 				gf_filter_pck_set_property(new_pck, GF_PROP_PCK_FILESUF, &PROP_STRING(szFileSuf) );
634 			}
635 		}
636 
637 		//rewrite timestamps
638 		ts = gf_filter_pck_get_cts(pck);
639 
640 		if (ts != GF_FILTER_NO_TS) {
641 			ts += st->tk_delay;
642 			ts += st->ts_at_range_end;
643 			ts -= st->ts_at_range_start_plus_one - 1;
644 			ts -= st->cts_init;
645 			if (ts<0) {
646 				reframer_update_ts_shift(ctx, -ts, st->timescale);
647 				ts = 0;
648 			}
649 
650 			gf_filter_pck_set_cts(new_pck, (u64) ts);
651 			if (ctx->raw) {
652 				gf_filter_pck_set_dts(new_pck, ts);
653 			}
654 		}
655 		if (!ctx->raw) {
656 			ts = gf_filter_pck_get_dts(pck);
657 			if (ts != GF_FILTER_NO_TS) {
658 				ts += st->tk_delay;
659 				ts -= st->ts_at_range_start_plus_one - 1;
660 				ts += st->ts_at_range_end;
661 				gf_filter_pck_set_dts(new_pck, (u64) ts);
662 			}
663 		}
664 		if (st->split_start) {
665 			u32 dur = gf_filter_pck_get_duration(pck);
666 			assert(dur>st->split_start);
667 			dur -= st->split_start;
668 			gf_filter_pck_set_duration(new_pck, dur);
669 			st->ts_at_range_start_plus_one += st->split_start;
670 			st->split_start = 0;
671 		}
672 		//last packet and forced duration
673 		if (st->split_end && (gf_list_count(st->pck_queue)==1)) {
674 			gf_filter_pck_set_duration(new_pck, st->split_end);
675 			st->split_end = 0;
676 		}
677 
678 		gf_filter_pck_send(new_pck);
679 	} else {
680 		gf_filter_pck_forward(pck, st->opid);
681 	}
682 
683 	reframer_drop_packet(ctx, st, pck, pck_is_ref);
684 	st->nb_frames++;
685 	return GF_TRUE;
686 }
687 
reframer_init_timing(GF_Filter * filter,GF_ReframerCtx * ctx)688 static Bool reframer_init_timing(GF_Filter *filter, GF_ReframerCtx *ctx)
689 {
690 	u32 i, count = gf_filter_get_ipid_count(filter);
691 	u64 min_dts_plus_one = 0;
692 	u32 pid_scale=0;
693 	for (i=0; i<count; i++) {
694 		u64 ts;
695 		GF_FilterPacket *pck;
696 		GF_FilterPid *ipid = gf_filter_get_ipid(filter, i);
697 		RTStream *st = ipid ? gf_filter_pid_get_udta(ipid) : NULL;
698 		if (!st) continue;
699 
700 		pck = gf_filter_pid_get_packet(ipid);
701 		if (!pck) {
702 			//not ready yet
703 			if (st->is_playing && !gf_filter_pid_is_eos(ipid))
704 				return GF_FALSE;
705 			//not playing or eos
706 			continue;
707 		}
708 		ts = gf_filter_pck_get_dts(pck);
709 		ts += st->tk_delay;
710 
711 		if (!min_dts_plus_one) {
712 			min_dts_plus_one = 1 + ts;
713 			pid_scale = st->timescale;
714 		} else if (ts * pid_scale < (min_dts_plus_one-1) * st->timescale) {
715 			min_dts_plus_one = 1 + ts;
716 			pid_scale = st->timescale;
717 		}
718 	}
719 	for (i=0; i<count; i++) {
720 		GF_FilterPid *ipid = gf_filter_get_ipid(filter, i);
721 		RTStream *st = ipid ? gf_filter_pid_get_udta(ipid) : NULL;
722 		if (!st) continue;
723 
724 		if (!pid_scale) pid_scale = st->timescale;
725 
726 		st->cts_init = min_dts_plus_one-1;
727 		if (st->timescale != pid_scale) {
728 			st->cts_init *= st->timescale;
729 			st->cts_init /= pid_scale;
730 		}
731 	}
732 	ctx->timing_initialized = GF_TRUE;
733 	return GF_TRUE;
734 }
735 
reframer_check_pck_range(GF_ReframerCtx * ctx,RTStream * st,u64 ts,u32 dur,u32 frame_idx)736 static u32 reframer_check_pck_range(GF_ReframerCtx *ctx, RTStream *st, u64 ts, u32 dur, u32 frame_idx)
737 {
738 	if (ctx->start_frame_idx_plus_one) {
739 		//frame not after our range start
740 		if (frame_idx<ctx->start_frame_idx_plus_one) {
741 			return 0;
742 		} else {
743 			//closed range, check
744 			if ((ctx->range_type!=RANGE_OPEN) && (frame_idx>=ctx->end_frame_idx_plus_one)) {
745 				return 2;
746 			}
747 			return 1;
748 		}
749 	} else {
750 		Bool before = GF_FALSE;
751 		Bool after = GF_FALSE;
752 
753 		//ts not after our range start
754 		if ((s64) (ts * ctx->cur_start.den) < ctx->cur_start.num * st->timescale) {
755 			before = GF_TRUE;
756 		}
757 		//closed range, check
758 		if ((ctx->range_type!=RANGE_OPEN) && ((s64) ((ts+dur) * ctx->cur_end.den) >= ctx->cur_end.num * st->timescale)) {
759 			after = GF_TRUE;
760 		}
761 		if (before) {
762 			if (!after)
763 				return 0;
764 			//long duration samples (typically text) can both start before and end after the target range
765 			else
766 				return 2;
767 		}
768 		if (after) return 2;
769 		return 1;
770 	}
771 	return 0;
772 }
773 
reframer_purge_queues(GF_ReframerCtx * ctx,u64 ts,u32 timescale)774 void reframer_purge_queues(GF_ReframerCtx *ctx, u64 ts, u32 timescale)
775 {
776 	u32 i, count = gf_list_count(ctx->streams);
777 	for (i=0; i<count; i++) {
778 		RTStream *st = gf_list_get(ctx->streams, i);
779 		u64 ts_rescale = ts;
780 		if (st->resinsert_single_pck)
781 			continue;
782 
783 		if (st->timescale != timescale) {
784 			ts_rescale *= st->timescale;
785 			ts_rescale /= timescale;
786 		}
787 		while (1) {
788 			GF_FilterPacket *pck = gf_list_get(st->pck_queue, 0);
789 			if (!pck) break;
790 			u64 dts = gf_filter_pck_get_dts(pck);
791 			if (dts==GF_FILTER_NO_TS)
792 				dts = gf_filter_pck_get_cts(pck);
793 
794 			dts += gf_filter_pck_get_duration(pck);
795 			if (dts >= ts_rescale) break;
796 			gf_list_rem(st->pck_queue, 0);
797 			gf_filter_pck_unref(pck);
798 			st->nb_frames++;
799 		}
800 	}
801 }
802 
check_gop_split(GF_ReframerCtx * ctx)803 static void check_gop_split(GF_ReframerCtx *ctx)
804 {
805 	u32 i, count = gf_list_count(ctx->streams);
806 	Bool flush_all = GF_FALSE;
807 
808 	if (!ctx->min_ts_scale) {
809 		u64 min_ts = 0;
810 		u32 min_timescale=0;
811 		u64 min_ts_a = 0;
812 		u32 min_timescale_a=0;
813 		u32 nb_eos = 0;
814 		Bool has_empty_streams = GF_FALSE;
815 		Bool wait_for_sap = GF_FALSE;
816 		for (i=0; i<count; i++) {
817 			u32 j, nb_pck, nb_sap;
818 			u64 last_sap_ts=0;
819 			RTStream *st = gf_list_get(ctx->streams, i);
820 			nb_pck = gf_list_count(st->pck_queue);
821 			nb_sap = 0;
822 			if (st->in_eos) {
823 				nb_eos++;
824 				if (!nb_pck) {
825 					has_empty_streams = GF_TRUE;
826 					continue;
827 				}
828 			}
829 
830 			for (j=0; j<nb_pck; j++) {
831 				u64 ts;
832 				GF_FilterPacket *pck = gf_list_get(st->pck_queue, j);
833 				if (!ctx->raw && !gf_filter_pck_get_sap(pck) ) {
834 					continue;
835 				}
836 				ts = gf_filter_pck_get_dts(pck);
837 				if (ts==GF_FILTER_NO_TS)
838 					ts = gf_filter_pck_get_cts(pck);
839 				ts += st->tk_delay;
840 
841 				nb_sap++;
842 				if (nb_sap <= 1 + ctx->gop_depth) {
843 					continue;
844 				}
845 
846 				last_sap_ts = ts;
847 				break;
848 			}
849 			//in SAP split, flush as soon as we no longer have 2 consecutive saps
850 			if (!last_sap_ts) {
851 				if (st->in_eos && !flush_all && !st->resinsert_single_pck) {
852 					flush_all = GF_TRUE;
853 				} else if (!st->all_saps) {
854 					wait_for_sap = GF_TRUE;
855 				}
856 			}
857 
858 			if (st->all_saps) {
859 				if (!min_ts_a || (last_sap_ts * min_timescale_a < min_ts_a * st->timescale) ) {
860 					min_ts_a = last_sap_ts;
861 					min_timescale_a = st->timescale;
862 				}
863 			} else {
864 				if (!min_ts || (last_sap_ts * min_timescale < min_ts * st->timescale) ) {
865 					min_ts = last_sap_ts;
866 					min_timescale = st->timescale;
867 				}
868 			}
869 		}
870 
871 		//in size split, flush as soon as one stream is in eos
872 		if (nb_eos && has_empty_streams) {
873 			flush_all = GF_TRUE;
874 		}
875 
876 		//if flush, get timestamp + dur of last packet in each stream and use this as final end time
877 		if (flush_all) {
878 			for (i=0; i<count; i++) {
879 				u64 ts;
880 				GF_FilterPacket *pck;
881 				RTStream *st = gf_list_get(ctx->streams, i);
882 				if (!st->in_eos)
883 					return;
884 
885 				pck = gf_list_last(st->pck_queue);
886 				if (!pck) continue;
887 				u32 dur = gf_filter_pck_get_duration(pck);
888 				if (!dur) dur=1;
889 				ts = gf_filter_pck_get_dts(pck);
890 				if (ts==GF_FILTER_NO_TS)
891 					ts = gf_filter_pck_get_cts(pck);
892 				ts += st->tk_delay;
893 				ts += dur;
894 				if (!min_ts || (ts * min_timescale > min_ts * st->timescale) ) {
895 					min_ts = ts;
896 					min_timescale = st->timescale;
897 				}
898 			}
899 		}
900 
901 		if (!min_ts) {
902 			//video not ready, need more input
903 			if (wait_for_sap)
904 				return;
905 			min_ts = min_ts_a;
906 			min_timescale = min_timescale_a;
907 		}
908 		if (!min_ts) {
909 			//other streams not ready, need more input
910 			if (nb_eos<count)
911 				return;
912 		} else {
913 			ctx->min_ts_scale = min_timescale;
914 			ctx->min_ts_computed = min_ts;
915 		}
916 	}
917 	//check all streams have reached min ts unless we are in final flush
918 	if (!flush_all) {
919 		for (i=0; i<count; i++) {
920 			u64 ts;
921 			GF_FilterPacket *pck;
922 			RTStream *st = gf_list_get(ctx->streams, i);
923 			if (st->range_start_computed==2) continue;
924 			if (st->resinsert_single_pck) continue;
925 			pck = gf_list_last(st->pck_queue);
926 			assert(pck);
927 			ts = gf_filter_pck_get_dts(pck);
928 			if (ts==GF_FILTER_NO_TS)
929 				ts = gf_filter_pck_get_cts(pck);
930 			ts += st->tk_delay;
931 
932 			if (ts * ctx->min_ts_scale < ctx->min_ts_computed * st->timescale) {
933 				return;
934 			}
935 		}
936 	}
937 
938 	//check condition
939 	if (ctx->extract_mode==EXTRACT_SIZE) {
940 		u32 nb_stop_at_min_ts = 0;
941 		u64 cumulated_size = 0;
942 		Bool use_prev = GF_FALSE;
943 		u32 nb_eos = 0;
944 
945 		//check all streams have reached min ts
946 		for (i=0; i<count; i++) {
947 			u32 j, nb_pck;
948 			Bool found=GF_FALSE;
949 			RTStream *st = gf_list_get(ctx->streams, i);
950 			nb_pck = gf_list_count(st->pck_queue);
951 
952 			for (j=0; j<nb_pck; j++) {
953 				u64 ts;
954 				u32 size;
955 				GF_FilterPacket *pck = gf_list_get(st->pck_queue, j);
956 
957 				ts = gf_filter_pck_get_dts(pck);
958 				if (ts==GF_FILTER_NO_TS)
959 					ts = gf_filter_pck_get_cts(pck);
960 				ts += st->tk_delay;
961 
962 				if (ts * ctx->min_ts_scale >= ctx->min_ts_computed * st->timescale) {
963 					nb_stop_at_min_ts ++;
964 					found = GF_TRUE;
965 					break;
966 				}
967 				gf_filter_pck_get_data(pck, &size);
968 				cumulated_size += size;
969 			}
970 			if ((j==nb_pck) && st->in_eos && !found) {
971 				nb_eos++;
972 			}
973 		}
974 		//some streams are not done and we have an estimated size less than target split
975 		if ((nb_eos < count ) && (cumulated_size < ctx->split_size)
976 			&& ctx->min_ts_scale
977 			//do this only if first time we estimate this chunk size, or if previous estimated min_ts is not the same as current min_ts
978 			&& (!ctx->prev_min_ts_computed || (ctx->prev_min_ts_computed < ctx->min_ts_computed))
979 		) {
980 			if ((nb_stop_at_min_ts + nb_eos) == count) {
981 				ctx->est_file_size = cumulated_size;
982 				ctx->prev_min_ts_computed = ctx->min_ts_computed;
983 				ctx->prev_min_ts_scale = ctx->min_ts_scale;
984 				ctx->min_ts_computed = 0;
985 				ctx->min_ts_scale = 0;
986 				ctx->gop_depth++;
987 			}
988 			return;
989 		}
990 		//decide which one we use
991 		if (ctx->xround==REFRAME_ROUND_BEFORE) {
992 			use_prev = GF_TRUE;
993 		} else if (ctx->xround==REFRAME_ROUND_AFTER) {
994 			use_prev = GF_FALSE;
995 		} else {
996 			s64 diff_prev = (s64) ctx->split_size;
997 			s64 diff_cur = (s64) ctx->split_size;
998 			diff_prev -= (s64) ctx->est_file_size;
999 			diff_cur -= (s64) cumulated_size;
1000 			if (ABS(diff_cur)<ABS(diff_prev))
1001 				use_prev = GF_FALSE;
1002 			else
1003 				use_prev = GF_TRUE;
1004 		}
1005 		if (!ctx->prev_min_ts_scale)
1006 			use_prev = GF_FALSE;
1007 
1008 		if (use_prev) {
1009 			//ctx->est_file_size = ctx->est_file_size;
1010 			ctx->min_ts_computed = ctx->prev_min_ts_computed;
1011 			ctx->min_ts_scale = ctx->prev_min_ts_scale;
1012 		} else {
1013 			ctx->est_file_size = cumulated_size;
1014 		}
1015 		GF_LOG(GF_LOG_INFO, GF_LOG_MEDIA, ("[Reframer] split computed using %s estimation of file size ("LLU")\n", use_prev ? "previous" : "current", ctx->est_file_size));
1016 		ctx->prev_min_ts_computed = 0;
1017 		ctx->prev_min_ts_scale = 0;
1018 	}
1019 
1020 	//good to go
1021 	ctx->in_range = GF_TRUE;
1022 	ctx->gop_depth = 0;
1023 	for (i=0; i<count; i++) {
1024 		u64 ts;
1025 		RTStream *st = gf_list_get(ctx->streams, i);
1026 		GF_FilterPacket *pck = gf_list_get(st->pck_queue, 0);
1027 		st->range_end_reached_ts = (ctx->min_ts_computed * st->timescale);
1028 		if (ctx->min_ts_scale)
1029 			st->range_end_reached_ts /= ctx->min_ts_scale;
1030 
1031 		st->range_end_reached_ts += 1;
1032 		st->first_pck_sent = GF_FALSE;
1033 		if (pck) {
1034 			ts = gf_filter_pck_get_dts(pck);
1035 			if (ts==GF_FILTER_NO_TS)
1036 				ts = gf_filter_pck_get_cts(pck);
1037 			ts += st->tk_delay;
1038 			st->ts_at_range_start_plus_one = ts + 1;
1039 		} else {
1040 			//this will be a eos signal
1041 			st->range_end_reached_ts = 0;
1042 			assert(st->range_start_computed==2);
1043 		}
1044 	}
1045 	ctx->cur_end.num = ctx->min_ts_computed;
1046 	ctx->cur_end.den = ctx->min_ts_scale;
1047 
1048 }
1049 
1050 
reframer_process(GF_Filter * filter)1051 GF_Err reframer_process(GF_Filter *filter)
1052 {
1053 	GF_ReframerCtx *ctx = gf_filter_get_udta(filter);
1054 	u32 i, nb_eos, nb_end_of_range, count = gf_filter_get_ipid_count(filter);
1055 
1056 	if (ctx->is_eos) {
1057 		return GF_EOS;
1058 	}
1059 	if (ctx->rt) {
1060 		ctx->reschedule_in = 0;
1061 		ctx->clock_val = gf_sys_clock_high_res();
1062 	}
1063 
1064 	/*active range, process as follows:
1065 		- if stream is marked as "start reached" or "end reached" do nothing
1066 		- queue up packets until we reach start range:
1067 		- if packet is in range:
1068 			- queue it (ref &nd detach from pid)
1069 			- if pck is SAP and first SAP after start and context is not yet marked "in range":
1070 				- check if we start from this SAP or from previous SAP (possibly before start) according to cround
1071 				- and mark stream as "start ready"
1072 				- if stream is video and xadjust is set, prevent all other stream processing
1073 		- if packet is out of range
1074 			- do NOT enqueue packet
1075 			- if stream was not marked as "start ready" (no SAP in active range), use previous SAP before start and mark as active
1076 			- mark as end of range reached
1077 			- if stream is video and xadjust is set, re-enable all other stream processing
1078 
1079 		Once all streams are marked as "start ready"
1080 			- compute min time at which we will adjust the start range for all streams
1081 			- purge all packets before this time
1082 			- mark global context as "in range"
1083 
1084 		The regular (non-range) process is then adjusted as follows:
1085 			- if context is "in range" get packet from internal queue
1086 			- if no more packets in internal queue, mark stream as "range done"
1087 
1088 		Once all streams are marked as "range done"
1089 			- adjust next_ts of each stream
1090 			- mark each stream as not "start ready" and not "range done"
1091 			- mark context as not "in range"
1092 			- load next range and let the algo loop
1093 	*/
1094 	if (ctx->range_type && (ctx->range_type!=RANGE_DONE)) {
1095 		u32 nb_start_range_reached = 0;
1096 		Bool check_split = GF_FALSE;
1097 		//init timing
1098 		if (!ctx->timing_initialized) {
1099 			if (!reframer_init_timing(filter, ctx)) return GF_OK;
1100 		}
1101 
1102 		//fetch input packets
1103 		for (i=0; i<count; i++) {
1104 			u64 ts;
1105 			u32 pck_in_range, dur;
1106 			Bool is_sap;
1107 			Bool drop_input = GF_TRUE;
1108 			GF_FilterPacket *pck;
1109 			GF_FilterPid *ipid = gf_filter_get_ipid(filter, i);
1110 			RTStream *st = gf_filter_pid_get_udta(ipid);
1111 
1112 			if (!st->is_playing) {
1113 				nb_start_range_reached++;
1114 				continue;
1115 			}
1116 
1117 			if (st->range_start_computed && !ctx->wait_video_range_adjust) {
1118 				nb_start_range_reached++;
1119 				continue;
1120 			}
1121 			//if eos is marked we are flushing so don't check range_end
1122 			if (!ctx->has_seen_eos && st->range_end_reached_ts) continue;
1123 
1124 			if (st->split_pck) {
1125 				pck = st->split_pck;
1126 				drop_input = GF_FALSE;
1127 			} else {
1128 				pck = gf_filter_pid_get_packet(ipid);
1129 			}
1130 			if (!pck) {
1131 				if (gf_filter_pid_is_eos(ipid)) {
1132 					//special case for PIDs with a single packet, we reinsert them at the begining of each extracted range
1133 					//this allows dealing with BIFS/OD/JPEG/PNG tracks
1134 					if (st->resinsert_single_pck) {
1135 						if (!ctx->in_range && !st->range_start_computed) {
1136 							st->range_start_computed = 3;
1137 							if (!gf_list_count(st->pck_queue)) {
1138 								pck = st->resinsert_single_pck;
1139 								gf_filter_pck_ref(&pck);
1140 								gf_list_add(st->pck_queue, pck);
1141 								if (!ctx->is_range_extraction) {
1142 									check_split = GF_TRUE;
1143 								}
1144 							}
1145 						}
1146 						if (st->range_start_computed) {
1147 							nb_start_range_reached++;
1148 						}
1149 						if (!ctx->is_range_extraction) {
1150 							st->in_eos = GF_TRUE;
1151 						}
1152 						continue;
1153 					}
1154 
1155 					if (!ctx->is_range_extraction) {
1156 						check_split = GF_TRUE;
1157 						st->in_eos = GF_TRUE;
1158 					} else {
1159 						st->range_start_computed = 2;
1160 						if (ctx->wait_video_range_adjust && ctx->xadjust && st->needs_adjust) {
1161 							ctx->wait_video_range_adjust = GF_FALSE;
1162 						}
1163 					}
1164 					//force flush in case of extract dur to avoid creating file with only a few samples of one track only
1165 					if (st->is_playing && (ctx->extract_mode==EXTRACT_DUR)) {
1166 						ctx->has_seen_eos = GF_TRUE;
1167 						ctx->in_range = 1;
1168 					}
1169 				}
1170 				continue;
1171 			}
1172 			st->nb_frames_range++;
1173 
1174 			ts = gf_filter_pck_get_dts(pck);
1175 			if (ts==GF_FILTER_NO_TS)
1176 				ts = gf_filter_pck_get_cts(pck);
1177 			ts += st->tk_delay;
1178 
1179 			//if nosap is set, consider all packet SAPs
1180 			is_sap = (ctx->nosap || ctx->raw || gf_filter_pck_get_sap(pck)) ? GF_TRUE : GF_FALSE;
1181 
1182 			if (!is_sap) {
1183 				if (st->all_saps) {
1184 					st->all_saps = GF_FALSE;
1185 					ctx->nb_non_saps++;
1186 					if (ctx->nb_non_saps>1) {
1187 						GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[Reframer] %d streams using predictive coding, results may be undefined or broken when aligning SAP, consider remuxing the source\n", ctx->nb_non_saps));
1188 					}
1189 
1190 					if (ctx->xadjust) {
1191 						st->needs_adjust = GF_TRUE;
1192 						if (st->range_start_computed==1) {
1193 							if (ctx->is_range_extraction) {
1194 								ctx->wait_video_range_adjust = GF_TRUE;
1195 							}
1196 						}
1197 					}
1198 				}
1199 			}
1200 
1201 			//SAP or size split, push packet in queue and ask for gop split check
1202 			if (!ctx->is_range_extraction) {
1203 				//add packet
1204 				gf_filter_pck_ref(&pck);
1205 				gf_filter_pid_drop_packet(st->ipid);
1206 				gf_list_add(st->pck_queue, pck);
1207 				check_split = GF_TRUE;
1208 				//keep ref to first packet until we see a second one
1209 				if (st->nb_frames_range==1) {
1210 					gf_filter_pck_ref(&pck);
1211 					st->resinsert_single_pck = pck;
1212 				} else if (st->resinsert_single_pck) {
1213 					gf_filter_pck_unref(st->resinsert_single_pck);
1214 					st->resinsert_single_pck = NULL;
1215 				}
1216 				continue;
1217 			}
1218 			dur = gf_filter_pck_get_duration(pck);
1219 
1220 			//dur split or range extraction but we wait for video end range to be adjusted, don't enqueue packet
1221 			if (ctx->wait_video_range_adjust && !st->needs_adjust)
1222 				continue;
1223 
1224 			//check if packet is in our range
1225 			pck_in_range = reframer_check_pck_range(ctx, st, ts, dur, st->nb_frames_range);
1226 
1227 
1228 			//SAP packet, decide if we cut here or at previous SAP
1229 			if (is_sap) {
1230 				//if streamtype is video or we have only one pid, purge all packets in all streams before this time
1231 				//
1232 				//for more complex cases we keep packets because we don't know if we will need SAP packets before the final
1233 				//decided start range
1234 				if (!pck_in_range && ((count==1) || !st->all_saps) ) {
1235 					reframer_purge_queues(ctx, ts, st->timescale);
1236 				}
1237 
1238 				//packet in range and global context not yet in range, mark which SAP will be the begining of our range
1239 				if (!ctx->in_range && (pck_in_range==1)) {
1240 					if (ctx->xround==REFRAME_ROUND_CLOSEST) {
1241 						Bool cur_closer = GF_FALSE;
1242 						//check which frame is closer
1243 						if (ctx->start_frame_idx_plus_one) {
1244 							s64 diff_prev = ctx->start_frame_idx_plus_one-1;
1245 							s64 diff_cur = ctx->start_frame_idx_plus_one-1;
1246 							diff_prev -= st->prev_sap_frame_idx;
1247 							diff_cur -= st->nb_frames_range;
1248 							if (ABS(diff_cur) < ABS(diff_prev)) cur_closer = GF_TRUE;
1249 						} else {
1250 							s64 diff_prev, diff_cur;
1251 							u64 start_range_ts = ctx->cur_start.num;
1252 							start_range_ts *= st->timescale;
1253 							start_range_ts /= ctx->cur_start.den;
1254 
1255 							diff_prev = diff_cur = start_range_ts;
1256 							diff_prev -= st->prev_sap_ts;
1257 							diff_cur -= ts;
1258 							if (ABS(diff_cur) < ABS(diff_prev)) cur_closer = GF_TRUE;
1259 						}
1260 						if (cur_closer) {
1261 							st->sap_ts_plus_one = ts+1;
1262 						} else {
1263 							st->sap_ts_plus_one = st->prev_sap_ts + 1;
1264 						}
1265 					} else if (ctx->xround==REFRAME_ROUND_BEFORE) {
1266 						st->sap_ts_plus_one = st->prev_sap_ts+1;
1267 					} else {
1268 						st->sap_ts_plus_one = ts+1;
1269 					}
1270 					st->range_start_computed = 1;
1271 					nb_start_range_reached++;
1272 				}
1273 				//remember prev sap time
1274 				if (pck_in_range!=2) {
1275 					st->prev_sap_ts = ts;
1276 					st->prev_sap_frame_idx = st->nb_frames_range;
1277 				}
1278 				//video stream start and xadjust set, prevent all other streams from being processed until wee determine the end of the video range
1279 				//and re-enable other streams processing
1280 				if (!ctx->wait_video_range_adjust && ctx->xadjust && st->needs_adjust) {
1281 					ctx->wait_video_range_adjust = GF_TRUE;
1282 				}
1283 			}
1284 
1285 			if ((ctx->extract_mode==EXTRACT_DUR) && ctx->has_seen_eos && (pck_in_range==2))
1286 				pck_in_range = 1;
1287 
1288 			//after range: whether SAP or not, mark end of range reached
1289 			if (pck_in_range==2) {
1290 				if (!ctx->xadjust || is_sap) {
1291 					Bool enqueue = GF_FALSE;
1292 					st->split_end = 0;
1293 					if (!st->range_start_computed) {
1294 						st->sap_ts_plus_one = st->prev_sap_ts + 1;
1295 						st->range_start_computed = 1;
1296 						nb_start_range_reached++;
1297 						if (st->prev_sap_ts == ts)
1298 							enqueue = GF_TRUE;
1299 					}
1300 					//remember the timestamp of first packet after range
1301 					st->range_end_reached_ts = ts + 1;
1302 
1303 					//time-based extraction or dur split, try to clone packet
1304 					if (st->can_split && !ctx->start_frame_idx_plus_one) {
1305 						if ((s64) (ts * ctx->cur_end.den) < ctx->cur_end.num * st->timescale) {
1306 							//force enqueing this packet
1307 							enqueue = GF_TRUE;
1308 							st->split_end = (u32) ( (ctx->cur_end.num * st->timescale) / ctx->cur_end.den - ts);
1309 							st->range_end_reached_ts += st->split_end;
1310 							//and remember it for next chunk - note that we dequeue the input to get proper eos notification
1311 							gf_filter_pck_ref(&pck);
1312 							st->split_pck = pck;
1313 						}
1314 					}
1315 					//video stream end detected and xadjust set, adjust cur_end to match the video stream end range
1316 					//and re-enable other streams processing
1317 					if (ctx->wait_video_range_adjust && ctx->xadjust && st->needs_adjust) {
1318 						ctx->cur_end.num = st->range_end_reached_ts-1;
1319 						ctx->cur_end.den = st->timescale;
1320 						ctx->wait_video_range_adjust = GF_FALSE;
1321 					}
1322 
1323 					//do NOT enqueue packet
1324 					if (!enqueue)
1325 						break;
1326 				}
1327 			}
1328 
1329 			//add packet
1330 			gf_filter_pck_ref(&pck);
1331 			gf_list_add(st->pck_queue, pck);
1332 			if (drop_input) {
1333 				gf_filter_pid_drop_packet(st->ipid);
1334 				//keep ref to first packet until we see a second one
1335 				if (st->nb_frames_range==1) {
1336 					gf_filter_pck_ref(&pck);
1337 					st->resinsert_single_pck = pck;
1338 				} else if (st->resinsert_single_pck) {
1339 					gf_filter_pck_unref(st->resinsert_single_pck);
1340 					st->resinsert_single_pck = NULL;
1341 				}
1342 			} else {
1343 				assert(pck == st->split_pck);
1344 				gf_filter_pck_unref(st->split_pck);
1345 				st->split_pck = NULL;
1346 			}
1347 		}
1348 
1349 		if (check_split) {
1350 			check_gop_split(ctx);
1351 		}
1352 
1353 		//all streams reached the start range, compute min ts
1354 		if (!ctx->in_range
1355 			&& (nb_start_range_reached==count)
1356 			&& ctx->is_range_extraction
1357 		) {
1358 			u64 min_ts = 0;
1359 			u32 min_timescale=0;
1360 			u64 min_ts_a = 0;
1361 			u32 min_timescale_a=0;
1362 			u64 min_ts_split = 0;
1363 			u32 min_timescale_split=0;
1364 			Bool purge_all = GF_FALSE;
1365 			for (i=0; i<count; i++) {
1366 				GF_FilterPid *ipid = gf_filter_get_ipid(filter, i);
1367 				RTStream *st = gf_filter_pid_get_udta(ipid);
1368 				if (!st->is_playing) continue;
1369 				assert(st->range_start_computed);
1370 				//eos
1371 				if (st->range_start_computed==2) {
1372 					continue;
1373 				}
1374 				//packet will be reinserted at cut time, do not check its timestamp
1375 				if (st->range_start_computed==3)
1376 					continue;
1377 
1378 				if (st->can_split) {
1379 					if (!min_ts_split || ((st->sap_ts_plus_one-1) * min_timescale_split < min_ts_split * st->timescale) ) {
1380 						min_ts_split = st->sap_ts_plus_one;
1381 						min_timescale_split = st->timescale;
1382 					}
1383 				}
1384 				else if (st->all_saps) {
1385 					if (!min_ts_a || ((st->sap_ts_plus_one-1) * min_timescale_a < min_ts_a * st->timescale) ) {
1386 						min_ts_a = st->sap_ts_plus_one;
1387 						min_timescale_a = st->timescale;
1388 					}
1389 				} else {
1390 					if (!min_ts || ((st->sap_ts_plus_one-1) * min_timescale < min_ts * st->timescale) ) {
1391 						min_ts = st->sap_ts_plus_one;
1392 						min_timescale = st->timescale;
1393 					}
1394 				}
1395 			}
1396 			if (!min_ts) {
1397 				min_ts = min_ts_a;
1398 				min_timescale = min_timescale_a;
1399 				if (!min_ts && min_ts_split) {
1400 					if (ctx->start_frame_idx_plus_one) {
1401 						min_ts = min_ts_split;
1402 						min_timescale = min_timescale_split;
1403 					} else {
1404 						min_ts = ctx->cur_start.num+1;
1405 						min_timescale = (u32) ctx->cur_start.den;
1406 					}
1407 				}
1408 			}
1409 			if (!min_ts) {
1410 				purge_all = GF_TRUE;
1411 				if (ctx->extract_mode==EXTRACT_RANGE) {
1412 					GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[Reframer] All streams in end of stream for desired start range "LLD"/"LLU"\n", ctx->cur_start.num, ctx->cur_start.den));
1413 				}
1414 				ctx->is_eos = GF_TRUE;
1415 			} else {
1416 				min_ts -= 1;
1417 			}
1418 			//purge everything before min ts
1419 			for (i=0; i<count; i++) {
1420 				GF_FilterPid *ipid = gf_filter_get_ipid(filter, i);
1421 				RTStream *st = gf_filter_pid_get_udta(ipid);
1422 
1423 				while (gf_list_count(st->pck_queue)) {
1424 					GF_FilterPacket *pck = gf_list_get(st->pck_queue, 0);
1425 					if (!purge_all) {
1426 						u32 is_start = 0;
1427 						u64 ts, ots;
1428 						u64 dur;
1429 						ts = gf_filter_pck_get_dts(pck);
1430 						if (ts==GF_FILTER_NO_TS)
1431 							ts = gf_filter_pck_get_cts(pck);
1432 						ts += st->tk_delay;
1433 						dur = (u64) gf_filter_pck_get_duration(pck);
1434 						if (!dur) dur=1;
1435 						ots = ts;
1436 						if (min_timescale != st->timescale) {
1437 							ts *= min_timescale;
1438 							ts /= st->timescale;
1439 							dur *= min_timescale;
1440 							dur /= st->timescale;
1441 						}
1442 
1443 						if (ts >= min_ts) {
1444 							is_start = 1;
1445 						}
1446 						else if (st->can_split && (ts+dur >= min_ts)) {
1447 							is_start = 2;
1448 						}
1449 						else if (st->range_start_computed==3) {
1450 							is_start = 1;
1451 						}
1452 
1453 						if (is_start) {
1454 							//remember TS at range start
1455 							s64 orig = min_ts;
1456 							if (st->timescale != min_timescale) {
1457 								orig *= st->timescale;
1458 								orig /= min_timescale;
1459 							}
1460 							st->split_start = 0;
1461 							if (is_start==2) {
1462 								st->split_start = (u32) (min_ts - ts);
1463 								if (min_timescale != st->timescale) {
1464 									st->split_start *= st->timescale;
1465 									st->split_start /= min_timescale;
1466 								}
1467 							}
1468 							st->ts_at_range_start_plus_one = ots + 1;
1469 
1470 							if ((st->range_start_computed==1)
1471 								&& (orig < (s64) ots)
1472 								&& ctx->splitrange
1473 								&& (ctx->cur_range_idx>1)
1474 							) {
1475 								s32 delay = (s32) ((s64) ots - (s64) orig);
1476 								gf_filter_pid_set_property(st->opid, GF_PROP_PID_DELAY, &PROP_SINT(delay) );
1477 							}
1478 							break;
1479 						}
1480 					}
1481 					gf_list_rem(st->pck_queue, 0);
1482 					gf_filter_pck_unref(pck);
1483 					st->nb_frames++;
1484 				}
1485 				//reset start range computed
1486 				st->range_start_computed = 0;
1487 				if (ctx->extract_mode==EXTRACT_DUR) {
1488 					st->first_pck_sent = GF_FALSE;
1489 				} else {
1490 					st->first_pck_sent = ctx->splitrange ? GF_FALSE : GF_TRUE;
1491 				}
1492 
1493 				if (purge_all && (ctx->extract_mode!=EXTRACT_RANGE)) {
1494 					gf_filter_pid_get_packet(st->ipid);
1495 					gf_filter_pid_set_eos(st->opid);
1496 				}
1497 			}
1498 			if (purge_all) {
1499 				if (ctx->extract_mode!=EXTRACT_RANGE)
1500 					return GF_EOS;
1501 
1502 				goto load_next_range;
1503 			}
1504 
1505 			//we are in the range
1506 			ctx->in_range = GF_TRUE;
1507 		}
1508 		if (!ctx->in_range)
1509 			return GF_OK;
1510 	}
1511 
1512 	nb_eos = 0;
1513 	nb_end_of_range = 0;
1514 	for (i=0; i<count; i++) {
1515 		GF_FilterPid *ipid = gf_filter_get_ipid(filter, i);
1516 		RTStream *st = gf_filter_pid_get_udta(ipid);
1517 
1518 		while (1) {
1519 			Bool forward = GF_TRUE;
1520 			Bool pck_is_ref = GF_FALSE;
1521 			GF_FilterPacket *pck;
1522 
1523 			//dequeue packet
1524 			if (ctx->range_type && (ctx->range_type!=RANGE_DONE) ) {
1525 				pck = gf_list_get(st->pck_queue, 0);
1526 				pck_is_ref = GF_TRUE;
1527 
1528 				if (pck && !ctx->is_range_extraction && st->range_end_reached_ts) {
1529 					u64 ts;
1530 					ts = gf_filter_pck_get_dts(pck);
1531 					if (ts==GF_FILTER_NO_TS)
1532 						ts = gf_filter_pck_get_cts(pck);
1533 					ts += st->tk_delay;
1534 					if (ts>= st->range_end_reached_ts-1) {
1535 						nb_end_of_range++;
1536 						break;
1537 					}
1538 				}
1539 
1540 			} else {
1541 				pck = gf_filter_pid_get_packet(ipid);
1542 			}
1543 
1544 			if (!pck) {
1545 				if (st->range_end_reached_ts) {
1546 					nb_end_of_range++;
1547 					break;
1548 				}
1549 
1550 				if (!st->is_playing) {
1551 					nb_eos++;
1552 				} else {
1553 					//force a eos check if this was a split pid
1554 					if (st->can_split)
1555 						gf_filter_pid_get_packet(st->ipid);
1556 
1557 					if (gf_filter_pid_is_eos(ipid)) {
1558 						gf_filter_pid_set_eos(st->opid);
1559 						nb_eos++;
1560 					}
1561 				}
1562 				break;
1563 			}
1564 
1565 			if (ctx->refs) {
1566 				u8 deps = gf_filter_pck_get_dependency_flags(pck);
1567 				deps >>= 2;
1568 				deps &= 0x3;
1569 				//not used as reference, don't forward
1570 				if (deps==2)
1571 					forward = GF_FALSE;
1572 			}
1573 			if (ctx->saps.nb_items) {
1574 				u32 sap = gf_filter_pck_get_sap(pck);
1575 				switch (sap) {
1576 				case GF_FILTER_SAP_1:
1577 					if (!ctx->filter_sap1) forward = GF_FALSE;
1578 					break;
1579 				case GF_FILTER_SAP_2:
1580 					if (!ctx->filter_sap2) forward = GF_FALSE;
1581 					break;
1582 				case GF_FILTER_SAP_3:
1583 					if (!ctx->filter_sap3) forward = GF_FALSE;
1584 					break;
1585 				case GF_FILTER_SAP_4:
1586 					if (!ctx->filter_sap4) forward = GF_FALSE;
1587 					break;
1588 				default:
1589 					if (!ctx->filter_sap_none) forward = GF_FALSE;
1590 					break;
1591 				}
1592 			}
1593 			if (ctx->range_type==RANGE_DONE)
1594 				forward = GF_FALSE;
1595 
1596 			if (!forward) {
1597 				reframer_drop_packet(ctx, st, pck, pck_is_ref);
1598 				st->nb_frames++;
1599 				continue;
1600 			}
1601 
1602 			if (! reframer_send_packet(filter, ctx, st, pck, pck_is_ref))
1603 				break;
1604 
1605 		}
1606 	}
1607 
1608 	//end of range
1609 	if (nb_end_of_range + nb_eos == count) {
1610 load_next_range:
1611 		nb_end_of_range = 0;
1612 		nb_eos=0;
1613 		for (i=0; i<count; i++) {
1614 			GF_FilterPid *ipid = gf_filter_get_ipid(filter, i);
1615 			RTStream *st = gf_filter_pid_get_udta(ipid);
1616 			//we reinsert the same PCK, so the ts_at_range_start_plus is always the packet cts
1617 			//we therefore need to compute the ts at and as the target end time minus the target start time
1618 			if (st->resinsert_single_pck) {
1619 				u64 start = ctx->cur_start.num;
1620 				start *= st->timescale;
1621 				start /= ctx->cur_start.den;
1622 				st->ts_at_range_end = ctx->cur_end.num;
1623 				st->ts_at_range_end *= st->timescale;
1624 				st->ts_at_range_end /= ctx->cur_end.den;
1625 				st->ts_at_range_end -= start;
1626 			} else {
1627 				st->ts_at_range_end = (st->range_end_reached_ts - 1)  - (st->ts_at_range_start_plus_one - 1);
1628 			}
1629 			st->ts_at_range_start_plus_one = 0;
1630 			st->range_end_reached_ts = 0;
1631 			st->range_start_computed = 0;
1632 			if (st->in_eos) {
1633 				if (gf_list_count(st->pck_queue)) {
1634 					nb_end_of_range++;
1635 				} else {
1636 					gf_filter_pid_set_eos(st->opid);
1637 					nb_eos++;
1638 				}
1639 			} else if (st->split_pck) {
1640 				nb_end_of_range++;
1641 			}
1642 		}
1643 		//and load next range
1644 		ctx->in_range = GF_FALSE;
1645 		reframer_load_range(ctx);
1646 		if (nb_end_of_range)
1647 			gf_filter_post_process_task(filter);
1648 	}
1649 
1650 	if (nb_eos==count) return GF_EOS;
1651 
1652 	if (ctx->rt) {
1653 		if (ctx->reschedule_in>2000) {
1654 			gf_filter_ask_rt_reschedule(filter, (u32) (ctx->reschedule_in - 2000));
1655 		} else if (ctx->reschedule_in>1000) {
1656 			gf_filter_ask_rt_reschedule(filter, (u32) (ctx->reschedule_in / 2));
1657 		}
1658 	}
1659 
1660 	return GF_OK;
1661 }
1662 
1663 static const GF_FilterCapability ReframerRAWCaps[] =
1664 {
1665 	CAP_UINT(GF_CAPS_INPUT_OUTPUT,  GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
1666 	CAP_UINT(GF_CAPS_INPUT_OUTPUT,  GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
1667 	CAP_UINT(GF_CAPS_INPUT_OUTPUT,  GF_PROP_PID_CODECID, GF_CODECID_RAW)
1668 };
1669 
reframer_initialize(GF_Filter * filter)1670 static GF_Err reframer_initialize(GF_Filter *filter)
1671 {
1672 	GF_ReframerCtx *ctx = gf_filter_get_udta(filter);
1673 
1674 	ctx->streams = gf_list_new();
1675 	ctx->seekable = GF_TRUE;
1676 	reframer_load_range(ctx);
1677 
1678 	if (ctx->raw) {
1679 		gf_filter_override_caps(filter, ReframerRAWCaps, sizeof(ReframerRAWCaps) / sizeof(GF_FilterCapability) );
1680 	}
1681 
1682 #ifdef GPAC_ENABLE_COVERAGE
1683 	if (gf_sys_is_cov_mode()) {
1684 		reframer_update_ts_shift(ctx, -1, 1000);
1685 	}
1686 #endif
1687 	return GF_OK;
1688 }
1689 
reframer_process_event(GF_Filter * filter,const GF_FilterEvent * evt)1690 static Bool reframer_process_event(GF_Filter *filter, const GF_FilterEvent *evt)
1691 {
1692 	GF_ReframerCtx *ctx = gf_filter_get_udta(filter);
1693 	GF_FilterEvent fevt;
1694 	RTStream *st;
1695 	if (!evt->base.on_pid) return GF_FALSE;
1696 	st = gf_filter_pid_get_udta(evt->base.on_pid);
1697 	if (!st) return GF_TRUE;
1698 	//if we have a PID, we always cancel the event and forward the same event to the associated input pid
1699 	fevt = *evt;
1700 	fevt.base.on_pid = st->ipid;
1701 
1702 	//if range extraction based on time, adjust start range
1703 	if (evt->base.type==GF_FEVT_PLAY) {
1704 		if (ctx->range_type && !ctx->start_frame_idx_plus_one) {
1705 			Double start_range = (Double) ctx->cur_start.num;
1706 			start_range /= ctx->cur_start.den;
1707 			//rewind safety offset
1708 			if (start_range > ctx->seeksafe)
1709 				start_range -= ctx->seeksafe;
1710 			else
1711 				start_range = 0.0;
1712 
1713 			fevt.play.start_range = start_range;
1714 		}
1715 		st->in_eos = GF_FALSE;
1716 		st->is_playing = GF_TRUE;
1717 		ctx->is_eos = GF_FALSE;
1718 	} else if (evt->base.type==GF_FEVT_STOP) {
1719 		st->is_playing = GF_FALSE;
1720 	}
1721 
1722 	gf_filter_pid_send_event(st->ipid, &fevt);
1723 	return GF_TRUE;
1724 }
1725 
reframer_finalize(GF_Filter * filter)1726 static void reframer_finalize(GF_Filter *filter)
1727 {
1728 	GF_ReframerCtx *ctx = gf_filter_get_udta(filter);
1729 
1730 	while (gf_list_count(ctx->streams)) {
1731 		RTStream *st = gf_list_pop_back(ctx->streams);
1732 		reframer_reset_stream(ctx, st);
1733 	}
1734 	gf_list_del(ctx->streams);
1735 }
1736 
1737 static const GF_FilterCapability ReframerCaps[] =
1738 {
1739 	CAP_UINT(GF_CAPS_INPUT_EXCLUDED,  GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
1740 	//we do accept everything, including raw streams
1741 	CAP_UINT(GF_CAPS_INPUT_EXCLUDED,  GF_PROP_PID_CODECID, GF_CODECID_NONE),
1742 	CAP_UINT(GF_CAPS_INPUT_EXCLUDED,  GF_PROP_PID_UNFRAMED, GF_TRUE),
1743 	//we don't accept files as input so don't output them
1744 	CAP_UINT(GF_CAPS_OUTPUT_EXCLUDED, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
1745 	//we don't produce RAW streams during dynamic chain resolution - this will avoid loading the filter for compositor/other raw access
1746 	CAP_UINT(GF_CAPS_OUTPUT_EXCLUDED, GF_PROP_PID_CODECID, GF_CODECID_RAW),
1747 	//but we may produce raw streams when filter is explicitly loaded (media exporter)
1748 	CAP_UINT(GF_CAPS_OUTPUT_LOADED_FILTER, GF_PROP_PID_CODECID, GF_CODECID_RAW)
1749 };
1750 
1751 
1752 #define OFFS(_n)	#_n, offsetof(GF_ReframerCtx, _n)
1753 static const GF_FilterArgs ReframerArgs[] =
1754 {
1755 	{ OFFS(exporter), "compatibility with old exporter, displays export results", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
1756 	{ OFFS(rt), "real-time regulation mode of input\n"
1757 	"- off: disables real-time regulation\n"
1758 	"- on: enables real-time regulation, one clock per pid\n"
1759 	"- sync: enables real-time regulation one clock for all pids", GF_PROP_UINT, "off", "off|on|sync", GF_FS_ARG_HINT_NORMAL},
1760 	{ OFFS(saps), "drop non-SAP packets, off by default. The list gives the SAP types (0,1,2,3,4) to forward. Note that forwarding only sap 0 will break the decoding", GF_PROP_UINT_LIST, NULL, "0|1|2|3|4", GF_FS_ARG_HINT_NORMAL},
1761 	{ OFFS(refs), "forward only frames used as reference frames, if indicated in the input stream", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_NORMAL},
1762 	{ OFFS(speed), "speed for real-time regulation mode - only positive value", GF_PROP_DOUBLE, "1.0", NULL, GF_FS_ARG_HINT_ADVANCED},
1763 	{ OFFS(raw), "force input streams to be in raw format (i.e. forces decoding of input)", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_NORMAL},
1764 	{ OFFS(frames), "drop all except listed frames (first being 1), off by default", GF_PROP_UINT_LIST, NULL, NULL, GF_FS_ARG_HINT_ADVANCED},
1765 	{ OFFS(xs), "extraction start time(s), see filter help", GF_PROP_STRING_LIST, NULL, NULL, GF_FS_ARG_HINT_NORMAL},
1766 	{ OFFS(xe), "extraction end time(s). If less values than start times, the last time interval extracted is an open range", GF_PROP_STRING_LIST, NULL, NULL, GF_FS_ARG_HINT_NORMAL},
1767 	{ OFFS(xround), "adjustment of extraction start range I-frame\n"
1768 	"- before: use first I-frame preceeding or equal to start range\n"
1769 	"- after: use first I-frame (if any) following or equal to start range\n"
1770 	"- closest: use I-frame closest to start range", GF_PROP_UINT, "before", "before|after|closest", GF_FS_ARG_HINT_ADVANCED},
1771 	{ OFFS(xadjust), "adjust end time of extraction range to be before next I-frame", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
1772 	{ OFFS(nosap), "do not cut at SAP when extracting range (may result in broken streams)", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
1773 	{ OFFS(splitrange), "signal file boundary at each extraction first packet for template-base file generation", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
1774 	{ OFFS(seeksafe), "rewind play requests by given seconds (to make sur I-frame preceeding start is catched)", GF_PROP_DOUBLE, "10.0", NULL, GF_FS_ARG_HINT_EXPERT},
1775 	{0}
1776 };
1777 
1778 GF_FilterRegister ReframerRegister = {
1779 	.name = "reframer",
1780 	GF_FS_SET_DESCRIPTION("Media Reframer")
1781 	GF_FS_SET_HELP("This filter provides various compressed domain tools on inputs:\n"
1782 		"- ensure reframing\n"
1783 		"- optionally force decoding\n"
1784 		"- real-time regulation\n"
1785 		"- packet filtering based on SAP types or frame numbers\n"
1786 		"- time-range extraction and spliting\n"
1787 		"This filter forces input pids to be properly framed (1 packet = 1 Access Unit).\n"
1788 		"It is typcially needed to force remultiplexing in file to file operations when source and destination files use the same format.\n"
1789 		"  \n"
1790 		"# SAP filtering\n"
1791 		"The filter can remove packets based on their SAP types using [-saps]() option.\n"
1792 		"For example, this can be used to extract only the key frame (SAP 1,2,3) of a video to create a trick mode version.\n"
1793 		"  \n"
1794 		"# Frame filtering\n"
1795 		"This filter can keep only specific Access Units of the source using [-frames]() option.\n"
1796 		"For example, this can be used to extract only specific key frame of a video to create a HEIF collection.\n"
1797 		"  \n"
1798 		"# Frame decoding\n"
1799 		"This filter can force input media streams to be decoded using the [-raw]() option.\n"
1800 		"EX gpac src=m.mp4 reframer:raw @ [dst]\n"
1801 		"# Real-time Regulation\n"
1802 		"The filter can perform real-time regulation of input packets, based on their timescale and timestamps.\n"
1803 		"For example to simulate a live DASH:\n"
1804 		"EX gpac src=m.mp4 reframer:rt=on @ dst=live.mpd:dynamic\n"
1805 		"  \n"
1806 		"# Range extraction\n"
1807 		"The filter can perform time range extraction of the source using [-xs]() and [-xe]() options.\n"
1808 		"The formats allowed for times specifiers are:\n"
1809 		"- 'T'H:M:S: specify time in hours, minutes, seconds\n"
1810 		"- 'T'H:M:S.MS: specify time in hours, minutes, seconds and milliseconds\n"
1811 		"- INT: specify time in millisecond\n"
1812 		"- NUM/DEN: specify time in seconds as fraction\n"
1813 		"- FNUM: specify time as frame number\n"
1814 		"In this mode, the timestamps are rewritten to form a continuous timeline.\n"
1815 		"When multiple ranges are given, the filter will try to seek if supported by source."
1816 		"\n"
1817 		"EX gpac src=m.mp4 reframer:xs=T00:00:10,T00:01:10,T00:02:00:xs=T00:00:20,T00:01:20 [dst]\n"
1818 		"This will extract the time ranges [10s,20s], [1m10s,1m20s] and all media starting from 2m\n"
1819 		"\n"
1820 		"It is possible to signal range boundaries in output packets using [-splitrange]().\n"
1821 		"This will expose on the first packet of each range in each pid the following properties:\n"
1822 		"- FileNumber: starting at 1 for the first range, to be used as replacement for $num$ in templates\n"
1823 		"- FileSuffix: corresponding to `StartRange_EndRange` or `StartRange` for open ranges, to be used as replacement for $FS$ in templates\n"
1824 		"\n"
1825 		"EX gpac src=m.mp4 reframer:xs=T00:00:10,T00:01:10:xe=T00:00:20:splitrange -o dump_$FS$.264\n"
1826 		"This will create two output files dump_T00.00.10_T00.02.00.264 and dump_T00.01.10.264.\n"
1827 		"Note: The `:` and `/` characters are replaced by `.` in `FileSuffix` property.\n"
1828 		"\n"
1829 		"# Other split actions\n"
1830 		"The filter can perform splitting of the source using [-xs]() option.\n"
1831 		"The additional formats allowed for [-xs]() option are:\n"
1832 		"- 'SAP': split source at each SAP/RAP\n"
1833 		"- 'D'VAL: split source by chunks of VAL ms\n"
1834 		"- 'D'NUM/DEN: split source by chunks of NUM/DEN seconds\n"
1835 		"- 'S'VAL: split source by chunks of estimated size VAL bytes, VAL can use property multipliers\n"
1836 		"\n"
1837 		"Note: In these modes, [-splitrange]() and [-xadjust]() are implicitly set.\n"
1838 	)
1839 	.private_size = sizeof(GF_ReframerCtx),
1840 	.max_extra_pids = (u32) -1,
1841 	.args = ReframerArgs,
1842 	//reframer is explicit only, so we don't load the reframer during resolution process
1843 	.flags = GF_FS_REG_EXPLICIT_ONLY,
1844 	SETCAPS(ReframerCaps),
1845 	.initialize = reframer_initialize,
1846 	.finalize = reframer_finalize,
1847 	.configure_pid = reframer_configure_pid,
1848 	.process = reframer_process,
1849 	.process_event = reframer_process_event,
1850 };
1851 
1852 
reframer_register(GF_FilterSession * session)1853 const GF_FilterRegister *reframer_register(GF_FilterSession *session)
1854 {
1855 	return &ReframerRegister;
1856 }
1857