root/src/isomedia/isom_store.c

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

DEFINITIONS

This source file includes following definitions.
  1. gf_isom_insert_copyright
  2. CleanWriters
  3. ResetWriters
  4. SetupWriters
  5. ShiftMetaOffset
  6. ShiftOffset
  7. WriteMoovAndMeta
  8. GetMoovAndMetaSize
  9. WriteSample
  10. DoWriteMeta
  11. DoWrite
  12. WriteFlat
  13. DoFullInterleave
  14. DoInterleave
  15. WriteInterleaved
  16. WriteToFile


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

#if !defined(GPAC_DISABLE_ISOM) && !defined(GPAC_DISABLE_ISOM_WRITE)

#define GPAC_ISOM_CPRT_NOTICE "IsoMedia File Produced with GPAC"
#define GPAC_ISOM_CPRT_NOTICE_VERSION GPAC_ISOM_CPRT_NOTICE" "GPAC_FULL_VERSION

static GF_Err gf_isom_insert_copyright(GF_ISOFile *movie)
{
        u32 i;
        GF_Box *a;
        GF_FreeSpaceBox *_free;
        i=0;
        while ((a = (GF_Box *)gf_list_enum(movie->TopBoxes, &i))) {
                if (a->type == GF_ISOM_BOX_TYPE_FREE) {
                        _free = (GF_FreeSpaceBox *)a;
                        if (_free->dataSize) {
                                if (!strcmp(_free->data, GPAC_ISOM_CPRT_NOTICE_VERSION)) return GF_OK;
                                if (strstr(_free->data, GPAC_ISOM_CPRT_NOTICE)) {
                                        gf_free(_free->data);
                                        _free->data = gf_strdup(movie->drop_date_version_info ? GPAC_ISOM_CPRT_NOTICE : GPAC_ISOM_CPRT_NOTICE_VERSION);
                                        _free->dataSize = 1 + (u32) strlen(_free->data);
                                        return GF_OK;
                                }
                        }
                }
        }
        a = gf_isom_box_new(GF_ISOM_BOX_TYPE_FREE);
        if (!a) return GF_OUT_OF_MEM;
        _free = (GF_FreeSpaceBox *)a;
        _free->data = gf_strdup(movie->drop_date_version_info ? GPAC_ISOM_CPRT_NOTICE : GPAC_ISOM_CPRT_NOTICE_VERSION);
        _free->dataSize = (u32) strlen(_free->data) + 1;
        if (!_free->data) return GF_OUT_OF_MEM;
        return gf_list_add(movie->TopBoxes, _free);
}

typedef struct
{
        /*the curent sample of this track*/
        u32 sampleNumber;
        /*timeScale of the media (for interleaving)*/
        u32 timeScale;
        /*this is for generic, time-based interleaving. Expressed in Media TimeScale*/
        u32 chunkDur;
        u64 DTSprev;
        u8 isDone;
        u64 prev_offset;
        GF_MediaBox *mdia;
        /*each writer has a sampleToChunck and ChunkOffset tables
        these tables are filled during emulation mode and then will     replace the table in the GF_SampleTableBox*/
        GF_SampleToChunkBox *stsc;
        /*we don't know if it's a large offset or not*/
        GF_Box *stco;
} TrackWriter;

typedef struct
{
        char *buffer;
        u32 size;
        GF_ISOFile *movie;
        u32 total_samples, nb_done;
} MovieWriter;

void CleanWriters(GF_List *writers)
{
        TrackWriter *writer;
        while (gf_list_count(writers)) {
                writer = (TrackWriter*)gf_list_get(writers, 0);
                gf_isom_box_del(writer->stco);
                gf_isom_box_del((GF_Box *)writer->stsc);
                gf_free(writer);
                gf_list_rem(writers, 0);
        }
}

void ResetWriters(GF_List *writers)
{
        u32 i;
        TrackWriter *writer;
        i=0;
        while ((writer = (TrackWriter *)gf_list_enum(writers, &i))) {
                writer->isDone = 0;
                writer->chunkDur = 0;
                writer->DTSprev = 0;
                writer->sampleNumber = 1;
                gf_isom_box_del((GF_Box *)writer->stsc);
                writer->stsc = (GF_SampleToChunkBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSC);
                if (writer->stco->type == GF_ISOM_BOX_TYPE_STCO) {
                        gf_free(((GF_ChunkOffsetBox *)writer->stco)->offsets);
                        ((GF_ChunkOffsetBox *)writer->stco)->offsets = NULL;
                        ((GF_ChunkOffsetBox *)writer->stco)->nb_entries = 0;
                        ((GF_ChunkOffsetBox *)writer->stco)->alloc_size = 0;
                } else {
                        gf_free(((GF_ChunkLargeOffsetBox *)writer->stco)->offsets);
                        ((GF_ChunkLargeOffsetBox *)writer->stco)->offsets = NULL;
                        ((GF_ChunkLargeOffsetBox *)writer->stco)->nb_entries = 0;
                        ((GF_ChunkLargeOffsetBox *)writer->stco)->alloc_size = 0;
                }
        }
}

GF_Err SetupWriters(MovieWriter *mw, GF_List *writers, u8 interleaving)
{
        u32 i, trackCount;
        TrackWriter *writer;
        GF_TrackBox *trak;
        GF_ISOFile *movie = mw->movie;

        mw->total_samples = mw->nb_done = 0;
        if (!movie->moov) return GF_OK;

        trackCount = gf_list_count(movie->moov->trackList);
        for (i = 0; i < trackCount; i++) {
                trak = gf_isom_get_track(movie->moov, i+1);

                GF_SAFEALLOC(writer, TrackWriter);
                if (!writer) goto exit;
                writer->sampleNumber = 1;
                writer->mdia = trak->Media;
                writer->timeScale = trak->Media->mediaHeader->timeScale;
                writer->isDone = 0;
                writer->DTSprev = 0;
                writer->chunkDur = 0;
                writer->stsc = (GF_SampleToChunkBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSC);
                if (trak->Media->information->sampleTable->ChunkOffset->type == GF_ISOM_BOX_TYPE_STCO) {
                        writer->stco = gf_isom_box_new(GF_ISOM_BOX_TYPE_STCO);
                } else {
                        writer->stco = gf_isom_box_new(GF_ISOM_BOX_TYPE_CO64);
                }
                /*stops from chunk escape*/
                if (interleaving) writer->mdia->information->sampleTable->MaxSamplePerChunk = 0;
                /*for progress, assume only one descIndex*/
                if (Media_IsSelfContained(writer->mdia, 1)) mw->total_samples += trak->Media->information->sampleTable->SampleSize->sampleCount;
                /*optimization for interleaving: put audio last (this can be overriden by priorities)*/
                if (movie->storageMode != GF_ISOM_STORE_INTERLEAVED) {
                        gf_list_add(writers, writer);
                } else {
                        if (writer->mdia->information->InfoHeader && writer->mdia->information->InfoHeader->type == GF_ISOM_BOX_TYPE_SMHD) {
                                gf_list_add(writers, writer);
                        } else {
                                gf_list_insert(writers, writer, 0);
                        }
                }
        }
        return GF_OK;

exit:
        CleanWriters(writers);
        return GF_OUT_OF_MEM;
}


