root/src/isomedia/movie_fragments.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. GetTrex
  2. GetTrep
  3. GetTraf
  4. gf_isom_set_movie_duration
  5. gf_isom_finalize_for_fragment
  6. gf_isom_change_track_fragment_defaults
  7. gf_isom_setup_track_fragment
  8. GetNumUsedValues
  9. ComputeFragmentDefaults
  10. gf_isom_set_fragment_option
  11. update_trun_offsets
  12. UpdateRuns
  13. moof_get_sap_info
  14. moof_get_duration
  15. moof_get_earliest_cts
  16. StoreFragment
  17. sidx_rewrite
  18. gf_isom_allocate_sidx
  19. gf_isom_write_styp
  20. gf_isom_flush_fragments
  21. get_presentation_time
  22. gf_isom_close_segment
  23. gf_isom_close_fragments
  24. gf_isom_start_segment
  25. gf_isom_set_fragment_reference_time
  26. gf_isom_set_traf_mss_timeext
  27. gf_isom_start_fragment
  28. GetRunSize
  29. gf_isom_fragment_add_sample
  30. gf_isom_fragment_add_sai
  31. gf_isom_fragment_append_data
  32. gf_isom_fragment_add_subsample
  33. gf_isom_fragment_copy_subsample
  34. gf_isom_is_track_fragmented
  35. gf_isom_is_fragmented
  36. gf_isom_set_traf_base_media_decode_time
  37. gf_isom_finalize_for_fragment
  38. gf_isom_setup_track_fragment
  39. gf_isom_set_fragment_option
  40. gf_isom_start_fragment
  41. gf_isom_fragment_add_sample
  42. gf_isom_is_track_fragmented
  43. gf_isom_is_fragmented
  44. gf_isom_fragment_add_subsample
  45. gf_isom_fragment_copy_subsample
  46. gf_isom_set_traf_base_media_decode_time
  47. gf_isom_set_traf_mss_timeext
  48. gf_isom_set_next_moof_number
  49. gf_isom_get_next_moof_number

/*
 *                      GPAC - Multimedia Framework C SDK
 *
 *                      Authors: Jean Le Feuvre
 *                      Copyright (c) Telecom ParisTech 2000-2012
 *                                      All rights reserved
 *
 *  This file is part of GPAC / ISO Media File Format sub-project
 *
 *  GPAC is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  GPAC is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include <gpac/internal/isomedia_dev.h>

#ifndef GPAC_DISABLE_ISOM

#ifndef GPAC_DISABLE_ISOM_FRAGMENTS

GF_TrackExtendsBox *GetTrex(GF_MovieBox *moov, u32 TrackID)
{
        u32 i;
        GF_TrackExtendsBox *trex;
        i=0;
        while ((trex = (GF_TrackExtendsBox *)gf_list_enum(moov->mvex->TrackExList, &i))) {
                if (trex->trackID == TrackID) return trex;
        }
        return NULL;
}

GF_TrackExtensionPropertiesBox *GetTrep(GF_MovieBox *moov, u32 TrackID)
{
        u32 i;
        GF_TrackExtensionPropertiesBox *trep;
        i=0;
        while ((trep = (GF_TrackExtensionPropertiesBox*) gf_list_enum(moov->mvex->TrackExPropList, &i))) {
                if (trep->trackID == TrackID) return trep;
        }
        return NULL;
}

GF_TrackFragmentBox *GetTraf(GF_ISOFile *mov, u32 TrackID)
{
        u32 i;
        GF_TrackFragmentBox *traf;
        if (!mov->moof) return NULL;

        //reverse browse the TRAFs, as there may be more than one per track ...
        for (i=gf_list_count(mov->moof->TrackList); i>0; i--) {
                traf = (GF_TrackFragmentBox *)gf_list_get(mov->moof->TrackList, i-1);
                if (traf->tfhd->trackID == TrackID) return traf;
        }
        return NULL;
}


#ifndef GPAC_DISABLE_ISOM_WRITE
GF_Err gf_isom_set_movie_duration(GF_ISOFile *movie, u64 duration)
{
        if (!movie->moov->mvex) return GF_BAD_PARAM;
        if (!movie->moov->mvex->mehd) {
                movie->moov->mvex->mehd = (GF_MovieExtendsHeaderBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MEHD);
        }
        movie->moov->mvex->mehd->fragment_duration = duration;
        movie->moov->mvhd->duration = 0;
        return GF_OK;
}


GF_EXPORT
GF_Err gf_isom_finalize_for_fragment(GF_ISOFile *movie, u32 media_segment_type)
{
        GF_Err e;
        u32 i;
        Bool store_file = GF_TRUE;
        GF_TrackExtendsBox *trex;
        if (!movie || !movie->moov) return GF_BAD_PARAM;

        if (movie->openMode==GF_ISOM_OPEN_CAT_FRAGMENTS) {
                /*from now on we are in write mode*/
                movie->openMode = GF_ISOM_OPEN_WRITE;
                store_file = GF_FALSE;
                movie->append_segment = GF_TRUE;
        } else {
                movie->NextMoofNumber = 1;
        }

        //this is only allowed in write mode
        if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE;

        if (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) return GF_OK;
        movie->FragmentsFlags = 0;

        if (store_file) {
                /*"dash" brand: this is a DASH Initialization Segment*/
                gf_isom_modify_alternate_brand(movie, GF_4CC('d','a','s','h'), 1);

                if (!movie->moov->mvex->mehd || !movie->moov->mvex->mehd->fragment_duration) {
                        //update durations
                        gf_isom_get_duration(movie);
                }

                i=0;
                while ((trex = (GF_TrackExtendsBox *)gf_list_enum(movie->moov->mvex->TrackExList, &i))) {
                        GF_TrackExtensionPropertiesBox *trep;
                        if (trex->type != GF_ISOM_BOX_TYPE_TREX) continue;
                        trep = GetTrep(movie->moov, trex->trackID);

                        if (!trep) {
                                trep = (GF_TrackExtensionPropertiesBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_TREP);
                                trep->trackID = trex->trackID;
                                gf_list_add(movie->moov->mvex->TrackExPropList, trep);
                        }

                        if (trex->track->Media->information->sampleTable->CompositionToDecode) {

                                if (!trex->track->Media->information->sampleTable->SampleSize || ! trex->track->Media->information->sampleTable->SampleSize->sampleCount) {
                                        gf_list_add(trep->other_boxes, trex->track->Media->information->sampleTable->CompositionToDecode);
                                        trex->track->Media->information->sampleTable->CompositionToDecode = NULL;
                                } else {
                                        GF_CompositionToDecodeBox *cslg;

                                        //clone it!
                                        GF_SAFEALLOC(cslg, GF_CompositionToDecodeBox);
                                        if (!cslg) return GF_OUT_OF_MEM;
                                        memcpy(cslg, trex->track->Media->information->sampleTable->CompositionToDecode, sizeof(GF_CompositionToDecodeBox) );
                                        cslg->other_boxes = gf_list_new();
                                        gf_list_add(trep->other_boxes, trex->track->Media->information->sampleTable->CompositionToDecode);
                                }
                        }

                        if (movie->moov->mvex->mehd && movie->moov->mvex->mehd->fragment_duration) {
                                trex->track->Header->duration = 0;
                                Media_SetDuration(trex->track);
                        }
                }

                //write movie
                e = WriteToFile(movie);
                if (e) return e;
        }

        //make sure we do have all we need. If not this is not an error, just consider
        //the file closed
        if (!movie->moov->mvex || !gf_list_count(movie->moov->mvex->TrackExList)) return GF_OK;

        i=0;
        while ((trex = (GF_TrackExtendsBox *)gf_list_enum(movie->moov->mvex->TrackExList, &i))) {
                if (!trex->trackID || !gf_isom_get_track_from_id(movie->moov, trex->trackID)) return GF_IO_ERR;
                //we could also check all our data refs are local but we'll do that at run time
                //in order to allow a mix of both (remote refs in MOOV and local in MVEX)

                //one thing that MUST be done is OD cross-dependancies. The movie fragment spec
                //is broken here, since it cannot allow dynamic insertion of new ESD and their
                //dependancies
        }

        //ok we are fine - note the data map is created at the beginning
        if (i) movie->FragmentsFlags |= GF_ISOM_FRAG_WRITE_READY;

        if (media_segment_type) {
                movie->use_segments = GF_TRUE;
                movie->moof_list = gf_list_new();
        }

        /*set brands for segment*/

        /*"msdh": it's a media segment */
        gf_isom_set_brand_info(movie, GF_4CC('m','s','d','h'), 0);
        /*remove all brands     */
        gf_isom_reset_alt_brands(movie);
        /*
                msdh: it's a media segment
                sims: it's a media segment with an SSIX
                msix: it's a media segment with an index
                lmsg: it's the last media segment
        */

        return GF_OK;
}

GF_Err gf_isom_change_track_fragment_defaults(GF_ISOFile *movie, u32 TrackID,
        u32 DefaultSampleDescriptionIndex,
        u32 DefaultSampleDuration,
        u32 DefaultSampleSize,
        u8 DefaultSampleIsSync,
        u8 DefaultSamplePadding,
        u16 DefaultDegradationPriority)
{
        GF_MovieExtendsBox *mvex;
        GF_TrackExtendsBox *trex;
        GF_TrackBox *trak;

        if (!movie || !movie->moov) return GF_BAD_PARAM;
        //this is only allowed in write mode
        if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE;

        trak = gf_isom_get_track_from_id(movie->moov, TrackID);
        if (!trak) return GF_BAD_PARAM;

        mvex = movie->moov->mvex;
        if (!mvex) return GF_BAD_PARAM;

        trex = GetTrex(movie->moov, TrackID);
        if (!trex)  return GF_BAD_PARAM;

        trex->def_sample_desc_index = DefaultSampleDescriptionIndex;
        trex->def_sample_duration = DefaultSampleDuration;
        trex->def_sample_size = DefaultSampleSize;
        trex->def_sample_flags = GF_ISOM_FORMAT_FRAG_FLAGS(DefaultSamplePadding, DefaultSampleIsSync, DefaultDegradationPriority);
        //if sample is sync by default, set sample_depends_on flags to 2 (does not depend on other samples)
        if (DefaultSampleIsSync) {
                trex->def_sample_flags |= (2<<24);
        }

        return GF_OK;
}

