LCOV - code coverage report
Current view: top level - filters - reframer.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 868 1071 81.0 %
Date: 2021-04-29 23:48:07 Functions: 15 16 93.8 %

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

Generated by: LCOV version 1.13