static void ShiftMetaOffset(GF_MetaBox *meta, u64 offset)
{
        u32 i, count;
        if (!meta->item_locations) return;

        count = gf_list_count(meta->item_locations->location_entries);
        for (i=0; i<count; i++) {
                GF_ItemLocationEntry *iloc = (GF_ItemLocationEntry *)gf_list_get(meta->item_locations->location_entries, i);
                if (iloc->data_reference_index) continue;
                if (iloc->construction_method == 2) continue;
                if (!iloc->base_offset) {
                        GF_ItemExtentEntry *entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0);
                        if (entry && !entry->extent_length && !entry->original_extent_offset && (gf_list_count(iloc->extent_entries)==1) )
                                continue;
                }

                iloc->base_offset += offset;
        }
}

static GF_Err ShiftOffset(GF_ISOFile *file, GF_List *writers, u64 offset)
{
        u32 i, j, k, l, last;
        TrackWriter *writer;
        GF_StscEntry *ent;

        if (file->meta) ShiftMetaOffset(file->meta, offset);
        if (file->moov && file->moov->meta) ShiftMetaOffset(file->moov->meta, offset);

        i=0;
        while ((writer = (TrackWriter *)gf_list_enum(writers, &i))) {
                if (writer->mdia->mediaTrack->meta) ShiftMetaOffset(writer->mdia->mediaTrack->meta, offset);

                //we have to proceed entry by entry in case a part of the media is not self-contained...
                for (j=0; j<writer->stsc->nb_entries; j++) {
                        ent = &writer->stsc->entries[j];
                        if (!Media_IsSelfContained(writer->mdia, ent->sampleDescriptionIndex)) continue;

                        //OK, get the chunk(s) number(s) and "shift" its (their) offset(s).
                        if (writer->stco->type == GF_ISOM_BOX_TYPE_STCO) {
                                GF_ChunkLargeOffsetBox *new_stco64 = NULL;
                                GF_ChunkOffsetBox *stco = (GF_ChunkOffsetBox *) writer->stco;

                                //be carefull for the last entry, nextChunk is set to 0 in edit mode...
                                last = ent->nextChunk ? ent->nextChunk : stco->nb_entries + 1;
                                for (k = ent->firstChunk; k < last; k++) {

                                        //we need to rewrite the table: only allocate co64 if not done previously and convert all offsets
                                        //to co64. Then (whether co64 was created or not) adjust the offset
                                        //Do not reassign table until we are done with the current sampleToChunk processing
                                        //since we have a test on stco->offsets[k-1], we need to keep stco untouched
                                        if (new_stco64 || file->force_co64 || (stco->offsets[k-1] + offset > 0xFFFFFFFF)) {
                                                if (!new_stco64) {
                                                        new_stco64 = (GF_ChunkLargeOffsetBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_CO64);
                                                        if (!new_stco64) return GF_OUT_OF_MEM;
                                                        new_stco64->nb_entries = stco->nb_entries;
                                                        new_stco64->offsets = (u64 *) gf_malloc(new_stco64->nb_entries * sizeof(u64));
                                                        if (!new_stco64->offsets) return GF_OUT_OF_MEM;
                                                        //copy over the stco table
                                                        for (l = 0; l < new_stco64->nb_entries; l++) {
                                                                new_stco64->offsets[l] = (u64) stco->offsets[l];
                                                        }
                                                }
                                                new_stco64->offsets[k-1] += offset;
                                        } else {
                                                stco->offsets[k-1] += (u32) offset;
                                        }
                                }
                                if (new_stco64) {
                                        //done with this sampleToChunk entry, replace the box if we moved to co64
                                        gf_isom_box_del(writer->stco);
                                        writer->stco = (GF_Box *)new_stco64;
                                        new_stco64 = NULL;
                                }
                        } else {
                                GF_ChunkLargeOffsetBox *stco64 = (GF_ChunkLargeOffsetBox *) writer->stco;
                                //be carefull for the last entry ...
                                last = ent->nextChunk ? ent->nextChunk : stco64->nb_entries + 1;
                                for (k = ent->firstChunk; k < last; k++) {
                                        stco64->offsets[k-1] += offset;
                                }
                        }
                }
        }

        return GF_OK;

}


//replace the chunk and offset tables...
static GF_Err WriteMoovAndMeta(GF_ISOFile *movie, GF_List *writers, GF_BitStream *bs)
{
        u32 i;
        TrackWriter *writer;
        GF_Err e;
        GF_Box *stco;
        GF_SampleToChunkBox *stsc;

        if (movie->meta) {
                //write the moov box...
                e = gf_isom_box_size((GF_Box *)movie->meta);
                if (e) return e;
                e = gf_isom_box_write((GF_Box *)movie->meta, bs);
                if (e) return e;
        }

        if (movie->moov) {
                //switch all our tables
                i=0;
                while ((writer = (TrackWriter*)gf_list_enum(writers, &i))) {
                        //don't delete them !!!
                        stsc = writer->mdia->information->sampleTable->SampleToChunk;
                        stco = writer->mdia->information->sampleTable->ChunkOffset;
                        writer->mdia->information->sampleTable->SampleToChunk = writer->stsc;
                        writer->mdia->information->sampleTable->ChunkOffset = writer->stco;
                        writer->stco = stco;
                        writer->stsc = stsc;
                }
                //write the moov box...
                e = gf_isom_box_size((GF_Box *)movie->moov);
                if (e) return e;
                e = gf_isom_box_write((GF_Box *)movie->moov, bs);
                //and re-switch our table. We have to do it that way because it is
                //needed when the moov is written first
                i=0;
                while ((writer = (TrackWriter*)gf_list_enum(writers, &i))) {
                        //don't delete them !!!
                        stsc = writer->stsc;
                        stco = writer->stco;
                        writer->stsc = writer->mdia->information->sampleTable->SampleToChunk;
                        writer->stco = writer->mdia->information->sampleTable->ChunkOffset;
                        writer->mdia->information->sampleTable->SampleToChunk = stsc;
                        writer->mdia->information->sampleTable->ChunkOffset = stco;
                }
                if (e) return e;
        }
        return GF_OK;
}