GF_EXPORT
GF_Err gf_isom_setup_track_fragment(GF_ISOFile *movie, u32 TrackID,
                                    u32 DefaultSampleDescriptionIndex,
                                    u32 DefaultSampleDuration,
                                    u32 DefaultSampleSize,
                                    u8 DefaultSampleIsSync,
                                    u8 DefaultSamplePadding,
                                    u16 DefaultDegradationPriority)
{
        GF_MovieExtendsBox *mvex;
        GF_TrackExtendsBox *trex;
        GF_TrackBox *trak;

        if (!movie || !movie->moov) return GF_BAD_PARAM;
        //this is only allowed in write mode
        if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE;
        //and only at setup
        if (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) return GF_BAD_PARAM;


        trak = gf_isom_get_track_from_id(movie->moov, TrackID);
        if (!trak) return GF_BAD_PARAM;

        //create MVEX if needed
        if (!movie->moov->mvex) {
                mvex = (GF_MovieExtendsBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MVEX);
                moov_AddBox((GF_Box*)movie->moov, (GF_Box *) mvex);
        } else {
                mvex = movie->moov->mvex;
        }
        if (!mvex->mehd) {
                mvex->mehd = (GF_MovieExtendsHeaderBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MEHD);
        }

        trex = GetTrex(movie->moov, TrackID);
        if (!trex) {
                trex = (GF_TrackExtendsBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_TREX);
                trex->trackID = TrackID;
                mvex_AddBox((GF_Box*)mvex, (GF_Box *) trex);
        }
        trex->track = trak;
        return gf_isom_change_track_fragment_defaults(movie, TrackID, DefaultSampleDescriptionIndex, DefaultSampleDuration, DefaultSampleSize, DefaultSampleIsSync, DefaultSamplePadding, DefaultDegradationPriority);
}


u32 GetNumUsedValues(GF_TrackFragmentBox *traf, u32 value, u32 index)
{
        u32 i, j, NumValue = 0;
        GF_TrackFragmentRunBox *trun;
        GF_TrunEntry *ent;

        i=0;
        while ((trun = (GF_TrackFragmentRunBox *)gf_list_enum(traf->TrackRuns, &i))) {
                j=0;
                while ((ent = (GF_TrunEntry *)gf_list_enum(trun->entries, &j))) {
                        switch (index) {
                        case 1:
                                if (value == ent->Duration) NumValue ++;
                                break;
                        case 2:
                                if (value == ent->size) NumValue ++;
                                break;
                        case 3:
                                if (value == ent->flags) NumValue ++;
                                break;
                        }
                }
        }
        return NumValue;
}


void ComputeFragmentDefaults(GF_TrackFragmentBox *traf)
{
        u32 i, j, MaxNum, DefValue, ret;
        GF_TrackFragmentRunBox *trun;
        GF_TrunEntry *ent;

        //Duration default
        MaxNum = DefValue = 0;
        i=0;
        while ((trun = (GF_TrackFragmentRunBox *)gf_list_enum(traf->TrackRuns, &i))) {
                j=0;
                while ((ent = (GF_TrunEntry *)gf_list_enum(trun->entries, &j))) {
                        ret = GetNumUsedValues(traf, ent->Duration, 1);
                        if (ret>MaxNum) {
                                //at least 2 duration, specify for all
                                if (MaxNum) {
                                        DefValue = 0;
                                        goto escape_duration;
                                }
                                MaxNum = ret;
                                DefValue = ent->Duration;
                        }
                }
        }
escape_duration:
        //store if #
        if (DefValue && (DefValue != traf->trex->def_sample_duration)) {
                traf->tfhd->def_sample_duration = DefValue;
        }

        //Size default
        MaxNum = DefValue = 0;
        i=0;
        while ((trun = (GF_TrackFragmentRunBox *)gf_list_enum(traf->TrackRuns, &i))) {
                j=0;
                while ((ent = (GF_TrunEntry*)gf_list_enum(trun->entries, &j))) {
                        ret = GetNumUsedValues(traf, ent->size, 2);
                        if (ret>MaxNum || (ret==1)) {
                                //at least 2 sizes so we must specify all sizes
                                if (MaxNum) {
                                        DefValue = 0;
                                        goto escape_size;
                                }
                                MaxNum = ret;
                                DefValue = ent->size;
                        }
                }
        }

escape_size:
        //store if #
        if (DefValue && (DefValue != traf->trex->def_sample_size)) {
                traf->tfhd->def_sample_size = DefValue;
        }

        //Flags default
        MaxNum = DefValue = 0;
        i=0;
        while ((trun = (GF_TrackFragmentRunBox *)gf_list_enum(traf->TrackRuns, &i))) {
                j=0;
                while ((ent = (GF_TrunEntry*)gf_list_enum(trun->entries, &j))) {
                        ret = GetNumUsedValues(traf, ent->flags, 3);
                        if (ret>MaxNum) {
                                MaxNum = ret;
                                DefValue = ent->flags;
                        }
                }
        }
        //store if #
        if (DefValue && (DefValue != traf->trex->def_sample_flags)) {
                traf->tfhd->def_sample_flags = DefValue;
        }
}


GF_Err gf_isom_set_fragment_option(GF_ISOFile *movie, u32 TrackID, u32 Code, u32 Param)
{
        GF_TrackFragmentBox *traf;
        if (!movie || !movie->moov) return GF_BAD_PARAM;
        //this is only allowed in write mode
        if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE;

        traf = GetTraf(movie, TrackID);
        if (!traf) return GF_BAD_PARAM;
        switch (Code) {
        case GF_ISOM_TRAF_EMPTY:
                traf->tfhd->EmptyDuration = Param;
                break;
        case GF_ISOM_TRAF_RANDOM_ACCESS:
                traf->tfhd->IFrameSwitching = Param;
                break;
        case GF_ISOM_TRAF_DATA_CACHE:
                //don't cache only one sample ...
                traf->DataCache = Param > 1 ? Param : 0;
                break;
        }
        return GF_OK;
}

//#define USE_BASE_DATA_OFFSET

void update_trun_offsets(GF_ISOFile *movie, s32 offset)
{
#ifndef USE_BASE_DATA_OFFSET
        GF_TrackFragmentRunBox *trun;
        u32 i, j;
        GF_TrackFragmentBox *traf;
        i=0;
        while ((traf = (GF_TrackFragmentBox*)gf_list_enum(movie->moof->TrackList, &i))) {
                /*remove base data*/
                traf->tfhd->base_data_offset = 0;
                j=0;
                while ((trun = (GF_TrackFragmentRunBox*)gf_list_enum(traf->TrackRuns, &j))) {
                        if (j==1) {
                                trun->data_offset += offset;
                        } else {
                                trun->data_offset = 0;
                        }
                }
        }
#endif
}

u32 UpdateRuns(GF_ISOFile *movie, GF_TrackFragmentBox *traf)
{
        u32 sampleCount, i, j, RunSize, RunDur, RunFlags, NeedFlags, UseCTS, count;
        /* enum:
           0 - use values per sample in the trun box
           1 - use default values from track fragment header
           2 - use default values from track extends header */
        u32 UseDefaultSize, UseDefaultDur, UseDefaultFlag;
        GF_TrackFragmentRunBox *trun;
        GF_TrunEntry *ent, *first_ent;

        sampleCount = 0;

#ifndef USE_BASE_DATA_OFFSET
        if (movie->use_segments) {
                traf->tfhd->flags = GF_ISOM_MOOF_BASE_OFFSET;
        } else
#endif
        {
                traf->tfhd->flags = GF_ISOM_TRAF_BASE_OFFSET;
        }

        //empty runs
        if (traf->tfhd->EmptyDuration) {
                while (gf_list_count(traf->TrackRuns)) {
                        trun = (GF_TrackFragmentRunBox *)gf_list_get(traf->TrackRuns, 0);
                        gf_list_rem(traf->TrackRuns, 0);
                        gf_isom_box_del((GF_Box *)trun);
                }
                traf->tfhd->flags = GF_ISOM_TRAF_DUR_EMPTY;
                if (traf->tfhd->EmptyDuration != traf->trex->def_sample_duration) {
                        traf->tfhd->def_sample_duration = traf->tfhd->EmptyDuration;
                        traf->tfhd->flags |= GF_ISOM_TRAF_SAMPLE_DUR;
                }
                return 0;
        }


        UseDefaultSize = 0;
        UseDefaultDur = 0;
        UseDefaultFlag = 0;

        i=0;
        while ((trun = (GF_TrackFragmentRunBox *)gf_list_enum(traf->TrackRuns, &i))) {
                RunSize = 0;
                RunDur = 0;
                RunFlags = 0;
                UseCTS = 0;
                NeedFlags = 0;

                first_ent = NULL;
                //process all samples in run
                count = gf_list_count(trun->entries);
                for (j=0; j<count; j++) {
                        ent = (GF_TrunEntry*)gf_list_get(trun->entries, j);
                        if (!j) {
                                first_ent = ent;
                                RunSize = ent->size;
                                RunDur = ent->Duration;
                        }
                        //we may have one entry only ...
                        if (j || (count==1)) {
                                //flags are only after first entry
                                if (j==1 || (count==1) ) RunFlags = ent->flags;

                                if (ent->size != RunSize) RunSize = 0;
                                if (ent->Duration != RunDur) RunDur = 0;
                                if (j && (RunFlags != ent->flags)) NeedFlags = 1;
                        }
                        if (ent->CTS_Offset) UseCTS = 1;
                }
                //empty list
                if (!first_ent) {
                        i--;
                        gf_list_rem(traf->TrackRuns, i);
                        continue;
                }
                trun->sample_count = gf_list_count(trun->entries);
                trun->flags = 0;

                //size checking
                //constant size, check if this is from current fragment default or global default
                if (RunSize && (traf->trex->def_sample_size == RunSize)) {
                        if (!UseDefaultSize) UseDefaultSize = 2;
                        else if (UseDefaultSize==1) RunSize = 0;
                } else if (RunSize && (traf->tfhd->def_sample_size == RunSize)) {
                        if (!UseDefaultSize) UseDefaultSize = 1;
                        else if (UseDefaultSize==2) RunSize = 0;
                }
                //we could check for single entry runs and set the default size in the tfhd but
                //that's no bit saving...
                else {
                        RunSize=0;
                }

                if (!RunSize) trun->flags |= GF_ISOM_TRUN_SIZE;

                //duration checking
                if (RunDur && (traf->trex->def_sample_duration == RunDur)) {
                        if (!UseDefaultDur) UseDefaultDur = 2;
                        else if (UseDefaultDur==1) RunDur = 0;
                } else if (RunDur && (traf->tfhd->def_sample_duration == RunDur)) {
                        if (!UseDefaultDur) UseDefaultDur = 1;
                        else if (UseDefaultDur==2) RunDur = 0;
                }
                if (!RunDur) trun->flags |= GF_ISOM_TRUN_DURATION;

                //flag checking
                if (!NeedFlags) {
                        // all samples flags are the same after the 2nd entry
                        if (RunFlags == traf->trex->def_sample_flags) {
                                /* this run can use trex flags */
                                if (!UseDefaultFlag) {
                                        /* if all previous runs used explicit flags per sample, we can still use trex flags for this run */
                                        UseDefaultFlag = 2;
                                } else if (UseDefaultFlag==1) {
                                        /* otherwise if one of the previous runs did use tfhd flags,
                                        we have no choice but to explicitly use flags per sample for this run */
                                        NeedFlags = GF_TRUE;
                                }
                        } else if (RunFlags == traf->tfhd->def_sample_flags) {
                                /* this run can use tfhd flags */
                                if (!UseDefaultFlag) {
                                        /* if all previous runs used explicit flags per sample, we can still use tfhd flags for this run */
                                        UseDefaultFlag = 1;
                                } else if(UseDefaultFlag==2) {
                                        /* otherwise if one of the previous runs did use trex flags,
                                        we have no choice but to explicitly use flags per sample for this run */
                                        NeedFlags = GF_TRUE;
                                }
                        } else {
                                /* the flags for the 2nd and following entries are different from trex and tfhd default values
                                   (possible case: 2 samples in trun, and first sample was used to set default flags) */
                                NeedFlags = GF_TRUE;
                        }
                }
                if (NeedFlags) {
                        //one flags entry per sample only
                        trun->flags |= GF_ISOM_TRUN_FLAGS;
                } else {
                        /* this run can use default flags for the 2nd and following entries,
                           we just need to check if the first entry flags need to be singled out*/
                        if (first_ent->flags != RunFlags) {
                                trun->flags |= GF_ISOM_TRUN_FIRST_FLAG;
                        }
                }

                //CTS flag
                if (UseCTS) trun->flags |= GF_ISOM_TRUN_CTS_OFFSET;

                //run data offset if the offset indicated is 0 (first sample in this MDAT) don't
                //indicate it
                if (trun->data_offset) trun->flags |= GF_ISOM_TRUN_DATA_OFFSET;

                sampleCount += trun->sample_count;
        }

        //after all runs in the traf are processed, update TRAF flags
        if (UseDefaultSize==1) traf->tfhd->flags |= GF_ISOM_TRAF_SAMPLE_SIZE;
        if (UseDefaultDur==1) traf->tfhd->flags |= GF_ISOM_TRAF_SAMPLE_DUR;
        if (UseDefaultFlag==1) traf->tfhd->flags |= GF_ISOM_TRAF_SAMPLE_FLAGS;
        if (traf->tfhd->sample_desc_index && traf->tfhd->sample_desc_index != traf->trex->def_sample_desc_index) traf->tfhd->flags |= GF_ISOM_TRAF_SAMPLE_DESC;


        return sampleCount;
}

