root/src/isomedia/stbl_write.c

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

DEFINITIONS

This source file includes following definitions.
  1. stbl_AddDTS
  2. AddCompositionOffset
  3. stbl_AddCTS
  4. stbl_repackCTS
  5. stbl_unpackCTS
  6. stbl_AddSize
  7. stbl_AddRAP
  8. stbl_AddRedundant
  9. stbl_AppendDependencyType
  10. stbl_AddShadow
  11. stbl_AddChunkOffset
  12. stbl_SetChunkOffset
  13. stbl_SetSampleCTS
  14. stbl_SetSampleSize
  15. stbl_SetSampleRAP
  16. stbl_SetRedundant
  17. stbl_SetSyncShadow
  18. stbl_RemoveDTS
  19. stbl_RemoveCTS
  20. stbl_RemoveSize
  21. stbl_RemoveChunk
  22. stbl_RemoveRAP
  23. stbl_RemoveRedundant
  24. stbl_RemoveShadow
  25. stbl_SetPaddingBits
  26. stbl_RemovePaddingBits
  27. stbl_RemoveSubSample
  28. stbl_RemoveSampleGroup
  29. stbl_AddSampleFragment
  30. stbl_RemoveSampleFragments
  31. stbl_SampleSizeAppend
  32. stbl_AppendTime
  33. stbl_AppendSize
  34. stbl_AppendChunk
  35. stbl_AppendSampleToChunk
  36. stbl_AppendRAP
  37. stbl_AppendPadding
  38. stbl_AppendCTSOffset
  39. stbl_AppendDegradation
  40. stbl_AppendDepType
  41. stbl_UnpackOffsets
  42. stbl_AddOffset
  43. stbl_SetChunkAndOffset
  44. gf_isom_refresh_size_info

/*
 *                      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

/*macro used for table gf_realloc - we allocate much more than needed in order to keep the number of
gf_realloc low, which greatly impacts performances for large files*/
#define ALLOC_INC(a)    a = ((a<10) ? 100 : (a*3)/2);

#ifndef GPAC_DISABLE_ISOM_WRITE

//adds a DTS in the table and get the sample number of this new sample
//we could return an error if a sample with the same DTS already exists
//but this is not true for QT or MJ2K, only for MP4...
//we assume the authoring tool tries to create a compliant MP4 file.
GF_Err stbl_AddDTS(GF_SampleTableBox *stbl, u64 DTS, u32 *sampleNumber, u32 LastAUDefDuration)
{
        u32 i, j, sampNum;
        u64 *DTSs, curDTS;
        Bool inserted;
        GF_SttsEntry *ent;

        GF_TimeToSampleBox *stts = stbl->TimeToSample;

        //reset the reading cache when adding a sample
        stts->r_FirstSampleInEntry = 0;

        *sampleNumber = 0;
        //if we don't have an entry, that's the first one...
        if (!stts->nb_entries) {
                //assert the first DTS is 0. If not, that will break the whole file
                if (DTS) return GF_BAD_PARAM;
                stts->alloc_size = 1;
                stts->nb_entries = 1;
                stts->entries = gf_malloc(sizeof(GF_SttsEntry));
                if (!stts->entries) return GF_OUT_OF_MEM;
                stts->entries[0].sampleCount = 1;
                stts->entries[0].sampleDelta = LastAUDefDuration;
                stts->w_currentSampleNum = (*sampleNumber) = 1;
                return GF_OK;
        }

        //check the last DTS...
        if (DTS > stts->w_LastDTS) {
                ent = &stts->entries[stts->nb_entries-1];
                //OK, we're adding at the end
                if (DTS == stts->w_LastDTS + ent->sampleDelta) {
                        ent->sampleCount ++;
                        stts->w_currentSampleNum ++;
                        (*sampleNumber) = stts->w_currentSampleNum;
                        stts->w_LastDTS = DTS;
                        return GF_OK;
                }
                //we need to split the entry
                if (ent->sampleCount == 1) {
                        //FIXME - we need more tests with timed text
#if 0
                        if (stts->w_LastDTS)
                                ent->sampleDelta += (u32) (DTS - stts->w_LastDTS);
                        else
                                ent->sampleDelta = (u32) DTS;
#else
                        //use this one and adjust...
                        ent->sampleDelta = (u32) (DTS - stts->w_LastDTS);
#endif
                        ent->sampleCount ++;
                        stts->w_currentSampleNum ++;
                        stts->w_LastDTS = DTS;
                        (*sampleNumber) = stts->w_currentSampleNum;
                        return GF_OK;
                }
                //we definitely need to split the entry ;)
                ent->sampleCount --;
                if (stts->alloc_size==stts->nb_entries) {
                        ALLOC_INC(stts->alloc_size);
                        stts->entries = gf_realloc(stts->entries, sizeof(GF_SttsEntry)*stts->alloc_size);
                        if (!stts->entries) return GF_OUT_OF_MEM;
                        memset(&stts->entries[stts->nb_entries], 0, sizeof(GF_SttsEntry)*(stts->alloc_size-stts->nb_entries) );
                }
                ent = &stts->entries[stts->nb_entries];
                stts->nb_entries++;

                ent->sampleCount = 2;
                ent->sampleDelta = (u32) (DTS - stts->w_LastDTS);
                stts->w_LastDTS = DTS;
                stts->w_currentSampleNum ++;
                (*sampleNumber) = stts->w_currentSampleNum;
                return GF_OK;
        }


        //unpack the DTSs and locate new sample...
        DTSs = (u64*)gf_malloc(sizeof(u64) * (stbl->SampleSize->sampleCount+2) );
        if (!DTSs) return GF_OUT_OF_MEM;
        curDTS = 0;
        sampNum = 0;
        ent = NULL;
        inserted = 0;
        for (i=0; i<stts->nb_entries; i++) {
                ent = & stts->entries[i];
                for (j = 0; j<ent->sampleCount; j++) {
                        if (!inserted && (curDTS > DTS)) {
                                DTSs[sampNum] = DTS;
                                sampNum++;
                                *sampleNumber = sampNum;
                                inserted = 1;
                        }
                        DTSs[sampNum] = curDTS;
                        curDTS += ent->sampleDelta;
                        sampNum ++;
                }
        }
        if (!inserted) {
                gf_free(DTSs);
                return GF_BAD_PARAM;
        }

        /*we will at most insert 2 new entries*/
        if (stts->nb_entries+2 >= stts->alloc_size) {
                stts->alloc_size += 2;
                stts->entries = gf_realloc(stts->entries, sizeof(GF_SttsEntry)*stts->alloc_size);
                if (!stts->entries) return GF_OUT_OF_MEM;
                memset(&stts->entries[stts->nb_entries], 0, sizeof(GF_SttsEntry)*(stts->alloc_size - stts->nb_entries) );
        }

        /*repack the DTSs*/
        j=0;
        stts->nb_entries = 1;
        stts->entries[0].sampleCount = 1;
        stts->entries[0].sampleDelta = (u32) DTSs[1] /* - (DTS[0] wichis 0)*/;
        for (i=1; i<stbl->SampleSize->sampleCount+1; i++) {
                if (i == stbl->SampleSize->sampleCount) {
                        //and by default, our last sample has the same delta as the prev
                        stts->entries[j].sampleCount++;
                } else if (stts->entries[j].sampleDelta == (u32) ( DTSs[i+1] - DTSs[i]) ) {
                        stts->entries[j].sampleCount ++;
                } else {
                        stts->nb_entries ++;
                        j++;
                        stts->entries[j].sampleCount = 1;
                        stts->entries[j].sampleDelta = (u32) (DTSs[i+1] - DTSs[i]);
                }
        }
        gf_free(DTSs);

        //reset the cache to the end
        stts->w_currentSampleNum = stbl->SampleSize->sampleCount + 1;
        return GF_OK;
}

GF_Err AddCompositionOffset(GF_CompositionOffsetBox *ctts, s32 offset)
{
        if (!ctts) return GF_BAD_PARAM;

        if (ctts->nb_entries && (ctts->entries[ctts->nb_entries-1].decodingOffset==offset)) {
                ctts->entries[ctts->nb_entries-1].sampleCount++;
        } else {
                if (ctts->alloc_size==ctts->nb_entries) {
                        ALLOC_INC(ctts->alloc_size);
                        ctts->entries = gf_realloc(ctts->entries, sizeof(GF_DttsEntry)*ctts->alloc_size);
                        if (!ctts->entries) return GF_OUT_OF_MEM;
                        memset(&ctts->entries[ctts->nb_entries], 0, sizeof(GF_DttsEntry)*(ctts->alloc_size-ctts->nb_entries) );
                }
                if (!ctts->entries) return GF_OUT_OF_MEM;
                
                ctts->entries[ctts->nb_entries].decodingOffset = offset;
                ctts->entries[ctts->nb_entries].sampleCount = 1;
                ctts->nb_entries++;
        }
        if (offset<0) ctts->version=1;
        ctts->w_LastSampleNumber++;
        return GF_OK;
}