//compute the size of the moov as it will be written.
u64 GetMoovAndMetaSize(GF_ISOFile *movie, GF_List *writers)
{
        u32 i;
        u64 size;
        TrackWriter *writer;

        size = 0;
        if (movie->moov) {
                gf_isom_box_size((GF_Box *)movie->moov);
                size = movie->moov->size;
                if (size > 0xFFFFFFFF) size += 8;

                i=0;
                while ((writer = (TrackWriter*)gf_list_enum(writers, &i))) {
                        size -= writer->mdia->information->sampleTable->ChunkOffset->size;
                        size -= writer->mdia->information->sampleTable->SampleToChunk->size;
                        gf_isom_box_size((GF_Box *)writer->stsc);
                        gf_isom_box_size(writer->stco);
                        size += writer->stsc->size;
                        size += writer->stco->size;
                }
        }
        if (movie->meta) {
                u64 msize;
                gf_isom_box_size((GF_Box *)movie->meta);
                msize = movie->meta->size;
                if (msize > 0xFFFFFFFF) msize += 8;
                size += msize;
        }
        return size;
}

//Write a sample to the file - this is only called for self-contained media
GF_Err WriteSample(MovieWriter *mw, u32 size, u64 offset, u8 isEdited, GF_BitStream *bs)
{
        GF_DataMap *map;
        u32 bytes;

        if (!size) return GF_OK;

        if (size>mw->size) {
                mw->buffer = (char*)gf_realloc(mw->buffer, size);
                mw->size = size;
        }

        if (!mw->buffer) return GF_OUT_OF_MEM;

        if (isEdited) {
                map = mw->movie->editFileMap;
        } else {
                map = mw->movie->movieFileMap;
        }
        //get the payload...
        bytes = gf_isom_datamap_get_data(map, mw->buffer, size, offset);
        if (bytes != size)
                return GF_IO_ERR;
        //write it to our stream...
        bytes = gf_bs_write_data(bs, mw->buffer, size);
        if (bytes != size)
                return GF_IO_ERR;

        mw->nb_done++;
        gf_set_progress("ISO File Writing", mw->nb_done, mw->total_samples);
        return GF_OK;
}


GF_Err DoWriteMeta(GF_ISOFile *file, GF_MetaBox *meta, GF_BitStream *bs, Bool Emulation, u64 baseOffset, u64 *mdatSize)
{
        GF_ItemExtentEntry *entry;
        u64 maxExtendOffset, maxExtendSize;
        u32 i, j, count;

        maxExtendOffset = 0;
        maxExtendSize = 0;
        *mdatSize = 0;
        if (!meta->item_locations) return GF_OK;

        count = gf_list_count(meta->item_locations->location_entries);
        for (i=0; i<count; i++) {
                u64 it_size;
                GF_ItemLocationEntry *iloc = (GF_ItemLocationEntry *)gf_list_get(meta->item_locations->location_entries, i);
                /*get item info*/
                GF_ItemInfoEntryBox *iinf = NULL;
                j=0;
                while ((iinf = (GF_ItemInfoEntryBox *)gf_list_enum(meta->item_infos->item_infos, &j))) {
                        if (iinf->item_ID==iloc->item_ID) break;
                        iinf = NULL;
                }

                if (!iloc->base_offset && (gf_list_count(iloc->extent_entries)==1)) {
                        entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0);
                        if (!entry->extent_length && !entry->original_extent_offset && !entry->extent_index) {
                                entry->extent_offset = 0;
                                continue;
                        }
                }

                it_size = 0;
                /*for self contained only*/
                if (!iloc->data_reference_index) {
                        if (iloc->construction_method != 2) {
                                iloc->base_offset = baseOffset;
                        }

                        /*new resource*/
                        if (iinf && iinf->full_path) {
                                FILE *src=NULL;

                                if (!iinf->data_len) {
                                        src = gf_fopen(iinf->full_path, "rb");
                                        if (!src) continue;
                                        gf_fseek(src, 0, SEEK_END);
                                        it_size = gf_ftell(src);
                                        gf_fseek(src, 0, SEEK_SET);
                                } else {
                                        it_size = iinf->data_len;
                                }
                                if (maxExtendSize<it_size) maxExtendSize = it_size;

                                if (!gf_list_count(iloc->extent_entries)) {
                                        GF_SAFEALLOC(entry, GF_ItemExtentEntry);
                                        gf_list_add(iloc->extent_entries, entry);
                                }
                                entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0);
                                entry->extent_offset = 0;
                                entry->extent_length = it_size;

                                /*OK write to mdat*/
                                if (!Emulation) {
                                        if (src) {
                                                char cache_data[4096];
                                                u64 remain = entry->extent_length;
                                                while (remain) {
                                                        u32 size_cache = (remain>4096) ? 4096 : (u32) remain;
                                                        size_t read = fread(cache_data, sizeof(char), size_cache, src);
                                                        if (read ==(size_t) -1) break;
                                                        gf_bs_write_data(bs, cache_data, (u32) read);
                                                        remain -= (u32) read;
                                                }
                                        } else {
                                                gf_bs_write_data(bs, iinf->full_path, iinf->data_len);
                                        }
                                }
                                if (src) gf_fclose(src);
                        }
                        else if (gf_list_count(iloc->extent_entries)) {
                                u32 j;
                                j=0;
                                while ((entry = (GF_ItemExtentEntry *)gf_list_enum(iloc->extent_entries, &j))) {
                                        if (entry->extent_index) continue;
                                        if (j && (maxExtendOffset<it_size) ) maxExtendOffset = it_size;
                                        /*compute new offset*/
                                        entry->extent_offset = baseOffset + it_size;

                                        it_size += entry->extent_length;
                                        if (maxExtendSize<entry->extent_length) maxExtendSize = entry->extent_length;

                                        /*Reading from the input file*/
                                        if (!Emulation) {
                                                char cache_data[4096];
                                                u64 remain = entry->extent_length;
                                                gf_bs_seek(file->movieFileMap->bs, entry->original_extent_offset + iloc->original_base_offset);
                                                while (remain) {
                                                        u32 size_cache = (remain>4096) ? 4096 : (u32) remain;
                                                        gf_bs_read_data(file->movieFileMap->bs, cache_data, size_cache);
                                                        /*Writing to the output file*/
                                                        gf_bs_write_data(bs, cache_data, size_cache);
                                                        remain -= size_cache;
                                                }
                                        }
                                }
                        }
                        baseOffset += it_size;
                        *mdatSize += it_size;
                } else {
                        /*we MUST have at least one extent for the dref data*/
                        if (!gf_list_count(iloc->extent_entries)) {
                                GF_SAFEALLOC(entry, GF_ItemExtentEntry);
                                gf_list_add(iloc->extent_entries, entry);
                        }
                        entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0);
                        entry->extent_offset = 0;
                        /*0 means full length of referenced file*/
                        entry->extent_length = 0;
                }
        }
        /*update offset & size length fields*/
        if (baseOffset>0xFFFFFFFF) meta->item_locations->base_offset_size = 8;
        else if (baseOffset) meta->item_locations->base_offset_size = 4;

        if (maxExtendSize>0xFFFFFFFF) meta->item_locations->length_size = 8;
        else if (maxExtendSize) meta->item_locations->length_size = 4;

        if (maxExtendOffset>0xFFFFFFFF) meta->item_locations->offset_size = 8;
        else if (maxExtendOffset) meta->item_locations->offset_size = 4;
        return GF_OK;
}