static u32 moof_get_sap_info(GF_MovieFragmentBox *moof, u32 refTrackID, u32 *sap_delta, Bool *starts_with_sap)
{
        u32 i, j, count, delta, earliest_cts, sap_type, sap_sample_num, cur_sample;
        Bool first = GF_TRUE;
        GF_TrunEntry *ent;
        GF_TrackFragmentBox *traf=NULL;
        GF_TrackFragmentRunBox *trun;
        sap_type = 0;
        *sap_delta = 0;
        *starts_with_sap = GF_FALSE;
        for (i=0; i<gf_list_count(moof->TrackList); i++) {
                traf = (GF_TrackFragmentBox*)gf_list_get(moof->TrackList, i);
                if (traf->tfhd->trackID==refTrackID) break;
                traf=NULL;
        }
        if (!traf) return sap_type;
        earliest_cts = 0;

        /*first check if we have a roll/rap sample in this traf, and mark its sample count*/
        sap_type = 0;
        sap_sample_num = 0;
        /*check RAP and ROLL*/
        count = traf->sampleGroups ? gf_list_count(traf->sampleGroups) : 0;
        for (i=0; i<count; i++) {
                GF_SampleGroupBox *sg;
                u32 j, first_sample;
                Bool rap_type = GF_FALSE;
                sg = (GF_SampleGroupBox*)gf_list_get(traf->sampleGroups, i);

                switch (sg->grouping_type) {
                case GF_4CC('r','a','p',' '):
                        rap_type = GF_TRUE;
                        break;
                case GF_4CC('r','o','l','l'):
                        break;
                default:
                        continue;
                }
                /*first entry is SAP*/
                first_sample = 1;
                for (j=0; j<sg->entry_count; j++) {
                        if (! sg->sample_entries[j].group_description_index) {
                                first_sample += sg->sample_entries[j].sample_count;
                                continue;
                        }
                        if (!j) {
                                *starts_with_sap = GF_TRUE;
                                sap_sample_num = 0;
                        }
                        if (!sap_sample_num || (sap_sample_num>first_sample)) {
                                sap_type = rap_type ? 3 : 4;
                                sap_sample_num = first_sample;
                        }
                        break;
                }
        }

        /*then browse all samples, looking for SYNC flag or sap_sample_num*/
        cur_sample = 1;
        delta = 0;
        i=0;
        while ((trun = (GF_TrackFragmentRunBox*)gf_list_enum(traf->TrackRuns, &i))) {
                if (trun->flags & GF_ISOM_TRUN_FIRST_FLAG) {
                        if (GF_ISOM_GET_FRAG_SYNC(trun->flags)) {
                                ent = (GF_TrunEntry*)gf_list_get(trun->entries, 0);
//                              if (!delta) earliest_cts = ent->CTS_Offset;
                                *sap_delta = delta + ent->CTS_Offset - ent->CTS_Offset;
                                *starts_with_sap = first;
                                sap_type = ent->SAP_type;
                                return sap_type;
                        }
                }
                j=0;
                while ((ent = (GF_TrunEntry*)gf_list_enum(trun->entries, &j))) {
                        if (!delta) earliest_cts = ent->CTS_Offset;

                        if (GF_ISOM_GET_FRAG_SYNC(ent->flags)) {
                                *sap_delta = delta + ent->CTS_Offset - earliest_cts;
                                *starts_with_sap = first;
                                sap_type = ent->SAP_type;
                                return sap_type;
                        }
                        /*we found our roll or rap sample*/
                        if (cur_sample==sap_sample_num) {
                                *sap_delta = delta + ent->CTS_Offset - earliest_cts;
                                return sap_type;
                        }
                        delta += ent->Duration;
                        first = GF_FALSE;
                        cur_sample++;
                }
        }
        /*not found*/
        return 0;
}

u32 moof_get_duration(GF_MovieFragmentBox *moof, u32 refTrackID)
{
        u32 i, j, duration;
        GF_TrunEntry *ent;
        GF_TrackFragmentBox *traf = NULL;
        GF_TrackFragmentRunBox *trun;
        for (i=0; i<gf_list_count(moof->TrackList); i++) {
                traf = (GF_TrackFragmentBox*)gf_list_get(moof->TrackList, i);
                if (traf->tfhd->trackID==refTrackID) break;
                traf=NULL;
        }
        if (!traf) return 0;

        duration = 0;
        i=0;
        while ((trun = (GF_TrackFragmentRunBox*)gf_list_enum(traf->TrackRuns, &i))) {
                j=0;
                while ((ent = (GF_TrunEntry*)gf_list_enum(trun->entries, &j))) {
                        if (ent->flags & GF_ISOM_TRAF_SAMPLE_DUR)
                                duration += ent->Duration;
                        else
                                duration += traf->trex->def_sample_duration;
                }
        }
        return duration;
}

static u64 moof_get_earliest_cts(GF_MovieFragmentBox *moof, u32 refTrackID)
{
        u32 i, j;
        u64 cts, duration;
        GF_TrunEntry *ent;
        GF_TrackFragmentBox *traf=NULL;
        GF_TrackFragmentRunBox *trun;
        for (i=0; i<gf_list_count(moof->TrackList); i++) {
                traf = (GF_TrackFragmentBox*)gf_list_get(moof->TrackList, i);
                if (traf->tfhd->trackID==refTrackID) break;
                traf=NULL;
        }
        if (!traf) return 0;

        duration = 0;
        cts = LLU_CAST (-1);
        i=0;
        while ((trun = (GF_TrackFragmentRunBox*)gf_list_enum(traf->TrackRuns, &i))) {
                j=0;
                while ((ent = (GF_TrunEntry*)gf_list_enum(trun->entries, &j))) {
                        if (duration + ent->CTS_Offset < cts)
                                cts = duration + ent->CTS_Offset;
                        duration += ent->Duration;
                }
        }
        return cts;
}

GF_Err StoreFragment(GF_ISOFile *movie, Bool load_mdat_only, s32 data_offset_diff, u32 *moof_size)
{
        GF_Err e;
        u64 moof_start, pos;
        u32 size, i, s_count, mdat_size;
        s32 offset;
        char *buffer;
        GF_TrackFragmentBox *traf;
        GF_TrackFragmentRunBox *trun;
        GF_BitStream *bs;
        if (!movie->moof) return GF_OK;

        bs = movie->editFileMap->bs;
        if (!movie->moof_first) load_mdat_only = GF_FALSE;
        mdat_size = 0;
        //1 - flush all caches
        i=0;
        while ((traf = (GF_TrackFragmentBox*)gf_list_enum(movie->moof->TrackList, &i))) {
                if (!traf->DataCache) continue;
                s_count = gf_list_count(traf->TrackRuns);
                if (!s_count) continue;
                trun = (GF_TrackFragmentRunBox *)gf_list_get(traf->TrackRuns, s_count-1);
                if (!trun->cache || !trun->sample_count) continue;

                //update offset
                trun->data_offset = (u32) (gf_bs_get_position(bs) - movie->moof->fragment_offset - 8);
                //write cache
                gf_bs_get_content(trun->cache, &buffer, &size);
                gf_bs_write_data(bs, buffer, size);
                gf_bs_del(trun->cache);
                gf_free(buffer);
                trun->cache = NULL;
                traf->DataCache=0;
        }

        if (load_mdat_only) {
                u64 pos = gf_bs_get_position(bs);
                //we assume we never write large MDATs in fragment mode which should always be true
                movie->moof->mdat_size = (u32) (pos - movie->moof->fragment_offset);

                if (movie->segment_bs) {
                        gf_bs_seek(bs, 0);
                        /*write mdat size*/
                        gf_bs_write_u32(bs, (u32) movie->moof->mdat_size);
                        /*and get internal buffer*/
                        gf_bs_seek(bs, movie->moof->mdat_size);
                        gf_bs_get_content(bs, &movie->moof->mdat, &movie->moof->mdat_size);

                        gf_bs_del(bs);
                        movie->editFileMap->bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
                } else {
                        u64 offset = movie->segment_start;
                        gf_bs_seek(bs, offset);
                        /*write mdat size*/
                        gf_bs_write_u32(bs, (u32) movie->moof->mdat_size);

                        movie->moof->mdat = (char*)gf_malloc(sizeof(char) * movie->moof->mdat_size);
                        if (!movie->moof->mdat) return GF_OUT_OF_MEM;

                        gf_bs_seek(bs, offset);
                        gf_bs_read_data(bs, movie->moof->mdat, movie->moof->mdat_size);

                        gf_bs_seek(bs, offset);
                        gf_bs_truncate(bs);
                }

                return GF_OK;
        }

        moof_start = gf_bs_get_position(bs);

        if (movie->moof->ntp) {
                moof_start += 8*4;
        }

        //2- update MOOF MDAT header
        if (!movie->moof->mdat) {
                gf_bs_seek(bs, movie->moof->fragment_offset);
                //we assume we never write large MDATs in fragment mode which should always be true
                mdat_size = (u32) (moof_start - movie->moof->fragment_offset);
                gf_bs_write_u32(bs, (u32) mdat_size);
                gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_MDAT);
                gf_bs_seek(bs, moof_start);
        }

        /*estimate moof size and shift trun offsets*/
#ifndef USE_BASE_DATA_OFFSET
        offset = 0;
        if (movie->use_segments) {
                e = gf_isom_box_size((GF_Box *) movie->moof);
                if (e) return e;
                offset = (s32) movie->moof->size;
                /*mdat size & type*/
                offset += 8;
                update_trun_offsets(movie, offset);
        }