//adds a CTS offset for a new sample
GF_Err stbl_AddCTS(GF_SampleTableBox *stbl, u32 sampleNumber, s32 offset)
{
        u32 i, j, sampNum, *CTSs;

        GF_CompositionOffsetBox *ctts = stbl->CompositionOffset;

        /*in unpack mode we're sure to have 1 ctts entry per sample*/
        if (ctts->unpack_mode) {
                if (ctts->nb_entries==ctts->alloc_size) {
                        ALLOC_INC(ctts->alloc_size);
                        ctts->entries = gf_realloc(ctts->entries, sizeof(GF_DttsEntry)*ctts->alloc_size);
                        if (!ctts->entries) return GF_OUT_OF_MEM;
                        memset(&ctts->entries[ctts->nb_entries], 0, sizeof(GF_DttsEntry)*(ctts->alloc_size - ctts->nb_entries) );
                }
                ctts->entries[ctts->nb_entries].decodingOffset = offset;
                ctts->entries[ctts->nb_entries].sampleCount = 1;
                ctts->nb_entries++;
                ctts->w_LastSampleNumber++;
                if (offset<0) ctts->version=1;
                return GF_OK;
        }
        //check if we're working in order...
        if (ctts->w_LastSampleNumber < sampleNumber) {
                //add some 0 till we get to the sample
                while (ctts->w_LastSampleNumber + 1 != sampleNumber) {
                        AddCompositionOffset(ctts, 0);
                }
                return AddCompositionOffset(ctts, offset);
        }

        //NOPE we are inserting a sample...
        CTSs = (u32*)gf_malloc(sizeof(u32) * (stbl->SampleSize->sampleCount+1) );
        if (!CTSs) return GF_OUT_OF_MEM;
        sampNum = 0;
        for (i=0; i<ctts->nb_entries; i++) {
                for (j = 0; j<ctts->entries[i].sampleCount; j++) {
                        if (sampNum > stbl->SampleSize->sampleCount) {
                                GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Too many CTS Offset entries for %d samples\n", stbl->SampleSize->sampleCount ));
                                gf_free(CTSs);
                                return GF_ISOM_INVALID_FILE;
                        }
                        if (sampNum+1==sampleNumber) {
                                CTSs[sampNum] = offset;
                                sampNum ++;
                        }
                        CTSs[sampNum] = ctts->entries[i].decodingOffset;
                        sampNum ++;
                }
        }

        /*we will at most add 2 new entries (splitting of an existing one)*/
        if (ctts->nb_entries+2>=ctts->alloc_size) {
                ctts->alloc_size += 2;
                ctts->entries = gf_realloc(ctts->entries, sizeof(GF_DttsEntry)*ctts->alloc_size);
                memset(&ctts->entries[ctts->nb_entries], 0, sizeof(GF_DttsEntry)*(ctts->alloc_size-ctts->nb_entries) );
        }

        ctts->entries[0].sampleCount = 1;
        ctts->entries[0].decodingOffset = CTSs[0];
        ctts->nb_entries = 1;
        j=0;
        for (i=1; i<stbl->SampleSize->sampleCount + 1; i++) {
                if (CTSs[i]==ctts->entries[j].decodingOffset) {
                        ctts->entries[j].sampleCount++;
                } else {
                        j++;
                        ctts->nb_entries++;
                        ctts->entries[j].sampleCount = 1;
                        ctts->entries[j].decodingOffset = CTSs[i];
                }
        }
        gf_free(CTSs);

        if (offset<0) ctts->version=1;

        /*we've inserted a sample, therefore the last sample (n) has now number n+1
        we cannot use SampleCount because we have probably skipped some samples
        (we're calling AddCTS only if the sample has a offset !!!)*/
        ctts->w_LastSampleNumber += 1;
        return GF_OK;
}

GF_Err stbl_repackCTS(GF_CompositionOffsetBox *ctts)
{
        u32 i, j;

        if (!ctts->unpack_mode) return GF_OK;
        ctts->unpack_mode = 0;

        j=0;
        for (i=1; i<ctts->nb_entries; i++) {
                if (ctts->entries[i].decodingOffset==ctts->entries[j].decodingOffset) {
                        ctts->entries[j].sampleCount++;
                } else {
                        j++;
                        ctts->entries[j].sampleCount = 1;
                        ctts->entries[j].decodingOffset = ctts->entries[i].decodingOffset;
                }
        }
        ctts->nb_entries=j+1;
        /*note we don't realloc*/
        return GF_OK;
}

GF_Err stbl_unpackCTS(GF_SampleTableBox *stbl)
{
        GF_DttsEntry *packed;
        u32 i, j, remain, count;
        GF_CompositionOffsetBox *ctts;
        ctts = stbl->CompositionOffset;
        if (!ctts || ctts->unpack_mode) return GF_OK;
        ctts->unpack_mode = 1;

        packed = ctts->entries;
        count = ctts->nb_entries;
        ctts->entries = NULL;
        ctts->nb_entries = 0;
        ctts->alloc_size = 0;
        for (i=0; i<count; i++) {
                for (j=0; j<packed[i].sampleCount; j++) {
                        if (ctts->nb_entries == ctts->alloc_size) {
                                ALLOC_INC(ctts->alloc_size);
                                ctts->entries = gf_realloc(ctts->entries, sizeof(GF_DttsEntry)*ctts->alloc_size);
                                memset(&ctts->entries[ctts->nb_entries], 0, sizeof(GF_DttsEntry)*(ctts->alloc_size-ctts->nb_entries) );
                        }
                        ctts->entries[ctts->nb_entries].decodingOffset = packed[i].decodingOffset;
                        ctts->entries[ctts->nb_entries].sampleCount = 1;
                        ctts->nb_entries++;
                }
        }
        gf_free(packed);

        remain = stbl->SampleSize->sampleCount - ctts->nb_entries;
        while (remain) {
                if (ctts->nb_entries == ctts->alloc_size) {
                        ALLOC_INC(ctts->alloc_size);
                        ctts->entries = gf_realloc(ctts->entries, sizeof(GF_DttsEntry)*ctts->alloc_size);
                        memset(&ctts->entries[ctts->nb_entries], 0, sizeof(GF_DttsEntry)*(ctts->alloc_size-ctts->nb_entries) );
                }
                ctts->entries[ctts->nb_entries].decodingOffset = 0;
                ctts->entries[ctts->nb_entries].sampleCount = 1;
                ctts->nb_entries++;
                remain--;
        }
        return GF_OK;
}

//add size
GF_Err stbl_AddSize(GF_SampleSizeBox *stsz, u32 sampleNumber, u32 size)
{
        u32 i, k;
        u32 *newSizes;
        if (!stsz /*|| !size */ || !sampleNumber) return GF_BAD_PARAM;

        if (sampleNumber > stsz->sampleCount + 1) return GF_BAD_PARAM;

        //all samples have the same size
        if (stsz->sizes == NULL) {
                //1 first sample added in NON COMPACT MODE
                if (! stsz->sampleCount && (stsz->type != GF_ISOM_BOX_TYPE_STZ2) ) {
                        stsz->sampleCount = 1;
                        stsz->sampleSize = size;
                        return GF_OK;
                }
                //2- sample has the same size
                if (stsz->sampleSize == size) {
                        stsz->sampleCount++;
                        return GF_OK;
                }
                //3- no, need to alloc a size table
                stsz->sizes = (u32*)gf_malloc(sizeof(u32) * (stsz->sampleCount + 1));
                if (!stsz->sizes) return GF_OUT_OF_MEM;
                stsz->alloc_size = stsz->sampleCount + 1;

                k = 0;
                for (i = 0 ; i < stsz->sampleCount; i++) {
                        if (i + 1 == sampleNumber) {
                                stsz->sizes[i + k] = size;
                                k = 1;
                        }
                        stsz->sizes[i+k] = stsz->sampleSize;
                }
                //this if we append a new sample
                if (stsz->sampleCount + 1 == sampleNumber) {
                        stsz->sizes[stsz->sampleCount] = size;
                }
                stsz->sampleSize = 0;
                stsz->sampleCount++;
                return GF_OK;
        }


        /*append*/
        if (stsz->sampleCount + 1 == sampleNumber) {
                if (!stsz->alloc_size) stsz->alloc_size = stsz->sampleCount;
                if (stsz->sampleCount == stsz->alloc_size) {
                        ALLOC_INC(stsz->alloc_size);
                        stsz->sizes = gf_realloc(stsz->sizes, sizeof(u32)*(stsz->alloc_size) );
                        if (!stsz->sizes) return GF_OUT_OF_MEM;
                        memset(&stsz->sizes[stsz->sampleCount], 0, sizeof(u32)*(stsz->alloc_size - stsz->sampleCount) );
                }
                stsz->sizes[stsz->sampleCount] = size;
        } else {
                newSizes = (u32*)gf_malloc(sizeof(u32)*(1 + stsz->sampleCount) );
                if (!newSizes) return GF_OUT_OF_MEM;
                k = 0;
                for (i = 0; i < stsz->sampleCount; i++) {
                        if (i + 1 == sampleNumber) {
                                newSizes[i + k] = size;
                                k = 1;
                        }
                        newSizes[i + k] = stsz->sizes[i];
                }
                gf_free(stsz->sizes);
                stsz->sizes = newSizes;
                stsz->alloc_size = 1 + stsz->sampleCount;
        }
        stsz->sampleCount++;
        return GF_OK;
}


GF_Err stbl_AddRAP(GF_SyncSampleBox *stss, u32 sampleNumber)
{
        u32 i, k;
        u32 *newNumbers;

        if (!stss || !sampleNumber) return GF_BAD_PARAM;

        if (stss->sampleNumbers == NULL) {
                ALLOC_INC(stss->alloc_size);
                stss->sampleNumbers = (u32*)gf_malloc(sizeof(u32)*stss->alloc_size);
                if (!stss->sampleNumbers) return GF_OUT_OF_MEM;
                stss->sampleNumbers[0] = sampleNumber;
                stss->nb_entries = 1;
                return GF_OK;
        }

        if (stss->sampleNumbers[stss->nb_entries-1] == sampleNumber) return GF_OK;

        if (stss->sampleNumbers[stss->nb_entries-1] < sampleNumber) {
                if (stss->nb_entries==stss->alloc_size) {
                        ALLOC_INC(stss->alloc_size);
                        stss->sampleNumbers = gf_realloc(stss->sampleNumbers, sizeof(u32) * stss->alloc_size);
                        if (!stss->sampleNumbers) return GF_OUT_OF_MEM;
                        memset(&stss->sampleNumbers[stss->nb_entries], 0, sizeof(u32) * (stss->alloc_size-stss->nb_entries) );
                }
                stss->sampleNumbers[stss->nb_entries] = sampleNumber;
        } else {
                newNumbers = (u32*)gf_malloc(sizeof(u32) * (stss->nb_entries + 1));
                if (!newNumbers) return GF_OUT_OF_MEM;
                //the table is in increasing order of sampleNumber
                k = 0;
                for (i = 0; i < stss->nb_entries; i++) {
                        if (stss->sampleNumbers[i] >= sampleNumber) {
                                newNumbers[i + k] = sampleNumber;
                                k = 1;
                        }
                        newNumbers[i + k] = stss->sampleNumbers[i] + k;
                }
                gf_free(stss->sampleNumbers);
                stss->sampleNumbers = newNumbers;
                stss->alloc_size = stss->nb_entries+1;
        }
        //update our list
        stss->nb_entries ++;
        return GF_OK;
}