//this function writes track by track in the order of tracks inside the moov...
GF_Err DoWrite(MovieWriter *mw, GF_List *writers, GF_BitStream *bs, u8 Emulation, u64 StartOffset)
{
        u32 i;
        GF_Err e;
        TrackWriter *writer;
        u64 offset, sampOffset, predOffset;
        u32 chunkNumber, descIndex, sampSize;
        u8 isEdited, force;
        u64 size, mdatSize = 0;
        GF_ISOFile *movie = mw->movie;

        /*write meta content first - WE DON'T support fragmentation of resources in ISOM atm*/
        if (movie->openMode != GF_ISOM_OPEN_WRITE) {
                if (movie->meta) {
                        e = DoWriteMeta(movie, movie->meta, bs, Emulation, StartOffset, &size);
                        if (e) return e;
                        mdatSize += size;
                        StartOffset += size;
                }
                if (movie->moov && movie->moov->meta) {
                        e = DoWriteMeta(movie, movie->meta, bs, Emulation, StartOffset, &size);
                        if (e) return e;
                        mdatSize += size;
                        StartOffset += size;
                }
                i=0;
                while ((writer = (TrackWriter*)gf_list_enum(writers, &i))) {
                        if (writer->mdia->mediaTrack->meta) {
                                e = DoWriteMeta(movie, movie->meta, bs, Emulation, StartOffset, &size);
                                if (e) return e;
                                mdatSize += size;
                                StartOffset += size;
                        }
                }
        }

        offset = StartOffset;
        predOffset = 0;
        i=0;
        while ((writer = (TrackWriter*)gf_list_enum(writers, &i))) {
                while (!writer->isDone) {
                        //To Check: are empty sample tables allowed ???
                        if (writer->sampleNumber > writer->mdia->information->sampleTable->SampleSize->sampleCount) {
                                writer->isDone = 1;
                                continue;
                        }
                        e = stbl_GetSampleInfos(writer->mdia->information->sampleTable, writer->sampleNumber, &sampOffset, &chunkNumber, &descIndex, &isEdited);
                        if (e) return e;
                        e = stbl_GetSampleSize(writer->mdia->information->sampleTable->SampleSize, writer->sampleNumber, &sampSize);
                        if (e) return e;

                        //update our chunks.
                        force = 0;
                        if (movie->openMode == GF_ISOM_OPEN_WRITE) {
                                offset = sampOffset;
                                if (predOffset != offset)
                                        force = 1;
                        }
                        //update our global offset...
                        if (Media_IsSelfContained(writer->mdia, descIndex) ) {
                                e = stbl_SetChunkAndOffset(writer->mdia->information->sampleTable, writer->sampleNumber, descIndex, writer->stsc, &writer->stco, offset, force);
                                if (e) return e;
                                if (movie->openMode == GF_ISOM_OPEN_WRITE) {
                                        predOffset = sampOffset + sampSize;
                                } else {
                                        offset += sampSize;
                                        mdatSize += sampSize;
                                }
                        } else {
                                if (predOffset != offset) force = 1;
                                predOffset = sampOffset + sampSize;
                                //we have a DataRef, so use the offset idicated in sampleToChunk and ChunkOffset tables...
                                e = stbl_SetChunkAndOffset(writer->mdia->information->sampleTable, writer->sampleNumber, descIndex, writer->stsc, &writer->stco, sampOffset, force);
                                if (e) return e;
                        }
                        //we write the sample if not emulation
                        if (!Emulation) {
                                if (Media_IsSelfContained(writer->mdia, descIndex) ) {
                                        e = WriteSample(mw, sampSize, sampOffset, isEdited, bs);
                                        if (e) return e;
                                }
                        }
                        //ok, the track is done
                        if (writer->sampleNumber == writer->mdia->information->sampleTable->SampleSize->sampleCount) {
                                writer->isDone = 1;
                        } else {
                                writer->sampleNumber ++;
                        }
                }
        }
        //set the mdatSize...
        movie->mdat->dataSize = mdatSize;
        return GF_OK;
}


