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