GF_Err stbl_AddRedundant(GF_SampleTableBox *stbl, u32 sampleNumber)
{
        GF_SampleDependencyTypeBox *sdtp;

        if (stbl->SampleDep == NULL) {
                stbl->SampleDep = (GF_SampleDependencyTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_SDTP);
                if (!stbl->SampleDep) return GF_OUT_OF_MEM;
        }
        sdtp = stbl->SampleDep;
        if (sdtp->sampleCount + 1 < sampleNumber) {
                u32 missed = sampleNumber-1 - sdtp->sampleCount;
                sdtp->sample_info = (u8*) gf_realloc(sdtp->sample_info, sizeof(u8) * (sdtp->sampleCount+missed) );
                memset(&sdtp->sample_info[sdtp->sampleCount], 0, sizeof(u8) * missed );
                while (missed) {
                        SAPType isRAP;
                        if (stbl->SyncSample) stbl_GetSampleRAP(stbl->SyncSample, sdtp->sampleCount+1, &isRAP, NULL, NULL);
                        else isRAP = 1;
                        sdtp->sample_info[sdtp->sampleCount] = isRAP ? 0x20 : 0;
                        sdtp->sampleCount++;
                        missed--;
                }
        }

        sdtp->sample_info = (u8*) gf_realloc(sdtp->sample_info, sizeof(u8) * (sdtp->sampleCount + 1));
        if (!sdtp->sample_info) return GF_OUT_OF_MEM;
        if (sdtp->sampleCount < sampleNumber) {
                sdtp->sample_info[sdtp->sampleCount] = 0x29;
        } else {
                u32 snum = sampleNumber-1;
                memmove(sdtp->sample_info+snum+1, sdtp->sample_info+snum, sizeof(u8) * (sdtp->sampleCount - snum) );
                sdtp->sample_info[snum] = 0x29;
        }
        //update our list
        sdtp->sampleCount ++;
        return GF_OK;
}

GF_Err stbl_AppendDependencyType(GF_SampleTableBox *stbl, u32 isLeading, u32 dependsOn, u32 dependedOn, u32 redundant)
{
        GF_SampleDependencyTypeBox *sdtp;
        u32 flags;
        if (stbl->SampleDep == NULL) {
                stbl->SampleDep = (GF_SampleDependencyTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_SDTP);
                if (!stbl->SampleDep) return GF_OUT_OF_MEM;
        }
        sdtp = stbl->SampleDep;

        flags = 0;
        flags |= isLeading << 6;
        flags |= dependsOn << 4;
        flags |= dependedOn << 2;
        flags |= redundant;


        sdtp->sample_info = (u8*) gf_realloc(sdtp->sample_info, sizeof(u8) * (sdtp->sampleCount + 1));
        if (!sdtp->sample_info) return GF_OUT_OF_MEM;
        sdtp->sample_info[sdtp->sampleCount] = flags;
        sdtp->sampleCount ++;
        return GF_OK;
}

//this function is always called in INCREASING order of shadow sample numbers
GF_Err stbl_AddShadow(GF_ShadowSyncBox *stsh, u32 sampleNumber, u32 shadowNumber)
{
        GF_StshEntry *ent;
        u32 i, count;
        count = gf_list_count(stsh->entries);
        for (i=0; i<count; i++) {
                ent = (GF_StshEntry*)gf_list_get(stsh->entries, i);
                if (ent->shadowedSampleNumber == shadowNumber) {
                        ent->syncSampleNumber = sampleNumber;
                        return GF_OK;
                } else if (ent->shadowedSampleNumber > shadowNumber) break;
        }
        ent = (GF_StshEntry*)gf_malloc(sizeof(GF_StshEntry));
        if (!ent) return GF_OUT_OF_MEM;
        ent->shadowedSampleNumber = shadowNumber;
        ent->syncSampleNumber = sampleNumber;
        if (i == gf_list_count(stsh->entries)) {
                return gf_list_add(stsh->entries, ent);
        } else {
                return gf_list_insert(stsh->entries, ent, i ? i-1 : 0);
        }
}

//used in edit/write, where sampleNumber == chunkNumber
GF_Err stbl_AddChunkOffset(GF_MediaBox *mdia, u32 sampleNumber, u32 StreamDescIndex, u64 offset)
{
        GF_SampleTableBox *stbl;
        GF_ChunkOffsetBox *stco;
        GF_SampleToChunkBox *stsc;
        GF_ChunkLargeOffsetBox *co64;
        GF_StscEntry *ent;
        u32 i, k, *newOff;
        u64 *newLarge;

        stbl = mdia->information->sampleTable;
        stsc = stbl->SampleToChunk;

        if (stsc->nb_entries + 1 < sampleNumber ) return GF_BAD_PARAM;

        //add the offset to the chunk...
        //and we change our offset
        if (stbl->ChunkOffset->type == GF_ISOM_BOX_TYPE_STCO) {
                stco = (GF_ChunkOffsetBox *)stbl->ChunkOffset;
                //if the new offset is a large one, we have to rewrite our table entry by entry (32->64 bit conv)...
                if (offset > 0xFFFFFFFF) {
                        co64 = (GF_ChunkLargeOffsetBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_CO64);
                        co64->nb_entries = stco->nb_entries + 1;
                        co64->alloc_size = co64->nb_entries;
                        co64->offsets = (u64*)gf_malloc(sizeof(u64) * co64->nb_entries);
                        if (!co64->offsets) return GF_OUT_OF_MEM;
                        k = 0;
                        for (i=0; i<stco->nb_entries; i++) {
                                if (i + 1 == sampleNumber) {
                                        co64->offsets[i] = offset;
                                        k = 1;
                                }
                                co64->offsets[i+k] = (u64) stco->offsets[i];
                        }
                        if (!k) co64->offsets[co64->nb_entries - 1] = offset;
                        gf_isom_box_del(stbl->ChunkOffset);
                        stbl->ChunkOffset = (GF_Box *) co64;
                } else {
                        //no, we can use this one.
                        if (sampleNumber > stco->nb_entries) {
                                if (!stco->alloc_size) stco->alloc_size = stco->nb_entries;
                                if (stco->nb_entries == stco->alloc_size) {
                                        ALLOC_INC(stco->alloc_size);
                                        stco->offsets = (u32*)gf_realloc(stco->offsets, sizeof(u32) * stco->alloc_size);
                                        if (!stco->offsets) return GF_OUT_OF_MEM;
                                        memset(&stco->offsets[stco->nb_entries], 0, sizeof(u32) * (stco->alloc_size-stco->nb_entries) );
                                }
                                stco->offsets[stco->nb_entries] = (u32) offset;
                                stco->nb_entries += 1;
                        } else {
                                //nope. we're inserting
                                newOff = (u32*)gf_malloc(sizeof(u32) * (stco->nb_entries + 1));
                                if (!newOff) return GF_OUT_OF_MEM;
                                k=0;
                                for (i=0; i<stco->nb_entries; i++) {
                                        if (i+1 == sampleNumber) {
                                                newOff[i] = (u32) offset;
                                                k=1;
                                        }
                                        newOff[i+k] = stco->offsets[i];
                                }
                                gf_free(stco->offsets);
                                stco->offsets = newOff;
                                stco->nb_entries ++;
                                stco->alloc_size = stco->nb_entries;
                        }
                }
        } else {
                //use large offset...
                co64 = (GF_ChunkLargeOffsetBox *)stbl->ChunkOffset;
                if (sampleNumber > co64->nb_entries) {
                        if (!co64->alloc_size) co64->alloc_size = co64->nb_entries;
                        if (co64->nb_entries == co64->alloc_size) {
                                ALLOC_INC(co64->alloc_size);
                                co64->offsets = (u64*)gf_realloc(co64->offsets, sizeof(u64) * co64->alloc_size);
                                if (!co64->offsets) return GF_OUT_OF_MEM;
                                memset(&co64->offsets[co64->nb_entries], 0, sizeof(u64) * (co64->alloc_size - co64->nb_entries) );
                        }
                        co64->offsets[co64->nb_entries] = offset;
                        co64->nb_entries += 1;
                } else {
                        //nope. we're inserting
                        newLarge = (u64*)gf_malloc(sizeof(u64) * (co64->nb_entries + 1));
                        if (!newLarge) return GF_OUT_OF_MEM;
                        k=0;
                        for (i=0; i<co64->nb_entries; i++) {
                                if (i+1 == sampleNumber) {
                                        newLarge[i] = offset;
                                        k=1;
                                }
                                newLarge[i+k] = co64->offsets[i];
                        }
                        gf_free(co64->offsets);
                        co64->offsets = newLarge;
                        co64->nb_entries++;
                        co64->alloc_size++;
                }
        }

        if (stsc->nb_entries==stsc->alloc_size) {
                ALLOC_INC(stsc->alloc_size);
                stsc->entries = gf_realloc(stsc->entries, sizeof(GF_StscEntry)*stsc->alloc_size);
                if (!stsc->entries) return GF_OUT_OF_MEM;
                memset(&stsc->entries[stsc->nb_entries], 0, sizeof(GF_StscEntry)*(stsc->alloc_size-stsc->nb_entries) );
        }
        if (sampleNumber == stsc->nb_entries + 1) {
                ent = &stsc->entries[stsc->nb_entries];
        } else {
                memmove(&stsc->entries[sampleNumber], &stsc->entries[sampleNumber-1], sizeof(GF_StscEntry)*(stsc->nb_entries+1-sampleNumber));
                ent = &stsc->entries[sampleNumber-1];
        }
        ent->isEdited = 0;
        ent->isEdited = (Media_IsSelfContained(mdia, StreamDescIndex)) ? 1 : 0;
        ent->sampleDescriptionIndex = StreamDescIndex;
        ent->samplesPerChunk = 1;
        ent->firstChunk = sampleNumber;
        ent->nextChunk = sampleNumber + 1;

        //OK, now if we've inserted a chunk, update the sample to chunk info...
        if (sampleNumber == stsc->nb_entries + 1) {
                ent->nextChunk = stsc->nb_entries + 1;
                if (stsc->nb_entries)
                        stsc->entries[stsc->nb_entries-1].nextChunk = ent->firstChunk;

                stbl->SampleToChunk->currentIndex = stsc->nb_entries;
                stbl->SampleToChunk->firstSampleInCurrentChunk = sampleNumber;
                //write - edit mode: sample number = chunk number
                stbl->SampleToChunk->currentChunk = sampleNumber;
                stbl->SampleToChunk->ghostNumber = 1;
        } else {
                /*offset remaining entries*/
                for (i = sampleNumber; i<stsc->nb_entries+1; i++) {
                        stsc->entries[i].firstChunk++;
                }
        }
        stsc->nb_entries++;
        return GF_OK;
}