//write the file track by track, with moov box before or after the mdat
GF_Err WriteFlat(MovieWriter *mw, u8 moovFirst, GF_BitStream *bs)
{
        GF_Err e;
        u32 i;
        u64 offset, finalOffset, totSize, begin, firstSize, finalSize;
        GF_Box *a;
        GF_List *writers = gf_list_new();
        GF_ISOFile *movie = mw->movie;

        begin = totSize = 0;

        //first setup the writers
        e = SetupWriters(mw, writers, 0);
        if (e) goto exit;

        if (!moovFirst) {
                if (movie->openMode == GF_ISOM_OPEN_WRITE) {
                        begin = 0;
                        totSize = gf_isom_datamap_get_offset(movie->editFileMap);
                        /*start boxes have not been written yet, do it*/
                        if (!totSize) {
                                if (movie->is_jp2) {
                                        gf_bs_write_u32(movie->editFileMap->bs, 12);
                                        gf_bs_write_u32(movie->editFileMap->bs, GF_4CC('j','P',' ',' '));
                                        gf_bs_write_u32(movie->editFileMap->bs, 0x0D0A870A);
                                        totSize += 12;
                                        begin += 12;
                                }
                                if (movie->brand) {
                                        e = gf_isom_box_size((GF_Box *)movie->brand);
                                        if (e) goto exit;
                                        e = gf_isom_box_write((GF_Box *)movie->brand, movie->editFileMap->bs);
                                        if (e) goto exit;
                                        totSize += movie->brand->size;
                                        begin += movie->brand->size;
                                }
                                if (movie->pdin) {
                                        e = gf_isom_box_size((GF_Box *)movie->pdin);
                                        if (e) goto exit;
                                        e = gf_isom_box_write((GF_Box *)movie->pdin, movie->editFileMap->bs);
                                        if (e) goto exit;
                                        totSize += movie->pdin->size;
                                        begin += movie->pdin->size;
                                }
                        } else {
                                if (movie->is_jp2) begin += 12;
                                if (movie->brand) begin += movie->brand->size;
                                if (movie->pdin) begin += movie->pdin->size;
                        }
                        totSize -= begin;
                } else {
                        if (movie->is_jp2) {
                                gf_bs_write_u32(bs, 12);
                                gf_bs_write_u32(bs, GF_4CC('j','P',' ',' '));
                                gf_bs_write_u32(bs, 0x0D0A870A);
                        }
                        if (movie->brand) {
                                e = gf_isom_box_size((GF_Box *)movie->brand);
                                if (e) goto exit;
                                e = gf_isom_box_write((GF_Box *)movie->brand, bs);
                                if (e) goto exit;
                        }
                        /*then progressive download*/
                        if (movie->pdin) {
                                e = gf_isom_box_size((GF_Box *)movie->pdin);
                                if (e) goto exit;
                                e = gf_isom_box_write((GF_Box *)movie->pdin, bs);
                                if (e) goto exit;
                        }
                }

                //if the moov is at the end, write directly
                i=0;
                while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
                        switch (a->type) {
                        /*written by hand*/
                        case GF_ISOM_BOX_TYPE_MOOV:
                        case GF_ISOM_BOX_TYPE_META:
                        case GF_ISOM_BOX_TYPE_FTYP:
                        case GF_ISOM_BOX_TYPE_PDIN:
#ifndef GPAC_DISABLE_ISOM_ADOBE
                        case GF_ISOM_BOX_TYPE_AFRA:
                        case GF_ISOM_BOX_TYPE_ABST:
#endif
                                break;
                        case GF_ISOM_BOX_TYPE_MDAT:
                                //in case we're capturing
                                if (movie->openMode == GF_ISOM_OPEN_WRITE) {
                                        //emulate a write to recreate our tables (media data already written)
                                        e = DoWrite(mw, writers, bs, 1, begin);
                                        if (e) goto exit;
                                        continue;
                                }
                                //to avoid computing the size each time write always 4 + 4 + 8 bytes before
                                begin = gf_bs_get_position(bs);
                                gf_bs_write_u64(bs, 0);
                                gf_bs_write_u64(bs, 0);
                                e = DoWrite(mw, writers, bs, 0, gf_bs_get_position(bs));
                                if (e) goto exit;
                                totSize = gf_bs_get_position(bs) - begin;
                                break;
                        default:
                                e = gf_isom_box_size(a);
                                if (e) goto exit;
                                e = gf_isom_box_write(a, bs);
                                if (e) goto exit;
                                break;
                        }
                }

                //OK, write the movie box.
                e = WriteMoovAndMeta(movie, writers, bs);
                if (e) goto exit;

#ifndef GPAC_DISABLE_ISOM_ADOBE
                i=0;
                while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
                        switch (a->type) {
                        case GF_ISOM_BOX_TYPE_AFRA:
                        case GF_ISOM_BOX_TYPE_ABST:
                                e = gf_isom_box_size(a);
                                if (e) goto exit;
                                e = gf_isom_box_write(a, bs);
                                if (e) goto exit;
                                break;
                        }
                }
#endif

                /*if data has been written, update mdat size*/
                if (totSize) {
                        offset = gf_bs_get_position(bs);
                        e = gf_bs_seek(bs, begin);
                        if (e) goto exit;
                        if (totSize > 0xFFFFFFFF) {
                                gf_bs_write_u32(bs, 1);
                        } else {
                                gf_bs_write_u32(bs, (u32) totSize);
                        }
                        gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_MDAT);
                        if (totSize > 0xFFFFFFFF) gf_bs_write_u64(bs, totSize);
                        e = gf_bs_seek(bs, offset);
                }
                movie->mdat->size = totSize;
                goto exit;
        }

        //nope, we have to write the moov first. The pb is that
        //1 - we don't know its size till the mdat is written
        //2 - we don't know the ofset at which the mdat will start...
        //3 - once the mdat is written, the chunkOffset table can have changed...

        if (movie->is_jp2) {
                gf_bs_write_u32(bs, 12);
                gf_bs_write_u32(bs, GF_4CC('j','P',' ',' '));
                gf_bs_write_u32(bs, 0x0D0A870A);
        }
        if (movie->brand) {
                e = gf_isom_box_size((GF_Box *)movie->brand);
                if (e) goto exit;
                e = gf_isom_box_write((GF_Box *)movie->brand, bs);
                if (e) goto exit;
        }
        /*then progressive dnload*/
        if (movie->pdin) {
                e = gf_isom_box_size((GF_Box *)movie->pdin);
                if (e) goto exit;
                e = gf_isom_box_write((GF_Box *)movie->pdin, bs);
                if (e) goto exit;
        }
        //What we will do is first emulate the write from the beginning...
        //note: this will set the size of the mdat
        e = DoWrite(mw, writers, bs, 1, gf_bs_get_position(bs));
        if (e) goto exit;

        firstSize = GetMoovAndMetaSize(movie, writers);
        //offset = (firstSize > 0xFFFFFFFF ? firstSize + 8 : firstSize) + 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0);
        offset = firstSize + 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0);
        e = ShiftOffset(movie, writers, offset);
        if (e) goto exit;
        //get the size and see if it has changed (eg, we moved to 64 bit offsets)
        finalSize = GetMoovAndMetaSize(movie, writers);
        if (firstSize != finalSize) {
                finalOffset = finalSize + 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0);
                //OK, now we're sure about the final size.
                //we don't need to re-emulate, as the only thing that changed is the offset
                //so just shift the offset
                e = ShiftOffset(movie, writers, finalOffset - offset);
                if (e) goto exit;
        }
        //now write our stuff
        e = WriteMoovAndMeta(movie, writers, bs);
        if (e) goto exit;
        e = gf_isom_box_size((GF_Box *)movie->mdat);
        if (e) goto exit;
        e = gf_isom_box_write((GF_Box *)movie->mdat, bs);
        if (e) goto exit;

        //we don't need the offset as the moov is already written...
        ResetWriters(writers);
        e = DoWrite(mw, writers, bs, 0, 0);
        if (e) goto exit;
        //then the rest
        i=0;
        while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
                switch (a->type) {
                case GF_ISOM_BOX_TYPE_MOOV:
                case GF_ISOM_BOX_TYPE_META:
                case GF_ISOM_BOX_TYPE_FTYP:
                case GF_ISOM_BOX_TYPE_PDIN:
                case GF_ISOM_BOX_TYPE_MDAT:
                        break;
                default:
                        e = gf_isom_box_size(a);
                        if (e) goto exit;
                        e = gf_isom_box_write(a, bs);
                        if (e) goto exit;
                }
        }

exit:
        CleanWriters(writers);
        gf_list_del(writers);
        return e;
}

