Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2017-2021
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_SEEK,
41 : REFRAME_ROUND_AFTER,
42 : REFRAME_ROUND_CLOSEST,
43 : };
44 :
45 : enum
46 : {
47 : RANGE_NONE=0,
48 : RANGE_CLOSED,
49 : RANGE_OPEN,
50 : RANGE_DONE
51 : };
52 :
53 : enum
54 : {
55 : EXTRACT_NONE=0,
56 : EXTRACT_RANGE,
57 : EXTRACT_SAP,
58 : EXTRACT_SIZE,
59 : EXTRACT_DUR,
60 : };
61 :
62 : enum
63 : {
64 : RAW_AV=0,
65 : RAW_AUDIO,
66 : RAW_VIDEO,
67 : RAW_NONE,
68 : };
69 :
70 : #define RT_PRECISION_US 2000
71 :
72 : typedef struct
73 : {
74 : GF_FilterPid *ipid, *opid;
75 : u32 timescale;
76 : u64 cts_us_at_init;
77 : u64 sys_clock_at_init;
78 : u32 nb_frames;
79 : Bool can_split;
80 : Bool all_saps;
81 : Bool needs_adjust;
82 : Bool use_blocking_refs;
83 :
84 : u64 ts_at_range_start_plus_one;
85 : u64 ts_at_range_end;
86 :
87 : GF_List *pck_queue;
88 : //0: not computed, 1: computed and valid TS, 2: end of stream on pid
89 : u32 range_start_computed;
90 : u64 range_end_reached_ts;
91 : u64 prev_sap_ts;
92 : u32 prev_sap_frame_idx;
93 : u32 nb_frames_range;
94 : u64 sap_ts_plus_one;
95 : Bool first_pck_sent;
96 :
97 : //only positive delay here
98 : u64 tk_delay;
99 : //only media skip (video, audio priming)
100 : u32 ts_sub;
101 : Bool in_eos;
102 : u32 split_start;
103 : u32 split_end;
104 :
105 : GF_FilterPacket *split_pck;
106 : GF_FilterPacket *reinsert_single_pck;
107 : Bool is_playing;
108 :
109 : Bool is_raw;
110 : u32 codec_id, stream_type;
111 : u32 nb_ch, sample_rate, abps;
112 : Bool audio_planar;
113 : //for uncompressed audio, number of audio samples to keep from split frame / trash from next split frame
114 : //for seek mode, duration of media to keep from split frame / trash from next split frame
115 : u32 audio_samples_to_keep;
116 :
117 : u32 nb_frames_until_start;
118 :
119 : Bool seek_mode;
120 : u64 probe_ref_frame_ts;
121 :
122 : } RTStream;
123 :
124 : typedef struct
125 : {
126 : //args
127 : Bool exporter;
128 : GF_PropUIntList saps;
129 : GF_PropUIntList frames;
130 : Bool refs;
131 : u32 rt;
132 : Double speed;
133 : u32 raw;
134 : GF_PropStringList xs, xe;
135 : Bool nosap, splitrange, xadjust, tcmdrw, no_audio_seek, probe_ref;
136 : u32 xround;
137 : Double seeksafe;
138 : GF_PropStringList props;
139 :
140 : //internal
141 : Bool filter_sap1;
142 : Bool filter_sap2;
143 : Bool filter_sap3;
144 : Bool filter_sap4;
145 : Bool filter_sap_none;
146 :
147 : GF_List *streams;
148 : RTStream *clock;
149 :
150 : u64 reschedule_in;
151 : u64 clock_val;
152 :
153 : u32 range_type;
154 : u32 cur_range_idx;
155 : GF_Fraction64 cur_start, cur_end;
156 : u64 start_frame_idx_plus_one, end_frame_idx_plus_one;
157 :
158 : Bool in_range;
159 :
160 : Bool seekable;
161 :
162 : GF_Fraction64 extract_dur;
163 : u32 extract_mode;
164 : Bool is_range_extraction;
165 : u32 file_idx;
166 :
167 : u64 min_ts_computed;
168 : u32 min_ts_scale;
169 : u64 split_size;
170 : u64 est_file_size;
171 : u64 prev_min_ts_computed;
172 : u32 prev_min_ts_scale;
173 : u32 gop_depth;
174 :
175 : u32 wait_video_range_adjust;
176 : Bool has_seen_eos;
177 : u32 eos_state;
178 : u32 nb_non_saps;
179 :
180 : u32 nb_video_frames_since_start_at_range_start;
181 : u32 nb_video_frames_since_start;
182 : } GF_ReframerCtx;
183 :
184 188 : static void reframer_reset_stream(GF_ReframerCtx *ctx, RTStream *st)
185 : {
186 188 : if (st->pck_queue) {
187 619 : while (gf_list_count(st->pck_queue)) {
188 431 : GF_FilterPacket *pck = gf_list_pop_front(st->pck_queue);
189 431 : gf_filter_pck_unref(pck);
190 : }
191 188 : gf_list_del(st->pck_queue);
192 : }
193 188 : if (st->split_pck) gf_filter_pck_unref(st->split_pck);
194 188 : if (st->reinsert_single_pck) gf_filter_pck_unref(st->reinsert_single_pck);
195 188 : gf_free(st);
196 188 : }
197 :
198 194 : static void reframer_push_props(GF_ReframerCtx *ctx, RTStream *st)
199 : {
200 194 : gf_filter_pid_reset_properties(st->opid);
201 194 : gf_filter_pid_copy_properties(st->opid, st->ipid);
202 : //if range processing, we drop frames not in the target playback range so do not forward delay
203 194 : if (ctx->range_type && (st->tk_delay>0)) {
204 0 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_DELAY, NULL);
205 : }
206 194 : if (ctx->filter_sap1 || ctx->filter_sap2)
207 4 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_HAS_SYNC, &PROP_BOOL(GF_FALSE)); //false: all samples are sync
208 :
209 : //seek mode, signal we have sample-accurate seek info for the pid
210 194 : if (st->seek_mode)
211 6 : gf_filter_pid_set_property(st->opid, GF_PROP_PCK_SKIP_BEGIN, &PROP_UINT(1));
212 194 : }
213 :
214 195 : GF_Err reframer_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
215 : {
216 : u32 i;
217 : const GF_PropertyValue *p;
218 195 : GF_ReframerCtx *ctx = gf_filter_get_udta(filter);
219 195 : RTStream *st = gf_filter_pid_get_udta(pid);
220 :
221 195 : if (is_remove) {
222 1 : if (st) {
223 1 : if (st->opid)
224 1 : gf_filter_pid_remove(st->opid);
225 1 : gf_list_del_item(ctx->streams, st);
226 1 : reframer_reset_stream(ctx, st);
227 : }
228 : return GF_OK;
229 : }
230 194 : if (! gf_filter_pid_check_caps(pid))
231 : return GF_NOT_SUPPORTED;
232 :
233 194 : if (!st) {
234 188 : GF_SAFEALLOC(st, RTStream);
235 188 : if (!st) return GF_OUT_OF_MEM;
236 :
237 188 : gf_list_add(ctx->streams, st);
238 188 : st->opid = gf_filter_pid_new(filter);
239 188 : gf_filter_pid_set_udta(pid, st);
240 188 : gf_filter_pid_set_udta(st->opid, st);
241 188 : st->ipid = pid;
242 188 : st->pck_queue = gf_list_new();
243 188 : st->all_saps = GF_TRUE;
244 : }
245 :
246 194 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_TIMESCALE);
247 194 : if (p) st->timescale = p->value.uint;
248 0 : else st->timescale = 1000;
249 :
250 194 : if (!st->all_saps) {
251 0 : ctx->nb_non_saps--;
252 0 : st->all_saps = GF_TRUE;
253 : }
254 194 : st->can_split = GF_FALSE;
255 194 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_STREAM_TYPE);
256 194 : st->stream_type = p ? p->value.uint : 0;
257 194 : switch (st->stream_type) {
258 51 : case GF_STREAM_TEXT:
259 : case GF_STREAM_METADATA:
260 51 : st->can_split = GF_TRUE;
261 51 : break;
262 : }
263 :
264 194 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_CODECID);
265 194 : st->codec_id = p ? p->value.uint : 0;
266 194 : st->nb_ch = st->abps = st->sample_rate = 0;
267 194 : st->audio_planar = GF_FALSE;
268 194 : st->is_raw = (st->codec_id==GF_CODECID_RAW) ? GF_TRUE : GF_FALSE;
269 :
270 194 : if (st->is_raw && (st->stream_type==GF_STREAM_AUDIO)) {
271 1 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_AUDIO_FORMAT);
272 1 : if (p) st->abps = gf_audio_fmt_bit_depth(p->value.uint) / 8;
273 1 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_NUM_CHANNELS);
274 1 : if (p) st->nb_ch = p->value.uint;
275 1 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_SAMPLE_RATE);
276 1 : st->sample_rate = p ? p->value.uint : st->timescale;
277 1 : st->abps *= st->nb_ch;
278 1 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_AUDIO_FORMAT);
279 1 : if (p && (p->value.uint>GF_AUDIO_FMT_LAST_PACKED))
280 0 : st->audio_planar = GF_TRUE;
281 : }
282 :
283 :
284 : //if non SAP1 detected and xadjust, move directly to needs_adjust=TRUE
285 : //otherwise consider we don't need to probe for nect sap, this will be updated
286 : //if one packet is not sap
287 194 : st->needs_adjust = (ctx->xadjust && !st->all_saps) ? GF_TRUE : GF_FALSE;
288 :
289 194 : st->tk_delay = 0;
290 194 : st->ts_sub = 0;
291 194 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_DELAY);
292 194 : if (p) {
293 : //delay negative is skip: this is CTS adjustment for B-frames: we keep that notif in the stream
294 15 : if (p->value.longsint<=0) {
295 15 : st->tk_delay = 0;
296 15 : st->ts_sub = (u32) -p->value.longsint;
297 : }
298 : //delay positive is delay, we keep the value for RT regulation and range
299 : else {
300 0 : st->tk_delay = (u64) p->value.longsint;
301 : }
302 : }
303 194 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_PLAYBACK_MODE);
304 194 : if (!p || (p->value.uint < GF_PLAYBACK_MODE_FASTFORWARD))
305 10 : ctx->seekable = GF_FALSE;
306 :
307 :
308 194 : ctx->filter_sap1 = ctx->filter_sap2 = ctx->filter_sap3 = ctx->filter_sap4 = ctx->filter_sap_none = GF_FALSE;
309 198 : for (i=0; i<ctx->saps.nb_items; i++) {
310 4 : switch (ctx->saps.vals[i]) {
311 4 : case 1:
312 4 : ctx->filter_sap1 = GF_TRUE;
313 4 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_HAS_SYNC, &PROP_BOOL(GF_FALSE)); //false: all samples are sync
314 4 : break;
315 0 : case 2:
316 0 : ctx->filter_sap2 = GF_TRUE;
317 0 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_HAS_SYNC, &PROP_BOOL(GF_FALSE)); //false: all samples are sync
318 0 : break;
319 0 : case 3: ctx->filter_sap3 = GF_TRUE; break;
320 0 : case 4: ctx->filter_sap4 = GF_TRUE; break;
321 0 : default: ctx->filter_sap_none = GF_TRUE; break;
322 : }
323 : }
324 194 : gf_filter_pid_set_framing_mode(pid, GF_TRUE);
325 :
326 : //NEVER use seek mode for uncompressed audio, packets are splitted anyway
327 194 : if (ctx->extract_mode==EXTRACT_RANGE) {
328 29 : if (st->abps) {
329 0 : st->seek_mode = GF_FALSE;
330 : }
331 : //use seek if seek is forced
332 29 : else if (ctx->xround==REFRAME_ROUND_SEEK) {
333 0 : st->seek_mode = GF_TRUE;
334 : }
335 : //use seek mode for compressed audio with or without priming
336 29 : else if (st->stream_type==GF_STREAM_AUDIO) {
337 6 : st->seek_mode = ctx->no_audio_seek ? GF_FALSE : GF_TRUE;
338 : } else {
339 23 : st->seek_mode = GF_FALSE;
340 : }
341 : } else {
342 165 : st->seek_mode = GF_FALSE;
343 : }
344 194 : reframer_push_props(ctx, st);
345 :
346 194 : if (ctx->cur_range_idx && (ctx->cur_range_idx <= ctx->props.nb_items)) {
347 0 : gf_filter_pid_push_properties(st->opid, ctx->props.vals[ctx->cur_range_idx-1], GF_FALSE, GF_FALSE);
348 : }
349 :
350 : return GF_OK;
351 : }
352 :
353 70 : static Bool reframer_parse_date(char *date, GF_Fraction64 *value, u64 *frame_idx_plus_one, u32 *extract_mode)
354 : {
355 : u64 v;
356 70 : value->num =0;
357 70 : value->den = 0;
358 :
359 70 : if (extract_mode)
360 42 : *extract_mode = EXTRACT_RANGE;
361 :
362 70 : if (date[0] == 'T') {
363 48 : u32 h=0, m=0, s=0, ms=0;
364 48 : if (strchr(date, '.')) {
365 48 : if (sscanf(date, "T%u:%u:%u.%u", &h, &m, &s, &ms) != 4) {
366 0 : if (sscanf(date, "T%u:%u.%u", &m, &s, &ms) != 3) {
367 0 : if (sscanf(date, "T%u.%u", &s, &ms) != 2) {
368 : goto exit;
369 : }
370 : }
371 : }
372 48 : if (ms>=1000) ms=0;
373 : } else {
374 0 : if (sscanf(date, "T%u:%u:%u", &h, &m, &s) != 3) {
375 0 : if (sscanf(date, "T%u:%u", &m, &s) != 2) {
376 : goto exit;
377 : }
378 : }
379 : }
380 48 : v = h*3600 + m*60 + s;
381 48 : v *= 1000;
382 48 : v += ms;
383 48 : value->num = v;
384 48 : value->den = 1000;
385 48 : return GF_TRUE;
386 : }
387 22 : if ((date[0]=='F') || (date[0]=='f')) {
388 2 : *frame_idx_plus_one = 1 + atoi(date+1);
389 1 : return GF_TRUE;
390 : }
391 21 : if (!strcmp(date, "RAP") || !strcmp(date, "SAP")) {
392 1 : if (extract_mode)
393 1 : *extract_mode = EXTRACT_SAP;
394 1 : value->num = 0;
395 1 : value->den = 1000;
396 1 : return GF_TRUE;
397 : }
398 20 : if ((date[0]=='D') || (date[0]=='d')) {
399 6 : if (extract_mode)
400 6 : *extract_mode = EXTRACT_DUR;
401 6 : if (sscanf(date+1, LLD"/"LLU, &value->num, &value->den)==2) {
402 : return GF_TRUE;
403 : }
404 6 : if (sscanf(date+1, LLU, &v)==1) {
405 6 : value->num = v;
406 6 : value->den = 1000;
407 6 : return GF_TRUE;
408 : }
409 : }
410 14 : if ((date[0]=='S') || (date[0]=='s')) {
411 : GF_PropertyValue p;
412 6 : if (extract_mode)
413 6 : *extract_mode = EXTRACT_SIZE;
414 6 : p = gf_props_parse_value(GF_PROP_LUINT, "size", date+1, NULL, ',');
415 6 : if (p.type==GF_PROP_LUINT) {
416 6 : value->den = p.value.longuint;
417 6 : return GF_TRUE;
418 : }
419 : }
420 :
421 8 : if (gf_parse_lfrac(date, value)) {
422 : return GF_TRUE;
423 : }
424 :
425 0 : exit:
426 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[Reframer] Unrecognized date format %s, expecting TXX:XX:XX[.XX], INT or FRAC\n", date));
427 0 : if (extract_mode)
428 0 : *extract_mode = EXTRACT_NONE;
429 : return GF_FALSE;
430 : }
431 :
432 1080 : static void reframer_load_range(GF_ReframerCtx *ctx)
433 : {
434 : u32 i, count;
435 1080 : Bool do_seek = ctx->seekable;
436 : Bool reset_asplit = GF_TRUE;
437 1080 : u64 prev_frame = ctx->start_frame_idx_plus_one;
438 : GF_Fraction64 prev_end;
439 : char *start_date=NULL, *end_date=NULL;
440 :
441 1080 : ctx->nb_video_frames_since_start_at_range_start = ctx->nb_video_frames_since_start;
442 :
443 1080 : if (ctx->extract_mode==EXTRACT_DUR) {
444 41 : ctx->cur_start.num += (ctx->extract_dur.num * ctx->cur_start.den) / ctx->extract_dur.den;
445 41 : ctx->cur_end.num += (ctx->extract_dur.num * ctx->cur_end.den) / ctx->extract_dur.den;
446 41 : ctx->file_idx++;
447 41 : return;
448 : }
449 1039 : if ((ctx->extract_mode==EXTRACT_SAP) || (ctx->extract_mode==EXTRACT_SIZE)) {
450 23 : ctx->cur_start = ctx->cur_end;
451 23 : ctx->min_ts_computed = 0;
452 23 : ctx->min_ts_scale = 0;
453 23 : ctx->file_idx++;
454 23 : return;
455 : }
456 1016 : prev_end = ctx->cur_end;
457 1016 : ctx->start_frame_idx_plus_one = 0;
458 1016 : ctx->end_frame_idx_plus_one = 0;
459 1016 : ctx->cur_start.num = 0;
460 1016 : ctx->cur_start.den = 0;
461 1016 : ctx->cur_end.num = 0;
462 1016 : ctx->cur_end.den = 0;
463 :
464 1016 : count = ctx->xs.nb_items;
465 1016 : if (!count) {
466 955 : if (ctx->range_type) goto range_done;
467 : return;
468 : }
469 61 : if (ctx->cur_range_idx>=count) {
470 : goto range_done;
471 : } else {
472 42 : start_date = ctx->xs.vals[ctx->cur_range_idx];
473 : end_date = NULL;
474 42 : if (ctx->cur_range_idx < ctx->xe.nb_items)
475 28 : end_date = ctx->xe.vals[ctx->cur_range_idx];
476 14 : else if (ctx->cur_range_idx + 1 < ctx->xs.nb_items)
477 0 : end_date = ctx->xs.vals[ctx->cur_range_idx+1];
478 : }
479 42 : if (!start_date)
480 : goto range_done;
481 :
482 42 : ctx->cur_range_idx++;
483 42 : if (!end_date) ctx->range_type = RANGE_OPEN;
484 28 : else ctx->range_type = RANGE_CLOSED;
485 :
486 42 : if (!reframer_parse_date(start_date, &ctx->cur_start, &ctx->start_frame_idx_plus_one, &ctx->extract_mode)) {
487 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[Reframer] cannot parse start date, assuming end of ranges\n"));
488 : //done
489 0 : ctx->range_type = RANGE_DONE;
490 0 : return;
491 : }
492 :
493 : //range in frame
494 42 : if (ctx->start_frame_idx_plus_one) {
495 : //either range is before or prev range was not frame-based
496 1 : if (ctx->start_frame_idx_plus_one > prev_frame)
497 : do_seek = GF_TRUE;
498 : }
499 : //range is time based, prev was frame-based, seek
500 41 : else if (!prev_end.den) {
501 : do_seek = GF_TRUE;
502 : } else {
503 : //cur start is before previous end, need to seek
504 10 : if (ctx->cur_start.num * prev_end.den < prev_end.num * ctx->cur_start.den) {
505 : do_seek = GF_TRUE;
506 : }
507 : //cur start is less than our seek safety from previous end, do not seek
508 10 : if (ctx->cur_start.num * prev_end.den < (prev_end.num + ctx->seeksafe*prev_end.den) * ctx->cur_start.den)
509 : do_seek = GF_FALSE;
510 : }
511 : //do not issue seek on first range, done when catching play requests
512 42 : if (ctx->cur_range_idx==1) {
513 : do_seek = GF_FALSE;
514 : }
515 :
516 42 : if (!ctx->seekable && do_seek) {
517 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[Reframer] ranges not in order and input not seekable, aborting extraction\n"));
518 : goto range_done;
519 : }
520 :
521 42 : ctx->is_range_extraction = ((ctx->extract_mode==EXTRACT_RANGE) || (ctx->extract_mode==EXTRACT_DUR)) ? GF_TRUE : GF_FALSE;
522 :
523 42 : if (ctx->extract_mode != EXTRACT_RANGE) {
524 : end_date = NULL;
525 13 : if (ctx->extract_mode==EXTRACT_DUR) {
526 6 : ctx->extract_dur = ctx->cur_start;
527 6 : ctx->cur_start.num = 0;
528 : ctx->cur_start.den = ctx->extract_dur.den;
529 6 : ctx->cur_end = ctx->extract_dur;
530 6 : ctx->range_type = RANGE_CLOSED;
531 6 : ctx->file_idx = 1;
532 6 : ctx->splitrange = GF_TRUE;
533 6 : ctx->xadjust = GF_TRUE;
534 : }
535 7 : else if (ctx->extract_mode==EXTRACT_SIZE) {
536 6 : ctx->splitrange = GF_TRUE;
537 6 : ctx->split_size = ctx->cur_start.den;
538 6 : if (!ctx->split_size) {
539 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[Reframer] invalid split size %d\n", ctx->split_size));
540 : goto range_done;
541 : }
542 6 : ctx->file_idx = 1;
543 : }
544 1 : else if (ctx->extract_mode==EXTRACT_SAP) {
545 1 : ctx->splitrange = GF_TRUE;
546 : }
547 : }
548 29 : if (end_date) {
549 28 : if (!reframer_parse_date(end_date, &ctx->cur_end, &ctx->end_frame_idx_plus_one, NULL)) {
550 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[Reframer] cannot parse end date, assuming open range\n"));
551 0 : ctx->range_type = RANGE_OPEN;
552 : }
553 : }
554 :
555 42 : if (prev_end.den && (prev_end.num * ctx->cur_start.den == prev_end.den * ctx->cur_start.num))
556 : reset_asplit = GF_FALSE;
557 :
558 : //reset realtime range and issue seek requests
559 42 : if (ctx->rt || do_seek || reset_asplit) {
560 : Double start_range = 0;
561 42 : if (do_seek) {
562 0 : start_range = (Double) ctx->cur_start.num;
563 0 : start_range /= ctx->cur_start.den;
564 0 : if (start_range > ctx->seeksafe)
565 0 : start_range -= ctx->seeksafe;
566 : else
567 : start_range = 0;
568 0 : ctx->has_seen_eos = GF_FALSE;
569 0 : ctx->nb_video_frames_since_start_at_range_start = 0;
570 0 : ctx->nb_video_frames_since_start = 0;
571 : }
572 42 : count = gf_list_count(ctx->streams);
573 62 : for (i=0; i<count; i++) {
574 20 : RTStream *st = gf_list_get(ctx->streams, i);
575 20 : if (ctx->rt) {
576 0 : st->cts_us_at_init = 0;
577 0 : st->sys_clock_at_init = 0;
578 : }
579 20 : if (do_seek) {
580 : GF_FilterEvent evt;
581 0 : GF_FEVT_INIT(evt, GF_FEVT_STOP, st->ipid);
582 0 : gf_filter_pid_send_event(st->ipid, &evt);
583 0 : GF_FEVT_INIT(evt, GF_FEVT_PLAY, st->ipid);
584 0 : evt.play.start_range = start_range;
585 0 : evt.play.speed = 1;
586 0 : gf_filter_pid_send_event(st->ipid, &evt);
587 :
588 0 : st->nb_frames = 0;
589 : }
590 20 : if (reset_asplit) {
591 20 : st->audio_samples_to_keep = 0;
592 : }
593 : }
594 : }
595 :
596 42 : if (ctx->cur_range_idx && (ctx->cur_range_idx <= ctx->props.nb_items)) {
597 0 : count = gf_list_count(ctx->streams);
598 0 : for (i=0; i<count; i++) {
599 0 : RTStream *st = gf_list_get(ctx->streams, i);
600 :
601 0 : reframer_push_props(ctx, st);
602 0 : gf_filter_pid_push_properties(st->opid, ctx->props.vals[ctx->cur_range_idx-1], GF_FALSE, GF_FALSE);
603 0 : gf_filter_pid_set_property_str(st->opid, "period_resume", &PROP_STRING("") );
604 : }
605 : }
606 :
607 : return;
608 :
609 19 : range_done:
610 : //done
611 19 : ctx->range_type = RANGE_DONE;
612 19 : count = gf_list_count(ctx->streams);
613 48 : for (i=0; i<count; i++) {
614 : GF_FilterEvent evt;
615 29 : RTStream *st = gf_list_get(ctx->streams, i);
616 29 : gf_filter_pid_set_discard(st->ipid, GF_TRUE);
617 29 : GF_FEVT_INIT(evt, GF_FEVT_STOP, st->ipid);
618 29 : gf_filter_pid_send_event(st->ipid, &evt);
619 29 : gf_filter_pid_set_eos(st->opid);
620 : }
621 :
622 : }
623 :
624 62966 : void reframer_drop_packet(GF_ReframerCtx *ctx, RTStream *st, GF_FilterPacket *pck, Bool pck_is_ref)
625 : {
626 62966 : if (pck_is_ref) {
627 7228 : gf_list_rem(st->pck_queue, 0);
628 7228 : gf_filter_pck_unref(pck);
629 : } else {
630 55738 : gf_filter_pid_drop_packet(st->ipid);
631 : }
632 62966 : }
633 :
634 0 : void reframer_copy_raw_audio(RTStream *st, const u8 *src, u32 src_size, u32 offset, u8 *dst, u32 nb_samp)
635 : {
636 0 : if (st->audio_planar) {
637 : u32 i, bps, stride;
638 0 : stride = src_size / st->nb_ch;
639 0 : bps = st->abps / st->nb_ch;
640 0 : for (i=0; i<st->nb_ch; i++) {
641 0 : memcpy(dst + i*bps*nb_samp, src + i*stride + offset * bps, nb_samp * bps);
642 : }
643 : } else {
644 0 : memcpy(dst, src + offset * st->abps, nb_samp * st->abps);
645 : }
646 0 : }
647 :
648 1100243 : Bool reframer_send_packet(GF_Filter *filter, GF_ReframerCtx *ctx, RTStream *st, GF_FilterPacket *pck, Bool pck_is_ref)
649 : {
650 : Bool do_send = GF_FALSE;
651 :
652 :
653 1100243 : if (!ctx->rt) {
654 : do_send = GF_TRUE;
655 : } else {
656 1041671 : u64 cts_us = gf_filter_pck_get_dts(pck);
657 1041671 : if (cts_us==GF_FILTER_NO_TS)
658 0 : cts_us = gf_filter_pck_get_cts(pck);
659 :
660 1041671 : if (cts_us==GF_FILTER_NO_TS) {
661 : do_send = GF_TRUE;
662 : } else {
663 1041671 : u64 clock = ctx->clock_val;
664 1041671 : cts_us += st->tk_delay;
665 :
666 1041671 : cts_us *= 1000000;
667 1041671 : cts_us /= st->timescale;
668 1041671 : if (ctx->rt==REFRAME_RT_SYNC) {
669 0 : if (!ctx->clock) ctx->clock = st;
670 :
671 0 : st = ctx->clock;
672 : }
673 1041671 : if (!st->sys_clock_at_init) {
674 13 : st->cts_us_at_init = cts_us;
675 13 : st->sys_clock_at_init = clock;
676 : do_send = GF_TRUE;
677 1041658 : } else if (cts_us < st->cts_us_at_init) {
678 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[Reframer] CTS less than CTS used to initialize clock, not delaying\n"));
679 : do_send = GF_TRUE;
680 : } else {
681 1041658 : u64 diff = cts_us - st->cts_us_at_init;
682 1041658 : if (ctx->speed>0) diff = (u64) ( diff / ctx->speed);
683 0 : else if (ctx->speed<0) diff = (u64) ( diff / -ctx->speed);
684 :
685 1041658 : clock -= st->sys_clock_at_init;
686 1041658 : if (clock + RT_PRECISION_US >= diff) {
687 : do_send = GF_TRUE;
688 2459 : if (clock > diff) {
689 1241 : 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));
690 : }
691 : } else {
692 1039199 : diff -= clock;
693 1039199 : if (!ctx->reschedule_in)
694 745684 : ctx->reschedule_in = diff;
695 293515 : else if (ctx->reschedule_in > diff)
696 94067 : ctx->reschedule_in = diff;
697 : }
698 : }
699 : }
700 : }
701 :
702 1100243 : if (!ctx->range_type && ctx->frames.nb_items) {
703 : u32 i;
704 : Bool found=GF_FALSE;
705 0 : for (i=0; i<ctx->frames.nb_items; i++) {
706 0 : if (ctx->frames.vals[i] == st->nb_frames + 1) {
707 : found=GF_TRUE;
708 : break;
709 : }
710 : }
711 0 : if (!found) {
712 : //drop
713 0 : gf_filter_pid_drop_packet(st->ipid);
714 0 : st->nb_frames++;
715 0 : return GF_TRUE;
716 : }
717 : }
718 :
719 1100243 : if (!do_send)
720 : return GF_FALSE;
721 :
722 : //range processing
723 61044 : if (st->ts_at_range_start_plus_one) {
724 : Bool is_split = GF_FALSE;
725 : s64 ts;
726 : s32 ts_adj = 0;
727 : u32 cts_offset=0;
728 : u32 dur=0;
729 : GF_FilterPacket *new_pck;
730 :
731 : //tmcd, rewrite sample
732 7228 : if (ctx->tcmdrw && (st->codec_id==GF_CODECID_TMCD) && st->split_start && ctx->nb_video_frames_since_start_at_range_start) {
733 : GF_BitStream *bs;
734 : u32 nb_frames;
735 0 : u8 *tcmd_data = NULL;
736 0 : new_pck = gf_filter_pck_new_copy(st->opid, pck, &tcmd_data);
737 0 : if (!new_pck) return GF_FALSE;
738 :
739 0 : bs = gf_bs_new(tcmd_data, 4, GF_BITSTREAM_READ);
740 0 : nb_frames = gf_bs_read_u32(bs);
741 0 : gf_bs_del(bs);
742 0 : bs = gf_bs_new(tcmd_data, 4, GF_BITSTREAM_WRITE);
743 0 : gf_bs_seek(bs, 0);
744 0 : gf_bs_write_u32(bs, nb_frames+ctx->nb_video_frames_since_start_at_range_start);
745 0 : gf_bs_del(bs);
746 0 : dur = gf_filter_pck_get_duration(pck);
747 0 : if (dur > st->split_start)
748 0 : dur -= st->split_start;
749 0 : if (st->split_end && (dur > st->split_end - st->split_start))
750 : dur = st->split_end - st->split_start;
751 0 : ts_adj = st->split_start;
752 :
753 7238 : } else if ((pck == st->split_pck) && st->audio_samples_to_keep) {
754 : const u8 *data;
755 : u32 pck_size;
756 10 : data = gf_filter_pck_get_data(pck, &pck_size);
757 10 : if (st->abps) {
758 0 : if (st->audio_samples_to_keep * st->abps <= pck_size) {
759 : u8 *output;
760 0 : new_pck = gf_filter_pck_new_alloc(st->opid, st->audio_samples_to_keep * st->abps, &output);
761 0 : if (!new_pck) return GF_FALSE;
762 0 : reframer_copy_raw_audio(st, data, pck_size, 0, output, st->audio_samples_to_keep);
763 : } else {
764 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[Reframer] Broken raw audio frame size/duration: %d samples to keep but packet is smaller (%d samples)\n", st->audio_samples_to_keep, pck_size/st->abps));
765 0 : st->audio_samples_to_keep = 0;
766 0 : new_pck = gf_filter_pck_new_ref(st->opid, 0, 0, pck);
767 0 : if (!new_pck) return GF_FALSE;
768 : }
769 : } else {
770 10 : new_pck = gf_filter_pck_new_ref(st->opid, 0, 0, pck);
771 10 : if (!new_pck) return GF_FALSE;
772 : }
773 10 : dur = st->audio_samples_to_keep;
774 7218 : } else if (st->audio_samples_to_keep) {
775 : u8 *output;
776 : const u8 *data;
777 : u32 pck_size;
778 8 : data = gf_filter_pck_get_data(pck, &pck_size);
779 8 : if (st->abps) {
780 0 : if (pck_size < st->audio_samples_to_keep * st->abps) {
781 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[Reframer] Broken raw audio frame size/duration: %d samples to keep but packet is smaller (%d samples)\n", st->audio_samples_to_keep, pck_size/st->abps));
782 0 : st->audio_samples_to_keep = 0;
783 : }
784 0 : new_pck = gf_filter_pck_new_alloc(st->opid, pck_size - st->audio_samples_to_keep * st->abps, &output);
785 0 : if (!new_pck) return GF_FALSE;
786 :
787 0 : reframer_copy_raw_audio(st, data, pck_size, st->audio_samples_to_keep, output, pck_size/st->abps - st->audio_samples_to_keep);
788 :
789 0 : dur = pck_size/st->abps - st->audio_samples_to_keep;
790 :
791 : cts_offset = st->audio_samples_to_keep;
792 : //if first range, add CTS offset to ts at range start
793 0 : if (ctx->cur_range_idx==1)
794 0 : st->ts_at_range_start_plus_one += cts_offset;
795 : } else {
796 8 : new_pck = gf_filter_pck_new_ref(st->opid, 0, 0, pck);
797 8 : if (!new_pck) return GF_FALSE;
798 :
799 8 : dur = gf_filter_pck_get_duration(pck);
800 8 : gf_filter_pck_set_property(new_pck, GF_PROP_PCK_SKIP_BEGIN, &PROP_UINT( st->audio_samples_to_keep ) );
801 : }
802 8 : st->audio_samples_to_keep = 0;
803 : } else {
804 7210 : new_pck = gf_filter_pck_new_ref(st->opid, 0, 0, pck);
805 7210 : if (!new_pck) return GF_FALSE;
806 : }
807 7228 : gf_filter_pck_merge_properties(pck, new_pck);
808 :
809 7228 : if (cts_offset || dur) {
810 18 : if (st->abps && (st->timescale!=st->sample_rate)) {
811 0 : cts_offset *= st->timescale;
812 0 : cts_offset /= st->sample_rate;
813 0 : dur *= st->timescale;
814 0 : dur /= st->sample_rate;
815 : }
816 18 : gf_filter_pck_set_duration(new_pck, dur);
817 : }
818 :
819 : //signal chunk start boundary
820 7228 : if (!st->first_pck_sent) {
821 : u64 start_t, end_t;
822 : char szFileSuf[1000];
823 : u32 i, len;
824 144 : char *file_suf_name = NULL;
825 144 : char *start = ctx->xs.vals[ctx->cur_range_idx-1];
826 : char *end = NULL;
827 144 : if ((ctx->range_type==1) && (ctx->cur_range_idx<ctx->xe.nb_items+1)) {
828 42 : end = ctx->xe.vals[ctx->cur_range_idx-1];
829 : }
830 144 : st->first_pck_sent = GF_TRUE;
831 :
832 144 : if (ctx->extract_mode==EXTRACT_RANGE) {
833 :
834 42 : gf_filter_pck_set_property(new_pck, GF_PROP_PCK_FILENUM, &PROP_UINT(ctx->cur_range_idx) );
835 :
836 42 : if (strchr(start, '/')) {
837 0 : start_t = ctx->cur_start.num;
838 0 : start_t /= ctx->cur_start.den;
839 0 : if (ctx->cur_end.den) {
840 0 : end_t = ctx->cur_end.num;
841 0 : end_t /= ctx->cur_end.den;
842 : sprintf(szFileSuf, LLU"-"LLU, start_t, end_t);
843 : } else {
844 : sprintf(szFileSuf, LLU, start_t);
845 : }
846 0 : gf_filter_pck_set_property(new_pck, GF_PROP_PCK_FILESUF, &PROP_STRING(szFileSuf) );
847 : } else {
848 :
849 42 : gf_dynstrcat(&file_suf_name, start, NULL);
850 42 : if (end)
851 42 : gf_dynstrcat(&file_suf_name, end, "_");
852 :
853 42 : len = (u32) strlen(file_suf_name);
854 : //replace : and / characters
855 1088 : for (i=0; i<len; i++) {
856 1046 : switch (file_suf_name[i]) {
857 152 : case ':':
858 : case '/':
859 152 : file_suf_name[i] = '.';
860 152 : break;
861 : }
862 : }
863 42 : gf_filter_pck_set_property(new_pck, GF_PROP_PCK_FILESUF, &PROP_STRING_NO_COPY(file_suf_name) );
864 : }
865 : } else {
866 102 : start_t = ctx->cur_start.num * 1000;
867 102 : start_t /= ctx->cur_start.den;
868 102 : end_t = ctx->cur_end.num * 1000;
869 102 : end_t /= ctx->cur_end.den;
870 :
871 102 : gf_filter_pck_set_property(new_pck, GF_PROP_PCK_FILENUM, &PROP_UINT(ctx->file_idx) );
872 : sprintf(szFileSuf, LLU"-"LLU, start_t, end_t);
873 102 : gf_filter_pck_set_property(new_pck, GF_PROP_PCK_FILESUF, &PROP_STRING(szFileSuf) );
874 : }
875 : }
876 :
877 : //rewrite timestamps
878 7228 : ts = gf_filter_pck_get_cts(pck) + cts_offset;
879 :
880 7228 : if (ts != GF_FILTER_NO_TS) {
881 7228 : if (ctx->is_range_extraction
882 4941 : && st->seek_mode
883 966 : && ((ts + ts_adj - st->ts_sub) * ctx->cur_start.den < ctx->cur_start.num * (u64) st->timescale)
884 : ) {
885 8 : gf_filter_pck_set_seek_flag(new_pck, GF_TRUE);
886 8 : gf_filter_pck_set_property(new_pck, GF_PROP_PCK_SKIP_BEGIN, NULL);
887 8 : if (st->stream_type!=GF_STREAM_VISUAL) {
888 8 : u32 dur = gf_filter_pck_get_duration(new_pck);
889 8 : if ((ts + ts_adj + dur - st->ts_sub) * ctx->cur_start.den > ctx->cur_start.num * (u64) st->timescale) {
890 8 : u32 ts_diff = (u32) (ctx->cur_start.num * st->timescale / ctx->cur_start.den - (ts + ts_adj - st->ts_sub) );
891 8 : gf_filter_pck_set_property(new_pck, GF_PROP_PCK_SKIP_BEGIN, &PROP_UINT(ts_diff));
892 8 : gf_filter_pck_set_seek_flag(new_pck, GF_FALSE);
893 : }
894 : }
895 : }
896 :
897 7228 : ts += st->tk_delay;
898 7228 : ts += st->ts_at_range_end;
899 7228 : ts -= st->ts_at_range_start_plus_one - 1;
900 :
901 7228 : if (ts<0) {
902 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[Reframer] Negative TS while splitting, something went wrong during range estimation, forcing to 0\n"));
903 : ts = 0;
904 : }
905 7228 : if (st->probe_ref_frame_ts && (gf_filter_pck_get_dts(pck) + st->tk_delay + 1 == st->probe_ref_frame_ts)) {
906 1 : gf_filter_pck_set_property(new_pck, GF_PROP_PCK_SKIP_PRES, &PROP_BOOL(GF_TRUE));
907 : }
908 :
909 7228 : gf_filter_pck_set_cts(new_pck, (u64) ts);
910 7228 : if (st->is_raw) {
911 0 : gf_filter_pck_set_dts(new_pck, ts);
912 : }
913 : }
914 7228 : if (!st->is_raw) {
915 7228 : ts = gf_filter_pck_get_dts(pck) + cts_offset;
916 7228 : if (ts != GF_FILTER_NO_TS) {
917 7228 : ts += st->tk_delay;
918 7228 : ts -= st->ts_at_range_start_plus_one - 1;
919 7228 : ts += st->ts_at_range_end;
920 7228 : gf_filter_pck_set_dts(new_pck, (u64) ts);
921 : }
922 : }
923 : //packet was split or was re-inserted
924 7228 : if (st->split_start) {
925 7 : if (!dur) {
926 7 : u32 dur = gf_filter_pck_get_duration(pck);
927 : //can happen if source packet is less than split period duration, we just copy with no timing adjustment
928 7 : if (dur > st->split_start)
929 5 : dur -= st->split_start;
930 7 : gf_filter_pck_set_duration(new_pck, dur);
931 : }
932 7 : st->ts_at_range_start_plus_one += st->split_start;
933 7 : st->split_start = 0;
934 : is_split = GF_TRUE;
935 : }
936 : //last packet and forced duration
937 7228 : if (st->split_end && (gf_list_count(st->pck_queue)==1)) {
938 8 : if (!dur) {
939 8 : gf_filter_pck_set_duration(new_pck, st->split_end);
940 : }
941 8 : st->split_end = 0;
942 : is_split = GF_TRUE;
943 : }
944 : //packet reinserted (not split), adjust duration and store offset in split start
945 7228 : if (!st->can_split && !is_split && st->reinsert_single_pck) {
946 28 : u32 dur = gf_filter_pck_get_duration(pck);
947 : //only for closed range
948 28 : if (st->range_end_reached_ts) {
949 : u64 ndur = st->range_end_reached_ts;
950 2 : ndur -= st->ts_at_range_start_plus_one-1;
951 2 : if (ndur && (ndur < dur))
952 0 : gf_filter_pck_set_duration(new_pck, (u32) ndur);
953 2 : st->split_start = (u32) ndur;
954 : }
955 : }
956 :
957 7228 : gf_filter_pck_send(new_pck);
958 :
959 : } else {
960 53816 : gf_filter_pck_forward(pck, st->opid);
961 : }
962 :
963 :
964 61044 : reframer_drop_packet(ctx, st, pck, pck_is_ref);
965 61044 : st->nb_frames++;
966 :
967 61044 : if (st->stream_type==GF_STREAM_VISUAL) {
968 16682 : if (st->nb_frames > ctx->nb_video_frames_since_start) {
969 15874 : ctx->nb_video_frames_since_start = st->nb_frames;
970 : }
971 : }
972 :
973 : return GF_TRUE;
974 : }
975 :
976 8169 : static u32 reframer_check_pck_range(GF_ReframerCtx *ctx, RTStream *st, u64 ts, u32 dur, u32 frame_idx, u32 *nb_audio_samples_to_keep)
977 : {
978 8169 : if (ctx->start_frame_idx_plus_one) {
979 : //frame not after our range start
980 250 : if (frame_idx<ctx->start_frame_idx_plus_one) {
981 : return 0;
982 : } else {
983 : //closed range, check
984 50 : if ((ctx->range_type!=RANGE_OPEN) && (frame_idx>=ctx->end_frame_idx_plus_one)) {
985 : return 2;
986 : }
987 50 : return 1;
988 : }
989 : } else {
990 : Bool before = GF_FALSE;
991 : Bool after = GF_FALSE;
992 :
993 : //check ts not after our range start:
994 : //if round_seek mode, check TS+dur is less than or equal to desired start, and we will notify the duration of data to skip from the packet
995 : //otherwise, check TS is strictly less than desired start
996 7919 : if (st->seek_mode) {
997 2150 : if ((s64) ((ts+dur) * ctx->cur_start.den) <= ctx->cur_start.num * st->timescale) {
998 : before = GF_TRUE;
999 1276 : if ((st->stream_type==GF_STREAM_AUDIO) && st->ts_sub) {
1000 0 : if ((s64) ((ts+st->ts_sub) * ctx->cur_start.den) > ctx->cur_start.num * st->timescale) {
1001 : before = GF_FALSE;
1002 : }
1003 : }
1004 : }
1005 : }
1006 5769 : else if ((s64) (ts * ctx->cur_start.den) < ctx->cur_start.num * st->timescale) {
1007 : before = GF_TRUE;
1008 2052 : if (st->abps && ( (s64) (ts+dur) * (s64) ctx->cur_start.den > ctx->cur_start.num * (s64) st->timescale)) {
1009 0 : u64 nb_samp = ctx->cur_start.num * st->timescale / ctx->cur_start.den - ts;
1010 0 : if (st->timescale != st->sample_rate) {
1011 0 : nb_samp *= st->sample_rate;
1012 0 : nb_samp /= st->timescale;
1013 : }
1014 0 : *nb_audio_samples_to_keep = (u32) nb_samp;
1015 : before = GF_FALSE;
1016 : }
1017 : }
1018 : //consider after if time+duration is STRICTLY greater than cut point
1019 7919 : if ((ctx->range_type!=RANGE_OPEN) && ((s64) ((ts+dur) * ctx->cur_end.den) > ctx->cur_end.num * st->timescale)) {
1020 110 : if ((st->abps || st->seek_mode )
1021 10 : && ( (s64) ts * (s64) ctx->cur_end.den < ctx->cur_end.num * (s64) st->timescale)
1022 : ) {
1023 10 : u64 nb_samp = ctx->cur_end.num * st->timescale / ctx->cur_end.den - ts;
1024 10 : if (st->abps && (st->timescale != st->sample_rate)) {
1025 0 : nb_samp *= st->sample_rate;
1026 0 : nb_samp /= st->timescale;
1027 : }
1028 10 : *nb_audio_samples_to_keep = (u32)nb_samp;
1029 : }
1030 : after = GF_TRUE;
1031 : }
1032 7919 : if (before) {
1033 3328 : if (!after)
1034 : return 0;
1035 : //long duration samples (typically text) can both start before and end after the target range
1036 : else
1037 0 : return 2;
1038 : }
1039 4591 : if (after)
1040 : return 2;
1041 4481 : return 1;
1042 : }
1043 : return 0;
1044 : }
1045 :
1046 446 : void reframer_purge_queues(GF_ReframerCtx *ctx, u64 ts, u32 timescale)
1047 : {
1048 446 : u32 i, count = gf_list_count(ctx->streams);
1049 952 : for (i=0; i<count; i++) {
1050 506 : RTStream *st = gf_list_get(ctx->streams, i);
1051 : u64 ts_rescale = ts;
1052 506 : if (st->reinsert_single_pck)
1053 26 : continue;
1054 :
1055 480 : if (st->timescale != timescale) {
1056 36 : ts_rescale *= st->timescale;
1057 36 : ts_rescale /= timescale;
1058 : }
1059 2808 : while (1) {
1060 3288 : GF_FilterPacket *pck = gf_list_get(st->pck_queue, 0);
1061 3288 : if (!pck) break;
1062 3241 : u64 dts = gf_filter_pck_get_dts(pck);
1063 3241 : if (dts==GF_FILTER_NO_TS)
1064 0 : dts = gf_filter_pck_get_cts(pck);
1065 :
1066 3241 : dts += gf_filter_pck_get_duration(pck);
1067 3241 : if (dts >= ts_rescale) break;
1068 2808 : gf_list_rem(st->pck_queue, 0);
1069 2808 : gf_filter_pck_unref(pck);
1070 2808 : st->nb_frames++;
1071 : }
1072 : }
1073 446 : }
1074 :
1075 2218 : static void check_gop_split(GF_ReframerCtx *ctx)
1076 : {
1077 2218 : u32 i, count = gf_list_count(ctx->streams);
1078 : Bool flush_all = GF_FALSE;
1079 :
1080 2218 : if (!ctx->min_ts_scale) {
1081 : u64 min_ts = 0;
1082 : u32 min_timescale=0;
1083 : u64 min_ts_a = 0;
1084 : u32 min_timescale_a=0;
1085 : u32 nb_eos = 0;
1086 : Bool has_empty_streams = GF_FALSE;
1087 : Bool wait_for_sap = GF_FALSE;
1088 2381 : for (i=0; i<count; i++) {
1089 : u32 j, nb_pck, nb_sap;
1090 : u64 last_sap_ts=0;
1091 2381 : RTStream *st = gf_list_get(ctx->streams, i);
1092 2381 : nb_pck = gf_list_count(st->pck_queue);
1093 : nb_sap = 0;
1094 2381 : if (st->in_eos) {
1095 681 : nb_eos++;
1096 681 : if (!nb_pck) {
1097 : has_empty_streams = GF_TRUE;
1098 0 : continue;
1099 : }
1100 : }
1101 :
1102 178770 : for (j=0; j<nb_pck; j++) {
1103 : u64 ts;
1104 177136 : GF_FilterPacket *pck = gf_list_get(st->pck_queue, j);
1105 177136 : if (!st->is_raw && !gf_filter_pck_get_sap(pck) ) {
1106 78199 : continue;
1107 : }
1108 98937 : ts = gf_filter_pck_get_dts(pck);
1109 98937 : if (ts==GF_FILTER_NO_TS)
1110 0 : ts = gf_filter_pck_get_cts(pck);
1111 98937 : ts += st->tk_delay;
1112 :
1113 98937 : nb_sap++;
1114 98937 : if (nb_sap <= 1 + ctx->gop_depth) {
1115 98190 : continue;
1116 : }
1117 :
1118 : last_sap_ts = ts;
1119 : break;
1120 : }
1121 : //in SAP split, flush as soon as we no longer have 2 consecutive saps
1122 2381 : if (!last_sap_ts) {
1123 1634 : if (st->in_eos && !flush_all && !st->reinsert_single_pck) {
1124 : flush_all = GF_TRUE;
1125 1541 : } else if (!st->all_saps) {
1126 : wait_for_sap = GF_TRUE;
1127 : }
1128 : }
1129 :
1130 2381 : if (st->all_saps) {
1131 1203 : if (!min_ts_a || (last_sap_ts * min_timescale_a < min_ts_a * st->timescale) ) {
1132 : min_ts_a = last_sap_ts;
1133 1118 : min_timescale_a = st->timescale;
1134 : }
1135 : } else {
1136 1178 : if (!min_ts || (last_sap_ts * min_timescale < min_ts * st->timescale) ) {
1137 : min_ts = last_sap_ts;
1138 1178 : min_timescale = st->timescale;
1139 : }
1140 : }
1141 : }
1142 :
1143 : //in size split, flush as soon as one stream is in eos
1144 1616 : if (nb_eos && has_empty_streams) {
1145 : flush_all = GF_TRUE;
1146 : }
1147 :
1148 : //if flush, get timestamp + dur of last packet in each stream and use this as final end time
1149 1616 : if (flush_all) {
1150 106 : for (i=0; i<count; i++) {
1151 : u64 ts;
1152 : GF_FilterPacket *pck;
1153 184 : RTStream *st = gf_list_get(ctx->streams, i);
1154 184 : if (!st->in_eos)
1155 : return;
1156 :
1157 106 : pck = gf_list_last(st->pck_queue);
1158 106 : if (!pck) continue;
1159 106 : u32 dur = gf_filter_pck_get_duration(pck);
1160 106 : if (!dur) dur=1;
1161 106 : ts = gf_filter_pck_get_dts(pck);
1162 106 : if (ts==GF_FILTER_NO_TS)
1163 0 : ts = gf_filter_pck_get_cts(pck);
1164 106 : ts += st->tk_delay;
1165 106 : ts += dur;
1166 106 : if (!min_ts || (ts * min_timescale > min_ts * st->timescale) ) {
1167 : min_ts = ts;
1168 102 : min_timescale = st->timescale;
1169 : }
1170 : }
1171 : }
1172 :
1173 1538 : if (!min_ts) {
1174 : //video not ready, need more input
1175 1463 : if (wait_for_sap)
1176 : return;
1177 : min_ts = min_ts_a;
1178 : min_timescale = min_timescale_a;
1179 : }
1180 512 : if (!min_ts) {
1181 : //other streams not ready, need more input
1182 7 : if (nb_eos<count)
1183 : return;
1184 : } else {
1185 505 : ctx->min_ts_scale = min_timescale;
1186 505 : ctx->min_ts_computed = min_ts;
1187 : }
1188 : }
1189 : //check all streams have reached min ts unless we are in final flush
1190 505 : if (!flush_all) {
1191 1142 : for (i=0; i<count; i++) {
1192 : u64 ts;
1193 : GF_FilterPacket *pck;
1194 1744 : RTStream *st = gf_list_get(ctx->streams, i);
1195 1744 : if (st->range_start_computed==2) continue;
1196 1744 : if (st->reinsert_single_pck) continue;
1197 1724 : pck = gf_list_last(st->pck_queue);
1198 : assert(pck);
1199 1724 : ts = gf_filter_pck_get_dts(pck);
1200 1724 : if (ts==GF_FILTER_NO_TS)
1201 0 : ts = gf_filter_pck_get_cts(pck);
1202 1724 : ts += st->tk_delay;
1203 :
1204 1724 : if (ts * ctx->min_ts_scale < ctx->min_ts_computed * st->timescale) {
1205 : return;
1206 : }
1207 : }
1208 : }
1209 :
1210 : //check condition
1211 505 : if (ctx->extract_mode==EXTRACT_SIZE) {
1212 : u32 nb_stop_at_min_ts = 0;
1213 : u64 cumulated_size = 0;
1214 : Bool use_prev = GF_FALSE;
1215 : u32 nb_eos = 0;
1216 :
1217 : //check all streams have reached min ts
1218 558 : for (i=0; i<count; i++) {
1219 : u32 j, nb_pck;
1220 : Bool found=GF_FALSE;
1221 558 : RTStream *st = gf_list_get(ctx->streams, i);
1222 558 : nb_pck = gf_list_count(st->pck_queue);
1223 :
1224 101824 : for (j=0; j<nb_pck; j++) {
1225 : u64 ts;
1226 : u32 size;
1227 101777 : GF_FilterPacket *pck = gf_list_get(st->pck_queue, j);
1228 :
1229 101777 : ts = gf_filter_pck_get_dts(pck);
1230 101777 : if (ts==GF_FILTER_NO_TS)
1231 0 : ts = gf_filter_pck_get_cts(pck);
1232 101777 : ts += st->tk_delay;
1233 :
1234 101777 : if (ts * ctx->min_ts_scale >= ctx->min_ts_computed * st->timescale) {
1235 511 : nb_stop_at_min_ts ++;
1236 : found = GF_TRUE;
1237 511 : break;
1238 : }
1239 101266 : gf_filter_pck_get_data(pck, &size);
1240 101266 : cumulated_size += size;
1241 : }
1242 558 : if ((j==nb_pck) && st->in_eos && !found) {
1243 47 : nb_eos++;
1244 : }
1245 : }
1246 : //not done yet (estimated size less than target split)
1247 495 : if (
1248 495 : (cumulated_size < ctx->split_size)
1249 487 : && ctx->min_ts_scale
1250 : //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
1251 487 : && (!ctx->prev_min_ts_computed || (ctx->prev_min_ts_computed < ctx->min_ts_computed))
1252 : ) {
1253 482 : if ((nb_stop_at_min_ts + nb_eos) == count) {
1254 482 : ctx->est_file_size = cumulated_size;
1255 482 : ctx->prev_min_ts_computed = ctx->min_ts_computed;
1256 482 : ctx->prev_min_ts_scale = ctx->min_ts_scale;
1257 482 : ctx->min_ts_computed = 0;
1258 482 : ctx->min_ts_scale = 0;
1259 482 : ctx->gop_depth++;
1260 : }
1261 : return;
1262 : }
1263 :
1264 : //decide which split size we use
1265 13 : if (ctx->xround<=REFRAME_ROUND_SEEK) {
1266 : use_prev = GF_TRUE;
1267 0 : } else if (ctx->xround==REFRAME_ROUND_AFTER) {
1268 : use_prev = GF_FALSE;
1269 : } else {
1270 0 : s64 diff_prev = (s64) ctx->split_size;
1271 : s64 diff_cur = (s64) ctx->split_size;
1272 0 : diff_prev -= (s64) ctx->est_file_size;
1273 0 : diff_cur -= (s64) cumulated_size;
1274 0 : if (ABS(diff_cur)<ABS(diff_prev))
1275 : use_prev = GF_FALSE;
1276 : else
1277 : use_prev = GF_TRUE;
1278 : }
1279 13 : if (!ctx->prev_min_ts_scale)
1280 : use_prev = GF_FALSE;
1281 :
1282 13 : if (use_prev) {
1283 : //ctx->est_file_size = ctx->est_file_size;
1284 13 : ctx->min_ts_computed = ctx->prev_min_ts_computed;
1285 13 : ctx->min_ts_scale = ctx->prev_min_ts_scale;
1286 : } else {
1287 0 : ctx->est_file_size = cumulated_size;
1288 : }
1289 13 : 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));
1290 13 : ctx->prev_min_ts_computed = 0;
1291 13 : ctx->prev_min_ts_scale = 0;
1292 : }
1293 :
1294 : //good to go
1295 23 : ctx->in_range = GF_TRUE;
1296 23 : ctx->gop_depth = 0;
1297 59 : for (i=0; i<count; i++) {
1298 : u64 ts;
1299 36 : RTStream *st = gf_list_get(ctx->streams, i);
1300 36 : GF_FilterPacket *pck = gf_list_get(st->pck_queue, 0);
1301 36 : st->range_end_reached_ts = (ctx->min_ts_computed * st->timescale);
1302 36 : if (ctx->min_ts_scale)
1303 36 : st->range_end_reached_ts /= ctx->min_ts_scale;
1304 :
1305 36 : st->range_end_reached_ts += 1;
1306 36 : st->first_pck_sent = GF_FALSE;
1307 36 : if (pck) {
1308 36 : ts = gf_filter_pck_get_dts(pck);
1309 36 : if (ts==GF_FILTER_NO_TS)
1310 0 : ts = gf_filter_pck_get_cts(pck);
1311 36 : ts += st->tk_delay;
1312 36 : st->ts_at_range_start_plus_one = ts + 1;
1313 : } else {
1314 : //this will be a eos signal
1315 0 : st->range_end_reached_ts = 0;
1316 : assert(st->range_start_computed==2);
1317 : }
1318 : }
1319 23 : ctx->cur_end.num = ctx->min_ts_computed;
1320 23 : ctx->cur_end.den = ctx->min_ts_scale;
1321 :
1322 : }
1323 :
1324 :
1325 796073 : GF_Err reframer_process(GF_Filter *filter)
1326 : {
1327 796073 : GF_ReframerCtx *ctx = gf_filter_get_udta(filter);
1328 796073 : u32 i, nb_eos, nb_end_of_range, count = gf_filter_get_ipid_count(filter);
1329 :
1330 796073 : if (ctx->eos_state) {
1331 41 : return (ctx->eos_state==2) ? GF_NOT_SUPPORTED : GF_EOS;
1332 : }
1333 796032 : if (ctx->rt) {
1334 747372 : ctx->reschedule_in = 0;
1335 747372 : ctx->clock_val = gf_sys_clock_high_res();
1336 : }
1337 :
1338 : /*active range, process as follows:
1339 : - if stream is marked as "start reached" or "end reached" do nothing
1340 : - queue up packets until we reach start range:
1341 : - if packet is in range:
1342 : - queue it (ref &nd detach from pid)
1343 : - if pck is SAP and first SAP after start and context is not yet marked "in range":
1344 : - check if we start from this SAP or from previous SAP (possibly before start) according to xround
1345 : - and mark stream as "start ready"
1346 : - if stream is video and xadjust is set, prevent all other stream processing
1347 : - if packet is out of range
1348 : - do NOT enqueue packet
1349 : - if stream was not marked as "start ready" (no SAP in active range), use previous SAP before start and mark as active
1350 : - mark as end of range reached
1351 : - if stream is video and xadjust is set, re-enable all other stream processing
1352 :
1353 : Once all streams are marked as "start ready"
1354 : - compute min time at which we will adjust the start range for all streams
1355 : - purge all packets before this time
1356 : - mark global context as "in range"
1357 :
1358 : The regular (non-range) process is then adjusted as follows:
1359 : - if context is "in range" get packet from internal queue
1360 : - if no more packets in internal queue, mark stream as "range done"
1361 :
1362 : Once all streams are marked as "range done"
1363 : - adjust next_ts of each stream
1364 : - mark each stream as not "start ready" and not "range done"
1365 : - mark context as not "in range"
1366 : - load next range and let the algo loop
1367 : */
1368 796032 : if (ctx->range_type && (ctx->range_type!=RANGE_DONE)) {
1369 : u32 nb_start_range_reached = 0;
1370 : u32 nb_not_playing = 0;
1371 : Bool check_split = GF_FALSE;
1372 :
1373 : //fetch input packets
1374 16430 : for (i=0; i<count; i++) {
1375 : u64 ts, check_ts;
1376 16500 : u32 nb_audio_samples_to_keep = 0;
1377 : u32 pck_in_range, dur;
1378 : Bool is_sap;
1379 : Bool drop_input = GF_TRUE;
1380 : GF_FilterPacket *pck;
1381 16500 : GF_FilterPid *ipid = gf_filter_get_ipid(filter, i);
1382 16500 : RTStream *st = gf_filter_pid_get_udta(ipid);
1383 :
1384 16500 : if (!st->is_playing) {
1385 0 : nb_start_range_reached++;
1386 0 : nb_not_playing++;
1387 8331 : continue;
1388 : }
1389 :
1390 16500 : if (st->range_start_computed && !ctx->wait_video_range_adjust) {
1391 3124 : nb_start_range_reached++;
1392 3124 : continue;
1393 : }
1394 : //if eos is marked we are flushing so don't check range_end
1395 13376 : if (!ctx->has_seen_eos && st->range_end_reached_ts)
1396 479 : continue;
1397 :
1398 12897 : if (st->split_pck) {
1399 210 : pck = st->split_pck;
1400 : drop_input = GF_FALSE;
1401 : } else {
1402 12687 : pck = gf_filter_pid_get_packet(ipid);
1403 : }
1404 12897 : if (!pck) {
1405 1362 : if (gf_filter_pid_is_eos(ipid)) {
1406 : //special case for PIDs with a single packet, we reinsert them at the beginning of each extracted range
1407 : //this allows dealing with BIFS/OD/JPEG/PNG tracks
1408 1362 : if (st->reinsert_single_pck) {
1409 684 : if (!ctx->in_range && !st->range_start_computed) {
1410 20 : st->range_start_computed = 3;
1411 20 : if (!gf_list_count(st->pck_queue)) {
1412 14 : pck = st->reinsert_single_pck;
1413 14 : gf_filter_pck_ref(&pck);
1414 14 : gf_list_add(st->pck_queue, pck);
1415 14 : if (!ctx->is_range_extraction) {
1416 : check_split = GF_TRUE;
1417 : }
1418 : }
1419 : }
1420 684 : if (st->range_start_computed) {
1421 20 : nb_start_range_reached++;
1422 : }
1423 684 : if (!ctx->is_range_extraction) {
1424 4 : st->in_eos = GF_TRUE;
1425 : }
1426 684 : continue;
1427 : }
1428 :
1429 678 : if (!ctx->is_range_extraction) {
1430 : check_split = GF_TRUE;
1431 668 : st->in_eos = GF_TRUE;
1432 : } else {
1433 10 : st->range_start_computed = 2;
1434 10 : if (ctx->wait_video_range_adjust && ctx->xadjust && st->needs_adjust) {
1435 4 : ctx->wait_video_range_adjust = GF_FALSE;
1436 : }
1437 : }
1438 678 : if (st->range_start_computed) {
1439 10 : nb_start_range_reached++;
1440 : }
1441 : //force flush in case of extract dur to avoid creating file with only a few samples of one track only
1442 678 : if (st->is_playing && (ctx->extract_mode==EXTRACT_DUR)) {
1443 10 : ctx->has_seen_eos = GF_TRUE;
1444 : }
1445 : }
1446 678 : continue;
1447 : }
1448 11535 : st->nb_frames_range++;
1449 :
1450 11535 : ts = gf_filter_pck_get_dts(pck);
1451 11535 : if (ts==GF_FILTER_NO_TS)
1452 0 : ts = gf_filter_pck_get_cts(pck);
1453 11535 : ts += st->tk_delay;
1454 :
1455 : //in range extraction we target the presentation time, use CTS and apply delay
1456 11535 : if (ctx->is_range_extraction) {
1457 8819 : check_ts = gf_filter_pck_get_cts(pck) + st->tk_delay;
1458 8819 : if (check_ts > st->ts_sub) check_ts -= st->ts_sub;
1459 : else check_ts = 0;
1460 : } else {
1461 : check_ts = ts;
1462 : }
1463 :
1464 : //if nosap is set, consider all packet SAPs
1465 11535 : if (ctx->nosap || st->is_raw) {
1466 : is_sap = GF_TRUE;
1467 : } else {
1468 11535 : GF_FilterSAPType sap = gf_filter_pck_get_sap(pck);
1469 11535 : if ((sap==GF_FILTER_SAP_1) || (sap==GF_FILTER_SAP_2) || (sap==GF_FILTER_SAP_3)) {
1470 : is_sap = GF_TRUE;
1471 : } else {
1472 : is_sap = GF_FALSE;
1473 : }
1474 : }
1475 :
1476 : if (!is_sap) {
1477 5973 : if (st->all_saps) {
1478 28 : st->all_saps = GF_FALSE;
1479 28 : ctx->nb_non_saps++;
1480 28 : if (ctx->nb_non_saps>1) {
1481 0 : 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));
1482 : }
1483 :
1484 28 : if (ctx->xadjust) {
1485 6 : st->needs_adjust = GF_TRUE;
1486 6 : if (st->range_start_computed==1) {
1487 0 : if (ctx->is_range_extraction) {
1488 0 : ctx->wait_video_range_adjust = GF_TRUE;
1489 : }
1490 : }
1491 : }
1492 : }
1493 : }
1494 :
1495 : //SAP or size split, push packet in queue and ask for gop split check
1496 11535 : if (!ctx->is_range_extraction) {
1497 2716 : if (gf_filter_pck_is_blocking_ref(pck)) {
1498 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[Reframer] cannot perform size/duration extraction with an input using blocking packet references (PID %s)\n\tCheck filter `%s` settings to allow for data copy\n", gf_filter_pid_get_name(st->ipid), gf_filter_pid_get_source_filter_name(st->ipid) ));
1499 0 : ctx->eos_state = 2;
1500 0 : return GF_NOT_SUPPORTED;
1501 : }
1502 : //add packet
1503 2716 : gf_filter_pck_ref(&pck);
1504 2716 : gf_filter_pid_drop_packet(st->ipid);
1505 2716 : gf_list_add(st->pck_queue, pck);
1506 : check_split = GF_TRUE;
1507 : //keep ref to first packet until we see a second one, except if blocking ref
1508 : //if blocking ref we assume the source is sending enough packets and we won't reinsert any
1509 2716 : if (!gf_filter_pck_is_blocking_ref(pck) && (st->nb_frames_range==1)) {
1510 12 : gf_filter_pck_ref(&pck);
1511 12 : st->reinsert_single_pck = pck;
1512 2704 : } else if (st->reinsert_single_pck) {
1513 10 : gf_filter_pck_unref(st->reinsert_single_pck);
1514 10 : st->reinsert_single_pck = NULL;
1515 : }
1516 2716 : continue;
1517 : }
1518 8819 : dur = gf_filter_pck_get_duration(pck);
1519 :
1520 : //dur split or range extraction but we wait for video end range to be adjusted, don't enqueue packet
1521 8819 : if (ctx->wait_video_range_adjust && !st->needs_adjust)
1522 650 : continue;
1523 :
1524 : //check if packet is in our range
1525 8169 : pck_in_range = reframer_check_pck_range(ctx, st, check_ts, dur, st->nb_frames_range, &nb_audio_samples_to_keep);
1526 :
1527 :
1528 : //SAP packet, decide if we cut here or at previous SAP
1529 8169 : if (is_sap) {
1530 : //if streamtype is video or we have only one pid, purge all packets in all streams before this time
1531 : //
1532 : //for more complex cases we keep packets because we don't know if we will need SAP packets before the final
1533 : //decided start range
1534 3637 : if (!pck_in_range && ((count==1) || !st->all_saps) ) {
1535 446 : reframer_purge_queues(ctx, ts, st->timescale);
1536 : }
1537 :
1538 : //packet in range and global context not yet in range, mark which SAP will be the beginning of our range
1539 3637 : if (!ctx->in_range && (pck_in_range==1)) {
1540 107 : if (!st->range_start_computed) {
1541 99 : u32 ts_adj = nb_audio_samples_to_keep;
1542 99 : if (ts_adj && (st->sample_rate!=st->timescale)) {
1543 0 : ts_adj *= st->timescale;
1544 0 : ts_adj /= st->sample_rate;
1545 : }
1546 :
1547 99 : if (ctx->xround==REFRAME_ROUND_CLOSEST) {
1548 : Bool cur_closer = GF_FALSE;
1549 : //check which frame is closer
1550 2 : if (ctx->start_frame_idx_plus_one) {
1551 0 : s64 diff_prev = ctx->start_frame_idx_plus_one-1;
1552 : s64 diff_cur = ctx->start_frame_idx_plus_one-1;
1553 0 : diff_prev -= st->prev_sap_frame_idx;
1554 0 : diff_cur -= st->nb_frames_range;
1555 0 : if (ABS(diff_cur) < ABS(diff_prev)) cur_closer = GF_TRUE;
1556 : } else {
1557 : s64 diff_prev, diff_cur;
1558 2 : u64 start_range_ts = ctx->cur_start.num;
1559 2 : start_range_ts *= st->timescale;
1560 2 : start_range_ts /= ctx->cur_start.den;
1561 :
1562 : diff_prev = diff_cur = start_range_ts;
1563 2 : diff_prev -= st->prev_sap_ts;
1564 2 : diff_cur -= ts+ts_adj;
1565 2 : if (ABS(diff_cur) < ABS(diff_prev)) cur_closer = GF_TRUE;
1566 : }
1567 : if (cur_closer) {
1568 2 : st->sap_ts_plus_one = ts+ts_adj+1;
1569 2 : st->nb_frames_until_start = 0;
1570 : } else {
1571 0 : st->sap_ts_plus_one = st->prev_sap_ts + 1;
1572 : }
1573 97 : } else if (ctx->xround<=REFRAME_ROUND_SEEK) {
1574 95 : st->sap_ts_plus_one = st->prev_sap_ts+1;
1575 :
1576 95 : if ((ctx->extract_mode==EXTRACT_RANGE) && !ctx->start_frame_idx_plus_one) {
1577 36 : u64 start_range_ts = ctx->cur_start.num;
1578 36 : start_range_ts *= st->timescale;
1579 36 : start_range_ts /= ctx->cur_start.den;
1580 36 : if (ts + ts_adj == start_range_ts) {
1581 0 : st->sap_ts_plus_one = ts+ts_adj+1;
1582 0 : st->nb_frames_until_start = 0;
1583 : }
1584 : }
1585 : } else {
1586 2 : st->sap_ts_plus_one = ts+ts_adj+1;
1587 2 : st->nb_frames_until_start = 0;
1588 : }
1589 99 : st->range_start_computed = 1;
1590 : }
1591 107 : nb_start_range_reached++;
1592 : }
1593 : //remember prev sap time
1594 3637 : if (pck_in_range!=2) {
1595 3566 : st->prev_sap_ts = ts;
1596 3566 : st->prev_sap_frame_idx = st->nb_frames_range;
1597 3566 : if (!st->range_start_computed && (ctx->xround==REFRAME_ROUND_SEEK) )
1598 0 : st->nb_frames_until_start = 1;
1599 : }
1600 : //video stream start and xadjust set, prevent all other streams from being processed until we determine the end of the video range
1601 : //and re-enable other streams processing
1602 3637 : if (!ctx->wait_video_range_adjust && ctx->xadjust && st->needs_adjust && !st->all_saps) {
1603 32 : ctx->wait_video_range_adjust = GF_TRUE;
1604 : }
1605 : }
1606 :
1607 8169 : if ((ctx->extract_mode==EXTRACT_DUR) && ctx->has_seen_eos && (pck_in_range==2))
1608 : pck_in_range = 1;
1609 :
1610 8161 : if (!pck_in_range && st->nb_frames_until_start)
1611 0 : st->nb_frames_until_start++;
1612 :
1613 : //video stream and not adjusting to next SAP:, packet was not sap, probe for P/B reference frame :
1614 : //we include this frame in the output but mark it as skip
1615 8169 : if (ctx->probe_ref && !ctx->xadjust && (pck_in_range==2) && (st->stream_type==GF_STREAM_VISUAL)) {
1616 2 : if (!st->probe_ref_frame_ts) {
1617 : pck_in_range = 1;
1618 1 : st->probe_ref_frame_ts = ts+1;
1619 : } else {
1620 1 : ts = st->probe_ref_frame_ts - 1;
1621 1 : st->probe_ref_frame_ts = 0;
1622 : }
1623 : }
1624 :
1625 : //after range: whether SAP or not, mark end of range reached
1626 8168 : if (pck_in_range==2) {
1627 101 : if (!ctx->xadjust || is_sap) {
1628 : Bool enqueue = GF_FALSE;
1629 88 : st->split_end = 0;
1630 88 : if (!st->range_start_computed) {
1631 82 : st->sap_ts_plus_one = st->prev_sap_ts + 1;
1632 82 : st->range_start_computed = 1;
1633 82 : nb_start_range_reached++;
1634 82 : if (st->prev_sap_ts == ts)
1635 : enqueue = GF_TRUE;
1636 : }
1637 : //remember the timestamp of first packet after range
1638 88 : st->range_end_reached_ts = ts + 1;
1639 :
1640 : //time-based extraction or dur split, try to clone packet
1641 88 : if (st->can_split && !ctx->start_frame_idx_plus_one) {
1642 8 : if ((s64) (ts * ctx->cur_end.den) < ctx->cur_end.num * st->timescale) {
1643 : //force enqueing this packet
1644 : enqueue = GF_TRUE;
1645 8 : st->split_end = (u32) ( (ctx->cur_end.num * st->timescale) / ctx->cur_end.den - ts);
1646 8 : st->range_end_reached_ts += st->split_end;
1647 : //and remember it for next chunk - note that we dequeue the input to get proper eos notification
1648 8 : gf_filter_pck_ref(&pck);
1649 8 : st->split_pck = pck;
1650 : }
1651 : }
1652 80 : else if (nb_audio_samples_to_keep && !ctx->start_frame_idx_plus_one) {
1653 : enqueue = GF_TRUE;
1654 10 : gf_filter_pck_ref(&pck);
1655 10 : st->split_pck = pck;
1656 10 : st->audio_samples_to_keep = nb_audio_samples_to_keep;
1657 : }
1658 :
1659 : //video stream end detected and xadjust set, adjust cur_end to match the video stream end range
1660 : //and re-enable other streams processing
1661 88 : if (ctx->wait_video_range_adjust && ctx->xadjust && st->needs_adjust) {
1662 27 : ctx->cur_end.num = st->range_end_reached_ts-1;
1663 27 : ctx->cur_end.den = st->timescale;
1664 27 : ctx->wait_video_range_adjust = GF_FALSE;
1665 : }
1666 :
1667 : //do NOT enqueue packet
1668 88 : if (!enqueue)
1669 : break;
1670 : }
1671 : }
1672 :
1673 : //add packet unless blocking ref
1674 8099 : if (gf_filter_pck_is_blocking_ref(pck) && !pck_in_range) {
1675 0 : st->use_blocking_refs = GF_TRUE;
1676 0 : if (drop_input)
1677 0 : gf_filter_pid_drop_packet(st->ipid);
1678 0 : continue;
1679 : }
1680 :
1681 8099 : gf_filter_pck_ref(&pck);
1682 8099 : gf_list_add(st->pck_queue, pck);
1683 8099 : if (drop_input) {
1684 8089 : gf_filter_pid_drop_packet(st->ipid);
1685 : //keep ref to first packet until we see a second one, except if blocking ref
1686 : //if blocking ref we assume the source is sending enough packets and we won't reinsert any
1687 8089 : if (!gf_filter_pck_is_blocking_ref(pck) && (st->nb_frames_range==1)) {
1688 40 : gf_filter_pck_ref(&pck);
1689 40 : st->reinsert_single_pck = pck;
1690 8049 : } else if (st->reinsert_single_pck) {
1691 34 : gf_filter_pck_unref(st->reinsert_single_pck);
1692 34 : st->reinsert_single_pck = NULL;
1693 : }
1694 : } else {
1695 : assert(pck == st->split_pck);
1696 10 : gf_filter_pck_unref(st->split_pck);
1697 10 : st->split_pck = NULL;
1698 : }
1699 : }
1700 :
1701 9359 : if (check_split) {
1702 2218 : check_gop_split(ctx);
1703 : }
1704 :
1705 : //all streams reached the start range, compute min ts
1706 9359 : if (!ctx->in_range
1707 5806 : && (nb_start_range_reached==count)
1708 5806 : && (nb_not_playing<count)
1709 71 : && ctx->is_range_extraction
1710 : ) {
1711 : u64 min_ts = 0;
1712 : u32 min_timescale=0;
1713 : u64 min_ts_a = 0;
1714 : u32 min_timescale_a=0;
1715 : u64 min_ts_split = 0;
1716 : u32 min_timescale_split=0;
1717 : Bool purge_all = GF_FALSE;
1718 116 : for (i=0; i<count; i++) {
1719 116 : GF_FilterPid *ipid = gf_filter_get_ipid(filter, i);
1720 116 : RTStream *st = gf_filter_pid_get_udta(ipid);
1721 116 : if (!st->is_playing) continue;
1722 : assert(st->range_start_computed);
1723 : //eos
1724 116 : if (st->range_start_computed==2) {
1725 3 : continue;
1726 : }
1727 : //packet will be reinserted at cut time, do not check its timestamp
1728 113 : if (st->range_start_computed==3)
1729 16 : continue;
1730 :
1731 97 : if (st->can_split) {
1732 9 : if (!min_ts_split || ((st->sap_ts_plus_one-1) * min_timescale_split < min_ts_split * st->timescale) ) {
1733 9 : min_ts_split = st->sap_ts_plus_one;
1734 9 : min_timescale_split = st->timescale;
1735 : }
1736 : }
1737 88 : else if (st->all_saps) {
1738 37 : if (!min_ts_a || ((st->sap_ts_plus_one-1) * min_timescale_a < min_ts_a * st->timescale) ) {
1739 37 : min_ts_a = st->sap_ts_plus_one;
1740 37 : min_timescale_a = st->timescale;
1741 : }
1742 : } else {
1743 51 : if (!min_ts || ((st->sap_ts_plus_one-1) * min_timescale < min_ts * st->timescale) ) {
1744 51 : min_ts = st->sap_ts_plus_one;
1745 51 : min_timescale = st->timescale;
1746 : }
1747 : }
1748 : }
1749 71 : if (!min_ts) {
1750 : min_ts = min_ts_a;
1751 : min_timescale = min_timescale_a;
1752 20 : if (!min_ts && min_ts_split) {
1753 0 : if (ctx->start_frame_idx_plus_one) {
1754 : min_ts = min_ts_split;
1755 : min_timescale = min_timescale_split;
1756 : } else {
1757 0 : min_ts = ctx->cur_start.num+1;
1758 0 : min_timescale = (u32) ctx->cur_start.den;
1759 : }
1760 : }
1761 : }
1762 71 : if (!min_ts) {
1763 : purge_all = GF_TRUE;
1764 1 : if (ctx->extract_mode==EXTRACT_RANGE) {
1765 0 : 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));
1766 : }
1767 1 : ctx->eos_state = 1;
1768 : } else {
1769 70 : min_ts -= 1;
1770 :
1771 70 : if ((ctx->extract_mode==EXTRACT_RANGE) && (ctx->xround!=REFRAME_ROUND_SEEK)) {
1772 29 : ctx->cur_start.num = min_ts;
1773 29 : ctx->cur_start.den = min_timescale;
1774 : }
1775 : }
1776 : //purge everything before min ts
1777 186 : for (i=0; i<count; i++) {
1778 : Bool start_found = GF_FALSE;
1779 116 : GF_FilterPid *ipid = gf_filter_get_ipid(filter, i);
1780 116 : RTStream *st = gf_filter_pid_get_udta(ipid);
1781 116 : if (!st->is_playing) continue;
1782 :
1783 478 : while (gf_list_count(st->pck_queue)) {
1784 477 : GF_FilterPacket *pck = gf_list_get(st->pck_queue, 0);
1785 477 : if (!purge_all) {
1786 : u32 is_start = 0;
1787 : u64 ts, ots, check_ts;
1788 : u64 dur, odur;
1789 : Bool check_priming = GF_FALSE;
1790 476 : ts = gf_filter_pck_get_dts(pck);
1791 476 : if (ts==GF_FILTER_NO_TS)
1792 0 : ts = gf_filter_pck_get_cts(pck);
1793 476 : ts += st->tk_delay;
1794 476 : odur = dur = (u64) gf_filter_pck_get_duration(pck);
1795 476 : if (!dur) dur=1;
1796 :
1797 : check_ts = ots = ts;
1798 476 : if (st->seek_mode && (st->stream_type==GF_STREAM_AUDIO) && st->ts_sub) {
1799 : check_priming = GF_TRUE;
1800 0 : check_ts += st->ts_sub;
1801 : }
1802 :
1803 476 : if (min_timescale != st->timescale) {
1804 324 : ts *= min_timescale;
1805 324 : ts /= st->timescale;
1806 324 : if (check_priming) {
1807 0 : check_ts *= min_timescale;
1808 0 : check_ts /= st->timescale;
1809 : }
1810 324 : dur *= min_timescale;
1811 324 : dur /= st->timescale;
1812 : }
1813 :
1814 476 : if (ts >= min_ts) {
1815 : is_start = 1;
1816 : }
1817 390 : else if (st->can_split && (ts+dur >= min_ts)) {
1818 : is_start = 2;
1819 : }
1820 385 : else if (check_priming && (check_ts >= min_ts)) {
1821 : is_start = 1;
1822 : }
1823 385 : else if ((st->abps || st->seek_mode) && (ts+dur >= min_ts) && (st->stream_type==GF_STREAM_AUDIO)) {
1824 : u64 diff;
1825 10 : diff = min_ts*st->timescale;
1826 10 : diff /= min_timescale;
1827 10 : diff -= ots;
1828 10 : if (st->abps) {
1829 0 : if (st->sample_rate != st->timescale) {
1830 0 : diff *= st->sample_rate;
1831 0 : diff /= st->timescale;
1832 : }
1833 : }
1834 10 : if (diff < odur) {
1835 8 : st->audio_samples_to_keep = (u32) diff;
1836 : is_start = 1;
1837 : }
1838 : }
1839 375 : else if (st->range_start_computed==3) {
1840 : is_start = 1;
1841 : }
1842 :
1843 : if (is_start) {
1844 : //remember TS at range start
1845 115 : s64 orig = min_ts;
1846 115 : if (st->timescale != min_timescale) {
1847 44 : orig *= st->timescale;
1848 44 : orig /= min_timescale;
1849 : }
1850 115 : st->split_start = 0;
1851 115 : if (is_start==2) {
1852 5 : st->split_start = (u32) (min_ts - ts);
1853 5 : if (min_timescale != st->timescale) {
1854 5 : st->split_start *= st->timescale;
1855 5 : st->split_start /= min_timescale;
1856 : }
1857 : }
1858 :
1859 115 : st->ts_at_range_start_plus_one = ots + 1;
1860 :
1861 115 : if ((st->range_start_computed==1)
1862 97 : && (orig < (s64) ots)
1863 33 : && ctx->splitrange
1864 33 : && (ctx->cur_range_idx>1)
1865 : ) {
1866 0 : s64 delay = (s64) ots - (s64) orig;
1867 0 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_DELAY, &PROP_LONGSINT(delay) );
1868 : }
1869 : start_found = GF_TRUE;
1870 : break;
1871 : }
1872 : }
1873 362 : gf_list_rem(st->pck_queue, 0);
1874 362 : gf_filter_pck_unref(pck);
1875 362 : st->nb_frames++;
1876 : }
1877 : //we couldn't find a sample with dts >= to our min_ts - this happens when the min_ts
1878 : //is located a few seconds AFTER the target split point
1879 : //so force stream to reevaluate and enqueue more packets
1880 1 : if (!start_found && !st->use_blocking_refs) {
1881 1 : st->range_start_computed = 0;
1882 1 : return GF_OK;
1883 : }
1884 : }
1885 :
1886 : //OK every stream has now packets starting at the min_ts, ready to go
1887 70 : ctx->nb_video_frames_since_start = 0;
1888 185 : for (i=0; i<count; i++) {
1889 115 : GF_FilterPid *ipid = gf_filter_get_ipid(filter, i);
1890 115 : RTStream *st = gf_filter_pid_get_udta(ipid);
1891 : //reset start range computed
1892 115 : st->range_start_computed = 0;
1893 :
1894 115 : if (ctx->extract_mode==EXTRACT_DUR) {
1895 66 : st->first_pck_sent = GF_FALSE;
1896 : } else {
1897 49 : st->first_pck_sent = ctx->splitrange ? GF_FALSE : GF_TRUE;
1898 : }
1899 :
1900 115 : if (purge_all && (ctx->extract_mode!=EXTRACT_RANGE)) {
1901 0 : gf_filter_pid_get_packet(st->ipid);
1902 0 : gf_filter_pid_set_eos(st->opid);
1903 : }
1904 :
1905 115 : if ((st->stream_type==GF_STREAM_VISUAL) && (st->nb_frames > ctx->nb_video_frames_since_start)) {
1906 53 : ctx->nb_video_frames_since_start = st->nb_frames;
1907 53 : if (st->nb_frames_until_start) {
1908 0 : ctx->nb_video_frames_since_start += st->nb_frames_until_start-1;
1909 0 : st->nb_frames_until_start = 0;
1910 : }
1911 : }
1912 : }
1913 70 : ctx->nb_video_frames_since_start_at_range_start = ctx->nb_video_frames_since_start;
1914 :
1915 70 : if (purge_all) {
1916 0 : if (ctx->extract_mode!=EXTRACT_RANGE)
1917 : return GF_EOS;
1918 :
1919 : goto load_next_range;
1920 : }
1921 :
1922 : //we are in the range
1923 70 : ctx->in_range = GF_TRUE;
1924 : }
1925 9358 : if (!ctx->in_range)
1926 : return GF_OK;
1927 : }
1928 :
1929 : nb_eos = 0;
1930 : nb_end_of_range = 0;
1931 1257684 : for (i=0; i<count; i++) {
1932 1257684 : GF_FilterPid *ipid = gf_filter_get_ipid(filter, i);
1933 1257684 : RTStream *st = gf_filter_pid_get_udta(ipid);
1934 :
1935 : while (1) {
1936 : Bool forward = GF_TRUE;
1937 : Bool pck_is_ref = GF_FALSE;
1938 : GF_FilterPacket *pck;
1939 :
1940 : //dequeue packet
1941 1320650 : if (ctx->range_type && (ctx->range_type!=RANGE_DONE) ) {
1942 13602 : pck = gf_list_get(st->pck_queue, 0);
1943 : pck_is_ref = GF_TRUE;
1944 :
1945 13602 : if (pck && !ctx->is_range_extraction && st->range_end_reached_ts) {
1946 : u64 ts;
1947 2310 : ts = gf_filter_pck_get_dts(pck);
1948 2310 : if (ts==GF_FILTER_NO_TS)
1949 0 : ts = gf_filter_pck_get_cts(pck);
1950 2310 : ts += st->tk_delay;
1951 2310 : if (ts>= st->range_end_reached_ts-1) {
1952 23 : nb_end_of_range++;
1953 23 : break;
1954 : }
1955 : }
1956 : } else {
1957 1307048 : pck = gf_filter_pid_get_packet(ipid);
1958 : }
1959 :
1960 1320627 : if (!pck) {
1961 218462 : if (st->range_end_reached_ts) {
1962 1592 : nb_end_of_range++;
1963 1592 : break;
1964 : }
1965 :
1966 216870 : if (!st->is_playing) {
1967 13 : nb_eos++;
1968 : } else {
1969 : //force a eos check if this was a split pid
1970 216857 : if (st->can_split)
1971 1203 : gf_filter_pid_get_packet(st->ipid);
1972 :
1973 216857 : if (gf_filter_pid_is_eos(ipid)) {
1974 167643 : gf_filter_pid_set_eos(st->opid);
1975 167643 : nb_eos++;
1976 : }
1977 : }
1978 : break;
1979 : }
1980 :
1981 1102165 : if (ctx->refs) {
1982 0 : u8 deps = gf_filter_pck_get_dependency_flags(pck);
1983 0 : deps >>= 2;
1984 0 : deps &= 0x3;
1985 : //not used as reference, don't forward
1986 0 : if (deps==2)
1987 : forward = GF_FALSE;
1988 : }
1989 1102165 : if (ctx->saps.nb_items) {
1990 2004 : u32 sap = gf_filter_pck_get_sap(pck);
1991 2004 : switch (sap) {
1992 82 : case GF_FILTER_SAP_1:
1993 82 : if (!ctx->filter_sap1) forward = GF_FALSE;
1994 : break;
1995 0 : case GF_FILTER_SAP_2:
1996 0 : if (!ctx->filter_sap2) forward = GF_FALSE;
1997 : break;
1998 0 : case GF_FILTER_SAP_3:
1999 0 : if (!ctx->filter_sap3) forward = GF_FALSE;
2000 : break;
2001 0 : case GF_FILTER_SAP_4:
2002 : case GF_FILTER_SAP_4_PROL:
2003 0 : if (!ctx->filter_sap4) forward = GF_FALSE;
2004 : break;
2005 1922 : default:
2006 1922 : if (!ctx->filter_sap_none) forward = GF_FALSE;
2007 : break;
2008 : }
2009 1100161 : }
2010 1102165 : if (ctx->range_type==RANGE_DONE)
2011 : forward = GF_FALSE;
2012 :
2013 1102165 : if (!forward) {
2014 1922 : reframer_drop_packet(ctx, st, pck, pck_is_ref);
2015 1922 : st->nb_frames++;
2016 1922 : continue;
2017 : }
2018 :
2019 1100243 : if (! reframer_send_packet(filter, ctx, st, pck, pck_is_ref))
2020 : break;
2021 :
2022 : }
2023 : }
2024 :
2025 : //end of range
2026 790296 : if (nb_end_of_range + nb_eos == count) {
2027 932 : load_next_range:
2028 : nb_end_of_range = 0;
2029 : nb_eos=0;
2030 1719 : for (i=0; i<count; i++) {
2031 1719 : GF_FilterPid *ipid = gf_filter_get_ipid(filter, i);
2032 1719 : RTStream *st = gf_filter_pid_get_udta(ipid);
2033 : //we reinsert the same PCK, so the ts_at_range_start_plus is always the packet cts
2034 : //we therefore need to compute the ts at and as the target end time minus the target start time
2035 1719 : if (st->reinsert_single_pck && ctx->cur_start.den) {
2036 22 : u64 start = ctx->cur_start.num;
2037 22 : start *= st->timescale;
2038 22 : start /= ctx->cur_start.den;
2039 : //closed range, compute TS at range end
2040 22 : if (ctx->cur_end.num && ctx->cur_end.den) {
2041 22 : st->ts_at_range_end = ctx->cur_end.num;
2042 22 : st->ts_at_range_end *= st->timescale;
2043 22 : st->ts_at_range_end /= ctx->cur_end.den;
2044 22 : st->ts_at_range_end -= start;
2045 : }
2046 : } else {
2047 1697 : st->ts_at_range_end += (st->range_end_reached_ts - 1) - (st->ts_at_range_start_plus_one - 1);
2048 : }
2049 1719 : st->ts_at_range_start_plus_one = 0;
2050 1719 : st->range_end_reached_ts = 0;
2051 1719 : st->range_start_computed = 0;
2052 1719 : if (st->in_eos) {
2053 20 : if (gf_list_count(st->pck_queue)) {
2054 7 : nb_end_of_range++;
2055 : } else {
2056 13 : gf_filter_pid_set_eos(st->opid);
2057 13 : nb_eos++;
2058 : }
2059 1699 : } else if (st->split_pck) {
2060 18 : nb_end_of_range++;
2061 : }
2062 : }
2063 : //and load next range
2064 932 : ctx->in_range = GF_FALSE;
2065 932 : reframer_load_range(ctx);
2066 932 : if (nb_end_of_range)
2067 18 : gf_filter_post_process_task(filter);
2068 : }
2069 :
2070 790296 : if (nb_eos==count) return GF_EOS;
2071 :
2072 790290 : if (ctx->rt) {
2073 : //while technically correct this increases the CPU load by shuffing the task around and querying gf_sys_clock_high_res too often
2074 : //needs more investigation
2075 : //using a simple callback every RT_PRECISION_US is a good workaround
2076 : #if 0
2077 : u32 rsus = 0;
2078 : if (ctx->reschedule_in > RT_PRECISION_US) {
2079 : rsus = (u32) (ctx->reschedule_in - RT_PRECISION_US);
2080 : if (rsus<RT_PRECISION_US) rsus = RT_PRECISION_US;
2081 : } else if (ctx->reschedule_in>1000) {
2082 : rsus = (u32) (ctx->reschedule_in / 2);
2083 : }
2084 : if (rsus) {
2085 : gf_filter_ask_rt_reschedule(filter, rsus);
2086 : }
2087 : #else
2088 747372 : if (ctx->reschedule_in) {
2089 745684 : gf_filter_ask_rt_reschedule(filter, RT_PRECISION_US);
2090 : }
2091 : #endif
2092 : }
2093 :
2094 : return GF_OK;
2095 : }
2096 :
2097 : static const GF_FilterCapability ReframerCaps_RAW_AV[] =
2098 : {
2099 : //raw audio and video only
2100 : CAP_UINT(GF_CAPS_INPUT_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
2101 : CAP_UINT(GF_CAPS_INPUT_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
2102 : CAP_UINT(GF_CAPS_INPUT_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_RAW),
2103 : {0},
2104 : //no restriction for media other than audio and video - cf regular caps for comments
2105 : CAP_UINT(GF_CAPS_IN_OUT_EXCLUDED, GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
2106 : CAP_UINT(GF_CAPS_IN_OUT_EXCLUDED, GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
2107 : CAP_UINT(GF_CAPS_IN_OUT_EXCLUDED, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
2108 : CAP_UINT(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
2109 : CAP_UINT(GF_CAPS_OUTPUT_EXCLUDED, GF_PROP_PID_CODECID, GF_CODECID_RAW),
2110 : CAP_UINT(GF_CAPS_OUTPUT_LOADED_FILTER, GF_PROP_PID_CODECID, GF_CODECID_RAW)
2111 : };
2112 :
2113 :
2114 : static const GF_FilterCapability ReframerCaps_RAW_A[] =
2115 : {
2116 : //raw audio only
2117 : CAP_UINT(GF_CAPS_INPUT_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
2118 : CAP_UINT(GF_CAPS_INPUT_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_RAW),
2119 : {0},
2120 : //no restriction for media other than audio - cf regular caps for comments
2121 : CAP_UINT(GF_CAPS_IN_OUT_EXCLUDED, GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
2122 : CAP_UINT(GF_CAPS_IN_OUT_EXCLUDED, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
2123 : CAP_UINT(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
2124 : CAP_UINT(GF_CAPS_OUTPUT_EXCLUDED, GF_PROP_PID_CODECID, GF_CODECID_RAW),
2125 : CAP_UINT(GF_CAPS_OUTPUT_LOADED_FILTER, GF_PROP_PID_CODECID, GF_CODECID_RAW)
2126 : };
2127 :
2128 :
2129 : static const GF_FilterCapability ReframerCaps_RAW_V[] =
2130 : {
2131 : //raw video only
2132 : CAP_UINT(GF_CAPS_INPUT_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
2133 : CAP_UINT(GF_CAPS_INPUT_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_RAW),
2134 : {0},
2135 : //no restriction for media other than video - cf regular caps for comments
2136 : CAP_UINT(GF_CAPS_IN_OUT_EXCLUDED, GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
2137 : CAP_UINT(GF_CAPS_IN_OUT_EXCLUDED, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
2138 : CAP_UINT(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
2139 : CAP_UINT(GF_CAPS_OUTPUT_EXCLUDED, GF_PROP_PID_CODECID, GF_CODECID_RAW),
2140 : CAP_UINT(GF_CAPS_OUTPUT_LOADED_FILTER, GF_PROP_PID_CODECID, GF_CODECID_RAW)
2141 : };
2142 :
2143 148 : static GF_Err reframer_initialize(GF_Filter *filter)
2144 : {
2145 : GF_Err e;
2146 148 : GF_ReframerCtx *ctx = gf_filter_get_udta(filter);
2147 :
2148 148 : ctx->streams = gf_list_new();
2149 148 : ctx->seekable = GF_TRUE;
2150 148 : if ((ctx->xs.nb_items>1) && (ctx->xround==REFRAME_ROUND_SEEK)) {
2151 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[Reframer] `xround=seek` can only be used for single range extraction\n"));
2152 : return GF_BAD_PARAM;
2153 : }
2154 :
2155 :
2156 148 : reframer_load_range(ctx);
2157 :
2158 148 : switch (ctx->raw) {
2159 1 : case RAW_AV:
2160 1 : e = gf_filter_override_caps(filter, ReframerCaps_RAW_AV, GF_ARRAY_LENGTH(ReframerCaps_RAW_AV));
2161 1 : break;
2162 0 : case RAW_VIDEO:
2163 0 : e = gf_filter_override_caps(filter, ReframerCaps_RAW_V, GF_ARRAY_LENGTH(ReframerCaps_RAW_V));
2164 0 : break;
2165 0 : case RAW_AUDIO:
2166 0 : e = gf_filter_override_caps(filter, ReframerCaps_RAW_A, GF_ARRAY_LENGTH(ReframerCaps_RAW_A));
2167 0 : break;
2168 : default:
2169 : e = GF_OK;
2170 : }
2171 : return e;
2172 : }
2173 :
2174 2020119 : static Bool reframer_process_event(GF_Filter *filter, const GF_FilterEvent *evt)
2175 : {
2176 2020119 : GF_ReframerCtx *ctx = gf_filter_get_udta(filter);
2177 : GF_FilterEvent fevt;
2178 : RTStream *st;
2179 2020119 : if (!evt->base.on_pid) return GF_FALSE;
2180 2020119 : st = gf_filter_pid_get_udta(evt->base.on_pid);
2181 2020119 : if (!st) return GF_TRUE;
2182 : //if we have a PID, we always cancel the event and forward the same event to the associated input pid
2183 2020119 : fevt = *evt;
2184 2020119 : fevt.base.on_pid = st->ipid;
2185 :
2186 : //if range extraction based on time, adjust start range
2187 2020119 : if (evt->base.type==GF_FEVT_PLAY) {
2188 188 : if (ctx->range_type && !ctx->start_frame_idx_plus_one) {
2189 51 : Double start_range = (Double) ctx->cur_start.num;
2190 51 : start_range /= ctx->cur_start.den;
2191 : //rewind safety offset
2192 51 : if (start_range > ctx->seeksafe)
2193 0 : start_range -= ctx->seeksafe;
2194 : else
2195 : start_range = 0.0;
2196 :
2197 51 : fevt.play.start_range = start_range;
2198 : }
2199 188 : st->in_eos = GF_FALSE;
2200 188 : st->is_playing = GF_TRUE;
2201 188 : if (ctx->eos_state==1)
2202 0 : ctx->eos_state = 0;
2203 2019931 : } else if (evt->base.type==GF_FEVT_STOP) {
2204 2 : st->is_playing = GF_FALSE;
2205 : }
2206 :
2207 2020119 : gf_filter_pid_send_event(st->ipid, &fevt);
2208 2020119 : return GF_TRUE;
2209 : }
2210 :
2211 148 : static void reframer_finalize(GF_Filter *filter)
2212 : {
2213 148 : GF_ReframerCtx *ctx = gf_filter_get_udta(filter);
2214 :
2215 483 : while (gf_list_count(ctx->streams)) {
2216 187 : RTStream *st = gf_list_pop_back(ctx->streams);
2217 187 : reframer_reset_stream(ctx, st);
2218 : }
2219 148 : gf_list_del(ctx->streams);
2220 148 : }
2221 :
2222 : static const GF_FilterCapability ReframerCaps[] =
2223 : {
2224 : CAP_UINT(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
2225 : //we do accept everything, including raw streams
2226 : CAP_UINT(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_CODECID, GF_CODECID_NONE),
2227 : CAP_UINT(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
2228 : //we don't accept files as input so don't output them
2229 : CAP_UINT(GF_CAPS_OUTPUT_EXCLUDED, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
2230 : //we don't produce RAW streams during dynamic chain resolution - this will avoid loading the filter for compositor/other raw access
2231 : CAP_UINT(GF_CAPS_OUTPUT_EXCLUDED, GF_PROP_PID_CODECID, GF_CODECID_RAW),
2232 : //but we may produce raw streams when filter is explicitly loaded (media exporter)
2233 : CAP_UINT(GF_CAPS_OUTPUT_LOADED_FILTER, GF_PROP_PID_CODECID, GF_CODECID_RAW)
2234 : };
2235 :
2236 :
2237 : #define OFFS(_n) #_n, offsetof(GF_ReframerCtx, _n)
2238 : static const GF_FilterArgs ReframerArgs[] =
2239 : {
2240 : { OFFS(exporter), "compatibility with old exporter, displays export results", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
2241 : { OFFS(rt), "real-time regulation mode of input\n"
2242 : "- off: disables real-time regulation\n"
2243 : "- on: enables real-time regulation, one clock per pid\n"
2244 : "- sync: enables real-time regulation one clock for all pids", GF_PROP_UINT, "off", "off|on|sync", GF_FS_ARG_HINT_NORMAL},
2245 : { 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},
2246 : { 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},
2247 : { OFFS(speed), "speed for real-time regulation mode - only positive value", GF_PROP_DOUBLE, "1.0", NULL, GF_FS_ARG_HINT_ADVANCED},
2248 : { OFFS(raw), "force input AV streams to be in raw format\n"
2249 : "- no: do not force decoding of inputs\n"
2250 : "- av: force decoding of audio and video inputs\n"
2251 : "- a: force decoding of audio inputs\n"
2252 : "- v: force decoding of video inputs", GF_PROP_UINT, "no", "av|a|v|no", GF_FS_ARG_HINT_NORMAL},
2253 : { OFFS(frames), "drop all except listed frames (first being 1), off by default", GF_PROP_UINT_LIST, NULL, NULL, GF_FS_ARG_HINT_ADVANCED},
2254 : { OFFS(xs), "extraction start time(s), see filter help", GF_PROP_STRING_LIST, NULL, NULL, GF_FS_ARG_HINT_NORMAL},
2255 : { 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},
2256 : { OFFS(xround), "adjust start time of extraction range to I-frame\n"
2257 : "- before: use first I-frame preceding or matching range start\n"
2258 : "- seek: see filter help\n"
2259 : "- after: use first I-frame (if any) following or matching range start\n"
2260 : "- closest: use I-frame closest to range start", GF_PROP_UINT, "before", "before|seek|after|closest", GF_FS_ARG_HINT_ADVANCED},
2261 : { OFFS(xadjust), "adjust end time of extraction range to be before next I-frame", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
2262 : { 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},
2263 : { 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},
2264 : { OFFS(seeksafe), "rewind play requests by given seconds (to make sur I-frame preceding start is catched)", GF_PROP_DOUBLE, "10.0", NULL, GF_FS_ARG_HINT_EXPERT},
2265 : { OFFS(tcmdrw), "rewrite TCMD samples when splitting", GF_PROP_BOOL, "true", NULL, GF_FS_ARG_HINT_EXPERT},
2266 : { OFFS(props), "extra output PID properties per extraction range", GF_PROP_STRING_LIST, NULL, NULL, GF_FS_ARG_HINT_EXPERT},
2267 : { OFFS(no_audio_seek), "disable seek mode on audio streams (no change of priming duration) - see filter help", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
2268 : { OFFS(probe_ref), "allow extracted range to be longer in case of B-frames with reference frames presented outside of range", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
2269 : {0}
2270 : };
2271 :
2272 : GF_FilterRegister ReframerRegister = {
2273 : .name = "reframer",
2274 : GF_FS_SET_DESCRIPTION("Media Reframer")
2275 : GF_FS_SET_HELP("This filter provides various compressed domain tools on inputs:\n"
2276 : "- ensure reframing\n"
2277 : "- optionally force decoding\n"
2278 : "- real-time regulation\n"
2279 : "- packet filtering based on SAP types or frame numbers\n"
2280 : "- time-range extraction and splitting\n"
2281 : "This filter forces input pids to be properly framed (1 packet = 1 Access Unit).\n"
2282 : "It is typcially needed to force remultiplexing in file to file operations when source and destination files use the same format.\n"
2283 : " \n"
2284 : "# SAP filtering\n"
2285 : "The filter can remove packets based on their SAP types using [-saps]() option.\n"
2286 : "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"
2287 : " \n"
2288 : "# Frame filtering\n"
2289 : "This filter can keep only specific Access Units of the source using [-frames]() option.\n"
2290 : "For example, this can be used to extract only specific key frame of a video to create a HEIF collection.\n"
2291 : " \n"
2292 : "# Frame decoding\n"
2293 : "This filter can force input media streams to be decoded using the [-raw]() option.\n"
2294 : "EX gpac src=m.mp4 reframer:raw=av @ [dst]\n"
2295 : "# Real-time Regulation\n"
2296 : "The filter can perform real-time regulation of input packets, based on their timescale and timestamps.\n"
2297 : "For example to simulate a live DASH:\n"
2298 : "EX gpac src=m.mp4 reframer:rt=on @ dst=live.mpd:dynamic\n"
2299 : " \n"
2300 : "# Range extraction\n"
2301 : "The filter can perform time range extraction of the source using [-xs]() and [-xe]() options.\n"
2302 : "The formats allowed for times specifiers are:\n"
2303 : "- 'T'H:M:S, 'T'M:S: specify time in hours, minutes, seconds\n"
2304 : "- 'T'H:M:S.MS, 'T'M:S.MS, 'T'S.MS: specify time in hours, minutes, seconds and milliseconds\n"
2305 : "- INT, FLOAT: specify time in seconds\n"
2306 : "- NUM/DEN: specify time in seconds as fraction\n"
2307 : "- 'F'NUM: specify time as frame number\n"
2308 : "In this mode, the timestamps are rewritten to form a continuous timeline.\n"
2309 : "When multiple ranges are given, the filter will try to seek if needed and supported by source.\n"
2310 : "\n"
2311 : "EX gpac src=m.mp4 reframer:xs=T00:00:10,T00:01:10,T00:02:00:xe=T00:00:20,T00:01:20 [dst]\n"
2312 : "This will extract the time ranges [10s,20s], [1m10s,1m20s] and all media starting from 2m\n"
2313 : "\n"
2314 : "If no end range is found for a given start range:\n"
2315 : "- if a following start range is set, the end range is set to this next start\n"
2316 : "- otherwise, the end range is open\n"
2317 : "\n"
2318 : "EX gpac src=m.mp4 reframer:xs=0,10,25:xe=5 [dst]\n"
2319 : "This will extract the time ranges [0s,5s], [10s,25s] and all media starting from 25s\n"
2320 : "EX gpac src=m.mp4 reframer:xs=0,10,25 [dst]\n"
2321 : "This will extract the time ranges [0s,10s], [10s,25s] and all media starting from 25s\n"
2322 : "\n"
2323 : "It is possible to signal range boundaries in output packets using [-splitrange]().\n"
2324 : "This will expose on the first packet of each range in each pid the following properties:\n"
2325 : "- FileNumber: starting at 1 for the first range, to be used as replacement for $num$ in templates\n"
2326 : "- FileSuffix: corresponding to `StartRange_EndRange` or `StartRange` for open ranges, to be used as replacement for $FS$ in templates\n"
2327 : "\n"
2328 : "EX gpac src=m.mp4 reframer:xs=T00:00:10,T00:01:10:xe=T00:00:20:splitrange -o dump_$FS$.264\n"
2329 : "This will create two output files dump_T00.00.10_T00.02.00.264 and dump_T00.01.10.264.\n"
2330 : "Note: The `:` and `/` characters are replaced by `.` in `FileSuffix` property.\n"
2331 : "\n"
2332 : "It is possible to modify PID properties per range using [-props](). Each set of property must be specified using the active separator set.\n"
2333 : "EX gpac src=m.mp4 reframer:xs=0,30:props=#Period=P1,#Period=P2:#foo=bar\n"
2334 : "This will assign to output PIDs\n"
2335 : "- during the range [0,30]: property `Period` to `P1`\n"
2336 : "- during the range [30, end]: properties `Period` to `P2` and property `foo` to `bar`\n"
2337 : "\n"
2338 : "For uncompressed audio pids, input frame will be split to closest audio sample number.\n"
2339 : "\n"
2340 : "When [-xround]() is set to `seek`, the following applies:\n"
2341 : "- a single range shall be specified\n"
2342 : "- the first I-frame preceding or matching the range start is used as split point\n"
2343 : "- all packets before range start are marked as seek points\n"
2344 : "- packets overlapping range start are forwarded with a `SkipBegin` property set to the amount of media to skip\n"
2345 : "- packets overlapping range end are forwarded with an adjusted duration to match the range end\n"
2346 : "This mode is typically used to extract a range in a frame/sample accurate way, rather than a GOP-aligned way.\n"
2347 : "\n"
2348 : "When [-xround]() is not set to `seek`, compressed audio streams will still use seek mode.\n"
2349 : "Consequently, these streams will have modified edit lists in ISOBMFF which might not be properly handled by players.\n"
2350 : "This can be avoided using [-no_audio_seek](), but this will introduce audio delay.\n"
2351 : "\n"
2352 : "# Other split actions\n"
2353 : "The filter can perform splitting of the source using [-xs]() option.\n"
2354 : "The additional formats allowed for [-xs]() option are:\n"
2355 : "- 'SAP': split source at each SAP/RAP\n"
2356 : "- 'D'VAL: split source by chunks of VAL ms\n"
2357 : "- 'D'NUM/DEN: split source by chunks of NUM/DEN seconds\n"
2358 : "- 'S'VAL: split source by chunks of estimated size VAL bytes, VAL can use property multipliers\n"
2359 : "\n"
2360 : "Note: In these modes, [-splitrange]() and [-xadjust]() are implicitly set.\n"
2361 : )
2362 : .private_size = sizeof(GF_ReframerCtx),
2363 : .max_extra_pids = (u32) -1,
2364 : .args = ReframerArgs,
2365 : //reframer is explicit only, so we don't load the reframer during resolution process
2366 : .flags = GF_FS_REG_EXPLICIT_ONLY,
2367 : SETCAPS(ReframerCaps),
2368 : .initialize = reframer_initialize,
2369 : .finalize = reframer_finalize,
2370 : .configure_pid = reframer_configure_pid,
2371 : .process = reframer_process,
2372 : .process_event = reframer_process_event,
2373 : };
2374 :
2375 :
2376 2877 : const GF_FilterRegister *reframer_register(GF_FilterSession *session)
2377 : {
2378 2877 : return &ReframerRegister;
2379 : }
|