#endif

        //3- clean our traf's
        i=0;
        while ((traf = (GF_TrackFragmentBox*) gf_list_enum(movie->moof->TrackList, &i))) {
                //compute default settings for the TRAF
                ComputeFragmentDefaults(traf);
                //updates all trun and set all flags, INCLUDING TRAF FLAGS (durations, ...)
                s_count = UpdateRuns(movie, traf);
                //empty fragment destroy it
                if (!traf->tfhd->EmptyDuration && !s_count) {
                        i--;
                        gf_list_rem(movie->moof->TrackList, i);
                        gf_isom_box_del((GF_Box *) traf);
                        continue;
                }
        }

        buffer = NULL;
        /*rewind bitstream and load mdat in memory */
        if (movie->moof_first && !movie->moof->mdat) {
                buffer = (char*)gf_malloc(sizeof(char)*mdat_size);
                gf_bs_seek(bs, movie->moof->fragment_offset);
                gf_bs_read_data(bs, buffer, mdat_size);
                /*back to mdat start and erase with moov*/
                gf_bs_seek(bs, movie->moof->fragment_offset);
                gf_bs_truncate(bs);
        }

        //4- Write moof
        e = gf_isom_box_size((GF_Box *) movie->moof);
        if (e) return e;
        /*moof first, update traf headers - THIS WILL IMPACT THE MOOF SIZE IF WE
        DECIDE NOT TO USE THE DATA-OFFSET FLAG*/
        if (movie->moof_first
#ifndef USE_BASE_DATA_OFFSET
                && !movie->use_segments
#endif
           ) {
                i=0;
                while ((traf = (GF_TrackFragmentBox*)gf_list_enum(movie->moof->TrackList, &i))) {
                        /*offset increases by moof size*/
                        traf->tfhd->base_data_offset += movie->moof->size;
                        traf->tfhd->base_data_offset += data_offset_diff;
                }
        }
#ifndef USE_BASE_DATA_OFFSET
        else if (movie->use_segments) {
                if (offset != (movie->moof->size+8)) {
                        offset = (s32) (movie->moof->size + 8 - offset);
                        update_trun_offsets(movie, offset);
                        e = gf_isom_box_size((GF_Box *) movie->moof);
                        if (e) return e;
                }
        }
#endif


        if (movie->moof->ntp) {
                gf_bs_write_u32(bs, 8*4);
                gf_bs_write_u32(bs, GF_4CC('p','r','f','t') );
                gf_bs_write_u8(bs, 1);
                gf_bs_write_u24(bs, 0);
                gf_bs_write_u32(bs, movie->moof->reference_track_ID);
                gf_bs_write_u64(bs, movie->moof->ntp);
                gf_bs_write_u64(bs, movie->moof->timestamp);
        }

        pos = gf_bs_get_position(bs);
        i=0;
        while ((traf = (GF_TrackFragmentBox*)gf_list_enum(movie->moof->TrackList, &i))) {
                traf->moof_start_in_bs = pos;
        }

        e = gf_isom_box_write((GF_Box *) movie->moof, bs);
        if (e) return e;

        //rewrite mdat after moof
        if (movie->moof->mdat) {
                gf_bs_write_data(bs, movie->moof->mdat, movie->moof->mdat_size);
                gf_free(movie->moof->mdat);
                movie->moof->mdat = NULL;
        } else if (buffer) {
                gf_bs_write_data(bs, buffer, mdat_size);
                gf_free(buffer);
        }

        if (moof_size) *moof_size = (u32) movie->moof->size;

        if (!movie->use_segments) {
                gf_isom_box_del((GF_Box *) movie->moof);
                movie->moof = NULL;
        }
        return GF_OK;
}

static GF_Err sidx_rewrite(GF_SegmentIndexBox *sidx, GF_BitStream *bs, u64 start_pos)
{
        GF_Err e;
        u64 pos = gf_bs_get_position(bs);
        /*write sidx*/
        gf_bs_seek(bs, start_pos);
        e = gf_isom_box_write((GF_Box *) sidx, bs);
        gf_bs_seek(bs, pos);
        return e;
}

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)
{
        GF_BitStream *bs;
        GF_Err e;

        //and only at setup
        if (!movie || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) return GF_BAD_PARAM;
        if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE;
        if (movie->root_sidx) return GF_BAD_PARAM;
        if (movie->moof) return GF_BAD_PARAM;
        if (gf_list_count(movie->moof_list)) return GF_BAD_PARAM;

        movie->root_sidx = (GF_SegmentIndexBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_SIDX);
        /*we don't write anything between sidx and following moov*/
        movie->root_sidx->first_offset = 0;

        /*for now we only store one ref per subsegment and don't support daisy-chaining*/
        movie->root_sidx->nb_refs = nb_segs;

        movie->root_sidx->refs = (GF_SIDXReference*)gf_malloc(sizeof(GF_SIDXReference) * movie->root_sidx->nb_refs);
        memset(movie->root_sidx->refs, 0, sizeof(GF_SIDXReference) * movie->root_sidx->nb_refs);

        movie->root_sidx_index = 0;

        /*remember start of sidx*/
        movie->root_sidx_offset = gf_bs_get_position(movie->editFileMap->bs);

        bs = movie->editFileMap->bs;

        e = gf_isom_box_size((GF_Box *) movie->root_sidx);
        if (e) return e;
        e = gf_isom_box_write((GF_Box *) movie->root_sidx, bs);
        if (e) return e;

        if (start_range) *start_range = (u32) movie->root_sidx_offset;
        if (end_range) *end_range = (u32) gf_bs_get_position(bs)-1;

        return GF_OK;
}


static GF_Err gf_isom_write_styp(GF_ISOFile *movie, Bool last_segment)
{
        GF_Err e = GF_OK;
        /*write STYP if we write to a different file or if we write the last segment*/
        if (!movie->append_segment && !movie->segment_start && !movie->styp_written) {

                /*modify brands STYP*/

                /*"msix" brand: this is a DASH Initialization Segment*/
                gf_isom_modify_alternate_brand(movie, GF_4CC('m','s','i','x'), 1);
                if (last_segment) {
                        /*"lmsg" brand: this is the last DASH Segment*/
                        gf_isom_modify_alternate_brand(movie, GF_4CC('l','m','s','g'), 1);
                }

                movie->brand->type = GF_ISOM_BOX_TYPE_STYP;
                e = gf_isom_box_size((GF_Box *) movie->brand);
                if (e) return e;
                e = gf_isom_box_write((GF_Box *) movie->brand, movie->editFileMap->bs);
                if (e) return e;

                movie->styp_written = GF_TRUE;
        }
        return GF_OK;
}

GF_EXPORT
GF_Err gf_isom_flush_fragments(GF_ISOFile *movie, Bool last_segment)
{
        GF_BitStream *temp_bs = NULL;
        GF_Err e;

        if (!movie || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) return GF_BAD_PARAM;
        if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE;

        /*flush our fragment (store in mem)*/
        if (movie->moof) {
                e = StoreFragment(movie, GF_TRUE, 0, NULL);
                if (e) return e;
        }

        if (movie->segment_bs) {
                temp_bs = movie->editFileMap->bs;
                movie->editFileMap->bs = movie->segment_bs;
        }

        gf_bs_seek(movie->editFileMap->bs, movie->segment_start);
        gf_bs_truncate(movie->editFileMap->bs);

        /*write styp to file if needed*/
        e = gf_isom_write_styp(movie, last_segment);
        if (e) return e;

        /*write all pending fragments to file*/
        while (gf_list_count(movie->moof_list)) {
                s32 offset_diff;
                u32 moof_size;

                movie->moof = (GF_MovieFragmentBox*)gf_list_get(movie->moof_list, 0);
                gf_list_rem(movie->moof_list, 0);

                offset_diff = (s32) (gf_bs_get_position(movie->editFileMap->bs) - movie->moof->fragment_offset);
                movie->moof->fragment_offset = gf_bs_get_position(movie->editFileMap->bs);

                e = StoreFragment(movie, GF_FALSE, offset_diff, &moof_size);
                if (e) return e;

                gf_isom_box_del((GF_Box *) movie->moof);
                movie->moof = NULL;
        }

        /*append mode: store fragment at the end of the regular movie bitstream, and delete the temp bitstream*/
        if (movie->append_segment) {
                char bloc[1024];
                u32 seg_size = (u32) gf_bs_get_size(movie->editFileMap->bs);
                gf_bs_seek(movie->editFileMap->bs, 0);
                while (seg_size) {
                        u32 size = gf_bs_read_data(movie->editFileMap->bs, bloc, (seg_size>1024) ? 1024 : seg_size);
                        gf_bs_write_data(movie->movieFileMap->bs, bloc, size);
                        seg_size -= size;
                }
                gf_isom_datamap_flush(movie->movieFileMap);

                gf_isom_datamap_del(movie->editFileMap);
                movie->editFileMap = gf_isom_fdm_new_temp(NULL);
        } else {
                gf_isom_datamap_flush(movie->editFileMap);
        }
        movie->segment_start = gf_bs_get_position(movie->editFileMap->bs);

        if (temp_bs) {
                movie->segment_bs = movie->editFileMap->bs;
                movie->editFileMap->bs = temp_bs;
        }
        return GF_OK;
}

typedef struct
{
        GF_SegmentIndexBox *sidx;
        u64 start_offset, end_offset;
} SIDXEntry;

static u64 get_presentation_time(u64 media_time, s32 ts_shift)
{
        if ((ts_shift<0) && (media_time < -ts_shift)) {
                media_time = 0;
        } else {
                media_time += ts_shift;
        }
        return media_time ;
}