GF_Err DoFullInterleave(MovieWriter *mw, GF_List *writers, GF_BitStream *bs, u8 Emulation, u64 StartOffset)
{

        u32 i, tracksDone;
        TrackWriter *tmp, *curWriter, *prevWriter;
        GF_Err e;
        u64 DTS, DTStmp, TStmp;
        s64 res;
        u32 descIndex, sampSize, chunkNumber;
        u16 curGroupID, curTrackPriority;
        u8 forceNewChunk, writeGroup, isEdited;
        //this is used to emulate the write ...
        u64 offset, totSize, sampOffset;
        GF_ISOFile *movie = mw->movie;

        totSize = 0;
        curGroupID = 1;

        prevWriter = NULL;
        //we emulate a write from this offset...
        offset = StartOffset;
        tracksDone = 0;

        //browse each groups
        while (1) {
                writeGroup = 1;

                //proceed a group
                while (writeGroup) {
                        //first get the appropriated sample for the min time in this group
                        curWriter = NULL;
                        DTStmp = (u64) -1;
                        TStmp = 0;
                        curTrackPriority = (u16) -1;

                        i=0;
                        while ((tmp = (TrackWriter*)gf_list_enum(writers, &i))) {

                                //is it done writing ?
                                //is it in our group ??
                                if (tmp->isDone || tmp->mdia->information->sampleTable->groupID != curGroupID) continue;

                                //OK, get the current sample in this track
                                stbl_GetSampleDTS(tmp->mdia->information->sampleTable->TimeToSample, tmp->sampleNumber, &DTS);
                                res = TStmp ? DTStmp * tmp->timeScale - DTS * TStmp : 0;
                                if (res < 0) continue;
                                if ((!res) && curTrackPriority <= tmp->mdia->information->sampleTable->trackPriority) continue;
                                curWriter = tmp;
                                curTrackPriority = tmp->mdia->information->sampleTable->trackPriority;
                                DTStmp = DTS;
                                TStmp = tmp->timeScale;
                        }
                        //no sample found, we're done with this group
                        if (!curWriter) {
                                //we're done with the group
                                writeGroup = 0;
                                continue;
                        }
                        //To Check: are empty sample tables allowed ???
                        if (curWriter->sampleNumber > curWriter->mdia->information->sampleTable->SampleSize->sampleCount) {
                                curWriter->isDone = 1;
                                tracksDone ++;
                                continue;
                        }

                        e = stbl_GetSampleInfos(curWriter->mdia->information->sampleTable, curWriter->sampleNumber, &sampOffset, &chunkNumber, &descIndex, &isEdited);
                        if (e) return e;
                        e = stbl_GetSampleSize(curWriter->mdia->information->sampleTable->SampleSize, curWriter->sampleNumber, &sampSize);
                        if (e) return e;

                        //do we actually write, or do we emulate ?
                        if (Emulation) {
                                //are we in the same track ??? If not, force a new chunk when adding this sample
                                if (curWriter != prevWriter) {
                                        forceNewChunk = 1;
                                } else {
                                        forceNewChunk = 0;
                                }
                                //update our offsets...
                                if (Media_IsSelfContained(curWriter->mdia, descIndex) ) {
                                        e = stbl_SetChunkAndOffset(curWriter->mdia->information->sampleTable, curWriter->sampleNumber, descIndex, curWriter->stsc, &curWriter->stco, offset, forceNewChunk);
                                        if (e) return e;
                                        offset += sampSize;
                                        totSize += sampSize;
                                } else {
//                                      if (curWriter->prev_offset != sampOffset) forceNewChunk = 1;
                                        curWriter->prev_offset = sampOffset + sampSize;

                                        //we have a DataRef, so use the offset idicated in sampleToChunk
                                        //and ChunkOffset tables...
                                        e = stbl_SetChunkAndOffset(curWriter->mdia->information->sampleTable, curWriter->sampleNumber, descIndex, curWriter->stsc, &curWriter->stco, sampOffset, 0);
                                        if (e) return e;
                                }
                        } else {
                                //this is no game, we're writing ....
                                if (Media_IsSelfContained(curWriter->mdia, descIndex) ) {
                                        e = WriteSample(mw, sampSize, sampOffset, isEdited, bs);
                                        if (e) return e;
                                }
                        }
                        //ok, the sample is done
                        if (curWriter->sampleNumber == curWriter->mdia->information->sampleTable->SampleSize->sampleCount) {
                                curWriter->isDone = 1;
                                //one more track done...
                                tracksDone ++;
                        } else {
                                curWriter->sampleNumber ++;
                        }
                        prevWriter = curWriter;
                }
                //if all our track are done, break
                if (tracksDone == gf_list_count(writers)) break;
                //go to next group
                curGroupID ++;
        }
        movie->mdat->dataSize = totSize;
        return GF_OK;
}

/*uncomment the following to easily test large file generation. This will prepend 4096*1MByte of 0 before the media data*/
//#define TEST_LARGE_FILES

