LCOV - code coverage report
Current view: top level - isomedia - movie_fragments.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1053 1497 70.3 %
Date: 2021-04-29 23:48:07 Functions: 42 42 100.0 %

          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 / ISO Media File Format sub-project
       9             :  *
      10             :  *  GPAC is free software; you can redistribute it and/or modify
      11             :  *  it under the terms of the GNU Lesser General Public License as published by
      12             :  *  the Free Software Foundation; either version 2, or (at your option)
      13             :  *  any later version.
      14             :  *
      15             :  *  GPAC is distributed in the hope that it will be useful,
      16             :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      17             :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      18             :  *  GNU Lesser General Public License for more details.
      19             :  *
      20             :  *  You should have received a copy of the GNU Lesser General Public
      21             :  *  License along with this library; see the file COPYING.  If not, write to
      22             :  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
      23             :  *
      24             :  */
      25             : 
      26             : #include <gpac/internal/isomedia_dev.h>
      27             : 
      28             : #ifndef GPAC_DISABLE_ISOM
      29             : 
      30             : #ifndef GPAC_DISABLE_ISOM_FRAGMENTS
      31             : 
      32         864 : GF_TrackExtendsBox *GetTrex(GF_MovieBox *moov, GF_ISOTrackID TrackID)
      33             : {
      34             :         u32 i;
      35             :         GF_TrackExtendsBox *trex;
      36         864 :         i=0;
      37        2064 :         while ((trex = (GF_TrackExtendsBox *)gf_list_enum(moov->mvex->TrackExList, &i))) {
      38         809 :                 if (trex->trackID == TrackID) return trex;
      39             :         }
      40             :         return NULL;
      41             : }
      42             : 
      43             : 
      44      151318 : GF_TrackFragmentBox *gf_isom_get_traf(GF_ISOFile *mov, GF_ISOTrackID TrackID)
      45             : {
      46             :         u32 i;
      47      151318 :         if (!mov->moof) return NULL;
      48             : 
      49             :         //reverse browse the TRAFs, as there may be more than one per track ...
      50      330156 :         for (i=gf_list_count(mov->moof->TrackList); i>0; i--) {
      51      178838 :                 GF_TrackFragmentBox *traf = (GF_TrackFragmentBox *)gf_list_get(mov->moof->TrackList, i-1);
      52      178838 :                 if (traf->tfhd->trackID == TrackID) return traf;
      53             :         }
      54             :         return NULL;
      55             : }
      56             : 
      57             : 
      58             : #ifndef GPAC_DISABLE_ISOM_WRITE
      59         327 : GF_Err gf_isom_set_movie_duration(GF_ISOFile *movie, u64 duration, Bool remove_mehd)
      60             : {
      61         327 :         if (!movie || !movie->moov || !movie->moov->mvex) return GF_BAD_PARAM;
      62             : 
      63         327 :         if (remove_mehd) {
      64           0 :                 if (!movie->moov->mvex->mehd) {
      65           0 :                         gf_isom_box_del_parent(&movie->moov->mvex->child_boxes, (GF_Box*)movie->moov->mvex->mehd);
      66           0 :                         movie->moov->mvex->mehd = NULL;
      67             :                 }
      68             :         } else {
      69         327 :                 if (!movie->moov->mvex->mehd) {
      70           0 :                         movie->moov->mvex->mehd = (GF_MovieExtendsHeaderBox *) gf_isom_box_new_parent(&movie->moov->mvex->child_boxes, GF_ISOM_BOX_TYPE_MEHD);
      71           0 :                         if (!movie->moov->mvex->mehd) return GF_OUT_OF_MEM;
      72             :                 }
      73         327 :                 movie->moov->mvex->mehd->fragment_duration = duration;
      74             :         }
      75         327 :         movie->moov->mvhd->duration = 0;
      76         327 :         return GF_OK;
      77             : }
      78             : 
      79             : 
      80             : GF_EXPORT
      81         339 : GF_Err gf_isom_finalize_for_fragment(GF_ISOFile *movie, u32 media_segment_type, Bool mvex_after_tracks)
      82             : {
      83             :         GF_Err e;
      84             :         u32 i;
      85             :         Bool store_file = GF_TRUE;
      86             :         GF_TrackExtendsBox *trex;
      87         339 :         if (!movie || !movie->moov) return GF_BAD_PARAM;
      88             : 
      89             : #if 0
      90             :         if (movie->openMode==GF_ISOM_OPEN_CAT_FRAGMENTS) {
      91             :                 /*from now on we are in write mode*/
      92             :                 movie->openMode = GF_ISOM_OPEN_WRITE;
      93             :                 store_file = GF_FALSE;
      94             :                 movie->append_segment = GF_TRUE;
      95             :         } else
      96             : #endif
      97             :         {
      98         339 :                 movie->NextMoofNumber = 1;
      99             :         }
     100         339 :         movie->moov->mvex_after_traks = mvex_after_tracks;
     101             :         
     102             :         //this is only allowed in write mode
     103         339 :         if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE;
     104             : 
     105         339 :         if (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) return GF_OK;
     106         339 :         movie->FragmentsFlags = 0;
     107             : 
     108             :         if (store_file) {
     109             :                 /* add DASH brand if requested*/
     110         339 :                 if (media_segment_type)
     111         326 :                         gf_isom_modify_alternate_brand(movie, GF_ISOM_BRAND_DASH, GF_TRUE);
     112             : 
     113         339 :                 if (!movie->moov->mvex->mehd || !movie->moov->mvex->mehd->fragment_duration) {
     114             :                         //update durations
     115          12 :                         gf_isom_get_duration(movie);
     116             :                 }
     117             : 
     118         339 :                 i=0;
     119        1067 :                 while ((trex = (GF_TrackExtendsBox *)gf_list_enum(movie->moov->mvex->TrackExList, &i))) {
     120         389 :                         if (trex->type != GF_ISOM_BOX_TYPE_TREX) continue;
     121         389 :                         if (trex->track->Media->information->sampleTable->CompositionToDecode) {
     122           0 :                                 u32 k=0;
     123             :                                 GF_TrackExtensionPropertiesBox *trep;
     124           0 :                                 while ((trep = (GF_TrackExtensionPropertiesBox*) gf_list_enum(movie->moov->mvex->TrackExPropList, &k))) {
     125           0 :                                         if (trep->trackID == trex->trackID) break;
     126             :                                 }
     127             : 
     128           0 :                                 if (!trep) {
     129           0 :                                         trep = (GF_TrackExtensionPropertiesBox*) gf_isom_box_new_parent(&movie->moov->mvex->child_boxes, GF_ISOM_BOX_TYPE_TREP);
     130           0 :                                         if (!trep) return GF_OUT_OF_MEM;
     131           0 :                                         trep->trackID = trex->trackID;
     132           0 :                                         gf_list_add(movie->moov->mvex->TrackExPropList, trep);
     133             :                                 }
     134             : 
     135           0 :                                 if (!trex->track->Media->information->sampleTable->SampleSize || ! trex->track->Media->information->sampleTable->SampleSize->sampleCount) {
     136           0 :                                         gf_list_add(trep->child_boxes, trex->track->Media->information->sampleTable->CompositionToDecode);
     137           0 :                                         trex->track->Media->information->sampleTable->CompositionToDecode = NULL;
     138             :                                 } else {
     139             :                                         GF_CompositionToDecodeBox *cslg;
     140             : 
     141             :                                         //clone it!
     142           0 :                                         GF_SAFEALLOC(cslg, GF_CompositionToDecodeBox);
     143           0 :                                         if (!cslg) return GF_OUT_OF_MEM;
     144           0 :                                         memcpy(cslg, trex->track->Media->information->sampleTable->CompositionToDecode, sizeof(GF_CompositionToDecodeBox) );
     145           0 :                                         cslg->child_boxes = gf_list_new();
     146           0 :                                         gf_list_add(trep->child_boxes, trex->track->Media->information->sampleTable->CompositionToDecode);
     147             :                                 }
     148             :                         }
     149             : 
     150         389 :                         if (movie->moov->mvex->mehd && movie->moov->mvex->mehd->fragment_duration) {
     151         377 :                                 trex->track->Header->duration = 0;
     152         377 :                                 Media_SetDuration(trex->track);
     153         377 :                                 if (trex->track->editBox && trex->track->editBox->editList) {
     154          59 :                                         GF_EdtsEntry *edts = gf_list_last(trex->track->editBox->editList->entryList);
     155          59 :                                         edts->segmentDuration = 0;
     156             :                                 }
     157             :                         }
     158             :                 }
     159             : 
     160             :                 //write movie
     161         339 :                 e = WriteToFile(movie, GF_TRUE);
     162         339 :                 if (e) return e;
     163             : 
     164         339 :                 if (movie->on_block_out) {
     165         339 :                         gf_bs_seek(movie->editFileMap->bs, 0);
     166         339 :                         gf_bs_truncate(movie->editFileMap->bs);
     167             :                 }
     168             :         }
     169             : 
     170             :         //make sure we do have all we need. If not this is not an error, just consider
     171             :         //the file closed
     172         339 :         if (!movie->moov->mvex || !gf_list_count(movie->moov->mvex->TrackExList)) return GF_OK;
     173             : 
     174         339 :         i=0;
     175        1067 :         while ((trex = (GF_TrackExtendsBox *)gf_list_enum(movie->moov->mvex->TrackExList, &i))) {
     176         389 :                 if (!trex->trackID || !gf_isom_get_track_from_id(movie->moov, trex->trackID)) return GF_IO_ERR;
     177             :                 //we could also check all our data refs are local but we'll do that at run time
     178             :                 //in order to allow a mix of both (remote refs in MOOV and local in MVEX)
     179             : 
     180             :                 //one thing that MUST be done is OD cross-dependencies. The movie fragment spec
     181             :                 //is broken here, since it cannot allow dynamic insertion of new ESD and their
     182             :                 //dependancies
     183             :         }
     184             : 
     185             :         //ok we are fine - note the data map is created at the beginning
     186         339 :         if (i) movie->FragmentsFlags |= GF_ISOM_FRAG_WRITE_READY;
     187             : 
     188         339 :         if (media_segment_type) {
     189         326 :                 movie->use_segments = GF_TRUE;
     190         326 :                 movie->moof_list = gf_list_new();
     191          13 :         } else if (movie->on_block_out) {
     192          13 :                 movie->moof_list = gf_list_new();
     193             :         }
     194             : 
     195             :         /*set brands for segment*/
     196             : 
     197             :         /*"msdh": it's a media segment */
     198         339 :         gf_isom_set_brand_info(movie, GF_ISOM_BRAND_MSDH, 0);
     199             :         /*remove all brands     */
     200         339 :         gf_isom_reset_alt_brands(movie);
     201             :         /*
     202             :                 msdh: it's a media segment
     203             :                 sims: it's a media segment with an SSIX
     204             :                 msix: it's a media segment with an index
     205             :                 lmsg: it's the last media segment
     206             :         */
     207             : 
     208         339 :         return GF_OK;
     209             : }
     210             : 
     211         389 : GF_Err gf_isom_change_track_fragment_defaults(GF_ISOFile *movie, GF_ISOTrackID TrackID,
     212             :         u32 DefaultSampleDescriptionIndex,
     213             :         u32 DefaultSampleDuration,
     214             :         u32 DefaultSampleSize,
     215             :         u8 DefaultSampleIsSync,
     216             :         u8 DefaultSamplePadding,
     217             :         u16 DefaultDegradationPriority,
     218             :         u8 force_traf_flags)
     219             : {
     220             :         GF_MovieExtendsBox *mvex;
     221             :         GF_TrackExtendsBox *trex;
     222             :         GF_TrackBox *trak;
     223             : 
     224         389 :         if (!movie || !movie->moov) return GF_BAD_PARAM;
     225             :         //this is only allowed in write mode
     226         389 :         if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE;
     227             : 
     228         389 :         trak = gf_isom_get_track_from_id(movie->moov, TrackID);
     229         389 :         if (!trak) return GF_BAD_PARAM;
     230             : 
     231         389 :         mvex = movie->moov->mvex;
     232         389 :         if (!mvex) return GF_BAD_PARAM;
     233             : 
     234         389 :         trex = GetTrex(movie->moov, TrackID);
     235         389 :         if (!trex)  return GF_BAD_PARAM;
     236             : 
     237         389 :         trex->def_sample_desc_index = DefaultSampleDescriptionIndex;
     238         389 :         trex->def_sample_duration = DefaultSampleDuration;
     239         389 :         trex->def_sample_size = DefaultSampleSize;
     240         389 :         trex->def_sample_flags = GF_ISOM_FORMAT_FRAG_FLAGS(DefaultSamplePadding, DefaultSampleIsSync, DefaultDegradationPriority);
     241             :         //if sample is sync by default, set sample_depends_on flags to 2 (does not depend on other samples)
     242         389 :         if (DefaultSampleIsSync) {
     243          89 :                 trex->def_sample_flags |= (2<<24);
     244             :         }
     245         389 :         trex->cannot_use_default = GF_FALSE;
     246             : 
     247         389 :         if (force_traf_flags) {
     248          11 :                 trex->cannot_use_default = GF_TRUE;
     249         378 :         } else if (DefaultSampleDescriptionIndex == 0 && DefaultSampleDuration == 0 && DefaultSampleSize == 0
     250           0 :                 && DefaultSampleIsSync == 0 && DefaultSamplePadding == 0 && DefaultDegradationPriority == 0) {
     251           0 :                 trex->cannot_use_default = GF_TRUE;
     252             :         }
     253             :         return GF_OK;
     254             : }
     255             : 
     256             : GF_EXPORT
     257         389 : GF_Err gf_isom_setup_track_fragment(GF_ISOFile *movie, GF_ISOTrackID TrackID,
     258             :                                     u32 DefaultSampleDescriptionIndex,
     259             :                                     u32 DefaultSampleDuration,
     260             :                                     u32 DefaultSampleSize,
     261             :                                     u8 DefaultSampleIsSync,
     262             :                                     u8 DefaultSamplePadding,
     263             :                                     u16 DefaultDegradationPriority,
     264             :                                     Bool force_traf_flags)
     265             : {
     266             :         GF_MovieExtendsBox *mvex;
     267             :         GF_TrackExtendsBox *trex;
     268             :         GF_TrackBox *trak;
     269             : 
     270         389 :         if (!movie || !movie->moov) return GF_BAD_PARAM;
     271             :         //this is only allowed in write mode
     272         389 :         if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE;
     273             :         //and only at setup
     274         389 :         if (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) return GF_BAD_PARAM;
     275             : 
     276             : 
     277         389 :         trak = gf_isom_get_track_from_id(movie->moov, TrackID);
     278         389 :         if (!trak) return GF_BAD_PARAM;
     279             : 
     280             :         //create MVEX if needed
     281         389 :         if (!movie->moov->mvex) {
     282         339 :                 mvex = (GF_MovieExtendsBox *) gf_isom_box_new_parent(&movie->moov->child_boxes, GF_ISOM_BOX_TYPE_MVEX);
     283         339 :                 if (!mvex) return GF_OUT_OF_MEM;
     284         339 :                 moov_on_child_box((GF_Box*)movie->moov, (GF_Box *) mvex, GF_FALSE);
     285             :         } else {
     286             :                 mvex = movie->moov->mvex;
     287             :         }
     288         389 :         if (!mvex->mehd) {
     289         339 :                 mvex->mehd = (GF_MovieExtendsHeaderBox *) gf_isom_box_new_parent(&mvex->child_boxes, GF_ISOM_BOX_TYPE_MEHD);
     290         339 :                 if (!mvex->mehd) return GF_OUT_OF_MEM;
     291             :         }
     292             : 
     293         389 :         trex = GetTrex(movie->moov, TrackID);
     294         389 :         if (!trex) {
     295         389 :                 trex = (GF_TrackExtendsBox *) gf_isom_box_new_parent(&mvex->child_boxes, GF_ISOM_BOX_TYPE_TREX);
     296         389 :                 if (!trex) return GF_OUT_OF_MEM;
     297         389 :                 trex->trackID = TrackID;
     298         389 :                 mvex_on_child_box((GF_Box*)mvex, (GF_Box *) trex, GF_FALSE);
     299             :         }
     300         389 :         trex->track = trak;
     301         389 :         return gf_isom_change_track_fragment_defaults(movie, TrackID, DefaultSampleDescriptionIndex, DefaultSampleDuration, DefaultSampleSize, DefaultSampleIsSync, DefaultSamplePadding, DefaultDegradationPriority, force_traf_flags);
     302             : }
     303             : 
     304             : #ifdef GF_ENABLE_CTRN
     305             : GF_EXPORT
     306             : GF_Err gf_isom_enable_traf_inherit(GF_ISOFile *movie, GF_ISOTrackID TrackID, GF_ISOTrackID BaseTrackID)
     307             : {
     308             :         GF_TrackBox *trak;
     309             :         GF_TrackExtendsBox *trex;
     310             :         GF_Err e=GF_OK;
     311             :         u32 track_num;
     312             :         if (!movie || !TrackID || !BaseTrackID)
     313             :                 return GF_BAD_PARAM;
     314             :         trak = gf_isom_get_track_from_id(movie->moov, TrackID);
     315             :         if (!trak) return GF_BAD_PARAM;
     316             :         track_num = 1 + gf_list_find(movie->moov->trackList, trak);
     317             : 
     318             :         e = gf_isom_set_track_reference(movie, track_num, GF_ISOM_REF_TRIN, BaseTrackID);
     319             :         if (e) return e;
     320             : 
     321             :         trex = GetTrex(movie->moov, TrackID);
     322             :         if (!trex) return GF_BAD_PARAM;
     323             :         trex->inherit_from_traf_id = BaseTrackID;
     324             :         return GF_OK;
     325             : }
     326             : #endif
     327             : 
     328             : GF_EXPORT
     329           5 : GF_Err gf_isom_setup_track_fragment_template(GF_ISOFile *movie, GF_ISOTrackID TrackID, u8 *boxes, u32 boxes_size, u8 force_traf_flags)
     330             : {
     331             :         GF_MovieExtendsBox *mvex;
     332             :         GF_TrackBox *trak;
     333             :         GF_BitStream *bs;
     334             :         GF_Err e=GF_OK;
     335           5 :         trak = gf_isom_get_track_from_id(movie->moov, TrackID);
     336           5 :         if (!trak) return GF_BAD_PARAM;
     337             : 
     338           5 :         bs = gf_bs_new(boxes, boxes_size, GF_BITSTREAM_READ);
     339          15 :         while (gf_bs_available(bs)) {
     340           5 :                 GF_Box *box=NULL;
     341           5 :                 gf_isom_box_parse(&box, bs);
     342           5 :                 if (!box) {
     343             :                         e = GF_BAD_PARAM;
     344           0 :                         break;
     345             :                 }
     346             : 
     347           5 :                 if (box->type==GF_ISOM_BOX_TYPE_TREX) {
     348             :                         GF_TrackExtendsBox *trex_o=NULL;
     349             :                         GF_TrackExtendsBox *trex = (GF_TrackExtendsBox *) box;
     350             : 
     351             :                         //create MVEX if needed
     352           5 :                         if (!movie->moov->mvex) {
     353           2 :                                 mvex = (GF_MovieExtendsBox *) gf_isom_box_new_parent(&movie->moov->child_boxes, GF_ISOM_BOX_TYPE_MVEX);
     354           2 :                                 moov_on_child_box((GF_Box*)movie->moov, (GF_Box *) mvex, GF_FALSE);
     355             :                         } else {
     356             :                                 mvex = movie->moov->mvex;
     357             :                         }
     358           5 :                         if (!mvex->mehd) {
     359           2 :                                 mvex->mehd = (GF_MovieExtendsHeaderBox *) gf_isom_box_new_parent(&mvex->child_boxes, GF_ISOM_BOX_TYPE_MEHD);
     360             :                         }
     361             : 
     362           5 :                         trex_o = GetTrex(movie->moov, TrackID);
     363           5 :                         if (trex_o) {
     364           3 :                                 gf_list_del_item(movie->moov->mvex->TrackExList, trex_o);
     365           3 :                                 gf_isom_box_del_parent(&movie->moov->mvex->child_boxes, (GF_Box *)trex_o);
     366             :                         }
     367           5 :                         trex->trackID = TrackID;
     368           5 :                         trex->track = trak;
     369           5 :                         if (force_traf_flags) trex->cannot_use_default = GF_TRUE;
     370           5 :                         gf_list_add(mvex->child_boxes, trex);
     371           5 :                         mvex_on_child_box((GF_Box*)mvex, (GF_Box *) trex, GF_FALSE);
     372             :                 }
     373             :         }
     374           5 :         gf_bs_del(bs);
     375           5 :         return e;
     376             : }
     377             : 
     378             : 
     379      232991 : u32 GetNumUsedValues(GF_TrackFragmentBox *traf, u32 value, u32 index)
     380             : {
     381             :         u32 i, j, NumValue = 0;
     382             :         GF_TrackFragmentRunBox *trun;
     383             : 
     384      232991 :         i=0;
     385      699393 :         while ((trun = (GF_TrackFragmentRunBox *)gf_list_enum(traf->TrackRuns, &i))) {
     386    12021319 :                 for (j=0; j<trun->nb_samples; j++) {
     387    12021319 :                         GF_TrunEntry *ent = &trun->samples[j];
     388    12021319 :                         switch (index) {
     389     5848823 :                         case 1:
     390     5848823 :                                 if (value == ent->Duration) NumValue ++;
     391             :                                 break;
     392      292914 :                         case 2:
     393      292914 :                                 if (value == ent->size) NumValue ++;
     394             :                                 break;
     395     5879582 :                         case 3:
     396     5879582 :                                 if (value == ent->flags) NumValue ++;
     397             :                                 break;
     398             :                         }
     399             :                 }
     400             :         }
     401      232991 :         return NumValue;
     402             : }
     403             : 
     404             : 
     405        4584 : void ComputeFragmentDefaults(GF_TrackFragmentBox *traf)
     406             : {
     407             :         u32 i, j, MaxNum, DefValue, ret;
     408             :         GF_TrackFragmentRunBox *trun;
     409             :         GF_TrunEntry *ent;
     410             : 
     411             :         //Duration default
     412             :         MaxNum = DefValue = 0;
     413        4584 :         i=0;
     414       12920 :         while ((trun = (GF_TrackFragmentRunBox *)gf_list_enum(traf->TrackRuns, &i))) {
     415      111103 :                 for (j=0; j<trun->nb_samples; j++) {
     416      111126 :                         ent = &trun->samples[j];
     417      111126 :                         ret = GetNumUsedValues(traf, ent->Duration, 1);
     418      111126 :                         if (ret>MaxNum) {
     419             :                                 //at least 2 duration, specify for all
     420        3787 :                                 if (MaxNum) {
     421             :                                         DefValue = 0;
     422             :                                         goto escape_duration;
     423             :                                 }
     424             :                                 MaxNum = ret;
     425        3764 :                                 DefValue = ent->Duration;
     426             :                         }
     427             :                 }
     428             :         }
     429        4561 : escape_duration:
     430             :         //store if #
     431        4584 :         if (DefValue && ((DefValue != traf->trex->def_sample_duration) || traf->trex->cannot_use_default ) ) {
     432         444 :                 traf->tfhd->def_sample_duration = DefValue;
     433             :         }
     434             : 
     435             :         //Size default
     436             :         MaxNum = DefValue = 0;
     437        4584 :         i=0;
     438        9347 :         while ((trun = (GF_TrackFragmentRunBox *)gf_list_enum(traf->TrackRuns, &i))) {
     439        6366 :                 for (j=0; j<trun->nb_samples; j++) {
     440             :                         u32 ssize;
     441        9951 :                         ent = &trun->samples[j];
     442        9951 :                         ssize = ent->size;
     443        9951 :                         if (ent->nb_pack>1)
     444           0 :                                 ssize /= ent->nb_pack;
     445        9951 :                         ret = GetNumUsedValues(traf, ssize, 2);
     446        9951 :                         if (ret>MaxNum || (ret==1)) {
     447             :                                 //at least 2 sizes so we must specify all sizes
     448        7349 :                                 if (MaxNum) {
     449             :                                         DefValue = 0;
     450             :                                         goto escape_size;
     451             :                                 }
     452             :                                 MaxNum = ret;
     453             :                                 DefValue = ssize;
     454             :                         }
     455             :                 }
     456             :         }
     457             : 
     458         999 : escape_size:
     459             :         //store if #
     460        4584 :         if (DefValue && (DefValue != traf->trex->def_sample_size)) {
     461         179 :                 traf->tfhd->def_sample_size = DefValue;
     462             :         }
     463             : 
     464             :         //Flags default
     465             :         MaxNum = DefValue = 0;
     466        4584 :         i=0;
     467       12943 :         while ((trun = (GF_TrackFragmentRunBox *)gf_list_enum(traf->TrackRuns, &i))) {
     468      111914 :                 for (j=0; j<trun->nb_samples; j++) {
     469      111914 :                         ent = &trun->samples[j];
     470      111914 :                         ret = GetNumUsedValues(traf, ent->flags, 3);
     471      111914 :                         if (ret>MaxNum) {
     472             :                                 MaxNum = ret;
     473        6233 :                                 DefValue = ent->flags;
     474             :                         }
     475             :                 }
     476             :         }
     477             :         //store if #
     478        4584 :         if (traf->trex->cannot_use_default || (DefValue && (DefValue != traf->trex->def_sample_flags))) {
     479         394 :                 traf->tfhd->def_sample_flags = DefValue;
     480             :         }
     481        4584 : }
     482             : 
     483             : GF_EXPORT
     484         472 : GF_Err gf_isom_set_fragment_option(GF_ISOFile *movie, GF_ISOTrackID TrackID, GF_ISOTrackFragmentOption Code, u32 Param)
     485             : {
     486             :         GF_TrackFragmentBox *traf;
     487         472 :         if (!movie || !movie->moov) return GF_BAD_PARAM;
     488             :         //this is only allowed in write mode
     489         472 :         if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE;
     490             : 
     491         472 :         switch (Code) {
     492           0 :         case GF_ISOM_TRAF_EMPTY:
     493           0 :                 traf = gf_isom_get_traf(movie, TrackID);
     494           0 :                 if (!traf) return GF_BAD_PARAM;
     495           0 :                 traf->tfhd->EmptyDuration = Param;
     496           0 :                 break;
     497         137 :         case GF_ISOM_TRAF_RANDOM_ACCESS:
     498         137 :                 traf = gf_isom_get_traf(movie, TrackID);
     499         137 :                 if (!traf) return GF_BAD_PARAM;
     500         137 :                 traf->IFrameSwitching = Param;
     501         137 :                 break;
     502           0 :         case GF_ISOM_TRAF_DATA_CACHE:
     503           0 :                 traf = gf_isom_get_traf(movie, TrackID);
     504           0 :                 if (!traf) return GF_BAD_PARAM;
     505             :                 //don't cache only one sample ...
     506           0 :                 traf->DataCache = Param > 1 ? Param : 0;
     507           0 :                 break;
     508         335 :         case GF_ISOM_TFHD_FORCE_MOOF_BASE_OFFSET:
     509         335 :                 movie->force_moof_base_offset = Param;
     510         335 :                 break;
     511           0 :         case GF_ISOM_TRAF_USE_SAMPLE_DEPS_BOX:
     512           0 :                 traf = gf_isom_get_traf(movie, TrackID);
     513           0 :                 if (!traf) return GF_BAD_PARAM;
     514           0 :                 traf->use_sdtp = (u8) Param;
     515           0 :                 break;
     516           0 :         case GF_ISOM_TRUN_FORCE:
     517           0 :                 traf = gf_isom_get_traf(movie, TrackID);
     518           0 :                 if (!traf) return GF_BAD_PARAM;
     519           0 :                 traf->force_new_trun = 1;
     520           0 :                 break;
     521           0 :         case GF_ISOM_TRUN_SET_INTERLEAVE_ID:
     522           0 :                 traf = gf_isom_get_traf(movie, TrackID);
     523           0 :                 if (!traf) return GF_BAD_PARAM;
     524           0 :                 traf->DataCache = 1;
     525           0 :                 traf->use_sample_interleave = 1;
     526           0 :                 if (traf->interleave_id != Param) {
     527           0 :                         traf->force_new_trun = 1;
     528           0 :                         traf->interleave_id = Param;
     529             :                 }
     530             :                 break;
     531           0 :         case GF_ISOM_TRAF_TRUNS_FIRST:
     532           0 :                 traf = gf_isom_get_traf(movie, TrackID);
     533           0 :                 if (!traf) return GF_BAD_PARAM;
     534           0 :                 traf->truns_first = Param;
     535           0 :                 break;
     536           0 :         case GF_ISOM_TRAF_TRUN_V1:
     537           0 :                 traf = gf_isom_get_traf(movie, TrackID);
     538           0 :                 if (!traf) return GF_BAD_PARAM;
     539           0 :                 traf->truns_v1 = Param;
     540           0 :                 break;
     541           0 :         case GF_ISOM_TRAF_USE_LARGE_TFDT:
     542           0 :                 traf = gf_isom_get_traf(movie, TrackID);
     543           0 :                 if (!traf) return GF_BAD_PARAM;
     544           0 :                 traf->large_tfdt = Param;
     545           0 :                 movie->force_sidx_v1 = Param ? GF_TRUE : GF_FALSE;
     546           0 :                 break;
     547             :         }
     548             :         return GF_OK;
     549             : }
     550             : 
     551             : //#define USE_BASE_DATA_OFFSET
     552             : 
     553        7028 : void update_trun_offsets(GF_ISOFile *movie, s32 offset)
     554             : {
     555             : #ifndef USE_BASE_DATA_OFFSET
     556             :         u32 i, j;
     557             :         GF_TrackFragmentBox *traf;
     558        7028 :         i=0;
     559       22207 :         while ((traf = (GF_TrackFragmentBox*)gf_list_enum(movie->moof->TrackList, &i))) {
     560             :                 GF_TrackFragmentRunBox *trun;
     561             :                 /*remove base data*/
     562        8151 :                 traf->tfhd->base_data_offset = 0;
     563        8151 :                 j=0;
     564       23654 :                 while ((trun = (GF_TrackFragmentRunBox*)gf_list_enum(traf->TrackRuns, &j))) {
     565        7352 :                         if ((j==1) || traf->use_sample_interleave) {
     566        7334 :                                 trun->data_offset += offset;
     567             :                         } else {
     568          18 :                                 trun->data_offset = 0;
     569             :                         }
     570             :                 }
     571             :         }
     572             : #endif
     573        7028 : }
     574             : 
     575             : static
     576        4584 : u32 UpdateRuns(GF_ISOFile *movie, GF_TrackFragmentBox *traf)
     577             : {
     578             :         u32 sampleCount, i, j, RunSize, RunDur, RunFlags, NeedFlags, UseCTS;
     579             :         /* enum:
     580             :            0 - use values per sample in the trun box
     581             :            1 - use default values from track fragment header
     582             :            2 - use default values from track extends header */
     583             :         u32 UseDefaultSize, UseDefaultDur, UseDefaultFlag;
     584             :         GF_TrackFragmentRunBox *trun;
     585             :         GF_TrunEntry *ent;
     586             : 
     587             :         sampleCount = 0;
     588             : 
     589             : #ifndef USE_BASE_DATA_OFFSET
     590        4584 :         if (movie->use_segments) {
     591        4436 :                 traf->tfhd->flags = GF_ISOM_MOOF_BASE_OFFSET;
     592             :         } else
     593             : #endif
     594             :         {
     595         148 :                 if (movie->force_moof_base_offset) {
     596          48 :                         traf->tfhd->flags = GF_ISOM_MOOF_BASE_OFFSET;
     597             :                 } else {
     598         100 :                         traf->tfhd->flags = GF_ISOM_TRAF_BASE_OFFSET;
     599             :                 }
     600             :         }
     601             : 
     602             :         //empty runs
     603        4584 :         if (traf->tfhd->EmptyDuration) {
     604           0 :                 while (gf_list_count(traf->TrackRuns)) {
     605           0 :                         trun = (GF_TrackFragmentRunBox *)gf_list_get(traf->TrackRuns, 0);
     606           0 :                         gf_list_rem(traf->TrackRuns, 0);
     607           0 :                         gf_isom_box_del_parent(&traf->child_boxes, (GF_Box *)trun);
     608             :                 }
     609           0 :                 traf->tfhd->flags |= GF_ISOM_TRAF_DUR_EMPTY;
     610           0 :                 if (traf->tfhd->EmptyDuration != traf->trex->def_sample_duration) {
     611           0 :                         traf->tfhd->def_sample_duration = traf->tfhd->EmptyDuration;
     612           0 :                         traf->tfhd->flags |= GF_ISOM_TRAF_SAMPLE_DUR;
     613             :                 }
     614             :                 return 0;
     615             :         }
     616             : 
     617             : 
     618             :         UseDefaultSize = 0;
     619             :         UseDefaultDur = 0;
     620             :         UseDefaultFlag = 0;
     621             : 
     622        4584 :         i=0;
     623        8359 :         while ((trun = (GF_TrackFragmentRunBox *)gf_list_enum(traf->TrackRuns, &i))) {
     624             :                 GF_TrunEntry *first_ent = NULL;
     625             :                 RunSize = 0;
     626             :                 RunDur = 0;
     627             :                 RunFlags = 0;
     628             :                 UseCTS = 0;
     629             :                 NeedFlags = 0;
     630             : 
     631             :                 //process all samples in run
     632      111914 :                 for (j=0; j<trun->nb_samples; j++) {
     633      111914 :                         ent = &trun->samples[j];
     634      111914 :                         if (!j) {
     635             :                                 first_ent = ent;
     636        3775 :                                 RunSize = ent->size;
     637        3775 :                                 if (ent->nb_pack) RunSize /= ent->nb_pack;
     638        3775 :                                 RunDur = ent->Duration;
     639             :                         }
     640             :                         //we may have one entry only ...
     641      111914 :                         if (j || (trun->nb_samples==1)) {
     642      108209 :                                 u32 ssize = ent->size;
     643      108209 :                                 if (ent->nb_pack) ssize /= ent->nb_pack;
     644             : 
     645             :                                 //flags are only after first entry
     646      108209 :                                 if (j==1 || (trun->nb_samples==1) ) RunFlags = ent->flags;
     647             : 
     648      108209 :                                 if (ssize != RunSize) RunSize = 0;
     649      108209 :                                 if (ent->Duration != RunDur)
     650             :                                         RunDur = 0;
     651      108209 :                                 if (j && (RunFlags != ent->flags)) NeedFlags = 1;
     652             :                         }
     653      111914 :                         if (ent->CTS_Offset) UseCTS = 1;
     654             :                 }
     655             :                 //empty list
     656        3775 :                 if (!first_ent) {
     657           0 :                         i--;
     658           0 :                         gf_list_rem(traf->TrackRuns, i);
     659           0 :                         continue;
     660             :                 }
     661        3775 :                 trun->flags = 0;
     662             : 
     663             :                 //size checking
     664             :                 //constant size, check if this is from current fragment default or global default
     665        3775 :                 if (RunSize && (traf->trex->def_sample_size == RunSize) && !traf->trex->cannot_use_default) {
     666           0 :                         if (!UseDefaultSize) UseDefaultSize = 2;
     667           0 :                         else if (UseDefaultSize==1) RunSize = 0;
     668        3775 :                 } else if (RunSize && (traf->tfhd->def_sample_size == RunSize)) {
     669         179 :                         if (!UseDefaultSize) UseDefaultSize = 1;
     670           0 :                         else if (UseDefaultSize==2) RunSize = 0;
     671             :                 }
     672             :                 //we could check for single entry runs and set the default size in the tfhd but
     673             :                 //that's no bit saving...
     674             :                 else {
     675             :                         RunSize=0;
     676             :                 }
     677             : 
     678         179 :                 if (!RunSize) trun->flags |= GF_ISOM_TRUN_SIZE;
     679             : 
     680             :                 //duration checking
     681        3775 :                 if (RunDur && (traf->trex->def_sample_duration == RunDur) && !traf->trex->cannot_use_default) {
     682        3291 :                         if (!UseDefaultDur) UseDefaultDur = 2;
     683          11 :                         else if (UseDefaultDur==1) RunDur = 0;
     684         484 :                 } else if (RunDur && (traf->tfhd->def_sample_duration == RunDur)) {
     685         374 :                         if (!UseDefaultDur) UseDefaultDur = 1;
     686           0 :                         else if (UseDefaultDur==2) RunDur = 0;
     687             :                 }
     688        3775 :                 if (!RunDur) trun->flags |= GF_ISOM_TRUN_DURATION;
     689             : 
     690             :                 //flag checking
     691        3775 :                 if (!NeedFlags) {
     692             :                         // all samples flags are the same after the 2nd entry
     693        3635 :                         if (RunFlags == traf->trex->def_sample_flags && !traf->trex->cannot_use_default) {
     694             :                                 /* this run can use trex flags */
     695        3287 :                                 if (!UseDefaultFlag) {
     696             :                                         /* if all previous runs used explicit flags per sample, we can still use trex flags for this run */
     697             :                                         UseDefaultFlag = 2;
     698          11 :                                 } else if (UseDefaultFlag==1) {
     699             :                                         /* otherwise if one of the previous runs did use tfhd flags,
     700             :                                         we have no choice but to explicitly use flags per sample for this run */
     701             :                                         NeedFlags = GF_TRUE;
     702             :                                 }
     703         348 :                         } else if (RunFlags == traf->tfhd->def_sample_flags) {
     704             :                                 /* this run can use tfhd flags */
     705         348 :                                 if (!UseDefaultFlag) {
     706             :                                         /* if all previous runs used explicit flags per sample, we can still use tfhd flags for this run */
     707             :                                         UseDefaultFlag = 1;
     708           0 :                                 } else if(UseDefaultFlag==2) {
     709             :                                         /* otherwise if one of the previous runs did use trex flags,
     710             :                                         we have no choice but to explicitly use flags per sample for this run */
     711             :                                         NeedFlags = GF_TRUE;
     712             :                                 }
     713             :                         } else {
     714             :                                 /* the flags for the 2nd and following entries are different from trex and tfhd default values
     715             :                                    (possible case: 2 samples in trun, and first sample was used to set default flags) */
     716             :                                 NeedFlags = GF_TRUE;
     717             :                         }
     718             :                 }
     719        3775 :                 if (NeedFlags) {
     720             :                         //one flags entry per sample only
     721         140 :                         trun->flags |= GF_ISOM_TRUN_FLAGS;
     722             :                 } else {
     723             :                         /* this run can use default flags for the 2nd and following entries,
     724             :                            we just need to check if the first entry flags need to be singled out*/
     725        3635 :                         if (first_ent->flags != RunFlags) {
     726        2386 :                                 trun->flags |= GF_ISOM_TRUN_FIRST_FLAG;
     727             :                         }
     728             :                 }
     729             : 
     730             :                 //CTS flag
     731        3775 :                 if (UseCTS) trun->flags |= GF_ISOM_TRUN_CTS_OFFSET;
     732             : 
     733             :                 //run data offset if the offset indicated is 0 (first sample in this MDAT) don't
     734             :                 //indicate it
     735        3775 :                 if (trun->data_offset)
     736        3709 :                         trun->flags |= GF_ISOM_TRUN_DATA_OFFSET;
     737             : 
     738        3775 :                 sampleCount += trun->sample_count;
     739             :         }
     740             : 
     741             :         //after all runs in the traf are processed, update TRAF flags
     742        4584 :         if (UseDefaultSize==1)
     743         179 :                 traf->tfhd->flags |= GF_ISOM_TRAF_SAMPLE_SIZE;
     744        4584 :         if (UseDefaultDur==1)
     745         374 :                 traf->tfhd->flags |= GF_ISOM_TRAF_SAMPLE_DUR;
     746        4584 :         if (UseDefaultFlag==1)
     747         348 :                 traf->tfhd->flags |= GF_ISOM_TRAF_SAMPLE_FLAGS;
     748        4584 :         if (traf->trex->cannot_use_default || (traf->tfhd->sample_desc_index != traf->trex->def_sample_desc_index))
     749        1126 :                 traf->tfhd->flags |= GF_ISOM_TRAF_SAMPLE_DESC;
     750             : 
     751             : 
     752             :         return sampleCount;
     753             : }
     754             : 
     755        2442 : static u32 moof_get_sap_info(GF_MovieFragmentBox *moof, GF_ISOTrackID refTrackID, u32 *sap_delta, Bool *starts_with_sap)
     756             : {
     757             :         u32 i, j, count, delta, earliest_cts, sap_type, sap_sample_num, cur_sample;
     758             :         Bool first = GF_TRUE;
     759             :         GF_TrunEntry *ent;
     760             :         GF_TrackFragmentBox *traf=NULL;
     761             :         GF_TrackFragmentRunBox *trun;
     762             :         sap_type = 0;
     763        2442 :         *sap_delta = 0;
     764        2442 :         *starts_with_sap = GF_FALSE;
     765        2442 :         for (i=0; i<gf_list_count(moof->TrackList); i++) {
     766        2442 :                 traf = (GF_TrackFragmentBox*)gf_list_get(moof->TrackList, i);
     767        2442 :                 if (traf->tfhd->trackID==refTrackID) break;
     768             :                 traf=NULL;
     769             :         }
     770        2442 :         if (!traf) return sap_type;
     771             :         earliest_cts = 0;
     772             : 
     773             :         /*first check if we have a roll/rap sample in this traf, and mark its sample count*/
     774             :         sap_type = 0;
     775             :         sap_sample_num = 0;
     776             :         /*check RAP and ROLL*/
     777        2442 :         count = traf->sampleGroups ? gf_list_count(traf->sampleGroups) : 0;
     778        2625 :         for (i=0; i<count; i++) {
     779             :                 GF_SampleGroupBox *sg;
     780             :                 u32 first_sample;
     781             :                 Bool rap_type = GF_FALSE;
     782         183 :                 sg = (GF_SampleGroupBox*)gf_list_get(traf->sampleGroups, i);
     783             : 
     784         183 :                 switch (sg->grouping_type) {
     785         110 :                 case GF_ISOM_SAMPLE_GROUP_RAP:
     786             :                 case GF_ISOM_SAMPLE_GROUP_SYNC:
     787             :                         rap_type = GF_TRUE;
     788             :                         break;
     789             :                 case GF_ISOM_SAMPLE_GROUP_ROLL:
     790             :                         break;
     791          58 :                 default:
     792          58 :                         continue;
     793             :                 }
     794             :                 /*first entry is SAP*/
     795             :                 first_sample = 1;
     796         233 :                 for (j=0; j<sg->entry_count; j++) {
     797         156 :                         if (! sg->sample_entries[j].group_description_index) {
     798          54 :                                 first_sample += sg->sample_entries[j].sample_count;
     799          54 :                                 continue;
     800             :                         }
     801         102 :                         if (!j) {
     802          71 :                                 *starts_with_sap = GF_TRUE;
     803             :                                 sap_sample_num = 0;
     804             :                         }
     805         102 :                         if (!sap_sample_num || (sap_sample_num>first_sample)) {
     806         102 :                                 sap_type = rap_type ? 3 : 4;
     807             :                                 sap_sample_num = first_sample;
     808             :                         }
     809             :                         break;
     810             :                 }
     811             :         }
     812             : 
     813             :         /*then browse all samples, looking for SYNC flag or sap_sample_num*/
     814             :         cur_sample = 1;
     815             :         delta = 0;
     816        2442 :         i=0;
     817        2456 :         while ((trun = (GF_TrackFragmentRunBox*)gf_list_enum(traf->TrackRuns, &i))) {
     818        2442 :                 if (trun->flags & GF_ISOM_TRUN_FIRST_FLAG) {
     819        1749 :                         if (GF_ISOM_GET_FRAG_SYNC(trun->flags)) {
     820        1749 :                                 ent = &trun->samples[0];
     821             : //                              if (!delta) earliest_cts = ent->CTS_Offset;
     822        1749 :                                 *sap_delta = delta + ent->CTS_Offset - ent->CTS_Offset;
     823        1749 :                                 *starts_with_sap = first;
     824        1749 :                                 sap_type = ent->SAP_type;
     825             :                                 return sap_type;
     826             :                         }
     827             :                 }
     828         457 :                 for (j=0; j<trun->nb_samples; j++) {
     829        1136 :                         ent = &trun->samples[j];
     830        1136 :                         if (!delta) earliest_cts = ent->CTS_Offset;
     831             : 
     832        1136 :                         if (GF_ISOM_GET_FRAG_SYNC(ent->flags)) {
     833         599 :                                 *sap_delta = delta + ent->CTS_Offset - earliest_cts;
     834         599 :                                 *starts_with_sap = first;
     835         599 :                                 sap_type = ent->SAP_type;
     836             :                                 return sap_type;
     837             :                         }
     838             :                         /*we found our roll or rap sample*/
     839         537 :                         if (cur_sample==sap_sample_num) {
     840          80 :                                 *sap_delta = delta + ent->CTS_Offset - earliest_cts;
     841             :                                 return sap_type;
     842             :                         }
     843         457 :                         delta += ent->Duration;
     844             :                         first = GF_FALSE;
     845         457 :                         cur_sample++;
     846             :                 }
     847             :         }
     848             :         /*not found*/
     849             :         return 0;
     850             : }
     851             : 
     852        2442 : u32 moof_get_duration(GF_MovieFragmentBox *moof, GF_ISOTrackID refTrackID)
     853             : {
     854             :         u32 i, j, duration;
     855             :         GF_TrackFragmentBox *traf = NULL;
     856             :         GF_TrackFragmentRunBox *trun;
     857        2442 :         for (i=0; i<gf_list_count(moof->TrackList); i++) {
     858        2442 :                 traf = (GF_TrackFragmentBox*)gf_list_get(moof->TrackList, i);
     859        2442 :                 if (traf->tfhd->trackID==refTrackID) break;
     860             :                 traf=NULL;
     861             :         }
     862        2442 :         if (!traf) return 0;
     863             : 
     864             :         duration = 0;
     865        2442 :         i=0;
     866        7326 :         while ((trun = (GF_TrackFragmentRunBox*)gf_list_enum(traf->TrackRuns, &i))) {
     867       85655 :                 for (j=0; j<trun->nb_samples; j++) {
     868       85655 :                         GF_TrunEntry *ent = &trun->samples[j];
     869       85655 :                         if (ent->flags & GF_ISOM_TRAF_SAMPLE_DUR)
     870           0 :                                 duration += ent->Duration;
     871             :                         else
     872       85655 :                                 duration += traf->trex->def_sample_duration;
     873             :                 }
     874             :         }
     875             :         return duration;
     876             : }
     877             : 
     878        4516 : static u64 moof_get_earliest_cts(GF_MovieFragmentBox *moof, GF_ISOTrackID refTrackID)
     879             : {
     880             :         u32 i, j;
     881             :         u64 cts, duration;
     882             :         GF_TrackFragmentBox *traf=NULL;
     883             :         GF_TrackFragmentRunBox *trun;
     884        4516 :         for (i=0; i<gf_list_count(moof->TrackList); i++) {
     885        4516 :                 traf = (GF_TrackFragmentBox*)gf_list_get(moof->TrackList, i);
     886        4516 :                 if (traf->tfhd->trackID==refTrackID) break;
     887             :                 traf=NULL;
     888             :         }
     889        4516 :         if (!traf) return 0;
     890             : 
     891             :         duration = 0;
     892             :         cts = (u64) -1;
     893        4516 :         i=0;
     894        9032 :         while ((trun = (GF_TrackFragmentRunBox*)gf_list_enum(traf->TrackRuns, &i))) {
     895      158806 :                 for (j=0; j<trun->nb_samples; j++) {
     896      158806 :                         GF_TrunEntry *ent = &trun->samples[j];
     897      158806 :                         if (duration + ent->CTS_Offset < cts)
     898             :                                 cts = duration + ent->CTS_Offset;
     899      158806 :                         duration += ent->Duration;
     900             :                 }
     901             :         }
     902             :         return cts;
     903             : }
     904             : 
     905             : 
     906             : GF_Err gf_isom_write_compressed_box(GF_ISOFile *mov, GF_Box *root_box, u32 repl_type, GF_BitStream *bs, u32 *box_csize);
     907             : 
     908        7155 : static GF_Err StoreFragment(GF_ISOFile *movie, Bool load_mdat_only, s32 data_offset_diff, u32 *moof_size, Bool reassign_bs)
     909             : {
     910             :         GF_Err e;
     911             :         u64 moof_start, pos;
     912             :         u32 size, i, s_count, mdat_size;
     913             :         s32 offset;
     914             :         u8 *buffer;
     915             :         GF_TrackFragmentBox *traf;
     916             :         GF_TrackFragmentRunBox *trun;
     917             :         GF_BitStream *bs, *bs_orig;
     918        7155 :         if (!movie->moof) return GF_OK;
     919             : 
     920        7142 :         bs = movie->editFileMap->bs;
     921        7142 :         if (!movie->moof_first) load_mdat_only = GF_FALSE;
     922             :         mdat_size = 0;
     923             :         //1 - flush all caches
     924        7142 :         i=0;
     925       23452 :         while ((traf = (GF_TrackFragmentBox*)gf_list_enum(movie->moof->TrackList, &i))) {
     926             :                 u32 j, nb_written, last_gid, cur_sample_idx;
     927             :                 /*do not write empty senc*/
     928        9168 :                 if (traf->sample_encryption && !gf_list_count(traf->sample_encryption->samp_aux_info)) {
     929           7 :                         gf_list_del_item(traf->child_boxes, traf->sample_encryption);
     930           7 :                         gf_isom_box_del((GF_Box *) traf->sample_encryption);
     931           7 :                         traf->sample_encryption = NULL;
     932             :                         /*remove saiz and saio (todo, check if other saiz/saio types are used*/
     933          14 :                         for (j=0; j<gf_list_count(traf->sai_sizes); j++) {
     934           7 :                                 GF_SampleAuxiliaryInfoSizeBox *saiz = gf_list_get(traf->sai_sizes, j);
     935           7 :                                 switch (saiz->aux_info_type) {
     936           7 :                                 case GF_ISOM_CENC_SCHEME:
     937             :                                 case GF_ISOM_CBC_SCHEME:
     938             :                                 case GF_ISOM_CENS_SCHEME:
     939             :                                 case GF_ISOM_CBCS_SCHEME:
     940             :                                 case 0:
     941           7 :                                         gf_list_rem(traf->sai_sizes, j);
     942           7 :                                         gf_list_del_item(traf->child_boxes, saiz);
     943           7 :                                         gf_isom_box_del((GF_Box *)saiz);
     944           7 :                                         j--;
     945           7 :                                         break;
     946             :                                 }
     947             :                         }
     948           7 :                         for (j=0; j<gf_list_count(traf->sai_offsets); j++) {
     949           7 :                                 GF_SampleAuxiliaryInfoOffsetBox *saio = gf_list_get(traf->sai_offsets, j);
     950           7 :                                 switch (saio->aux_info_type) {
     951           7 :                                 case GF_ISOM_CENC_SCHEME:
     952             :                                 case GF_ISOM_CBC_SCHEME:
     953             :                                 case GF_ISOM_CENS_SCHEME:
     954             :                                 case GF_ISOM_CBCS_SCHEME:
     955             :                                 case 0:
     956           7 :                                         gf_list_rem(traf->sai_offsets, j);
     957           7 :                                         gf_list_del_item(traf->child_boxes, saio);
     958           7 :                                         gf_isom_box_del((GF_Box *)saio);
     959           7 :                                         j--;
     960           7 :                                         break;
     961             :                                 }
     962             :                         }
     963             :                 }
     964        9168 :                 if (!traf->DataCache) continue;
     965           0 :                 s_count = gf_list_count(traf->TrackRuns);
     966           0 :                 if (!s_count) continue;
     967             : 
     968             :                 //store all cached truns - there may be more than one when using sample interleaving in truns
     969             :                 nb_written = 0;
     970             :                 last_gid = 0;
     971             :                 cur_sample_idx = 0;
     972           0 :                 while (nb_written<s_count) {
     973             :                         u32 min_next_gid = 0xFFFFFFFF;
     974             : 
     975           0 :                         for (j=0; j<s_count; j++) {
     976           0 :                                 trun = (GF_TrackFragmentRunBox *)gf_list_get(traf->TrackRuns, j);
     977             :                                 //done
     978           0 :                                 if (!trun->cache || !trun->sample_count) continue;
     979             : 
     980           0 :                                 if (!traf->use_sample_interleave || (last_gid!=trun->interleave_id)) {
     981           0 :                                         if (trun->interleave_id < min_next_gid)
     982             :                                                 min_next_gid = trun->interleave_id;
     983           0 :                                         continue;
     984             :                                 }
     985             : 
     986             :                                 //update offset
     987           0 :                                 trun->data_offset = (u32) (gf_bs_get_position(bs) - movie->moof->fragment_offset - 8);
     988             :                                 //write cache
     989           0 :                                 gf_bs_get_content(trun->cache, &buffer, &size);
     990           0 :                                 gf_bs_write_data(bs, buffer, size);
     991           0 :                                 gf_bs_del(trun->cache);
     992           0 :                                 gf_free(buffer);
     993           0 :                                 trun->cache = NULL;
     994           0 :                                 trun->first_sample_idx = cur_sample_idx;
     995           0 :                                 cur_sample_idx += trun->sample_count;
     996             : 
     997           0 :                                 nb_written++;
     998             :                         }
     999             :                         last_gid = min_next_gid;
    1000             :                 }
    1001             : 
    1002           0 :                 traf->DataCache=0;
    1003             : 
    1004             :                 /*merge all truns*/
    1005           0 :                 if (traf->merge_sample_interleave) {
    1006             :                         u32 k, cur_idx = 0;
    1007           0 :                         trun = (GF_TrackFragmentRunBox *)gf_list_get(traf->TrackRuns, 0);
    1008           0 :                         trun->sample_order = gf_malloc(sizeof(u32) * cur_sample_idx);
    1009             : 
    1010           0 :                         for (k=0; k<trun->sample_count; k++) {
    1011           0 :                                 trun->sample_order[cur_idx] = trun->first_sample_idx + k;
    1012           0 :                                 cur_idx ++;
    1013             :                         }
    1014             : 
    1015           0 :                         while (s_count>1) {
    1016           0 :                                 GF_TrackFragmentRunBox *atrun = (GF_TrackFragmentRunBox *)gf_list_get(traf->TrackRuns, 1);
    1017           0 :                                 trun->sample_count += atrun->sample_count;
    1018             : 
    1019           0 :                                 trun->sample_alloc = trun->nb_samples + atrun->nb_samples;
    1020           0 :                                 trun->samples = gf_realloc(trun->samples, sizeof(GF_TrunEntry) * trun->sample_alloc);
    1021           0 :                                 if (!trun->samples) return GF_OUT_OF_MEM;
    1022             : 
    1023           0 :                                 memcpy(&trun->samples[trun->nb_samples], atrun->samples, sizeof(GF_TrunEntry)*atrun->nb_samples);
    1024           0 :                                 trun->nb_samples += atrun->nb_samples;
    1025             : 
    1026           0 :                                 for (k=0; k<atrun->sample_count; k++) {
    1027           0 :                                         trun->sample_order[cur_idx] = atrun->first_sample_idx + k;
    1028           0 :                                         cur_idx ++;
    1029             :                                 }
    1030           0 :                                 gf_list_rem(traf->TrackRuns, 1);
    1031           0 :                                 gf_list_del_item(traf->child_boxes, atrun);
    1032           0 :                                 gf_isom_box_del((GF_Box*)atrun);
    1033           0 :                                 s_count--;
    1034             :                         }
    1035             : 
    1036             :                 }
    1037             :         }
    1038             : 
    1039        7142 :         if (load_mdat_only) {
    1040        3571 :                 pos = gf_bs_get_position(bs);
    1041        3571 :                 if (movie->moof->fragment_offset > pos)
    1042             :                         return GF_CORRUPTED_DATA;
    1043             : 
    1044             :                 //we assume we never write large MDATs in fragment mode which should always be true
    1045        3571 :                 movie->moof->mdat_size = (u32) (pos - movie->moof->fragment_offset);
    1046             : 
    1047        3571 :                 if (movie->segment_bs) {
    1048           0 :                         e = gf_bs_seek(bs, 0);
    1049           0 :                         if (e) return e;
    1050             :                         /*write mdat size*/
    1051           0 :                         gf_bs_write_u32(bs, (u32) movie->moof->mdat_size);
    1052             :                         /*and get internal buffer*/
    1053           0 :                         e = gf_bs_seek(bs, movie->moof->mdat_size);
    1054           0 :                         if (e) return e;
    1055           0 :                         gf_bs_get_content(bs, &movie->moof->mdat, &movie->moof->mdat_size);
    1056             : 
    1057           0 :                         gf_bs_del(bs);
    1058           0 :                         movie->editFileMap->bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
    1059             :                 } else {
    1060        3571 :                         u64 frag_offset = movie->segment_start;
    1061        3571 :                         e = gf_bs_seek(bs, frag_offset);
    1062        3571 :                         if (e) return e;
    1063             :                         /*write mdat size*/
    1064        3571 :                         gf_bs_write_u32(bs, (u32) movie->moof->mdat_size);
    1065             : 
    1066        3571 :                         movie->moof->mdat = (char*)gf_malloc(sizeof(char) * movie->moof->mdat_size);
    1067        3571 :                         if (!movie->moof->mdat) return GF_OUT_OF_MEM;
    1068             : 
    1069        3571 :                         e = gf_bs_seek(bs, frag_offset);
    1070        3571 :                         if (e) return e;
    1071        3571 :                         gf_bs_read_data(bs, movie->moof->mdat, movie->moof->mdat_size);
    1072             : 
    1073        3571 :                         e = gf_bs_seek(bs, frag_offset);
    1074        3571 :                         if (e) return e;
    1075        3571 :                         gf_bs_truncate(bs);
    1076             :                 }
    1077             : 
    1078             :                 return GF_OK;
    1079             :         }
    1080             : 
    1081        3571 :         moof_start = gf_bs_get_position(bs);
    1082             : 
    1083        3571 :         if (movie->moof->ntp) {
    1084           0 :                 moof_start += 8*4;
    1085             :         }
    1086             : 
    1087             :         //2- update MOOF MDAT header
    1088        3571 :         if (!movie->moof->mdat) {
    1089           0 :                 e = gf_bs_seek(bs, movie->moof->fragment_offset);
    1090           0 :                 if (e) return e;
    1091             :                 //we assume we never write large MDATs in fragment mode which should always be true
    1092           0 :                 mdat_size = (u32) (moof_start - movie->moof->fragment_offset);
    1093           0 :                 gf_bs_write_u32(bs, (u32) mdat_size);
    1094           0 :                 gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_MDAT);
    1095           0 :                 e = gf_bs_seek(bs, moof_start);
    1096           0 :                 if (e) return e;
    1097             :         }
    1098             : 
    1099             :         /*estimate moof size and shift trun offsets*/
    1100             : #ifndef USE_BASE_DATA_OFFSET
    1101             :         offset = 0;
    1102        3571 :         if (movie->use_segments || movie->force_moof_base_offset) {
    1103        3514 :                 e = gf_isom_box_size((GF_Box *) movie->moof);
    1104        3514 :                 if (e) return e;
    1105        3514 :                 offset = (s32) movie->moof->size;
    1106             :                 /*mdat size & type*/
    1107        3514 :                 offset += 8;
    1108        3514 :                 update_trun_offsets(movie, offset);
    1109             :         }
    1110             : #endif
    1111             : 
    1112             :         //3- clean our traf's
    1113        3571 :         i=0;
    1114       11726 :         while ((traf = (GF_TrackFragmentBox*) gf_list_enum(movie->moof->TrackList, &i))) {
    1115             :                 //compute default settings for the TRAF
    1116        4584 :                 ComputeFragmentDefaults(traf);
    1117             :                 //updates all trun and set all flags, INCLUDING TRAF FLAGS (durations, ...)
    1118        4584 :                 s_count = UpdateRuns(movie, traf);
    1119             :                 //empty fragment destroy it
    1120        4584 :                 if (!traf->tfhd->EmptyDuration && !s_count) {
    1121         820 :                         i--;
    1122         820 :                         gf_list_rem(movie->moof->TrackList, i);
    1123         820 :                         gf_isom_box_del_parent(&movie->moof->child_boxes, (GF_Box *) traf);
    1124         820 :                         continue;
    1125             :                 }
    1126             :         }
    1127             : 
    1128        3571 :         buffer = NULL;
    1129             :         /*rewind bitstream and load mdat in memory */
    1130        3571 :         if (movie->moof_first && !movie->moof->mdat) {
    1131           0 :                 buffer = (char*)gf_malloc(sizeof(char)*mdat_size);
    1132           0 :                 if (!buffer) return GF_OUT_OF_MEM;
    1133           0 :                 e = gf_bs_seek(bs, movie->moof->fragment_offset);
    1134           0 :                 if (e) return e;
    1135           0 :                 gf_bs_read_data(bs, buffer, mdat_size);
    1136             :                 /*back to mdat start and erase with moov*/
    1137           0 :                 e = gf_bs_seek(bs, movie->moof->fragment_offset);
    1138           0 :                 if (e) return e;
    1139           0 :                 gf_bs_truncate(bs);
    1140             :         }
    1141             : 
    1142             :         //4- Write moof
    1143        3571 :         e = gf_isom_box_size((GF_Box *) movie->moof);
    1144        3571 :         if (e) return e;
    1145             :         /*moof first, update traf headers - THIS WILL IMPACT THE MOOF SIZE IF WE
    1146             :         DECIDE NOT TO USE THE DATA-OFFSET FLAG*/
    1147        3571 :         if (movie->moof_first
    1148             : #ifndef USE_BASE_DATA_OFFSET
    1149        3571 :                 && !(movie->use_segments || movie->force_moof_base_offset)
    1150             : #endif
    1151             :            ) {
    1152          57 :                 i=0;
    1153         211 :                 while ((traf = (GF_TrackFragmentBox*)gf_list_enum(movie->moof->TrackList, &i))) {
    1154             :                         /*offset increases by moof size*/
    1155          97 :                         traf->tfhd->base_data_offset += movie->moof->size;
    1156          97 :                         traf->tfhd->base_data_offset += data_offset_diff;
    1157          97 :                         if (movie->on_block_out) {
    1158          97 :                                 traf->tfhd->base_data_offset += movie->fragmented_file_pos;
    1159             :                         }
    1160             :                 }
    1161             :         }
    1162             : #ifndef USE_BASE_DATA_OFFSET
    1163        3514 :         else if (movie->use_segments || movie->force_moof_base_offset) {
    1164        3514 :                 if (offset != (movie->moof->size+8)) {
    1165        3514 :                         offset = (s32) (movie->moof->size + 8 - offset);
    1166        3514 :                         update_trun_offsets(movie, offset);
    1167        3514 :                         e = gf_isom_box_size((GF_Box *) movie->moof);
    1168        3514 :                         if (e) return e;
    1169             :                 }
    1170             :         }
    1171             : #endif
    1172             : 
    1173             :         bs_orig = bs;
    1174        3571 :         if (reassign_bs && movie->on_block_out) {
    1175           0 :                 bs = gf_bs_new_cbk(movie->on_block_out, movie->on_block_out_usr_data, movie->on_block_out_block_size);
    1176             :         }
    1177             : 
    1178        3571 :         if (movie->moof->ntp) {
    1179           0 :                 gf_bs_write_u32(bs, 8*4);
    1180           0 :                 gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_PRFT );
    1181           0 :                 gf_bs_write_u8(bs, 1);
    1182           0 :                 gf_bs_write_u24(bs, 0);
    1183           0 :                 gf_bs_write_u32(bs, movie->moof->reference_track_ID);
    1184           0 :                 gf_bs_write_u64(bs, movie->moof->ntp);
    1185           0 :                 gf_bs_write_u64(bs, movie->moof->timestamp);
    1186             :         }
    1187             : 
    1188        3571 :         if (moof_size) *moof_size = (u32) movie->moof->size;
    1189             : 
    1190        3571 :         pos = gf_bs_get_position(bs);
    1191             : 
    1192        3571 :         i=0;
    1193       10906 :         while ((traf = (GF_TrackFragmentBox*)gf_list_enum(movie->moof->TrackList, &i))) {
    1194        3764 :                 traf->moof_start_in_bs = pos;
    1195             :         }
    1196             : 
    1197             :         /*we don't want to dispatch any block until done writing the moof*/
    1198        3571 :         if (movie->on_block_out)
    1199        3571 :                 gf_bs_prevent_dispatch(bs, GF_TRUE);
    1200             : 
    1201        3571 :         if (movie->compress_mode>GF_ISOM_COMP_MOOV) {
    1202          10 :                 e = gf_isom_write_compressed_box(movie, (GF_Box *) movie->moof, GF_4CC('!', 'm', 'o', 'f'), bs, moof_size);
    1203             :         } else {
    1204        3561 :                 e = gf_isom_box_write((GF_Box *) movie->moof, bs);
    1205             :         }
    1206             : 
    1207        3571 :         if (movie->on_block_out)
    1208        3571 :                 gf_bs_prevent_dispatch(bs, GF_FALSE);
    1209             : 
    1210        3571 :         if (e) return e;
    1211             : 
    1212             :         //rewrite mdat after moof
    1213        3571 :         if (movie->moof->mdat) {
    1214        3571 :                 gf_bs_write_data(bs, movie->moof->mdat, movie->moof->mdat_size);
    1215        3571 :                 gf_free(movie->moof->mdat);
    1216        3571 :                 movie->moof->mdat = NULL;
    1217           0 :         } else if (buffer) {
    1218           0 :                 gf_bs_write_data(bs, buffer, mdat_size);
    1219           0 :                 gf_free(buffer);
    1220             :         }
    1221             : 
    1222        3571 :         if (bs != bs_orig) {
    1223           0 :                 u64 frag_size = gf_bs_get_position(bs);
    1224           0 :                 gf_bs_del(bs);
    1225           0 :                 movie->fragmented_file_pos += frag_size;
    1226           0 :                 gf_bs_seek(bs_orig, 0);
    1227           0 :                 gf_bs_truncate(bs_orig);
    1228             :         }
    1229        3571 :         else if (movie->on_block_out) {
    1230        3571 :                 u64 frag_size = gf_bs_get_position(bs);
    1231        3571 :                 movie->fragmented_file_pos += frag_size;
    1232             :         }
    1233             : 
    1234        3571 :         if (!movie->use_segments) {
    1235          80 :                 gf_isom_box_del((GF_Box *) movie->moof);
    1236          80 :                 movie->moof = NULL;
    1237             :         }
    1238             :         return GF_OK;
    1239             : }
    1240             : 
    1241        2074 : static GF_Err sidx_rewrite(GF_SegmentIndexBox *sidx, GF_BitStream *bs, u64 start_pos, GF_SubsegmentIndexBox *ssix)
    1242             : {
    1243             :         GF_Err e = GF_OK;
    1244        2074 :         u64 pos = gf_bs_get_position(bs);
    1245        2074 :         if (ssix) {
    1246           0 :                 e = gf_isom_box_size((GF_Box *)ssix);
    1247           0 :                 sidx->first_offset = ssix->size;
    1248             :         }
    1249             :         /*write sidx*/
    1250        2074 :         gf_bs_seek(bs, start_pos);
    1251        2074 :         if (!e) e = gf_isom_box_write((GF_Box *) sidx, bs);
    1252        2074 :         if (!e && ssix) {
    1253           0 :                 e = gf_isom_box_write((GF_Box *) ssix, bs);
    1254             :         }
    1255        2074 :         gf_bs_seek(bs, pos);
    1256        2074 :         return e;
    1257             : }
    1258             : 
    1259          17 : GF_Err gf_isom_allocate_sidx(GF_ISOFile *movie, s32 subsegs_per_sidx, Bool daisy_chain_sidx, u32 nb_segs, u32 *frags_per_segment, u32 *start_range, u32 *end_range, Bool use_ssix)
    1260             : {
    1261             :         GF_BitStream *bs;
    1262             :         GF_Err e;
    1263             :         u32 i;
    1264             : 
    1265             :         //and only at setup
    1266          17 :         if (!movie || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) return GF_BAD_PARAM;
    1267          17 :         if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE;
    1268          17 :         if (movie->root_sidx) return GF_BAD_PARAM;
    1269          17 :         if (movie->root_ssix) return GF_BAD_PARAM;
    1270          17 :         if (movie->moof) return GF_BAD_PARAM;
    1271          17 :         if (gf_list_count(movie->moof_list)) return GF_BAD_PARAM;
    1272             : 
    1273          17 :         movie->root_sidx = (GF_SegmentIndexBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_SIDX);
    1274          17 :         if (!movie->root_sidx) return GF_OUT_OF_MEM;
    1275             :         /*we don't write anything between sidx and following moov*/
    1276          17 :         movie->root_sidx->first_offset = 0;
    1277             : 
    1278             :         /*for now we only store one ref per subsegment and don't support daisy-chaining*/
    1279          17 :         movie->root_sidx->nb_refs = nb_segs;
    1280             : 
    1281          17 :         if (use_ssix) {
    1282           1 :                 movie->root_ssix = (GF_SubsegmentIndexBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_SSIX);
    1283           1 :                 movie->root_ssix->subsegment_count = nb_segs;
    1284           1 :                 movie->root_ssix->subsegment_alloc = movie->root_ssix->subsegment_count;
    1285             :         }
    1286             : 
    1287             :         //dynamic mode
    1288          17 :         if (!nb_segs) {
    1289          17 :                 movie->dyn_root_sidx = GF_TRUE;
    1290          17 :                 return GF_OK;
    1291             :         }
    1292             : 
    1293           0 :         movie->root_sidx->refs = (GF_SIDXReference*)gf_malloc(sizeof(GF_SIDXReference) * movie->root_sidx->nb_refs);
    1294           0 :         if (!movie->root_sidx->refs) return GF_OUT_OF_MEM;
    1295           0 :         memset(movie->root_sidx->refs, 0, sizeof(GF_SIDXReference) * movie->root_sidx->nb_refs);
    1296             : 
    1297           0 :         movie->root_sidx_index = 0;
    1298             : 
    1299           0 :         if (use_ssix) {
    1300           0 :                 movie->root_ssix->subsegments = gf_malloc(sizeof(GF_SubsegmentInfo) * nb_segs);
    1301           0 :                 if (!movie->root_ssix->subsegments) return GF_OUT_OF_MEM;
    1302           0 :                 for (i=0; i<nb_segs; i++) {
    1303           0 :                         movie->root_ssix->subsegments[i].range_count = 2;
    1304           0 :                         movie->root_ssix->subsegments[i].ranges = gf_malloc(sizeof(GF_SubsegmentRangeInfo)*2);
    1305           0 :                         if (!movie->root_ssix->subsegments[i].ranges) return GF_OUT_OF_MEM;
    1306           0 :                         movie->root_ssix->subsegments[i].ranges[0].level = 0;
    1307           0 :                         movie->root_ssix->subsegments[i].ranges[0].range_size = 0;
    1308           0 :                         movie->root_ssix->subsegments[i].ranges[1].level = 1;
    1309           0 :                         movie->root_ssix->subsegments[i].ranges[1].range_size = 0;
    1310             :                 }
    1311             :         }
    1312             :         
    1313             :         /*remember start of sidx*/
    1314           0 :         movie->root_sidx_offset = gf_bs_get_position(movie->editFileMap->bs);
    1315             : 
    1316           0 :         bs = movie->editFileMap->bs;
    1317             : 
    1318           0 :         e = gf_isom_box_size((GF_Box *) movie->root_sidx);
    1319           0 :         if (e) return e;
    1320           0 :         e = gf_isom_box_write((GF_Box *) movie->root_sidx, bs);
    1321           0 :         if (e) return e;
    1322             : 
    1323           0 :         if (movie->root_ssix) {
    1324           0 :                 e = gf_isom_box_size((GF_Box *) movie->root_ssix);
    1325           0 :                 if (e) return e;
    1326           0 :                 e = gf_isom_box_write((GF_Box *) movie->root_ssix, bs);
    1327           0 :                 if (e) return e;
    1328             :         }
    1329             : 
    1330             :         //include ssix in index range - spec is not clear whether this is forbidden
    1331           0 :         if (start_range) *start_range = (u32) movie->root_sidx_offset;
    1332           0 :         if (end_range) *end_range = (u32) gf_bs_get_position(bs)-1;
    1333             : 
    1334             :         return GF_OK;
    1335             : }
    1336             : 
    1337             : 
    1338        3479 : static GF_Err gf_isom_write_styp(GF_ISOFile *movie, Bool last_segment)
    1339             : {
    1340             :         /*write STYP if we write to a different file or if we write the last segment*/
    1341        3479 :         if (movie->use_segments && !movie->append_segment && !movie->segment_start && !movie->styp_written) {
    1342             :                 GF_Err e;
    1343             : 
    1344             :                 /*modify brands STYP*/
    1345             : 
    1346             :                 /*"msix" brand: this is a DASH Initialization Segment*/
    1347        2487 :                 gf_isom_modify_alternate_brand(movie, GF_ISOM_BRAND_MSIX, GF_TRUE);
    1348        2487 :                 if (last_segment) {
    1349             :                         /*"lmsg" brand: this is the last DASH Segment*/
    1350         248 :                         gf_isom_modify_alternate_brand(movie, GF_ISOM_BRAND_LMSG, GF_TRUE);
    1351             :                 }
    1352             : 
    1353        2487 :                 movie->brand->type = GF_ISOM_BOX_TYPE_STYP;
    1354        2487 :                 e = gf_isom_box_size((GF_Box *) movie->brand);
    1355        2487 :                 if (e) return e;
    1356        2487 :                 e = gf_isom_box_write((GF_Box *) movie->brand, movie->editFileMap->bs);
    1357        2487 :                 if (e) return e;
    1358             : 
    1359        2487 :                 movie->styp_written = GF_TRUE;
    1360             :         }
    1361             :         return GF_OK;
    1362             : }
    1363             : 
    1364             : GF_EXPORT
    1365         453 : GF_Err gf_isom_flush_fragments(GF_ISOFile *movie, Bool last_segment)
    1366             : {
    1367             :         GF_BitStream *temp_bs = NULL, *orig_bs;
    1368             :         GF_Err e;
    1369             : 
    1370         453 :         if (!movie || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) return GF_BAD_PARAM;
    1371         453 :         if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE;
    1372             : 
    1373             :         /*flush our fragment (store in mem)*/
    1374         453 :         if (movie->moof) {
    1375         453 :                 e = StoreFragment(movie, GF_TRUE, 0, NULL, GF_FALSE);
    1376         453 :                 if (e) return e;
    1377             :         }
    1378             : 
    1379         453 :         if (movie->segment_bs) {
    1380           0 :                 temp_bs = movie->editFileMap->bs;
    1381           0 :                 movie->editFileMap->bs = movie->segment_bs;
    1382             :         }
    1383             : 
    1384         453 :         gf_bs_seek(movie->editFileMap->bs, movie->segment_start);
    1385         453 :         gf_bs_truncate(movie->editFileMap->bs);
    1386         453 :         orig_bs = movie->editFileMap->bs;
    1387         453 :         if (movie->on_block_out) {
    1388         453 :                 if (!movie->block_buffer) movie->block_buffer_size = movie->on_block_out_block_size;
    1389         453 :                 movie->editFileMap->bs = gf_bs_new_cbk_buffer(movie->on_block_out, movie->on_block_out_usr_data, movie->block_buffer, movie->block_buffer_size);
    1390             :         }
    1391             : 
    1392             :         /*write styp to file if needed*/
    1393         453 :         e = gf_isom_write_styp(movie, last_segment);
    1394         453 :         if (e) goto exit;
    1395             : 
    1396             :         /*write all pending fragments to file*/
    1397         906 :         while (gf_list_count(movie->moof_list)) {
    1398             :                 s32 offset_diff;
    1399             :                 u32 moof_size;
    1400             : 
    1401         453 :                 movie->moof = (GF_MovieFragmentBox*)gf_list_get(movie->moof_list, 0);
    1402         453 :                 gf_list_rem(movie->moof_list, 0);
    1403             : 
    1404         453 :                 offset_diff = (s32) (gf_bs_get_position(movie->editFileMap->bs) - movie->moof->fragment_offset);
    1405         453 :                 movie->moof->fragment_offset = gf_bs_get_position(movie->editFileMap->bs);
    1406             : 
    1407         453 :                 e = StoreFragment(movie, GF_FALSE, offset_diff, &moof_size, GF_FALSE);
    1408         453 :                 if (e) goto exit;
    1409             : 
    1410         453 :                 gf_isom_box_del((GF_Box *) movie->moof);
    1411         453 :                 movie->moof = NULL;
    1412             :         }
    1413             : 
    1414             :         /*append mode: store fragment at the end of the regular movie bitstream, and delete the temp bitstream*/
    1415         453 :         if (movie->append_segment) {
    1416             :                 char bloc[1024];
    1417           0 :                 u32 seg_size = (u32) gf_bs_get_size(movie->editFileMap->bs);
    1418           0 :                 gf_bs_seek(movie->editFileMap->bs, 0);
    1419           0 :                 while (seg_size) {
    1420           0 :                         u32 size = gf_bs_read_data(movie->editFileMap->bs, bloc, (seg_size>1024) ? 1024 : seg_size);
    1421           0 :                         gf_bs_write_data(movie->movieFileMap->bs, bloc, size);
    1422           0 :                         seg_size -= size;
    1423             :                 }
    1424           0 :                 gf_isom_datamap_flush(movie->movieFileMap);
    1425             : 
    1426           0 :                 gf_isom_datamap_del(movie->editFileMap);
    1427           0 :                 movie->editFileMap = gf_isom_fdm_new_temp(NULL);
    1428             :         } else {
    1429         453 :                 gf_isom_datamap_flush(movie->editFileMap);
    1430             :         }
    1431         453 :         movie->segment_start = gf_bs_get_position(movie->editFileMap->bs);
    1432             : 
    1433         453 :         if (temp_bs) {
    1434           0 :                 movie->segment_bs = movie->editFileMap->bs;
    1435           0 :                 movie->editFileMap->bs = temp_bs;
    1436             :         }
    1437             : 
    1438         453 :         if (orig_bs != movie->editFileMap->bs) {
    1439             :                 u32 tmpsize;
    1440         453 :                 gf_bs_get_content_no_truncate(movie->editFileMap->bs, &movie->block_buffer, &tmpsize, &movie->block_buffer_size);
    1441         453 :                 gf_bs_del(movie->editFileMap->bs);
    1442         453 :                 movie->editFileMap->bs = orig_bs;
    1443             :                 //we are dispatching through callbacks, the movie segment start is always 0
    1444         453 :                 movie->segment_start = 0;
    1445             :         }
    1446           0 : exit:
    1447             :         return e;
    1448             : }
    1449             : 
    1450             : typedef struct
    1451             : {
    1452             :         GF_SegmentIndexBox *sidx;
    1453             :         u64 start_offset, end_offset;
    1454             : } SIDXEntry;
    1455             : 
    1456             : static u64 get_presentation_time(u64 media_time, s32 ts_shift)
    1457             : {
    1458      175828 :         if ((ts_shift<0) && (media_time < -ts_shift)) {
    1459             :                 media_time = 0;
    1460             :         } else {
    1461      175828 :                 media_time += ts_shift;
    1462             :         }
    1463             :         return media_time ;
    1464             : }
    1465             : 
    1466             : 
    1467             : #if 0 //unused
    1468             : /*! gets name of current segment (or last segment if called between close_segment and start_segment)
    1469             : \param isom_file the target ISO file
    1470             : \return associated file name of the segment
    1471             : */
    1472             : GF_EXPORT
    1473             : const char *gf_isom_get_segment_name(GF_ISOFile *movie)
    1474             : {
    1475             :         if (!movie) return NULL;
    1476             :         if (movie->append_segment) return movie->movieFileMap->szName;
    1477             :         return movie->editFileMap->szName;
    1478             : }
    1479             : #endif
    1480             : 
    1481        3043 : static void compute_seg_size(GF_ISOFile *movie, u64 *out_seg_size)
    1482             : {
    1483             :         u64 final_size = 0;
    1484        3043 :         if (out_seg_size) {
    1485        3026 :                 if (movie->append_segment) {
    1486           0 :                         final_size = gf_bs_get_position(movie->movieFileMap->bs);
    1487           0 :                         final_size -= movie->segment_start;
    1488        3026 :                 } else if (movie->editFileMap) {
    1489        3026 :                         final_size = gf_bs_get_position(movie->editFileMap->bs);
    1490             :                 }
    1491        3026 :                 *out_seg_size = final_size;
    1492             :         }
    1493        3043 : }
    1494             : 
    1495          30 : static u32 moof_get_first_sap_end(GF_MovieFragmentBox *moof)
    1496             : {
    1497          30 :         u32 i, count = gf_list_count(moof->TrackList);
    1498           0 :         for (i=0; i<count; i++) {
    1499             :                 u32 j, nb_trun;
    1500          30 :                 GF_TrackFragmentBox *traf = gf_list_get(moof->TrackList, i);
    1501          30 :                 u32 base_offset = (u32) traf->tfhd->base_data_offset;
    1502             : 
    1503          30 :                 nb_trun = gf_list_count(traf->TrackRuns);
    1504           0 :                 for (j=0; j<nb_trun; j++) {
    1505             :                         u32 k;
    1506          30 :                         GF_TrackFragmentRunBox *trun = gf_list_get(traf->TrackRuns, j);
    1507          30 :                         u32 offset = base_offset + trun->data_offset;
    1508           0 :                         for (k=0; k<trun->nb_samples; k++) {
    1509          30 :                                 GF_TrunEntry *ent = &trun->samples[k];
    1510          30 :                                 if (ent->SAP_type) return offset + ent->size;
    1511             : 
    1512           0 :                                 offset += ent->size;
    1513             :                         }
    1514             :                 }
    1515             :         }
    1516             :         return 0;
    1517             : }
    1518             : 
    1519        2350 : static u64 estimate_next_moof_earliest_presentation_time(u64 ref_track_decode_time, s32 ts_shift, u32 refTrackID, GF_ISOFile *movie)
    1520             : {
    1521             :         u32 i, j, nb_aus, nb_ctso, nb_moof;
    1522             :         u64 duration;
    1523             :         GF_TrunEntry *ent;
    1524             :         GF_TrackFragmentBox *traf=NULL;
    1525             :         GF_TrackFragmentRunBox *trun;
    1526             :         u32 timescale;
    1527             :         u64 min_next_cts = -1;
    1528             : 
    1529        2350 :         GF_MovieFragmentBox *moof = gf_list_get(movie->moof_list, 0);
    1530             : 
    1531        2350 :         for (i=0; i<gf_list_count(moof->TrackList); i++) {
    1532        2350 :                 traf = (GF_TrackFragmentBox*)gf_list_get(moof->TrackList, i);
    1533        2350 :                 if (traf->tfhd->trackID==refTrackID) break;
    1534             :                 traf = NULL;
    1535             :         }
    1536             :         //no ref track, nothing to estimate
    1537        2350 :         if (!traf) return -1;
    1538        2350 :         timescale = traf->trex->track->Media->mediaHeader->timeScale;
    1539             : 
    1540             :         nb_aus = 0;
    1541             :         duration = 0;
    1542             :         nb_ctso = 0;
    1543        2350 :         nb_moof = 0;
    1544             : 
    1545        7142 :         while ((moof = (GF_MovieFragmentBox*)gf_list_enum(movie->moof_list, &nb_moof))) {
    1546             : 
    1547        2442 :                 for (i=0; i<gf_list_count(moof->TrackList); i++) {
    1548        2442 :                         traf = (GF_TrackFragmentBox*)gf_list_get(moof->TrackList, i);
    1549        2442 :                         if (traf->tfhd->trackID==refTrackID) break;
    1550             :                         traf = NULL;
    1551             :                 }
    1552        2442 :                 if (!traf) continue;
    1553             : 
    1554        2442 :                 i=0;
    1555        7326 :                 while ((trun = (GF_TrackFragmentRunBox*)gf_list_enum(traf->TrackRuns, &i))) {
    1556       85655 :                         for (j=0; j<trun->nb_samples; j++) {
    1557       85655 :                                 ent = &trun->samples[j];
    1558       85655 :                                 if (nb_aus + 1 + movie->sidx_pts_store_count > movie->sidx_pts_store_alloc) {
    1559       19596 :                                         movie->sidx_pts_store_alloc = movie->sidx_pts_store_count+nb_aus+1;
    1560       19596 :                                         movie->sidx_pts_store = gf_realloc(movie->sidx_pts_store, sizeof(u64) * movie->sidx_pts_store_alloc);
    1561       19596 :                                         movie->sidx_pts_next_store = gf_realloc(movie->sidx_pts_next_store, sizeof(u64) * movie->sidx_pts_store_alloc);
    1562             :                                 }
    1563             :                                 //get PTS for this AU, push to regular list
    1564      171310 :                                 movie->sidx_pts_store[movie->sidx_pts_store_count + nb_aus] = get_presentation_time( ref_track_decode_time + duration + ent->CTS_Offset, ts_shift);
    1565             :                                 //get PTS for this AU shifted by its presentation duration, push to shifted list
    1566      171310 :                                 movie->sidx_pts_next_store[movie->sidx_pts_store_count + nb_aus] = get_presentation_time( ref_track_decode_time + duration + ent->CTS_Offset + ent->Duration, ts_shift);
    1567       85655 :                                 duration += ent->Duration;
    1568       85655 :                                 if (ent->CTS_Offset)
    1569        8753 :                                         nb_ctso++;
    1570             : 
    1571       85655 :                                 nb_aus++;
    1572             :                         }
    1573             :                 }
    1574             :         }
    1575             : 
    1576        2350 :         movie->sidx_pts_store_count += nb_aus;
    1577             : 
    1578             :         //no AUs, nothing to estimate
    1579        2350 :         if (!nb_aus) {
    1580           0 :                 movie->sidx_pts_store_count = 0;
    1581           0 :                 return -1;
    1582             :         }
    1583             :         //no cts offset, assume earliest PTS in next segment is last PTS in this segment + duration
    1584        2350 :         if (!nb_ctso) {
    1585        2151 :                 min_next_cts = movie->sidx_pts_next_store[movie->sidx_pts_store_count - 1];
    1586        2151 :                 movie->sidx_pts_store_count = 0;
    1587        2151 :                 return min_next_cts;
    1588             :         }
    1589             : 
    1590             :         //look for all shifted PTS of this segment in the regular list. If found in the shifted list, the AU is in this segment
    1591             :         //remove from both list
    1592       13190 :         for (i=0; i<movie->sidx_pts_store_count; i++) {
    1593       31107 :                 for (j=i; j<movie->sidx_pts_store_count; j++) {
    1594             :                         /*
    1595             : 
    1596             :                         if (movie->sidx_pts_next_store[i] == movie->sidx_pts_store[j]) {
    1597             :                         
    1598             :                         take care of misaligned timescale eg 24fps but 10000 timescale), we may not find exactly
    1599             :                         the same sample - if diff is below one ms consider it a match
    1600             :                         not doing so would accumulate PTSs in the list, slowing down the muxing*/
    1601       43867 :                         s64 diff = movie->sidx_pts_next_store[i];
    1602       43867 :                         diff -= (s64) movie->sidx_pts_store[j];
    1603       43867 :                         if (timescale>1000) {
    1604       23614 :                                 if (ABS(diff) * 1000 < 1 * timescale)
    1605             :                                         diff = 0;
    1606             :                         }
    1607       37295 :                         if (diff==0) {
    1608       12760 :                                 if (movie->sidx_pts_store_count >= i + 1)
    1609       12760 :                                         memmove(&movie->sidx_pts_next_store[i], &movie->sidx_pts_next_store[i+1], sizeof(u64) * (movie->sidx_pts_store_count - i - 1) );
    1610       12760 :                                 if (movie->sidx_pts_store_count >= j + 1)
    1611       12760 :                                         memmove(&movie->sidx_pts_store[j], &movie->sidx_pts_store[j+1], sizeof(u64) * (movie->sidx_pts_store_count - j - 1) );
    1612       12760 :                                 movie->sidx_pts_store_count--;
    1613       12760 :                                 i--;
    1614       12760 :                                 break;
    1615             :                         }
    1616             :                 }
    1617             :         }
    1618             :         //the shifted list contain all AUs not yet in this segment, keep the smallest to compute the earliest PTS in next seg
    1619             :         //note that we assume the durations were correctly set
    1620         430 :         for (i=0; i<movie->sidx_pts_store_count; i++) {
    1621         231 :                 if (min_next_cts > movie->sidx_pts_next_store[i])
    1622             :                         min_next_cts = movie->sidx_pts_next_store[i];
    1623             :         }
    1624             :         return min_next_cts;
    1625             : }
    1626             : 
    1627             : 
    1628             : GF_EXPORT
    1629        3043 : GF_Err gf_isom_close_segment(GF_ISOFile *movie, s32 subsegments_per_sidx, GF_ISOTrackID referenceTrackID, u64 ref_track_decode_time, s32 ts_shift, u64 ref_track_next_cts, Bool daisy_chain_sidx, Bool use_ssix, Bool last_segment, Bool close_segment_handle, u32 segment_marker_4cc, u64 *index_start_range, u64 *index_end_range, u64 *out_seg_size)
    1630             : {
    1631             :         GF_SegmentIndexBox *sidx=NULL;
    1632             :         GF_SegmentIndexBox *root_sidx=NULL;
    1633             :         GF_SubsegmentIndexBox *ssix=NULL;
    1634             :         GF_List *daisy_sidx = NULL;
    1635             :         GF_BitStream *orig_bs;
    1636             :         u64 sidx_start, sidx_end;
    1637             :         Bool first_frag_in_subseg;
    1638             :         Bool no_sidx = GF_FALSE;
    1639             :         u32 count, cur_idx, cur_dur, sidx_dur, sidx_idx, idx_offset, frag_count;
    1640             :         u64 last_top_box_pos, root_prev_offset, local_sidx_start, local_sidx_end, prev_earliest_cts, next_earliest_cts;
    1641             :         GF_TrackBox *trak = NULL;
    1642             :         GF_Err e;
    1643             :         /*number of subsegment in this segment (eg nb references in the first SIDX found)*/
    1644             :         u32 nb_subsegs=0;
    1645             :         /*number of subsegment per sidx (eg number of references of any sub-SIDX*/
    1646             :         u32 subseg_per_sidx;
    1647             :         /*number of fragments per subsegment*/
    1648             :         u32 frags_per_subseg;
    1649             :         /*number of fragments per subsidx*/
    1650             :         u32 frags_per_subsidx;
    1651             : 
    1652             :         sidx_start = sidx_end = 0;
    1653             : 
    1654        3043 :         if (index_start_range) *index_start_range = 0;
    1655        3043 :         if (index_end_range) *index_end_range = 0;
    1656             : 
    1657             :         //and only at setup
    1658        3043 :         if (!movie || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) return GF_BAD_PARAM;
    1659        3043 :         if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE;
    1660             : 
    1661        3043 :         count = gf_list_count(movie->moov->mvex->TrackExList);
    1662        3043 :         if (!count) return GF_BAD_PARAM;
    1663             : 
    1664             :         /*store fragment*/
    1665        3043 :         if (movie->moof) {
    1666        3026 :                 e = StoreFragment(movie, GF_TRUE, 0, NULL, GF_FALSE);
    1667        3026 :                 if (e) return e;
    1668             :         }
    1669             :         /*restore final bitstream*/
    1670        3043 :         if (movie->segment_bs) {
    1671           0 :                 gf_bs_del(movie->editFileMap->bs);
    1672           0 :                 movie->editFileMap->bs = movie->segment_bs;
    1673           0 :                 movie->segment_bs = NULL;
    1674             :         }
    1675             : 
    1676        3043 :         count = gf_list_count(movie->moof_list);
    1677        3043 :         if (!count) {
    1678             :                 /*append segment marker box*/
    1679          17 :                 if (segment_marker_4cc) {
    1680           0 :                         if (movie->append_segment) {
    1681           0 :                                 gf_bs_write_u32(movie->movieFileMap->bs, 8);      //write size field
    1682           0 :                                 gf_bs_write_u32(movie->movieFileMap->bs, segment_marker_4cc); //write box type field
    1683             :                         } else {
    1684           0 :                                 gf_bs_write_u32(movie->editFileMap->bs, 8);       //write size field
    1685           0 :                                 gf_bs_write_u32(movie->editFileMap->bs, segment_marker_4cc); //write box type field
    1686             :                         }
    1687             :                 }
    1688             : 
    1689          17 :                 compute_seg_size(movie, out_seg_size);
    1690             : 
    1691          17 :                 if (close_segment_handle) {
    1692          17 :                         gf_isom_datamap_del(movie->editFileMap);
    1693          17 :                         movie->editFileMap = NULL;
    1694             :                 }
    1695             : 
    1696             :                 return GF_OK;
    1697             :         }
    1698             : 
    1699        3026 :         gf_bs_seek(movie->editFileMap->bs, movie->segment_start);
    1700        3026 :         gf_bs_truncate(movie->editFileMap->bs);
    1701             : 
    1702             :         idx_offset = 0;
    1703             : 
    1704        3026 :         if (referenceTrackID) {
    1705        2350 :                 trak = gf_isom_get_track_from_id(movie->moov, referenceTrackID);
    1706        2350 :                 if (!trak) return GF_BAD_PARAM;
    1707             :         }
    1708             : 
    1709        3026 :         if (subsegments_per_sidx < 0) {
    1710             :                 referenceTrackID = 0;
    1711             :                 subsegments_per_sidx = 0;
    1712             :         }
    1713        3026 :         if (!subsegments_per_sidx && !referenceTrackID) {
    1714             :                 no_sidx = GF_TRUE;
    1715             :         }
    1716             : 
    1717        3026 :         orig_bs = movie->editFileMap->bs;
    1718        3026 :         if (movie->on_block_out) {
    1719        3026 :                 if (!movie->block_buffer) movie->block_buffer_size = movie->on_block_out_block_size;
    1720        3026 :                 movie->editFileMap->bs = gf_bs_new_cbk_buffer(movie->on_block_out, movie->on_block_out_usr_data, movie->block_buffer, movie->block_buffer_size);
    1721        3026 :                 if (referenceTrackID) gf_bs_prevent_dispatch(movie->editFileMap->bs, GF_TRUE);
    1722             :         }
    1723             : 
    1724        3026 :         e = gf_isom_write_styp(movie, last_segment);
    1725        3026 :         if (e) goto exit;
    1726             : 
    1727             :         frags_per_subseg = 0;
    1728             :         subseg_per_sidx = 0;
    1729             :         frags_per_subsidx = 0;
    1730             : 
    1731             :         prev_earliest_cts = 0;
    1732             :         next_earliest_cts = 0;
    1733             : 
    1734        3026 :         if (daisy_chain_sidx)
    1735           0 :                 daisy_sidx = gf_list_new();
    1736             : 
    1737             :         /*prepare SIDX: we write a blank SIDX box with the right number of entries, and will rewrite it later on*/
    1738        3026 :         if (referenceTrackID) {
    1739             :                 Bool is_root_sidx = GF_FALSE;
    1740             : 
    1741        2350 :                 prev_earliest_cts = get_presentation_time( ref_track_decode_time + moof_get_earliest_cts((GF_MovieFragmentBox*)gf_list_get(movie->moof_list, 0), referenceTrackID), ts_shift);
    1742             : 
    1743             :                 //we don't trust ref_track_next_cts to be the earliest in the following segment
    1744        2350 :                 next_earliest_cts = estimate_next_moof_earliest_presentation_time(ref_track_decode_time, ts_shift, referenceTrackID, movie);
    1745             : 
    1746        2350 :                 if (movie->root_sidx) {
    1747             :                         sidx = movie->root_sidx;
    1748             :                 } else {
    1749        2074 :                         sidx = (GF_SegmentIndexBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_SIDX);
    1750        2074 :                         if (!sidx) return GF_OUT_OF_MEM;
    1751        2074 :                         if (movie->force_sidx_v1)
    1752           0 :                                 sidx->version = 1;
    1753             :                 }
    1754        2350 :                 sidx->reference_ID = referenceTrackID;
    1755        2350 :                 sidx->timescale = trak->Media->mediaHeader->timeScale;
    1756             :                 /*we don't write anything between sidx and following moov*/
    1757        2350 :                 sidx->first_offset = 0;
    1758             : 
    1759             :                 /*we allocated our sidx to have one ref per "segment" (eg per call to close_segment)*/
    1760        2350 :                 if (movie->root_sidx) {
    1761         276 :                         if (!movie->root_sidx_index) {
    1762          17 :                                 sidx->earliest_presentation_time = prev_earliest_cts;
    1763             :                         }
    1764             :                         nb_subsegs = 1;
    1765             :                         frags_per_subseg = count;
    1766             :                         frags_per_subsidx = count;
    1767             :                         subseg_per_sidx = 1;
    1768             :                         daisy_chain_sidx = GF_FALSE;
    1769             : 
    1770         276 :                         idx_offset = movie->root_sidx_index;
    1771         276 :                         sidx_end = gf_bs_get_position(movie->editFileMap->bs);
    1772             :                 } else {
    1773        2074 :                         sidx->earliest_presentation_time = prev_earliest_cts;
    1774             : 
    1775             :                         /*if more subsegments requested than fragments available, make a single sidx*/
    1776        2074 :                         if ((s32) count <= subsegments_per_sidx)
    1777             :                                 subsegments_per_sidx = 0;
    1778             : 
    1779        2074 :                         if (daisy_chain_sidx && (subsegments_per_sidx<2))
    1780             :                                 subsegments_per_sidx = 2;
    1781             : 
    1782             :                         /*single SIDX, each fragment is a subsegment and we reference all subsegments*/
    1783        2074 :                         if (!subsegments_per_sidx) {
    1784             :                                 nb_subsegs = count;
    1785             :                                 /*we consider that each fragment is a subsegment - this could be controled by another parameter*/
    1786             :                                 frags_per_subseg = 1;
    1787             :                                 frags_per_subsidx = count;
    1788             :                                 subseg_per_sidx = count;
    1789             : 
    1790        2074 :                                 sidx->nb_refs = nb_subsegs;
    1791             :                                 daisy_chain_sidx = GF_FALSE;
    1792             :                         }
    1793             :                         /*daisy-chain SIDX: each SIDX describes a subsegment made of frags_per_subseg fragments plus next */
    1794           0 :                         else if (daisy_chain_sidx) {
    1795           0 :                                 frags_per_subsidx = count/subsegments_per_sidx;
    1796           0 :                                 if (frags_per_subsidx * subsegments_per_sidx < count) frags_per_subsidx++;
    1797             : 
    1798             :                                 nb_subsegs = subsegments_per_sidx;
    1799             : 
    1800             :                                 /*we consider that each fragment is a subsegment - this could be controled by another parameter*/
    1801             :                                 frags_per_subseg = 1;
    1802             :                                 subseg_per_sidx = frags_per_subsidx / frags_per_subseg;
    1803             :                                 if (subseg_per_sidx * frags_per_subseg < frags_per_subsidx) subseg_per_sidx++;
    1804             : 
    1805           0 :                                 sidx->nb_refs = subseg_per_sidx + 1;
    1806             :                         }
    1807             :                         /*hierarchical SIDX*/
    1808             :                         else {
    1809           0 :                                 frags_per_subsidx = count/subsegments_per_sidx;
    1810           0 :                                 if (frags_per_subsidx * subsegments_per_sidx < count) frags_per_subsidx++;
    1811             : 
    1812             :                                 nb_subsegs = subsegments_per_sidx;
    1813             : 
    1814             :                                 /*we consider that each fragment is a subsegment - this could be controled by another parameter*/
    1815             :                                 frags_per_subseg = 1;
    1816             :                                 subseg_per_sidx = frags_per_subsidx / frags_per_subseg;
    1817             :                                 if (subseg_per_sidx * frags_per_subseg < frags_per_subsidx) subseg_per_sidx++;
    1818             : 
    1819           0 :                                 sidx->nb_refs = nb_subsegs;
    1820             :                                 is_root_sidx = GF_TRUE;
    1821             :                         }
    1822             : 
    1823        2074 :                         sidx->refs = (GF_SIDXReference*)gf_malloc(sizeof(GF_SIDXReference)*sidx->nb_refs);
    1824        2074 :                         if (!sidx->refs) return GF_OUT_OF_MEM;
    1825        2074 :                         memset(sidx->refs, 0, sizeof(GF_SIDXReference)*sidx->nb_refs);
    1826             : 
    1827             :                         /*remember start of sidx*/
    1828        2074 :                         sidx_start = gf_bs_get_position(movie->editFileMap->bs);
    1829             : 
    1830        2074 :                         e = gf_isom_box_size((GF_Box *) sidx);
    1831        2074 :                         if (e) goto exit;
    1832        2074 :                         e = gf_isom_box_write((GF_Box *) sidx, movie->editFileMap->bs);
    1833        2074 :                         if (e) goto exit;
    1834             : 
    1835        2074 :                         if (use_ssix && !ssix && !movie->root_ssix) {
    1836             :                                 u32 k;
    1837           0 :                                 ssix = (GF_SubsegmentIndexBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_SSIX);
    1838           0 :                                 if (!ssix) return GF_OUT_OF_MEM;
    1839           0 :                                 ssix->subsegments = gf_malloc(sizeof(GF_SubsegmentInfo) * sidx->nb_refs);
    1840           0 :                                 if (!ssix->subsegments) return GF_OUT_OF_MEM;
    1841           0 :                                 ssix->subsegment_count = sidx->nb_refs;
    1842           0 :                                 ssix->subsegment_alloc = ssix->subsegment_count;
    1843           0 :                                 for (k=0; k<sidx->nb_refs; k++) {
    1844           0 :                                         GF_SubsegmentInfo *subs = &ssix->subsegments[k];
    1845           0 :                                         subs->range_count = 2;
    1846           0 :                                         subs->ranges = gf_malloc(sizeof(GF_SubsegmentRangeInfo)*2);
    1847           0 :                                         if (!subs->ranges) return GF_OUT_OF_MEM;
    1848           0 :                                         subs->ranges[0].level = 1;
    1849           0 :                                         subs->ranges[1].level = 2;
    1850           0 :                                         subs->ranges[0].range_size = subs->ranges[1].range_size = 0;
    1851             :                                 }
    1852             : 
    1853           0 :                                 e = gf_isom_box_size((GF_Box *) ssix);
    1854           0 :                                 if (e) return e;
    1855           0 :                                 e = gf_isom_box_write((GF_Box *) ssix, movie->editFileMap->bs);
    1856           0 :                                 if (e) return e;
    1857             :                         }
    1858             : 
    1859        2074 :                         sidx_end = gf_bs_get_position(movie->editFileMap->bs);
    1860             : 
    1861        2074 :                         if (daisy_sidx) {
    1862             :                                 SIDXEntry *entry;
    1863           0 :                                 GF_SAFEALLOC(entry, SIDXEntry);
    1864           0 :                                 if (!entry) {
    1865             :                                         e = GF_OUT_OF_MEM;
    1866             :                                         goto exit;
    1867             :                                 }
    1868           0 :                                 entry->sidx = sidx;
    1869           0 :                                 entry->start_offset = sidx_start;
    1870           0 :                                 gf_list_add(daisy_sidx, entry);
    1871             :                         }
    1872             :                 }
    1873             : 
    1874        2350 :                 if (is_root_sidx) {
    1875             :                         root_sidx = sidx;
    1876             :                         sidx = NULL;
    1877             :                 }
    1878             :                 count = cur_idx = 0;
    1879             :         }
    1880             : 
    1881             : 
    1882             :         last_top_box_pos = root_prev_offset = sidx_end;
    1883             :         sidx_idx = 0;
    1884             :         sidx_dur = 0;
    1885             :         local_sidx_start = local_sidx_end = 0;
    1886             : 
    1887             :         /*cumulated segments duration since start of the sidx */
    1888             :         frag_count = frags_per_subsidx;
    1889             :         cur_dur = 0;
    1890             :         cur_idx = 0;
    1891             :         first_frag_in_subseg = GF_TRUE;
    1892             :         e = GF_OK;
    1893        9170 :         while (gf_list_count(movie->moof_list)) {
    1894             :                 s32 offset_diff;
    1895             :                 u32 moof_size;
    1896             : 
    1897        3118 :                 movie->moof = (GF_MovieFragmentBox*)gf_list_get(movie->moof_list, 0);
    1898        3118 :                 gf_list_rem(movie->moof_list, 0);
    1899             : 
    1900             :                 /*hierarchical or daisy-chain SIDXs*/
    1901        3118 :                 if (!no_sidx && !sidx && (root_sidx || daisy_chain_sidx) ) {
    1902             :                         u32 subsegments_remaining;
    1903           0 :                         sidx = (GF_SegmentIndexBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_SIDX);
    1904           0 :                         if (!sidx) return GF_OUT_OF_MEM;
    1905           0 :                         sidx->reference_ID = referenceTrackID;
    1906           0 :                         sidx->timescale = trak ? trak->Media->mediaHeader->timeScale : 1000;
    1907           0 :                         sidx->earliest_presentation_time = get_presentation_time( ref_track_decode_time + sidx_dur + moof_get_earliest_cts(movie->moof, referenceTrackID), ts_shift);
    1908             : 
    1909             :                         frag_count = frags_per_subsidx;
    1910             : 
    1911             :                         /*last segment, add only one ref*/
    1912           0 :                         subsegments_remaining = 1 + gf_list_count(movie->moof_list);
    1913           0 :                         if (subseg_per_sidx*frags_per_subseg > subsegments_remaining) {
    1914           0 :                                 subseg_per_sidx = subsegments_remaining / frags_per_subseg;
    1915           0 :                                 if (subseg_per_sidx * frags_per_subseg < subsegments_remaining) subseg_per_sidx++;
    1916             :                         }
    1917             :                         /*we don't write anything between sidx and following moov*/
    1918           0 :                         sidx->first_offset = 0;
    1919           0 :                         sidx->nb_refs = subseg_per_sidx;
    1920           0 :                         if (daisy_chain_sidx && (nb_subsegs>1)) {
    1921           0 :                                 sidx->nb_refs += 1;
    1922             :                         }
    1923           0 :                         sidx->refs = (GF_SIDXReference*)gf_malloc(sizeof(GF_SIDXReference)*sidx->nb_refs);
    1924           0 :                         if (!sidx->refs) return GF_OUT_OF_MEM;
    1925           0 :                         memset(sidx->refs, 0, sizeof(GF_SIDXReference)*sidx->nb_refs);
    1926             : 
    1927           0 :                         if (root_sidx)
    1928           0 :                                 root_sidx->refs[sidx_idx].reference_type = GF_TRUE;
    1929             : 
    1930             :                         /*remember start of sidx*/
    1931           0 :                         local_sidx_start = gf_bs_get_position(movie->editFileMap->bs);
    1932             : 
    1933             :                         /*write it*/
    1934           0 :                         e = gf_isom_box_size((GF_Box *) sidx);
    1935           0 :                         if (e) goto exit;
    1936           0 :                         e = gf_isom_box_write((GF_Box *) sidx, movie->editFileMap->bs);
    1937           0 :                         if (e) goto exit;
    1938             : 
    1939           0 :                         local_sidx_end = gf_bs_get_position(movie->editFileMap->bs);
    1940             : 
    1941             :                         /*adjust prev offset*/
    1942             :                         last_top_box_pos = local_sidx_end;
    1943             : 
    1944           0 :                         if (daisy_sidx) {
    1945             :                                 SIDXEntry *entry;
    1946           0 :                                 GF_SAFEALLOC(entry, SIDXEntry);
    1947           0 :                                 if (!entry) {
    1948             :                                         e = GF_OUT_OF_MEM;
    1949             :                                         goto exit;
    1950             :                                 }
    1951           0 :                                 entry->sidx = sidx;
    1952           0 :                                 entry->start_offset = local_sidx_start;
    1953           0 :                                 gf_list_add(daisy_sidx, entry);
    1954             :                         }
    1955             :                 }
    1956             : 
    1957        3118 :                 offset_diff = (s32) (gf_bs_get_position(movie->editFileMap->bs) - movie->moof->fragment_offset);
    1958        3118 :                 movie->moof->fragment_offset = gf_bs_get_position(movie->editFileMap->bs);
    1959             : 
    1960        3118 :                 if (!e) {
    1961             :                         Bool generate_ssix = GF_FALSE;
    1962        3118 :                         if (movie->root_ssix) generate_ssix = GF_TRUE;
    1963        3088 :                         else if (use_ssix && ssix) generate_ssix = GF_TRUE;
    1964             : 
    1965        3118 :                         e = StoreFragment(movie, GF_FALSE, offset_diff, &moof_size, GF_FALSE);
    1966        3118 :                         if (e) {
    1967             :                                 e = GF_OUT_OF_MEM;
    1968             :                                 goto exit;
    1969             :                         }
    1970             : 
    1971        3118 :                         if (sidx) {
    1972        2442 :                                 u32 cur_index = idx_offset + cur_idx;
    1973             : 
    1974             :                                 /*do not compute earliest CTS if single segment sidx since we only have set the info for one subsegment*/
    1975        2442 :                                 if (!movie->root_sidx && first_frag_in_subseg) {
    1976        2166 :                                         u64 first_cts = get_presentation_time( ref_track_decode_time + sidx_dur + cur_dur +  moof_get_earliest_cts(movie->moof, referenceTrackID), ts_shift);
    1977        2166 :                                         if (cur_index) {
    1978          92 :                                                 u32 subseg_dur = (u32) (first_cts - prev_earliest_cts);
    1979          92 :                                                 sidx->refs[cur_index-1].subsegment_duration = subseg_dur;
    1980          92 :                                                 if (root_sidx) root_sidx->refs[sidx_idx].subsegment_duration += subseg_dur;
    1981             :                                         }
    1982             :                                         prev_earliest_cts = first_cts;
    1983             :                                         first_frag_in_subseg = GF_FALSE;
    1984             :                                 }
    1985             : 
    1986        2442 :                                 if (sidx->nb_refs<=cur_index) {
    1987         276 :                                         sidx->nb_refs = cur_index+1;
    1988         276 :                                         sidx->refs = gf_realloc(sidx->refs, sizeof(GF_SIDXReference)*sidx->nb_refs);
    1989         276 :                                         memset(&sidx->refs[cur_index], 0, sizeof(GF_SIDXReference));
    1990             :                                 }
    1991             : 
    1992             :                                 /*we refer to next moof*/
    1993        2442 :                                 sidx->refs[cur_index].reference_type = GF_FALSE;
    1994        2442 :                                 if (!sidx->refs[cur_index].SAP_type) {
    1995        2442 :                                         sidx->refs[cur_index].SAP_type = moof_get_sap_info(movie->moof, referenceTrackID, & sidx->refs[cur_index].SAP_delta_time, & sidx->refs[cur_index].starts_with_SAP);
    1996        2442 :                                         if (sidx->refs[cur_index].SAP_type) {
    1997        2428 :                                                 if (root_sidx && !root_sidx->refs[sidx_idx].SAP_type) {
    1998           0 :                                                         root_sidx->refs[sidx_idx].SAP_type = sidx->refs[cur_index].SAP_type;
    1999           0 :                                                         root_sidx->refs[sidx_idx].SAP_delta_time = sidx->refs[cur_index].SAP_delta_time;
    2000           0 :                                                         root_sidx->refs[sidx_idx].starts_with_SAP = sidx->refs[cur_index].starts_with_SAP;
    2001             :                                                 }
    2002             :                                         }
    2003             :                                 }
    2004        2442 :                                 cur_dur += moof_get_duration(movie->moof, referenceTrackID);
    2005             : 
    2006             :                                 /*reference size is end of the moof we just wrote minus last_box_pos*/
    2007        2442 :                                 sidx->refs[cur_index].reference_size += (u32) ( gf_bs_get_position(movie->editFileMap->bs) - last_top_box_pos) ;
    2008        2442 :                                 last_top_box_pos = gf_bs_get_position(movie->editFileMap->bs);
    2009             : 
    2010        2442 :                                 count++;
    2011             : 
    2012        2442 :                                 if (generate_ssix) {
    2013             :                                         u32 last_sseg_range0_size, remain_size;
    2014          30 :                                         if (movie->root_ssix) {
    2015             :                                                 ssix = movie->root_ssix;
    2016          30 :                                                 if (ssix->subsegment_count <= cur_index) {
    2017             :                                                         assert(ssix->subsegment_count == cur_index);
    2018          30 :                                                         ssix->subsegment_count = cur_index+1;
    2019          30 :                                                         ssix->subsegment_alloc = ssix->subsegment_count;
    2020          30 :                                                         ssix->subsegments = gf_realloc(ssix->subsegments, ssix->subsegment_count * sizeof(GF_SubsegmentInfo));
    2021          30 :                                                         ssix->subsegments[cur_index].range_count = 2;
    2022          30 :                                                         ssix->subsegments[cur_index].ranges = gf_malloc(sizeof(GF_SubsegmentRangeInfo)*2);
    2023             :                                                 }
    2024             :                                         }
    2025             :                                         assert(ssix);
    2026          30 :                                         ssix->subsegments[cur_index].ranges[0].level = 1;
    2027          30 :                                         ssix->subsegments[cur_index].ranges[0].range_size = moof_get_first_sap_end(movie->moof);
    2028             : 
    2029          30 :                                         last_sseg_range0_size = (cur_index < ssix->subsegment_count) ? ssix->subsegments[cur_index].ranges[0].range_size : 0;
    2030          30 :                                         ssix->subsegments[cur_index].ranges[1].level = 2;
    2031             : 
    2032          30 :                                         remain_size = sidx->refs[cur_index].reference_size - last_sseg_range0_size;
    2033          30 :                                         ssix->subsegments[cur_index].ranges[1].range_size = remain_size;
    2034          30 :                                         if (remain_size>0xFFFFFF) {
    2035           0 :                                                 GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso fragment] Remaining subsegment size %d larger than max ssix range size 0xFFFFFF, file might be broken\n", remain_size));
    2036             :                                         }
    2037             : 
    2038          30 :                                         if (movie->root_ssix)
    2039             :                                                 ssix = NULL;
    2040             :                                 }
    2041             : 
    2042             :                                 /*we are switching subsegment*/
    2043        2442 :                                 frag_count--;
    2044             : 
    2045        2442 :                                 if (count==frags_per_subseg) {
    2046             :                                         count = 0;
    2047             :                                         first_frag_in_subseg = GF_TRUE;
    2048        2442 :                                         cur_idx++;
    2049             :                                 }
    2050             : 
    2051             :                                 /*switching to next SIDX*/
    2052        2442 :                                 if ((cur_idx==subseg_per_sidx) || !frag_count) {
    2053             :                                         u32 subseg_dur;
    2054             :                                         /*update last ref duration*/
    2055             : 
    2056             :                                         //get next segment earliest cts - if estimation failed, use ref_track_next_cts
    2057        2350 :                                         if ((next_earliest_cts==-1) || (next_earliest_cts < prev_earliest_cts))  {
    2058             :                                                 u64 next_cts;
    2059           2 :                                                 if (gf_list_count(movie->moof_list)) {
    2060           0 :                                                         next_cts = get_presentation_time( ref_track_decode_time + sidx_dur + cur_dur + moof_get_earliest_cts((GF_MovieFragmentBox*)gf_list_get(movie->moof_list, 0), referenceTrackID), ts_shift);
    2061             :                                                 } else {
    2062             :                                                         next_cts = get_presentation_time( ref_track_next_cts, ts_shift);
    2063             :                                                 }
    2064           2 :                                                 subseg_dur = (u32) (next_cts - prev_earliest_cts);
    2065             :                                         } else {
    2066        2348 :                                                 subseg_dur = (u32) (next_earliest_cts - prev_earliest_cts);
    2067             :                                         }
    2068             : 
    2069        2350 :                                         if (movie->root_sidx) {
    2070         276 :                                                 sidx->refs[idx_offset].subsegment_duration = subseg_dur;
    2071             :                                         }
    2072             :                                         /*if daisy chain and not the last sidx, we have an extra entry at the end*/
    2073        2074 :                                         else if (daisy_chain_sidx && (nb_subsegs>1)) {
    2074           0 :                                                 sidx->refs[sidx->nb_refs - 2].subsegment_duration = subseg_dur;
    2075             :                                         } else {
    2076        2074 :                                                 sidx->refs[sidx->nb_refs-1].subsegment_duration = subseg_dur;
    2077             :                                         }
    2078             : 
    2079        2350 :                                         if (root_sidx) {
    2080             : 
    2081           0 :                                                 root_sidx->refs[sidx_idx].subsegment_duration += subseg_dur;
    2082             : 
    2083             : 
    2084           0 :                                                 root_sidx->refs[sidx_idx].reference_size = (u32) (gf_bs_get_position(movie->editFileMap->bs) - local_sidx_start);
    2085           0 :                                                 if (!sidx_idx) {
    2086           0 :                                                         root_sidx->earliest_presentation_time = sidx->earliest_presentation_time;
    2087             :                                                 }
    2088           0 :                                                 sidx_rewrite(sidx, movie->editFileMap->bs, local_sidx_start, ssix);
    2089           0 :                                                 gf_isom_box_del((GF_Box*)sidx);
    2090             :                                                 sidx = NULL;
    2091        2350 :                                         } else if (daisy_chain_sidx) {
    2092           0 :                                                 SIDXEntry *entry = (SIDXEntry*)gf_list_last(daisy_sidx);
    2093           0 :                                                 entry->end_offset = gf_bs_get_position(movie->editFileMap->bs);
    2094           0 :                                                 nb_subsegs--;
    2095             :                                                 sidx = NULL;
    2096             :                                         }
    2097        2350 :                                         sidx_dur += cur_dur;
    2098             :                                         cur_dur = 0;
    2099             :                                         count = 0;
    2100             :                                         cur_idx=0;
    2101        2350 :                                         if (movie->root_sidx)
    2102         276 :                                                 movie->root_sidx_index++;
    2103        2350 :                                         sidx_idx++;
    2104             :                                 }
    2105             :                         }
    2106             :                 }
    2107        3118 :                 gf_isom_box_del((GF_Box *) movie->moof);
    2108        3118 :                 movie->moof = NULL;
    2109             :         }
    2110             : 
    2111             :         /*append segment marker box*/
    2112        3026 :         if (segment_marker_4cc) {
    2113           0 :                 gf_bs_write_u32(movie->editFileMap->bs, 8);       //write size field
    2114           0 :                 gf_bs_write_u32(movie->editFileMap->bs, segment_marker_4cc); //write box type field
    2115             :         }
    2116             : 
    2117        3026 :         if (movie->root_sidx) {
    2118         276 :                 if (last_segment && !movie->dyn_root_sidx) {
    2119             :                         assert(movie->root_sidx_index == movie->root_sidx->nb_refs);
    2120             : 
    2121           0 :                         sidx_rewrite(movie->root_sidx, movie->editFileMap->bs, movie->root_sidx_offset, movie->root_ssix);
    2122           0 :                         gf_isom_box_del((GF_Box*) movie->root_sidx);
    2123           0 :                         movie->root_sidx = NULL;
    2124             : 
    2125           0 :                         if (movie->root_ssix) {
    2126           0 :                                 gf_isom_box_del((GF_Box*)movie->root_ssix);
    2127           0 :                                 movie->root_ssix = NULL;
    2128             :                         }
    2129             :                 }
    2130         276 :                 if (ssix)
    2131           0 :                         gf_isom_box_del((GF_Box*)ssix);
    2132             : 
    2133         276 :                 compute_seg_size(movie, out_seg_size);
    2134         276 :                 goto exit;
    2135             :         }
    2136             : 
    2137        2750 :         if (sidx) {
    2138             :                 assert(!root_sidx);
    2139        2074 :                 sidx_rewrite(sidx, movie->editFileMap->bs, sidx_start, ssix);
    2140        2074 :                 gf_isom_box_del((GF_Box*)sidx);
    2141             :         }
    2142        2750 :         if (ssix) {
    2143           0 :                 gf_isom_box_del((GF_Box*)ssix);
    2144             :                 ssix = NULL;
    2145             :         }
    2146             : 
    2147        2750 :         if (daisy_sidx) {
    2148             :                 u32 i, j;
    2149             :                 u64 last_entry_end_offset = 0;
    2150             : 
    2151           0 :                 count = gf_list_count(daisy_sidx);
    2152           0 :                 for (i=count; i>1; i--) {
    2153           0 :                         SIDXEntry *entry = (SIDXEntry*)gf_list_get(daisy_sidx, i-2);
    2154           0 :                         SIDXEntry *next_entry = (SIDXEntry*)gf_list_get(daisy_sidx, i-1);
    2155             : 
    2156           0 :                         if (!last_entry_end_offset) {
    2157           0 :                                 last_entry_end_offset = next_entry->end_offset;
    2158             :                                 /*rewrite last sidx*/
    2159           0 :                                 sidx_rewrite(next_entry->sidx, movie->editFileMap->bs, next_entry->start_offset, NULL);
    2160             :                         }
    2161             :                         /*copy over SAP info for last item (which points to next item !)*/
    2162           0 :                         entry->sidx->refs[entry->sidx->nb_refs-1] = next_entry->sidx->refs[0];
    2163             :                         /*and rewrite reference type, size and dur*/
    2164           0 :                         entry->sidx->refs[entry->sidx->nb_refs-1].reference_type = GF_TRUE;
    2165           0 :                         entry->sidx->refs[entry->sidx->nb_refs-1].reference_size = (u32) (last_entry_end_offset - next_entry->start_offset);
    2166           0 :                         entry->sidx->refs[entry->sidx->nb_refs-1].subsegment_duration = 0;
    2167           0 :                         for (j=0; j<next_entry->sidx->nb_refs; j++) {
    2168           0 :                                 entry->sidx->refs[entry->sidx->nb_refs-1].subsegment_duration += next_entry->sidx->refs[j].subsegment_duration;
    2169             :                         }
    2170           0 :                         sidx_rewrite(entry->sidx, movie->editFileMap->bs, entry->start_offset, NULL);
    2171             :                 }
    2172           0 :                 while (gf_list_count(daisy_sidx)) {
    2173           0 :                         SIDXEntry *entry = (SIDXEntry*)gf_list_last(daisy_sidx);
    2174           0 :                         gf_isom_box_del((GF_Box*)entry->sidx);
    2175           0 :                         gf_free(entry);
    2176           0 :                         gf_list_rem_last(daisy_sidx);
    2177             :                 }
    2178           0 :                 gf_list_del(daisy_sidx);
    2179             :         }
    2180        2750 :         if (root_sidx) {
    2181           0 :                 sidx_rewrite(root_sidx, movie->editFileMap->bs, sidx_start, NULL);
    2182           0 :                 gf_isom_box_del((GF_Box*)root_sidx);
    2183             :         }
    2184             : 
    2185        2750 :         if ((root_sidx || sidx) && !daisy_chain_sidx) {
    2186        2074 :                 if (index_start_range) *index_start_range = sidx_start;
    2187        2074 :                 if (index_end_range) *index_end_range = sidx_end - 1;
    2188             :         }
    2189             : 
    2190        2750 :         if (movie->append_segment) {
    2191             :                 char bloc[1024];
    2192           0 :                 u32 seg_size = (u32) gf_bs_get_size(movie->editFileMap->bs);
    2193           0 :                 gf_bs_seek(movie->editFileMap->bs, 0);
    2194           0 :                 while (seg_size) {
    2195           0 :                         u32 size = gf_bs_read_data(movie->editFileMap->bs, bloc, (seg_size>1024) ? 1024 : seg_size);
    2196           0 :                         gf_bs_write_data(movie->movieFileMap->bs, bloc, size);
    2197           0 :                         seg_size -= size;
    2198             :                 }
    2199           0 :                 gf_isom_datamap_del(movie->editFileMap);
    2200           0 :                 movie->editFileMap = gf_isom_fdm_new_temp(NULL);
    2201        2750 :         } else if (close_segment_handle == GF_TRUE) {
    2202           0 :                 gf_isom_datamap_del(movie->editFileMap);
    2203           0 :                 movie->editFileMap = NULL;
    2204             :         }
    2205        2750 :         compute_seg_size(movie, out_seg_size);
    2206             : 
    2207        3026 : exit:
    2208        3026 :         if (movie->editFileMap && (orig_bs != movie->editFileMap->bs)) {
    2209             :                 u32 tmpsize;
    2210        3026 :                 gf_bs_get_content_no_truncate(movie->editFileMap->bs, &movie->block_buffer, &tmpsize, &movie->block_buffer_size);
    2211        3026 :                 gf_bs_del(movie->editFileMap->bs);
    2212        3026 :                 movie->editFileMap->bs = orig_bs;
    2213             :         }
    2214             :         return e;
    2215             : }
    2216             : 
    2217             : GF_EXPORT
    2218          17 : GF_Err gf_isom_flush_sidx(GF_ISOFile *movie, u32 sidx_max_size, Bool force_v1)
    2219             : {
    2220             :         GF_BitStream *bs;
    2221             :         GF_Err e;
    2222             :         u32 size;
    2223             :         //and only at setup
    2224          17 :         if (!movie || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) return GF_BAD_PARAM;
    2225          17 :         if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE;
    2226             : 
    2227          17 :         if (! movie->on_block_out) return GF_BAD_PARAM;
    2228          17 :         if (! movie->root_sidx) return GF_BAD_PARAM;
    2229             : 
    2230          17 :         if (!movie->block_buffer_size) movie->block_buffer_size = movie->on_block_out_block_size;
    2231          17 :         bs = gf_bs_new_cbk_buffer(movie->on_block_out, movie->on_block_out_usr_data, movie->block_buffer, movie->block_buffer_size);
    2232          17 :         gf_bs_prevent_dispatch(bs, GF_TRUE);
    2233             :         
    2234             :         assert(movie->root_sidx_index == movie->root_sidx->nb_refs);
    2235             : 
    2236          17 :         if (force_v1)
    2237           0 :                 movie->root_sidx->version = 1;
    2238             :                 
    2239          17 :         e = gf_isom_box_size((GF_Box*)movie->root_sidx);
    2240          17 :         size = (u32) movie->root_sidx->size;
    2241          17 :         if (movie->root_ssix) {
    2242           1 :                 e = gf_isom_box_size((GF_Box*)movie->root_ssix);
    2243           1 :                 size += (u32) movie->root_ssix->size;
    2244           1 :                 movie->root_sidx->first_offset = (u32) movie->root_ssix->size;
    2245             :         }
    2246             : 
    2247          17 :         if (sidx_max_size && (size > sidx_max_size) ) {
    2248             : #ifndef GPAC_DISABLE_LOG
    2249           0 :                 u32 orig_seg_count = movie->root_sidx->nb_refs;
    2250             : #endif
    2251             :                 //trash 8 bytes to be able to write a free box before
    2252           0 :                 sidx_max_size -= 8;
    2253           0 :                 GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso fragment] SIDX size %d is larger than allocated SIDX block %d, merging final segments\n", movie->root_sidx->size, sidx_max_size));
    2254           0 :                 while (movie->root_sidx->nb_refs>2) {
    2255           0 :                         movie->root_sidx->refs[movie->root_sidx->nb_refs-2].subsegment_duration += movie->root_sidx->refs[movie->root_sidx->nb_refs-1].subsegment_duration;
    2256           0 :                         movie->root_sidx->refs[movie->root_sidx->nb_refs-2].reference_size += movie->root_sidx->refs[movie->root_sidx->nb_refs-1].reference_size;
    2257           0 :                         movie->root_sidx->nb_refs--;
    2258           0 :                         if (movie->root_ssix) {
    2259           0 :                                 movie->root_ssix->subsegments[movie->root_ssix->subsegment_count-2].ranges[1].range_size += movie->root_ssix->subsegments[movie->root_ssix->subsegment_count-1].ranges[0].range_size;
    2260           0 :                                 movie->root_ssix->subsegments[movie->root_ssix->subsegment_count-2].ranges[1].range_size += movie->root_ssix->subsegments[movie->root_ssix->subsegment_count-1].ranges[1].range_size;
    2261           0 :                                 movie->root_ssix->subsegment_count--;
    2262             :                         }
    2263             : 
    2264           0 :                         e = gf_isom_box_size((GF_Box*)movie->root_sidx);
    2265           0 :                         size = (u32) movie->root_sidx->size;
    2266           0 :                         if (movie->root_ssix) {
    2267           0 :                                 e = gf_isom_box_size((GF_Box*)movie->root_ssix);
    2268           0 :                                 size += (u32) movie->root_ssix->size;
    2269           0 :                                 movie->root_sidx->first_offset = (u32) movie->root_ssix->size;
    2270             :                         }
    2271             : 
    2272           0 :                         if (size < sidx_max_size) break;
    2273             :                 }
    2274           0 :                 if (size > sidx_max_size) {
    2275           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso fragment] SIDX size %d is larger than allocated SIDX block and no more segments to merge\n", size, sidx_max_size));
    2276             :                         return GF_IO_ERR;
    2277             :                 } else {
    2278           0 :                         GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso fragment] Merged %d segments in SIDX to fit allocated block, remaining segments %d\n", orig_seg_count - movie->root_sidx->nb_refs, movie->root_sidx->nb_refs));
    2279             :                 }
    2280             :         }
    2281          17 :         if (!e) {
    2282          17 :                 if (movie->root_ssix) {
    2283           1 :                         gf_isom_box_size((GF_Box *) movie->root_ssix);
    2284             : 
    2285           1 :                         if (movie->compress_mode>=GF_ISOM_COMP_MOOF_SSIX) {
    2286             :                                 u32 ssix_comp_size;
    2287             :                                 //compute ssix compressed size by using NULL destination bitstream
    2288             :                                 //not really optimum since we compress twice the ssix, to optimize ...
    2289           0 :                                 e = gf_isom_write_compressed_box(movie, (GF_Box *) movie->root_ssix, GF_4CC('!', 's', 's', 'x'), NULL, &ssix_comp_size);
    2290           0 :                                 movie->root_sidx->first_offset = ssix_comp_size;
    2291             :                         } else {
    2292           1 :                                 movie->root_sidx->first_offset = (u32) movie->root_ssix->size;
    2293             :                         }
    2294             :                 }
    2295          17 :                 if (!e) {
    2296          17 :                         if (movie->compress_mode>=GF_ISOM_COMP_MOOF_SIDX) {
    2297           1 :                                 e = gf_isom_write_compressed_box(movie, (GF_Box *) movie->root_sidx, GF_4CC('!', 's', 'i', 'x'), bs, NULL);
    2298             :                         } else {
    2299          16 :                                 e = gf_isom_box_write((GF_Box *) movie->root_sidx, bs);
    2300             :                         }
    2301             :                 }
    2302             : 
    2303          17 :                 if (!e && movie->root_ssix) {
    2304           1 :                         if (movie->compress_mode>=GF_ISOM_COMP_MOOF_SSIX) {
    2305           0 :                                 e = gf_isom_write_compressed_box(movie, (GF_Box *) movie->root_ssix, GF_4CC('!', 's', 's', 'x'), bs, NULL);
    2306             :                         } else {
    2307           1 :                                 e = gf_isom_box_write((GF_Box *) movie->root_ssix, bs);
    2308             :                         }
    2309             :                 }
    2310             :         }
    2311             : 
    2312          17 :         gf_isom_box_del((GF_Box*) movie->root_sidx);
    2313          17 :         movie->root_sidx = NULL;
    2314          17 :         if (movie->root_ssix) {
    2315           1 :                 gf_isom_box_del((GF_Box*) movie->root_ssix);
    2316           1 :                 movie->root_ssix = NULL;
    2317             :         }
    2318             : 
    2319          17 :         gf_bs_get_content_no_truncate(bs, &movie->block_buffer, &size, &movie->block_buffer_size);
    2320          17 :         gf_bs_del(bs);
    2321          17 :         return e;
    2322             : }
    2323             : 
    2324             : GF_EXPORT
    2325          30 : GF_Err gf_isom_close_fragments(GF_ISOFile *movie)
    2326             : {
    2327          30 :         if (movie->use_segments) {
    2328          17 :                 return gf_isom_close_segment(movie, 0, 0, 0, 0, 0, 0, GF_FALSE, GF_FALSE, 1, 0, NULL, NULL, NULL);
    2329             :         } else {
    2330          13 :                 return StoreFragment(movie, GF_FALSE, 0, NULL, GF_TRUE);
    2331             :         }
    2332             : }
    2333             : 
    2334             : GF_EXPORT
    2335        3044 : GF_Err gf_isom_start_segment(GF_ISOFile *movie, const char *SegName, Bool memory_mode)
    2336             : {
    2337             :         GF_Err e;
    2338             :         //and only at setup
    2339        3044 :         if (!movie || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) return GF_BAD_PARAM;
    2340        3044 :         if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE;
    2341             : 
    2342        3044 :         if (gf_list_count(movie->moof_list))
    2343             :                 return GF_BAD_PARAM;
    2344             : 
    2345        3044 :         movie->segment_bs = NULL;
    2346        3044 :         movie->append_segment = GF_FALSE;
    2347             :         /*update segment file*/
    2348        3044 :         if (SegName || !gf_isom_get_filename(movie)) {
    2349        2503 :                 if (movie->editFileMap) gf_isom_datamap_del(movie->editFileMap);
    2350        2503 :                 e = gf_isom_datamap_new(SegName, NULL, GF_ISOM_DATA_MAP_WRITE, &movie->editFileMap);
    2351        2503 :                 movie->segment_start = 0;
    2352        2503 :                 movie->styp_written = GF_FALSE;
    2353        2503 :                 if (e) return e;
    2354             :         } else {
    2355             :                 assert(gf_list_count(movie->moof_list) == 0);
    2356         541 :                 movie->segment_start = gf_bs_get_position(movie->editFileMap->bs);
    2357             :                 /*if movieFileMap is not null, we are concatenating segments to the original movie, force a copy*/
    2358         541 :                 if (movie->movieFileMap)
    2359           0 :                         movie->append_segment = GF_TRUE;
    2360         541 :                 movie->styp_written = GF_TRUE;
    2361             :         }
    2362             : 
    2363             :         /*create a memory bitstream for all file IO until final flush*/
    2364        3044 :         if (memory_mode) {
    2365           0 :                 movie->segment_bs = movie->editFileMap->bs;
    2366           0 :                 movie->editFileMap->bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
    2367             :         }
    2368             :         return GF_OK;
    2369             : }
    2370             : 
    2371             : GF_EXPORT
    2372           1 : GF_Err gf_isom_set_fragment_reference_time(GF_ISOFile *movie, GF_ISOTrackID reference_track_ID, u64 ntp, u64 timestamp)
    2373             : {
    2374           1 :         if (!movie || !movie->moof) return GF_BAD_PARAM;
    2375           0 :         movie->moof->reference_track_ID = reference_track_ID;
    2376           0 :         movie->moof->ntp = ntp;
    2377           0 :         movie->moof->timestamp = timestamp;
    2378           0 :         return GF_OK;
    2379             : }
    2380             : 
    2381             : GF_EXPORT
    2382           1 : GF_Err gf_isom_set_traf_mss_timeext(GF_ISOFile *movie, GF_ISOTrackID reference_track_ID, u64 ntp_in_track_timescale, u64 traf_duration_in_track_timescale)
    2383             : {
    2384             :         u32 i;
    2385           1 :         if (!movie || !movie->moof)
    2386             :                 return GF_BAD_PARAM;
    2387           0 :         for (i=0; i<gf_list_count(movie->moof->TrackList); i++) {
    2388           0 :                 GF_TrackFragmentBox *traf = (GF_TrackFragmentBox*)gf_list_get(movie->moof->TrackList, i);
    2389           0 :                 if (!traf)
    2390             :                         return GF_BAD_PARAM;
    2391           0 :                 if (traf->tfxd)
    2392           0 :                         gf_isom_box_del_parent(&traf->child_boxes, (GF_Box*)traf->tfxd);
    2393           0 :                 traf->tfxd = (GF_MSSTimeExtBox *)gf_isom_box_new_parent(&traf->child_boxes, GF_ISOM_BOX_UUID_TFXD);
    2394           0 :                 if (!traf->tfxd) return GF_OUT_OF_MEM;
    2395           0 :                 traf->tfxd->absolute_time_in_track_timescale = ntp_in_track_timescale;
    2396           0 :                 traf->tfxd->fragment_duration_in_track_timescale = traf_duration_in_track_timescale;
    2397             :         }
    2398             :         return GF_OK;
    2399             : }
    2400             : 
    2401             : GF_EXPORT
    2402        3571 : GF_Err gf_isom_start_fragment(GF_ISOFile *movie, GF_ISOStartFragmentFlags flags)
    2403             : {
    2404             :         u32 i, count;
    2405             :         GF_TrackExtendsBox *trex;
    2406             :         GF_TrackFragmentBox *traf;
    2407             :         GF_Err e;
    2408        3571 :         Bool moof_first = (flags & GF_ISOM_FRAG_MOOF_FIRST) ? GF_TRUE : GF_FALSE;
    2409             : #ifdef GF_ENABLE_CTRN
    2410             :         Bool use_ctrn = (flags & GF_ISOM_FRAG_USE_COMPACT) ? GF_TRUE : GF_FALSE;
    2411             : #endif
    2412             : 
    2413             :         //and only at setup
    2414        3571 :         if (!movie || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) )
    2415             :                 return GF_BAD_PARAM;
    2416        3571 :         if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE;
    2417             : 
    2418        3571 :         count = gf_list_count(movie->moov->mvex->TrackExList);
    2419        3571 :         if (!count)
    2420             :                 return GF_BAD_PARAM;
    2421             : 
    2422             :         /*always force cached mode when writing movie segments*/
    2423        3571 :         if (movie->use_segments) moof_first = GF_TRUE;
    2424        3571 :         movie->moof_first = moof_first;
    2425             : 
    2426             :         //store existing fragment
    2427        3571 :         if (movie->moof) {
    2428          92 :                 e = StoreFragment(movie, movie->use_segments ? GF_TRUE : GF_FALSE, 0, NULL, movie->use_segments ? GF_TRUE : GF_FALSE);
    2429          92 :                 if (e) return e;
    2430             :         }
    2431             : 
    2432             :         //create new fragment
    2433        3571 :         movie->moof = (GF_MovieFragmentBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MOOF);
    2434        3571 :         if (!movie->moof) return GF_OUT_OF_MEM;
    2435        3571 :         movie->moof->mfhd = (GF_MovieFragmentHeaderBox *) gf_isom_box_new_parent(&movie->moof->child_boxes, GF_ISOM_BOX_TYPE_MFHD);
    2436        3571 :         if (!movie->moof->mfhd) return GF_OUT_OF_MEM;
    2437        3571 :         movie->moof->mfhd->sequence_number = movie->NextMoofNumber;
    2438        3571 :         movie->NextMoofNumber ++;
    2439        3571 :         if (movie->use_segments || movie->on_block_out)
    2440        3571 :                 gf_list_add(movie->moof_list, movie->moof);
    2441             : 
    2442             : 
    2443             :         /*remember segment offset*/
    2444        3571 :         movie->moof->fragment_offset = gf_bs_get_position(movie->editFileMap->bs);
    2445             : 
    2446             :         /*prepare MDAT*/
    2447        3571 :         gf_bs_write_u32(movie->editFileMap->bs, 0);
    2448        3571 :         gf_bs_write_u32(movie->editFileMap->bs, GF_ISOM_BOX_TYPE_MDAT);
    2449             : 
    2450             :         //we create a TRAF for each setup track, unused ones will be removed at store time
    2451        8153 :         for (i=0; i<count; i++) {
    2452        4582 :                 trex = (GF_TrackExtendsBox*)gf_list_get(movie->moov->mvex->TrackExList, i);
    2453        4582 :                 traf = (GF_TrackFragmentBox *) gf_isom_box_new_parent(&movie->moof->child_boxes, GF_ISOM_BOX_TYPE_TRAF);
    2454        4582 :                 if (!traf) return GF_OUT_OF_MEM;
    2455        4582 :                 traf->trex = trex;
    2456        4582 :                 traf->tfhd = (GF_TrackFragmentHeaderBox *) gf_isom_box_new_parent(&traf->child_boxes, GF_ISOM_BOX_TYPE_TFHD);
    2457        4582 :                 if (!traf->tfhd) return GF_OUT_OF_MEM;
    2458        4582 :                 traf->tfhd->trackID = trex->trackID;
    2459             :                 //add 8 bytes (MDAT size+type) to avoid the data_offset in the first trun
    2460        4582 :                 traf->tfhd->base_data_offset = movie->moof->fragment_offset + 8;
    2461             : #ifdef GF_ENABLE_CTRN
    2462             :                 traf->use_ctrn = use_ctrn;
    2463             :                 if (trex->inherit_from_traf_id)
    2464             :                         traf->use_inherit = GF_TRUE;
    2465             : #endif
    2466        4582 :                 gf_list_add(movie->moof->TrackList, traf);
    2467             : 
    2468        4582 :                 if (movie->mfra) {
    2469             :                         GF_TrackFragmentRandomAccessBox *tfra;
    2470             :                         GF_RandomAccessEntry *raf;
    2471          24 :                         if (!traf->trex->tfra) {
    2472           3 :                                 tfra = (GF_TrackFragmentRandomAccessBox *)gf_isom_box_new_parent(&movie->mfra->child_boxes, GF_ISOM_BOX_TYPE_TFRA);
    2473           3 :                                 if (!tfra) return GF_OUT_OF_MEM;
    2474           3 :                                 tfra->track_id = traf->trex->trackID;
    2475           3 :                                 tfra->traf_bits = 8;
    2476           3 :                                 tfra->trun_bits = 8;
    2477           3 :                                 tfra->sample_bits = 8;
    2478           3 :                                 gf_list_add(movie->mfra->tfra_list, tfra);
    2479           3 :                                 traf->trex->tfra = tfra;
    2480             :                         }
    2481          24 :                         tfra = traf->trex->tfra;
    2482          24 :                         tfra->entries = (GF_RandomAccessEntry *)gf_realloc(tfra->entries, (tfra->nb_entries+1)*sizeof(GF_RandomAccessEntry));
    2483          24 :                         tfra->nb_entries++;
    2484          24 :                         raf = &tfra->entries[tfra->nb_entries-1];
    2485          24 :                         raf->sample_number = 1;
    2486          24 :                         raf->time = 0;
    2487          24 :                         raf->traf_number = i+1;
    2488             :                         //trun number is set once we fond a sync
    2489          24 :                         raf->trun_number = 0;
    2490          24 :                         if (!strcmp(movie->fileName, "_gpac_isobmff_redirect")) {
    2491          24 :                                 raf->moof_offset = movie->fragmented_file_pos;
    2492             :                         } else {
    2493           0 :                                 raf->moof_offset = movie->moof->fragment_offset;
    2494             :                         }
    2495             :                 }
    2496             :         }
    2497             :         return GF_OK;
    2498             : }
    2499             : 
    2500             : 
    2501          57 : GF_Err gf_isom_set_fragment_template(GF_ISOFile *movie, u8 *tpl_data, u32 tpl_size, Bool *has_tfdt, GF_SegmentIndexBox **out_sidx)
    2502             : {
    2503             :         GF_BitStream *bs;
    2504             :         GF_Err e=GF_OK;
    2505          57 :         if (out_sidx) *out_sidx = NULL;
    2506          57 :         if (!movie->moof) return GF_BAD_PARAM;
    2507             : 
    2508          57 :         bs = gf_bs_new(tpl_data, tpl_size, GF_BITSTREAM_READ);
    2509         285 :         while (gf_bs_available(bs)) {
    2510             :                 GF_Box *a;
    2511         171 :                 e = gf_isom_box_parse(&a, bs);
    2512         171 :                 if (e) break;
    2513         171 :                 if (a->type==GF_ISOM_BOX_TYPE_STYP) {
    2514          57 :                         if (movie->brand) {
    2515          57 :                                 gf_list_del_item(movie->TopBoxes, movie->brand);
    2516          57 :                                 gf_isom_box_del((GF_Box *)movie->brand);
    2517             :                         }
    2518          57 :                         movie->brand = (GF_FileTypeBox *) a;
    2519          57 :                         gf_list_add(movie->TopBoxes, movie->brand);
    2520         228 :                         continue;
    2521             :                 }
    2522         114 :                 if (a->type==GF_ISOM_BOX_TYPE_OTYP) {
    2523           0 :                         if (movie->otyp) {
    2524           0 :                                 gf_list_del_item(movie->TopBoxes, movie->otyp);
    2525           0 :                                 gf_isom_box_del(movie->otyp);
    2526             :                         }
    2527           0 :                         movie->otyp = (GF_Box *) a;
    2528           0 :                         gf_list_add(movie->TopBoxes, movie->otyp);
    2529           0 :                         continue;
    2530             :                 }
    2531         114 :                 if (a->type==GF_ISOM_BOX_TYPE_MOOF) {
    2532             :                         u32 i, count, j, nb_trex;
    2533             :                         s32 idx;
    2534             :                         Bool single_track=GF_FALSE;
    2535             :                         GF_MovieFragmentBox *moof = (GF_MovieFragmentBox *)a;
    2536             : 
    2537          57 :                         moof->fragment_offset = movie->moof->fragment_offset;
    2538          57 :                         nb_trex = gf_list_count(movie->moov->mvex->TrackExList);
    2539          57 :                         count = gf_list_count(moof->TrackList);
    2540         114 :                         for (i=0; i<count; i++) {
    2541          57 :                                 GF_TrackFragmentBox *traf = gf_list_get(moof->TrackList, i);
    2542          57 :                                 GF_TrackBox *trak = traf->tfhd ? gf_isom_get_track_from_id(movie->moov, traf->tfhd->trackID) : NULL;
    2543          57 :                                 if (traf->tfhd && !trak && !single_track && (gf_list_count(movie->moov->trackList)==1)) {
    2544           0 :                                         trak = gf_list_get(movie->moov->trackList, 0);
    2545             :                                         single_track = GF_TRUE;
    2546           0 :                                         traf->tfhd->trackID = trak->Header->trackID;
    2547             :                                 }
    2548          57 :                                 for (j=0; j<nb_trex && trak; j++) {
    2549          57 :                                         GF_TrackExtendsBox *trex = gf_list_get(movie->moov->mvex->TrackExList, j);
    2550          57 :                                         if (trex->trackID == traf->tfhd->trackID) {
    2551          57 :                                                 traf->trex = trex;
    2552          57 :                                                 break;
    2553             :                                         }
    2554             :                                 }
    2555          57 :                                 if (!trak || !traf->trex) {
    2556           0 :                                         gf_list_rem(moof->TrackList, i);
    2557           0 :                                         i--;
    2558           0 :                                         count--;
    2559           0 :                                         gf_isom_box_del((GF_Box*)traf);
    2560           0 :                                         continue;
    2561             :                                 }
    2562          57 :                                 traf->tfhd->base_data_offset = movie->moof->fragment_offset + 8;
    2563          57 :                                 if (traf->tfdt && has_tfdt)
    2564          57 :                                         *has_tfdt = GF_TRUE;
    2565             :                         }
    2566             :                         //remove old moof and switch with this one
    2567          57 :                         idx = gf_list_find(movie->moof_list, movie->moof);
    2568          57 :                         if (idx>=0) {
    2569          57 :                                 gf_list_rem(movie->moof_list, idx);
    2570          57 :                                 gf_list_add(movie->moof_list, moof);
    2571             :                         }
    2572          57 :                         gf_isom_box_del((GF_Box *)movie->moof);
    2573          57 :                         movie->moof = moof;
    2574          57 :                         continue;
    2575             :                 }
    2576          57 :                 if (a->type==GF_ISOM_BOX_TYPE_SIDX) {
    2577          57 :                         if (out_sidx && !*out_sidx) {
    2578          57 :                                 *out_sidx = (GF_SegmentIndexBox *) a;
    2579          57 :                                 continue;
    2580             :                         }
    2581             :                 }
    2582           0 :                 gf_isom_box_del(a);
    2583             :         }
    2584          57 :         gf_bs_del(bs);
    2585          57 :         return e;
    2586             : }
    2587             : 
    2588             : static
    2589             : u32 GetRunSize(GF_TrackFragmentRunBox *trun)
    2590             : {
    2591             :         u32 i, size=0;
    2592     2883243 :         for (i=0; i<trun->nb_samples; i++) {
    2593     2883243 :                 size += trun->samples[i].size;
    2594             :         }
    2595             :         return size;
    2596             : }
    2597             : 
    2598             : GF_EXPORT
    2599      111914 : GF_Err gf_isom_fragment_add_sample(GF_ISOFile *movie, GF_ISOTrackID TrackID, const GF_ISOSample *sample, u32 DescIndex,
    2600             :                                    u32 Duration, u8 PaddingBits, u16 DegradationPriority, Bool redundant_coding)
    2601             : {
    2602             :         u32 count, buffer_size;
    2603             :         u8 *buffer;
    2604             :         u64 pos;
    2605      111914 :         GF_ISOSample *od_sample = NULL;
    2606             :         GF_TrunEntry ent, *prev_ent;
    2607             :         GF_TrackFragmentBox *traf, *traf_2;
    2608             :         GF_TrackFragmentRunBox *trun;
    2609             : 
    2610      111914 :         if (!movie->moof || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) || !sample)
    2611             :                 return GF_BAD_PARAM;
    2612             : 
    2613      111914 :         traf = gf_isom_get_traf(movie, TrackID);
    2614      111914 :         if (!traf)
    2615             :                 return GF_BAD_PARAM;
    2616             : 
    2617      111914 :         if (!traf->tfhd->sample_desc_index)
    2618        3762 :                 traf->tfhd->sample_desc_index = DescIndex ? DescIndex : traf->trex->def_sample_desc_index;
    2619             : 
    2620      111914 :         pos = gf_bs_get_position(movie->editFileMap->bs);
    2621             : 
    2622             : 
    2623             :         //WARNING: we change stream description, create a new TRAF
    2624      111914 :         if ( DescIndex && (traf->tfhd->sample_desc_index != DescIndex)) {
    2625             :                 //if we're caching flush the current run
    2626           2 :                 if (traf->DataCache && !traf->use_sample_interleave) {
    2627           0 :                         count = gf_list_count(traf->TrackRuns);
    2628           0 :                         if (count) {
    2629           0 :                                 trun = (GF_TrackFragmentRunBox *)gf_list_get(traf->TrackRuns, count-1);
    2630           0 :                                 trun->data_offset = (u32) (pos - movie->moof->fragment_offset - 8);
    2631           0 :                                 gf_bs_get_content(trun->cache, &buffer, &buffer_size);
    2632           0 :                                 gf_bs_write_data(movie->editFileMap->bs, buffer, buffer_size);
    2633           0 :                                 gf_bs_del(trun->cache);
    2634           0 :                                 trun->cache = NULL;
    2635           0 :                                 gf_free(buffer);
    2636             :                         }
    2637             :                 }
    2638           2 :                 traf_2 = (GF_TrackFragmentBox *) gf_isom_box_new_parent(&movie->moof->child_boxes, GF_ISOM_BOX_TYPE_TRAF);
    2639           2 :                 if (!traf_2) return GF_OUT_OF_MEM;
    2640           2 :                 traf_2->trex = traf->trex;
    2641           2 :                 traf_2->tfhd = (GF_TrackFragmentHeaderBox *) gf_isom_box_new_parent(&traf_2->child_boxes, GF_ISOM_BOX_TYPE_TFHD);
    2642           2 :                 if (!traf_2->tfhd) return GF_OUT_OF_MEM;
    2643           2 :                 traf_2->tfhd->trackID = traf->tfhd->trackID;
    2644             :                 //keep the same offset
    2645           2 :                 traf_2->tfhd->base_data_offset = movie->moof->fragment_offset + 8;
    2646           2 :                 gf_list_add(movie->moof->TrackList, traf_2);
    2647             : 
    2648             :                 //duplicate infos
    2649           2 :                 traf_2->IFrameSwitching = traf->IFrameSwitching;
    2650           2 :                 traf_2->use_sample_interleave = traf->use_sample_interleave;
    2651           2 :                 traf_2->interleave_id = traf->interleave_id;
    2652           2 :                 traf_2->truns_first = traf->truns_first;
    2653           2 :                 traf_2->truns_v1 = traf->truns_v1;
    2654           2 :                 traf_2->large_tfdt = traf->large_tfdt;
    2655           2 :                 traf_2->DataCache  = traf->DataCache;
    2656           2 :                 traf_2->tfhd->sample_desc_index  = DescIndex;
    2657             : 
    2658             :                 //switch them ...
    2659             :                 traf = traf_2;
    2660             :         }
    2661             : 
    2662      111914 :         pos = gf_bs_get_position(movie->editFileMap->bs);
    2663             :         //check if we need a new trun entry
    2664      111914 :         count = (traf->use_sample_interleave && traf->force_new_trun) ? 0 : gf_list_count(traf->TrackRuns);
    2665      111914 :         if (count) {
    2666      108150 :                 trun = (GF_TrackFragmentRunBox *)gf_list_get(traf->TrackRuns, count-1);
    2667             :                 //check data offset when no caching as trun entries shall ALWAYS be contiguous samples
    2668      216300 :                 if (!traf->DataCache && (movie->moof->fragment_offset + 8 + trun->data_offset + GetRunSize(trun) != pos) )
    2669             :                         count = 0;
    2670             : 
    2671             :                 //check I-frame detection
    2672      108150 :                 if (traf->IFrameSwitching && sample->IsRAP)
    2673             :                         count = 0;
    2674             : 
    2675      108150 :                 if (traf->DataCache && (traf->DataCache==trun->sample_count) && !traf->use_sample_interleave)
    2676             :                         count = 0;
    2677             : 
    2678      108150 :                 if (traf->force_new_trun)
    2679             :                         count = 0;
    2680             : 
    2681             :                 //if data cache is on and we're changing TRUN, store the cache and update data offset
    2682      108150 :                 if (!count && traf->DataCache && !traf->use_sample_interleave) {
    2683           0 :                         trun->data_offset = (u32) (pos - movie->moof->fragment_offset - 8);
    2684           0 :                         gf_bs_get_content(trun->cache, &buffer, &buffer_size);
    2685           0 :                         gf_bs_write_data(movie->editFileMap->bs, buffer, buffer_size);
    2686           0 :                         gf_bs_del(trun->cache);
    2687           0 :                         trun->cache = NULL;
    2688           0 :                         gf_free(buffer);
    2689             :                 }
    2690             :         }
    2691      111914 :         traf->force_new_trun = 0;
    2692             : 
    2693             :         //new run
    2694      111914 :         if (!count) {
    2695        3775 :                 trun = (GF_TrackFragmentRunBox *) gf_isom_box_new_parent(&traf->child_boxes, GF_ISOM_BOX_TYPE_TRUN);
    2696        3775 :                 if (!trun) return GF_OUT_OF_MEM;
    2697             :                 //store data offset (we have the 8 btyes offset of the MDAT)
    2698        3775 :                 trun->data_offset = (u32) (pos - movie->moof->fragment_offset - 8);
    2699        3775 :                 gf_list_add(traf->TrackRuns, trun);
    2700             : #ifdef GF_ENABLE_CTRN
    2701             :                 trun->use_ctrn = traf->use_ctrn;
    2702             :                 trun->use_inherit = traf->use_inherit;
    2703             :                 trun->ctso_multiplier = traf->trex->def_sample_duration;
    2704             : #endif
    2705        3775 :                 trun->interleave_id = traf->interleave_id;
    2706        3775 :                 if (traf->truns_v1)
    2707           0 :                         trun->version = 1;
    2708             : 
    2709             :                 //if we use data caching, create a bitstream
    2710        3775 :                 if (traf->DataCache)
    2711           0 :                         trun->cache = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
    2712             :         }
    2713             : 
    2714             :         memset(&ent, 0, sizeof(GF_TrunEntry));
    2715      111914 :         ent.CTS_Offset = sample->CTS_Offset;
    2716      111914 :         ent.Duration = Duration;
    2717      111914 :         ent.dts = sample->DTS;
    2718      111914 :         ent.size = sample->dataLength;
    2719      111914 :         ent.nb_pack = sample->nb_pack;
    2720      111914 :         ent.flags = GF_ISOM_FORMAT_FRAG_FLAGS(PaddingBits, sample->IsRAP, DegradationPriority);
    2721      111914 :         if (sample->IsRAP) {
    2722       33220 :                 ent.flags |= GF_ISOM_GET_FRAG_DEPEND_FLAGS(0, 2, 0, (redundant_coding ? 1 : 0) );
    2723       33220 :                 ent.SAP_type = sample->IsRAP;
    2724             :         }
    2725      111914 :         if (trun->nb_samples) {
    2726      108139 :                 prev_ent = &trun->samples[trun->nb_samples-1];
    2727             :         } else {
    2728             :                 prev_ent = NULL;
    2729             :         }
    2730             : 
    2731      108139 :         if (prev_ent && prev_ent->dts && sample->DTS) {
    2732      107786 :                 u32 nsamp = prev_ent->nb_pack ? prev_ent->nb_pack : 1;
    2733      107786 :                 if (nsamp*prev_ent->Duration != sample->DTS - prev_ent->dts)
    2734        2533 :                         prev_ent->Duration = (u32) (sample->DTS - prev_ent->dts) / nsamp;
    2735             :         }
    2736      111914 :         if (trun->nb_samples >= trun->sample_alloc) {
    2737        4149 :                 trun->sample_alloc += 50;
    2738        4149 :                 if (trun->nb_samples >= trun->sample_alloc) trun->sample_alloc = trun->nb_samples+1;
    2739        4149 :                 trun->samples = gf_realloc(trun->samples, sizeof(GF_TrunEntry)*trun->sample_alloc);
    2740        4149 :                 if (!trun->samples) return GF_OUT_OF_MEM;
    2741             :         }
    2742      111914 :         trun->samples[trun->nb_samples] = ent;
    2743      111914 :         trun->nb_samples ++;
    2744             :         
    2745      111914 :         if (sample->CTS_Offset<0) {
    2746        1488 :                 trun->version = 1;
    2747             :         }
    2748      111914 :         trun->sample_count += sample->nb_pack ? sample->nb_pack : 1;
    2749             : 
    2750             :         //rewrite OD frames
    2751      111914 :         if (traf->trex->track->Media->handler->handlerType == GF_ISOM_MEDIA_OD) {
    2752             :                 //this may fail if dependencies are not well done ...
    2753           2 :                 Media_ParseODFrame(traf->trex->track->Media, sample, &od_sample);
    2754           2 :                 sample = od_sample;
    2755             :         }
    2756             : 
    2757             :         //finally write the data
    2758      111914 :         if (sample->dataLength) {
    2759      111914 :                 if (!traf->DataCache) {
    2760      111914 :                         if (!gf_bs_write_data(movie->editFileMap->bs, sample->data, sample->dataLength)) {
    2761           0 :                                 GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso fragment] Could not add a sample with a size of %u bytes (no DataCache)\n", sample->dataLength));
    2762             :                                 return GF_OUT_OF_MEM;
    2763             :                         }
    2764           0 :                 } else if (trun->cache) {
    2765           0 :                         if (!gf_bs_write_data(trun->cache, sample->data, sample->dataLength)) {
    2766           0 :                                 GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso fragment] Could not add a sample with a size of %u bytes (with cache)\n", sample->dataLength));
    2767             :                                 return GF_OUT_OF_MEM;
    2768             :                         }
    2769             :                 } else {
    2770             :                         return GF_BAD_PARAM;
    2771             :                 }
    2772             :         }
    2773      111914 :         if (od_sample) gf_isom_sample_del(&od_sample);
    2774             : 
    2775      111914 :         if (traf->trex->tfra) {
    2776             :                 GF_RandomAccessEntry *raf;
    2777         514 :                 raf = &traf->trex->tfra->entries[traf->trex->tfra->nb_entries-1];
    2778             :                 //if sample is sync, store its time and trun number
    2779         514 :                 if (!raf->trun_number && sample->IsRAP) {
    2780          18 :                         raf->time = sample->DTS + sample->CTS_Offset;
    2781          18 :                         raf->trun_number = gf_list_count(traf->TrackRuns);
    2782          18 :                         raf->sample_number = trun->sample_count;
    2783             :                 }
    2784             :         }
    2785             :         return GF_OK;
    2786             : }
    2787             : 
    2788             : GF_EXPORT
    2789       20396 : GF_Err gf_isom_fragment_set_cenc_sai(GF_ISOFile *output, GF_ISOTrackID TrackID, u8 *sai_b, u32 sai_b_size, Bool use_subsamples, Bool use_saio_32bit, Bool use_multikey)
    2790             : {
    2791             :         GF_CENCSampleAuxInfo *sai;
    2792       20396 :         GF_TrackFragmentBox  *traf = gf_isom_get_traf(output, TrackID);
    2793             :         GF_SampleEncryptionBox *senc;
    2794             : 
    2795       20396 :         if (!traf)  return GF_BAD_PARAM;
    2796             : 
    2797       20396 :         if (!traf->sample_encryption) {
    2798         344 :                 if (!traf->trex->track->sample_encryption) {
    2799           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[isofile] trying to add CENC SAI without storage box allocated\n" ));
    2800             :                         return GF_BAD_PARAM;
    2801             :                 }
    2802         344 :                 if (traf->trex->track->sample_encryption->type == GF_ISOM_BOX_TYPE_SENC) {
    2803         332 :                         traf->sample_encryption = gf_isom_create_samp_enc_box(0, 0);
    2804             :                 } else {
    2805             :                         GF_SampleEncryptionBox *psec = (GF_SampleEncryptionBox *) traf->trex->track->sample_encryption;
    2806             :                         if (!psec) return GF_ISOM_INVALID_FILE;
    2807          12 :                         traf->sample_encryption = gf_isom_create_piff_psec_box(1, 0, psec->AlgorithmID, psec->IV_size, psec->KID);
    2808             :                 }
    2809         344 :                 if (!traf->sample_encryption) return GF_OUT_OF_MEM;
    2810         344 :                 traf->sample_encryption->traf = traf;
    2811             : 
    2812         344 :                 if (!traf->child_boxes) traf->child_boxes = gf_list_new();
    2813         344 :                 gf_list_add(traf->child_boxes, traf->sample_encryption);
    2814             :         }
    2815       20396 :         senc = (GF_SampleEncryptionBox *) traf->sample_encryption;
    2816             : 
    2817       20396 :         if (!sai_b_size && !sai_b) {
    2818        2737 :                 gf_isom_cenc_set_saiz_saio(senc, NULL, traf, 0, use_saio_32bit, use_multikey);
    2819        2737 :                 return GF_OK;
    2820             :         }
    2821             : 
    2822       17659 :         GF_SAFEALLOC(sai, GF_CENCSampleAuxInfo);
    2823       17659 :         if (!sai) return GF_OUT_OF_MEM;
    2824             : 
    2825       17659 :         if (sai_b && sai_b_size) {
    2826       17659 :                 sai->cenc_data_size = sai_b_size;
    2827       17659 :                 sai->cenc_data = gf_malloc(sizeof(u8) * sai_b_size);
    2828       17659 :                 if (!sai->cenc_data) {
    2829           0 :                         gf_free(sai);
    2830           0 :                         return GF_OUT_OF_MEM;
    2831             :                 }
    2832             :                 memcpy(sai->cenc_data, sai_b, sai_b_size);
    2833             :         } else {
    2834           0 :                 sai->isNotProtected = 1;
    2835             :         }
    2836             : 
    2837       17659 :         gf_list_add(senc->samp_aux_info, sai);
    2838       17659 :         if (use_subsamples)
    2839       12151 :                 senc->flags = 0x00000002;
    2840       17659 :         if (use_multikey)
    2841           0 :                 senc->version = 1;
    2842             : 
    2843       17659 :         gf_isom_cenc_set_saiz_saio(senc, NULL, traf, sai->cenc_data_size, use_saio_32bit, use_multikey);
    2844       17659 :         return GF_OK;
    2845             : }
    2846             : 
    2847             : GF_EXPORT
    2848          16 : GF_Err gf_isom_fragment_append_data(GF_ISOFile *movie, GF_ISOTrackID TrackID, u8 *data, u32 data_size, u8 PaddingBits)
    2849             : {
    2850             :         u32 count;
    2851             :         u8 rap;
    2852             :         u16 degp;
    2853             :         GF_TrunEntry *ent;
    2854             :         GF_TrackFragmentBox *traf;
    2855             :         GF_TrackFragmentRunBox *trun;
    2856          16 :         if (!movie->moof || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) return GF_BAD_PARAM;
    2857             : 
    2858          16 :         traf = gf_isom_get_traf(movie, TrackID);
    2859          16 :         if (!traf || !traf->tfhd->sample_desc_index) return GF_BAD_PARAM;
    2860             : 
    2861             :         //add TRUN entry
    2862          16 :         count = gf_list_count(traf->TrackRuns);
    2863          16 :         if (!count) return GF_BAD_PARAM;
    2864             : 
    2865          16 :         trun = (GF_TrackFragmentRunBox *)gf_list_get(traf->TrackRuns, count-1);
    2866          16 :         if (!trun->nb_samples) return GF_BAD_PARAM;
    2867          16 :         ent = &trun->samples[trun->nb_samples-1];
    2868          16 :         ent->size += data_size;
    2869             : 
    2870          16 :         rap = GF_ISOM_GET_FRAG_SYNC(ent->flags);
    2871          16 :         degp = GF_ISOM_GET_FRAG_DEG(ent->flags);
    2872          16 :         ent->flags = GF_ISOM_FORMAT_FRAG_FLAGS(PaddingBits, rap, degp);
    2873             : 
    2874             :         //finally write the data
    2875          16 :         if (!traf->DataCache) {
    2876          16 :                 gf_bs_write_data(movie->editFileMap->bs, data, data_size);
    2877           0 :         } else if (trun->cache) {
    2878           0 :                 gf_bs_write_data(trun->cache, data, data_size);
    2879             :         } else {
    2880             :                 return GF_BAD_PARAM;
    2881             :         }
    2882             :         return GF_OK;
    2883             : }
    2884             : 
    2885        1523 : GF_Err gf_isom_fragment_add_subsample(GF_ISOFile *movie, GF_ISOTrackID TrackID, u32 flags, u32 subSampleSize, u8 priority, u32 reserved, Bool discardable)
    2886             : {
    2887             :         u32 i, count, last_sample;
    2888             :         GF_TrackFragmentBox *traf;
    2889             :         GF_SubSampleInformationBox *subs = NULL;
    2890        1523 :         if (!movie->moof || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) return GF_BAD_PARAM;
    2891             : 
    2892        1523 :         traf = gf_isom_get_traf(movie, TrackID);
    2893        1523 :         if (!traf || !traf->tfhd->sample_desc_index) return GF_BAD_PARAM;
    2894             : 
    2895             :         /*compute last sample number in traf*/
    2896             :         last_sample = 0;
    2897        1523 :         count = gf_list_count(traf->TrackRuns);
    2898        3046 :         for (i=0; i<count; i++) {
    2899        1523 :                 GF_TrackFragmentRunBox *trun = (GF_TrackFragmentRunBox*)gf_list_get(traf->TrackRuns, i);
    2900        1523 :                 last_sample += trun->sample_count;
    2901             :         }
    2902             : 
    2903        1523 :         if (!traf->sub_samples) {
    2904          15 :                 traf->sub_samples = gf_list_new();
    2905             :         }
    2906        1523 :         count = gf_list_count(traf->sub_samples);
    2907        1523 :         for (i=0; i<count;i++) {
    2908        1508 :                 subs = gf_list_get(traf->sub_samples, i);
    2909        1508 :                 if (subs->flags==flags) break;
    2910             :                 subs=NULL;
    2911             :         }
    2912        1523 :         if (!subs) {
    2913          15 :                 subs = (GF_SubSampleInformationBox *) gf_isom_box_new_parent(&traf->child_boxes, GF_ISOM_BOX_TYPE_SUBS);
    2914          15 :                 if (!subs) return GF_OUT_OF_MEM;
    2915          15 :                 subs->version = (subSampleSize>0xFFFF) ? 1 : 0;
    2916          15 :                 subs->flags = flags;
    2917          15 :                 gf_list_add(traf->sub_samples, subs);
    2918             :         }
    2919        1523 :         return gf_isom_add_subsample_info(subs, last_sample, subSampleSize, priority, reserved, discardable);
    2920             : }
    2921             : 
    2922             : #if 0 //unused
    2923             : static GF_Err gf_isom_copy_sample_group_entry_to_traf(GF_TrackFragmentBox *traf, GF_SampleTableBox *stbl, u32 grouping_type, u32 grouping_type_parameter, u32 sampleGroupDescriptionIndex, Bool sgpd_in_traf)
    2924             : {
    2925             :         if (sgpd_in_traf) {
    2926             :                 void *entry = NULL;
    2927             :                 u32 i, count;
    2928             :                 GF_SampleGroupDescriptionBox *sgdesc = NULL;
    2929             :                 GF_BitStream *bs;
    2930             : 
    2931             :                 count = gf_list_count(stbl->sampleGroupsDescription);
    2932             :                 for (i = 0; i < count; i++) {
    2933             :                         sgdesc = (GF_SampleGroupDescriptionBox *)gf_list_get(stbl->sampleGroupsDescription, i);
    2934             :                         if (sgdesc->grouping_type == grouping_type)
    2935             :                                 break;
    2936             :                         sgdesc = NULL;
    2937             :                 }
    2938             :                 if (!sgdesc)
    2939             :                         return GF_BAD_PARAM;
    2940             : 
    2941             :                 entry = gf_list_get(sgdesc->group_descriptions, sampleGroupDescriptionIndex-1);
    2942             :                 if (!entry)
    2943             :                         return GF_BAD_PARAM;
    2944             : 
    2945             :                 switch (grouping_type) {
    2946             :                 case GF_ISOM_SAMPLE_GROUP_RAP:
    2947             :                 {
    2948             :                         char udta[2];
    2949             :                         bs = gf_bs_new(udta, 2*sizeof(char), GF_BITSTREAM_WRITE);
    2950             :                         gf_bs_write_u8(bs, ((GF_VisualRandomAccessEntry *)entry)->num_leading_samples_known);
    2951             :                         gf_bs_write_u8(bs, ((GF_VisualRandomAccessEntry *)entry)->num_leading_samples);
    2952             :                         gf_bs_del(bs);
    2953             :                         return gf_isom_set_sample_group_info_ex(NULL, traf, 0, grouping_type, 0, udta, sg_rap_create_entry, sg_rap_compare_entry);
    2954             :                 }
    2955             :                 case GF_ISOM_SAMPLE_GROUP_SYNC:
    2956             :                 {
    2957             :                         char udta[1];
    2958             :                         bs = gf_bs_new(udta, 1*sizeof(char), GF_BITSTREAM_WRITE);
    2959             :                         gf_bs_write_int(bs, 0, 2);
    2960             :                         gf_bs_write_int(bs, ((GF_SYNCEntry *)entry)->NALU_type, 6);
    2961             :                         gf_bs_del(bs);
    2962             :                         return gf_isom_set_sample_group_info_ex(NULL, traf, 0, grouping_type, 0, udta, sg_rap_create_entry, sg_rap_compare_entry);
    2963             :                 }
    2964             :                 case GF_ISOM_SAMPLE_GROUP_ROLL:
    2965             :                 {
    2966             :                         char udta[2];
    2967             :                         bs = gf_bs_new(udta, 2*sizeof(char), GF_BITSTREAM_WRITE);
    2968             :                         gf_bs_write_u16(bs, ((GF_RollRecoveryEntry *)entry)->roll_distance);
    2969             :                         gf_bs_del(bs);
    2970             :                         return gf_isom_set_sample_group_info_ex(NULL, traf, 0, grouping_type, 0, udta, sg_roll_create_entry, sg_roll_compare_entry);
    2971             :                 }
    2972             :                 case GF_ISOM_SAMPLE_GROUP_SEIG:
    2973             :                 {
    2974             :                         return gf_isom_set_sample_group_info_ex(NULL, traf, 0, grouping_type, 0, entry, sg_encryption_create_entry, sg_encryption_compare_entry);
    2975             :                 }
    2976             :                 default:
    2977             :                         return GF_BAD_PARAM;
    2978             :                 }
    2979             :         }
    2980             : 
    2981             :         return gf_isom_add_sample_group_entry(traf->sampleGroups, 0, grouping_type, grouping_type_parameter, sampleGroupDescriptionIndex, NULL);
    2982             : }
    2983             : /*copy over the subsample and sampleToGroup information of the given sample from the source track/file to the last sample added to the current track fragment of the destination file*/
    2984             : GF_Err gf_isom_fragment_copy_subsample(GF_ISOFile *dest, GF_ISOTrackID TrackID, GF_ISOFile *orig, u32 track, u32 sampleNumber, Bool sgpd_in_traf)
    2985             : {
    2986             :         u32 i, count, last_sample, idx, subs_flags;
    2987             :         GF_SubSampleInfoEntry *sub_sample;
    2988             :         GF_Err e;
    2989             :         GF_TrackBox *trak;
    2990             :         GF_TrackFragmentBox *traf;
    2991             :         GF_TrunEntry *ent;
    2992             :         GF_TrackFragmentRunBox *trun;
    2993             :         if (!dest->moof || !(dest->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) return GF_BAD_PARAM;
    2994             : 
    2995             :         traf = gf_isom_get_traf(dest, TrackID);
    2996             :         if (!traf || !traf->tfhd->sample_desc_index) return GF_BAD_PARAM;
    2997             : 
    2998             :         trak = gf_isom_get_track_from_file(orig, track);
    2999             :         if (!trak) return GF_BAD_PARAM;
    3000             : 
    3001             :         /*modify depends flags*/
    3002             :         if (trak->Media->information->sampleTable->SampleDep) {
    3003             :                 u32 isLeading, dependsOn, dependedOn, redundant;
    3004             : 
    3005             :                 isLeading = dependsOn = dependedOn = redundant = 0;
    3006             :                 count = gf_list_count(traf->TrackRuns);
    3007             :                 if (!count) return GF_BAD_PARAM;
    3008             :                 trun = (GF_TrackFragmentRunBox *)gf_list_get(traf->TrackRuns, count-1);
    3009             :                 count = gf_list_count(trun->entries);
    3010             :                 if (!count) return GF_BAD_PARAM;
    3011             : 
    3012             :                 ent = (GF_TrunEntry *)gf_list_get(trun->entries, count-1);
    3013             :                 e = stbl_GetSampleDepType(trak->Media->information->sampleTable->SampleDep, sampleNumber, &isLeading, &dependsOn, &dependedOn, &redundant);
    3014             :                 if (e) return e;
    3015             : 
    3016             :                 GF_ISOM_RESET_FRAG_DEPEND_FLAGS(ent->flags);
    3017             : 
    3018             :                 if (traf->use_sdtp) {
    3019             :                         u8 sflags=0;
    3020             :                         if (!traf->sdtp) {
    3021             :                                 traf->sdtp = (GF_SampleDependencyTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_SDTP);
    3022             :                                 if (!traf->sdtp) return GF_OUT_OF_MEM;
    3023             :                         }
    3024             :                         sflags |= isLeading << 6;
    3025             :                         sflags |= dependsOn << 4;
    3026             :                         sflags |= dependedOn << 2;
    3027             :                         sflags |= redundant;
    3028             : 
    3029             :                         traf->sdtp->sample_info = gf_realloc(traf->sdtp->sample_info, sizeof(u8)*(traf->sdtp->sampleCount+1));
    3030             :                         traf->sdtp->sample_info[traf->sdtp->sampleCount] = (u8) sflags;
    3031             :                         traf->sdtp->sampleCount++;
    3032             :                         traf->sdtp->sample_alloc = traf->sdtp->sampleCount+1;
    3033             : 
    3034             : 
    3035             :                         if (traf->use_sdtp==2) {
    3036             :                                 ent->flags |= GF_ISOM_GET_FRAG_DEPEND_FLAGS(isLeading, dependsOn, dependedOn, redundant);
    3037             :                         }
    3038             :                 } else {
    3039             :                         ent->flags |= GF_ISOM_GET_FRAG_DEPEND_FLAGS(isLeading, dependsOn, dependedOn, redundant);
    3040             :                 }
    3041             :         }
    3042             : 
    3043             :         /*copy subsample info if any*/
    3044             :         idx=1;
    3045             :         while (gf_isom_get_subsample_types(orig, track, idx, &subs_flags)) {
    3046             :                 GF_SubSampleInformationBox *subs_traf=NULL;
    3047             :                 idx++;
    3048             :                 if (! gf_isom_sample_get_subsample_entry(orig, track, sampleNumber, subs_flags, &sub_sample))
    3049             :                         continue;
    3050             : 
    3051             :                 if (!traf || !traf->tfhd->sample_desc_index) return GF_BAD_PARAM;
    3052             : 
    3053             :                 /*compute last sample number in traf*/
    3054             :                 last_sample = 0;
    3055             :                 count = gf_list_count(traf->TrackRuns);
    3056             :                 for (i=0; i<count; i++) {
    3057             :                         GF_TrackFragmentRunBox *trun = (GF_TrackFragmentRunBox*)gf_list_get(traf->TrackRuns, i);
    3058             :                         last_sample += trun->sample_count;
    3059             :                 }
    3060             : 
    3061             :                 /*create subsample if needed*/
    3062             :                 if (!traf->sub_samples) {
    3063             :                         traf->sub_samples = gf_list_new();
    3064             :                 }
    3065             :                 count = gf_list_count(traf->sub_samples);
    3066             :                 for (i=0; i<count; i++) {
    3067             :                         subs_traf = gf_list_get(traf->sub_samples, i);
    3068             :                         if (subs_traf->flags==subs_flags) break;
    3069             :                         subs_traf = NULL;
    3070             :                 }
    3071             :                 if (!subs_traf) {
    3072             :                         subs_traf = (GF_SubSampleInformationBox *) gf_isom_box_new_parent(&traf->child_boxes, GF_ISOM_BOX_TYPE_SUBS);
    3073             :                         if (!subs_traf) return GF_OUT_OF_MEM;
    3074             :                         subs_traf->version = 0;
    3075             :                         subs_traf->flags = subs_flags;
    3076             :                         gf_list_add(traf->sub_samples, subs_traf);
    3077             :                 }
    3078             : 
    3079             :                 count = gf_list_count(sub_sample->SubSamples);
    3080             :                 for (i=0; i<count; i++) {
    3081             :                         GF_SubSampleEntry *entry = (GF_SubSampleEntry*)gf_list_get(sub_sample->SubSamples, i);
    3082             :                         e = gf_isom_add_subsample_info(subs_traf, last_sample, entry->subsample_size, entry->subsample_priority, entry->reserved, entry->discardable);
    3083             :                         if (e) return e;
    3084             :                 }
    3085             :         }
    3086             :         /*copy sampleToGroup info if any*/
    3087             :         if (trak->Media->information->sampleTable->sampleGroups) {
    3088             :                 count = gf_list_count(trak->Media->information->sampleTable->sampleGroups);
    3089             :                 for (i=0; i<count; i++) {
    3090             :                         GF_SampleGroupBox *sg;
    3091             :                         Bool found = GF_FALSE;
    3092             :                         u32 j;
    3093             :                         u32 first_sample_in_entry, last_sample_in_entry;
    3094             :                         first_sample_in_entry = 1;
    3095             : 
    3096             :                         sg = (GF_SampleGroupBox*)gf_list_get(trak->Media->information->sampleTable->sampleGroups, i);
    3097             :                         for (j=0; j<sg->entry_count; j++) {
    3098             :                                 last_sample_in_entry = first_sample_in_entry + sg->sample_entries[j].sample_count - 1;
    3099             :                                 if ((sampleNumber<first_sample_in_entry) || (sampleNumber>last_sample_in_entry)) {
    3100             :                                         first_sample_in_entry = last_sample_in_entry+1;
    3101             :                                         continue;
    3102             :                                 }
    3103             : 
    3104             :                                 if (!traf->sampleGroups)
    3105             :                                         traf->sampleGroups = gf_list_new();
    3106             : 
    3107             :                                 /*found our sample, add it to trak->sampleGroups*/
    3108             :                                 e = gf_isom_copy_sample_group_entry_to_traf(traf, trak->Media->information->sampleTable, sg->grouping_type, sg->grouping_type_parameter,  sg->sample_entries[j].group_description_index, sgpd_in_traf);
    3109             :                                 if (e) return e;
    3110             : 
    3111             :                                 found = GF_TRUE;
    3112             :                                 break;
    3113             :                         }
    3114             :                         //unmapped sample
    3115             :                         if (!found) {
    3116             :                                 if (!traf->sampleGroups)
    3117             :                                         traf->sampleGroups = gf_list_new();
    3118             : 
    3119             :                                 e = gf_isom_copy_sample_group_entry_to_traf(traf, trak->Media->information->sampleTable, sg->grouping_type, sg->grouping_type_parameter,  0, sgpd_in_traf);
    3120             :                                 if (e) return e;
    3121             :                         }
    3122             :                 }
    3123             :         }
    3124             :         return GF_OK;
    3125             : }
    3126             : #endif
    3127             : 
    3128             : 
    3129        1575 : GF_Err gf_isom_fragment_set_sample_flags(GF_ISOFile *movie, GF_ISOTrackID trackID, u32 is_leading, u32 dependsOn, u32 dependedOn, u32 redundant)
    3130             : {
    3131             :         u32 count;
    3132             :         GF_TrackFragmentBox *traf;
    3133             :         GF_TrunEntry *ent;
    3134             :         GF_TrackFragmentRunBox *trun;
    3135        1575 :         if (!movie || !movie->moof || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) return GF_BAD_PARAM;
    3136             : 
    3137        1575 :         traf = gf_isom_get_traf(movie, trackID);
    3138        1575 :         if (!traf->tfhd->sample_desc_index) return GF_BAD_PARAM;
    3139             : 
    3140        1575 :         count = gf_list_count(traf->TrackRuns);
    3141        1575 :         if (!count) return GF_BAD_PARAM;
    3142        1575 :         trun = (GF_TrackFragmentRunBox *)gf_list_get(traf->TrackRuns, count-1);
    3143        1575 :         if (!trun->nb_samples) return GF_BAD_PARAM;
    3144        1575 :         ent = &trun->samples[trun->nb_samples-1];
    3145             : 
    3146        1575 :         GF_ISOM_RESET_FRAG_DEPEND_FLAGS(ent->flags);
    3147             : 
    3148        1575 :         if (traf->use_sdtp) {
    3149             :                 u8 sflags=0;
    3150           0 :                 if (!traf->sdtp) {
    3151           0 :                         traf->sdtp = (GF_SampleDependencyTypeBox *) gf_isom_box_new_parent(&traf->child_boxes, GF_ISOM_BOX_TYPE_SDTP);
    3152           0 :                         if (!traf->sdtp) return GF_OUT_OF_MEM;
    3153             :                 }
    3154           0 :                 sflags |= is_leading << 6;
    3155           0 :                 sflags |= dependsOn << 4;
    3156           0 :                 sflags |= dependedOn << 2;
    3157           0 :                 sflags |= redundant;
    3158             : 
    3159           0 :                 traf->sdtp->sample_info = gf_realloc(traf->sdtp->sample_info, sizeof(u8)*(traf->sdtp->sampleCount+1));
    3160           0 :                 traf->sdtp->sample_info[traf->sdtp->sampleCount] = (u8) sflags;
    3161           0 :                 traf->sdtp->sampleCount++;
    3162           0 :                 traf->sdtp->sample_alloc = traf->sdtp->sampleCount;
    3163           0 :                 if (traf->use_sdtp==2) {
    3164           0 :                         ent->flags |= GF_ISOM_GET_FRAG_DEPEND_FLAGS(is_leading, dependsOn, dependedOn, redundant);
    3165             :                 }
    3166             :         } else {
    3167        1575 :                 ent->flags |= GF_ISOM_GET_FRAG_DEPEND_FLAGS(is_leading, dependsOn, dependedOn, redundant);
    3168             :         }
    3169             :         return GF_OK;
    3170             : }
    3171             : 
    3172             : 
    3173             : #endif  /*GPAC_DISABLE_ISOM_WRITE*/
    3174             : 
    3175             : GF_EXPORT
    3176         219 : Bool gf_isom_is_track_fragmented(GF_ISOFile *movie, GF_ISOTrackID TrackID)
    3177             : {
    3178         219 :         if (!movie || !movie->moov || !movie->moov->mvex) return GF_FALSE;
    3179           3 :         return (GetTrex(movie->moov, TrackID) != NULL) ? GF_TRUE : GF_FALSE;
    3180             : }
    3181             : 
    3182             : GF_EXPORT
    3183        1196 : Bool gf_isom_is_fragmented(GF_ISOFile *movie)
    3184             : {
    3185        1196 :         if (!movie || !movie->moov) return GF_FALSE;
    3186             :         /* By default if the Moov has an mvex, the file is fragmented */
    3187        1166 :         if (movie->moov->mvex) return GF_TRUE;
    3188         970 :         return GF_FALSE;
    3189             : }
    3190             : 
    3191             : GF_EXPORT
    3192        3438 : GF_Err gf_isom_set_traf_base_media_decode_time(GF_ISOFile *movie, GF_ISOTrackID TrackID, u64 decode_time)
    3193             : {
    3194             :         GF_TrackFragmentBox *traf;
    3195        3438 :         if (!movie || !movie->moof || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) return GF_BAD_PARAM;
    3196             : 
    3197        3438 :         traf = gf_isom_get_traf(movie, TrackID);
    3198        3438 :         if (!traf) return GF_BAD_PARAM;
    3199             : 
    3200        3438 :         if (!traf->tfdt) {
    3201        3381 :                 traf->tfdt = (GF_TFBaseMediaDecodeTimeBox *) gf_isom_box_new_parent(&traf->child_boxes, GF_ISOM_BOX_TYPE_TFDT);
    3202        3381 :                 if (!traf->tfdt) return GF_OUT_OF_MEM;
    3203             :         }
    3204        3438 :         traf->tfdt->baseMediaDecodeTime = decode_time;
    3205        3438 :         if (traf->large_tfdt)
    3206           0 :                 traf->tfdt->version = 1;
    3207             :         return GF_OK;
    3208             : }
    3209             : 
    3210           1 : GF_Err gf_isom_enable_mfra(GF_ISOFile *file)
    3211             : {
    3212           1 :         if (!file) return GF_BAD_PARAM;
    3213           1 :         file->mfra = (GF_MovieFragmentRandomAccessBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_MFRA);
    3214           1 :         if (!file->mfra) return GF_OUT_OF_MEM;
    3215           1 :         return GF_OK;
    3216             : }
    3217             : 
    3218             : #endif /*GPAC_DISABLE_ISOM_FRAGMENTS)*/
    3219             : 
    3220             : 
    3221             : GF_EXPORT
    3222        3571 : void gf_isom_set_next_moof_number(GF_ISOFile *movie, u32 value)
    3223             : {
    3224             : #ifndef GPAC_DISABLE_ISOM_FRAGMENTS
    3225        3571 :         if (movie) movie->NextMoofNumber = value;
    3226             : #endif
    3227        3571 : }
    3228             : 
    3229             : GF_EXPORT
    3230           1 : u32 gf_isom_get_next_moof_number(GF_ISOFile *movie)
    3231             : {
    3232             : #ifndef GPAC_DISABLE_ISOM_FRAGMENTS
    3233           1 :         if (movie) return movie->NextMoofNumber;
    3234             : #endif
    3235             :         return 0;
    3236             : }
    3237             : 
    3238             : #endif /*GPAC_DISABLE_ISOM*/

Generated by: LCOV version 1.13