GF_EXPORT
GF_Err gf_isom_close_segment(GF_ISOFile *movie, s32 subsegments_per_sidx, u32 referenceTrackID, u64 ref_track_decode_time, s32 ts_shift, u64 ref_track_next_cts, Bool daisy_chain_sidx, Bool last_segment, u32 segment_marker_4cc, u64 *index_start_range, u64 *index_end_range)
{
        GF_SegmentIndexBox *sidx=NULL;
        GF_SegmentIndexBox *root_sidx=NULL;
        GF_List *daisy_sidx = NULL;
        u64 sidx_start, sidx_end;
        Bool first_frag_in_subseg;
        Bool no_sidx = GF_FALSE;
        u32 count, cur_idx, cur_dur, sidx_dur, sidx_idx, idx_offset, frag_count;
        u64 last_top_box_pos, root_prev_offset, local_sidx_start, local_sidx_end, prev_earliest_cts;
        GF_TrackBox *trak = NULL;
        GF_Err e;
        /*number of subsegment in this segment (eg nb references in the first SIDX found)*/
        u32 nb_subsegs=0;
        /*number of subsegment per sidx (eg number of references of any sub-SIDX*/
        u32 subseg_per_sidx;
        /*number of fragments per subsegment*/
        u32 frags_per_subseg;
        /*number of fragments per subsidx*/
        u32 frags_per_subsidx;

        sidx_start = sidx_end = 0;

        if (index_start_range) *index_start_range = 0;
        if (index_end_range) *index_end_range = 0;

        //and only at setup
        if (!movie || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) return GF_BAD_PARAM;
        if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE;

        count = gf_list_count(movie->moov->mvex->TrackExList);
        if (!count) return GF_BAD_PARAM;

        /*store fragment*/
        if (movie->moof) {
                e = StoreFragment(movie, GF_TRUE, 0, NULL);
                if (e) return e;
        }
        /*restore final bitstream*/
        if (movie->segment_bs) {
                gf_bs_del(movie->editFileMap->bs);
                movie->editFileMap->bs = movie->segment_bs;
                movie->segment_bs = NULL;
        }

        count = gf_list_count(movie->moof_list);
        if (!count) {
                /*append segment marker box*/
                if (segment_marker_4cc) {
                        if (movie->append_segment) {
                                gf_bs_write_u32(movie->movieFileMap->bs, 8);    //write size field
                                gf_bs_write_u32(movie->movieFileMap->bs, segment_marker_4cc); //write box type field
                        } else {
                                gf_bs_write_u32(movie->editFileMap->bs, 8);     //write size field
                                gf_bs_write_u32(movie->editFileMap->bs, segment_marker_4cc); //write box type field
                        }
                }
                return GF_OK;
        }

        gf_bs_seek(movie->editFileMap->bs, movie->segment_start);
        gf_bs_truncate(movie->editFileMap->bs);

        idx_offset = 0;

        if (referenceTrackID) {
                trak = gf_isom_get_track_from_id(movie->moov, referenceTrackID);
                if (!trak) return GF_BAD_PARAM;
        }

        if (subsegments_per_sidx < 0) {
                referenceTrackID = 0;
                subsegments_per_sidx = 0;
        }
        if (!subsegments_per_sidx && !referenceTrackID) {
                no_sidx = GF_TRUE;
        }

        e = gf_isom_write_styp(movie, last_segment);
        if (e) return e;

        frags_per_subseg = 0;
        subseg_per_sidx = 0;
        frags_per_subsidx = 0;

        prev_earliest_cts = 0;

        if (daisy_chain_sidx)
                daisy_sidx = gf_list_new();

        /*prepare SIDX: we write a blank SIDX box with the right number of entries, and will rewrite it later on*/
        if (referenceTrackID) {
                Bool is_root_sidx = GF_FALSE;

                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);

                if (movie->root_sidx) {
                        sidx = movie->root_sidx;
                } else {
                        sidx = (GF_SegmentIndexBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_SIDX);
                }
                sidx->reference_ID = referenceTrackID;
                sidx->timescale = trak->Media->mediaHeader->timeScale;
                /*we don't write anything between sidx and following moov*/
                sidx->first_offset = 0;

                /*we allocated our sidx to have one ref per "segment" (eg per call to close_segment)*/
                if (movie->root_sidx) {
                        if (!movie->root_sidx_index) {
                                sidx->earliest_presentation_time = prev_earliest_cts;
                        }
                        nb_subsegs = 1;
                        frags_per_subseg = count;
                        frags_per_subsidx = count;
                        subseg_per_sidx = 1;
                        daisy_chain_sidx = GF_FALSE;

                        idx_offset = movie->root_sidx_index;
                        sidx_end = gf_bs_get_position(movie->editFileMap->bs);
                } else {
                        sidx->earliest_presentation_time = prev_earliest_cts;

                        /*if more subsegments requested than fragments available, make a single sidx*/
                        if ((s32) count <= subsegments_per_sidx)
                                subsegments_per_sidx = 0;

                        if (daisy_chain_sidx && (subsegments_per_sidx<2))
                                subsegments_per_sidx = 2;

                        /*single SIDX, each fragment is a subsegment and we reference all subsegments*/
                        if (!subsegments_per_sidx) {
                                nb_subsegs = count;
                                /*we consider that each fragment is a subsegment - this could be controled by another parameter*/
                                frags_per_subseg = 1;
                                frags_per_subsidx = count;
                                subseg_per_sidx = count;

                                sidx->nb_refs = nb_subsegs;
                                daisy_chain_sidx = GF_FALSE;
                        }
                        /*daisy-chain SIDX: each SIDX describes a subsegment made of frags_per_subseg fragments plus next */
                        else if (daisy_chain_sidx) {
                                frags_per_subsidx = count/subsegments_per_sidx;
                                if (frags_per_subsidx * subsegments_per_sidx < count) frags_per_subsidx++;

                                nb_subsegs = subsegments_per_sidx;

                                /*we consider that each fragment is a subsegment - this could be controled by another parameter*/
                                frags_per_subseg = 1;
                                subseg_per_sidx = frags_per_subsidx / frags_per_subseg;
                                if (subseg_per_sidx * frags_per_subseg < frags_per_subsidx) subseg_per_sidx++;

                                sidx->nb_refs = subseg_per_sidx + 1;
                        }
                        /*hierarchical SIDX*/
                        else {
                                frags_per_subsidx = count/subsegments_per_sidx;
                                if (frags_per_subsidx * subsegments_per_sidx < count) frags_per_subsidx++;

                                nb_subsegs = subsegments_per_sidx;

                                /*we consider that each fragment is a subsegment - this could be controled by another parameter*/
                                frags_per_subseg = 1;
                                subseg_per_sidx = frags_per_subsidx / frags_per_subseg;
                                if (subseg_per_sidx * frags_per_subseg < frags_per_subsidx) subseg_per_sidx++;

                                sidx->nb_refs = nb_subsegs;
                                is_root_sidx = GF_TRUE;
                        }

                        sidx->refs = (GF_SIDXReference*)gf_malloc(sizeof(GF_SIDXReference)*sidx->nb_refs);
                        memset(sidx->refs, 0, sizeof(GF_SIDXReference)*sidx->nb_refs);

                        /*remember start of sidx*/
                        sidx_start = gf_bs_get_position(movie->editFileMap->bs);

                        e = gf_isom_box_size((GF_Box *) sidx);
                        if (e) return e;
                        e = gf_isom_box_write((GF_Box *) sidx, movie->editFileMap->bs);
                        if (e) return e;

                        sidx_end = gf_bs_get_position(movie->editFileMap->bs);

                        if (daisy_sidx) {
                                SIDXEntry *entry;
                                GF_SAFEALLOC(entry, SIDXEntry);
                                entry->sidx = sidx;
                                entry->start_offset = sidx_start;
                                gf_list_add(daisy_sidx, entry);
                        }
                }

                if (is_root_sidx) {
                        root_sidx = sidx;
                        sidx = NULL;
                }
                count = cur_idx = 0;
        }


        last_top_box_pos = root_prev_offset = sidx_end;
        sidx_idx = 0;
        sidx_dur = 0;
        local_sidx_start = local_sidx_end = 0;

        /*cumulated segments duration since start of the sidx */
        frag_count = frags_per_subsidx;
        cur_dur = 0;
        cur_idx = 0;
        first_frag_in_subseg = GF_TRUE;
        e = GF_OK;
        while (gf_list_count(movie->moof_list)) {
                s32 offset_diff;
                u32 moof_size;

                movie->moof = (GF_MovieFragmentBox*)gf_list_get(movie->moof_list, 0);
                gf_list_rem(movie->moof_list, 0);

                /*hierarchical or daisy-chain SIDXs*/
                if (!no_sidx && !sidx && (root_sidx || daisy_chain_sidx) ) {
                        u32 subsegments_remaining;
                        sidx = (GF_SegmentIndexBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_SIDX);
                        sidx->reference_ID = referenceTrackID;
                        sidx->timescale = trak ? trak->Media->mediaHeader->timeScale : 1000;
                        sidx->earliest_presentation_time = get_presentation_time( ref_track_decode_time + sidx_dur + moof_get_earliest_cts(movie->moof, referenceTrackID), ts_shift);

                        frag_count = frags_per_subsidx;

                        /*last segment, add only one ref*/
                        subsegments_remaining = 1 + gf_list_count(movie->moof_list);
                        if (subseg_per_sidx*frags_per_subseg > subsegments_remaining) {
                                subseg_per_sidx = subsegments_remaining / frags_per_subseg;
                                if (subseg_per_sidx * frags_per_subseg < subsegments_remaining) subseg_per_sidx++;
                        }
                        /*we don't write anything between sidx and following moov*/
                        sidx->first_offset = 0;
                        sidx->nb_refs = subseg_per_sidx;
                        if (daisy_chain_sidx && (nb_subsegs>1)) {
                                sidx->nb_refs += 1;
                        }
                        sidx->refs = (GF_SIDXReference*)gf_malloc(sizeof(GF_SIDXReference)*sidx->nb_refs);
                        memset(sidx->refs, 0, sizeof(GF_SIDXReference)*sidx->nb_refs);

                        if (root_sidx)
                                root_sidx->refs[sidx_idx].reference_type = GF_TRUE;

                        /*remember start of sidx*/
                        local_sidx_start = gf_bs_get_position(movie->editFileMap->bs);

                        /*write it*/
                        e = gf_isom_box_size((GF_Box *) sidx);
                        if (e) return e;
                        e = gf_isom_box_write((GF_Box *) sidx, movie->editFileMap->bs);
                        if (e) return e;

                        local_sidx_end = gf_bs_get_position(movie->editFileMap->bs);

                        /*adjust prev offset*/
                        last_top_box_pos = local_sidx_end;

                        if (daisy_sidx) {
                                SIDXEntry *entry;
                                GF_SAFEALLOC(entry, SIDXEntry);
                                if (!entry) return GF_OUT_OF_MEM;
                                entry->sidx = sidx;
                                entry->start_offset = local_sidx_start;
                                gf_list_add(daisy_sidx, entry);
                        }
                }

                offset_diff = (s32) (gf_bs_get_position(movie->editFileMap->bs) - movie->moof->fragment_offset);
                movie->moof->fragment_offset = gf_bs_get_position(movie->editFileMap->bs);

                if (!e) {
                        e = StoreFragment(movie, GF_FALSE, offset_diff, &moof_size);


                        if (sidx) {
                                u32 cur_index = idx_offset + cur_idx;

                                /*do not compute earliest CTS if single segment sidx since we only have set the info for one subsegment*/
                                if (!movie->root_sidx && first_frag_in_subseg) {
                                        u64 first_cts = get_presentation_time( ref_track_decode_time + sidx_dur + cur_dur +  moof_get_earliest_cts(movie->moof, referenceTrackID), ts_shift);
                                        u32 subseg_dur = (u32) (first_cts - prev_earliest_cts);
                                        if (cur_index) {
                                                sidx->refs[cur_index-1].subsegment_duration = subseg_dur;
                                                if (root_sidx) root_sidx->refs[sidx_idx].subsegment_duration += subseg_dur;
                                        }
                                        prev_earliest_cts = first_cts;
                                        first_frag_in_subseg = GF_FALSE;
                                }


                                /*we refer to next moof*/
                                sidx->refs[cur_index].reference_type = GF_FALSE;
                                if (!sidx->refs[cur_index].SAP_type) {
                                        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);
                                        if (sidx->refs[cur_index].SAP_type) {
                                                if (root_sidx && !root_sidx->refs[sidx_idx].SAP_type) {
                                                        root_sidx->refs[sidx_idx].SAP_type = sidx->refs[cur_index].SAP_type;
                                                        root_sidx->refs[sidx_idx].SAP_delta_time = sidx->refs[cur_index].SAP_delta_time;
                                                        root_sidx->refs[sidx_idx].starts_with_SAP = sidx->refs[cur_index].starts_with_SAP;
                                                }
                                        }
                                }
                                cur_dur += moof_get_duration(movie->moof, referenceTrackID);

                                /*reference size is end of the moof we just wrote minus last_box_pos*/
                                sidx->refs[cur_index].reference_size += (u32) ( gf_bs_get_position(movie->editFileMap->bs) - last_top_box_pos) ;
                                last_top_box_pos = gf_bs_get_position(movie->editFileMap->bs);

                                count++;

                                /*we are switching subsegment*/
                                frag_count--;

                                if (count==frags_per_subseg) {
                                        count = 0;
                                        first_frag_in_subseg = GF_TRUE;
                                        cur_idx++;
                                }

                                /*switching to next SIDX*/
                                if ((cur_idx==subseg_per_sidx) || !frag_count) {
                                        u32 subseg_dur;
                                        u64 next_cts;
                                        /*update last ref duration*/
                                        if (gf_list_count(movie->moof_list)) {
                                                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);
                                        } else {
                                                next_cts = get_presentation_time( ref_track_next_cts, ts_shift);
                                        }
                                        subseg_dur = (u32) (next_cts - prev_earliest_cts);
                                        if (movie->root_sidx) {
                                                sidx->refs[idx_offset].subsegment_duration = subseg_dur;
                                        }
                                        /*if daisy chain and not the last sidx, we have an extra entry at the end*/
                                        else if (daisy_chain_sidx && (nb_subsegs>1)) {
                                                sidx->refs[sidx->nb_refs - 2].subsegment_duration = subseg_dur;
                                        } else {
                                                sidx->refs[sidx->nb_refs-1].subsegment_duration = subseg_dur;
                                        }
                                        if (root_sidx) root_sidx->refs[sidx_idx].subsegment_duration += subseg_dur;

                                        if (root_sidx) {
                                                root_sidx->refs[sidx_idx].reference_size = (u32) (gf_bs_get_position(movie->editFileMap->bs) - local_sidx_start);
                                                if (!sidx_idx) {
                                                        root_sidx->earliest_presentation_time = sidx->earliest_presentation_time;
                                                }
                                                sidx_rewrite(sidx, movie->editFileMap->bs, local_sidx_start);
                                                gf_isom_box_del((GF_Box*)sidx);
                                                sidx = NULL;
                                        } else if (daisy_chain_sidx) {
                                                SIDXEntry *entry = (SIDXEntry*)gf_list_last(daisy_sidx);
                                                entry->end_offset = gf_bs_get_position(movie->editFileMap->bs);
                                                nb_subsegs--;
                                                sidx = NULL;
                                        }
                                        sidx_dur += cur_dur;
                                        cur_dur = 0;
                                        count = 0;
                                        cur_idx=0;
                                        if (movie->root_sidx)
                                                movie->root_sidx_index++;
                                        sidx_idx++;
                                }
                        }
                }
                gf_isom_box_del((GF_Box *) movie->moof);
                movie->moof = NULL;
        }

        /*append segment marker box*/
        if (segment_marker_4cc) {
                gf_bs_write_u32(movie->editFileMap->bs, 8);     //write size field
                gf_bs_write_u32(movie->editFileMap->bs, segment_marker_4cc); //write box type field
        }

        if (movie->root_sidx) {
                if (last_segment) {
                        assert(movie->root_sidx_index == movie->root_sidx->nb_refs);

                        sidx_rewrite(movie->root_sidx, movie->editFileMap->bs, movie->root_sidx_offset);
                        gf_isom_box_del((GF_Box*) movie->root_sidx);
                        movie->root_sidx = NULL;
                }
                return GF_OK;
        }

        if (sidx) {
                assert(!root_sidx);
                sidx_rewrite(sidx, movie->editFileMap->bs, sidx_start);
                gf_isom_box_del((GF_Box*)sidx);
        }
        if (daisy_sidx) {
                u32 i, j;
                u64 last_entry_end_offset = 0;
                u32 count = gf_list_count(daisy_sidx);
                for (i=count; i>1; i--) {
                        SIDXEntry *entry = (SIDXEntry*)gf_list_get(daisy_sidx, i-2);
                        SIDXEntry *next_entry = (SIDXEntry*)gf_list_get(daisy_sidx, i-1);

                        if (!last_entry_end_offset) {
                                last_entry_end_offset = next_entry->end_offset;
                                /*rewrite last sidx*/
                                sidx_rewrite(next_entry->sidx, movie->editFileMap->bs, next_entry->start_offset);
                        }
                        /*copy over SAP info for last item (which points to next item !)*/
                        entry->sidx->refs[entry->sidx->nb_refs-1] = next_entry->sidx->refs[0];
                        /*and rewrite reference type, size and dur*/
                        entry->sidx->refs[entry->sidx->nb_refs-1].reference_type = GF_TRUE;
                        entry->sidx->refs[entry->sidx->nb_refs-1].reference_size = (u32) (last_entry_end_offset - next_entry->start_offset);
                        entry->sidx->refs[entry->sidx->nb_refs-1].subsegment_duration = 0;
                        for (j=0; j<next_entry->sidx->nb_refs; j++) {
                                entry->sidx->refs[entry->sidx->nb_refs-1].subsegment_duration += next_entry->sidx->refs[j].subsegment_duration;
                        }
                        sidx_rewrite(entry->sidx, movie->editFileMap->bs, entry->start_offset);
                }
                while (gf_list_count(daisy_sidx)) {
                        SIDXEntry *entry = (SIDXEntry*)gf_list_last(daisy_sidx);
                        gf_isom_box_del((GF_Box*)entry->sidx);
                        gf_free(entry);
                        gf_list_rem_last(daisy_sidx);
                }
                gf_list_del(daisy_sidx);
        }
        if (root_sidx) {
                sidx_rewrite(root_sidx, movie->editFileMap->bs, sidx_start);
                gf_isom_box_del((GF_Box*)root_sidx);
        }

        if ((root_sidx || sidx) && !daisy_chain_sidx) {
                if (index_start_range) *index_start_range = sidx_start;
                if (index_end_range) *index_end_range = sidx_end - 1;
        }

        if (movie->append_segment) {
                char bloc[1024];
                u32 seg_size = (u32) gf_bs_get_size(movie->editFileMap->bs);
                gf_bs_seek(movie->editFileMap->bs, 0);
                while (seg_size) {
                        u32 size = gf_bs_read_data(movie->editFileMap->bs, bloc, (seg_size>1024) ? 1024 : seg_size);
                        gf_bs_write_data(movie->movieFileMap->bs, bloc, size);
                        seg_size -= size;
                }
                gf_isom_datamap_del(movie->editFileMap);
                movie->editFileMap = gf_isom_fdm_new_temp(NULL);
        }

        return e;
}