GF_Err DoInterleave(MovieWriter *mw, GF_List *writers, GF_BitStream *bs, u8 Emulation, u64 StartOffset, Bool drift_inter)
{
        u32 i, tracksDone;
        TrackWriter *tmp, *curWriter;
        GF_Err e;
        u32 descIndex, sampSize, chunkNumber;
        u64 DTS;
        u16 curGroupID;
        u8 forceNewChunk, writeGroup, isEdited;
        //this is used to emulate the write ...
        u64 offset, sampOffset, size, mdatSize;
        u32 count;
        GF_ISOFile *movie = mw->movie;
        if (!movie->moov || !movie->moov->mvhd) return GF_NON_COMPLIANT_BITSTREAM;

        mdatSize = 0;

#ifdef TEST_LARGE_FILES
        if (!Emulation) {
                char *blank;
                u32 count, i;
                i = count = 0;
                blank = gf_malloc(sizeof(char)*1024*1024);
                memset(blank, 0, sizeof(char)*1024*1024);
                count = 4096;
                memset(blank, 0, sizeof(char)*1024*1024);
                while (i<count) {
                        u32 res = gf_bs_write_data(bs, blank, 1024*1024);
                        if (res != 1024*1024) fprintf(stderr, "error writing to disk: only %d bytes written\n", res);
                        i++;
                        fprintf(stderr, "writing blank block: %.02f done - %d/%d \r", (100.0*i)/count , i, count);
                }
                gf_free(blank);
        }
        mdatSize = 4096*1024;
        mdatSize *= 1024;
#endif

        /*write meta content first - WE DON'T support fragmentation of resources in ISOM atm*/
        if (movie->meta) {
                e = DoWriteMeta(movie, movie->meta, bs, Emulation, StartOffset, &size);
                if (e) return e;
                mdatSize += size;
                StartOffset += (u32) size;
        }
        if (movie->moov && movie->moov->meta) {
                e = DoWriteMeta(movie, movie->moov->meta, bs, Emulation, StartOffset, &size);
                if (e) return e;
                mdatSize += size;
                StartOffset += (u32) size;
        }
        i=0;
        while ((tmp = (TrackWriter*)gf_list_enum(writers, &i))) {
                if (tmp->mdia->mediaTrack->meta) {
                        e = DoWriteMeta(movie, tmp->mdia->mediaTrack->meta, bs, Emulation, StartOffset, &size);
                        if (e) return e;
                        mdatSize += size;
                        StartOffset += (u32) size;
                }
        }



        if (movie->storageMode == GF_ISOM_STORE_TIGHT)
                return DoFullInterleave(mw, writers, bs, Emulation, StartOffset);

        curGroupID = 1;
        //we emulate a write from this offset...
        offset = StartOffset;
        tracksDone = 0;

#ifdef TEST_LARGE_FILES
        offset += mdatSize;
#endif

        count = gf_list_count(writers);
        //browse each groups
        while (1) {
                /*the max DTS the chunk of the current writer*/
                u64 chunkLastDTS = 0;
                /*the timescale related to the max DTS*/
                u32 chunkLastScale = 0;

                writeGroup = 1;

                //proceed a group
                while (writeGroup) {
                        curWriter = NULL;
                        for (i=0 ; i < count; i++) {
                                tmp = (TrackWriter*)gf_list_get(writers, i);

                                //is it done writing ?
                                if (tmp->isDone) continue;

                                //is it in our group ??
                                if (tmp->mdia->information->sampleTable->groupID != curGroupID) continue;

                                //write till this chunk is full on this track...
                                while (1) {
                                        //To Check: are empty sample tables allowed ???
                                        if (tmp->sampleNumber > tmp->mdia->information->sampleTable->SampleSize->sampleCount) {
                                                tmp->isDone = 1;
                                                tracksDone ++;
                                                break;
                                        }

                                        //OK, get the current sample in this track
                                        stbl_GetSampleDTS(tmp->mdia->information->sampleTable->TimeToSample, tmp->sampleNumber, &DTS);

                                        //can this sample fit in our chunk ?
                                        if ( ( (DTS - tmp->DTSprev) + tmp->chunkDur) *  movie->moov->mvhd->timeScale > movie->interleavingTime * tmp->timeScale
                                                /*drfit check: reject sample if outside our check window*/
                                                || (drift_inter && chunkLastDTS && ( ((u64)tmp->DTSprev*chunkLastScale) > ((u64)chunkLastDTS*tmp->timeScale)) )
                                           ) {
                                                //in case the sample is longer than InterleaveTime
                                                if (!tmp->chunkDur) {
                                                        forceNewChunk = 1;
                                                } else {
                                                        //this one is full. go to next one (exit the loop)
                                                        tmp->chunkDur = 0;
                                                        break;
                                                }
                                        } else {
                                                forceNewChunk = tmp->chunkDur ? 0 : 1;
                                        }
                                        //OK, we can write this track
                                        curWriter = tmp;

                                        //small check for first 2 samples (DTS = 0 :)
                                        if (tmp->sampleNumber == 2 && !tmp->chunkDur) forceNewChunk = 0;

                                        tmp->chunkDur += (u32) (DTS - tmp->DTSprev);
                                        tmp->DTSprev = DTS;

                                        e = stbl_GetSampleInfos(curWriter->mdia->information->sampleTable, curWriter->sampleNumber, &sampOffset, &chunkNumber, &descIndex, &isEdited);
                                        if (e) return e;
                                        e = stbl_GetSampleSize(curWriter->mdia->information->sampleTable->SampleSize, curWriter->sampleNumber, &sampSize);
                                        if (e) return e;

                                        //do we actually write, or do we emulate ?
                                        if (Emulation) {
                                                //update our offsets...
                                                if (Media_IsSelfContained(curWriter->mdia, descIndex) ) {
                                                        e = stbl_SetChunkAndOffset(curWriter->mdia->information->sampleTable, curWriter->sampleNumber, descIndex, curWriter->stsc, &curWriter->stco, offset, forceNewChunk);
                                                        if (e) return e;
                                                        offset += sampSize;
                                                        mdatSize += sampSize;
                                                } else {
                                                        if (curWriter->prev_offset != sampOffset) forceNewChunk = 1;
                                                        curWriter->prev_offset = sampOffset + sampSize;

                                                        //we have a DataRef, so use the offset idicated in sampleToChunk
                                                        //and ChunkOffset tables...
                                                        e = stbl_SetChunkAndOffset(curWriter->mdia->information->sampleTable, curWriter->sampleNumber, descIndex, curWriter->stsc, &curWriter->stco, sampOffset, forceNewChunk);
                                                        if (e) return e;
                                                }
                                        } else {
                                                //this is no game, we're writing ....
                                                if (Media_IsSelfContained(curWriter->mdia, descIndex) ) {
                                                        e = WriteSample(mw, sampSize, sampOffset, isEdited, bs);
                                                        if (e) return e;
                                                }
                                        }
                                        //ok, the sample is done
                                        if (curWriter->sampleNumber == curWriter->mdia->information->sampleTable->SampleSize->sampleCount) {
                                                curWriter->isDone = 1;
                                                //one more track done...
                                                tracksDone ++;
                                                break;
                                        } else {
                                                curWriter->sampleNumber ++;
                                        }
                                }
                                /*record chunk end-time & track timescale for drift-controled interleaving*/
                                if (drift_inter && curWriter) {
                                        chunkLastScale = curWriter->timeScale;
                                        chunkLastDTS = curWriter->DTSprev;
                                        /*add one interleave window drift - since the "maxDTS" is the previously written one, we will
                                        have the following cases:
                                        - sample doesn't fit: post-pone and force new chunk
                                        - sample time larger than previous chunk time + interleave: post-pone and force new chunk
                                        - otherwise store and track becomes current reference

                                        this ensures a proper drift regulation (max DTS diff is less than the interleaving window)
                                        */
                                        chunkLastDTS += curWriter->timeScale * movie->interleavingTime / movie->moov->mvhd->timeScale;
                                }
                        }
                        //no sample found, we're done with this group
                        if (!curWriter) {
                                writeGroup = 0;
                                continue;
                        }
                }
                //if all our track are done, break
                if (tracksDone == gf_list_count(writers)) break;
                //go to next group
                curGroupID ++;
        }
        if (movie->mdat) movie->mdat->dataSize = mdatSize;
        return GF_OK;
}