GF_Err stbl_SetChunkOffset(GF_MediaBox *mdia, u32 sampleNumber, u64 offset)
{
        GF_StscEntry *ent;
        u32 i;
        GF_ChunkLargeOffsetBox *co64;
        GF_SampleTableBox *stbl = mdia->information->sampleTable;

        if (!sampleNumber || !stbl) return GF_BAD_PARAM;

        ent = &stbl->SampleToChunk->entries[sampleNumber - 1];

        //we edit our entry if self contained
        if (Media_IsSelfContained(mdia, ent->sampleDescriptionIndex))
                ent->isEdited = 1;

        //and we change our offset
        if (stbl->ChunkOffset->type == GF_ISOM_BOX_TYPE_STCO) {
                //if the new offset is a large one, we have to rewrite our table...
                if (offset > 0xFFFFFFFF) {
                        co64 = (GF_ChunkLargeOffsetBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_CO64);
                        co64->nb_entries = ((GF_ChunkOffsetBox *)stbl->ChunkOffset)->nb_entries;
                        co64->alloc_size = co64->nb_entries;
                        co64->offsets = (u64*)gf_malloc(sizeof(u64)*co64->nb_entries);
                        if (!co64->offsets) return GF_OUT_OF_MEM;
                        for (i=0; i<co64->nb_entries; i++) {
                                co64->offsets[i] = (u64) ((GF_ChunkOffsetBox *)stbl->ChunkOffset)->offsets[i];
                        }
                        co64->offsets[ent->firstChunk - 1] = offset;
                        gf_isom_box_del(stbl->ChunkOffset);
                        stbl->ChunkOffset = (GF_Box *) co64;
                        return GF_OK;
                }
                ((GF_ChunkOffsetBox *)stbl->ChunkOffset)->offsets[ent->firstChunk - 1] = (u32) offset;
        } else {
                ((GF_ChunkLargeOffsetBox *)stbl->ChunkOffset)->offsets[ent->firstChunk - 1] = offset;
        }
        return GF_OK;
}


GF_Err stbl_SetSampleCTS(GF_SampleTableBox *stbl, u32 sampleNumber, s32 offset)
{
        GF_CompositionOffsetBox *ctts = stbl->CompositionOffset;

        assert(ctts->unpack_mode);

        //if we're setting the CTS of a sample we've skipped...
        if (ctts->w_LastSampleNumber < sampleNumber) {
                //add some 0 till we get to the sample
                while (ctts->w_LastSampleNumber + 1 != sampleNumber) {
                        AddCompositionOffset(ctts, 0);
                }
                return AddCompositionOffset(ctts, offset);
        }
        if (offset<0) ctts->version=1;
        ctts->entries[sampleNumber-1].decodingOffset = offset;
        return GF_OK;
}

GF_Err stbl_SetSampleSize(GF_SampleSizeBox *stsz, u32 SampleNumber, u32 size)
{
        u32 i;
        if (!SampleNumber || (stsz->sampleCount < SampleNumber)) return GF_BAD_PARAM;

        if (stsz->sampleSize) {
                if (stsz->sampleSize == size) return GF_OK;
                if (stsz->sampleCount == 1) {
                        stsz->sampleSize = size;
                        return GF_OK;
                }
                //nope, we have to rewrite a table
                stsz->sizes = (u32*)gf_malloc(sizeof(u32)*stsz->sampleCount);
                if (!stsz->sizes) return GF_OUT_OF_MEM;
                for (i=0; i<stsz->sampleCount; i++) stsz->sizes[i] = stsz->sampleSize;
                stsz->sampleSize = 0;
        }
        stsz->sizes[SampleNumber - 1] = size;
        return GF_OK;
}


GF_Err stbl_SetSampleRAP(GF_SyncSampleBox *stss, u32 SampleNumber, u8 isRAP)
{
        u32 i;

        //check if we have already a sync sample
        for (i = 0; i < stss->nb_entries; i++) {

                if (stss->sampleNumbers[i] < SampleNumber) continue;
                else if (stss->sampleNumbers[i] > SampleNumber) break;

                /*found our sample number*/
                if (isRAP) return GF_OK;
                /*remove it...*/
                if (i+1 < stss->nb_entries)
                        memcpy(stss->sampleNumbers + i, stss->sampleNumbers + i + 1, sizeof(u32) * (stss->nb_entries - i - 1));
                stss->nb_entries--;
                return GF_OK;
        }
        //we need to insert a RAP somewhere if RAP ...
        if (!isRAP) return GF_OK;
        if (stss->nb_entries==stss->alloc_size) {
                ALLOC_INC(stss->alloc_size);
                stss->sampleNumbers = gf_realloc(stss->sampleNumbers, sizeof(u32)*stss->alloc_size);
                if (!stss->sampleNumbers) return GF_OUT_OF_MEM;
                memset(&stss->sampleNumbers[stss->nb_entries], 0, sizeof(u32)*(stss->alloc_size - stss->nb_entries) );
        }

        if (i+1 < stss->nb_entries)
                memcpy(stss->sampleNumbers + i + 1, stss->sampleNumbers + i, sizeof(u32) * (stss->nb_entries - i - 1));
        stss->sampleNumbers[i] = SampleNumber;
        stss->nb_entries ++;
        return GF_OK;
}

GF_Err stbl_SetRedundant(GF_SampleTableBox *stbl, u32 sampleNumber)
{
        if (stbl->SampleDep->sampleCount < sampleNumber) {
                return stbl_AddRedundant(stbl, sampleNumber);
        } else {
                stbl->SampleDep->sample_info[sampleNumber-1] = 0x29;
                return GF_OK;
        }
}

GF_Err stbl_SetSyncShadow(GF_ShadowSyncBox *stsh, u32 sampleNumber, u32 syncSample)
{
        u32 i, count;
        GF_StshEntry *ent;

        count = gf_list_count(stsh->entries);
        for (i=0; i<count; i++) {
                ent = (GF_StshEntry*)gf_list_get(stsh->entries, i);
                if (ent->shadowedSampleNumber == sampleNumber) {
                        ent->syncSampleNumber = syncSample;
                        return GF_OK;
                }
                if (ent->shadowedSampleNumber > sampleNumber) break;
        }
        //we need a new one...
        ent = (GF_StshEntry*)gf_malloc(sizeof(GF_StshEntry));
        if (!ent) return GF_OUT_OF_MEM;
        ent->shadowedSampleNumber = sampleNumber;
        ent->syncSampleNumber = syncSample;
        //insert or append ?
        if (i == gf_list_count(stsh->entries)) {
                //don't update the cache ...
                return gf_list_add(stsh->entries, ent);
        } else {
                //update the cache
                stsh->r_LastEntryIndex = i;
                stsh->r_LastFoundSample = sampleNumber;
                return gf_list_insert(stsh->entries, ent, i);
        }
}


//always called before removing the sample from SampleSize
GF_Err stbl_RemoveDTS(GF_SampleTableBox *stbl, u32 sampleNumber, u32 LastAUDefDuration)
{
        u64 *DTSs, curDTS;
        u32 i, j, k, sampNum;
        GF_SttsEntry *ent;
        GF_TimeToSampleBox *stts;

        stts = stbl->TimeToSample;

        //we're removing the only sample: empty the sample table
        if (stbl->SampleSize->sampleCount == 1) {
                stts->nb_entries = 0;
                stts->r_FirstSampleInEntry = stts->r_currentEntryIndex = 0;
                stts->r_CurrentDTS = 0;
                return GF_OK;
        }
        //we're removing the last sample
        if (sampleNumber == stbl->SampleSize->sampleCount) {
                ent = &stts->entries[stts->nb_entries-1];
                ent->sampleCount--;
                if (!ent->sampleCount) stts->nb_entries--;
        } else {
                //unpack the DTSs...
                DTSs = (u64*)gf_malloc(sizeof(u64) * (stbl->SampleSize->sampleCount - 1));
                if (!DTSs) return GF_OUT_OF_MEM;
                memset(DTSs, 0, sizeof(u64) * (stbl->SampleSize->sampleCount - 1) );

                curDTS = 0;
                sampNum = 0;
                ent = NULL;
                k=0;

                for (i=0; i<stts->nb_entries; i++) {
                        ent = & stts->entries[i];
                        for (j=0; j<ent->sampleCount; j++) {
                                if (sampNum == sampleNumber - 1) {
                                        k=1;
                                } else {
                                        DTSs[sampNum-k] = curDTS;
                                }
                                curDTS += ent->sampleDelta;
                                sampNum ++;
                        }
                }
                j=0;
                stts->nb_entries = 1;
                stts->entries[0].sampleCount = 1;
                if (stbl->SampleSize->sampleCount == 2) {
                        stts->entries[0].sampleDelta = LastAUDefDuration;
                } else {
                        stts->entries[0].sampleDelta = (u32) DTSs[1] /*- DTS[0]==0 */;
                }
                sampNum = 1;
                for (i=1; i<stbl->SampleSize->sampleCount-1; i++) {
                        if (i+1 == stbl->SampleSize->sampleCount-1) {
                                //and by default, our last sample has the same delta as the prev
                                stts->entries[j].sampleCount++;
                                sampNum ++;
                        } else if (DTSs[i+1] - DTSs[i] == stts->entries[j].sampleDelta) {
                                stts->entries[j].sampleCount += 1;
                                sampNum ++;
                        } else {
                                stts->nb_entries++;
                                if (j+1==stts->alloc_size) {
                                        stts->alloc_size++;
                                        stts->entries = gf_realloc(stts->entries, sizeof(GF_SttsEntry) * stts->alloc_size);
                                }
                                j++;
                                stts->entries[j].sampleCount = 1;
                                stts->entries[j].sampleDelta = (u32) (DTSs[i+1] - DTSs[i]);
                                sampNum ++;
                        }
                }
                stts->w_LastDTS = DTSs[stbl->SampleSize->sampleCount - 2];
                gf_free(DTSs);
                assert(sampNum == stbl->SampleSize->sampleCount - 1);

        }

        //reset write the cache to the end
        stts->w_currentSampleNum = stbl->SampleSize->sampleCount - 1;
        //reset read the cache to the beginning
        stts->r_FirstSampleInEntry = stts->r_currentEntryIndex = 0;
        stts->r_CurrentDTS = 0;
        return GF_OK;
}