GF_EXPORT
GF_Err gf_isom_close_fragments(GF_ISOFile *movie)
{
        if (movie->use_segments) {
                return gf_isom_close_segment(movie, 0, 0, 0, 0, 0, GF_FALSE, 1, 0, NULL, NULL);
        } else {
                return StoreFragment(movie, GF_FALSE, 0, NULL);
        }
}

GF_EXPORT
GF_Err gf_isom_start_segment(GF_ISOFile *movie, const char *SegName, Bool memory_mode)
{
        GF_Err e;
        //and only at setup
        if (!movie || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) return GF_BAD_PARAM;
        if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE;

        if (gf_list_count(movie->moof_list))
                return GF_BAD_PARAM;

        movie->segment_bs = NULL;
        movie->append_segment = GF_FALSE;
        /*update segment file*/
        if (SegName) {
                gf_isom_datamap_del(movie->editFileMap);
                e = gf_isom_datamap_new(SegName, NULL, GF_ISOM_DATA_MAP_WRITE, & movie->editFileMap);
                movie->segment_start = 0;
                movie->styp_written = GF_FALSE;
                if (e) return e;
        } else {
                assert(gf_list_count(movie->moof_list) == 0);
                movie->segment_start = gf_bs_get_position(movie->editFileMap->bs);
                /*if movieFileMap is not null, we are concatenating segments to the original movie, force a copy*/
                if (movie->movieFileMap)
                        movie->append_segment = GF_TRUE;
        }

        /*create a memory bitstream for all file IO until final flush*/
        if (memory_mode) {
                movie->segment_bs = movie->editFileMap->bs;
                movie->editFileMap->bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
        }
        return GF_OK;
}

GF_EXPORT
GF_Err gf_isom_set_fragment_reference_time(GF_ISOFile *movie, u32 reference_track_ID, u64 ntp, u64 timestamp)
{
        if (!movie->moof) return GF_BAD_PARAM;
        movie->moof->reference_track_ID = reference_track_ID;
        movie->moof->ntp = ntp;
        movie->moof->timestamp = timestamp;
        return GF_OK;
}

GF_EXPORT
GF_Err gf_isom_set_traf_mss_timeext(GF_ISOFile *movie, u32 reference_track_ID, u64 ntp_in_track_timescale, u64 traf_duration_in_track_timescale)
{
        u32 i;
        if (!movie || !movie->moof)
                return GF_BAD_PARAM;
        for (i=0; i<gf_list_count(movie->moof->TrackList); i++) {
                GF_TrackFragmentBox *traf = (GF_TrackFragmentBox*)gf_list_get(movie->moof->TrackList, i);
                if (!traf)
                        return GF_BAD_PARAM;
                if (traf->tfxd)
                        gf_isom_box_del((GF_Box*)traf->tfxd);
                traf->tfxd = (GF_MSSTimeExtBox *)gf_isom_box_new(GF_ISOM_BOX_UUID_TFXD);
                traf->tfxd->absolute_time_in_track_timescale = ntp_in_track_timescale;
                traf->tfxd->fragment_duration_in_track_timescale = traf_duration_in_track_timescale;
        }
        return GF_OK;
}