static GF_Err WriteInterleaved(MovieWriter *mw, GF_BitStream *bs, Bool drift_inter)
{
        GF_Err e;
        u32 i;
        GF_Box *a;
        u64 firstSize, finalSize, offset, finalOffset;
        GF_List *writers = gf_list_new();
        GF_ISOFile *movie = mw->movie;

        //first setup the writers
        e = SetupWriters(mw, writers, 1);
        if (e) goto exit;


        if (movie->is_jp2) {
                gf_bs_write_u32(bs, 12);
                gf_bs_write_u32(bs, GF_4CC('j','P',' ',' '));
                gf_bs_write_u32(bs, 0x0D0A870A);
        }
        if (movie->brand) {
                e = gf_isom_box_size((GF_Box *)movie->brand);
                if (e) goto exit;
                e = gf_isom_box_write((GF_Box *)movie->brand, bs);
                if (e) goto exit;
        }
        if (movie->pdin) {
                e = gf_isom_box_size((GF_Box *)movie->pdin);
                if (e) goto exit;
                e = gf_isom_box_write((GF_Box *)movie->pdin, bs);
                if (e) goto exit;
        }

        e = DoInterleave(mw, writers, bs, 1, gf_bs_get_position(bs), drift_inter);
        if (e) goto exit;

        firstSize = GetMoovAndMetaSize(movie, writers);
        offset = firstSize;
        if (movie->mdat && movie->mdat->dataSize) offset += 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0);
        e = ShiftOffset(movie, writers, offset);
        if (e) goto exit;
        //get the size and see if it has changed (eg, we moved to 64 bit offsets)
        finalSize = GetMoovAndMetaSize(movie, writers);
        if (firstSize != finalSize) {
                finalOffset = finalSize;
                if (movie->mdat && movie->mdat->dataSize) finalOffset += 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0);
                //OK, now we're sure about the final size -> shift the offsets
                //we don't need to re-emulate, as the only thing that changed is the offset
                //so just shift the offset
                e = ShiftOffset(movie, writers, finalOffset - offset);
                if (e) goto exit;
                /*firstSize = */GetMoovAndMetaSize(movie, writers);
        }
        //now write our stuff
        e = WriteMoovAndMeta(movie, writers, bs);
        if (e) goto exit;

        /*we have 8 extra bytes for large size (not computed in gf_isom_box_size) */
        if (movie->mdat && movie->mdat->dataSize) {
                if (movie->mdat->dataSize > 0xFFFFFFFF) movie->mdat->dataSize += 8;
                e = gf_isom_box_size((GF_Box *)movie->mdat);
                if (e) goto exit;
                e = gf_isom_box_write((GF_Box *)movie->mdat, bs);
                if (e) goto exit;
        }

        //we don't need the offset as we are writing...
        ResetWriters(writers);
        e = DoInterleave(mw, writers, bs, 0, 0, drift_inter);
        if (e) goto exit;

        //then the rest
        i=0;
        while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
                switch (a->type) {
                case GF_ISOM_BOX_TYPE_MOOV:
                case GF_ISOM_BOX_TYPE_META:
                case GF_ISOM_BOX_TYPE_FTYP:
                case GF_ISOM_BOX_TYPE_PDIN:
                case GF_ISOM_BOX_TYPE_MDAT:
                        break;
                default:
                        e = gf_isom_box_size(a);
                        if (e) goto exit;
                        e = gf_isom_box_write(a, bs);
                        if (e) goto exit;
                }
        }

exit:
        CleanWriters(writers);
        gf_list_del(writers);
        return e;
}

extern u32 default_write_buffering_size;

GF_Err WriteToFile(GF_ISOFile *movie)
{
        FILE *stream;
        GF_BitStream *bs;
        MovieWriter mw;
        GF_Err e = GF_OK;
        if (!movie) return GF_BAD_PARAM;

        if (movie->openMode == GF_ISOM_OPEN_READ) return GF_BAD_PARAM;

        e = gf_isom_insert_copyright(movie);
        if (e) return e;

        memset(&mw, 0, sizeof(mw));
        mw.movie = movie;

        if (movie->drop_date_version_info && movie->moov) {
                u32 i;
                GF_TrackBox *trak;
                movie->moov->mvhd->creationTime = 0;
                movie->moov->mvhd->modificationTime = 0;
                i=0;
                while ( (trak = gf_list_enum(movie->moov->trackList, &i))) {
                        trak->Header->creationTime = 0;
                        trak->Header->modificationTime = 0;
                        if (trak->Media->handler->nameUTF8 && strstr(trak->Media->handler->nameUTF8, "@GPAC")) {
                                gf_free(trak->Media->handler->nameUTF8);
                                trak->Media->handler->nameUTF8 = gf_strdup("MediaHandler");
                        }
                        trak->Media->mediaHeader->creationTime = 0;
                        trak->Media->mediaHeader->modificationTime = 0;
                }
        }

        //capture mode: we don't need a new bitstream
        if (movie->openMode == GF_ISOM_OPEN_WRITE) {
                e = WriteFlat(&mw, 0, movie->editFileMap->bs);
        } else {
                u32 buffer_size = movie->editFileMap ? gf_bs_get_output_buffering(movie->editFileMap->bs) : 0;
                Bool is_stdout = 0;
                if (!strcmp(movie->finalName, "std"))
                        is_stdout = 1;

                //OK, we need a new bitstream
                stream = is_stdout ? stdout : gf_fopen(movie->finalName, "w+b");
                if (!stream)
                        return GF_IO_ERR;
                bs = gf_bs_from_file(stream, GF_BITSTREAM_WRITE);
                if (!bs) {
                        if (!is_stdout)
                                gf_fclose(stream);
                        return GF_OUT_OF_MEM;
                }

                if (buffer_size) {
                        gf_bs_set_output_buffering(bs, buffer_size);
                }

                if (!movie->moov) {
                        /* in case of file with only a meta box, we force a flat storage */
                        movie->storageMode = GF_ISOM_STORE_FLAT;
                }

                switch (movie->storageMode) {
                case GF_ISOM_STORE_TIGHT:
                case GF_ISOM_STORE_INTERLEAVED:
                        e = WriteInterleaved(&mw, bs, 0);
                        break;
                case GF_ISOM_STORE_DRIFT_INTERLEAVED:
                        e = WriteInterleaved(&mw, bs, 1);
                        break;
                case GF_ISOM_STORE_STREAMABLE:
                        e = WriteFlat(&mw, 1, bs);
                        break;
                default:
                        e = WriteFlat(&mw, 0, bs);
                        break;
                }

                gf_bs_del(bs);
                if (!is_stdout)
                        gf_fclose(stream);
        }
        if (mw.buffer) gf_free(mw.buffer);
        if (mw.nb_done<mw.total_samples) {
                gf_set_progress("ISO File Writing", mw.total_samples, mw.total_samples);
        }
        return e;
}



#endif  /*!defined(GPAC_DISABLE_ISOM) && !defined(GPAC_DISABLE_ISOM_WRITE)*/

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