//always called before removing the sample from SampleSize
GF_Err stbl_RemoveCTS(GF_SampleTableBox *stbl, u32 sampleNumber)
{
        GF_CompositionOffsetBox *ctts = stbl->CompositionOffset;
        assert(ctts->unpack_mode);

        //last one...
        if (stbl->SampleSize->sampleCount == 1) {
                gf_isom_box_del((GF_Box *) ctts);
                stbl->CompositionOffset = NULL;
                return GF_OK;
        }

        //the number of entries is NOT ALWAYS the number of samples !
        //instead, use the cache
        //first case, we're removing a sample that was not added yet
        if (sampleNumber > ctts->w_LastSampleNumber) return GF_OK;

        memmove(&ctts->entries[sampleNumber-1], &ctts->entries[sampleNumber], sizeof(GF_DttsEntry)* (ctts->nb_entries-sampleNumber) );
        ctts->nb_entries--;

        ctts->w_LastSampleNumber -= 1;
        return GF_OK;
}

GF_Err stbl_RemoveSize(GF_SampleSizeBox *stsz, u32 sampleNumber)
{
        //last sample
        if (stsz->sampleCount == 1) {
                if (stsz->sizes) gf_free(stsz->sizes);
                stsz->sizes = NULL;
                stsz->sampleCount = 0;
                return GF_OK;
        }
        //one single size
        if (stsz->sampleSize) {
                stsz->sampleCount -= 1;
                return GF_OK;
        }
        if (sampleNumber < stsz->sampleCount) {
                memcpy(stsz->sizes + sampleNumber - 1, stsz->sizes + sampleNumber, sizeof(u32) * (stsz->sampleCount - sampleNumber));
        }
        stsz->sampleCount--;
        return GF_OK;
}

//always called after removing the sample from SampleSize
GF_Err stbl_RemoveChunk(GF_SampleTableBox *stbl, u32 sampleNumber)
{
        u32 i, k;
        u32 *offsets;
        u64 *Loffsets;
        GF_SampleToChunkBox *stsc = stbl->SampleToChunk;

        //remove the entry in SampleToChunk (1 <-> 1 in edit mode)
        memmove(&stsc->entries[sampleNumber-1], &stsc->entries[sampleNumber], sizeof(GF_StscEntry)*(stsc->nb_entries-sampleNumber));
        stsc->nb_entries--;

        //update the firstchunk info
        for (i=sampleNumber-1; i < stsc->nb_entries; i++) {
                stsc->entries[i].firstChunk -= 1;
                stsc->entries[i].nextChunk -= 1;
        }
        //update the cache
        stbl->SampleToChunk->firstSampleInCurrentChunk = 1;
        stbl->SampleToChunk->currentIndex = 0;
        stbl->SampleToChunk->currentChunk = 1;
        stbl->SampleToChunk->ghostNumber = 1;

        //realloc the chunk offset
        if (stbl->ChunkOffset->type == GF_ISOM_BOX_TYPE_STCO) {
                if (!stbl->SampleSize->sampleCount) {
                        gf_free(((GF_ChunkOffsetBox *)stbl->ChunkOffset)->offsets);
                        ((GF_ChunkOffsetBox *)stbl->ChunkOffset)->offsets = NULL;
                        ((GF_ChunkOffsetBox *)stbl->ChunkOffset)->nb_entries = 0;
                        ((GF_ChunkOffsetBox *)stbl->ChunkOffset)->alloc_size = 0;
                        return GF_OK;
                }
                offsets = (u32*)gf_malloc(sizeof(u32) * (stbl->SampleSize->sampleCount));
                if (!offsets) return GF_OUT_OF_MEM;
                k=0;
                for (i=0; i<stbl->SampleSize->sampleCount+1; i++) {
                        if (i+1 == sampleNumber) {
                                k=1;
                        } else {
                                offsets[i-k] = ((GF_ChunkOffsetBox *)stbl->ChunkOffset)->offsets[i];
                        }
                }
                gf_free(((GF_ChunkOffsetBox *)stbl->ChunkOffset)->offsets);
                ((GF_ChunkOffsetBox *)stbl->ChunkOffset)->offsets = offsets;
                ((GF_ChunkOffsetBox *)stbl->ChunkOffset)->alloc_size = stbl->SampleSize->sampleCount;
                ((GF_ChunkOffsetBox *)stbl->ChunkOffset)->nb_entries -= 1;
        } else {
                if (!stbl->SampleSize->sampleCount) {
                        gf_free(((GF_ChunkLargeOffsetBox *)stbl->ChunkOffset)->offsets);
                        ((GF_ChunkLargeOffsetBox *)stbl->ChunkOffset)->offsets = NULL;
                        ((GF_ChunkLargeOffsetBox *)stbl->ChunkOffset)->nb_entries = 0;
                        ((GF_ChunkLargeOffsetBox *)stbl->ChunkOffset)->alloc_size = 0;
                        return GF_OK;
                }

                Loffsets = (u64*)gf_malloc(sizeof(u64) * (stbl->SampleSize->sampleCount));
                if (!Loffsets) return GF_OUT_OF_MEM;
                k=0;
                for (i=0; i<stbl->SampleSize->sampleCount+1; i++) {
                        if (i+1 == sampleNumber) {
                                k=1;
                        } else {
                                Loffsets[i-k] = ((GF_ChunkLargeOffsetBox *)stbl->ChunkOffset)->offsets[i];
                        }
                }
                gf_free(((GF_ChunkLargeOffsetBox *)stbl->ChunkOffset)->offsets);
                ((GF_ChunkLargeOffsetBox *)stbl->ChunkOffset)->offsets = Loffsets;
                ((GF_ChunkLargeOffsetBox *)stbl->ChunkOffset)->alloc_size = stbl->SampleSize->sampleCount;
                ((GF_ChunkLargeOffsetBox *)stbl->ChunkOffset)->nb_entries -= 1;
        }
        return GF_OK;
}


GF_Err stbl_RemoveRAP(GF_SampleTableBox *stbl, u32 sampleNumber)
{
        u32 i;

        GF_SyncSampleBox *stss = stbl->SyncSample;
        //we remove the only one around...
        if (stss->nb_entries == 1) {
                if (stss->sampleNumbers[0] != sampleNumber) return GF_OK;
                //free our numbers but don't delete (all samples are NON-sync
                gf_free(stss->sampleNumbers);
                stss->sampleNumbers = NULL;
                stss->r_LastSampleIndex = stss->r_LastSyncSample = 0;
                stss->alloc_size = stss->nb_entries = 0;
                return GF_OK;
        }

        for (i=0; i<stss->nb_entries; i++) {
                //found the sample
                if (sampleNumber == stss->sampleNumbers[i]) {
                        memmove(&stss->sampleNumbers[i], &stss->sampleNumbers[i+1], sizeof(u32)* (stss->nb_entries-i-1) );
                        stss->nb_entries--;
                }

                if (sampleNumber < stss->sampleNumbers[i]) {
                        assert(stss->sampleNumbers[i]);
                        stss->sampleNumbers[i]--;
                }
        }
        return GF_OK;
}

GF_Err stbl_RemoveRedundant(GF_SampleTableBox *stbl, u32 SampleNumber)
{
        u32 i;

        if (!stbl->SampleDep) return GF_OK;
        if (stbl->SampleDep->sampleCount < SampleNumber) return GF_BAD_PARAM;

        i = stbl->SampleDep->sampleCount - SampleNumber;
        if (i) memmove(&stbl->SampleDep->sample_info[SampleNumber-1], & stbl->SampleDep->sample_info[SampleNumber], sizeof(u8)*i);
        stbl->SampleDep->sample_info = (u8*)gf_realloc(stbl->SampleDep->sample_info, sizeof(u8) * (stbl->SampleDep->sampleCount-1));
        stbl->SampleDep->sampleCount-=1;
        return GF_OK;
}

GF_Err stbl_RemoveShadow(GF_ShadowSyncBox *stsh, u32 sampleNumber)
{
        u32 i;
        GF_StshEntry *ent;

        //we loop for the whole chain cause the spec doesn't say if we can have several
        //shadows for 1 sample...
        i=0;
        while ((ent = (GF_StshEntry *)gf_list_enum(stsh->entries, &i))) {
                if (ent->shadowedSampleNumber == sampleNumber) {
                        i--;
                        gf_list_rem(stsh->entries, i);
                }
        }
        //reset the cache
        stsh->r_LastEntryIndex = 0;
        stsh->r_LastFoundSample = 0;
        return GF_OK;
}


GF_Err stbl_SetPaddingBits(GF_SampleTableBox *stbl, u32 SampleNumber, u8 bits)
{
        u8 *p;
        //make sure the sample is a good one
        if (SampleNumber > stbl->SampleSize->sampleCount) return GF_BAD_PARAM;

        //create the table
        if (!stbl->PaddingBits) stbl->PaddingBits = (GF_PaddingBitsBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_PADB);

        //alloc
        if (!stbl->PaddingBits->padbits || !stbl->PaddingBits->SampleCount) {
                stbl->PaddingBits->SampleCount = stbl->SampleSize->sampleCount;
                stbl->PaddingBits->padbits = (u8*)gf_malloc(sizeof(u8)*stbl->PaddingBits->SampleCount);
                if (!stbl->PaddingBits->padbits) return GF_OUT_OF_MEM;
                memset(stbl->PaddingBits->padbits, 0, sizeof(u8)*stbl->PaddingBits->SampleCount);
        }
        //realloc (this is needed in case n out of k samples get padding added)
        if (stbl->PaddingBits->SampleCount < stbl->SampleSize->sampleCount) {
                p = (u8*)gf_malloc(sizeof(u8) * stbl->SampleSize->sampleCount);
                if (!p) return GF_OUT_OF_MEM;
                //set everything to 0
                memset(p, 0, stbl->SampleSize->sampleCount);
                //copy our previous table
                memcpy(p, stbl->PaddingBits->padbits, stbl->PaddingBits->SampleCount);
                gf_free(stbl->PaddingBits->padbits);
                stbl->PaddingBits->padbits = p;
                stbl->PaddingBits->SampleCount = stbl->SampleSize->sampleCount;
        }
        stbl->PaddingBits->padbits[SampleNumber-1] = bits;
        return GF_OK;
}