GF_EXPORT
GF_Err gf_isom_start_fragment(GF_ISOFile *movie, Bool moof_first)
{
        u32 i, count;
        GF_TrackExtendsBox *trex;
        GF_TrackFragmentBox *traf;
        GF_Err e;
        //and only at setup
        if (!movie || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) )
                return GF_BAD_PARAM;
        if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE;

        count = gf_list_count(movie->moov->mvex->TrackExList);
        if (!count)
                return GF_BAD_PARAM;

        /*always force cached mode when writing movie segments*/
        if (movie->use_segments) moof_first = GF_TRUE;
        movie->moof_first = moof_first;

        //store existing fragment
        if (movie->moof) {
                e = StoreFragment(movie, movie->use_segments ? GF_TRUE : GF_FALSE, 0, NULL);
                if (e) return e;
        }

        //create new fragment
        movie->moof = (GF_MovieFragmentBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MOOF);
        movie->moof->mfhd = (GF_MovieFragmentHeaderBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MFHD);
        movie->moof->mfhd->sequence_number = movie->NextMoofNumber;
        movie->NextMoofNumber ++;
        if (movie->use_segments)
                gf_list_add(movie->moof_list, movie->moof);


        /*remember segment offset*/
        movie->moof->fragment_offset = gf_bs_get_position(movie->editFileMap->bs);
        /*prepare MDAT*/
        gf_bs_write_u32(movie->editFileMap->bs, 0);
        gf_bs_write_u32(movie->editFileMap->bs, GF_ISOM_BOX_TYPE_MDAT);

        //we create a TRAF for each setup track, unused ones will be removed at store time
        for (i=0; i<count; i++) {
                trex = (GF_TrackExtendsBox*)gf_list_get(movie->moov->mvex->TrackExList, i);
                traf = (GF_TrackFragmentBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_TRAF);
                traf->trex = trex;
                traf->tfhd = (GF_TrackFragmentHeaderBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_TFHD);
                traf->tfhd->trackID = trex->trackID;
                //add 8 bytes (MDAT size+type) to avoid the data_offset in the first trun
                traf->tfhd->base_data_offset = movie->moof->fragment_offset + 8;
                gf_list_add(movie->moof->TrackList, traf);
        }

        return GF_OK;
}

u32 GetRunSize(GF_TrackFragmentRunBox *trun)
{
        u32 i, size;
        GF_TrunEntry *ent;
        size = 0;
        i=0;
        while ((ent = (GF_TrunEntry*)gf_list_enum(trun->entries, &i))) {
                size += ent->size;
        }
        return size;
}

GF_EXPORT
GF_Err gf_isom_fragment_add_sample(GF_ISOFile *movie, u32 TrackID, const GF_ISOSample *sample, u32 DescIndex,
                                   u32 Duration, u8 PaddingBits, u16 DegradationPriority, Bool redundant_coding)
{
        u32 count, buffer_size;
        char *buffer;
        u64 pos;
        GF_ISOSample *od_sample = NULL;
        GF_TrunEntry *ent;
        GF_TrackFragmentBox *traf, *traf_2;
        GF_TrackFragmentRunBox *trun;
        if (!movie->moof || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) || !sample)
                return GF_BAD_PARAM;

        traf = GetTraf(movie, TrackID);
        if (!traf)
                return GF_BAD_PARAM;

        if (!traf->tfhd->sample_desc_index) traf->tfhd->sample_desc_index = DescIndex ? DescIndex : traf->trex->def_sample_desc_index;

        pos = gf_bs_get_position(movie->editFileMap->bs);


        //WARNING: we change stream description, create a new TRAF
        if ( DescIndex && (traf->tfhd->sample_desc_index != DescIndex)) {
                //if we're caching flush the current run
                if (traf->DataCache) {
                        count = gf_list_count(traf->TrackRuns);
                        if (count) {
                                trun = (GF_TrackFragmentRunBox *)gf_list_get(traf->TrackRuns, count-1);
                                trun->data_offset = (u32) (pos - movie->moof->fragment_offset - 8);
                                gf_bs_get_content(trun->cache, &buffer, &buffer_size);
                                gf_bs_write_data(movie->editFileMap->bs, buffer, buffer_size);
                                gf_bs_del(trun->cache);
                                trun->cache = NULL;
                                gf_free(buffer);
                        }
                }
                traf_2 = (GF_TrackFragmentBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_TRAF);
                traf_2->trex = traf->trex;
                traf_2->tfhd = (GF_TrackFragmentHeaderBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_TFHD);
                traf_2->tfhd->trackID = traf->tfhd->trackID;
                //keep the same offset
                traf_2->tfhd->base_data_offset = movie->moof->fragment_offset + 8;
                gf_list_add(movie->moof->TrackList, traf_2);

                //duplicate infos
                traf_2->tfhd->IFrameSwitching = traf->tfhd->IFrameSwitching;
                traf_2->DataCache  = traf->DataCache;
                traf_2->tfhd->sample_desc_index  = DescIndex;

                //switch them ...
                traf = traf_2;
        }

        pos = gf_bs_get_position(movie->editFileMap->bs);
        //add TRUN entry
        count = gf_list_count(traf->TrackRuns);
        if (count) {
                trun = (GF_TrackFragmentRunBox *)gf_list_get(traf->TrackRuns, count-1);
                //check data offset when no caching as trun entries shall ALWAYS be contiguous samples
                if (!traf->DataCache && (movie->moof->fragment_offset + 8 + trun->data_offset + GetRunSize(trun) != pos) )
                        count = 0;

                //check I-frame detection
                if (traf->tfhd->IFrameSwitching && sample->IsRAP)
                        count = 0;

                if (traf->DataCache && (traf->DataCache==trun->sample_count) )
                        count = 0;

                //if data cache is on and we're changing TRUN, store the cache and update data offset
                if (!count && traf->DataCache) {
                        trun->data_offset = (u32) (pos - movie->moof->fragment_offset - 8);
                        gf_bs_get_content(trun->cache, &buffer, &buffer_size);
                        gf_bs_write_data(movie->editFileMap->bs, buffer, buffer_size);
                        gf_bs_del(trun->cache);
                        trun->cache = NULL;
                        gf_free(buffer);
                }
        }

        //new run
        if (!count) {
                trun = (GF_TrackFragmentRunBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_TRUN);
                //store data offset (we have the 8 btyes offset of the MDAT)
                trun->data_offset = (u32) (pos - movie->moof->fragment_offset - 8);
                gf_list_add(traf->TrackRuns, trun);

                //if we use data caching, create a bitstream
                if (traf->DataCache)
                        trun->cache = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
        }

        GF_SAFEALLOC(ent, GF_TrunEntry);
        if (!ent) return GF_OUT_OF_MEM;
        ent->CTS_Offset = sample->CTS_Offset;
        ent->Duration = Duration;
        ent->size = sample->dataLength;
        ent->flags = GF_ISOM_FORMAT_FRAG_FLAGS(PaddingBits, sample->IsRAP, DegradationPriority);
        if (sample->IsRAP) {
                ent->flags |= GF_ISOM_GET_FRAG_DEPEND_FLAGS(0, 2, 0, (redundant_coding ? 1 : 0) );
                ent->SAP_type = sample->IsRAP;
        }
        gf_list_add(trun->entries, ent);

        if (sample->CTS_Offset<0) {
                trun->version = 1;
        }
        trun->sample_count += 1;

        //rewrite OD frames
        if (traf->trex->track->Media->handler->handlerType == GF_ISOM_MEDIA_OD) {
                //this may fail if depandancies are not well done ...
                Media_ParseODFrame(traf->trex->track->Media, sample, &od_sample);
                sample = od_sample;
        }

        //finally write the data
        if (sample->dataLength) {
                if (!traf->DataCache) {
                        if (!gf_bs_write_data(movie->editFileMap->bs, sample->data, sample->dataLength)) {
                                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));
                                return GF_OUT_OF_MEM;
                        }
                } else if (trun->cache) {
                        if (!gf_bs_write_data(trun->cache, sample->data, sample->dataLength)) {
                                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));
                                return GF_OUT_OF_MEM;
                        }
                } else {
                        return GF_BAD_PARAM;
                }
        }
        if (od_sample) gf_isom_sample_del(&od_sample);
        return GF_OK;
}

GF_EXPORT
GF_Err gf_isom_fragment_add_sai(GF_ISOFile *output, GF_ISOFile *input, u32 TrackID, u32 SampleNum)
{
        u32 trackNum;
        GF_Err e = GF_OK;

        trackNum = gf_isom_get_track_by_id(input, TrackID);
        if (gf_isom_is_cenc_media(input, trackNum, 1)) {
                GF_CENCSampleAuxInfo *sai;
                GF_TrackFragmentBox  *traf = GetTraf(output, TrackID);
                GF_TrackBox  *src_trak = gf_isom_get_track_from_file(input, TrackID);
                u32 boxType;
                GF_SampleEncryptionBox *senc;
                u8 IV_size;
                u32 IsEncrypted;

                if (!traf)  return GF_BAD_PARAM;

                sai = NULL;
                gf_isom_get_sample_cenc_info(input, trackNum, SampleNum, &IsEncrypted, &IV_size, NULL, NULL, NULL, NULL, NULL);
                e = gf_isom_cenc_get_sample_aux_info(input, trackNum, SampleNum, &sai, &boxType);
                if (e) return e;
                sai->IV_size = IV_size;

                switch (boxType) {
                case GF_ISOM_BOX_UUID_PSEC:
                        //have we seen cases where the psec box is in trak but not in traf ? to check
                        if (!traf->sample_encryption) {
                                GF_SampleEncryptionBox *psec = (GF_SampleEncryptionBox *) src_trak->sample_encryption;
                                if (!psec) return GF_ISOM_INVALID_FILE;
                                traf->sample_encryption = gf_isom_create_piff_psec_box(1, 0, psec->AlgorithmID, psec->IV_size, psec->KID);
                                if (!traf->sample_encryption)
                                        return GF_OUT_OF_MEM;
                                traf->sample_encryption->traf = traf;
                        }

                        senc = (GF_SampleEncryptionBox *) traf->sample_encryption;
                        break;
                case GF_ISOM_BOX_TYPE_SENC:
                        if (!traf->sample_encryption) {
                                traf->sample_encryption = gf_isom_create_samp_enc_box(0, 0);
                                traf->sample_encryption->traf = traf;
                        }

                        if (!traf->sample_encryption) {
                                return GF_IO_ERR;
                        }
                        senc = (GF_SampleEncryptionBox *) traf->sample_encryption;
                        break;
                default:
                        return GF_NOT_SUPPORTED;
                }

                gf_list_add(senc->samp_aux_info, sai);
                if (sai->subsample_count) senc->flags = 0x00000002;
                //this assumes that we don't have a cenc sample info with subsamples indicated and subsample_count=0
                gf_isom_cenc_set_saiz_saio(senc, NULL, traf, IsEncrypted ? IV_size + (2+6)*sai->subsample_count : 0);
        }

        return GF_OK;
}


GF_Err gf_isom_fragment_append_data(GF_ISOFile *movie, u32 TrackID, char *data, u32 data_size, u8 PaddingBits)
{
        u32 count;
        u8 rap;
        u16 degp;
        GF_TrunEntry *ent;
        GF_TrackFragmentBox *traf;
        GF_TrackFragmentRunBox *trun;
        if (!movie->moof || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) return GF_BAD_PARAM;

        traf = GetTraf(movie, TrackID);
        if (!traf || !traf->tfhd->sample_desc_index) return GF_BAD_PARAM;

        //add TRUN entry
        count = gf_list_count(traf->TrackRuns);
        if (!count) return GF_BAD_PARAM;

        trun = (GF_TrackFragmentRunBox *)gf_list_get(traf->TrackRuns, count-1);
        count = gf_list_count(trun->entries);
        if (!count) return GF_BAD_PARAM;
        ent = (GF_TrunEntry *)gf_list_get(trun->entries, count-1);
        ent->size += data_size;

        rap = GF_ISOM_GET_FRAG_SYNC(ent->flags);
        degp = GF_ISOM_GET_FRAG_DEG(ent->flags);
        ent->flags = GF_ISOM_FORMAT_FRAG_FLAGS(PaddingBits, rap, degp);

        //finally write the data
        if (!traf->DataCache) {
                gf_bs_write_data(movie->editFileMap->bs, data, data_size);
        } else if (trun->cache) {
                gf_bs_write_data(trun->cache, data, data_size);
        } else {
                return GF_BAD_PARAM;
        }
        return GF_OK;
}

