LCOV - code coverage report
Current view: top level - filters - isoffin_read.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 620 784 79.1 %
Date: 2021-04-29 23:48:07 Functions: 17 19 89.5 %

          Line data    Source code
       1             : /*
       2             :  *                      GPAC - Multimedia Framework C SDK
       3             :  *
       4             :  *                      Authors: Jean Le Feuvre
       5             :  *                      Copyright (c) Telecom ParisTech 2000-2021
       6             :  *                                      All rights reserved
       7             :  *
       8             :  *  This file is part of GPAC / ISOBMFF reader 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 "isoffin.h"
      27             : 
      28             : #ifndef GPAC_DISABLE_ISOM
      29             : 
      30             : #include <gpac/crypt_tools.h>
      31             : #include <gpac/media_tools.h>
      32             : 
      33           0 : ISOMChannel *isor_get_channel(ISOMReader *reader, GF_FilterPid *pid)
      34             : {
      35       76195 :         u32 i=0;
      36             :         ISOMChannel *ch;
      37      235616 :         while ((ch = (ISOMChannel *)gf_list_enum(reader->channels, &i))) {
      38      162041 :                 if (ch->pid == pid) return ch;
      39             :         }
      40             :         return NULL;
      41             : }
      42             : 
      43             : 
      44         677 : static GFINLINE Bool isor_is_local(const char *url)
      45             : {
      46         677 :         if (!strnicmp(url, "file://", 7)) return GF_TRUE;
      47         677 :         if (!strnicmp(url, "gmem://", 7)) return GF_TRUE;
      48         623 :         if (!strnicmp(url, "gfio://", 7)) return GF_TRUE;
      49         622 :         if (!strnicmp(url, "isobmff://", 10)) return GF_TRUE;
      50         617 :         if (strstr(url, "://")) return GF_FALSE;
      51             :         /*the rest is local (mounted on FS)*/
      52         617 :         return GF_TRUE;
      53             : }
      54             : 
      55             : 
      56         677 : static GF_Err isoffin_setup(GF_Filter *filter, ISOMReader *read)
      57             : {
      58             :         char szURL[2048];
      59             :         char *tmp, *src;
      60             :         GF_Err e;
      61             :         const GF_PropertyValue *prop;
      62         677 :         if (!read) return GF_SERVICE_ERROR;
      63             : 
      64         677 :         if (read->pid) {
      65         677 :                 prop = gf_filter_pid_get_property(read->pid, GF_PROP_PID_FILEPATH);
      66             :                 assert(prop);
      67         677 :                 src = prop->value.string;
      68             :         } else {
      69           0 :                 src = read->src;
      70             :         }
      71         677 :         if (!src)  return GF_SERVICE_ERROR;
      72             : 
      73         677 :         read->src_crc = gf_crc_32(src, (u32) strlen(src));
      74             : 
      75             :         strcpy(szURL, src);
      76         677 :         tmp = gf_file_ext_start(szURL);
      77         677 :         if (tmp) {
      78             :                 Bool truncate = GF_TRUE;
      79         618 :                 tmp = strchr(tmp, '#');
      80         618 :                 if (!tmp && read->pid) {
      81         617 :                         prop = gf_filter_pid_get_property(read->pid, GF_PROP_PID_URL);
      82         617 :                         if (prop && prop->value.string) {
      83         617 :                                 tmp = gf_file_ext_start(prop->value.string);
      84         617 :                                 if (tmp) tmp = strchr(tmp, '#');
      85             :                                 truncate = GF_FALSE;
      86             :                         }
      87             :                 }
      88         618 :                 if (tmp) {
      89           1 :                         if (!strnicmp(tmp, "#audio", 6)) {
      90           0 :                                 read->play_only_first_media = GF_ISOM_MEDIA_AUDIO;
      91           1 :                         } else if (!strnicmp(tmp, "#video", 6)) {
      92           1 :                                 read->play_only_first_media = GF_ISOM_MEDIA_VISUAL;
      93           0 :                         } else if (!strnicmp(tmp, "#auxv", 5)) {
      94           0 :                                 read->play_only_first_media = GF_ISOM_MEDIA_AUXV;
      95           0 :                         } else if (!strnicmp(tmp, "#pict", 5)) {
      96           0 :                                 read->play_only_first_media = GF_ISOM_MEDIA_PICT;
      97           0 :                         } else if (!strnicmp(tmp, "#text", 5)) {
      98           0 :                                 read->play_only_first_media = GF_ISOM_MEDIA_TEXT;
      99           0 :                         } else if (!strnicmp(tmp, "#trackID=", 9)) {
     100           0 :                                 read->play_only_track_id = atoi(tmp+9);
     101           0 :                         } else if (!strnicmp(tmp, "#ID=", 4)) {
     102           0 :                                 read->play_only_track_id = atoi(tmp+4);
     103             :                         } else {
     104           0 :                                 read->play_only_track_id = atoi(tmp+1);
     105             :                         }
     106           1 :                         if (truncate) tmp[0] = 0;
     107             :                 }
     108             :         }
     109             : 
     110         677 :         if (! isor_is_local(szURL)) {
     111             :                 return GF_NOT_SUPPORTED;
     112             :         }
     113         677 :         read->start_range = read->end_range = 0;
     114         677 :         prop = gf_filter_pid_get_property(read->pid, GF_PROP_PID_FILE_RANGE);
     115         677 :         if (prop) {
     116          14 :                 read->start_range = prop->value.lfrac.num;
     117          14 :                 read->end_range = prop->value.lfrac.den;
     118             :         }
     119             : 
     120         677 :         e = gf_isom_open_progressive(szURL, read->start_range, read->end_range, read->sigfrag, &read->mov, &read->missing_bytes);
     121             : 
     122         677 :         if (e == GF_ISOM_INCOMPLETE_FILE) {
     123          25 :                 read->moov_not_loaded = 1;
     124          25 :                 return GF_OK;
     125             :         }
     126         652 :         read->input_loaded = GF_TRUE;
     127             : 
     128         652 :         if (e != GF_OK) {
     129           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[IsoMedia] error while opening %s, error=%s\n", szURL,gf_error_to_string(e)));
     130           0 :                 gf_filter_setup_failure(filter, e);
     131           0 :                 return e;
     132             :         }
     133         652 :         read->frag_type = gf_isom_is_fragmented(read->mov) ? 1 : 0;
     134         652 :     if (!read->frag_type && read->sigfrag) {
     135             :         e = GF_BAD_PARAM;
     136           0 :         GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[IsoMedia] sigfrag requested but file %s is not fragmented\n", szURL));
     137           0 :         gf_filter_setup_failure(filter, e);
     138           0 :         return e;
     139             :     }
     140             :     
     141         652 :         read->time_scale = gf_isom_get_timescale(read->mov);
     142         652 :         if (!read->input_loaded && read->frag_type)
     143           0 :                 read->refresh_fragmented = GF_TRUE;
     144             : 
     145         652 :         if (read->strtxt)
     146           1 :                 gf_isom_text_set_streaming_mode(read->mov, GF_TRUE);
     147             : 
     148         652 :         return isor_declare_objects(read);
     149             : }
     150             : 
     151        1184 : static void isoffin_delete_channel(ISOMChannel *ch)
     152             : {
     153        1184 :         isor_reset_reader(ch);
     154        1184 :         if (ch->nal_bs) gf_bs_del(ch->nal_bs);
     155        1184 :         if (ch->avcc) gf_odf_avc_cfg_del(ch->avcc);
     156        1184 :         if (ch->hvcc) gf_odf_hevc_cfg_del(ch->hvcc);
     157        1184 :         if (ch->vvcc) gf_odf_vvc_cfg_del(ch->vvcc);
     158        1184 :         gf_free(ch);
     159        1184 : }
     160             : 
     161          13 : static void isoffin_disconnect(ISOMReader *read)
     162             : {
     163          13 :         read->disconnected = GF_TRUE;
     164          57 :         while (gf_list_count(read->channels)) {
     165          31 :                 ISOMChannel *ch = (ISOMChannel *)gf_list_get(read->channels, 0);
     166          31 :                 gf_list_rem(read->channels, 0);
     167          31 :                 if (ch->pid)
     168          31 :                         gf_filter_pid_remove(ch->pid);
     169          31 :                 isoffin_delete_channel(ch);
     170             :         }
     171             : 
     172          13 :         if (read->mov) gf_isom_close(read->mov);
     173          13 :         read->mov = NULL;
     174             : 
     175          13 :         read->pid = NULL;
     176          13 : }
     177             : 
     178        3013 : static GF_Err isoffin_reconfigure(GF_Filter *filter, ISOMReader *read, const char *next_url)
     179             : {
     180             :         const GF_PropertyValue *prop;
     181             :         u32 i, count;
     182             :         Bool is_new_mov = GF_FALSE;
     183             :         u64 tfdt;
     184             : //      GF_ISOTrackID trackID;
     185             :         GF_ISOSegOpenMode flags=0;
     186             :         GF_Err e;
     187             : 
     188        3013 :         prop = gf_filter_pid_get_property(read->pid, GF_PROP_PID_FILE_CACHED);
     189        3013 :         if (prop && prop->value.boolean)
     190        2515 :                 read->input_loaded = GF_TRUE;
     191             : 
     192        3013 :         read->refresh_fragmented = GF_FALSE;
     193        3013 :         read->full_segment_flush = GF_TRUE;
     194        3013 :         GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[IsoMedia] reconfigure triggered, URL %s\n", next_url));
     195             : 
     196             :         //no need to lock blob if next_url is a blob, all parsing and probing functions below will lock the blob if any
     197             : 
     198        3013 :         switch (gf_isom_probe_file_range(next_url, read->start_range, read->end_range)) {
     199             :         //this is a fragment
     200        2961 :         case 3:
     201        2961 :                 gf_isom_release_segment(read->mov, 1);
     202        2961 :                 gf_isom_reset_fragment_info(read->mov, GF_TRUE);
     203             : 
     204        2961 :                 if (read->no_order_check) flags |= GF_ISOM_SEGMENT_NO_ORDER_FLAG;
     205             : #ifdef FILTER_FIXME
     206             :                 if (scalable_segment) flags |= GF_ISOM_SEGMENT_SCALABLE_FLAG;
     207             : #endif
     208        2961 :                 e = gf_isom_open_segment(read->mov, next_url, read->start_range, read->end_range, flags);
     209        2961 :                 if (!read->input_loaded && (e==GF_ISOM_INCOMPLETE_FILE)) {
     210             :                         e = GF_OK;
     211             :                 }
     212             :                 //always refresh fragmented files, since we could have a full moof+mdat in buffer (not incomplete file)
     213             :                 //but still further fragments to be pushed
     214        2961 :                 if (!read->start_range && !read->end_range)
     215        2680 :                         read->refresh_fragmented = GF_TRUE;
     216        2961 :                 read->seg_name_changed = GF_TRUE;
     217             : 
     218       20516 :                 for (i=0; i<gf_list_count(read->channels); i++) {
     219       17555 :                         ISOMChannel *ch = gf_list_get(read->channels, i);
     220       17555 :                         if (ch->last_state==GF_EOS)
     221       17555 :                                 ch->last_state=GF_OK;
     222             :                 }
     223             : 
     224             : #ifndef GPAC_DISABLE_LOG
     225        2961 :                 if (e<0) {
     226           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[IsoMedia] Error opening new segment %s at UTC "LLU": %s\n", next_url, gf_net_get_utc(), gf_error_to_string(e) ));
     227        2961 :                 } else if (read->end_range) {
     228         281 :                         GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[IsoMedia] Playing new range in %s: "LLU"-"LLU"\n", next_url, read->start_range, read->end_range));
     229             :                 } else {
     230        2680 :                         GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[IsoMedia] playing new segment %s\n", next_url));
     231             :                 }
     232             : #endif
     233             :                 break;
     234             :         //this is a movie, reload
     235          52 :         case 2:
     236             :         case 1:
     237             :                 //get tfdt of next segment (cumulated sample dur since moov load)
     238             :                 //if the next segment has a tfdt or a tfrx, this will be ignored
     239             :                 //otherwise this value will be used as base tfdt for next segment
     240          52 :                 tfdt = gf_isom_get_smooth_next_tfdt(read->mov, 1);
     241          52 :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[IsoMedia] Switching between files - opening new init segment %s (time offset="LLU") - range "LLU"-"LLU"\n", next_url, tfdt, read->start_range, read->end_range));
     242             : 
     243          52 :                 if (gf_isom_is_smooth_streaming_moov(read->mov)) {
     244           1 :                         char *tfdt_val = strstr(next_url, "tfdt=");
     245             :                         //smooth addressing, replace tfdt=0000000000000000 with proper value
     246           1 :                         if (tfdt_val) {
     247           1 :                                 sprintf(tfdt_val+5, LLX, tfdt);
     248             :                         } else {
     249           0 :                                 GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[IsoMedia] Error finding init time for init segment %s at UTC "LLU"\n", next_url, gf_net_get_utc() ));
     250             :                         }
     251             :                 }
     252             : 
     253          52 :                 if (read->mov) gf_isom_close(read->mov);
     254          52 :                 e = gf_isom_open_progressive(next_url, read->start_range, read->end_range, read->sigfrag, &read->mov, &read->missing_bytes);
     255             : 
     256             :                 //init seg not completely downloaded, retry at next packet
     257          52 :                 if (!read->input_loaded && (e==GF_ISOM_INCOMPLETE_FILE)) {
     258           0 :                         read->src_crc = 0;
     259           0 :                         read->moov_not_loaded = 2;
     260           0 :                         return GF_OK;
     261             :                 }
     262             : 
     263          52 :                 read->moov_not_loaded = 0;
     264          52 :                 if (e < 0) {
     265           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[IsoMedia] Error opening init segment %s at UTC "LLU": %s\n", next_url, gf_net_get_utc(), gf_error_to_string(e) ));
     266             :                 }
     267          52 :                 if (read->sigfrag)
     268           0 :                         gf_isom_enable_traf_map_templates(read->mov);
     269             : 
     270             :                 is_new_mov = GF_TRUE;
     271             :                 break;
     272             :         //empty file
     273             :         case 4:
     274             :                 return GF_OK;
     275           0 :         default:
     276           0 :                 if (!read->mov) {
     277             :             return GF_NOT_SUPPORTED;
     278             :                 }
     279             :         e = GF_ISOM_INVALID_FILE;
     280             :         break;
     281             :         }
     282             : 
     283        3013 :         gf_filter_post_process_task(filter);
     284             : 
     285        3013 :         count = gf_list_count(read->channels);
     286             :         
     287        3013 :         if (e<0) {
     288           0 :                 count = gf_list_count(read->channels);
     289           0 :                 gf_isom_release_segment(read->mov, 1);
     290           0 :         read->invalid_segment = GF_TRUE;
     291             :                 //error opening the segment, reset everything ...
     292           0 :                 gf_isom_reset_fragment_info(read->mov, GF_FALSE);
     293           0 :                 for (i=0; i<count; i++) {
     294           0 :                         ISOMChannel *ch = gf_list_get(read->channels, i);
     295           0 :             if (ch) {
     296           0 :                 ch->sample_num = 0;
     297           0 :                 ch->eos_sent = GF_FALSE;
     298             :             }
     299             :                 }
     300           0 :         GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[IsoMedia] Error opening current segment %s: %s\n", next_url, gf_error_to_string(e) ));
     301             :                 return GF_OK;
     302             :         }
     303             :         //segment is the first in our cache, we may need a refresh
     304        3013 :         if (!read->input_loaded) {
     305         498 :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[IsoMedia] Opening current segment in progressive mode (download in progress)\n"));
     306             :         } else {
     307        2515 :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[IsoMedia] Opening current segment in non-progressive mode (completely downloaded)\n"));
     308             :         }
     309             : 
     310        3013 :         isor_check_producer_ref_time(read);
     311             : 
     312       20620 :         for (i=0; i<count; i++) {
     313       17607 :                 ISOMChannel *ch = gf_list_get(read->channels, i);
     314       17607 :                 ch->last_state = GF_OK;
     315       17607 :                 ch->eos_sent = GF_FALSE;
     316             : 
     317             :                 //old code from master, currently no longer used
     318             :                 //in filters we don't use extractors for the time being, we only do implicit reconstruction at the decoder side
     319             : #if 0
     320             :                 if (ch->base_track) {
     321             :                         if (scalable_segment)
     322             :                                 trackID = gf_isom_get_highest_track_in_scalable_segment(read->mov, ch->base_track);
     323             :                                 if (trackID) {
     324             :                                         ch->track_id = trackID;
     325             :                                         ch->track = gf_isom_get_track_by_id(read->mov, ch->track_id);
     326             :                                 }
     327             :                         } else {
     328             :                                 ch->track = ch->base_track;
     329             :                                 ch->track_id = gf_isom_get_track_id(read->mov, ch->track);
     330             :                         }
     331             :                 }
     332             : #endif
     333             : 
     334       17607 :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[IsoMedia] Track %d - cur sample %d - new sample count %d\n", ch->track, ch->sample_num, gf_isom_get_sample_count(ch->owner->mov, ch->track) ));
     335             : 
     336             :                 //TODO: signal all discontinuities here
     337       17607 :                 if (is_new_mov) {
     338          52 :                         ch->track = gf_isom_get_track_by_id(read->mov, ch->track_id);
     339          52 :                         if (!ch->track) {
     340           0 :                                 if (gf_isom_get_track_count(read->mov)==1) {
     341           0 :                                         GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[IsoMedia] Mismatch between track IDs of different representations\n"));
     342           0 :                                         ch->track = 1;
     343             :                                 } else {
     344           0 :                                         GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[IsoMedia] Mismatch between track IDs of different representations\n"));
     345             :                                 }
     346             :                         }
     347             : 
     348             :                         /*we changed our moov structure, sample_num now starts from 0*/
     349          52 :                         ch->sample_num = 0;
     350             :                         //this may happen if we reload moov before initializing the channel
     351          52 :                         if (!ch->last_sample_desc_index)
     352           1 :                                 ch->last_sample_desc_index = 1;
     353             :                         //and update channel config
     354          52 :                         isor_update_channel_config(ch);
     355             : 
     356             :                         /*restore NAL extraction mode*/
     357          52 :                         gf_isom_set_nalu_extract_mode(read->mov, ch->track, ch->nalu_extract_mode);
     358             : 
     359          52 :                         if (ch->is_cenc) {
     360           7 :                                 isor_set_crypt_config(ch);
     361             :                         }
     362             :                 }
     363             : 
     364       17607 :                 ch->last_state = GF_OK;
     365             :         }
     366             :         return e;
     367             : }
     368             : 
     369        3682 : GF_Err isoffin_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
     370             : {
     371             :         const GF_PropertyValue *prop;
     372        3682 :         ISOMReader *read = gf_filter_get_udta(filter);
     373             : 
     374        3682 :         if (is_remove) {
     375          13 :                 isoffin_disconnect(read);
     376          13 :                 return GF_OK;
     377             :         }
     378             :         //check if we  have a file path; if not, this is a pure stream of boxes (no local file cache)
     379        3669 :         prop = gf_filter_pid_get_property(pid, GF_PROP_PID_FILEPATH);
     380        3669 :         if (!prop || !prop->value.string) {
     381           4 :                 if (!read->mem_load_mode)
     382           4 :                         read->mem_load_mode = 1;
     383           4 :                 if (!read->pid) read->pid = pid;
     384           4 :                 read->input_loaded = GF_FALSE;
     385           4 :                 return GF_OK;
     386             :         }
     387             : 
     388        3665 :         if (read->pid && prop->value.string) {
     389             :                 const char *next_url = prop->value.string;
     390             :                 u64 sr, er;
     391        3013 :                 u32 crc = gf_crc_32(next_url, (u32) strlen(next_url) );
     392             : 
     393             :                 sr = er = 0;
     394        3013 :                 prop = gf_filter_pid_get_property(read->pid, GF_PROP_PID_FILE_RANGE);
     395        3013 :                 if (prop) {
     396         281 :                         sr = prop->value.lfrac.num;
     397         281 :                         er = prop->value.lfrac.den;
     398             :                 }
     399             : 
     400             :                 //if eos is signaled, don't check for crc since we might have the same blob address (same alloc)
     401        3013 :                 if (!read->eos_signaled && (read->src_crc == crc) && (read->start_range==sr) && (read->end_range==er)) {
     402           0 :                         GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[IsoMedia] same URL crc and range for %s, skipping reconfigure\n", next_url));
     403             :                         return GF_OK;
     404             :                 }
     405        3013 :                 read->src_crc = crc;
     406        3013 :                 read->start_range = sr;
     407        3013 :                 read->end_range = er;
     408        3013 :                 read->input_loaded = GF_FALSE;
     409        3013 :                 read->eos_signaled = GF_FALSE;
     410             :                 
     411             :                 //we need to reconfigure
     412        3013 :                 return isoffin_reconfigure(filter, read, next_url);
     413             :         }
     414             : 
     415         652 :         read->pid = pid;
     416         652 :         prop = gf_filter_pid_get_property(pid, GF_PROP_PID_FILE_CACHED);
     417         652 :         if (prop && prop->value.boolean) {
     418             :                 GF_FilterEvent evt;
     419         588 :                 read->input_loaded = GF_TRUE;
     420         588 :                 GF_FEVT_INIT(evt, GF_FEVT_PLAY_HINT, pid);
     421         588 :                 evt.play.full_file_only=1;
     422         588 :                 gf_filter_pid_send_event(pid, &evt);
     423             :         }
     424         652 :         return isoffin_setup(filter, read);
     425             : }
     426             : 
     427         947 : GF_Err isoffin_initialize(GF_Filter *filter)
     428             : {
     429         947 :         ISOMReader *read = gf_filter_get_udta(filter);
     430             :         GF_Err e = GF_OK;
     431         947 :         read->filter = filter;
     432         947 :         read->channels = gf_list_new();
     433             : 
     434         947 :         if (read->xps_check==MP4DMX_XPS_AUTO) {
     435         947 :                 read->xps_check = (read->smode==MP4DMX_SPLIT_EXTRACTORS) ? MP4DMX_XPS_KEEP : MP4DMX_XPS_REMOVE;
     436             :         }
     437             : 
     438         947 :         if (read->src) {
     439           0 :                 read->input_loaded = GF_TRUE;
     440           0 :                 return isoffin_setup(filter, read);
     441             :         }
     442         947 :         else if (read->mov) {
     443         291 :                 read->extern_mov = GF_TRUE;
     444         291 :                 read->input_loaded = GF_TRUE;
     445         291 :                 read->frag_type = gf_isom_is_fragmented(read->mov) ? 1 : 0;
     446         291 :                 read->time_scale = gf_isom_get_timescale(read->mov);
     447             : 
     448         291 :                 if (read->sigfrag) {
     449           3 :                         gf_isom_enable_traf_map_templates(read->mov);
     450             :                 }
     451             : 
     452         291 :                 if (read->catseg) {
     453           3 :                         e = gf_isom_open_segment(read->mov, read->catseg, 0, 0, 0);
     454             :                 }
     455           3 :                 if (!e)
     456         291 :                         e = isor_declare_objects(read);
     457             : 
     458         291 :                 gf_filter_post_process_task(filter);
     459             :         }
     460             :         return e;
     461             : }
     462             : 
     463             : 
     464         947 : static void isoffin_finalize(GF_Filter *filter)
     465             : {
     466         947 :         ISOMReader *read = (ISOMReader *) gf_filter_get_udta(filter);
     467             : 
     468         947 :         read->disconnected = GF_TRUE;
     469             : 
     470        3047 :         while (gf_list_count(read->channels)) {
     471        1153 :                 ISOMChannel *ch = (ISOMChannel *)gf_list_get(read->channels, 0);
     472        1153 :                 gf_list_rem(read->channels, 0);
     473        1153 :                 isoffin_delete_channel(ch);
     474             :         }
     475         947 :         gf_list_del(read->channels);
     476             : 
     477         947 :         if (!read->extern_mov && read->mov) gf_isom_close(read->mov);
     478         947 :         read->mov = NULL;
     479             : 
     480         947 :         if (read->mem_blob.data) gf_free(read->mem_blob.data);
     481         947 :         if (read->mem_url) gf_free(read->mem_url);
     482         947 : }
     483             : 
     484         206 : void isor_declare_pssh(ISOMChannel *ch)
     485             : {
     486             :         u32 i, PSSH_count;
     487             :         u8 *psshd;
     488         206 :         GF_BitStream *pssh_bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
     489             :         u32 s;
     490             : 
     491         206 :         PSSH_count = gf_isom_get_pssh_count(ch->owner->mov);
     492         206 :         gf_bs_write_u32(pssh_bs, PSSH_count);
     493             : 
     494             :         /*fill PSSH in the structure. We will free it in CENC_Setup*/
     495         688 :         for (i=0; i<PSSH_count; i++) {
     496             :                 bin128 SystemID;
     497             :                 u32 version;
     498             :                 u32 KID_count;
     499             :                 bin128 *KIDs;
     500             :                 u32 private_data_size;
     501             :                 u8 *private_data;
     502         276 :                 gf_isom_get_pssh_info(ch->owner->mov, i+1, SystemID, &version, &KID_count, (const bin128 **) & KIDs, (const u8 **) &private_data, &private_data_size);
     503             : 
     504         276 :                 gf_bs_write_data(pssh_bs, SystemID, 16);
     505         276 :                 gf_bs_write_u32(pssh_bs, version);
     506         276 :                 gf_bs_write_u32(pssh_bs, KID_count);
     507         535 :                 for (s=0; s<KID_count; s++) {
     508         259 :                         gf_bs_write_data(pssh_bs, KIDs[s], 16);
     509             :                 }
     510         276 :                 gf_bs_write_u32(pssh_bs, private_data_size);
     511         276 :                 gf_bs_write_data(pssh_bs, private_data, private_data_size);
     512             :         }
     513         206 :         gf_bs_get_content(pssh_bs, &psshd, &s);
     514         206 :         gf_bs_del(pssh_bs);
     515         206 :         gf_filter_pid_set_property(ch->pid, GF_PROP_PID_CENC_PSSH, & PROP_DATA_NO_COPY(psshd, s) );
     516         206 : }
     517             : 
     518         215 : void isor_set_crypt_config(ISOMChannel *ch)
     519             : {
     520         215 :         GF_ISOFile *mov = ch->owner->mov;
     521         215 :         u32 track = ch->track;
     522             :         u32 scheme_type, scheme_version, i, count;
     523             :         const char *kms_uri, *scheme_uri;
     524         215 :         Bool selectiveEncryption=0;
     525         215 :         u32 IVLength=0;
     526         215 :         u32 KeyIndicationLength=0;
     527         215 :         const char *txtHdr=NULL;
     528         215 :         const char *contentID=NULL;
     529         215 :         u32 txtHdrLen=0;
     530         215 :         u64 plainTextLen=0;
     531         215 :         u32 crypt_type=0;
     532         215 :         u32 stsd_idx = ch->owner->stsd ? ch->owner->stsd : 1;
     533             : 
     534         215 :         if (!ch->is_encrypted) return;
     535             : 
     536         215 :         scheme_type = scheme_version = 0;
     537         215 :         kms_uri = scheme_uri = NULL;
     538             : 
     539             :         /*ugly fix to detect when an stsd uses both clear and encrypted sample descriptions*/
     540         215 :         count = gf_isom_get_sample_description_count(ch->owner->mov, ch->track);
     541         215 :         if (count>1) {
     542             :                 u32 first_crypted_stsd = 0;
     543             :                 u32 nb_same_mtype = 1;
     544             :                 u32 nb_clear=0, nb_encrypted=0;
     545             :                 u32 base_subtype = 0;
     546             :                 Bool first_is_clear = GF_FALSE;
     547          18 :                 for (i=0; i<count; i++) {
     548          12 :                         u32 mtype = gf_isom_get_media_subtype(ch->owner->mov, ch->track, i+1);
     549          12 :                         if ( gf_isom_is_media_encrypted(ch->owner->mov, track, i+1)) {
     550           6 :                                 gf_isom_get_original_format_type(ch->owner->mov, ch->track, i+1, &mtype);
     551           6 :                                 nb_encrypted++;
     552           6 :                                 if (!first_crypted_stsd) first_crypted_stsd = i+1;
     553             :                         } else {
     554           6 :                                 nb_clear++;
     555           6 :                                 if (!i) first_is_clear = GF_TRUE;
     556             :                         }
     557          12 :                         if (!i) base_subtype = mtype;
     558           6 :                         else if (base_subtype==mtype) {
     559           6 :                                 nb_same_mtype++;
     560             :                         }
     561             :                 }
     562           6 :                 if ((nb_same_mtype==count) && (nb_clear==nb_encrypted)) {
     563           6 :                         gf_filter_pid_set_property(ch->pid, GF_PROP_PID_CENC_STSD_MODE, &PROP_UINT(first_is_clear ? 1 : 2) );
     564             :                         stsd_idx = first_crypted_stsd;
     565             :                 }
     566             : 
     567             :         }
     568             : 
     569         215 :         if (gf_isom_is_ismacryp_media(mov, track, stsd_idx)) {
     570          12 :                 gf_isom_get_ismacryp_info(mov, track, stsd_idx, NULL, &scheme_type, &scheme_version, &scheme_uri, &kms_uri, &selectiveEncryption, &IVLength, &KeyIndicationLength);
     571         203 :         } else if (gf_isom_is_omadrm_media(mov, track, stsd_idx)) {
     572             :                 //u8 hash[20];
     573           0 :                 gf_isom_get_omadrm_info(mov, track, stsd_idx, NULL, &scheme_type, &scheme_version, &contentID, &kms_uri, &txtHdr, &txtHdrLen, &plainTextLen, &crypt_type, &selectiveEncryption, &IVLength, &KeyIndicationLength);
     574             : 
     575             :                 //gf_media_get_file_hash(gf_isom_get_filename(mov), hash);
     576         203 :         } else if (gf_isom_is_cenc_media(mov, track, stsd_idx)) {
     577         193 :                 ch->is_cenc = GF_TRUE;
     578             : 
     579         193 :                 gf_isom_get_cenc_info(ch->owner->mov, ch->track, stsd_idx, NULL, &scheme_type, &scheme_version);
     580             : 
     581             :                 //if no PSSH declared, DO update the properties (PSSH is not mandatory)
     582          10 :         } else if (gf_isom_is_adobe_protection_media(mov, track, stsd_idx)) {
     583             :                 u32 ofmt;
     584          10 :                 scheme_version = 1;
     585          10 :                 scheme_type = GF_ISOM_ADOBE_SCHEME;
     586          10 :                 const char *metadata = NULL;
     587             : 
     588          10 :                 gf_isom_get_adobe_protection_info(mov, track, stsd_idx, &ofmt, &scheme_type, &scheme_version, &metadata);
     589          10 :                 if (metadata)
     590          10 :                         gf_filter_pid_set_property(ch->pid, GF_PROP_PID_ADOBE_CRYPT_META, &PROP_DATA((char *)metadata, (u32) strlen(metadata) ) );
     591             :         }
     592             : 
     593         215 :         gf_filter_pid_set_property(ch->pid, GF_PROP_PID_PROTECTION_SCHEME_TYPE, &PROP_4CC(scheme_type) );
     594         215 :         gf_filter_pid_set_property(ch->pid, GF_PROP_PID_PROTECTION_SCHEME_VERSION, &PROP_UINT(scheme_version) );
     595         215 :         if (scheme_uri) gf_filter_pid_set_property(ch->pid, GF_PROP_PID_PROTECTION_SCHEME_URI, &PROP_STRING((char*) scheme_uri) );
     596         215 :         if (kms_uri) gf_filter_pid_set_property(ch->pid, GF_PROP_PID_PROTECTION_KMS_URI, &PROP_STRING((char*) kms_uri) );
     597             : 
     598         215 :         if (selectiveEncryption) gf_filter_pid_set_property(ch->pid, GF_PROP_PID_ISMA_SELECTIVE_ENC, &PROP_BOOL(GF_TRUE) );
     599         215 :         if (IVLength) gf_filter_pid_set_property(ch->pid, GF_PROP_PID_ISMA_IV_LENGTH, &PROP_UINT(IVLength) );
     600         215 :         if (KeyIndicationLength) gf_filter_pid_set_property(ch->pid, GF_PROP_PID_ISMA_KI_LENGTH, &PROP_UINT(KeyIndicationLength) );
     601         215 :         if (crypt_type) gf_filter_pid_set_property(ch->pid, GF_PROP_PID_OMA_CRYPT_TYPE, &PROP_UINT(crypt_type) );
     602         215 :         if (contentID) gf_filter_pid_set_property(ch->pid, GF_PROP_PID_OMA_CID, &PROP_STRING(contentID) );
     603         215 :         if (txtHdr) gf_filter_pid_set_property(ch->pid, GF_PROP_PID_OMA_TXT_HDR, &PROP_STRING(txtHdr) );
     604         215 :         if (plainTextLen) gf_filter_pid_set_property(ch->pid, GF_PROP_PID_OMA_CLEAR_LEN, &PROP_LONGUINT(plainTextLen) );
     605             : 
     606         215 :         if (ch->is_cenc) {
     607             :                 const u8 *key_info;
     608             :                 u32 key_info_size;
     609             :                 u32 container_type;
     610             : 
     611         193 :                 isor_declare_pssh(ch);
     612             : 
     613         193 :                 gf_isom_cenc_get_default_info(ch->owner->mov, ch->track, stsd_idx, &container_type, &ch->pck_encrypted, &ch->crypt_byte_block, &ch->skip_byte_block, &key_info, &key_info_size);
     614             : 
     615         193 :                 gf_filter_pid_set_property(ch->pid, GF_PROP_PID_CENC_STORE, &PROP_4CC(container_type) );
     616             : 
     617         193 :                 gf_filter_pid_set_property(ch->pid, GF_PROP_PID_ENCRYPTED, &PROP_BOOL(ch->pck_encrypted) );
     618             : 
     619         193 :                 if (ch->skip_byte_block || ch->crypt_byte_block) {
     620          39 :                         gf_filter_pid_set_property(ch->pid, GF_PROP_PID_CENC_PATTERN, &PROP_FRAC_INT(ch->skip_byte_block, ch->crypt_byte_block) );
     621             :                 }
     622         193 :                 gf_filter_pid_set_property(ch->pid, GF_PROP_PID_CENC_KEY_INFO, &PROP_DATA((u8 *)key_info, key_info_size) );
     623         193 :                 ch->key_info_crc = gf_crc_32(key_info, key_info_size);
     624             :         }
     625             : }
     626             : 
     627             : 
     628        1184 : ISOMChannel *isor_create_channel(ISOMReader *read, GF_FilterPid *pid, u32 track, u32 item_id, Bool force_no_extractors)
     629             : {
     630             :         ISOMChannel *ch;
     631             :         const GF_PropertyValue *p;
     632             :         s64 ts_shift;
     633        1184 :         if (!read->mov) return NULL;
     634             : 
     635        1184 :         GF_SAFEALLOC(ch, ISOMChannel);
     636        1184 :         if (!ch) {
     637             :                 return NULL;
     638             :         }
     639        1184 :         ch->owner = read;
     640        1184 :         ch->pid = pid;
     641        1184 :         ch->to_init = GF_TRUE;
     642        1184 :         gf_list_add(read->channels, ch);
     643        1184 :         ch->track = track;
     644        1184 :         ch->item_id = item_id;
     645             : 
     646        1184 :         ch->nalu_extract_mode = 0;
     647        1184 :         ch->track_id = gf_isom_get_track_id(read->mov, ch->track);
     648        1184 :         switch (gf_isom_get_media_type(ch->owner->mov, ch->track)) {
     649           0 :         case GF_ISOM_MEDIA_OCR:
     650           0 :                 ch->streamType = GF_STREAM_OCR;
     651           0 :                 break;
     652          20 :         case GF_ISOM_MEDIA_SCENE:
     653          20 :                 ch->streamType = GF_STREAM_SCENE;
     654          20 :                 break;
     655         751 :         case GF_ISOM_MEDIA_VISUAL:
     656             :         case GF_ISOM_MEDIA_AUXV:
     657             :         case GF_ISOM_MEDIA_PICT:
     658         751 :                 gf_isom_get_reference(ch->owner->mov, ch->track, GF_ISOM_REF_BASE, 1, &ch->base_track);
     659             :                 //use base track only if avc/svc or hevc/lhvc. If avc+lhvc we need different rules
     660         751 :                 if ( gf_isom_get_avc_svc_type(ch->owner->mov, ch->base_track, 1) == GF_ISOM_AVCTYPE_AVC_ONLY) {
     661           4 :                         if ( gf_isom_get_hevc_lhvc_type(ch->owner->mov, ch->track, 1) >= GF_ISOM_HEVCTYPE_HEVC_ONLY) {
     662           0 :                                 ch->base_track=0;
     663             :                         }
     664             :                 }
     665         751 :                 ch->next_track = 0;
     666             :                 /*in scalable mode add SPS/PPS in-band*/
     667         751 :                 if (ch->base_track)
     668           6 :                         ch->nalu_extract_mode = GF_ISOM_NALU_EXTRACT_INBAND_PS_FLAG /*| GF_ISOM_NALU_EXTRACT_ANNEXB_FLAG*/;
     669             :                 break;
     670             :         }
     671        1184 :         if (!read->noedit) {
     672        1107 :                 ch->ts_offset = 0;
     673        1107 :                 ch->has_edit_list = gf_isom_get_edit_list_type(ch->owner->mov, ch->track, &ch->ts_offset) ? GF_TRUE : GF_FALSE;
     674        1107 :                 if (!ch->has_edit_list && ch->ts_offset) {
     675             :                         //if >0 this is a hold, we signal positive delay
     676             :                         //if <0 this is a skip, we signal negative delay
     677         218 :                         gf_filter_pid_set_property(pid, GF_PROP_PID_DELAY, &PROP_LONGSINT( ch->ts_offset) );
     678             :                 }
     679             :         } else
     680          77 :                 ch->has_edit_list = GF_FALSE;
     681             : 
     682        1184 :         ch->has_rap = (gf_isom_has_sync_points(ch->owner->mov, ch->track)==1) ? GF_TRUE : GF_FALSE;
     683        1184 :         gf_filter_pid_set_property(pid, GF_PROP_PID_HAS_SYNC, &PROP_BOOL(ch->has_rap) );
     684             :         //some fragmented files do not advertize a sync sample table (legal) so we need to update as soon as we fetch a fragment
     685             :         //to see if we are all-intra (as detected here) or not
     686        1184 :         if (!ch->has_rap && ch->owner->frag_type)
     687         255 :                 ch->check_has_rap = GF_TRUE;
     688        1184 :         ch->time_scale = gf_isom_get_media_timescale(ch->owner->mov, ch->track);
     689             : 
     690        1184 :         ts_shift = gf_isom_get_cts_to_dts_shift(ch->owner->mov, ch->track);
     691        1184 :         if (ts_shift) {
     692           6 :                 gf_filter_pid_set_property(pid, GF_PROP_PID_CTS_SHIFT, &PROP_UINT((u32) ts_shift) );
     693             :         }
     694             : 
     695        1184 :         if (!track || !gf_isom_is_track_encrypted(read->mov, track)) {
     696         976 :                 if (force_no_extractors) {
     697           0 :                         ch->nalu_extract_mode = GF_ISOM_NALU_EXTRACT_LAYER_ONLY;
     698             :                 } else {
     699         976 :                         switch (read->smode) {
     700         132 :                         case MP4DMX_SPLIT_EXTRACTORS:
     701         132 :                                 ch->nalu_extract_mode = GF_ISOM_NALU_EXTRACT_INSPECT | GF_ISOM_NALU_EXTRACT_TILE_ONLY;
     702         132 :                                 break;
     703         844 :                         case MP4DMX_SPLIT:
     704         844 :                                 ch->nalu_extract_mode = GF_ISOM_NALU_EXTRACT_LAYER_ONLY | GF_ISOM_NALU_EXTRACT_TILE_ONLY;
     705         844 :                                 break;
     706             :                         default:
     707             :                                 break;
     708             :                         }
     709             :                 }
     710             : 
     711         976 :                 if (ch->nalu_extract_mode) {
     712         976 :                         gf_isom_set_nalu_extract_mode(ch->owner->mov, ch->track, ch->nalu_extract_mode);
     713             :                 }
     714             :                 return ch;
     715             :         }
     716         208 :         if (ch->owner->nocrypt) {
     717           0 :                 ch->is_encrypted = GF_FALSE;
     718           0 :                 return ch;
     719             :         }
     720         208 :         ch->is_encrypted = GF_TRUE;
     721         208 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_STREAM_TYPE);
     722         208 :         if (p) gf_filter_pid_set_property(pid, GF_PROP_PID_ORIG_STREAM_TYPE, &PROP_UINT(p->value.uint) );
     723             : 
     724         208 :         gf_filter_pid_set_property(pid, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(GF_STREAM_ENCRYPTED) );
     725             : 
     726         208 :         isor_set_crypt_config(ch);
     727             : 
     728         208 :         if (ch->nalu_extract_mode) {
     729           0 :                 if (ch->is_encrypted) {
     730           0 :                         GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[IsoMedia] using sample NAL rewrite with encryption is not yet supported, patch welcome\n"));
     731             :                 } else {
     732           0 :                         gf_isom_set_nalu_extract_mode(ch->owner->mov, ch->track, ch->nalu_extract_mode);
     733             :                 }
     734             :         }
     735             :         return ch;
     736             : }
     737             : 
     738             : /*switch channel quality. Return next channel or current channel if error*/
     739             : static
     740           0 : u32 isoffin_channel_switch_quality(ISOMChannel *ch, GF_ISOFile *the_file, Bool switch_up)
     741             : {
     742             :         u32 i, count, next_track, trackID, cur_track;
     743             :         s32 ref_count;
     744             : 
     745           0 :         cur_track = ch->next_track ? ch->next_track : ch->track;
     746           0 :         count = gf_isom_get_track_count(the_file);
     747           0 :         trackID = gf_isom_get_track_id(the_file, cur_track);
     748           0 :         next_track = 0;
     749             : 
     750           0 :         if (switch_up) {
     751           0 :                 for (i = 0; i < count; i++) {
     752           0 :                         ref_count = gf_isom_get_reference_count(the_file, i+1, GF_ISOM_REF_SCAL);
     753           0 :                         if (ref_count < 0)
     754             :                                 return cur_track; //error
     755           0 :                         if (ref_count == 0)
     756           0 :                                 continue;
     757             :                         /*next track is the one that has the last reference of type GF_ISOM_REF_SCAL refers to this current track*/
     758           0 :                         if ((u32)ref_count == gf_isom_has_track_reference(the_file, i+1, GF_ISOM_REF_SCAL, trackID)) {
     759           0 :                                 next_track = i+1;
     760           0 :                                 break;
     761             :                         }
     762             :                 }
     763             :                 /*this is the highest quality*/
     764           0 :                 if (!next_track) {
     765           0 :                         ch->playing = GF_TRUE;
     766           0 :                         ref_count = gf_isom_get_reference_count(the_file, ch->track, GF_ISOM_REF_BASE);
     767           0 :                         trackID = 0;
     768           0 :                         if (ref_count) {
     769           0 :                                 gf_isom_get_reference(the_file, ch->track, GF_ISOM_REF_BASE, 1, &trackID);
     770           0 :                                 for (i=0; i<gf_list_count(ch->owner->channels) && trackID; i++) {
     771           0 :                                         ISOMChannel *base = gf_list_get(ch->owner->channels, i);
     772           0 :                                         if (base->track_id==trackID) {
     773             :                                                 u32 sample_desc_index;
     774             :                                                 u64 resume_at;
     775             :                                                 GF_Err e;
     776             :                                                 //try to locate sync after current time in base
     777           0 :                                                 resume_at = base->static_sample->DTS;
     778           0 :                                                 resume_at *= ch->time_scale;
     779           0 :                                                 resume_at /= base->time_scale;
     780           0 :                                                 e = gf_isom_get_sample_for_media_time(ch->owner->mov, ch->track, resume_at, &sample_desc_index, GF_ISOM_SEARCH_SYNC_FORWARD, &ch->static_sample, &ch->sample_num, &ch->sample_data_offset);
     781             :                                                 //found, rewind so that next fetch is the sync
     782           0 :                                                 if (e==GF_OK) {
     783           0 :                                                         ch->sample = NULL;
     784             :                                                 }
     785             :                                                 //no further sync found, realign with base timescale
     786           0 :                                                 else if (e==GF_EOS) {
     787           0 :                                                         e = gf_isom_get_sample_for_media_time(ch->owner->mov, ch->track, resume_at, &sample_desc_index, GF_ISOM_SEARCH_FORWARD, &ch->static_sample, &ch->sample_num, &ch->sample_data_offset);
     788             :                                                 }
     789             :                                                 //unknown state, realign sample num with base
     790           0 :                                                 if (e<0) {
     791           0 :                                                         ch->sample_num = base->sample_num;
     792             :                                                 }
     793             :                                                 break;
     794             :                                         }
     795             :                                 }
     796             :                         }
     797             :                         return cur_track;
     798             :                 }
     799             :         } else {
     800           0 :                 if (cur_track == ch->base_track)
     801             :                         return cur_track;
     802           0 :                 ref_count = gf_isom_get_reference_count(the_file, cur_track, GF_ISOM_REF_SCAL);
     803           0 :                 if (ref_count <= 0)
     804             :                         return cur_track;
     805           0 :                 gf_isom_get_reference(the_file, cur_track, GF_ISOM_REF_SCAL, ref_count, &next_track);
     806           0 :                 if (!next_track)
     807             :                         return cur_track;
     808             : 
     809           0 :                 if (ch->track != next_track) {
     810           0 :                         ch->playing = GF_FALSE;
     811           0 :                         ch->eos_sent = GF_TRUE;
     812           0 :                         gf_filter_pid_set_eos(ch->pid);
     813             :                 }
     814             :         }
     815             : 
     816             :         /*in scalable mode add SPS/PPS in-band*/
     817           0 :         if (ch->owner->smode)
     818           0 :                 gf_isom_set_nalu_extract_mode(the_file, next_track, ch->nalu_extract_mode);
     819             : 
     820           0 :         return next_track;
     821             : }
     822             : 
     823       76195 : static Bool isoffin_process_event(GF_Filter *filter, const GF_FilterEvent *evt)
     824             : {
     825             :         u32 count, i;
     826             :         Bool cancel_event = GF_TRUE;
     827             :         ISOMChannel *ch;
     828       76195 :         ISOMReader *read = gf_filter_get_udta(filter);
     829             : 
     830       76195 :         if (!read || read->disconnected) return GF_FALSE;
     831             : 
     832       76195 :         if (evt->base.type == GF_FEVT_QUALITY_SWITCH) {
     833           0 :                 count = gf_list_count(read->channels);
     834           0 :                 for (i = 0; i < count; i++) {
     835           0 :                         ch = (ISOMChannel *)gf_list_get(read->channels, i);
     836           0 :                         if (ch->base_track && gf_isom_needs_layer_reconstruction(read->mov)) {
     837             :                                 /*ch->next_track = */ //old code, see not in isoffin_reconfigure
     838           0 :                                 isoffin_channel_switch_quality(ch, read->mov, evt->quality_switch.up);
     839             :                         }
     840             :                 }
     841             :                 return GF_TRUE;
     842             :         }
     843             : 
     844       76195 :         if (!evt->base.on_pid) return GF_FALSE;
     845             : 
     846             :         ch = isor_get_channel(read, evt->base.on_pid);
     847       76195 :         if (!ch)
     848             :                 return GF_FALSE;
     849             : 
     850        2620 :         switch (evt->base.type) {
     851        1207 :         case GF_FEVT_PLAY:
     852        1207 :                 isor_reset_reader(ch);
     853        1207 :                 ch->eos_sent = GF_FALSE;
     854        1207 :                 ch->speed = evt->play.speed;
     855        1207 :                 ch->initial_play_seen = GF_TRUE;
     856        1207 :                 read->reset_frag_state = 1;
     857             :                 //it can happen that input_is_stop is still TRUE because we did not get called back after the stop - reset to FALSE since we now play
     858        1207 :                 read->input_is_stop = GF_FALSE;
     859        1207 :                 if (read->frag_type)
     860         262 :                         read->frag_type = 1;
     861             : 
     862        1207 :                 ch->start = ch->end = 0;
     863        1207 :                 if (evt->play.speed>=0) {
     864             :                         Double t;
     865        1205 :                         if (evt->play.start_range>=0) {
     866             :                                 t = evt->play.start_range;
     867        1205 :                                 t *= ch->time_scale;
     868        1205 :                                 ch->start = (u64) t;
     869             :                         }
     870        1205 :                         if (evt->play.end_range >= evt->play.start_range) {
     871        1197 :                                 ch->end = (u64) -1;
     872        1197 :                                 if (evt->play.end_range<FLT_MAX) {
     873             :                                         t = evt->play.end_range;
     874        1191 :                                         t *= ch->time_scale;
     875        1191 :                                         ch->end = (u64) t;
     876             :                                 }
     877             :                         }
     878             :                 } else {
     879           2 :                         Double end = evt->play.end_range;
     880           2 :                         if (end==-1) end = 0;
     881           2 :                         ch->start = (u64) (s64) (evt->play.start_range * ch->time_scale);
     882           2 :                         if (end <= evt->play.start_range)
     883           2 :                                 ch->end = (u64) (s64) (end  * ch->time_scale);
     884             :                 }
     885        1207 :                 ch->playing = GF_TRUE;
     886        1207 :                 ch->sample_num = evt->play.from_pck;
     887             : 
     888        1207 :                 ch->sap_only = evt->play.drop_non_ref ? GF_TRUE : GF_FALSE;
     889             : 
     890        1207 :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[IsoMedia] Starting channel playback "LLD" to "LLD" (%g to %g)\n", ch->start, ch->end, evt->play.start_range, evt->play.end_range));
     891             : 
     892        1207 :                 if (!read->nb_playing)
     893        1005 :                         gf_isom_reset_seq_num(read->mov);
     894             : 
     895        1207 :                 if (read->is_partial_download) read->input_loaded = GF_FALSE;
     896             : 
     897        1207 :                 if (evt->play.no_byterange_forward) {
     898             :                         //new segment will be loaded, reset
     899         242 :                         gf_isom_reset_tables(read->mov, GF_TRUE);
     900         242 :                         gf_isom_reset_data_offset(read->mov, NULL);
     901         242 :                         read->refresh_fragmented = GF_TRUE;
     902         242 :                         read->mem_blob.size = 0;
     903             :                         //send play event
     904             :                         cancel_event = GF_FALSE;
     905         965 :                 } else if (!read->nb_playing && read->pid && !read->input_loaded) {
     906             :                         GF_FilterEvent fevt;
     907             :                         Bool is_sidx_seek = GF_FALSE;
     908           4 :                         u64 max_offset = GF_FILTER_NO_BO;
     909           4 :                         count = gf_list_count(read->channels);
     910             : 
     911             :                         //try sidx
     912           4 :                         if (read->frag_type) {
     913             :                                 u32 ts;
     914           1 :                                 u64 dur=0;
     915           1 :                                 GF_Err e = gf_isom_get_file_offset_for_time(read->mov, evt->play.start_range, &max_offset);
     916           1 :                                 if (e==GF_OK) {
     917           1 :                                         if (evt->play.start_range>0)
     918           0 :                                                 gf_isom_reset_tables(read->mov, GF_TRUE);
     919             : 
     920             :                                         is_sidx_seek = GF_TRUE;
     921             :                                         //in case we loaded moov but not sidx, update duration
     922           1 :                                         if ((gf_isom_get_sidx_duration(read->mov, &dur, &ts)==GF_OK) && dur) {
     923           1 :                                                 dur *= read->time_scale;
     924           1 :                                                 dur /= ts;
     925           1 :                                                 if (ch->duration != dur) {
     926           1 :                                                         ch->duration = dur;
     927           1 :                                                         gf_filter_pid_set_property(ch->pid, GF_PROP_PID_DURATION, &PROP_FRAC64_INT(ch->duration, read->time_scale));
     928             :                                                 }
     929             :                                         }
     930             :                                 }
     931             :                         }
     932             : 
     933             :                         if (!is_sidx_seek) {
     934           3 :                                 for (i=0; i< count; i++) {
     935             :                                         u32 mode, sample_desc_index, sample_num;
     936             :                                         u64 data_offset;
     937             :                                         GF_Err e;
     938             :                                         u64 time;
     939           3 :                                         ch = gf_list_get(read->channels, i);
     940           3 :                                         mode = ch->disable_seek ? GF_ISOM_SEARCH_BACKWARD : GF_ISOM_SEARCH_SYNC_BACKWARD;
     941           3 :                                         time = (u64) (evt->play.start_range * ch->time_scale);
     942             : 
     943             :                                         /*take care of seeking out of the track range*/
     944           3 :                                         if (!read->frag_type && (ch->duration < time)) {
     945           0 :                                                 e = gf_isom_get_sample_for_movie_time(read->mov, ch->track, ch->duration,      &sample_desc_index, mode, NULL, &sample_num, &data_offset);
     946             :                                         } else {
     947           3 :                                                 e = gf_isom_get_sample_for_movie_time(read->mov, ch->track, time, &sample_desc_index, mode, NULL, &sample_num, &data_offset);
     948             :                                         }
     949           3 :                                         if ((e == GF_OK) && (data_offset<max_offset))
     950           3 :                                                 max_offset = data_offset;
     951             :                                 }
     952             :                         }
     953             : 
     954           4 :                         if ((evt->play.start_range || read->is_partial_download)  && (max_offset != GF_FILTER_NO_BO) ) {
     955             : 
     956             :                                 //send a seek request
     957           0 :                                 read->is_partial_download = GF_TRUE;
     958           0 :                                 read->wait_for_source = GF_TRUE;
     959           0 :                                 read->refresh_fragmented = GF_TRUE;
     960             : 
     961           0 :                                 GF_FEVT_INIT(fevt, GF_FEVT_SOURCE_SEEK, read->pid);
     962           0 :                                 fevt.seek.start_offset = max_offset;
     963           0 :                                 gf_filter_pid_send_event(read->pid, &fevt);
     964           0 :                                 gf_isom_set_byte_offset(read->mov, is_sidx_seek ? 0 : max_offset);
     965             : 
     966             :                         }
     967             :                 }
     968             :                 //always request a process task upon a play
     969        1207 :                 gf_filter_post_process_task(read->filter);
     970        1207 :                 read->nb_playing++;
     971             :                 //cancel event unless dash mode
     972        1207 :                 return cancel_event;
     973             : 
     974         285 :         case GF_FEVT_STOP:
     975         285 :                 if (read->nb_playing) read->nb_playing--;
     976         285 :                 isor_reset_reader(ch);
     977             :                 //don't send a stop if some of our channels are still waiting for initial play
     978         764 :                 for (i=0; i<gf_list_count(read->channels); i++) {
     979         517 :                         ISOMChannel *a_ch = gf_list_get(read->channels, i);
     980         517 :                         if (ch==a_ch) continue;
     981         251 :                         if (!a_ch->initial_play_seen) return GF_TRUE;
     982             :                 }
     983             :                 //cancel event if nothing playing
     984         247 :                 if (read->nb_playing) return GF_TRUE;
     985         215 :                 read->input_is_stop = GF_TRUE;
     986         215 :                 return GF_FALSE;
     987             : 
     988         214 :         case GF_FEVT_SET_SPEED:
     989             :         case GF_FEVT_RESUME:
     990         214 :                 ch->speed = evt->play.speed;
     991         214 :                 if (ch->sap_only && !evt->play.drop_non_ref) {
     992           0 :                         ch->sap_only = 2;
     993             :                 } else {
     994         214 :                         ch->sap_only = evt->play.drop_non_ref ? GF_TRUE : GF_FALSE;
     995             :                 }
     996             :                 //cancel event
     997             :                 return GF_TRUE;
     998             :         default:
     999             :                 break;
    1000             :         }
    1001             :         //by default don't cancel event
    1002             :         return GF_FALSE;
    1003             : }
    1004             : 
    1005        3981 : static void isoffin_push_buffer(GF_Filter *filter, ISOMReader *read, const u8 *pck_data, u32 data_size)
    1006             : {
    1007             :         u64 bytes_missing;
    1008             :         GF_Err e;
    1009             : 
    1010        3981 :         if (!read->mem_url) {
    1011             :                 char szPath[200];
    1012           4 :                 sprintf(szPath, "gmem://%p", &read->mem_blob);
    1013           4 :                 read->mem_url = gf_strdup(szPath);
    1014             :         }
    1015        3981 :         read->mem_blob.data = gf_realloc(read->mem_blob.data, read->mem_blob.size + data_size);
    1016        3981 :         memcpy(read->mem_blob.data + read->mem_blob.size, pck_data, data_size);
    1017        3981 :         read->mem_blob.size += data_size;
    1018             : 
    1019        3981 :         if (read->mem_load_mode==1) {
    1020             :                 u32 box_type;
    1021           8 :                 e = gf_isom_open_progressive_ex(read->mem_url, 0, 0, GF_FALSE, &read->mov, &bytes_missing, &box_type);
    1022             : 
    1023           8 :                 if (e && (e != GF_ISOM_INCOMPLETE_FILE)) {
    1024           0 :                         gf_filter_setup_failure(filter, e);
    1025           0 :                         read->mem_load_mode = 0;
    1026           0 :                         read->in_error = e;
    1027           0 :                         return;
    1028             :                 }
    1029           8 :                 if (!read->mov) {
    1030           4 :                         switch (box_type) {
    1031           0 :                         case GF_4CC('m','d','a','t'):
    1032           0 :                                 GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[IsoMedia] non fragmented ISOBMFF with moof after mdat and no underlying file cache (pipe or other stream input), not supported !\n"));
    1033           0 :                                 gf_filter_setup_failure(filter, GF_NOT_SUPPORTED);
    1034           0 :                                 read->mem_load_mode = 0;
    1035           0 :                                 read->in_error = GF_NOT_SUPPORTED;
    1036           0 :                                 break;
    1037           4 :                         default:
    1038           4 :                                 read->moov_not_loaded = 1;
    1039           4 :                                 break;
    1040             :                         }
    1041             :                         return;
    1042             :                 }
    1043             : 
    1044           4 :                 read->frag_type = gf_isom_is_fragmented(read->mov) ? 1 : 0;
    1045           4 :                 read->time_scale = gf_isom_get_timescale(read->mov);
    1046           4 :                 isor_declare_objects(read);
    1047           4 :                 read->mem_load_mode = 2;
    1048           4 :                 read->moov_not_loaded = 0;
    1049           4 :                 return;
    1050             :         }
    1051             :         //refresh file
    1052        3973 :         gf_isom_refresh_fragmented(read->mov, &bytes_missing, read->mem_url);
    1053             : 
    1054        3973 :         if ((read->mem_load_mode==2) && bytes_missing)
    1055        3756 :                 read->force_fetch = GF_TRUE;
    1056             : 
    1057             : }
    1058             : 
    1059       30768 : static void isoffin_purge_mem(ISOMReader *read, u64 min_offset)
    1060             : {
    1061             :         u32 i, count;
    1062             :         u64 top_offset;
    1063             :         u32 nb_bytes_to_purge;
    1064             :         u64 bytes_missing;
    1065             : 
    1066             :         //purge every
    1067       30768 :         if (read->mstore_purge && (min_offset - read->last_min_offset < read->mstore_purge))
    1068       30693 :                 return;
    1069             : 
    1070         361 :         if (read->frag_type) {
    1071             :                 //get position of current box being parsed - if new offset is greater than this box we cannot remove
    1072             :                 //bytes (we would trash the top-level box header)
    1073          82 :                 gf_isom_get_current_top_box_offset(read->mov, &top_offset);
    1074          82 :                 if (top_offset<min_offset) {
    1075             :                         return;
    1076             :                 }
    1077             :         }
    1078         354 :         read->last_min_offset = min_offset;
    1079             : 
    1080             :         assert(min_offset>=read->bytes_removed);
    1081             :         //min_offset is given in absolute file position
    1082         354 :         nb_bytes_to_purge = (u32) (min_offset - read->bytes_removed);
    1083             :         assert(nb_bytes_to_purge<=read->mem_blob.size);
    1084             : 
    1085         354 :         memmove(read->mem_blob.data, read->mem_blob.data+nb_bytes_to_purge, read->mem_blob.size - nb_bytes_to_purge);
    1086         354 :         read->mem_blob.size -= nb_bytes_to_purge;
    1087         354 :         read->bytes_removed += nb_bytes_to_purge;
    1088         354 :         gf_isom_set_removed_bytes(read->mov, read->bytes_removed);
    1089             : 
    1090         354 :         GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[IsoMedia] mem mode %d bytes in mem, "LLU" bytes trashed since start\n", read->mem_blob.size, read->bytes_removed));
    1091             : 
    1092             :         //force a refresh
    1093         354 :         gf_isom_refresh_fragmented(read->mov, &bytes_missing, read->mem_url);
    1094             : 
    1095         354 :         if (!read->frag_type)
    1096             :                 return;
    1097             : 
    1098             :         //fragmented file, cleanup sample tables
    1099          75 :         count = gf_list_count(read->channels);
    1100         150 :         for (i=0; i<count; i++) {
    1101          75 :                 ISOMChannel *ch = gf_list_get(read->channels, i);
    1102             :                 u32 num_samples;
    1103          75 :                 u32 prev_samples = gf_isom_get_sample_count(read->mov, ch->track);
    1104             :                 //don't run this too often
    1105          75 :                 if (ch->sample_num<=1+read->mstore_samples) continue;
    1106             : 
    1107          75 :                 num_samples = ch->sample_num-1;
    1108          75 :                 if (num_samples>=prev_samples) continue;
    1109             : 
    1110          75 :                 if (gf_isom_purge_samples(read->mov, ch->track, num_samples) == GF_OK)
    1111          75 :                         ch->sample_num = 1;
    1112             : 
    1113          75 :                 num_samples = gf_isom_get_sample_count(read->mov, ch->track);
    1114             :                 assert(ch->sample_num<=num_samples);
    1115          75 :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[IsoMedia] mem mode %d samples now in track %d (prev %d)\n", num_samples, ch->track_id, prev_samples));
    1116             :         }
    1117             : }
    1118             : 
    1119      293308 : static GF_Err isoffin_process(GF_Filter *filter)
    1120             : {
    1121      293308 :         ISOMReader *read = gf_filter_get_udta(filter);
    1122      293308 :         u32 i, count = gf_list_count(read->channels);
    1123             :         Bool is_active = GF_FALSE;
    1124             :         Bool in_is_eos = GF_FALSE;
    1125             :         Bool check_forced_end = GF_FALSE;
    1126             :         Bool has_new_data = GF_FALSE;
    1127             :         u64 min_offset_plus_one = 0;
    1128             :         u32 nb_forced_end=0;
    1129      293308 :         if (read->in_error)
    1130             :                 return read->in_error;
    1131             : 
    1132      293308 :         if (read->pid) {
    1133             :                 Bool fetch_input = GF_TRUE;
    1134             : 
    1135             :                 //we failed at loading the init segment during a dash switch, retry
    1136      245291 :                 if (!read->is_partial_download && !read->mem_load_mode && (read->moov_not_loaded==2) ) {
    1137           0 :                         isoffin_configure_pid(filter, read->pid, GF_FALSE);
    1138           0 :                         if (read->moov_not_loaded) return GF_OK;
    1139             :                 }
    1140      245291 :                 if (read->mem_load_mode==2) {
    1141       30771 :                         if (!read->force_fetch && read->mem_blob.size > read->mstore_size) {
    1142             :                                 fetch_input = GF_FALSE;
    1143             :                         }
    1144       30771 :                         read->force_fetch = GF_FALSE;
    1145             :                 }
    1146      319305 :                 while (fetch_input) {
    1147      308624 :                         GF_FilterPacket *pck = gf_filter_pid_get_packet(read->pid);
    1148      308624 :                         if (!pck) {
    1149             :                                 //we issued a seek, wait for the first packet to be received before fetching channels
    1150             :                                 //otherwise we could end up reading from the wrong cache
    1151      234610 :                                 if (read->wait_for_source) {
    1152             :                                         //something went wrong during the seek request
    1153           0 :                                         if (gf_filter_pid_is_eos(read->pid))
    1154             :                                                 return GF_EOS;
    1155           0 :                                         return GF_OK;
    1156             :                                 }
    1157             :                                 break;
    1158             :                         }
    1159       74014 :                         read->wait_for_source = GF_FALSE;
    1160             : 
    1161       74014 :                         if (read->mem_load_mode) {
    1162             :                                 u32 data_size;
    1163        3981 :                                 const u8 *pck_data = gf_filter_pck_get_data(pck, &data_size);
    1164        3981 :                                 isoffin_push_buffer(filter, read, pck_data, data_size);
    1165             :                         }
    1166             :                         //we just had a switch but init seg is not completely done: input packet is only a part of the init, drop it
    1167       70033 :                         else if (read->moov_not_loaded==2) {
    1168           0 :                                 gf_filter_pid_drop_packet(read->pid);
    1169           0 :                                 return GF_OK;
    1170             :                         }
    1171       74014 :                         gf_filter_pid_drop_packet(read->pid);
    1172             :                         has_new_data = GF_TRUE;
    1173       74014 :                         if (read->in_error)
    1174             :                                 return read->in_error;
    1175             :                 }
    1176      245291 :                 if (gf_filter_pid_is_eos(read->pid)) {
    1177      132820 :                         read->input_loaded = GF_TRUE;
    1178             :                         in_is_eos = GF_TRUE;
    1179             :                 }
    1180      245291 :                 if (read->input_is_stop) {
    1181         121 :                         read->input_loaded = GF_TRUE;
    1182             :                         in_is_eos = GF_TRUE;
    1183         121 :                         read->input_is_stop = GF_FALSE;
    1184             :                 }
    1185      245291 :                 if (!read->frag_type && read->input_loaded) {
    1186             :                         in_is_eos = GF_TRUE;
    1187             :                 }
    1188             :         //segment is invalid, wait for eos on input an send eos on all channels
    1189      245291 :         if (read->invalid_segment) {
    1190           0 :             if (!in_is_eos) return GF_OK;
    1191           0 :             read->invalid_segment = GF_FALSE;
    1192             : 
    1193           0 :             for (i=0; i<count; i++) {
    1194           0 :                 ISOMChannel *ch = gf_list_get(read->channels, i);
    1195           0 :                 if (!ch->playing) {
    1196           0 :                     continue;
    1197             :                 }
    1198           0 :                 if (!ch->eos_sent) {
    1199           0 :                     ch->eos_sent = GF_TRUE;
    1200           0 :                     gf_filter_pid_set_eos(ch->pid);
    1201             :                 }
    1202             :             }
    1203           0 :             read->eos_signaled = GF_TRUE;
    1204           0 :             return GF_EOS;
    1205             :         }
    1206       48017 :         } else if (read->extern_mov) {
    1207             :                 in_is_eos = GF_TRUE;
    1208       48017 :                 read->input_loaded = GF_TRUE;
    1209             :         }
    1210      293308 :         if (read->moov_not_loaded==1) {
    1211          29 :                 if (read->mem_load_mode)
    1212             :                         return GF_OK;
    1213          25 :                 read->moov_not_loaded = GF_FALSE;
    1214          25 :                 return isoffin_setup(filter, read);
    1215             :         }
    1216             : 
    1217      293279 :         if (read->refresh_fragmented) {
    1218             :                 const GF_PropertyValue *prop;
    1219             : 
    1220        3959 :                 if (in_is_eos) {
    1221        1251 :                         read->refresh_fragmented = GF_FALSE;
    1222             :                 } else {
    1223        2708 :                         prop = gf_filter_pid_get_property(read->pid, GF_PROP_PID_FILE_CACHED);
    1224        2708 :                         if (prop && prop->value.boolean)
    1225        1595 :                                 read->refresh_fragmented = GF_FALSE;
    1226             :                 }
    1227             : 
    1228        3959 :                 if (has_new_data) {
    1229        3366 :                         u64 bytesMissing=0;
    1230             :                         GF_Err e;
    1231             :                         const char *new_url = NULL;
    1232        3366 :                         prop = gf_filter_pid_get_property(read->pid, GF_PROP_PID_FILEPATH);
    1233        3366 :                         if (prop) new_url = prop->value.string;
    1234             : 
    1235        3366 :                         e = gf_isom_refresh_fragmented(read->mov, &bytesMissing, new_url);
    1236             : 
    1237        3366 :                         if (e && (e!= GF_ISOM_INCOMPLETE_FILE)) {
    1238           0 :                                 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[IsoMedia] Failed to refresh current segment: %s\n", gf_error_to_string(e) ));
    1239           0 :                                 read->refresh_fragmented = GF_FALSE;
    1240             :                         } else {
    1241        3366 :                                 GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[IsoMedia] Refreshing current segment at UTC "LLU" - "LLU" bytes still missing - input is EOS %d\n", gf_net_get_utc(), bytesMissing, in_is_eos));
    1242             :                         }
    1243             : 
    1244        3366 :                         if (!read->refresh_fragmented && (e==GF_ISOM_INCOMPLETE_FILE)) {
    1245           0 :                                 GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[IsoMedia] Incomplete Segment received - "LLU" bytes missing but EOF found\n", bytesMissing ));
    1246             :                         }
    1247             : 
    1248             : #ifndef GPAC_DISABLE_LOG
    1249        3366 :                         if (gf_log_tool_level_on(GF_LOG_DASH, GF_LOG_DEBUG)) {
    1250          71 :                                 for (i=0; i<count; i++) {
    1251          71 :                                         ISOMChannel *ch = gf_list_get(read->channels, i);
    1252          71 :                                         GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[IsoMedia] refresh track %d fragment - cur sample %d - new sample count %d\n", ch->track, ch->sample_num, gf_isom_get_sample_count(ch->owner->mov, ch->track) ));
    1253             :                                 }
    1254             :                         }
    1255             : #endif
    1256        3366 :                         isor_check_producer_ref_time(read);
    1257        3366 :                         if (!read->frag_type)
    1258           0 :                                 read->refresh_fragmented = GF_FALSE;
    1259             :                 }
    1260             :         }
    1261             : 
    1262      503094 :         for (i=0; i<count; i++) {
    1263             :                 u8 *data;
    1264             :                 u32 nb_pck=50;
    1265             :                 ISOMChannel *ch;
    1266      503094 :                 ch = gf_list_get(read->channels, i);
    1267      503094 :                 if (!ch->playing) {
    1268        5553 :                         nb_forced_end++;
    1269        5553 :                         continue;
    1270             :                 }
    1271             :                 //eos not sent on this channel, we are active
    1272      497541 :                 if (!ch->eos_sent)
    1273             :                         is_active = GF_TRUE;
    1274             : 
    1275      894459 :                 while (nb_pck) {
    1276      893993 :                         ch->sample_data_offset = 0;
    1277      893993 :                         if (!read->full_segment_flush && gf_filter_pid_would_block(ch->pid) )
    1278             :                                 break;
    1279             : 
    1280      642432 :                         if (ch->item_id) {
    1281         118 :                                 isor_reader_get_sample_from_item(ch);
    1282             :                         } else {
    1283      642314 :                                 isor_reader_get_sample(ch);
    1284             :                         }
    1285             : 
    1286      642432 :                         if (read->stsd && (ch->last_sample_desc_index != read->stsd) && ch->sample) {
    1287           0 :                                 isor_reader_release_sample(ch);
    1288           0 :                                 continue;
    1289             :                         }
    1290      642432 :                         if (ch->sample) {
    1291             :                                 u32 sample_dur;
    1292             :                                 u8 dep_flags;
    1293             :                                 u8 *subs_buf;
    1294             :                                 u32 subs_buf_size;
    1295             :                                 GF_FilterPacket *pck;
    1296      396918 :                                 if (ch->needs_pid_reconfig) {
    1297           5 :                                         isor_update_channel_config(ch);
    1298           5 :                                         ch->needs_pid_reconfig = GF_FALSE;
    1299             :                                 }
    1300             : 
    1301             :                                 //we have at least two samples, update GF_PROP_PID_HAS_SYNC if needed
    1302      396918 :                                 if (ch->check_has_rap && (gf_isom_get_sample_count(ch->owner->mov, ch->track)>1) && (gf_isom_has_sync_points(ch->owner->mov, ch->track)==1)) {
    1303         187 :                                         ch->check_has_rap = GF_FALSE;
    1304         187 :                                         ch->has_rap = GF_TRUE;
    1305         187 :                                         gf_filter_pid_set_property(ch->pid, GF_PROP_PID_HAS_SYNC, &PROP_BOOL(ch->has_rap) );
    1306             :                                 }
    1307             : 
    1308             :                                 //strip param sets from payload, trigger reconfig if needed
    1309      396918 :                                 isor_reader_check_config(ch);
    1310             : 
    1311      396918 :                                 if (read->nodata) {
    1312           0 :                                         pck = gf_filter_pck_new_shared(ch->pid, NULL, ch->sample->dataLength, NULL);
    1313           0 :                                         if (!pck) return GF_OUT_OF_MEM;
    1314             :                                 } else {
    1315      396918 :                                         pck = gf_filter_pck_new_alloc(ch->pid, ch->sample->dataLength, &data);
    1316      396918 :                                         if (!pck) return GF_OUT_OF_MEM;
    1317             : 
    1318      396918 :                                         memcpy(data, ch->sample->data, ch->sample->dataLength);
    1319             :                                 }
    1320      396918 :                                 gf_filter_pck_set_dts(pck, ch->dts);
    1321      396918 :                                 gf_filter_pck_set_cts(pck, ch->cts);
    1322      396918 :                                 if (ch->sample->IsRAP==-1) {
    1323           0 :                                         gf_filter_pck_set_sap(pck, GF_FILTER_SAP_1);
    1324           0 :                                         ch->redundant = 1;
    1325             :                                 } else {
    1326      396918 :                                         gf_filter_pck_set_sap(pck, (GF_FilterSAPType) ch->sample->IsRAP);
    1327             :                                 }
    1328             : 
    1329      396918 :                                 if (ch->sap_3)
    1330         355 :                                         gf_filter_pck_set_sap(pck, GF_FILTER_SAP_3);
    1331      396563 :                                 else if (ch->sap_4_type) {
    1332          93 :                                         gf_filter_pck_set_sap(pck, (ch->sap_4_type==GF_ISOM_SAMPLE_PREROLL) ? GF_FILTER_SAP_4_PROL : GF_FILTER_SAP_4);
    1333          93 :                                         gf_filter_pck_set_roll_info(pck, ch->roll);
    1334             :                                 }
    1335             : 
    1336      396918 :                                 sample_dur = ch->au_duration;
    1337      396918 :                                 if (ch->sample->nb_pack)
    1338         482 :                                         sample_dur *= ch->sample->nb_pack;
    1339      396918 :                                 gf_filter_pck_set_duration(pck, sample_dur);
    1340      396918 :                                 gf_filter_pck_set_seek_flag(pck, ch->seek_flag);
    1341             : 
    1342      396918 :                                 dep_flags = ch->isLeading;
    1343      396918 :                                 dep_flags <<= 2;
    1344      396918 :                                 dep_flags |= ch->dependsOn;
    1345      396918 :                                 dep_flags <<= 2;
    1346      396918 :                                 dep_flags |= ch->dependedOn;
    1347      396918 :                                 dep_flags <<= 2;
    1348      396918 :                                 dep_flags |= ch->redundant;
    1349             : 
    1350      396918 :                                 if (dep_flags)
    1351       26972 :                                         gf_filter_pck_set_dependency_flags(pck, dep_flags);
    1352             : 
    1353      396918 :                                 gf_filter_pck_set_crypt_flags(pck, ch->pck_encrypted ? GF_FILTER_PCK_CRYPT : 0);
    1354      396918 :                                 gf_filter_pck_set_seq_num(pck, ch->sample_num);
    1355             : 
    1356             : 
    1357      396918 :                                 subs_buf = gf_isom_sample_get_subsamples_buffer(read->mov, ch->track, ch->sample_num, &subs_buf_size);
    1358      396918 :                                 if (subs_buf) {
    1359         768 :                                         gf_filter_pck_set_property(pck, GF_PROP_PCK_SUBS, &PROP_DATA_NO_COPY(subs_buf, subs_buf_size) );
    1360             :                                 }
    1361             : 
    1362      396918 :                                 if (ch->sai_buffer && ch->pck_encrypted) {
    1363             :                                         assert(ch->sai_buffer_size);
    1364       33013 :                                         gf_filter_pck_set_property(pck, GF_PROP_PCK_CENC_SAI, &PROP_DATA(ch->sai_buffer, ch->sai_buffer_size) );
    1365             :                                 }
    1366             : 
    1367      396918 :                                 if (read->sigfrag) {
    1368             :                                         GF_ISOFragmentBoundaryInfo finfo;
    1369        7323 :                                         if (gf_isom_sample_is_fragment_start(read->mov, ch->track, ch->sample_num, &finfo) ) {
    1370             :                                                 u64 start=0;
    1371         195 :                                                 u32 traf_start = finfo.seg_start_plus_one ? 2 : 1;
    1372             : 
    1373         195 :                                                 if (finfo.seg_start_plus_one)
    1374          87 :                                                         gf_filter_pck_set_property(pck, GF_PROP_PCK_CUE_START, &PROP_BOOL(GF_TRUE));
    1375             : 
    1376         195 :                                                 gf_filter_pck_set_property(pck, GF_PROP_PCK_FRAG_START, &PROP_UINT(traf_start));
    1377             : 
    1378         195 :                                                 start = finfo.frag_start;
    1379         195 :                                                 if (finfo.seg_start_plus_one) start = finfo.seg_start_plus_one-1;
    1380         195 :                                                 gf_filter_pck_set_property(pck, GF_PROP_PCK_FRAG_RANGE, &PROP_FRAC64_INT(start, finfo.mdat_end));
    1381         195 :                                                 if (finfo.moof_template) {
    1382          87 :                                                         gf_filter_pck_set_property(pck, GF_PROP_PCK_MOOF_TEMPLATE, &PROP_DATA((u8 *)finfo.moof_template, finfo.moof_template_size));
    1383             :                                                 }
    1384         195 :                                                 if (finfo.sidx_end) {
    1385          87 :                                                         gf_filter_pck_set_property(pck, GF_PROP_PCK_SIDX_RANGE, &PROP_FRAC64_INT(finfo.sidx_start , finfo.sidx_end));
    1386             :                                                 }
    1387             : 
    1388         195 :                                                 if (read->seg_name_changed) {
    1389          81 :                                                         const GF_PropertyValue *p = gf_filter_pid_get_property(read->pid, GF_PROP_PID_URL);
    1390          81 :                                                         read->seg_name_changed = GF_FALSE;
    1391          81 :                                                         if (p && p->value.string) {
    1392          81 :                                                                 gf_filter_pck_set_property(pck, GF_PROP_PID_URL, &PROP_STRING(p->value.string));
    1393             :                                                         }
    1394             :                                                 }
    1395             :                                         }
    1396             :                                 }
    1397      396918 :                                 if (ch->sender_ntp) {
    1398           0 :                                         gf_filter_pck_set_property(pck, GF_PROP_PCK_SENDER_NTP, &PROP_LONGUINT(ch->sender_ntp));
    1399           0 :                                         if (ch->ntp_at_server_ntp) {
    1400           0 :                                                 gf_filter_pck_set_property(pck, GF_PROP_PCK_RECEIVER_NTP, &PROP_LONGUINT(ch->ntp_at_server_ntp));
    1401             :                                         }
    1402             :                                 }
    1403      396918 :                                 ch->eos_sent = GF_FALSE;
    1404      396918 :                                 gf_filter_pck_send(pck);
    1405      396918 :                                 isor_reader_release_sample(ch);
    1406             : 
    1407      396918 :                                 ch->last_valid_sample_data_offset = ch->sample_data_offset;
    1408      396918 :                                 nb_pck--;
    1409      245514 :                         } else if (ch->last_state==GF_EOS) {
    1410      243981 :                                 if (ch->playing == 2) {
    1411           0 :                                         if (in_is_eos) {
    1412           0 :                                                 ch->playing = GF_FALSE;
    1413             :                                         } else {
    1414           0 :                                                 nb_forced_end++;
    1415             :                                                 check_forced_end = GF_TRUE;
    1416             :                                         }
    1417             :                                 }
    1418      243981 :                                 if (in_is_eos && !ch->eos_sent) {
    1419             :                                         void *tfrf;
    1420             :                                         const void *gf_isom_get_tfrf(GF_ISOFile *movie, u32 trackNumber);
    1421             : 
    1422       18669 :                                         ch->eos_sent = GF_TRUE;
    1423       18669 :                                         read->eos_signaled = GF_TRUE;
    1424             : 
    1425       18669 :                                         tfrf = (void *) gf_isom_get_tfrf(read->mov, ch->track);
    1426       18669 :                                         if (tfrf) {
    1427           0 :                                                 gf_filter_pid_set_info_str(ch->pid, "smooth_tfrf", &PROP_POINTER(tfrf) );
    1428             :                                         } else {
    1429       18669 :                                                 gf_filter_pid_set_info_str(ch->pid, "smooth_tfrf", NULL );
    1430             :                                         }
    1431             : 
    1432       18669 :                                         gf_filter_pid_set_eos(ch->pid);
    1433             :                                 }
    1434             :                                 break;
    1435             :                         } else {
    1436        1533 :                                 read->force_fetch = GF_TRUE;
    1437        1533 :                                 break;
    1438             :                         }
    1439             :                 }
    1440      497541 :                 if (!min_offset_plus_one || (min_offset_plus_one - 1 > ch->last_valid_sample_data_offset))
    1441      300245 :                         min_offset_plus_one = 1 + ch->last_valid_sample_data_offset;
    1442             :         }
    1443      293279 :         if (read->mem_load_mode && min_offset_plus_one) {
    1444       30768 :                 isoffin_purge_mem(read, min_offset_plus_one-1);
    1445             :         }
    1446             : 
    1447             :         //we reached end of playback due to play range request, we must send eos - however for safety reason with DASH, we first need to cancel the input
    1448      293279 :         if (read->pid && check_forced_end && (nb_forced_end==count)) {
    1449             :                 //abort input
    1450             :                 GF_FilterEvent evt;
    1451           0 :                 GF_FEVT_INIT(evt, GF_FEVT_STOP, read->pid);
    1452           0 :                 gf_filter_pid_send_event(read->pid, &evt);
    1453             :         }
    1454             : 
    1455             : 
    1456      293279 :         if (!is_active) {
    1457             :                 return GF_EOS;
    1458             :         }
    1459             :         //if (in_is_eos)
    1460             : //      gf_filter_ask_rt_reschedule(filter, 1);
    1461      279774 :         return GF_OK;
    1462             : 
    1463             : }
    1464             : 
    1465        3074 : static const char *isoffin_probe_data(const u8 *data, u32 size, GF_FilterProbeScore *score)
    1466             : {
    1467        3074 :         if (gf_isom_probe_data(data, size)) {
    1468         715 :                 *score = GF_FPROBE_SUPPORTED;
    1469         715 :                 return "video/mp4";
    1470             :         }
    1471             :         return NULL;
    1472             : }
    1473             : 
    1474             : #define OFFS(_n)        #_n, offsetof(ISOMReader, _n)
    1475             : 
    1476             : static const GF_FilterArgs ISOFFInArgs[] =
    1477             : {
    1478             :         { OFFS(src), "location of source content (only used when explicitly loading the demuxer)", GF_PROP_NAME, NULL, NULL, 0},
    1479             :         { OFFS(allt), "load all tracks even if unknown", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
    1480             :         { OFFS(noedit), "do not use edit lists", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
    1481             :         { OFFS(itt), "convert all items of root meta into a single PID", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
    1482             :         { OFFS(itemid), "keep item IDs in PID properties", GF_PROP_BOOL, "true", NULL, GF_FS_ARG_HINT_ADVANCED},
    1483             :         { OFFS(smode), "load mode for scalable/tile tracks\n"
    1484             :         "- split: each track is declared, extractors are removed\n"
    1485             :         "- splitx: each track is declared, extractors are kept\n"
    1486             :         "- single: a single track is declared (highest level for scalable, tile base for tiling)", GF_PROP_UINT, "split", "split|splitx|single", GF_FS_ARG_HINT_ADVANCED},
    1487             :         { OFFS(alltk), "declare all tracks even disabled ones", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
    1488             :         { OFFS(frame_size), "frame size for raw audio samples (dispatches frame_size samples per packet)", GF_PROP_UINT, "1024", NULL, GF_FS_ARG_HINT_ADVANCED},
    1489             :         { OFFS(expart), "expose cover art as a dedicated video pid", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
    1490             :         { OFFS(sigfrag), "signal fragment and segment boundaries of source on output packets", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
    1491             : 
    1492             :         { OFFS(tkid), "declare only track based on given param\n"
    1493             :         "- integer value: declares track with the given ID\n"
    1494             :         "- audio: declares first audio track\n"
    1495             :         "- video: declares first video track\n"
    1496             :         "- 4CC: declares first track with matching 4CC for handler type", GF_PROP_STRING, NULL, NULL, GF_FS_ARG_HINT_EXPERT},
    1497             :         { OFFS(stsd), "only extract sample mapped to the given sample description index. 0 means no filter", GF_PROP_UINT, "0", NULL, GF_FS_ARG_HINT_EXPERT},
    1498             :         { OFFS(mov), "pointer to a read/edit ISOBMF file used internally by importers and exporters", GF_PROP_POINTER, NULL, NULL, GF_FS_ARG_HINT_HIDE},
    1499             :         { OFFS(analyze), "skip reformat of decoder config and SEI and dispatch all NAL in input order - shall only be used with inspect filter analyze mode!", GF_PROP_UINT, "off", "off|on|bs|full", GF_FS_ARG_HINT_HIDE},
    1500             :         { OFFS(catseg), "append the given segment to the movie at init time (only local file supported)", GF_PROP_STRING, NULL, NULL, GF_FS_ARG_HINT_HIDE},
    1501             :         { OFFS(nocrypt), "signal encrypted tracks as non encrypted (mostly used for export)", GF_PROP_BOOL, NULL, NULL, GF_FS_ARG_HINT_ADVANCED},
    1502             :         { OFFS(mstore_size), "target buffer size in bytes", GF_PROP_UINT, "1000000", NULL, GF_FS_ARG_HINT_EXPERT},
    1503             :         { OFFS(mstore_purge), "minimum size in bytes between memory purges when reading from memory stream (pipe etc...), 0 means purge as soon as possible", GF_PROP_UINT, "50000", NULL, GF_FS_ARG_HINT_EXPERT},
    1504             :         { OFFS(mstore_samples), "minimum number of samples to be present before purging sample tables when reading from memory stream (pipe etc...), 0 means purge as soon as possible", GF_PROP_UINT, "50", NULL, GF_FS_ARG_HINT_EXPERT},
    1505             :         { OFFS(strtxt), "load text tracks (apple/tx3g) as MPEG-4 streaming text tracks", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
    1506             :         { OFFS(xps_check), "parameter sets extraction mode from AVC/HEVC/VVC samples\n"
    1507             :         "- keep: do not inspect sample (assumes input file is compliant when generating DASH/HLS/CMAF)\n"
    1508             :         "- rem: removes all inband xPS and notify configuration changes accordingly\n"
    1509             :         "- auto: resolves to `keep` for `smode=splix` (dasher mode), `rem` otherwise"
    1510             :         , GF_PROP_UINT, "auto", "auto|keep|rem", GF_FS_ARG_HINT_EXPERT},
    1511             :         { OFFS(nodata), "do not load sample data", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
    1512             :         {0}
    1513             : };
    1514             : 
    1515             : static const GF_FilterCapability ISOFFInCaps[] =
    1516             : {
    1517             :         CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
    1518             :         CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_FILE_EXT, "mp4|mpg4|m4a|m4i|3gp|3gpp|3g2|3gp2|iso|m4s|heif|heic|avci|mj2|mov|qt"),
    1519             :         CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_MIME, "application/x-isomedia|application/mp4|video/mp4|audio/mp4|video/3gpp|audio/3gpp|video/3gp2|audio/3gp2|video/iso.segment|audio/iso.segment|image/heif|image/heic|image/avci|video/quicktime"),
    1520             :         CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
    1521             :         CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
    1522             :         CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_SCENE),
    1523             :         CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_OD),
    1524             :         CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_TEXT),
    1525             :         CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_METADATA),
    1526             :         CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_ENCRYPTED),
    1527             : 
    1528             :         CAP_BOOL(GF_CAPS_OUTPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
    1529             :         //we don't set output cap for streamtype FILE for now.
    1530             : };
    1531             : 
    1532             : GF_FilterRegister ISOFFInRegister = {
    1533             :         .name = "mp4dmx",
    1534             :         GF_FS_SET_DESCRIPTION("ISOBMFF/QT demuxer")
    1535             :         GF_FS_SET_HELP("This filter demultiplexes ISOBMF and QT files (regular or fragmented).\n"
    1536             :                 "# Track Selection\n"
    1537             :                 "The filter can use fragment identifiers of source to select a single track for playback. The allowed fragments are:\n"
    1538             :                 " - #audio: only use the first audio track\n"
    1539             :                 " - #video: only use the first video track\n"
    1540             :                 " - #auxv: only use the first auxiliary video track\n"
    1541             :                 " - #pict: only use the first picture track\n"
    1542             :                 " - #text: only use the first text track\n"
    1543             :                 " - #trackID=VAL: only use the track with given ID\n"
    1544             :                 " - #ID=VAL: only use the track with given ID\n"
    1545             :                 " - #VAL: only use the track with given ID\n"
    1546             :                 "\n"
    1547             :                 "# Scalable Tracks\n"
    1548             :                 "When scalable tracks are present in a file, the reader can operate in 3 modes using [-smode]() option:\n"\
    1549             :                 "- smode=single: resolves all extractors to extract a single bitstream from a scalable set. The highest level is used\n"\
    1550             :                 "In this mode, there is no enhancement decoder config, only a base one resulting from the merge of the configs\n"\
    1551             :                 "- smode=split: all extractors are removed and every track of the scalable set is declared. In this mode, each enhancement track has no base decoder config\n"
    1552             :                 "and an enhancement decoder config.\n"\
    1553             :                 "- smode=splitx: extractors are kept in the bitstream, and every track of the scalable set is declared. In this mode, each enhancement track has a base decoder config\n"
    1554             :                 " (copied from base) and an enhancement decoder config. This is mostly used for DASHing content.\n"\
    1555             :                 "Warning: smode=splitx will result in extractor NAL units still present in the output bitstream, which shall only be true if the output is ISOBMFF based\n")
    1556             :         .private_size = sizeof(ISOMReader),
    1557             :         .args = ISOFFInArgs,
    1558             :         .initialize = isoffin_initialize,
    1559             :         .finalize = isoffin_finalize,
    1560             :         .process = isoffin_process,
    1561             :         .configure_pid = isoffin_configure_pid,
    1562             :         SETCAPS(ISOFFInCaps),
    1563             :         .process_event = isoffin_process_event,
    1564             :         .probe_data = isoffin_probe_data
    1565             : };
    1566             : 
    1567             : 
    1568             : #endif /*GPAC_DISABLE_ISOM*/
    1569             : 
    1570        2877 : const GF_FilterRegister *isoffin_register(GF_FilterSession *session)
    1571             : {
    1572             : #ifdef GPAC_DISABLE_ISOM
    1573             :         return NULL;
    1574             : #else
    1575        2877 :         return &ISOFFInRegister;
    1576             : #endif /*GPAC_DISABLE_ISOM*/
    1577             : }
    1578             : 

Generated by: LCOV version 1.13