GF_Err stbl_RemovePaddingBits(GF_SampleTableBox *stbl, u32 SampleNumber)
{
        u8 *p;
        u32 i, k;

        if (!stbl->PaddingBits) return GF_OK;
        if (stbl->PaddingBits->SampleCount < SampleNumber) return GF_BAD_PARAM;

        //last sample - remove the table
        if (stbl->PaddingBits->SampleCount == 1) {
                gf_isom_box_del((GF_Box *) stbl->PaddingBits);
                stbl->PaddingBits = NULL;
                return GF_OK;
        }

        //reallocate and check size by the way...
        p = (u8 *)gf_malloc(sizeof(u8) * (stbl->PaddingBits->SampleCount - 1));
        if (!p) return GF_OUT_OF_MEM;

        k=0;
        for (i=0; i<stbl->PaddingBits->SampleCount; i++) {
                if (i+1 != SampleNumber) {
                        p[k] = stbl->PaddingBits->padbits[i];
                        k++;
                }
        }

        stbl->PaddingBits->SampleCount -= 1;
        gf_free(stbl->PaddingBits->padbits);
        stbl->PaddingBits->padbits = p;
        return GF_OK;
}

GF_Err stbl_RemoveSubSample(GF_SampleTableBox *stbl, u32 SampleNumber)
{
        u32 i, count, j, subs_count, prev_sample, delta=0;

        if (! stbl->sub_samples) return GF_OK;
        subs_count = gf_list_count(stbl->sub_samples);
        for (j=0; j<subs_count; j++) {
                GF_SubSampleInformationBox *subs = gf_list_get(stbl->sub_samples, j);
                if (! subs->Samples) continue;

                prev_sample = 0;
                count = gf_list_count(subs->Samples);
                for (i=0; i<count; i++) {
                        GF_SubSampleInfoEntry *e = gf_list_get(subs->Samples, i);
                        prev_sample += e->sample_delta;
                        if (prev_sample==SampleNumber) {
                                gf_list_rem(subs->Samples, i);
                                while (gf_list_count(e->SubSamples)) {
                                        GF_SubSampleEntry *pSubSamp = (GF_SubSampleEntry*) gf_list_get(e->SubSamples, 0);
                                        gf_free(pSubSamp);
                                        gf_list_rem(e->SubSamples, 0);
                                }
                                gf_list_del(e->SubSamples);
                                gf_free(e);
                                i--;
                                count--;
                                delta=1;
                                continue;
                        }
                        e->sample_delta+=delta;
                }
        }
        return GF_OK;
}


GF_Err stbl_RemoveSampleGroup(GF_SampleTableBox *stbl, u32 SampleNumber)
{
        u32 i, k, count, prev_sample;

        if (!stbl->sampleGroups) return GF_OK;

        count = gf_list_count(stbl->sampleGroups);
        prev_sample = 0;
        for (i=0; i<count; i++) {
                GF_SampleGroupBox *e = gf_list_get(stbl->sampleGroups, i);
                for (k=0; k<e->entry_count; k++) {
                        if ((SampleNumber>prev_sample) && (SampleNumber <= prev_sample + e->sample_entries[k].sample_count) ) {
                                e->sample_entries[k].sample_count--;
                                if (!e->sample_entries[k].sample_count) {
                                        memcpy(&e->sample_entries[k], &e->sample_entries[k+1], sizeof(GF_SampleGroupEntry) * (e->entry_count-k-1));
                                        e->entry_count--;
                                }
                                break;
                        }
                }
                if (!e->entry_count) {
                        gf_list_rem(stbl->sampleGroups, i);
                        i--;
                        count--;
                        gf_isom_box_del((GF_Box *) e);
                }
        }
        return GF_OK;
}




GF_Err stbl_AddSampleFragment(GF_SampleTableBox *stbl, u32 sampleNumber, u16 size)
{
        GF_Err e;
        u32 i, count;
        GF_StsfEntry *ent;
        GF_SampleFragmentBox *stsf;
        stsf = stbl->Fragments;

        if (!stsf) {
                //create table if any
                stsf = (GF_SampleFragmentBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSF);
                if (!stsf) return GF_OUT_OF_MEM;
                e = stbl_AddBox((GF_Box*)stbl, (GF_Box *) stsf);
                if (e) return e;
        }

        //check cache
        if (!stsf->w_currentEntry || (stsf->w_currentEntry->SampleNumber < sampleNumber)) {
                stsf->w_currentEntry = NULL;
                stsf->w_currentEntryIndex = 0;
        }
        i = stsf->w_currentEntryIndex;

        count = gf_list_count(stsf->entryList);
        for (; i<count; i++) {
                ent = (GF_StsfEntry *)gf_list_get(stsf->entryList, i);
                if (ent->SampleNumber > sampleNumber) {
                        ent = (GF_StsfEntry *)gf_malloc(sizeof(GF_StsfEntry));
                        if (!ent) return GF_OUT_OF_MEM;
                        memset(ent, 0, sizeof(GF_StsfEntry));
                        ent->SampleNumber = sampleNumber;
                        gf_list_insert(stsf->entryList, ent, i);
                        stsf->w_currentEntry = ent;
                        stsf->w_currentEntryIndex = i;
                        goto ent_found;
                }
                else if (ent->SampleNumber == sampleNumber) {
                        stsf->w_currentEntry = ent;
                        stsf->w_currentEntryIndex = i;
                        goto ent_found;
                }
        }
        //if we get here add a new entry
        GF_SAFEALLOC(ent, GF_StsfEntry);
        if (!ent) return GF_OUT_OF_MEM;
        ent->SampleNumber = sampleNumber;
        gf_list_add(stsf->entryList, ent);
        stsf->w_currentEntry = ent;
        stsf->w_currentEntryIndex = gf_list_count(stsf->entryList)-1;

ent_found:
        if (!ent->fragmentCount) {
                ent->fragmentCount = 1;
                ent->fragmentSizes = (u16*)gf_malloc(sizeof(u16));
                if (!ent->fragmentSizes) return GF_OUT_OF_MEM;
                ent->fragmentSizes[0] = size;
                return GF_OK;
        }
        ent->fragmentSizes = (u16*)gf_realloc(ent->fragmentSizes, sizeof(u16) * (ent->fragmentCount+1));
        if (!ent->fragmentSizes) return GF_OUT_OF_MEM;
        ent->fragmentSizes[ent->fragmentCount] = size;
        ent->fragmentCount += 1;

        return GF_OK;
}

GF_Err stbl_RemoveSampleFragments(GF_SampleTableBox *stbl, u32 sampleNumber)
{
        u32 i;
        GF_StsfEntry *ent;
        GF_SampleFragmentBox *stsf = stbl->Fragments;

        i=0;
        while ((ent = (GF_StsfEntry *)gf_list_enum(stsf->entryList, &i))) {
                if (ent->SampleNumber == sampleNumber) {
                        gf_list_rem(stsf->entryList, i-1);
                        if (ent->fragmentSizes) gf_free(ent->fragmentSizes);
                        gf_free(ent);
                        goto exit;
                }
        }
exit:
        //empty table, remove it
        if (!gf_list_count(stsf->entryList)) {
                stbl->Fragments = NULL;
                gf_isom_box_del((GF_Box *)stsf);
        }
        return GF_OK;
}

GF_Err stbl_SampleSizeAppend(GF_SampleSizeBox *stsz, u32 data_size)
{
        u32 i;
        if (!stsz || !stsz->sampleCount) return GF_BAD_PARAM;

        //we must realloc our table
        if (stsz->sampleSize) {
                stsz->sizes = (u32*)gf_malloc(sizeof(u32)*stsz->sampleCount);
                if (!stsz->sizes) return GF_OUT_OF_MEM;
                for (i=0; i<stsz->sampleCount; i++) stsz->sizes[i] = stsz->sampleSize;
                stsz->sampleSize = 0;
        }
        if (!stsz->sizes) {
                stsz->sampleSize = data_size;
        } else {
                stsz->sizes[stsz->sampleCount-1] += data_size;
        }
        return GF_OK;
}

#endif  /*GPAC_DISABLE_ISOM_WRITE*/



void stbl_AppendTime(GF_SampleTableBox *stbl, u32 duration)
{
        GF_TimeToSampleBox *stts = stbl->TimeToSample;

        if (stts->nb_entries) {
                if (stts->entries[stts->nb_entries-1].sampleDelta == duration) {
                        stts->entries[stts->nb_entries-1].sampleCount += 1;
                        return;
                }
        }
        if (stts->nb_entries==stts->alloc_size) {
                ALLOC_INC(stts->alloc_size);
                stts->entries = gf_realloc(stts->entries, sizeof(GF_SttsEntry)*stts->alloc_size);
                if (!stts->entries) return;
                memset(&stts->entries[stts->nb_entries], 0, sizeof(GF_SttsEntry)*(stts->alloc_size-stts->nb_entries) );
        }
        stts->entries[stts->nb_entries].sampleCount = 1;
        stts->entries[stts->nb_entries].sampleDelta = duration;
        stts->nb_entries++;
}

void stbl_AppendSize(GF_SampleTableBox *stbl, u32 size)
{
        u32 i;

        if (!stbl->SampleSize->sampleCount) {
                stbl->SampleSize->sampleSize = size;
                stbl->SampleSize->sampleCount = 1;
                return;
        }
        if (stbl->SampleSize->sampleSize && (stbl->SampleSize->sampleSize==size)) {
                stbl->SampleSize->sampleCount += 1;
                return;
        }
        if (!stbl->SampleSize->sizes || (stbl->SampleSize->sampleCount==stbl->SampleSize->alloc_size)) {
                Bool init_table = (stbl->SampleSize->sizes==NULL) ? 1 : 0;
                ALLOC_INC(stbl->SampleSize->alloc_size);
                if (stbl->SampleSize->sampleCount >= stbl->SampleSize->alloc_size)
                        stbl->SampleSize->alloc_size = stbl->SampleSize->sampleCount+1;

                stbl->SampleSize->sizes = (u32 *)gf_realloc(stbl->SampleSize->sizes, sizeof(u32)*stbl->SampleSize->alloc_size);
                if (!stbl->SampleSize->sizes) return;
                memset(&stbl->SampleSize->sizes[stbl->SampleSize->sampleCount], 0, sizeof(u32) * (stbl->SampleSize->alloc_size - stbl->SampleSize->sampleCount) );

                if (init_table) {
                        for (i=0; i<stbl->SampleSize->sampleCount; i++)
                                stbl->SampleSize->sizes[i] = stbl->SampleSize->sampleSize;
                }
        }
        stbl->SampleSize->sampleSize = 0;
        stbl->SampleSize->sizes[stbl->SampleSize->sampleCount] = size;
        stbl->SampleSize->sampleCount += 1;
}