GF_Err gf_isom_fragment_add_subsample(GF_ISOFile *movie, u32 TrackID, u32 flags, u32 subSampleSize, u8 priority, u32 reserved, Bool discardable)
{
        u32 i, count, last_sample;
        GF_TrackFragmentBox *traf;
        GF_SubSampleInformationBox *subs = NULL;
        if (!movie->moof || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) return GF_BAD_PARAM;

        traf = GetTraf(movie, TrackID);
        if (!traf || !traf->tfhd->sample_desc_index) return GF_BAD_PARAM;

        /*compute last sample number in traf*/
        last_sample = 0;
        count = gf_list_count(traf->TrackRuns);
        for (i=0; i<count; i++) {
                GF_TrackFragmentRunBox *trun = (GF_TrackFragmentRunBox*)gf_list_get(traf->TrackRuns, i);
                last_sample += trun->sample_count;
        }

        if (!traf->sub_samples) {
                traf->sub_samples = gf_list_new();
        }
        count = gf_list_count(traf->sub_samples);
        for (i=0; i<count;i++) {
                subs = gf_list_get(traf->sub_samples, i);
                if (subs->flags==flags) break;
                subs=NULL;
        }
        if (!subs) {
                subs = (GF_SubSampleInformationBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_SUBS);
                subs->version = (subSampleSize>0xFFFF) ? 1 : 0;
                subs->flags = flags;
        }
        return gf_isom_add_subsample_info(subs, last_sample, subSampleSize, priority, reserved, discardable);
}

GF_Err gf_isom_fragment_copy_subsample(GF_ISOFile *dest, u32 TrackID, GF_ISOFile *orig, u32 track, u32 sampleNumber, Bool sgpd_in_traf)
{
        u32 i, count, last_sample, idx, subs_flags;
        GF_SubSampleInfoEntry *sub_sample;
        GF_Err e;
        GF_TrackBox *trak;
        GF_TrackFragmentBox *traf;
        GF_TrunEntry *ent;
        GF_TrackFragmentRunBox *trun;
        if (!dest->moof || !(dest->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) return GF_BAD_PARAM;

        traf = GetTraf(dest, TrackID);
        if (!traf || !traf->tfhd->sample_desc_index) return GF_BAD_PARAM;

        trak = gf_isom_get_track_from_file(orig, track);
        if (!trak) return GF_BAD_PARAM;

        /*modify depends flags*/
        if (trak->Media->information->sampleTable->SampleDep) {
                u32 isLeading, dependsOn, dependedOn, redundant;

                isLeading = dependsOn = dependedOn = redundant = 0;
                count = gf_list_count(traf->TrackRuns);
                if (!count) return GF_BAD_PARAM;
                trun = (GF_TrackFragmentRunBox *)gf_list_get(traf->TrackRuns, count-1);
                count = gf_list_count(trun->entries);
                if (!count) return GF_BAD_PARAM;

                ent = (GF_TrunEntry *)gf_list_get(trun->entries, count-1);
                e = stbl_GetSampleDepType(trak->Media->information->sampleTable->SampleDep, sampleNumber, &isLeading, &dependsOn, &dependedOn, &redundant);
                if (e) return e;

                GF_ISOM_RESET_FRAG_DEPEND_FLAGS(ent->flags);
                ent->flags |= GF_ISOM_GET_FRAG_DEPEND_FLAGS(0, dependsOn, dependedOn, redundant);
        }

        /*copy subsample info if any*/
        idx=1;
        while (gf_isom_get_subsample_types(orig, track, idx, &subs_flags)) {
                GF_SubSampleInformationBox *subs_traf=NULL;
                idx++;
                if (! gf_isom_sample_get_subsample_entry(orig, track, sampleNumber, subs_flags, &sub_sample))
                        continue;

                if (!traf || !traf->tfhd->sample_desc_index) return GF_BAD_PARAM;

                /*compute last sample number in traf*/
                last_sample = 0;
                count = gf_list_count(traf->TrackRuns);
                for (i=0; i<count; i++) {
                        GF_TrackFragmentRunBox *trun = (GF_TrackFragmentRunBox*)gf_list_get(traf->TrackRuns, i);
                        last_sample += trun->sample_count;
                }

                /*create subsample if needed*/
                if (!traf->sub_samples) {
                        traf->sub_samples = gf_list_new();
                }
                count = gf_list_count(traf->sub_samples);
                for (i=0; i<count; i++) {
                        subs_traf = gf_list_get(traf->sub_samples, i);
                        if (subs_traf->flags==subs_flags) break;
                        subs_traf = NULL;
                }
                if (!subs_traf) {
                        subs_traf = (GF_SubSampleInformationBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_SUBS);
                        subs_traf->version = 0;
                        subs_traf->flags = subs_flags;
                        gf_list_add(traf->sub_samples, subs_traf);
                }

                count = gf_list_count(sub_sample->SubSamples);
                for (i=0; i<count; i++) {
                        GF_SubSampleEntry *entry = (GF_SubSampleEntry*)gf_list_get(sub_sample->SubSamples, i);
                        e = gf_isom_add_subsample_info(subs_traf, last_sample, entry->subsample_size, entry->subsample_priority, entry->reserved, entry->discardable);
                        if (e) return e;
                }
        }
        /*copy sampleToGroup info if any*/
        if (trak->Media->information->sampleTable->sampleGroups) {
                count = gf_list_count(trak->Media->information->sampleTable->sampleGroups);
                for (i=0; i<count; i++) {
                        GF_SampleGroupBox *sg;
                        u32 j;
                        u32 first_sample_in_entry, last_sample_in_entry;
                        first_sample_in_entry = 1;

                        sg = (GF_SampleGroupBox*)gf_list_get(trak->Media->information->sampleTable->sampleGroups, i);
                        for (j=0; j<sg->entry_count; j++) {
                                last_sample_in_entry = first_sample_in_entry + sg->sample_entries[j].sample_count - 1;
                                if ((sampleNumber<first_sample_in_entry) || (sampleNumber>last_sample_in_entry)) {
                                        first_sample_in_entry = last_sample_in_entry+1;
                                        continue;
                                }

                                if (!traf->sampleGroups)
                                        traf->sampleGroups = gf_list_new();

                                /*found our sample, add it to trak->sampleGroups*/
                                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);
                                if (e) return e;
                                
                                break;
                        }
                }
        }
        return GF_OK;
}


#endif  /*GPAC_DISABLE_ISOM_WRITE*/

GF_EXPORT
u32 gf_isom_is_track_fragmented(GF_ISOFile *movie, u32 TrackID)
{
        if (!movie || !movie->moov || !movie->moov->mvex) return 0;
        return (GetTrex(movie->moov, TrackID) != NULL) ? 1 : 0;
}

GF_EXPORT
u32 gf_isom_is_fragmented(GF_ISOFile *movie)
{
        if (!movie || !movie->moov) return 0;
        /* By default if the Moov has an mvex, the file is fragmented */
        if (movie->moov->mvex) return 1;
        /* deprecated old code */
        /*only check moof number (in read mode, mvex can be deleted on the fly)*/
        //return movie->NextMoofNumber ? 1 : 0;
        return 0;
}

GF_EXPORT
GF_Err gf_isom_set_traf_base_media_decode_time(GF_ISOFile *movie, u32 TrackID, u64 decode_time)
{
        GF_TrackFragmentBox *traf;
        if (!movie || !movie->moof || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) return GF_BAD_PARAM;

        traf = GetTraf(movie, TrackID);
        if (!traf) return GF_BAD_PARAM;

        if (!traf->tfdt) {
                traf->tfdt = (GF_TFBaseMediaDecodeTimeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_TFDT);
                if (!traf->tfdt) return GF_OUT_OF_MEM;
        }
        traf->tfdt->baseMediaDecodeTime = decode_time;
        return GF_OK;
}

#else

GF_Err gf_isom_finalize_for_fragment(GF_ISOFile *the_file, u32 media_segment_type)
{
        return GF_NOT_SUPPORTED;
}

GF_Err gf_isom_setup_track_fragment(GF_ISOFile *the_file, u32 TrackID,
                                    u32 DefaultSampleDescriptionIndex,
                                    u32 DefaultSampleDuration,
                                    u32 DefaultSampleSize,
                                    u8 DefaultSampleIsSync,
                                    u8 DefaultSamplePadding,
                                    u16 DefaultDegradationPriority)
{
        return GF_NOT_SUPPORTED;
}

GF_Err gf_isom_set_fragment_option(GF_ISOFile *the_file, u32 TrackID, u32 Code, u32 Param)
{
        return GF_NOT_SUPPORTED;
}

GF_Err gf_isom_start_fragment(GF_ISOFile *the_file, u32 free_data_insert_size)
{
        return GF_NOT_SUPPORTED;
}

GF_Err gf_isom_fragment_add_sample(GF_ISOFile *the_file, u32 TrackID, const GF_ISOSample *sample, u32 DescIndex,
                                   u32 Duration, u8 PaddingBits, u16 DegradationPriority, Bool redCoded)
{
        return GF_NOT_SUPPORTED;
}


GF_EXPORT
u32 gf_isom_is_track_fragmented(GF_ISOFile *the_file, u32 TrackID)
{
        return 0;
}

GF_EXPORT
u32 gf_isom_is_fragmented(GF_ISOFile *the_file)
{
        return 0;
}

GF_Err gf_isom_fragment_add_subsample(GF_ISOFile *movie, u32 TrackID, u32 flags, u32 subSampleSize, u8 priority, u32 reserved, Bool discardable)
{
        return GF_NOT_SUPPORTED;
}

GF_Err gf_isom_fragment_copy_subsample(GF_ISOFile *dest, u32 TrackID, GF_ISOFile *orig, u32 track, u32 sampleNumber, Bool sgpd_in_traf)
{
        return GF_NOT_SUPPORTED;
}

GF_Err gf_isom_set_traf_base_media_decode_time(GF_ISOFile *movie, u32 TrackID, u64 decode_time)
{
        return GF_NOT_SUPPORTED;
}

GF_Err gf_isom_set_traf_mss_timeext(GF_ISOFile *movie, u32 reference_track_ID, u64 ntp_in_10mhz, u64 traf_duration_in_10mhz)
{
        return GF_NOT_SUPPORTED;
}

#endif /*GPAC_DISABLE_ISOM_FRAGMENTS)*/


GF_EXPORT
void gf_isom_set_next_moof_number(GF_ISOFile *movie, u32 value)
{
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
        if (movie) movie->NextMoofNumber = value;
#endif
}

GF_EXPORT
u32 gf_isom_get_next_moof_number(GF_ISOFile *movie)
{
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
        if (movie) return movie->NextMoofNumber;
#endif
        return 0;
}

#endif /*GPAC_DISABLE_ISOM*/

/* [<][>][^][v][top][bottom][index][help] */