void stbl_AppendChunk(GF_SampleTableBox *stbl, u64 offset)
{
        GF_ChunkOffsetBox *stco;
        GF_ChunkLargeOffsetBox *co64;
        u32 *new_offsets, i;
        u64 *off_64;

        //we may have to convert the table...
        if (stbl->ChunkOffset->type==GF_ISOM_BOX_TYPE_STCO) {
                stco = (GF_ChunkOffsetBox *)stbl->ChunkOffset;

                if (offset>0xFFFFFFFF) {
                        co64 = (GF_ChunkLargeOffsetBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_CO64);
                        co64->nb_entries = stco->nb_entries + 1;
                        co64->alloc_size = co64->nb_entries;
                        co64->offsets = (u64*)gf_malloc(sizeof(u64) * co64->nb_entries);
                        if (!co64->offsets) return;
                        for (i=0; i<stco->nb_entries; i++) co64->offsets[i] = stco->offsets[i];
                        co64->offsets[i] = offset;
                        gf_isom_box_del(stbl->ChunkOffset);
                        stbl->ChunkOffset = (GF_Box *) co64;
                        return;
                }
                //we're fine
                new_offsets = (u32*)gf_malloc(sizeof(u32)*(stco->nb_entries+1));
                if (!new_offsets) return;
                for (i=0; i<stco->nb_entries; i++) new_offsets[i] = stco->offsets[i];
                new_offsets[i] = (u32) offset;
                if (stco->offsets) gf_free(stco->offsets);
                stco->offsets = new_offsets;
                stco->nb_entries += 1;
                stco->alloc_size = stco->nb_entries;
        }
        //large offsets
        else {
                co64 = (GF_ChunkLargeOffsetBox *)stbl->ChunkOffset;
                off_64 = (u64*)gf_malloc(sizeof(u64)*(co64->nb_entries+1));
                if (!off_64) return;
                for (i=0; i<co64->nb_entries; i++) off_64[i] = co64->offsets[i];
                off_64[i] = offset;
                if (co64->offsets) gf_free(co64->offsets);
                co64->offsets = off_64;
                co64->nb_entries += 1;
                co64->alloc_size = co64->nb_entries;
        }
}

void stbl_AppendSampleToChunk(GF_SampleTableBox *stbl, u32 DescIndex, u32 samplesInChunk)
{
        u32 nextChunk;
        GF_SampleToChunkBox *stsc= stbl->SampleToChunk;
        GF_StscEntry *ent;

        nextChunk = ((GF_ChunkOffsetBox *) stbl->ChunkOffset)->nb_entries;

        if (stsc->nb_entries) {
                ent = &stsc->entries[stsc->nb_entries-1];
                //good we can use this one
                if ( (ent->sampleDescriptionIndex == DescIndex) && (ent->samplesPerChunk==samplesInChunk))
                        return;

                //set the next chunk btw ...
                ent->nextChunk = nextChunk;
        }
        if (stsc->nb_entries==stsc->alloc_size) {
                ALLOC_INC(stsc->alloc_size);
                stsc->entries = gf_realloc(stsc->entries, sizeof(GF_StscEntry)*stsc->alloc_size);
                if (!stsc->entries) return;
                memset(&stsc->entries[stsc->nb_entries], 0, sizeof(GF_StscEntry)*(stsc->alloc_size - stsc->nb_entries) );
        }
        //ok we need a new entry - this assumes this function is called AFTER AppendChunk
        ent = &stsc->entries[stsc->nb_entries];
        ent->firstChunk = nextChunk;
        ent->sampleDescriptionIndex = DescIndex;
        ent->samplesPerChunk = samplesInChunk;
        ent->isEdited = 0;
        stsc->nb_entries++;
}

//called AFTER AddSize
void stbl_AppendRAP(GF_SampleTableBox *stbl, u8 isRap)
{
        u32 i;

        //no sync table
        if (!stbl->SyncSample) {
                //all samples RAP - no table
                if (isRap) return;

                //nope, create one
                stbl->SyncSample = (GF_SyncSampleBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSS);
                if (stbl->SampleSize->sampleCount > 1) {
                        stbl->SyncSample->sampleNumbers = (u32*)gf_malloc(sizeof(u32) * (stbl->SampleSize->sampleCount-1));
                        if (!stbl->SyncSample->sampleNumbers) return;
                        for (i=0; i<stbl->SampleSize->sampleCount-1; i++)
                                stbl->SyncSample->sampleNumbers[i] = i+1;

                }
                stbl->SyncSample->nb_entries = stbl->SampleSize->sampleCount-1;
                stbl->SyncSample->alloc_size = stbl->SyncSample->nb_entries;
                return;
        }
        if (!isRap) return;

        if (stbl->SyncSample->alloc_size == stbl->SyncSample->nb_entries) {
                ALLOC_INC(stbl->SyncSample->alloc_size);
                stbl->SyncSample->sampleNumbers = (u32*) gf_realloc(stbl->SyncSample->sampleNumbers, sizeof(u32) * stbl->SyncSample->alloc_size);
                if (!stbl->SyncSample->sampleNumbers) return;
                memset(&stbl->SyncSample->sampleNumbers[stbl->SyncSample->nb_entries], 0, sizeof(u32) * (stbl->SyncSample->alloc_size-stbl->SyncSample->nb_entries) );
        }
        stbl->SyncSample->sampleNumbers[stbl->SyncSample->nb_entries] = stbl->SampleSize->sampleCount;
        stbl->SyncSample->nb_entries += 1;
}

void stbl_AppendPadding(GF_SampleTableBox *stbl, u8 padding)
{
        u32 i;
        u8 *pad_bits;
        if (!stbl->PaddingBits) stbl->PaddingBits = (GF_PaddingBitsBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_PADB);

        pad_bits = (u8*)gf_malloc(sizeof(u8) * stbl->SampleSize->sampleCount);
        if (!pad_bits) return;
        memset(pad_bits, 0, sizeof(u8) * stbl->SampleSize->sampleCount);
        for (i=0; i<stbl->PaddingBits->SampleCount; i++) pad_bits[i] = stbl->PaddingBits->padbits[i];
        pad_bits[stbl->SampleSize->sampleCount-1] = padding;
        if (stbl->PaddingBits->padbits) gf_free(stbl->PaddingBits->padbits);
        stbl->PaddingBits->padbits = pad_bits;
        stbl->PaddingBits->SampleCount = stbl->SampleSize->sampleCount;
}

void stbl_AppendCTSOffset(GF_SampleTableBox *stbl, s32 offset)
{
        GF_CompositionOffsetBox *ctts;

        if (!stbl->CompositionOffset) stbl->CompositionOffset = (GF_CompositionOffsetBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_CTTS);

        ctts = stbl->CompositionOffset;

        if (ctts->nb_entries && (ctts->entries[ctts->nb_entries-1].decodingOffset == offset) ) {
                ctts->entries[ctts->nb_entries-1].sampleCount++;
                return;
        }
        if (ctts->nb_entries==ctts->alloc_size) {
                ALLOC_INC(ctts->alloc_size);
                ctts->entries = gf_realloc(ctts->entries, sizeof(GF_DttsEntry)*ctts->alloc_size);
                memset(&ctts->entries[ctts->nb_entries], 0, sizeof(GF_DttsEntry)*(ctts->alloc_size-ctts->nb_entries) );
        }
        ctts->entries[ctts->nb_entries].decodingOffset = offset;
        ctts->entries[ctts->nb_entries].sampleCount = 1;
        ctts->nb_entries++;
        if (offset<0) ctts->version=1;
}

void stbl_AppendDegradation(GF_SampleTableBox *stbl, u16 DegradationPriority)
{
        if (!stbl->DegradationPriority) stbl->DegradationPriority = (GF_DegradationPriorityBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STDP);

        stbl->DegradationPriority->priorities = (u16 *)gf_realloc(stbl->DegradationPriority->priorities, sizeof(u16) * stbl->SampleSize->sampleCount);
        stbl->DegradationPriority->priorities[stbl->SampleSize->sampleCount-1] = DegradationPriority;
        stbl->DegradationPriority->nb_entries = stbl->SampleSize->sampleCount;
}

void stbl_AppendDepType(GF_SampleTableBox *stbl, u32 DepType)
{
        if (!stbl->SampleDep) stbl->SampleDep= (GF_SampleDependencyTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_SDTP);
        stbl->SampleDep->sample_info = (u8*)gf_realloc(stbl->SampleDep->sample_info, sizeof(u8)*stbl->SampleSize->sampleCount );
        stbl->SampleDep->sample_info[stbl->SampleDep->sampleCount] = DepType;
        stbl->SampleDep->sampleCount = stbl->SampleSize->sampleCount;

}



//This functions unpack the offset for easy editing, eg each sample
//is contained in one chunk...
GF_Err stbl_UnpackOffsets(GF_SampleTableBox *stbl)
{
        GF_Err e;
        u8 isEdited;
        u32 i, chunkNumber, sampleDescIndex;
        u64 dataOffset;
        GF_StscEntry *ent;
        GF_ChunkOffsetBox *stco_tmp;
        GF_ChunkLargeOffsetBox *co64_tmp;
        GF_SampleToChunkBox *stsc_tmp;

        if (!stbl) return GF_ISOM_INVALID_FILE;

        //we should have none of the mandatory boxes (allowed in the spec)
        if (!stbl->ChunkOffset && !stbl->SampleDescription && !stbl->SampleSize && !stbl->SampleToChunk && !stbl->TimeToSample)
                return GF_OK;
        /*empty track (just created)*/
        if (!stbl->SampleToChunk && !stbl->TimeToSample) return GF_OK;

        //or all the mandatory ones ...
        if (!stbl->ChunkOffset || !stbl->SampleDescription || !stbl->SampleSize || !stbl->SampleToChunk || !stbl->TimeToSample)
                return GF_ISOM_INVALID_FILE;

        //do we need to unpack? Not if we have only one sample per chunk.
        if (stbl->SampleSize->sampleCount == stbl->SampleToChunk->nb_entries) return GF_OK;

        //check the offset type and create a new table...
        if (stbl->ChunkOffset->type == GF_ISOM_BOX_TYPE_STCO) {
                co64_tmp = NULL;
                stco_tmp = (GF_ChunkOffsetBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STCO);
                if (!stco_tmp) return GF_OUT_OF_MEM;
                stco_tmp->nb_entries = stbl->SampleSize->sampleCount;
                stco_tmp->offsets = (u32*)gf_malloc(stco_tmp->nb_entries * sizeof(u32));
                if (!stco_tmp->offsets) {
                        gf_isom_box_del((GF_Box*)stco_tmp);
                        return GF_OUT_OF_MEM;
                }
                stco_tmp->alloc_size = stco_tmp->nb_entries;
        } else if (stbl->ChunkOffset->type == GF_ISOM_BOX_TYPE_CO64) {
                stco_tmp = NULL;
                co64_tmp = (GF_ChunkLargeOffsetBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_CO64);
                if (!co64_tmp) return GF_OUT_OF_MEM;
                co64_tmp->nb_entries = stbl->SampleSize->sampleCount;
                co64_tmp->offsets = (u64*)gf_malloc(co64_tmp->nb_entries * sizeof(u64));
                if (!co64_tmp->offsets) {
                        gf_isom_box_del((GF_Box*)co64_tmp);
                        return GF_OUT_OF_MEM;
                }
                co64_tmp->alloc_size = co64_tmp->nb_entries;
        } else {
                return GF_ISOM_INVALID_FILE;
        }

        //create a new SampleToChunk table
        stsc_tmp = (GF_SampleToChunkBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSC);

        stsc_tmp->nb_entries = stsc_tmp->alloc_size = stbl->SampleSize->sampleCount;
        stsc_tmp->entries = gf_malloc(sizeof(GF_StscEntry)*stsc_tmp->nb_entries);
        if (!stsc_tmp->entries) return GF_OUT_OF_MEM;

        //OK write our two tables...
        ent = NULL;
        for (i = 0; i < stbl->SampleSize->sampleCount; i++) {
                //get the data info for the sample
                e = stbl_GetSampleInfos(stbl, i+1, &dataOffset, &chunkNumber, &sampleDescIndex, &isEdited);
                if (e) goto err_exit;
                ent = &stsc_tmp->entries[i];
                ent->isEdited = 0;
                ent->sampleDescriptionIndex = sampleDescIndex;
                //here's the trick: each sample is in ONE chunk
                ent->firstChunk = i+1;
                ent->nextChunk = i+2;
                ent->samplesPerChunk = 1;
                if (stco_tmp) {
                        stco_tmp->offsets[i] = (u32) dataOffset;
                } else {
                        co64_tmp->offsets[i] = dataOffset;
                }
        }
        //close the list
        if (ent) ent->nextChunk = 0;


        //done, remove our previous tables
        gf_isom_box_del(stbl->ChunkOffset);
        gf_isom_box_del((GF_Box *)stbl->SampleToChunk);
        //and set these ones...
        if (stco_tmp) {
                stbl->ChunkOffset = (GF_Box *)stco_tmp;
        } else {
                stbl->ChunkOffset = (GF_Box *)co64_tmp;
        }
        stbl->SampleToChunk = stsc_tmp;
        stbl->SampleToChunk->currentIndex = 0;
        stbl->SampleToChunk->currentChunk = 0;
        stbl->SampleToChunk->firstSampleInCurrentChunk = 0;
        return GF_OK;

err_exit:
        if (stco_tmp) gf_isom_box_del((GF_Box *) stco_tmp);
        if (co64_tmp) gf_isom_box_del((GF_Box *) co64_tmp);
        if (stsc_tmp) gf_isom_box_del((GF_Box *) stsc_tmp);
        return e;
}

#ifndef GPAC_DISABLE_ISOM_WRITE

static GFINLINE GF_Err stbl_AddOffset(GF_Box **a, u64 offset)
{
        GF_ChunkOffsetBox *stco;
        GF_ChunkLargeOffsetBox *co64;
        u32 i;

        if ((*a)->type == GF_ISOM_BOX_TYPE_STCO) {
                stco = (GF_ChunkOffsetBox *) *a;
                //if dataOffset is bigger than 0xFFFFFFFF, move to LARGE offset
                if (offset > 0xFFFFFFFF) {
                        co64 = (GF_ChunkLargeOffsetBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_CO64);
                        if (!co64) return GF_OUT_OF_MEM;
                        co64->nb_entries = stco->nb_entries + 1;
                        co64->alloc_size = co64->nb_entries;
                        co64->offsets = (u64*)gf_malloc(co64->nb_entries * sizeof(u64));
                        if (!co64->offsets) {
                                gf_isom_box_del((GF_Box *)co64);
                                return GF_OUT_OF_MEM;
                        }
                        for (i = 0; i< co64->nb_entries - 1; i++) {
                                co64->offsets[i] = (u64) stco->offsets[i];
                        }
                        co64->offsets[i] = offset;
                        //delete the box...
                        gf_isom_box_del(*a);
                        *a = (GF_Box *)co64;
                        return GF_OK;
                }
                //OK, stick with regular...
                if (stco->nb_entries==stco->alloc_size) {
                        ALLOC_INC(stco->alloc_size);
                        stco->offsets = (u32*)gf_realloc(stco->offsets, stco->alloc_size * sizeof(u32));
                        if (!stco->offsets) return GF_OUT_OF_MEM;
                        memset(&stco->offsets[stco->nb_entries], 0, (stco->alloc_size - stco->nb_entries) * sizeof(u32));
                }

                stco->offsets[stco->nb_entries] = (u32) offset;
                stco->nb_entries += 1;
        } else {
                //this is a large offset
                co64 = (GF_ChunkLargeOffsetBox *) *a;
                if (co64->nb_entries==co64->alloc_size) {
                        ALLOC_INC(co64->alloc_size);
                        co64->offsets = (u64*)gf_realloc(co64->offsets, co64->alloc_size * sizeof(u64));
                        if (!co64->offsets) return GF_OUT_OF_MEM;
                        memset(&co64->offsets[co64->nb_entries], 0, (co64->alloc_size - co64->nb_entries) * sizeof(u64) );
                }
                co64->offsets[co64->nb_entries] = offset;
                co64->nb_entries += 1;
        }
        return GF_OK;
}

//This function packs the offset after easy editing, eg samples
//are re-arranged in chunks according to the chunkOffsets
//NOTE: this has to be called once interleaving or whatever is done and
//the final MDAT is written!!!
GF_Err stbl_SetChunkAndOffset(GF_SampleTableBox *stbl, u32 sampleNumber, u32 StreamDescIndex, GF_SampleToChunkBox *the_stsc, GF_Box **the_stco, u64 data_offset, u8 forceNewChunk)
{
        GF_Err e;
        u8 newChunk;
        GF_StscEntry *ent, *newEnt, *cur_ent;

        if (!stbl) return GF_ISOM_INVALID_FILE;

        newChunk = 0;
        //do we need a new chunk ??? For that, we need
        //1 - make sure this sample data is contiguous to the prev one

        //force new chunk is set during writing (flat / interleaved)
        //it is set to 1 when data is not contiguous in the media (eg, interleaving)
        //when writing flat files, it is never used
        if (forceNewChunk) newChunk = 1;

        cur_ent = NULL;
        //2 - make sure we have the table inited (i=0)
        if (! the_stsc->entries) {
                newChunk = 1;
        } else {
                cur_ent = &the_stsc->entries[the_stsc->nb_entries - 1];
                //3 - make sure we do not exceed the MaxSamplesPerChunk and we have the same descIndex
                if (StreamDescIndex != cur_ent->sampleDescriptionIndex)
                        newChunk = 1;
                if (stbl->MaxSamplePerChunk && cur_ent->samplesPerChunk == stbl->MaxSamplePerChunk)
                        newChunk = 1;
        }

        //no need for a new chunk
        if (!newChunk) {
                cur_ent->samplesPerChunk += 1;
                return GF_OK;
        }

        //OK, we have to create a new chunk...
        //check if we can remove the current sampleToChunk entry (same properties)
        if (the_stsc->nb_entries > 1) {
                ent = &the_stsc->entries[the_stsc->nb_entries - 2];
                if (!ent) return GF_OUT_OF_MEM;
                if ( (ent->sampleDescriptionIndex == cur_ent->sampleDescriptionIndex)
                        && (ent->samplesPerChunk == cur_ent->samplesPerChunk)
                   ) {
                        //OK, it's the same SampleToChunk, so delete it
                        ent->nextChunk = cur_ent->firstChunk;
                        the_stsc->nb_entries--;
                }
        }

        //add our offset
        e = stbl_AddOffset(the_stco, data_offset);
        if (e) return e;

        if (the_stsc->nb_entries==the_stsc->alloc_size) {
                ALLOC_INC(the_stsc->alloc_size);
                the_stsc->entries = gf_realloc(the_stsc->entries, sizeof(GF_StscEntry)*the_stsc->alloc_size);
                if (!the_stsc->entries) return GF_OUT_OF_MEM;
                memset(&the_stsc->entries[the_stsc->nb_entries], 0, sizeof(GF_StscEntry)*(the_stsc->alloc_size-the_stsc->nb_entries));
        }
        //create a new entry (could be the first one, BTW)
        newEnt = &the_stsc->entries[the_stsc->nb_entries];
        if (!newEnt) return GF_OUT_OF_MEM;

        //get the first chunk value
        if ((*the_stco)->type == GF_ISOM_BOX_TYPE_STCO) {
                newEnt->firstChunk = ((GF_ChunkOffsetBox *) (*the_stco) )->nb_entries;
        } else {
                newEnt->firstChunk = ((GF_ChunkLargeOffsetBox *) (*the_stco) )->nb_entries;
        }
        newEnt->sampleDescriptionIndex = StreamDescIndex;
        newEnt->samplesPerChunk = 1;
        newEnt->nextChunk = 0;
        //if we already have an entry, adjust its next chunk to point to our new chunk
        if (the_stsc->nb_entries)
                the_stsc->entries[the_stsc->nb_entries-1].nextChunk = newEnt->firstChunk;
        the_stsc->nb_entries++;
        return GF_OK;
}


GF_Err gf_isom_refresh_size_info(GF_ISOFile *file, u32 trackNumber)
{
        u32 i, size;
        GF_TrackBox *trak;
        GF_SampleSizeBox *stsz;
        trak = gf_isom_get_track_from_file(file, trackNumber);
        if (!trak) return GF_BAD_PARAM;

        stsz = trak->Media->information->sampleTable->SampleSize;
        if (stsz->sampleSize || !stsz->sampleCount) return GF_OK;

        size = stsz->sizes[0];
        for (i=1; i<stsz->sampleCount; i++) {
                if (stsz->sizes[i] != size) {
                        size = 0;
                        break;
                }
        }
        if (size) {
                gf_free(stsz->sizes);
                stsz->sizes = NULL;
                stsz->sampleSize = size;
        }
        return GF_OK;
}

#endif /*GPAC_DISABLE_ISOM_WRITE*/

#endif /*GPAC_DISABLE_ISOM*/

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