LCOV - code coverage report
Current view: top level - isomedia - isom_store.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1000 1266 79.0 %
Date: 2021-04-29 23:48:07 Functions: 27 27 100.0 %

          Line data    Source code
       1             : 
       2             : /*
       3             :  *                      GPAC - Multimedia Framework C SDK
       4             :  *
       5             :  *                      Authors: Jean Le Feuvre
       6             :  *                      Copyright (c) Telecom ParisTech 2000-2021
       7             :  *                                      All rights reserved
       8             :  *
       9             :  *  This file is part of GPAC / ISO Media File Format sub-project
      10             :  *
      11             :  *  GPAC is free software; you can redistribute it and/or modify
      12             :  *  it under the terms of the GNU Lesser General Public License as published by
      13             :  *  the Free Software Foundation; either version 2, or (at your option)
      14             :  *  any later version.
      15             :  *
      16             :  *  GPAC is distributed in the hope that it will be useful,
      17             :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      18             :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      19             :  *  GNU Lesser General Public License for more details.
      20             :  *
      21             :  *  You should have received a copy of the GNU Lesser General Public
      22             :  *  License along with this library; see the file COPYING.  If not, write to
      23             :  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
      24             :  *
      25             :  */
      26             : 
      27             : #include <gpac/internal/isomedia_dev.h>
      28             : 
      29             : #if !defined(GPAC_DISABLE_ISOM) && !defined(GPAC_DISABLE_ISOM_WRITE)
      30             : 
      31             : #define GPAC_ISOM_CPRT_NOTICE "IsoMedia File Produced with GPAC"
      32             : 
      33             : #include <gpac/revision.h>
      34             : #define GPAC_ISOM_CPRT_NOTICE_VERSION GPAC_ISOM_CPRT_NOTICE" "GPAC_VERSION "-rev" GPAC_GIT_REVISION
      35             : 
      36        1458 : static GF_Err gf_isom_insert_copyright(GF_ISOFile *movie)
      37             : {
      38             :         u32 i;
      39             :         GF_Box *a;
      40             :         GF_FreeSpaceBox *_free;
      41        1458 :         i=0;
      42        5860 :         while ((a = (GF_Box *)gf_list_enum(movie->TopBoxes, &i))) {
      43        4610 :                 if (a->type == GF_ISOM_BOX_TYPE_FREE) {
      44             :                         _free = (GF_FreeSpaceBox *)a;
      45         242 :                         if (_free->dataSize) {
      46             :                                 u32 cp_len = (u32) strlen(GPAC_ISOM_CPRT_NOTICE_VERSION);
      47         208 :                                 if ((cp_len==_free->dataSize) && !memcmp(_free->data, GPAC_ISOM_CPRT_NOTICE_VERSION, _free->dataSize)) return GF_OK;
      48             : 
      49             :                                 cp_len = (u32) strlen(GPAC_ISOM_CPRT_NOTICE);
      50         208 :                                 if (cp_len>_free->dataSize)
      51             :                                         cp_len = _free->dataSize;
      52         208 :                                 if (!memcmp(_free->data, GPAC_ISOM_CPRT_NOTICE, cp_len)) {
      53         208 :                                         gf_free(_free->data);
      54         208 :                                         _free->data = gf_strdup(gf_sys_is_test_mode() ? GPAC_ISOM_CPRT_NOTICE : GPAC_ISOM_CPRT_NOTICE_VERSION);
      55         208 :                                         _free->dataSize = 1 + (u32) strlen(_free->data);
      56             :                                         return GF_OK;
      57             :                                 }
      58             :                         }
      59             :                 }
      60             :         }
      61        1250 :         a = gf_isom_box_new(GF_ISOM_BOX_TYPE_FREE);
      62        1250 :         if (!a) return GF_OUT_OF_MEM;
      63             :         _free = (GF_FreeSpaceBox *)a;
      64        1250 :         _free->data = gf_strdup(gf_sys_is_test_mode() ? GPAC_ISOM_CPRT_NOTICE : GPAC_ISOM_CPRT_NOTICE_VERSION);
      65        1250 :         _free->dataSize = (u32) strlen(_free->data) + 1;
      66             :         if (!_free->data) return GF_OUT_OF_MEM;
      67        1250 :         return gf_list_add(movie->TopBoxes, _free);
      68             : }
      69             : 
      70             : typedef struct
      71             : {
      72             :         /*the curent sample of this track*/
      73             :         u32 sampleNumber;
      74             :         /*timeScale of the media (for interleaving)*/
      75             :         u32 timeScale;
      76             :         /*this is for generic, time-based interleaving. Expressed in Media TimeScale*/
      77             :         u64 chunkDur;
      78             :         u32 chunkSize;
      79             :         u32 constant_size, constant_dur;
      80             : 
      81             :         u64 DTSprev;
      82             :         u8 isDone;
      83             :         u64 prev_offset;
      84             :         GF_MediaBox *mdia;
      85             :         GF_SampleTableBox *stbl;
      86             : 
      87             :         u32 all_dref_mode;
      88             : 
      89             :         /*each writer has a sampleToChunck and ChunkOffset tables
      90             :         these tables are filled during emulation mode and then will     replace the table in the GF_SampleTableBox*/
      91             :         GF_SampleToChunkBox *stsc;
      92             :         /*we don't know if it's a large offset or not*/
      93             :         GF_Box *stco;
      94             :         //track uses a box requiring seeking into the moov during write, we cannot dispatch blocks
      95             :         Bool prevent_dispatch;
      96             : } TrackWriter;
      97             : 
      98             : typedef struct
      99             : {
     100             :         char *buffer;
     101             :         u32 alloc_size;
     102             :         GF_ISOFile *movie;
     103             :         u64 total_samples, nb_done;
     104             : } MovieWriter;
     105             : 
     106        1402 : void CleanWriters(GF_List *writers)
     107             : {
     108        4621 :         while (gf_list_count(writers)) {
     109        1817 :                 TrackWriter *writer = (TrackWriter*)gf_list_get(writers, 0);
     110             :                 //in case we have an error in the middle of file write, remove our created stco and stsc from sample table
     111        1817 :                 gf_list_del_item(writer->stbl->child_boxes, writer->stco);
     112        1817 :                 gf_list_del_item(writer->stbl->child_boxes, writer->stsc);
     113        1817 :                 gf_isom_box_del(writer->stco);
     114        1817 :                 gf_isom_box_del((GF_Box *)writer->stsc);
     115        1817 :                 gf_free(writer);
     116        1817 :                 gf_list_rem(writers, 0);
     117             :         }
     118        1402 : }
     119             : 
     120        1057 : GF_Err ResetWriters(GF_List *writers)
     121             : {
     122             :         u32 i;
     123             :         TrackWriter *writer;
     124        1057 :         i=0;
     125        3531 :         while ((writer = (TrackWriter *)gf_list_enum(writers, &i))) {
     126        1417 :                 writer->isDone = 0;
     127        1417 :                 writer->chunkDur = 0;
     128        1417 :                 writer->chunkSize = 0;
     129        1417 :                 writer->DTSprev = 0;
     130        1417 :                 writer->sampleNumber = 1;
     131        1417 :                 gf_isom_box_del((GF_Box *)writer->stsc);
     132        1417 :                 writer->stsc = (GF_SampleToChunkBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSC);
     133        1417 :                 if (!writer->stsc) return GF_OUT_OF_MEM;
     134        1417 :                 if (writer->stco->type == GF_ISOM_BOX_TYPE_STCO) {
     135        1416 :                         gf_free(((GF_ChunkOffsetBox *)writer->stco)->offsets);
     136        1416 :                         ((GF_ChunkOffsetBox *)writer->stco)->offsets = NULL;
     137        1416 :                         ((GF_ChunkOffsetBox *)writer->stco)->nb_entries = 0;
     138        1416 :                         ((GF_ChunkOffsetBox *)writer->stco)->alloc_size = 0;
     139             :                 } else {
     140           1 :                         gf_free(((GF_ChunkLargeOffsetBox *)writer->stco)->offsets);
     141           1 :                         ((GF_ChunkLargeOffsetBox *)writer->stco)->offsets = NULL;
     142           1 :                         ((GF_ChunkLargeOffsetBox *)writer->stco)->nb_entries = 0;
     143           1 :                         ((GF_ChunkLargeOffsetBox *)writer->stco)->alloc_size = 0;
     144             :                 }
     145             :         }
     146             :         return GF_OK;
     147             : }
     148             : 
     149        1402 : GF_Err SetupWriters(MovieWriter *mw, GF_List *writers, u8 interleaving)
     150             : {
     151             :         u32 i, trackCount;
     152             :         TrackWriter *writer;
     153             :         GF_TrackBox *trak;
     154        1402 :         GF_ISOFile *movie = mw->movie;
     155             : 
     156        1402 :         mw->total_samples = mw->nb_done = 0;
     157        1402 :         if (!movie->moov) return GF_OK;
     158             : 
     159        1315 :         trackCount = gf_list_count(movie->moov->trackList);
     160        4447 :         for (i = 0; i < trackCount; i++) {
     161             :                 GF_SampleTableBox *stbl;
     162        1817 :                 trak = gf_isom_get_track(movie->moov, i+1);
     163             : 
     164        1817 :                 stbl = (trak->Media && trak->Media->information) ? trak->Media->information->sampleTable : NULL;
     165        1817 :                 if (!stbl || !stbl->SampleSize || !stbl->ChunkOffset || !stbl->SampleToChunk || !stbl->SampleSize) {
     166           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[Isom] Box '%s' missing from track, cannot write\n",
     167             :                                 !trak->Media ? "mdia" :
     168             :                                 !trak->Media->information ? "minf" :
     169             :                                 !stbl ? "stbl" :
     170             :                                 !stbl->SampleSize ? "stsz" :
     171             :                                 !stbl->ChunkOffset ? "stco" :
     172             :                                 !stbl->SampleToChunk ? "stsc" :
     173             :                                 "stsz"
     174             :                         ));
     175             : 
     176             :                         return GF_ISOM_INVALID_FILE;
     177             :                 }
     178             : 
     179        1817 :                 GF_SAFEALLOC(writer, TrackWriter);
     180        1817 :                 if (!writer) goto exit;
     181        1817 :                 writer->sampleNumber = 1;
     182        1817 :                 writer->mdia = trak->Media;
     183        1817 :                 writer->stbl = trak->Media->information->sampleTable;
     184        1817 :                 writer->timeScale = trak->Media->mediaHeader->timeScale;
     185        1817 :                 writer->all_dref_mode = Media_SelfContainedType(writer->mdia);
     186             : 
     187        1817 :                 if (trak->sample_encryption)
     188         212 :                         writer->prevent_dispatch = GF_TRUE;
     189             : 
     190        1817 :                 writer->isDone = 0;
     191        1817 :                 writer->DTSprev = 0;
     192        1817 :                 writer->chunkDur = 0;
     193        1817 :                 writer->chunkSize = 0;
     194        1817 :                 writer->constant_size = writer->constant_dur = 0;
     195        1817 :                 if (writer->stbl->SampleSize->sampleSize)
     196         295 :                         writer->constant_size = writer->stbl->SampleSize->sampleSize;
     197        1817 :                 if (writer->stbl->TimeToSample->nb_entries==1) {
     198        1170 :                         writer->constant_dur = writer->stbl->TimeToSample->entries[0].sampleDelta;
     199        1170 :                         if (writer->constant_dur>1) writer->constant_dur = 0;
     200             :                 }
     201        1817 :                 if (!writer->constant_dur || !writer->constant_size || (writer->constant_size>=10))
     202        1789 :                         writer->constant_size = writer->constant_dur = 0;
     203             : 
     204        1817 :                 writer->stsc = (GF_SampleToChunkBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSC);
     205        1817 :                 if (!writer->stsc) return GF_OUT_OF_MEM;
     206        1817 :                 if (writer->stbl->ChunkOffset->type == GF_ISOM_BOX_TYPE_STCO) {
     207        1817 :                         writer->stco = gf_isom_box_new(GF_ISOM_BOX_TYPE_STCO);
     208             :                 } else {
     209           0 :                         writer->stco = gf_isom_box_new(GF_ISOM_BOX_TYPE_CO64);
     210             :                 }
     211        1817 :                 if (!writer->stco) return GF_OUT_OF_MEM;
     212             :                 /*stops from chunk escape*/
     213        1817 :                 if (interleaving)
     214        1416 :                         writer->stbl->MaxSamplePerChunk = 0;
     215         401 :                 else if (writer->stbl->MaxChunkDur && trak->Media->mediaHeader->duration) {
     216           0 :                         writer->stbl->MaxSamplePerChunk = (u32) ((u64) writer->stbl->MaxChunkDur * writer->stbl->SampleSize->sampleCount / trak->Media->mediaHeader->duration);
     217             :                 }
     218             :                 /*for progress, assume only one descIndex*/
     219        1817 :                 if (Media_IsSelfContained(writer->mdia, 1))
     220        1814 :                         mw->total_samples += writer->stbl->SampleSize->sampleCount;
     221             :                 /*optimization for interleaving: put audio last (this can be overridden by priorities)*/
     222        1817 :                 if (movie->storageMode != GF_ISOM_STORE_INTERLEAVED) {
     223        1817 :                         gf_list_add(writers, writer);
     224             :                 } else {
     225           0 :                         if (writer->mdia->information->InfoHeader && writer->mdia->information->InfoHeader->type == GF_ISOM_BOX_TYPE_SMHD) {
     226           0 :                                 gf_list_add(writers, writer);
     227             :                         } else {
     228           0 :                                 gf_list_insert(writers, writer, 0);
     229             :                         }
     230             :                 }
     231        1817 :                 if (movie->sample_groups_in_traf && trak->Media->information->sampleTable) {
     232           1 :                         gf_isom_box_array_del_parent(&trak->Media->information->sampleTable->child_boxes, trak->Media->information->sampleTable->sampleGroupsDescription);
     233           1 :                         trak->Media->information->sampleTable->sampleGroupsDescription = NULL;
     234             :                 }
     235             :         }
     236             :         return GF_OK;
     237             : 
     238           0 : exit:
     239           0 :         CleanWriters(writers);
     240           0 :         return GF_OUT_OF_MEM;
     241             : }
     242             : 
     243             : 
     244         122 : static void ShiftMetaOffset(GF_MetaBox *meta, u64 offset)
     245             : {
     246             :         u32 i, count;
     247             :         u64 max_offset = 0;
     248         122 :         if (!meta->item_locations) return;
     249             : 
     250          95 :         count = gf_list_count(meta->item_locations->location_entries);
     251         250 :         for (i=0; i<count; i++) {
     252         250 :                 GF_ItemLocationEntry *iloc = (GF_ItemLocationEntry *)gf_list_get(meta->item_locations->location_entries, i);
     253         250 :                 if (iloc->data_reference_index) continue;
     254         250 :                 if (iloc->construction_method == 2) continue;
     255         249 :                 if (!iloc->base_offset) {
     256           0 :                         GF_ItemExtentEntry *entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0);
     257           0 :                         if (entry && !entry->extent_length && !entry->original_extent_offset && (gf_list_count(iloc->extent_entries)==1) )
     258           0 :                                 continue;
     259             :                 }
     260         249 :                 iloc->base_offset += offset;
     261         249 :                 if (max_offset < iloc->base_offset)
     262             :                         max_offset = iloc->base_offset;
     263             :         }
     264             : 
     265             :         /*update offset & size length fields*/
     266          95 :         if (max_offset>0xFFFFFFFF) meta->item_locations->base_offset_size = 8;
     267          95 :         else if (max_offset) meta->item_locations->base_offset_size = 4;
     268             : }
     269             : 
     270             : 
     271        1465 : static GF_Err shift_chunk_offsets(GF_SampleToChunkBox *stsc, GF_MediaBox *mdia, GF_Box *_stco, u64 offset, Bool force_co64, GF_Box **new_stco)
     272             : {
     273             :         u32 j, k, l, last;
     274             :         GF_StscEntry *ent;
     275             : 
     276             :         //we have to proceed entry by entry in case a part of the media is not self-contained...
     277        5097 :         for (j=0; j<stsc->nb_entries; j++) {
     278        5097 :                 ent = &stsc->entries[j];
     279        5097 :                 if (!Media_IsSelfContained(mdia, ent->sampleDescriptionIndex))
     280           0 :                         continue;
     281             :                 //OK, get the chunk(s) number(s) and "shift" its (their) offset(s).
     282        5097 :                 if (_stco->type == GF_ISOM_BOX_TYPE_STCO) {
     283             :                         GF_ChunkLargeOffsetBox *new_stco64 = NULL;
     284             :                         GF_ChunkOffsetBox *stco = (GF_ChunkOffsetBox *) _stco;
     285             : 
     286             :                         //be careful for the last entry, nextChunk is set to 0 in edit mode...
     287        5092 :                         last = ent->nextChunk ? ent->nextChunk : stco->nb_entries + 1;
     288       69185 :                         for (k = ent->firstChunk; k < last; k++) {
     289             : 
     290             :                                 //we need to rewrite the table: only allocate co64 if not done previously and convert all offsets
     291             :                                 //to co64. Then (whether co64 was created or not) adjust the offset
     292             :                                 //Do not reassign table until we are done with the current sampleToChunk processing
     293             :                                 //since we have a test on stco->offsets[k-1], we need to keep stco untouched
     294       64093 :                                 if (new_stco64 || force_co64 || (stco->offsets[k-1] + offset > 0xFFFFFFFF)) {
     295           1 :                                         if (!new_stco64) {
     296           1 :                                                 new_stco64 = (GF_ChunkLargeOffsetBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_CO64);
     297           1 :                                                 if (!new_stco64) return GF_OUT_OF_MEM;
     298           1 :                                                 new_stco64->nb_entries = stco->nb_entries;
     299           1 :                                                 new_stco64->offsets = (u64 *) gf_malloc(new_stco64->nb_entries * sizeof(u64));
     300           1 :                                                 if (!new_stco64->offsets) return GF_OUT_OF_MEM;
     301             :                                                 //copy over the stco table
     302          15 :                                                 for (l = 0; l < new_stco64->nb_entries; l++) {
     303          15 :                                                         new_stco64->offsets[l] = (u64) stco->offsets[l];
     304             :                                                 }
     305             :                                         }
     306           1 :                                         new_stco64->offsets[k-1] += offset;
     307             :                                 } else {
     308       64092 :                                         stco->offsets[k-1] += (u32) offset;
     309             :                                 }
     310             :                         }
     311        5092 :                         if (new_stco64) {
     312           1 :                                 *new_stco = (GF_Box *) new_stco64;
     313             :                                 _stco = (GF_Box *) new_stco64;
     314             :                                 new_stco64 = NULL;
     315             :                         }
     316             :                 } else {
     317             :                         GF_ChunkLargeOffsetBox *stco64 = (GF_ChunkLargeOffsetBox *) _stco;
     318             :                         //be careful for the last entry ...
     319           5 :                         last = ent->nextChunk ? ent->nextChunk : stco64->nb_entries + 1;
     320          34 :                         for (k = ent->firstChunk; k < last; k++) {
     321          29 :                                 stco64->offsets[k-1] += offset;
     322             :                         }
     323             :                 }
     324             :         }
     325             :         return GF_OK;
     326             : }
     327        1062 : static GF_Err ShiftOffset(GF_ISOFile *file, GF_List *writers, u64 offset)
     328             : {
     329             :         u32 i;
     330             :         TrackWriter *writer;
     331             : 
     332        1062 :         if (file->meta) ShiftMetaOffset(file->meta, offset);
     333        1062 :         if (file->moov && file->moov->meta) ShiftMetaOffset(file->moov->meta, offset);
     334             : 
     335        1062 :         i=0;
     336        3549 :         while ((writer = (TrackWriter *)gf_list_enum(writers, &i))) {
     337             :                 GF_Err e;
     338        1425 :                 GF_Box *new_stco=NULL;
     339        1425 :                 if (writer->mdia->mediaTrack->meta) ShiftMetaOffset(writer->mdia->mediaTrack->meta, offset);
     340        1425 :                 if (writer->all_dref_mode==ISOM_DREF_EXT)
     341           3 :                         continue;
     342             : 
     343        1422 :                 e = shift_chunk_offsets(writer->stsc, writer->mdia, writer->stco, offset, file->force_co64, &new_stco);
     344        1422 :                 if (e) return e;
     345             : 
     346        1422 :                 if (new_stco) {
     347             :                         //done with this sampleToChunk entry, replace the box if we moved to co64
     348           1 :                         gf_isom_box_del(writer->stco);
     349           1 :                         writer->stco = (GF_Box *) new_stco;
     350             :                 }
     351             :         }
     352             :         return GF_OK;
     353             : 
     354             : }
     355             : 
     356             : #define COMP_BOX_COST_BYTES             8
     357             : 
     358          13 : GF_Err gf_isom_write_compressed_box(GF_ISOFile *mov, GF_Box *root_box, u32 repl_type, GF_BitStream *bs, u32 *box_csize)
     359             : {
     360             : #ifdef GPAC_DISABLE_ZLIB
     361             :         return GF_NOT_SUPPORTED;
     362             : #else
     363             :         GF_Err e;
     364          13 :         GF_BitStream *comp_bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
     365          13 :         e = gf_isom_box_write(root_box, comp_bs);
     366             : 
     367          13 :         if (!e) {
     368             :                 u8 *box_data;
     369             :                 u32 box_size, comp_size;
     370             : 
     371          13 :                 if (box_csize)
     372          10 :                         *box_csize = (u32) root_box->size;
     373             : 
     374          13 :                 gf_bs_get_content(comp_bs, &box_data, &box_size);
     375          13 :                 gf_gz_compress_payload_ex(&box_data, box_size, &comp_size, 8, GF_FALSE, NULL);
     376          13 :                 if ((mov->compress_flags & GF_ISOM_COMP_FORCE_ALL) || (comp_size + COMP_BOX_COST_BYTES < box_size)) {
     377          13 :                         if (bs) {
     378          13 :                                 gf_bs_write_u32(bs, comp_size+8);
     379          13 :                                 gf_bs_write_u32(bs, repl_type);
     380          13 :                                 gf_bs_write_data(bs, box_data, comp_size);
     381             :                         }
     382          13 :                         if (box_csize)
     383          10 :                                 *box_csize = comp_size + COMP_BOX_COST_BYTES;
     384           0 :                 } else if (bs) {
     385           0 :                         gf_isom_box_write(root_box, bs);
     386             :                 }
     387          13 :                 gf_free(box_data);
     388             :         }
     389          13 :         gf_bs_del(comp_bs);
     390          13 :         return e;
     391             : #endif /*GPAC_DISABLE_ZLIB*/
     392             : }
     393             : 
     394             : //replace the chunk and offset tables...
     395        1402 : static GF_Err WriteMoovAndMeta(GF_ISOFile *movie, GF_List *writers, GF_BitStream *bs)
     396             : {
     397             :         u32 i;
     398             :         TrackWriter *writer;
     399             :         GF_Err e;
     400             :         GF_Box *stco;
     401             :         GF_SampleToChunkBox *stsc;
     402             : 
     403        1402 :         if (movie->meta) {
     404             :                 //write the moov box...
     405          94 :                 e = gf_isom_box_size((GF_Box *)movie->meta);
     406          94 :                 if (e) return e;
     407          94 :                 e = gf_isom_box_write((GF_Box *)movie->meta, bs);
     408          94 :                 if (e) return e;
     409             :         }
     410             : 
     411        1402 :         if (movie->moov) {
     412             :                 Bool prevent_dispatch = GF_FALSE;
     413             :                 //switch all our tables
     414        1315 :                 i=0;
     415        4447 :                 while ((writer = (TrackWriter*)gf_list_enum(writers, &i))) {
     416             :                         //don't delete them !!!
     417        1817 :                         stsc = writer->stbl->SampleToChunk;
     418        1817 :                         stco = writer->stbl->ChunkOffset;
     419        1817 :                         s32 stsc_pos = gf_list_del_item(writer->stbl->child_boxes, stsc);
     420        1817 :                         s32 stco_pos = gf_list_del_item(writer->stbl->child_boxes, stco);
     421        1817 :                         writer->stbl->SampleToChunk = writer->stsc;
     422        1817 :                         writer->stbl->ChunkOffset = writer->stco;
     423        1817 :                         gf_list_insert(writer->stbl->child_boxes, writer->stsc, stsc_pos);
     424        1817 :                         gf_list_insert(writer->stbl->child_boxes, writer->stco, stco_pos);
     425        1817 :                         writer->stco = stco;
     426        1817 :                         writer->stsc = stsc;
     427        1817 :                         if (writer->prevent_dispatch)
     428             :                                 prevent_dispatch = GF_TRUE;
     429             :                 }
     430        1315 :                 if (prevent_dispatch) {
     431         212 :                         gf_bs_prevent_dispatch(bs, GF_TRUE);
     432             :                 }
     433             :                 //write the moov box...
     434        1315 :                 e = gf_isom_box_size((GF_Box *)movie->moov);
     435        1315 :                 if (e) return e;
     436             : 
     437        1315 :                 if ((movie->compress_mode==GF_ISOM_COMP_ALL) || (movie->compress_mode==GF_ISOM_COMP_MOOV)) {
     438           2 :                         e = gf_isom_write_compressed_box(movie, (GF_Box *) movie->moov, GF_4CC('!', 'm', 'o', 'v'), bs, NULL);
     439             :                 } else {
     440        1313 :                         e = gf_isom_box_write((GF_Box *)movie->moov, bs);
     441             :                 }
     442             : 
     443        1315 :                 if (prevent_dispatch) {
     444         212 :                         gf_bs_prevent_dispatch(bs, GF_FALSE);
     445             :                 }
     446             : 
     447             :                 //and re-switch our table. We have to do it that way because it is
     448             :                 //needed when the moov is written first
     449        1315 :                 i=0;
     450        4447 :                 while ((writer = (TrackWriter*)gf_list_enum(writers, &i))) {
     451             :                         //don't delete them !!!
     452        1817 :                         stsc = writer->stsc;
     453        1817 :                         stco = writer->stco;
     454        1817 :                         writer->stsc = writer->stbl->SampleToChunk;
     455        1817 :                         writer->stco = writer->stbl->ChunkOffset;
     456        1817 :                         s32 stsc_pos = gf_list_del_item(writer->stbl->child_boxes, writer->stsc);
     457        1817 :                         s32 stco_pos = gf_list_del_item(writer->stbl->child_boxes, writer->stco);
     458             : 
     459        1817 :                         writer->stbl->SampleToChunk = stsc;
     460        1817 :                         writer->stbl->ChunkOffset = stco;
     461        1817 :                         gf_list_insert(writer->stbl->child_boxes, stsc, stsc_pos);
     462        1817 :                         gf_list_insert(writer->stbl->child_boxes, stco, stco_pos);
     463             :                 }
     464        1315 :                 if (e) return e;
     465             :         }
     466             :         return GF_OK;
     467             : }
     468             : 
     469             : //compute the size of the moov as it will be written.
     470        2122 : u64 GetMoovAndMetaSize(GF_ISOFile *movie, GF_List *writers)
     471             : {
     472             :         u32 i;
     473             :         u64 size;
     474             : 
     475             :         size = 0;
     476        2122 :         if (movie->moov) {
     477             :                 TrackWriter *writer;
     478        1948 :                 gf_isom_box_size((GF_Box *)movie->moov);
     479        1948 :                 size = movie->moov->size;
     480        1948 :                 if (size > 0xFFFFFFFF) size += 8;
     481             : 
     482        1948 :                 i=0;
     483        6744 :                 while ((writer = (TrackWriter*)gf_list_enum(writers, &i))) {
     484        2848 :                         size -= writer->stbl->ChunkOffset->size;
     485        2848 :                         size -= writer->stbl->SampleToChunk->size;
     486        2848 :                         gf_isom_box_size((GF_Box *)writer->stsc);
     487        2848 :                         gf_isom_box_size(writer->stco);
     488        2848 :                         size += writer->stsc->size;
     489        2848 :                         size += writer->stco->size;
     490             :                 }
     491             :         }
     492        2122 :         if (movie->meta) {
     493             :                 u64 msize;
     494         189 :                 gf_isom_box_size((GF_Box *)movie->meta);
     495         189 :                 msize = movie->meta->size;
     496         189 :                 if (msize > 0xFFFFFFFF) msize += 8;
     497         189 :                 size += msize;
     498             :         }
     499        2122 :         return size;
     500             : }
     501             : 
     502     1037731 : static void muxer_report_progress(MovieWriter *mw)
     503             : {
     504     1037731 :         if (mw->movie->progress_cbk) {
     505      112277 :                 mw->movie->progress_cbk(mw->movie->progress_cbk_udta, mw->nb_done, mw->total_samples);
     506             :         } else {
     507      925454 :                 gf_set_progress("ISO File Writing", mw->nb_done, mw->total_samples);
     508             :         }
     509     1037731 : }
     510             : 
     511             : //Write a sample to the file - this is only called for self-contained media
     512     1036280 : GF_Err WriteSample(MovieWriter *mw, u32 size, u64 offset, u8 isEdited, GF_BitStream *bs, u32 nb_samp)
     513             : {
     514             :         GF_DataMap *map;
     515             :         u32 bytes;
     516             : 
     517     1036280 :         if (!size) return GF_OK;
     518             : 
     519     1036280 :         if (size>mw->alloc_size) {
     520        4156 :                 mw->buffer = (char*)gf_realloc(mw->buffer, size);
     521        4156 :                 mw->alloc_size = size;
     522             :         }
     523             : 
     524     1036280 :         if (!mw->buffer) return GF_OUT_OF_MEM;
     525             : 
     526     1036280 :         if (isEdited) {
     527      900660 :                 map = mw->movie->editFileMap;
     528             :         } else {
     529      135620 :                 map = mw->movie->movieFileMap;
     530             :         }
     531             :         //get the payload...
     532     1036280 :         bytes = gf_isom_datamap_get_data(map, mw->buffer, size, offset);
     533     1036280 :         if (bytes != size)
     534             :                 return GF_IO_ERR;
     535             :         //write it to our stream...
     536     1036280 :         bytes = gf_bs_write_data(bs, mw->buffer, size);
     537     1036280 :         if (bytes != size)
     538             :                 return GF_IO_ERR;
     539             : 
     540     1036280 :         mw->nb_done+=nb_samp;
     541     1036280 :         muxer_report_progress(mw);
     542     1036280 :         return GF_OK;
     543             : }
     544             : 
     545             : //flush as much as possible from current chunk for constand size and duration (typically raw audio)
     546             : // We don't want to write samples outside of the current source chunk
     547             : //since the next chunk might be edited (different bitstream object), which would complexify WriteSample code
     548             : //not flushing the chunk will work, but result in very slow writing of raw audio
     549     2074237 : void update_writer_constant_dur(GF_ISOFile *movie, TrackWriter *tkw, GF_StscEntry *stsc_ent, u32 *nb_samp, u32 *samp_size, Bool is_flat)
     550             : {
     551             :         u64 chunk_dur;
     552             :         u32 nb_in_run;
     553             :         u32 samp_idx_in_chunk, nb_samp_left_in_src_chunk;
     554     2074237 :         if (!tkw->constant_dur) return;
     555             : 
     556      750376 :         samp_idx_in_chunk = tkw->sampleNumber - tkw->stbl->SampleToChunk->firstSampleInCurrentChunk;
     557      750376 :         nb_samp_left_in_src_chunk = stsc_ent->samplesPerChunk - samp_idx_in_chunk;
     558             : 
     559      750376 :         if (nb_samp_left_in_src_chunk<=1) return;
     560             : 
     561      749920 :         if (is_flat) {
     562             :                 nb_in_run = nb_samp_left_in_src_chunk;
     563             :         } else {
     564             : 
     565      749920 :                 chunk_dur = movie->interleavingTime * tkw->timeScale;
     566      749920 :                 if (movie->moov && movie->moov->mvhd && movie->moov->mvhd->timeScale)
     567      749920 :                         chunk_dur /= movie->moov->mvhd->timeScale;
     568             : 
     569      749920 :                 chunk_dur -= tkw->chunkDur;
     570             : 
     571      749920 :                 if (chunk_dur <= tkw->chunkDur) return;
     572         560 :                 chunk_dur -= tkw->constant_dur;
     573             : 
     574         560 :                 nb_in_run = (u32) (chunk_dur / tkw->constant_dur);
     575             : 
     576         560 :                 if (nb_in_run > nb_samp_left_in_src_chunk) {
     577             :                         nb_in_run = nb_samp_left_in_src_chunk;
     578             :                 }
     579             :         }
     580         560 :         if (tkw->sampleNumber + nb_in_run >= tkw->stbl->SampleSize->sampleCount) {
     581          24 :                 nb_in_run = tkw->stbl->SampleSize->sampleCount - tkw->sampleNumber;
     582             :         }
     583             : 
     584         560 :         chunk_dur = nb_in_run * tkw->constant_dur;
     585             : 
     586         560 :         tkw->chunkDur += (u32) chunk_dur - tkw->constant_dur; //because tkw->chunkDur already include duration of first sample of chunk
     587         560 :         tkw->DTSprev += chunk_dur - tkw->constant_dur; //because nb_samp += nb_in_run-1
     588             : 
     589         560 :         *nb_samp = nb_in_run;
     590         560 :         *samp_size = nb_in_run * tkw->constant_size;
     591             : }
     592             : 
     593             : 
     594             : //replace the chunk and offset tables...
     595         101 : static GF_Err store_meta_item_sample_ref_offsets(GF_ISOFile *movie, GF_List *writers, GF_MetaBox *meta)
     596             : {
     597             :         u32 i, count;
     598             :         TrackWriter *writer;
     599             :         GF_Box *stco;
     600             :         GF_SampleToChunkBox *stsc;
     601             :         u64 max_base_offset = 0;
     602             :         u64 max_ext_offset = 0;
     603             :         u64 max_ext_length = 0;
     604             : 
     605         101 :         if (!movie->moov) return GF_OK;
     606          15 :         if (!meta->item_locations) return GF_OK;
     607          13 :         if (!meta->use_item_sample_sharing) return GF_OK;
     608             : 
     609             :         //switch all our tables
     610           3 :         if (writers) {
     611           3 :                 i=0;
     612           9 :                 while ((writer = (TrackWriter*)gf_list_enum(writers, &i))) {
     613             :                         //don't delete them !!!
     614           3 :                         stsc = writer->stbl->SampleToChunk;
     615           3 :                         stco = writer->stbl->ChunkOffset;
     616           3 :                         s32 stsc_pos = gf_list_del_item(writer->stbl->child_boxes, stsc);
     617           3 :                         s32 stco_pos = gf_list_del_item(writer->stbl->child_boxes, stco);
     618           3 :                         writer->stbl->SampleToChunk = writer->stsc;
     619           3 :                         writer->stbl->ChunkOffset = writer->stco;
     620           3 :                         gf_list_insert(writer->stbl->child_boxes, writer->stsc, stsc_pos);
     621           3 :                         gf_list_insert(writer->stbl->child_boxes, writer->stco, stco_pos);
     622           3 :                         writer->stco = stco;
     623           3 :                         writer->stsc = stsc;
     624             :                 }
     625             :         }
     626             : 
     627           3 :         count = gf_list_count(meta->item_locations->location_entries);
     628           6 :         for (i=0; i<count; i++) {
     629             :                 u32 j;
     630             :                 GF_ItemExtentEntry *entry;
     631           3 :                 GF_ItemLocationEntry *iloc = (GF_ItemLocationEntry *)gf_list_get(meta->item_locations->location_entries, i);
     632             :                 /*get item info*/
     633             :                 GF_ItemInfoEntryBox *iinf = NULL;
     634           3 :                 j=0;
     635           6 :                 while ((iinf = (GF_ItemInfoEntryBox *)gf_list_enum(meta->item_infos->item_infos, &j))) {
     636           3 :                         if (iinf->item_ID==iloc->item_ID) break;
     637             :                 }
     638           3 :                 if (!iinf) continue;
     639             : 
     640           3 :                 if (iloc->base_offset > max_base_offset)
     641             :                         max_base_offset = iloc->base_offset;
     642             : 
     643           3 :                 if (!iinf->tk_id || !iinf->sample_num) {
     644           0 :                         for (j=0; j<gf_list_count(iloc->extent_entries); j++) {
     645           0 :                                 entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, j);
     646           0 :                                 if (entry->extent_offset > max_ext_offset)
     647             :                                         max_ext_offset = entry->extent_offset;
     648           0 :                                 if (entry->extent_length > max_ext_length)
     649             :                                         max_ext_length = entry->extent_length;
     650             :                         }
     651           0 :                         continue;
     652             :                 }
     653             : 
     654           3 :                 entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0);
     655           3 :                 if (!entry) continue;
     656             : 
     657           3 :                 GF_ISOSample *samp = gf_isom_get_sample_info(movie, gf_isom_get_track_by_id(movie, iinf->tk_id), iinf->sample_num, NULL, &entry->extent_offset);
     658           3 :                 if (samp) gf_isom_sample_del(&samp);
     659           3 :                 entry->extent_offset -= iloc->base_offset;
     660           3 :                 if (entry->extent_offset > max_ext_offset)
     661             :                         max_ext_offset = entry->extent_offset;
     662           3 :                 if (entry->extent_length > max_ext_length)
     663             :                         max_ext_length = entry->extent_length;
     664             :         }
     665             : 
     666             :         /*update offset & size length fields*/
     667           3 :         if (max_base_offset>0xFFFFFFFF) meta->item_locations->base_offset_size = 8;
     668           3 :         else if (max_base_offset) meta->item_locations->base_offset_size = 4;
     669             : 
     670           3 :         if (max_ext_length>0xFFFFFFFF) meta->item_locations->length_size = 8;
     671           3 :         else if (max_ext_length) meta->item_locations->length_size = 4;
     672             : 
     673           3 :         if (max_ext_offset>0xFFFFFFFF) meta->item_locations->offset_size = 8;
     674           3 :         else if (max_ext_offset) meta->item_locations->offset_size = 4;
     675             : 
     676             :         //and re-switch our table. We have to do it that way because it is
     677             :         //needed when the moov is written first
     678           3 :         if (writers) {
     679           3 :                 i=0;
     680           9 :                 while ((writer = (TrackWriter*)gf_list_enum(writers, &i))) {
     681             :                         //don't delete them !!!
     682           3 :                         stsc = writer->stsc;
     683           3 :                         stco = writer->stco;
     684           3 :                         writer->stsc = writer->stbl->SampleToChunk;
     685           3 :                         writer->stco = writer->stbl->ChunkOffset;
     686           3 :                         s32 stsc_pos = gf_list_del_item(writer->stbl->child_boxes, writer->stsc);
     687           3 :                         s32 stco_pos = gf_list_del_item(writer->stbl->child_boxes, writer->stco);
     688             : 
     689           3 :                         writer->stbl->SampleToChunk = stsc;
     690           3 :                         writer->stbl->ChunkOffset = stco;
     691           3 :                         gf_list_insert(writer->stbl->child_boxes, stsc, stsc_pos);
     692           3 :                         gf_list_insert(writer->stbl->child_boxes, stco, stco_pos);
     693             :                 }
     694             :         }
     695             :         return GF_OK;
     696             : }
     697             : 
     698             : 
     699         238 : GF_Err DoWriteMeta(GF_ISOFile *file, GF_MetaBox *meta, GF_BitStream *bs, Bool Emulation, u64 baseOffset, u64 *mdatSize)
     700             : {
     701             :         GF_ItemExtentEntry *entry;
     702             :         u64 maxExtendOffset, maxExtendSize;
     703             :         u32 i, j, count;
     704             : 
     705             :         maxExtendOffset = 0;
     706             :         maxExtendSize = 0;
     707         238 :         if (mdatSize) *mdatSize = 0;
     708         238 :         if (!meta->item_locations) return GF_OK;
     709         184 :         if (!meta->item_infos) return GF_OK;
     710             : 
     711         184 :         count = gf_list_count(meta->item_locations->location_entries);
     712         674 :         for (i=0; i<count; i++) {
     713             :                 u64 it_size;
     714         490 :                 GF_ItemLocationEntry *iloc = (GF_ItemLocationEntry *)gf_list_get(meta->item_locations->location_entries, i);
     715             :                 /*get item info*/
     716             :                 GF_ItemInfoEntryBox *iinf = NULL;
     717         490 :                 j=0;
     718        2646 :                 while ((iinf = (GF_ItemInfoEntryBox *)gf_list_enum(meta->item_infos->item_infos, &j))) {
     719        2156 :                         if (iinf->item_ID==iloc->item_ID) break;
     720             :                         iinf = NULL;
     721             :                 }
     722             : 
     723         490 :                 if (!iloc->base_offset && (gf_list_count(iloc->extent_entries)==1)) {
     724           1 :                         entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0);
     725           1 :                         if (!entry->extent_length && !entry->original_extent_offset && !entry->extent_index) {
     726           0 :                                 entry->extent_offset = 0;
     727           0 :                                 continue;
     728             :                         }
     729             :                 }
     730             : 
     731             :                 it_size = 0;
     732             :                 /*for self contained only*/
     733         490 :                 if (!iloc->data_reference_index) {
     734         490 :                         if (iloc->construction_method != 2) {
     735         488 :                                 iloc->base_offset = baseOffset;
     736             :                         }
     737             : 
     738             :                         /*new resource*/
     739         490 :                         if (iinf && (iinf->full_path || (iinf->tk_id && iinf->sample_num))) {
     740             :                                 FILE *src=NULL;
     741             : 
     742         488 :                                 if (!iinf->data_len && iinf->full_path) {
     743          10 :                                         src = gf_fopen(iinf->full_path, "rb");
     744          10 :                                         if (!src) continue;
     745          10 :                                         it_size = gf_fsize(src);
     746             :                                 } else {
     747         478 :                                         it_size = iinf->data_len;
     748             :                                 }
     749         488 :                                 if (maxExtendSize<it_size) maxExtendSize = it_size;
     750             : 
     751         488 :                                 if (!gf_list_count(iloc->extent_entries)) {
     752         242 :                                         GF_SAFEALLOC(entry, GF_ItemExtentEntry);
     753         242 :                                         if (!entry) return GF_OUT_OF_MEM;
     754         242 :                                         gf_list_add(iloc->extent_entries, entry);
     755             :                                 }
     756         488 :                                 entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0);
     757         488 :                                 entry->extent_offset = 0;
     758         488 :                                 entry->extent_length = it_size;
     759             : 
     760             :                                 //shared data, do not count it
     761         488 :                                 if (iinf->tk_id && iinf->sample_num) {
     762             :                                         it_size = 0;
     763             : //                                      maxExtendOffset = 0xFFFFFFFFFFUL;
     764           4 :                                         if (Emulation) {
     765           2 :                                                 meta->use_item_sample_sharing = GF_TRUE;
     766             :                                         }
     767             :                                 }
     768             : 
     769             :                                 /*OK write to mdat*/
     770         488 :                                 if (!Emulation) {
     771         244 :                                         if (iinf->tk_id && iinf->sample_num) {
     772             :                                         }
     773         242 :                                         else if (src) {
     774             :                                                 char cache_data[4096];
     775           5 :                                                 u64 remain = entry->extent_length;
     776          31 :                                                 while (remain) {
     777          21 :                                                         u32 size_cache = (remain>4096) ? 4096 : (u32) remain;
     778          21 :                                                         size_t read = gf_fread(cache_data, size_cache, src);
     779          21 :                                                         if (read ==(size_t) -1) break;
     780          21 :                                                         gf_bs_write_data(bs, cache_data, (u32) read);
     781          21 :                                                         remain -= (u32) read;
     782             :                                                 }
     783             :                                         } else {
     784         237 :                                                 gf_bs_write_data(bs, iinf->full_path, iinf->data_len);
     785             :                                         }
     786             :                                 }
     787         488 :                                 if (src) gf_fclose(src);
     788             :                         }
     789           2 :                         else if (gf_list_count(iloc->extent_entries)) {
     790           2 :                                 j=0;
     791          22 :                                 while ((entry = (GF_ItemExtentEntry *)gf_list_enum(iloc->extent_entries, &j))) {
     792          18 :                                         if (entry->extent_index) continue;
     793           0 :                                         if (j && (maxExtendOffset<it_size) ) maxExtendOffset = it_size;
     794             :                                         /*compute new offset*/
     795           0 :                                         if (iloc->construction_method != 2) {
     796           0 :                                                 entry->extent_offset = it_size;
     797             :                                         } else {
     798           0 :                                                 entry->extent_offset = baseOffset + it_size;
     799             :                                         }
     800           0 :                                         it_size += entry->extent_length;
     801           0 :                                         if (maxExtendSize<entry->extent_length) maxExtendSize = entry->extent_length;
     802             : 
     803             :                                         /*Reading from the input file*/
     804           0 :                                         if (!Emulation) {
     805             :                                                 char cache_data[4096];
     806             :                                                 u64 remain = entry->extent_length;
     807           0 :                                                 gf_bs_seek(file->movieFileMap->bs, entry->original_extent_offset + iloc->original_base_offset);
     808           0 :                                                 while (remain) {
     809           0 :                                                         u32 size_cache = (remain>4096) ? 4096 : (u32) remain;
     810           0 :                                                         gf_bs_read_data(file->movieFileMap->bs, cache_data, size_cache);
     811             :                                                         /*Writing to the output file*/
     812           0 :                                                         gf_bs_write_data(bs, cache_data, size_cache);
     813           0 :                                                         remain -= size_cache;
     814             :                                                 }
     815             :                                         }
     816             :                                 }
     817             :                         }
     818         490 :                         baseOffset += it_size;
     819         490 :                         if (mdatSize)
     820         490 :                                 *mdatSize += it_size;
     821             :                 } else {
     822             :                         /*we MUST have at least one extent for the dref data*/
     823           0 :                         if (!gf_list_count(iloc->extent_entries)) {
     824           0 :                                 GF_SAFEALLOC(entry, GF_ItemExtentEntry);
     825           0 :                                 if (!entry) return GF_OUT_OF_MEM;
     826           0 :                                 gf_list_add(iloc->extent_entries, entry);
     827             :                         }
     828           0 :                         entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0);
     829           0 :                         entry->extent_offset = 0;
     830             :                         /*0 means full length of referenced file*/
     831           0 :                         entry->extent_length = 0;
     832             :                 }
     833             :         }
     834             : 
     835             :         /*update offset & size length fields*/
     836         184 :         if (baseOffset>0xFFFFFFFF) meta->item_locations->base_offset_size = 8;
     837         184 :         else if (baseOffset) meta->item_locations->base_offset_size = 4;
     838             : 
     839         184 :         if (maxExtendSize>0xFFFFFFFF) meta->item_locations->length_size = 8;
     840         184 :         else if (maxExtendSize) meta->item_locations->length_size = 4;
     841             : 
     842         184 :         if (maxExtendOffset>0xFFFFFFFF) meta->item_locations->offset_size = 8;
     843         184 :         else if (maxExtendOffset) meta->item_locations->offset_size = 4;
     844             :         return GF_OK;
     845             : }
     846             : 
     847             : //this function writes track by track in the order of tracks inside the moov...
     848         350 : GF_Err DoWrite(MovieWriter *mw, GF_List *writers, GF_BitStream *bs, u8 Emulation, u64 StartOffset)
     849             : {
     850             :         u32 i;
     851             :         GF_Err e;
     852             :         TrackWriter *writer;
     853             :         u64 offset, sampOffset, predOffset;
     854             :         u32 chunkNumber, descIndex, sampSize;
     855             :         Bool force;
     856             :         GF_StscEntry *stsc_ent;
     857             :         u64 size, mdatSize = 0;
     858         350 :         GF_ISOFile *movie = mw->movie;
     859             : 
     860             :         /*write meta content first - WE DON'T support fragmentation of resources in ISOM atm*/
     861         350 :         if (movie->openMode != GF_ISOM_OPEN_WRITE) {
     862           5 :                 if (movie->meta) {
     863           0 :                         e = DoWriteMeta(movie, movie->meta, bs, Emulation, StartOffset, &size);
     864           0 :                         if (e) return e;
     865           0 :                         mdatSize += size;
     866           0 :                         StartOffset += size;
     867             :                 }
     868           5 :                 if (movie->moov && movie->moov->meta) {
     869           0 :                         e = DoWriteMeta(movie, movie->meta, bs, Emulation, StartOffset, &size);
     870           0 :                         if (e) return e;
     871           0 :                         mdatSize += size;
     872           0 :                         StartOffset += size;
     873             :                 }
     874           5 :                 i=0;
     875          17 :                 while ((writer = (TrackWriter*)gf_list_enum(writers, &i))) {
     876           7 :                         if (writer->mdia->mediaTrack->meta) {
     877           0 :                                 e = DoWriteMeta(movie, movie->meta, bs, Emulation, StartOffset, &size);
     878           0 :                                 if (e) return e;
     879           0 :                                 mdatSize += size;
     880           0 :                                 StartOffset += size;
     881             :                         }
     882             :                 }
     883             :         }
     884             : 
     885             :         offset = StartOffset;
     886             :         predOffset = 0;
     887         350 :         i=0;
     888        1108 :         while ((writer = (TrackWriter*)gf_list_enum(writers, &i))) {
     889        3512 :                 while (!writer->isDone) {
     890             :                         Bool self_contained;
     891        3104 :                         u32 nb_samp=1;
     892             :                         //To Check: are empty sample tables allowed ???
     893        3104 :                         if (writer->sampleNumber > writer->stbl->SampleSize->sampleCount) {
     894         389 :                                 writer->isDone = 1;
     895         389 :                                 continue;
     896             :                         }
     897        2715 :                         e = stbl_GetSampleInfos(writer->stbl, writer->sampleNumber, &sampOffset, &chunkNumber, &descIndex, &stsc_ent);
     898        2715 :                         if (e) return e;
     899        2715 :                         e = stbl_GetSampleSize(writer->stbl->SampleSize, writer->sampleNumber, &sampSize);
     900        2715 :                         if (e) return e;
     901             : 
     902        2715 :                         update_writer_constant_dur(movie, writer, stsc_ent, &nb_samp, &sampSize, GF_TRUE);
     903             : 
     904             :                         //update our chunks.
     905             :                         force = 0;
     906        2715 :                         if (movie->openMode == GF_ISOM_OPEN_WRITE) {
     907        1509 :                                 offset = sampOffset;
     908        1509 :                                 if (predOffset != offset)
     909             :                                         force = 1;
     910             :                         }
     911             : 
     912        2715 :                         if (writer->stbl->MaxChunkSize && (writer->chunkSize + sampSize > writer->stbl->MaxChunkSize)) {
     913           0 :                                 writer->chunkSize = 0;
     914             :                                 force = 1;
     915             :                         }
     916        2715 :                         writer->chunkSize += sampSize;
     917             : 
     918        2715 :                         self_contained = ((writer->all_dref_mode==ISOM_DREF_SELF) || Media_IsSelfContained(writer->mdia, descIndex) ) ? GF_TRUE : GF_FALSE;
     919             : 
     920             :                         //update our global offset...
     921        2715 :                         if (self_contained) {
     922        2715 :                                 e = stbl_SetChunkAndOffset(writer->stbl, writer->sampleNumber, descIndex, writer->stsc, &writer->stco, offset, force, nb_samp);
     923        2715 :                                 if (e) return e;
     924        2715 :                                 if (movie->openMode == GF_ISOM_OPEN_WRITE) {
     925        1509 :                                         predOffset = sampOffset + sampSize;
     926             :                                 } else {
     927        1206 :                                         offset += sampSize;
     928        1206 :                                         mdatSize += sampSize;
     929             :                                 }
     930             :                         } else {
     931           0 :                                 if (predOffset != offset) force = 1;
     932           0 :                                 predOffset = sampOffset + sampSize;
     933             :                                 //we have a DataRef, so use the offset idicated in sampleToChunk and ChunkOffset tables...
     934           0 :                                 e = stbl_SetChunkAndOffset(writer->stbl, writer->sampleNumber, descIndex, writer->stsc, &writer->stco, sampOffset, force, nb_samp);
     935           0 :                                 if (e) return e;
     936             :                         }
     937             :                         //we write the sample if not emulation
     938        2715 :                         if (!Emulation) {
     939        1033 :                                 if (self_contained) {
     940        1033 :                                         e = WriteSample(mw, sampSize, sampOffset, stsc_ent->isEdited, bs, 1);
     941        1033 :                                         if (e) return e;
     942             :                                 }
     943             :                         }
     944             :                         //ok, the track is done
     945        2715 :                         if (writer->sampleNumber >= writer->stbl->SampleSize->sampleCount) {
     946          13 :                                 writer->isDone = 1;
     947             :                         } else {
     948        2702 :                                 writer->sampleNumber += nb_samp;
     949             :                         }
     950             :                 }
     951             :         }
     952             :         //set the mdatSize...
     953         350 :         movie->mdat->dataSize = mdatSize;
     954         350 :         return GF_OK;
     955             : }
     956             : 
     957             : 
     958             : //write the file track by track, with moov box before or after the mdat
     959         346 : static GF_Err WriteFlat(MovieWriter *mw, u8 moovFirst, GF_BitStream *bs, Bool non_seakable, Bool for_fragments, GF_BitStream *moov_bs)
     960             : {
     961             :         GF_Err e;
     962             :         u32 i;
     963             :         u64 offset, finalOffset, totSize, begin, firstSize, finalSize;
     964             :         GF_Box *a, *cprt_box=NULL;
     965         346 :         GF_List *writers = gf_list_new();
     966         346 :         GF_ISOFile *movie = mw->movie;
     967             :         s32 moov_meta_pos=-1;
     968             : 
     969             :         //in case we did a read on the file while producing it, seek to end of edit
     970         346 :         totSize = gf_bs_get_size(bs);
     971         346 :         if (gf_bs_get_position(bs) != totSize) {
     972           0 :                 gf_bs_seek(bs, totSize);
     973             :         }
     974             :         begin = totSize = 0;
     975             : 
     976             :         //first setup the writers
     977         346 :         e = SetupWriters(mw, writers, 0);
     978         346 :         if (e) goto exit;
     979             : 
     980         346 :         if (!moovFirst) {
     981         345 :                 if ((movie->openMode == GF_ISOM_OPEN_WRITE) && !non_seakable) {
     982             :                         begin = 0;
     983           2 :                         totSize = gf_isom_datamap_get_offset(movie->editFileMap);
     984             :                         /*start boxes have not been written yet, do it*/
     985           2 :                         if (!totSize) {
     986           0 :                                 if (movie->is_jp2) {
     987           0 :                                         gf_bs_write_u32(movie->editFileMap->bs, 12);
     988           0 :                                         gf_bs_write_u32(movie->editFileMap->bs, GF_ISOM_BOX_TYPE_JP);
     989           0 :                                         gf_bs_write_u32(movie->editFileMap->bs, 0x0D0A870A);
     990             :                                         totSize += 12;
     991             :                                         begin += 12;
     992             :                                 }
     993           0 :                                 if (movie->brand) {
     994           0 :                                         e = gf_isom_box_size((GF_Box *)movie->brand);
     995           0 :                                         if (e) goto exit;
     996           0 :                                         e = gf_isom_box_write((GF_Box *)movie->brand, movie->editFileMap->bs);
     997           0 :                                         if (e) goto exit;
     998           0 :                                         totSize += movie->brand->size;
     999           0 :                                         begin += movie->brand->size;
    1000             :                                 }
    1001           0 :                                 if (movie->otyp) {
    1002           0 :                                         e = gf_isom_box_size((GF_Box *)movie->otyp);
    1003           0 :                                         if (e) goto exit;
    1004           0 :                                         e = gf_isom_box_write((GF_Box *)movie->otyp, movie->editFileMap->bs);
    1005           0 :                                         if (e) goto exit;
    1006           0 :                                         totSize += movie->otyp->size;
    1007           0 :                                         begin += movie->otyp->size;
    1008             :                                 }
    1009           0 :                                 if (movie->pdin) {
    1010           0 :                                         e = gf_isom_box_size((GF_Box *)movie->pdin);
    1011           0 :                                         if (e) goto exit;
    1012           0 :                                         e = gf_isom_box_write((GF_Box *)movie->pdin, movie->editFileMap->bs);
    1013           0 :                                         if (e) goto exit;
    1014           0 :                                         totSize += movie->pdin->size;
    1015           0 :                                         begin += movie->pdin->size;
    1016             :                                 }
    1017             :                         } else {
    1018           2 :                                 if (movie->is_jp2) begin += 12;
    1019           2 :                                 if (movie->brand) {
    1020           2 :                                         e = gf_isom_box_size((GF_Box *)movie->brand);
    1021           2 :                                         if (e) goto exit;
    1022           2 :                                         begin += movie->brand->size;
    1023             :                                 }
    1024           2 :                                 if (movie->otyp) {
    1025           0 :                                         e = gf_isom_box_size((GF_Box *)movie->otyp);
    1026           0 :                                         if (e) goto exit;
    1027           0 :                                         begin += movie->otyp->size;
    1028             :                                 }
    1029           2 :                                 if (movie->pdin) {
    1030           0 :                                         e = gf_isom_box_size((GF_Box *)movie->pdin);
    1031           0 :                                         if (e) goto exit;
    1032           0 :                                         begin += movie->pdin->size;
    1033             :                                 }
    1034             :                         }
    1035           2 :                         totSize -= begin;
    1036         343 :                 } else if (!non_seakable || for_fragments) {
    1037         342 :                         if (movie->is_jp2) {
    1038           0 :                                 gf_bs_write_u32(bs, 12);
    1039           0 :                                 gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_JP);
    1040           0 :                                 gf_bs_write_u32(bs, 0x0D0A870A);
    1041             :                         }
    1042         342 :                         if (movie->brand) {
    1043         342 :                                 e = gf_isom_box_size((GF_Box *)movie->brand);
    1044         342 :                                 if (e) goto exit;
    1045         342 :                                 e = gf_isom_box_write((GF_Box *)movie->brand, bs);
    1046         342 :                                 if (e) goto exit;
    1047             :                         }
    1048         342 :                         if (movie->otyp) {
    1049           0 :                                 e = gf_isom_box_size((GF_Box *)movie->otyp);
    1050           0 :                                 if (e) goto exit;
    1051           0 :                                 e = gf_isom_box_write((GF_Box *)movie->otyp, bs);
    1052           0 :                                 if (e) goto exit;
    1053             :                         }
    1054             :                         /*then progressive download*/
    1055         342 :                         if (movie->pdin) {
    1056           0 :                                 e = gf_isom_box_size((GF_Box *)movie->pdin);
    1057           0 :                                 if (e) goto exit;
    1058           0 :                                 e = gf_isom_box_write((GF_Box *)movie->pdin, bs);
    1059           0 :                                 if (e) goto exit;
    1060             :                         }
    1061             :                 }
    1062             : 
    1063             :                 //if the moov is at the end, write directly
    1064         345 :                 i=0;
    1065        2071 :                 while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
    1066        1381 :                         switch (a->type) {
    1067             :                         /*written by hand*/
    1068         345 :                         case GF_ISOM_BOX_TYPE_MOOV:
    1069             :                         case GF_ISOM_BOX_TYPE_META:
    1070         345 :                                 moov_meta_pos = i-1;
    1071             :                         case GF_ISOM_BOX_TYPE_FTYP:
    1072             :                         case GF_ISOM_BOX_TYPE_OTYP:
    1073             :                         case GF_ISOM_BOX_TYPE_PDIN:
    1074             : #ifndef GPAC_DISABLE_ISOM_ADOBE
    1075             :                         case GF_ISOM_BOX_TYPE_AFRA:
    1076             :                         case GF_ISOM_BOX_TYPE_ABST:
    1077             : #endif
    1078             :                                 break;
    1079         345 :                         case GF_ISOM_BOX_TYPE_MDAT:
    1080             :                                 //in case we're capturing
    1081         345 :                                 if (movie->openMode == GF_ISOM_OPEN_WRITE) {
    1082             :                                         //emulate a write to recreate our tables (media data already written)
    1083         342 :                                         e = DoWrite(mw, writers, bs, 1, begin);
    1084         342 :                                         if (e) goto exit;
    1085         342 :                                         continue;
    1086             :                                 }
    1087           3 :                                 if (non_seakable) {
    1088           0 :                                         begin = gf_bs_get_position(bs);
    1089             :                                         //do a sim pass to get the true mdat size
    1090           0 :                                         e = DoWrite(mw, writers, bs, 1, begin);
    1091           0 :                                         if (e) goto exit;
    1092             : 
    1093           0 :                                         if (movie->mdat->dataSize > 0xFFFFFFFF) {
    1094           0 :                                                 gf_bs_write_u32(bs, 1);
    1095             :                                         } else {
    1096           0 :                                                 gf_bs_write_u32(bs, (u32) movie->mdat->dataSize + 8);
    1097             :                                         }
    1098           0 :                                         gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_MDAT);
    1099           0 :                                         if (movie->mdat->dataSize > 0xFFFFFFFF) gf_bs_write_u64(bs, movie->mdat->dataSize + 8 + 8);
    1100             :                                         //reset writers and write samples
    1101           0 :                                         ResetWriters(writers);
    1102           0 :                                         e = DoWrite(mw, writers, bs, 0, gf_bs_get_position(bs));
    1103           0 :                                         if (e) goto exit;
    1104           0 :                                         movie->mdat->size = movie->mdat->dataSize;
    1105             :                                         totSize = 0;
    1106             :                                 } else {
    1107             :                                         //to avoid computing the size each time write always 4 + 4 + 8 bytes before
    1108           3 :                                         begin = gf_bs_get_position(bs);
    1109           3 :                                         gf_bs_write_u64(bs, 0);
    1110           3 :                                         gf_bs_write_u64(bs, 0);
    1111           3 :                                         e = DoWrite(mw, writers, bs, 0, gf_bs_get_position(bs));
    1112           3 :                                         if (e) goto exit;
    1113           3 :                                         totSize = gf_bs_get_position(bs) - begin;
    1114             :                                 }
    1115             :                                 break;
    1116             : 
    1117         345 :                         case GF_ISOM_BOX_TYPE_FREE:
    1118             :                                 //for backward compat with old arch, keep copyright before moov
    1119         345 :                                 if (((GF_FreeSpaceBox*)a)->dataSize>4) {
    1120             :                                         GF_FreeSpaceBox *fr = (GF_FreeSpaceBox*) a;
    1121         345 :                                         if ((fr->dataSize>20) && !strncmp(fr->data, "IsoMedia File", 13)) {
    1122         345 :                                                 e = gf_isom_box_size(a);
    1123         345 :                                                 if (e) goto exit;
    1124         345 :                                                 e = gf_isom_box_write(a, bs);
    1125         345 :                                                 if (e) goto exit;
    1126             :                                                 cprt_box = a;
    1127             :                                                 break;
    1128             :                                         }
    1129             :                                 }
    1130             :                         default:
    1131           1 :                                 if (moov_meta_pos < 0) {
    1132           1 :                                         e = gf_isom_box_size(a);
    1133           1 :                                         if (e) goto exit;
    1134           1 :                                         e = gf_isom_box_write(a, bs);
    1135           1 :                                         if (e) goto exit;
    1136             :                                 }
    1137             :                                 break;
    1138             :                         }
    1139             :                 }
    1140             : 
    1141         345 :                 if (moov_bs) {
    1142           3 :                         e = DoWrite(mw, writers, bs, 1, movie->mdat->bsOffset);
    1143           3 :                         if (e) goto exit;
    1144             : 
    1145           3 :                         firstSize = GetMoovAndMetaSize(movie, writers);
    1146             : 
    1147             :                         offset = firstSize;
    1148           3 :                         e = ShiftOffset(movie, writers, offset);
    1149           3 :                         if (e) goto exit;
    1150             :                         //get the size and see if it has changed (eg, we moved to 64 bit offsets)
    1151           3 :                         finalSize = GetMoovAndMetaSize(movie, writers);
    1152           3 :                         if (firstSize != finalSize) {
    1153             :                                 finalOffset = finalSize;
    1154             :                                 //OK, now we're sure about the final size.
    1155             :                                 //we don't need to re-emulate, as the only thing that changed is the offset
    1156             :                                 //so just shift the offset
    1157           0 :                                 e = ShiftOffset(movie, writers, finalOffset - offset);
    1158           0 :                                 if (e) goto exit;
    1159             :                         }
    1160             :                 }
    1161             :                 //get real sample offsets for meta items
    1162         345 :                 if (movie->meta) {
    1163           0 :                         store_meta_item_sample_ref_offsets(movie, writers, movie->meta);
    1164             :                 }
    1165             :                 //OK, write the movie box.
    1166         345 :                 e = WriteMoovAndMeta(movie, writers, moov_bs ? moov_bs : bs);
    1167         345 :                 if (e) goto exit;
    1168             : 
    1169             : #ifndef GPAC_DISABLE_ISOM_ADOBE
    1170         345 :                 i=0;
    1171        2071 :                 while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
    1172        1381 :                         switch (a->type) {
    1173           0 :                         case GF_ISOM_BOX_TYPE_AFRA:
    1174             :                         case GF_ISOM_BOX_TYPE_ABST:
    1175           0 :                                 e = gf_isom_box_size(a);
    1176           0 :                                 if (e) goto exit;
    1177           0 :                                 e = gf_isom_box_write(a, bs);
    1178           0 :                                 if (e) goto exit;
    1179             :                                 break;
    1180             :                         }
    1181             :                 }
    1182             : #endif
    1183             : 
    1184             :                 /*if data has been written, update mdat size*/
    1185         345 :                 if (totSize) {
    1186           5 :                         offset = gf_bs_get_position(bs);
    1187           5 :                         e = gf_bs_seek(bs, begin);
    1188           5 :                         if (e) goto exit;
    1189           5 :                         if (totSize > 0xFFFFFFFF) {
    1190           0 :                                 gf_bs_write_u32(bs, 1);
    1191             :                         } else {
    1192           5 :                                 gf_bs_write_u32(bs, (u32) totSize);
    1193             :                         }
    1194           5 :                         gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_MDAT);
    1195           5 :                         if (totSize > 0xFFFFFFFF) gf_bs_write_u64(bs, totSize);
    1196           5 :                         e = gf_bs_seek(bs, offset);
    1197           5 :                         movie->mdat->size = totSize;
    1198             :                 }
    1199             : 
    1200             :                 //then the rest
    1201         345 :                 i = (u32) (moov_meta_pos + 1);
    1202        1037 :                 while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
    1203         347 :                         if (a==cprt_box) continue;
    1204             : 
    1205           2 :                         switch (a->type) {
    1206             :                         case GF_ISOM_BOX_TYPE_MOOV:
    1207             :                         case GF_ISOM_BOX_TYPE_META:
    1208             :                         case GF_ISOM_BOX_TYPE_FTYP:
    1209             :                         case GF_ISOM_BOX_TYPE_OTYP:
    1210             :                         case GF_ISOM_BOX_TYPE_PDIN:
    1211             :                         case GF_ISOM_BOX_TYPE_MDAT:
    1212             :                                 break;
    1213           0 :                         default:
    1214           0 :                                 e = gf_isom_box_size(a);
    1215           0 :                                 if (e) goto exit;
    1216           0 :                                 e = gf_isom_box_write(a, bs);
    1217           0 :                                 if (e) goto exit;
    1218             :                         }
    1219             :                 }
    1220             :                 goto exit;
    1221             :         }
    1222             : 
    1223             :         //nope, we have to write the moov first. The pb is that
    1224             :         //1 - we don't know its size till the mdat is written
    1225             :         //2 - we don't know the ofset at which the mdat will start...
    1226             :         //3 - once the mdat is written, the chunkOffset table can have changed...
    1227             : 
    1228           1 :         if (movie->is_jp2) {
    1229           0 :                 gf_bs_write_u32(bs, 12);
    1230           0 :                 gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_JP);
    1231           0 :                 gf_bs_write_u32(bs, 0x0D0A870A);
    1232             :         }
    1233           1 :         if (movie->brand) {
    1234           1 :                 e = gf_isom_box_size((GF_Box *)movie->brand);
    1235           1 :                 if (e) goto exit;
    1236           1 :                 e = gf_isom_box_write((GF_Box *)movie->brand, bs);
    1237           1 :                 if (e) goto exit;
    1238             :         }
    1239           1 :         if (movie->otyp) {
    1240           0 :                 e = gf_isom_box_size((GF_Box *)movie->otyp);
    1241           0 :                 if (e) goto exit;
    1242           0 :                 e = gf_isom_box_write((GF_Box *)movie->otyp, bs);
    1243           0 :                 if (e) goto exit;
    1244             :         }
    1245             :         /*then progressive dnload*/
    1246           1 :         if (movie->pdin) {
    1247           0 :                 e = gf_isom_box_size((GF_Box *)movie->pdin);
    1248           0 :                 if (e) goto exit;
    1249           0 :                 e = gf_isom_box_write((GF_Box *)movie->pdin, bs);
    1250           0 :                 if (e) goto exit;
    1251             :         }
    1252             : 
    1253             :         //write all boxes before moov
    1254           1 :         i=0;
    1255           7 :         while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
    1256           5 :                 switch (a->type) {
    1257           1 :                 case GF_ISOM_BOX_TYPE_MOOV:
    1258             :                 case GF_ISOM_BOX_TYPE_META:
    1259           1 :                         moov_meta_pos = i-1;
    1260           1 :                         break;
    1261             :                 case GF_ISOM_BOX_TYPE_FTYP:
    1262             :                 case GF_ISOM_BOX_TYPE_OTYP:
    1263             :                 case GF_ISOM_BOX_TYPE_PDIN:
    1264             :                 case GF_ISOM_BOX_TYPE_MDAT:
    1265             :                         break;
    1266             :                 //for backward compat with old arch keep out copyright after moov
    1267           1 :                 case GF_ISOM_BOX_TYPE_FREE:
    1268           1 :                         if (((GF_FreeSpaceBox*)a)->dataSize>4) {
    1269             :                                 GF_FreeSpaceBox *fr = (GF_FreeSpaceBox*) a;
    1270           1 :                                 if ((fr->dataSize>20) && !strncmp(fr->data, "IsoMedia File", 13)) {
    1271             :                                         cprt_box = a;
    1272             :                                         break;
    1273             :                                 }
    1274             :                         }
    1275             :                 default:
    1276           1 :                         if (moov_meta_pos<0) {
    1277           1 :                                 e = gf_isom_box_size(a);
    1278           1 :                                 if (e) goto exit;
    1279           1 :                                 e = gf_isom_box_write(a, bs);
    1280           1 :                                 if (e) goto exit;
    1281             :                         }
    1282             :                         break;
    1283             :                 }
    1284             :         }
    1285             : 
    1286             :         //What we will do is first emulate the write from the beginning...
    1287             :         //note: this will set the size of the mdat
    1288           1 :         e = DoWrite(mw, writers, bs, 1, gf_bs_get_position(bs));
    1289           1 :         if (e) goto exit;
    1290             : 
    1291           1 :         firstSize = GetMoovAndMetaSize(movie, writers);
    1292             :         //offset = (firstSize > 0xFFFFFFFF ? firstSize + 8 : firstSize) + 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0);
    1293           1 :         offset = firstSize + 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0);
    1294           1 :         e = ShiftOffset(movie, writers, offset);
    1295           1 :         if (e) goto exit;
    1296             :         //get the size and see if it has changed (eg, we moved to 64 bit offsets)
    1297           1 :         finalSize = GetMoovAndMetaSize(movie, writers);
    1298           1 :         if (firstSize != finalSize) {
    1299           0 :                 finalOffset = finalSize + 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0);
    1300             :                 //OK, now we're sure about the final size.
    1301             :                 //we don't need to re-emulate, as the only thing that changed is the offset
    1302             :                 //so just shift the offset
    1303           0 :                 e = ShiftOffset(movie, writers, finalOffset - offset);
    1304           0 :                 if (e) goto exit;
    1305             :         }
    1306             :         //now write our stuff
    1307           1 :         e = WriteMoovAndMeta(movie, writers, bs);
    1308           1 :         if (e) goto exit;
    1309           1 :         e = gf_isom_box_size((GF_Box *)movie->mdat);
    1310           1 :         if (e) goto exit;
    1311           1 :         e = gf_isom_box_write((GF_Box *)movie->mdat, bs);
    1312           1 :         if (e) goto exit;
    1313             : 
    1314             :         //we don't need the offset as the moov is already written...
    1315           1 :         ResetWriters(writers);
    1316           1 :         e = DoWrite(mw, writers, bs, 0, 0);
    1317           1 :         if (e) goto exit;
    1318             : 
    1319             :         //then the rest
    1320           1 :         i=0;
    1321           7 :         while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
    1322           5 :                 if ((i-1<= (u32) moov_meta_pos) && (a!=cprt_box)) continue;
    1323           2 :                 switch (a->type) {
    1324             :                 case GF_ISOM_BOX_TYPE_MOOV:
    1325             :                 case GF_ISOM_BOX_TYPE_META:
    1326             :                 case GF_ISOM_BOX_TYPE_FTYP:
    1327             :                 case GF_ISOM_BOX_TYPE_OTYP:
    1328             :                 case GF_ISOM_BOX_TYPE_PDIN:
    1329             :                 case GF_ISOM_BOX_TYPE_MDAT:
    1330             :                         break;
    1331           1 :                 default:
    1332           1 :                         e = gf_isom_box_size(a);
    1333           1 :                         if (e) goto exit;
    1334           1 :                         e = gf_isom_box_write(a, bs);
    1335           1 :                         if (e) goto exit;
    1336             :                 }
    1337             :         }
    1338             : 
    1339           1 : exit:
    1340         346 :         CleanWriters(writers);
    1341         346 :         gf_list_del(writers);
    1342         346 :         return e;
    1343             : }
    1344             : 
    1345           2 : GF_Err DoFullInterleave(MovieWriter *mw, GF_List *writers, GF_BitStream *bs, u8 Emulation, u64 StartOffset)
    1346             : {
    1347             : 
    1348             :         u32 i, tracksDone;
    1349             :         TrackWriter *tmp, *curWriter, *prevWriter;
    1350             :         GF_Err e;
    1351             :         u64 DTS, DTStmp, TStmp;
    1352             :         s64 res;
    1353             :         u32 descIndex, sampSize, chunkNumber;
    1354             :         u16 curGroupID, curTrackPriority;
    1355             :         Bool forceNewChunk, writeGroup;
    1356             :         GF_StscEntry *stsc_ent;
    1357             :         //this is used to emulate the write ...
    1358             :         u64 offset, totSize, sampOffset;
    1359           2 :         GF_ISOFile *movie = mw->movie;
    1360             : 
    1361             :         totSize = 0;
    1362             :         curGroupID = 1;
    1363             : 
    1364             :         prevWriter = NULL;
    1365             :         //we emulate a write from this offset...
    1366             :         offset = StartOffset;
    1367             :         tracksDone = 0;
    1368             : 
    1369             :         //browse each groups
    1370             :         while (1) {
    1371           0 :                 writeGroup = 1;
    1372             : 
    1373             :                 //proceed a group
    1374        1488 :                 while (writeGroup) {
    1375        1484 :                         u32 nb_samp = 1;
    1376             :                         Bool self_contained, chunked_forced=GF_FALSE;
    1377             :                         //first get the appropriated sample for the min time in this group
    1378             :                         curWriter = NULL;
    1379             :                         DTStmp = (u64) -1;
    1380             :                         TStmp = 0;
    1381             :                         curTrackPriority = (u16) -1;
    1382             : 
    1383        1484 :                         i=0;
    1384        8904 :                         while ((tmp = (TrackWriter*)gf_list_enum(writers, &i))) {
    1385             : 
    1386             :                                 //is it done writing ?
    1387             :                                 //is it in our group ??
    1388        5936 :                                 if (tmp->isDone || tmp->stbl->groupID != curGroupID) continue;
    1389             : 
    1390             :                                 //OK, get the current sample in this track
    1391        5888 :                                 stbl_GetSampleDTS(tmp->stbl->TimeToSample, tmp->sampleNumber, &DTS);
    1392        5888 :                                 res = TStmp ? DTStmp * tmp->timeScale - DTS * TStmp : 0;
    1393        4406 :                                 if (res < 0) continue;
    1394        3018 :                                 if ((!res) && curTrackPriority <= tmp->stbl->trackPriority) continue;
    1395             :                                 curWriter = tmp;
    1396        2372 :                                 curTrackPriority = tmp->stbl->trackPriority;
    1397        2372 :                                 DTStmp = DTS;
    1398        2372 :                                 TStmp = tmp->timeScale;
    1399             :                         }
    1400             :                         //no sample found, we're done with this group
    1401        1484 :                         if (!curWriter) {
    1402             :                                 //we're done with the group
    1403             :                                 writeGroup = 0;
    1404           4 :                                 continue;
    1405             :                         }
    1406             :                         //To Check: are empty sample tables allowed ???
    1407        1482 :                         if (curWriter->sampleNumber > curWriter->stbl->SampleSize->sampleCount) {
    1408           0 :                                 curWriter->isDone = 1;
    1409           0 :                                 tracksDone ++;
    1410           0 :                                 continue;
    1411             :                         }
    1412             : 
    1413        1482 :                         e = stbl_GetSampleInfos(curWriter->stbl, curWriter->sampleNumber, &sampOffset, &chunkNumber, &descIndex, &stsc_ent);
    1414        1482 :                         if (e) return e;
    1415        1482 :                         e = stbl_GetSampleSize(curWriter->stbl->SampleSize, curWriter->sampleNumber, &sampSize);
    1416        1482 :                         if (e) return e;
    1417             : 
    1418        1482 :                         update_writer_constant_dur(movie, curWriter, stsc_ent, &nb_samp, &sampSize, GF_FALSE);
    1419             : 
    1420        1482 :                         if (curWriter->stbl->MaxChunkSize && (curWriter->chunkSize + sampSize > curWriter->stbl->MaxChunkSize)) {
    1421           0 :                                 curWriter->chunkSize = 0;
    1422             :                                 chunked_forced = forceNewChunk = 1;
    1423             :                         }
    1424        1482 :                         curWriter->chunkSize += sampSize;
    1425             : 
    1426        1482 :                         self_contained = ((curWriter->all_dref_mode==ISOM_DREF_SELF) || Media_IsSelfContained(curWriter->mdia, descIndex) ) ? GF_TRUE : GF_FALSE;
    1427             : 
    1428             :                         //do we actually write, or do we emulate ?
    1429        1482 :                         if (Emulation) {
    1430             :                                 //are we in the same track ??? If not, force a new chunk when adding this sample
    1431         741 :                                 if (!chunked_forced) {
    1432         741 :                                         if (curWriter != prevWriter) {
    1433             :                                                 forceNewChunk = 1;
    1434             :                                         } else {
    1435             :                                                 forceNewChunk = 0;
    1436             :                                         }
    1437             :                                 }
    1438             :                                 //update our offsets...
    1439         741 :                                 if (self_contained) {
    1440         741 :                                         e = stbl_SetChunkAndOffset(curWriter->stbl, curWriter->sampleNumber, descIndex, curWriter->stsc, &curWriter->stco, offset, forceNewChunk, nb_samp);
    1441         741 :                                         if (e) return e;
    1442         741 :                                         offset += sampSize;
    1443         741 :                                         totSize += sampSize;
    1444             :                                 } else {
    1445             : //                                      if (curWriter->prev_offset != sampOffset) forceNewChunk = 1;
    1446           0 :                                         curWriter->prev_offset = sampOffset + sampSize;
    1447             : 
    1448             :                                         //we have a DataRef, so use the offset idicated in sampleToChunk
    1449             :                                         //and ChunkOffset tables...
    1450           0 :                                         e = stbl_SetChunkAndOffset(curWriter->stbl, curWriter->sampleNumber, descIndex, curWriter->stsc, &curWriter->stco, sampOffset, chunked_forced, nb_samp);
    1451           0 :                                         if (e) return e;
    1452             :                                 }
    1453             :                         } else {
    1454             :                                 //this is no game, we're writing ....
    1455         741 :                                 if (self_contained) {
    1456         741 :                                         e = WriteSample(mw, sampSize, sampOffset, stsc_ent->isEdited, bs, 1);
    1457         741 :                                         if (e) return e;
    1458             :                                 }
    1459             :                         }
    1460             :                         //ok, the sample is done
    1461        1482 :                         if (curWriter->sampleNumber == curWriter->stbl->SampleSize->sampleCount) {
    1462           8 :                                 curWriter->isDone = 1;
    1463             :                                 //one more track done...
    1464           8 :                                 tracksDone ++;
    1465             :                         } else {
    1466        1474 :                                 curWriter->sampleNumber += nb_samp;
    1467             :                         }
    1468             :                         prevWriter = curWriter;
    1469             :                 }
    1470             :                 //if all our track are done, break
    1471           2 :                 if (tracksDone == gf_list_count(writers)) break;
    1472             :                 //go to next group
    1473           0 :                 curGroupID ++;
    1474             :         }
    1475           2 :         if (movie->mdat)
    1476           2 :                 movie->mdat->dataSize = totSize;
    1477             :         return GF_OK;
    1478             : }
    1479             : 
    1480             : 
    1481             : 
    1482             : /*uncomment the following to easily test large file generation. This will prepend 4096*1MByte of 0 before the media data*/
    1483             : //#define TEST_LARGE_FILES
    1484             : 
    1485        2112 : GF_Err DoInterleave(MovieWriter *mw, GF_List *writers, GF_BitStream *bs, u8 Emulation, u64 StartOffset, Bool drift_inter)
    1486             : {
    1487             :         u32 i, tracksDone;
    1488             :         TrackWriter *tmp, *curWriter;
    1489             :         GF_Err e;
    1490             :         u32 descIndex, sampSize, chunkNumber;
    1491             :         u64 DTS;
    1492             :         u32 moov_timescale;
    1493             :         u16 curGroupID;
    1494             :         Bool forceNewChunk, writeGroup;
    1495             :         GF_StscEntry *stsc_ent;
    1496             :         //this is used to emulate the write ...
    1497             :         u64 offset, sampOffset, size, mdatSize;
    1498             :         u32 count;
    1499        2112 :         GF_ISOFile *movie = mw->movie;
    1500             : 
    1501             :         mdatSize = 0;
    1502             : 
    1503             : #ifdef TEST_LARGE_FILES
    1504             :         if (!Emulation) {
    1505             :                 char *blank;
    1506             :                 u32 count, i;
    1507             :                 i = count = 0;
    1508             :                 blank = gf_malloc(sizeof(char)*1024*1024);
    1509             :                 memset(blank, 0, sizeof(char)*1024*1024);
    1510             :                 count = 4096;
    1511             :                 memset(blank, 0, sizeof(char)*1024*1024);
    1512             :                 while (i<count) {
    1513             :                         u32 res = gf_bs_write_data(bs, blank, 1024*1024);
    1514             :                         if (res != 1024*1024) {
    1515             :                                 GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("error writing to disk: only %d bytes written\n", res));
    1516             :                         }
    1517             :                         i++;
    1518             :                         GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("writing blank block: %.02f done - %d/%d \r", (100.0*i)/count , i, count));
    1519             :                 }
    1520             :                 gf_free(blank);
    1521             :         }
    1522             :         mdatSize = 4096*1024;
    1523             :         mdatSize *= 1024;
    1524             : #endif
    1525             : 
    1526             :         /*write meta content first - WE DON'T support fragmentation of resources in ISOM atm*/
    1527        2112 :         if (movie->meta) {
    1528         188 :                 e = DoWriteMeta(movie, movie->meta, bs, Emulation, StartOffset, &size);
    1529         188 :                 if (e) return e;
    1530         188 :                 mdatSize += size;
    1531         188 :                 StartOffset += (u32) size;
    1532             :         }
    1533        2112 :         if (movie->moov) {
    1534        1938 :                 if (movie->moov->meta) {
    1535           0 :                         e = DoWriteMeta(movie, movie->moov->meta, bs, Emulation, StartOffset, &size);
    1536           0 :                         if (e) return e;
    1537           0 :                         mdatSize += size;
    1538           0 :                         StartOffset += (u32) size;
    1539             :                 }
    1540        1938 :                 i=0;
    1541        6708 :                 while ((tmp = (TrackWriter*)gf_list_enum(writers, &i))) {
    1542        2832 :                         if (tmp->mdia->mediaTrack->meta) {
    1543          50 :                                 e = DoWriteMeta(movie, tmp->mdia->mediaTrack->meta, bs, Emulation, StartOffset, &size);
    1544          50 :                                 if (e) return e;
    1545          50 :                                 mdatSize += size;
    1546          50 :                                 StartOffset += (u32) size;
    1547             :                         }
    1548             :                 }
    1549             :         }
    1550             : 
    1551             : 
    1552        2112 :         if (movie->storageMode == GF_ISOM_STORE_TIGHT)
    1553           2 :                 return DoFullInterleave(mw, writers, bs, Emulation, StartOffset);
    1554             : 
    1555             :         curGroupID = 1;
    1556             :         //we emulate a write from this offset...
    1557             :         offset = StartOffset;
    1558             :         tracksDone = 0;
    1559             : 
    1560             : #ifdef TEST_LARGE_FILES
    1561             :         offset += mdatSize;
    1562             : #endif
    1563             : 
    1564        2110 :         moov_timescale = movie->moov && movie->moov->mvhd ? movie->moov->mvhd->timeScale : 1000;
    1565             : 
    1566        2110 :         count = gf_list_count(writers);
    1567             :         //browse each groups
    1568      196700 :         while (1) {
    1569             :                 /*the max DTS the chunk of the current writer*/
    1570             :                 u64 chunkLastDTS = 0;
    1571             :                 /*the timescale related to the max DTS*/
    1572             :                 u32 chunkLastScale = 0;
    1573             : 
    1574             :                 writeGroup = 1;
    1575             : 
    1576             :                 //proceed a group
    1577      702032 :                 while (writeGroup) {
    1578             :                         curWriter = NULL;
    1579     2403110 :                         for (i=0 ; i < count; i++) {
    1580     2098698 :                                 tmp = (TrackWriter*)gf_list_get(writers, i);
    1581             : 
    1582             :                                 //is it done writing ?
    1583     2098698 :                                 if (tmp->isDone) continue;
    1584             : 
    1585             :                                 //is it in our group ??
    1586      323284 :                                 if (tmp->stbl->groupID != curGroupID) continue;
    1587             : 
    1588             :                                 //write till this chunk is full on this track...
    1589     2067264 :                                 while (1) {
    1590             :                                         Bool self_contained;
    1591     2193240 :                                         u32 nb_samp = 1;
    1592             :                                         u32 sample_dur;
    1593             :                                         u64 chunk_prev_dur;
    1594             :                                         //To Check: are empty sample tables allowed ???
    1595     2193240 :                                         if (tmp->sampleNumber > tmp->stbl->SampleSize->sampleCount) {
    1596          48 :                                                 tmp->isDone = 1;
    1597          48 :                                                 tracksDone ++;
    1598      126024 :                                                 break;
    1599             :                                         }
    1600             : 
    1601             :                                         //OK, get the current sample in this track
    1602     2193192 :                                         stbl_GetSampleDTS_and_Duration(tmp->stbl->TimeToSample, tmp->sampleNumber, &DTS, &sample_dur);
    1603             : 
    1604             :                                         //can this sample fit in our chunk ?
    1605     2193192 :                                         if ( ( (DTS - tmp->DTSprev) + tmp->chunkDur) * moov_timescale > movie->interleavingTime * tmp->timeScale
    1606             :                                                 /*drift check: reject sample if outside our check window*/
    1607     2030970 :                                                 || (drift_inter && chunkLastDTS && ( ((u64)tmp->DTSprev*chunkLastScale) > ((u64)chunkLastDTS*tmp->timeScale)) )
    1608             :                                            ) {
    1609             :                                                 //in case the sample is longer than InterleaveTime
    1610      163866 :                                                 if (!tmp->chunkDur) {
    1611             :                                                         forceNewChunk = 1;
    1612             :                                                 } else {
    1613             :                                                         //this one is full. go to next one (exit the loop)
    1614      123152 :                                                         tmp->chunkDur = 0;
    1615             :                                                         //forceNewChunk = 0;
    1616      123152 :                                                         break;
    1617             :                                                 }
    1618             :                                         } else {
    1619     2029326 :                                                 forceNewChunk = tmp->chunkDur ? 0 : 1;
    1620             :                                         }
    1621             :                                         //OK, we can write this track
    1622             :                                         curWriter = tmp;
    1623             : 
    1624             :                                         //small check for first 2 samples (DTS = 0)
    1625             :                                         //only in the old mode can chunkdur be 0 for dts 0
    1626     2070040 :                                         if (tmp->sampleNumber == 2 && !tmp->chunkDur && gf_sys_old_arch_compat() ) {
    1627             :                                                 forceNewChunk = 0;
    1628             :                                         }
    1629             : 
    1630     2070040 :                                         chunk_prev_dur = tmp->chunkDur;
    1631             :                                         //FIXME we do not apply patch in test mode for now since this breaks all our hashes, remove this
    1632             :                                         //once we move to filters permanently
    1633     2070040 :                                         if (!gf_sys_old_arch_compat()) {
    1634         346 :                                                 tmp->chunkDur += sample_dur;
    1635             :                                         } else {
    1636             :                                                 //old style, compute based on DTS diff
    1637     2069694 :                                                 tmp->chunkDur += (u32) (DTS - tmp->DTSprev);
    1638             :                                         }
    1639     2070040 :                                         tmp->DTSprev = DTS;
    1640             : 
    1641     2070040 :                                         e = stbl_GetSampleInfos(curWriter->stbl, curWriter->sampleNumber, &sampOffset, &chunkNumber, &descIndex, &stsc_ent);
    1642     2070040 :                                         if (e)
    1643           0 :                                                 return e;
    1644     2070040 :                                         e = stbl_GetSampleSize(curWriter->stbl->SampleSize, curWriter->sampleNumber, &sampSize);
    1645     2070040 :                                         if (e)
    1646             :                                                 return e;
    1647             : 
    1648     2070040 :                                         self_contained = ((curWriter->all_dref_mode==ISOM_DREF_SELF) || Media_IsSelfContained(curWriter->mdia, descIndex)) ? GF_TRUE : GF_FALSE;
    1649             : 
    1650     2070040 :                                         update_writer_constant_dur(movie, curWriter, stsc_ent, &nb_samp, &sampSize, GF_FALSE);
    1651             : 
    1652     2070040 :                                         if (curWriter->stbl->MaxChunkSize && (curWriter->chunkSize + sampSize > curWriter->stbl->MaxChunkSize)) {
    1653          28 :                                                 curWriter->chunkSize = 0;
    1654          28 :                                                 tmp->chunkDur -= chunk_prev_dur;
    1655             :                                                 forceNewChunk = 1;
    1656             :                                         }
    1657     2070040 :                                         curWriter->chunkSize += sampSize;
    1658             : 
    1659             :                                         //do we actually write, or do we emulate ?
    1660     2070040 :                                         if (Emulation) {
    1661             :                                                 //update our offsets...
    1662     1035020 :                                                 if (self_contained) {
    1663     1034506 :                                                         e = stbl_SetChunkAndOffset(curWriter->stbl, curWriter->sampleNumber, descIndex, curWriter->stsc, &curWriter->stco, offset, forceNewChunk, nb_samp);
    1664     1034506 :                                                         if (e)
    1665             :                                                                 return e;
    1666     1034506 :                                                         offset += sampSize;
    1667     1034506 :                                                         mdatSize += sampSize;
    1668             :                                                 } else {
    1669         514 :                                                         if (curWriter->prev_offset != sampOffset) forceNewChunk = 1;
    1670         514 :                                                         curWriter->prev_offset = sampOffset + sampSize;
    1671             : 
    1672             :                                                         //we have a DataRef, so use the offset idicated in sampleToChunk
    1673             :                                                         //and ChunkOffset tables...
    1674         514 :                                                         e = stbl_SetChunkAndOffset(curWriter->stbl, curWriter->sampleNumber, descIndex, curWriter->stsc, &curWriter->stco, sampOffset, forceNewChunk, nb_samp);
    1675         514 :                                                         if (e) return e;
    1676             :                                                 }
    1677             :                                         } else {
    1678             :                                                 //we're writing ....
    1679     1035020 :                                                 if (self_contained) {
    1680     1034506 :                                                         e = WriteSample(mw, sampSize, sampOffset, stsc_ent->isEdited, bs, nb_samp);
    1681     1034506 :                                                         if (e)
    1682             :                                                                 return e;
    1683             :                                                 }
    1684             :                                         }
    1685             :                                         //ok, the sample is done
    1686     2070040 :                                         if (curWriter->sampleNumber >= curWriter->stbl->SampleSize->sampleCount) {
    1687        2776 :                                                 curWriter->isDone = 1;
    1688             :                                                 //one more track done...
    1689        2776 :                                                 tracksDone ++;
    1690        2776 :                                                 break;
    1691             :                                         } else {
    1692     2067264 :                                                 curWriter->sampleNumber += nb_samp;
    1693             :                                         }
    1694             :                                 }
    1695             :                                 /*record chunk end-time & track timescale for drift-controled interleaving*/
    1696      125976 :                                 if (drift_inter && curWriter) {
    1697      125960 :                                         chunkLastScale = curWriter->timeScale;
    1698      125960 :                                         chunkLastDTS = curWriter->DTSprev;
    1699             :                                         /*add one interleave window drift - since the "maxDTS" is the previously written one, we will
    1700             :                                         have the following cases:
    1701             :                                         - sample doesn't fit: post-pone and force new chunk
    1702             :                                         - sample time larger than previous chunk time + interleave: post-pone and force new chunk
    1703             :                                         - otherwise store and track becomes current reference
    1704             : 
    1705             :                                         this ensures a proper drift regulation (max DTS diff is less than the interleaving window)
    1706             :                                         */
    1707      125960 :                                         chunkLastDTS += curWriter->timeScale * movie->interleavingTime / moov_timescale;
    1708             :                                 }
    1709             :                         }
    1710             :                         //no sample found, we're done with this group
    1711      304412 :                         if (!curWriter) {
    1712             :                                 writeGroup = 0;
    1713      198810 :                                 continue;
    1714             :                         }
    1715             :                 }
    1716             :                 //if all our track are done, break
    1717      198810 :                 if (tracksDone == gf_list_count(writers)) break;
    1718             :                 //go to next group
    1719      196700 :                 curGroupID ++;
    1720             :         }
    1721        2110 :         if (movie->mdat) movie->mdat->dataSize = mdatSize;
    1722             :         return GF_OK;
    1723             : }
    1724             : 
    1725             : 
    1726          77 : static GF_Err write_blank_data(GF_BitStream *bs, u32 size)
    1727             : {
    1728             :         u8 data[1000];
    1729             : 
    1730             :         memset(data, 0, 1000);
    1731          77 :         strcpy(data, gf_sys_is_test_mode() ? GPAC_ISOM_CPRT_NOTICE : GPAC_ISOM_CPRT_NOTICE_VERSION);
    1732             : 
    1733          91 :         while (size) {
    1734          68 :                 if (size > 1000) {
    1735          14 :                         gf_bs_write_data(bs, data, 1000);
    1736          14 :                         size-=1000;
    1737             :                 } else {
    1738          54 :                         gf_bs_write_data(bs, data, size);
    1739          54 :                         break;
    1740             :                 }
    1741             :         }
    1742          77 :         return GF_OK;
    1743             : }
    1744             : 
    1745          56 : static GF_Err write_free_box(GF_BitStream *bs, u32 size)
    1746             : {
    1747          56 :         if (size<8) return GF_BAD_PARAM;
    1748          56 :         gf_bs_write_u32(bs, size);
    1749          56 :         gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_FREE);
    1750          56 :         return write_blank_data(bs, size-8);
    1751             : }
    1752             : 
    1753             : 
    1754        1056 : static GF_Err WriteInterleaved(MovieWriter *mw, GF_BitStream *bs, Bool drift_inter)
    1755             : {
    1756             :         GF_Err e;
    1757             :         u32 i;
    1758             :         s32 moov_meta_pos=-1;
    1759             :         GF_Box *a, *cprt_box=NULL;
    1760             :         u64 firstSize, finalSize, offset, finalOffset;
    1761        1056 :         GF_List *writers = gf_list_new();
    1762        1056 :         GF_ISOFile *movie = mw->movie;
    1763             : 
    1764             :         //first setup the writers
    1765        1056 :         if (movie->no_inplace_rewrite) {
    1766        1056 :                 e = SetupWriters(mw, writers, 1);
    1767        1056 :                 if (e) goto exit;
    1768             :         }
    1769             : 
    1770        1056 :         if (movie->is_jp2) {
    1771           0 :                 gf_bs_write_u32(bs, 12);
    1772           0 :                 gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_JP);
    1773           0 :                 gf_bs_write_u32(bs, 0x0D0A870A);
    1774             :         }
    1775        1056 :         if (movie->brand) {
    1776        1054 :                 e = gf_isom_box_size((GF_Box *)movie->brand);
    1777        1054 :                 if (e) goto exit;
    1778        1054 :                 e = gf_isom_box_write((GF_Box *)movie->brand, bs);
    1779        1054 :                 if (e) goto exit;
    1780             :         }
    1781        1056 :         if (movie->otyp) {
    1782           0 :                 e = gf_isom_box_size((GF_Box *)movie->otyp);
    1783           0 :                 if (e) goto exit;
    1784           0 :                 e = gf_isom_box_write((GF_Box *)movie->otyp, bs);
    1785           0 :                 if (e) goto exit;
    1786             :         }
    1787        1056 :         if (movie->pdin) {
    1788           0 :                 e = gf_isom_box_size((GF_Box *)movie->pdin);
    1789           0 :                 if (e) goto exit;
    1790           0 :                 e = gf_isom_box_write((GF_Box *)movie->pdin, bs);
    1791           0 :                 if (e) goto exit;
    1792             :         }
    1793             : 
    1794             :         //write all boxes before moov
    1795        1056 :         i=0;
    1796        6356 :         while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
    1797        4244 :                 switch (a->type) {
    1798        1063 :                 case GF_ISOM_BOX_TYPE_MOOV:
    1799             :                 case GF_ISOM_BOX_TYPE_META:
    1800        1063 :                         moov_meta_pos = i-1;
    1801        1063 :                         break;
    1802             :                 case GF_ISOM_BOX_TYPE_FTYP:
    1803             :                 case GF_ISOM_BOX_TYPE_OTYP:
    1804             :                 case GF_ISOM_BOX_TYPE_PDIN:
    1805             :                 case GF_ISOM_BOX_TYPE_MDAT:
    1806             :                         break;
    1807        1069 :                 case GF_ISOM_BOX_TYPE_FREE:
    1808             :                         //for backward compat with old arch, keep copyright before moov
    1809        1069 :                         if (((GF_FreeSpaceBox*)a)->dataSize>4) {
    1810             :                                 GF_FreeSpaceBox *fr = (GF_FreeSpaceBox*) a;
    1811        1056 :                                 if ((fr->dataSize>20) && !strncmp(fr->data, "IsoMedia File", 13)) {
    1812             :                                         cprt_box = a;
    1813             :                                         break;
    1814             :                                 }
    1815             :                         }
    1816             :                 default:
    1817          15 :                         if (moov_meta_pos<0) {
    1818           2 :                                 e = gf_isom_box_size(a);
    1819           2 :                                 if (e) goto exit;
    1820           2 :                                 e = gf_isom_box_write(a, bs);
    1821           2 :                                 if (e) goto exit;
    1822             :                         }
    1823             :                         break;
    1824             :                 }
    1825             :         }
    1826             : 
    1827        1056 :         e = DoInterleave(mw, writers, bs, 1, gf_bs_get_position(bs), drift_inter);
    1828        1056 :         if (e) goto exit;
    1829             : 
    1830        1056 :         firstSize = GetMoovAndMetaSize(movie, writers);
    1831        1056 :         if (movie->padding)
    1832           0 :                 firstSize += movie->padding;
    1833             : 
    1834             :         offset = firstSize;
    1835        1056 :         if (movie->mdat && movie->mdat->dataSize) offset += 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0);
    1836        1056 :         e = ShiftOffset(movie, writers, offset);
    1837        1056 :         if (e) goto exit;
    1838             : 
    1839             :         //get real sample offsets for meta items
    1840        1056 :         if (movie->meta) {
    1841          94 :                 store_meta_item_sample_ref_offsets(movie, writers, movie->meta);
    1842             :         }
    1843             : 
    1844             :         //get the size and see if it has changed (eg, we moved to 64 bit offsets)
    1845        1056 :         finalSize = GetMoovAndMetaSize(movie, writers);
    1846        1056 :         if (movie->padding)
    1847           0 :                 finalSize += movie->padding;
    1848             : 
    1849        1056 :         if (firstSize != finalSize) {
    1850             :                 finalOffset = finalSize;
    1851           2 :                 if (movie->mdat && movie->mdat->dataSize) finalOffset += 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0);
    1852             :                 //OK, now we're sure about the final size -> shift the offsets
    1853             :                 //we don't need to re-emulate, as the only thing that changed is the offset
    1854             :                 //so just shift the offset
    1855           2 :                 e = ShiftOffset(movie, writers, finalOffset - offset);
    1856           2 :                 if (e) goto exit;
    1857           2 :                 /*firstSize = */GetMoovAndMetaSize(movie, writers);
    1858             : 
    1859             :                 //readjust real sample offsets for meta items
    1860           2 :                 if (movie->meta) {
    1861           1 :                         store_meta_item_sample_ref_offsets(movie, writers, movie->meta);
    1862             :                 }
    1863             :         }
    1864             : 
    1865             :         //now write our stuff
    1866        1056 :         e = WriteMoovAndMeta(movie, writers, bs);
    1867        1056 :         if (e) goto exit;
    1868             : 
    1869        1056 :         if (movie->padding) {
    1870           0 :                 e = write_free_box(bs, movie->padding);
    1871           0 :                 if (e) goto exit;
    1872             :         }
    1873             : 
    1874             :         /*we have 8 extra bytes for large size (not computed in gf_isom_box_size) */
    1875        1056 :         if (movie->mdat && movie->mdat->dataSize) {
    1876        1045 :                 if (movie->mdat->dataSize > 0xFFFFFFFF) movie->mdat->dataSize += 8;
    1877        1045 :                 e = gf_isom_box_size((GF_Box *)movie->mdat);
    1878        1045 :                 if (e) goto exit;
    1879        1045 :                 e = gf_isom_box_write((GF_Box *)movie->mdat, bs);
    1880        1045 :                 if (e) goto exit;
    1881             :         }
    1882             : 
    1883             :         //we don't need the offset as we are writing...
    1884        1056 :         ResetWriters(writers);
    1885        1056 :         e = DoInterleave(mw, writers, bs, 0, 0, drift_inter);
    1886        1056 :         if (e) goto exit;
    1887             : 
    1888             :         //then the rest
    1889        1056 :         i=0;
    1890        6356 :         while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
    1891        4244 :                 if ((i-1 < (u32) moov_meta_pos) && (a != cprt_box))
    1892        1973 :                         continue;
    1893        2271 :                 switch (a->type) {
    1894             :                 case GF_ISOM_BOX_TYPE_MOOV:
    1895             :                 case GF_ISOM_BOX_TYPE_META:
    1896             :                 case GF_ISOM_BOX_TYPE_FTYP:
    1897             :                 case GF_ISOM_BOX_TYPE_OTYP:
    1898             :                 case GF_ISOM_BOX_TYPE_PDIN:
    1899             :                 case GF_ISOM_BOX_TYPE_MDAT:
    1900             :                         break;
    1901        1069 :                 default:
    1902        1069 :                         e = gf_isom_box_size(a);
    1903        1069 :                         if (e) goto exit;
    1904        1069 :                         e = gf_isom_box_write(a, bs);
    1905        1069 :                         if (e) goto exit;
    1906             :                 }
    1907             :         }
    1908             : 
    1909        1056 : exit:
    1910        1056 :         CleanWriters(writers);
    1911        1056 :         gf_list_del(writers);
    1912        1056 :         return e;
    1913             : }
    1914             : 
    1915             : 
    1916        2605 : void purge_free_boxes(GF_Box *par)
    1917             : {
    1918        2605 :         u32 i, count = gf_list_count(par->child_boxes);
    1919        5148 :         for (i=0; i<count; i++) {
    1920        2543 :                 GF_Box *child = gf_list_get(par->child_boxes, i);
    1921        2543 :                 if ((child->type==GF_ISOM_BOX_TYPE_FREE) || (child->type==GF_ISOM_BOX_TYPE_SKIP)) {
    1922           0 :                         gf_list_rem(par->child_boxes, i);
    1923           0 :                         i--;
    1924           0 :                         count--;
    1925           0 :                         gf_isom_box_del(child);
    1926           0 :                         continue;
    1927             :                 }
    1928             : 
    1929        2543 :                 if (child->type==GF_ISOM_BOX_TYPE_UDTA) {
    1930             :                         GF_UserDataBox *udta = (GF_UserDataBox *)child;
    1931          87 :                         u32 k, nb_maps = gf_list_count(udta->recordList);
    1932         160 :                         for (k=0; k<nb_maps; k++) {
    1933          73 :                                 GF_UserDataMap *map = gf_list_get(udta->recordList, k);
    1934          73 :                                 if ((map->boxType == GF_ISOM_BOX_TYPE_FREE) || (map->boxType == GF_ISOM_BOX_TYPE_SKIP)) {
    1935           0 :                                         gf_isom_box_array_del(map->boxes);
    1936           0 :                                         gf_free(map);
    1937           0 :                                         gf_list_rem(udta->recordList, k);
    1938           0 :                                         k--;
    1939           0 :                                         nb_maps--;
    1940             :                                 }
    1941             :                         }
    1942             :                 }
    1943        2543 :                 purge_free_boxes(child);
    1944             :         }
    1945        2605 : }
    1946             : 
    1947          21 : static GF_Err inplace_shift_moov_meta_offsets(GF_ISOFile *movie, u32 shift_offset)
    1948             : {
    1949             :         u32 i, count = 0;
    1950          21 :         if (movie->meta) {
    1951           2 :                 ShiftMetaOffset(movie->meta, shift_offset);
    1952             :         }
    1953             : 
    1954          21 :         if (movie->moov) {
    1955          21 :                 if (movie->moov->meta) 
    1956           0 :                         ShiftMetaOffset(movie->moov->meta, shift_offset);
    1957             : 
    1958          21 :                 count = gf_list_count(movie->moov->trackList);
    1959             :         }
    1960             :         //shift offsets
    1961          64 :         for (i=0; i<count; i++) {
    1962             :                 GF_Err e;
    1963             :                 GF_SampleTableBox *stbl;
    1964          43 :                 GF_Box *new_stco = NULL;
    1965          43 :                 GF_TrackBox *trak = gf_list_get(movie->moov->trackList, i);
    1966             : 
    1967          43 :                 if (trak->meta)
    1968           0 :                         ShiftMetaOffset(trak->meta, shift_offset);
    1969             : 
    1970          43 :                 stbl = trak->Media->information->sampleTable;
    1971          43 :                 e = shift_chunk_offsets(stbl->SampleToChunk, trak->Media, stbl->ChunkOffset, shift_offset, movie->force_co64, &new_stco);
    1972          43 :                 if (e) return e;
    1973             : 
    1974          43 :                 if (new_stco) {
    1975           0 :                         gf_list_del_item(stbl->child_boxes, stbl->ChunkOffset);
    1976           0 :                         gf_isom_box_del(stbl->ChunkOffset);
    1977           0 :                         stbl->ChunkOffset = new_stco;
    1978           0 :                         gf_list_add(stbl->child_boxes, new_stco);
    1979             :                 }
    1980             :         }
    1981             :         return GF_OK;
    1982             : }
    1983             : 
    1984          21 : static GF_Err inplace_shift_mdat(MovieWriter *mw, u64 *shift_offset, GF_BitStream *bs, Bool moov_first)
    1985             : {
    1986             :         GF_Err e;
    1987             :         u8 data[1024];
    1988          21 :         GF_ISOFile *movie = mw->movie;
    1989             :         u64 moov_size = 0;
    1990             :         u64 meta_size = 0;
    1991             :         u64 cur_r, cur_w, byte_offset, orig_offset;
    1992             : 
    1993          21 :         orig_offset = gf_bs_get_position(bs);
    1994             : 
    1995          21 :         if (moov_first) {
    1996          21 :                 if (movie->meta) {
    1997           2 :                         e = gf_isom_box_size((GF_Box *)movie->meta);
    1998           2 :                         if (e) return e;
    1999           2 :                         meta_size = movie->meta->size;
    2000             :                 }
    2001          21 :                 if (movie->moov) {
    2002          21 :                         e = gf_isom_box_size((GF_Box *)movie->moov);
    2003          21 :                         if (e) return e;
    2004          21 :                         moov_size = movie->moov->size;
    2005             :                 }
    2006             :         }
    2007             :         //shift offsets, potentially in 2 pass if we moov from 32bit offsets to 64 bit offsets
    2008          21 :         e = inplace_shift_moov_meta_offsets(movie, (u32) *shift_offset);
    2009          21 :         if (e) return e;
    2010             : 
    2011          21 :         if (moov_first) {
    2012             :                 u32 reshift = 0;
    2013          21 :                 if (movie->meta) {
    2014           2 :                         e = gf_isom_box_size((GF_Box *)movie->meta);
    2015           2 :                         if (e) return e;
    2016           2 :                         if (meta_size < movie->meta->size) {
    2017           0 :                                 reshift += (u32) (movie->meta->size - meta_size);
    2018             :                         }
    2019             :                 }
    2020          21 :                 if (movie->moov) {
    2021          21 :                         e = gf_isom_box_size((GF_Box *)movie->moov);
    2022          21 :                         if (e) return e;
    2023          21 :                         if (moov_size < movie->moov->size) {
    2024           0 :                                 reshift += (u32) (movie->moov->size - moov_size);
    2025             :                         }
    2026             :                 }
    2027             : 
    2028          21 :                 if (reshift) {
    2029           0 :                         e = inplace_shift_moov_meta_offsets(movie, reshift);
    2030           0 :                         if (e) return e;
    2031           0 :                         *shift_offset += reshift;
    2032             :                 }
    2033             :         }
    2034             : 
    2035             :         //move data
    2036             : 
    2037          21 :         gf_bs_seek(bs, gf_bs_get_size(bs));
    2038          21 :         cur_r = gf_bs_get_position(bs);
    2039          21 :         write_blank_data(bs, (u32) *shift_offset);
    2040          21 :         cur_w = gf_bs_get_position(bs);
    2041             : 
    2042          21 :         byte_offset = movie->first_data_toplevel_offset;
    2043             : 
    2044          21 :         mw->total_samples = cur_r - byte_offset;
    2045          21 :         mw->nb_done = 0;
    2046          21 :         muxer_report_progress(mw);
    2047             : 
    2048        1469 :         while (cur_r > byte_offset) {
    2049             :                 u32 nb_write;
    2050             :                 u32 move_bytes = 1024;
    2051        1427 :                 if (cur_r - byte_offset < move_bytes)
    2052          21 :                         move_bytes = (u32) (cur_r - byte_offset);
    2053             : 
    2054        1427 :                 gf_bs_seek(bs, cur_r - move_bytes);
    2055        1427 :                 nb_write = (u32) gf_bs_read_data(bs, data, move_bytes);
    2056        1427 :                 if (nb_write!=move_bytes) {
    2057           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[Isom] Read error, got %d bytes but had %d to read\n", nb_write, move_bytes));
    2058             :                         return GF_IO_ERR;
    2059             :                 }
    2060             : 
    2061        1427 :                 gf_bs_seek(bs, cur_w - move_bytes);
    2062        1427 :                 nb_write = (u32) gf_bs_write_data(bs, data, move_bytes);
    2063             : 
    2064        1427 :                 if (nb_write!=move_bytes) {
    2065           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[FileOut] Write error, wrote %d bytes but had %d to write\n", nb_write, move_bytes));
    2066             :                         return GF_IO_ERR;
    2067             :                 }
    2068             :                 cur_r -= move_bytes;
    2069             :                 cur_w -= move_bytes;
    2070             : 
    2071        1427 :                 mw->nb_done += move_bytes;
    2072        1427 :                 muxer_report_progress(mw);
    2073             :         }
    2074          21 :         gf_bs_seek(bs, orig_offset);
    2075             : 
    2076          21 :         return GF_OK;
    2077             : }
    2078             : 
    2079          56 : static GF_Err WriteInplace(MovieWriter *mw, GF_BitStream *bs)
    2080             : {
    2081             :         GF_Err e;
    2082             :         u32 i;
    2083             :         u64 size=0;
    2084             :         u64 offset, shift_offset;
    2085             :         Bool moov_first;
    2086             :         u32 mdat_offset = 0;
    2087             :         u64 moov_meta_offset;
    2088             :         u32 rewind_meta=0;
    2089          56 :         u64 shift_meta = 0;
    2090             :         s32 moov_meta_pos=-1;
    2091             :         s32 mdat_pos=-1;
    2092             :         GF_Box *a;
    2093          56 :         GF_ISOFile *movie = mw->movie;
    2094             : 
    2095             :         //get size of all boxes before moov/meta/mdat
    2096          56 :         if (movie->is_jp2) mdat_offset += 12;
    2097          56 :         if (movie->brand) {
    2098          56 :                 e = gf_isom_box_size((GF_Box *)movie->brand);
    2099          56 :                 if (e) return e;
    2100          56 :                 mdat_offset += (u32) movie->brand->size;
    2101             :         }
    2102          56 :         if (movie->otyp) {
    2103           0 :                 e = gf_isom_box_size((GF_Box *)movie->otyp);
    2104           0 :                 if (e) return e;
    2105           0 :                 mdat_offset += (u32) movie->otyp->size;
    2106             :         }
    2107          56 :         if (movie->pdin) {
    2108           0 :                 e = gf_isom_box_size((GF_Box *)movie->pdin);
    2109           0 :                 if (e) return e;
    2110           0 :                 mdat_offset += (u32) movie->pdin->size;
    2111             :         }
    2112             : 
    2113          56 :         i=0;
    2114         387 :         while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
    2115         275 :                 switch (a->type) {
    2116          62 :                 case GF_ISOM_BOX_TYPE_MOOV:
    2117             :                 case GF_ISOM_BOX_TYPE_META:
    2118          62 :                         moov_meta_pos = i-1;
    2119          62 :                         break;
    2120          56 :                 case GF_ISOM_BOX_TYPE_MDAT:
    2121          56 :                         mdat_pos = i-1;
    2122          56 :                         break;
    2123             :                 case GF_ISOM_BOX_TYPE_FTYP:
    2124             :                 case GF_ISOM_BOX_TYPE_OTYP:
    2125             :                 case GF_ISOM_BOX_TYPE_PDIN:
    2126             :                         break;
    2127         101 :                 default:
    2128         101 :                         if ((moov_meta_pos<0) && (mdat_pos<0) ) {
    2129           0 :                                 e = gf_isom_box_size(a);
    2130           0 :                                 if (e) return e;
    2131           0 :                                 mdat_offset += (u32) a->size;
    2132             :                         }
    2133             :                         break;
    2134             :                 }
    2135             :         }
    2136             : 
    2137          56 :         if (movie->meta) {
    2138           6 :                 purge_free_boxes((GF_Box*)movie->meta);
    2139           6 :                 store_meta_item_sample_ref_offsets(movie, NULL, movie->meta);
    2140             :         }
    2141          56 :         if (movie->moov)
    2142          56 :                 purge_free_boxes((GF_Box*)movie->moov);
    2143             : 
    2144          56 :         moov_meta_offset = movie->original_moov_offset;
    2145          56 :         if (movie->original_meta_offset && (moov_meta_offset > movie->original_meta_offset))
    2146             :                 moov_meta_offset = movie->original_meta_offset;
    2147             : 
    2148             :         //mdat was before moof, check if we need to shift it due to brand changes
    2149          56 :         if (moov_meta_offset > movie->first_data_toplevel_offset) {
    2150             :                 moov_first = GF_FALSE;
    2151           0 :                 if (mdat_offset < movie->first_data_toplevel_offset) {
    2152           0 :                         rewind_meta = (u32) movie->first_data_toplevel_offset - mdat_offset;
    2153           0 :                         if ((movie->first_data_toplevel_size<0xFFFFFFFFUL)
    2154           0 :                                 && (movie->first_data_toplevel_size+rewind_meta > 0xFFFFFFFFUL)
    2155           0 :                                 && (rewind_meta<8)
    2156             :                         ) {
    2157           0 :                                 shift_meta = 8 - rewind_meta;
    2158             :                         }
    2159             :                 } else {
    2160           0 :                         shift_meta = mdat_offset - (u32) movie->first_data_toplevel_offset;
    2161             :                 }
    2162             : 
    2163           0 :                 if (shift_meta) {
    2164           0 :                         e = inplace_shift_mdat(mw, &shift_meta, bs, GF_FALSE);
    2165           0 :                         if (e) return e;
    2166           0 :                         moov_meta_offset += shift_meta;
    2167             :                 }
    2168             :         } else {
    2169             :                 moov_first = GF_TRUE;
    2170             :         }
    2171             : 
    2172             :         //write everything before moov/meta/mdat
    2173          56 :         if (movie->is_jp2) {
    2174           0 :                 gf_bs_write_u32(bs, 12);
    2175           0 :                 gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_JP);
    2176           0 :                 gf_bs_write_u32(bs, 0x0D0A870A);
    2177             :         }
    2178          56 :         if (movie->brand) {
    2179          56 :                 e = gf_isom_box_write((GF_Box *)movie->brand, bs);
    2180          56 :                 if (e) return e;
    2181             :         }
    2182          56 :         if (movie->otyp) {
    2183           0 :                 e = gf_isom_box_write((GF_Box *)movie->otyp, bs);
    2184           0 :                 if (e) return e;
    2185             :         }
    2186          56 :         if (movie->pdin) {
    2187           0 :                 e = gf_isom_box_write((GF_Box *)movie->pdin, bs);
    2188           0 :                 if (e) return e;
    2189             :         }
    2190             : 
    2191             :         //write all boxes before moov
    2192          56 :         i=0;
    2193         387 :         while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
    2194         275 :                 switch (a->type) {
    2195             :                 case GF_ISOM_BOX_TYPE_MOOV:
    2196             :                 case GF_ISOM_BOX_TYPE_META:
    2197             :                 case GF_ISOM_BOX_TYPE_FTYP:
    2198             :                 case GF_ISOM_BOX_TYPE_OTYP:
    2199             :                 case GF_ISOM_BOX_TYPE_PDIN:
    2200             :                 case GF_ISOM_BOX_TYPE_MDAT:
    2201             :                         break;
    2202         101 :                 default:
    2203         101 :                         if (((s32) i < moov_meta_pos) && ((s32)i < mdat_pos)) {
    2204           0 :                                 e = gf_isom_box_write(a, bs);
    2205           0 :                                 if (e) return e;
    2206             :                         }
    2207             :                         break;
    2208             :                 }
    2209             :         }
    2210             : 
    2211          56 :         if (!moov_first) {
    2212           0 :                 if (rewind_meta) {
    2213             :                         //rewrite mdat header
    2214           0 :                         u64 mdat_size = movie->first_data_toplevel_size + rewind_meta;
    2215           0 :                         if (mdat_size>0xFFFFFFUL) gf_bs_write_u32(bs, 1);
    2216           0 :                         else gf_bs_write_u32(bs, (u32) mdat_size);
    2217           0 :                         gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_MDAT);
    2218           0 :                         if (mdat_size>0xFFFFFFUL) gf_bs_write_u64(bs, mdat_size);
    2219             :                 }
    2220             : 
    2221           0 :                 gf_bs_seek(bs, moov_meta_offset);
    2222           0 :                 if (movie->meta) {
    2223           0 :                         e = gf_isom_box_size((GF_Box *)movie->meta);
    2224           0 :                         if (e) return e;
    2225           0 :                         e = gf_isom_box_write((GF_Box *)movie->meta, bs);
    2226           0 :                         if (e) return e;
    2227             :                 }
    2228           0 :                 if (movie->moov) {
    2229           0 :                         e = gf_isom_box_size((GF_Box *)movie->moov);
    2230           0 :                         if (e) return e;
    2231           0 :                         e = gf_isom_box_write((GF_Box *)movie->moov, bs);
    2232           0 :                         if (e) return e;
    2233             :                 }
    2234             :                 //trash all boxes after moov/meta
    2235             :                 return GF_OK;
    2236             :         }
    2237             : 
    2238             :         //moov+meta before mdat, remember start pos and compute moov/meta size
    2239          56 :         offset = gf_bs_get_position(bs);
    2240          56 :         if (movie->meta) {
    2241           6 :                 e = gf_isom_box_size((GF_Box *)movie->meta);
    2242           6 :                 if (e) return e;
    2243           6 :                 size += movie->meta->size;
    2244           6 :                 if (movie->meta->size > 0xFFFFFFFF) size += 8;
    2245             :         }
    2246          56 :         if (movie->moov) {
    2247          56 :                 e = gf_isom_box_size((GF_Box *)movie->moov);
    2248          56 :                 if (e) return e;
    2249          56 :                 size += movie->moov->size;
    2250          56 :                 if (movie->moov->size > 0xFFFFFFFF) size += 8;
    2251             :         }
    2252             : 
    2253          56 :         shift_offset = 0;
    2254             :         //we need to shift mdat
    2255          56 :         if (offset + size > movie->first_data_toplevel_offset) {
    2256          14 :                 shift_offset = 8 + offset + size - movie->first_data_toplevel_offset;
    2257             :         } else {
    2258          42 :                 u64 pad = movie->first_data_toplevel_offset - offset - size;
    2259             :                 //less than 8 bytes available between end of moov/meta and original mdat, shift so that we can insert a free box
    2260          42 :                 if (pad < 8) {
    2261           7 :                         shift_offset = 8 - pad;
    2262             :                 }
    2263             :         }
    2264          56 :         if (movie->padding && (shift_offset < movie->padding))
    2265           0 :                 shift_offset = movie->padding;
    2266             : 
    2267             :         //move data
    2268          56 :         if (shift_offset) {
    2269          21 :                 e = inplace_shift_mdat(mw, &shift_offset, bs, GF_TRUE);
    2270          21 :                 if (e) return e;
    2271             :         }
    2272             :         //write meta and moov
    2273          56 :         if (movie->meta) {
    2274           6 :                 e = gf_isom_box_write((GF_Box *)movie->meta, bs);
    2275           6 :                 if (e) return e;
    2276             :         }
    2277          56 :         if (movie->moov) {
    2278          56 :                 e = gf_isom_box_write((GF_Box *)movie->moov, bs);
    2279          56 :                 if (e) return e;
    2280             :         }
    2281             :         //insert a free box in-between
    2282          56 :         size = movie->first_data_toplevel_offset + shift_offset - gf_bs_get_position(bs);
    2283          56 :         return write_free_box(bs, (u32) size);
    2284             : }
    2285             : 
    2286             : 
    2287             : extern u32 default_write_buffering_size;
    2288             : GF_Err gf_isom_flush_chunk(GF_TrackBox *trak, Bool is_final);
    2289             : 
    2290        1458 : GF_Err WriteToFile(GF_ISOFile *movie, Bool for_fragments)
    2291             : {
    2292             :         MovieWriter mw;
    2293             :         GF_Err e = GF_OK;
    2294        1458 :         if (!movie) return GF_BAD_PARAM;
    2295             : 
    2296        1458 :         if (movie->openMode == GF_ISOM_OPEN_READ) return GF_BAD_PARAM;
    2297             : 
    2298        1458 :         e = gf_isom_insert_copyright(movie);
    2299        1458 :         if (e) return e;
    2300             : 
    2301             :         memset(&mw, 0, sizeof(mw));
    2302        1458 :         mw.movie = movie;
    2303             : 
    2304        1458 :         if ((movie->compress_flags & GF_ISOM_COMP_WRAP_FTYPE) && !movie->otyp && movie->brand) {
    2305             :                 u32 i, found=0;
    2306           0 :                 for (i=0; i<movie->brand->altCount; i++) {
    2307           0 :                         if ((movie->brand->altBrand[i] == GF_ISOM_BRAND_COMP)
    2308           0 :                                 || (movie->brand->altBrand[i] == GF_ISOM_BRAND_ISOC)
    2309             :                         ) {
    2310             :                                 found = 1;
    2311             :                                 break;
    2312             :                         }
    2313             :                 }
    2314           0 :                 if (!found) {
    2315           0 :                         u32 brand = (movie->compress_mode==GF_ISOM_COMP_ALL) ? GF_ISOM_BRAND_COMP : GF_ISOM_BRAND_ISOC;
    2316           0 :                         u32 pos = gf_list_find(movie->TopBoxes, movie->brand);
    2317           0 :                         GF_Box *otyp = gf_isom_box_new(GF_ISOM_BOX_TYPE_OTYP);
    2318           0 :                         gf_list_rem(movie->TopBoxes, pos);
    2319           0 :                         gf_list_insert(movie->TopBoxes, otyp, pos);
    2320           0 :                         otyp->child_boxes = gf_list_new();
    2321           0 :                         gf_list_add(otyp->child_boxes, movie->brand);
    2322           0 :                         movie->otyp = otyp;
    2323           0 :                         movie->brand = (GF_FileTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_FTYP);
    2324           0 :                         gf_list_insert(movie->TopBoxes, movie->brand, pos);
    2325           0 :                         movie->brand->majorBrand = brand;
    2326           0 :                         movie->brand->altCount = 1;
    2327           0 :                         movie->brand->altBrand = gf_malloc(sizeof(u32));
    2328           0 :                         movie->brand->altBrand[0] = brand;
    2329             :                 }
    2330             :         }
    2331             : 
    2332             : 
    2333        1458 :         if (movie->moov) {
    2334             :                 u32 i;
    2335             :                 GF_TrackBox *trak;
    2336        1371 :                 if (gf_sys_is_test_mode()) {
    2337        1370 :                         movie->moov->mvhd->creationTime = 0;
    2338        1370 :                         movie->moov->mvhd->modificationTime = 0;
    2339             :                 }
    2340        1371 :                 i=0;
    2341        4673 :                 while ( (trak = gf_list_enum(movie->moov->trackList, &i))) {
    2342        1931 :                         if (gf_sys_is_test_mode()) {
    2343        1930 :                                 trak->Header->creationTime = 0;
    2344        1930 :                                 trak->Header->modificationTime = 0;
    2345        1930 :                                 if (trak->Media->handler && trak->Media->handler->nameUTF8 && strstr(trak->Media->handler->nameUTF8, "@GPAC")) {
    2346         491 :                                         gf_free(trak->Media->handler->nameUTF8);
    2347         491 :                                         trak->Media->handler->nameUTF8 = gf_strdup("MediaHandler");
    2348             :                                 }
    2349        1930 :                                 trak->Media->mediaHeader->creationTime = 0;
    2350        1930 :                                 trak->Media->mediaHeader->modificationTime = 0;
    2351             :                         }
    2352        1931 :                         if (trak->chunk_cache) {
    2353           4 :                                 gf_isom_flush_chunk(trak, GF_TRUE);
    2354             :                         }
    2355             :                 }
    2356             :         }
    2357             :         //capture mode: we don't need a new bitstream
    2358        1458 :         if (movie->openMode == GF_ISOM_OPEN_WRITE) {
    2359         342 :                 if (!strcmp(movie->fileName, "_gpac_isobmff_redirect")) {
    2360             :                         GF_BitStream *bs, *moov_bs=NULL;
    2361         340 :                         u64 mdat_end = gf_bs_get_position(movie->editFileMap->bs);
    2362         340 :                         u64 mdat_start = movie->mdat->bsOffset;
    2363         340 :                         u64 mdat_size = mdat_end - mdat_start;
    2364             : 
    2365         340 :                         if (for_fragments) {
    2366         339 :                                 if (!movie->on_block_out) {
    2367           0 :                                         GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISOBMFF] Missing output block callback, cannot write\n"));
    2368             :                                         return GF_BAD_PARAM;
    2369             :                                 }
    2370         339 :                                 bs = gf_bs_new_cbk(movie->on_block_out, movie->on_block_out_usr_data, movie->on_block_out_block_size);
    2371         339 :                                 e = WriteFlat(&mw, 0, bs, GF_TRUE, GF_TRUE, NULL);
    2372         339 :                                 movie->fragmented_file_pos = gf_bs_get_position(bs);
    2373         339 :                                 gf_bs_del(bs);
    2374         339 :                                 return e;
    2375             :                         }
    2376             :                         //seek at end in case we had a read of the file
    2377           1 :                         gf_bs_seek(movie->editFileMap->bs, gf_bs_get_size(movie->editFileMap->bs) );
    2378             : 
    2379           1 :                         if ((movie->storageMode==GF_ISOM_STORE_FASTSTART) && mdat_start && mdat_size) {
    2380           1 :                                 moov_bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
    2381             :                         }
    2382             :                         //write as non-seekable
    2383           1 :                         e = WriteFlat(&mw, 0, movie->editFileMap->bs, GF_TRUE, GF_FALSE, moov_bs);
    2384             : 
    2385           1 :                         movie->fragmented_file_pos = gf_bs_get_position(movie->editFileMap->bs);
    2386             : 
    2387           1 :                         if (mdat_start && mdat_size) {
    2388             :                                 u8 data[16];
    2389           1 :                                 if (!movie->on_block_out) {
    2390           0 :                                         GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISOBMFF] Missing output block patch callback, cannot patch mdat size in flat storage\n"));
    2391           0 :                                         return GF_BAD_PARAM;
    2392             :                                 }
    2393             : 
    2394             :                                 //create a patch packet for mdat covering out 16 bytes (cf FlushCapture)
    2395           1 :                                 bs = gf_bs_new(data, 16, GF_BITSTREAM_WRITE);
    2396           1 :                                 gf_bs_write_u32(bs, (mdat_size>0xFFFFFFFF) ? 1 : (u32) mdat_size);
    2397           1 :                                 gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_MDAT);
    2398           1 :                                 if  (mdat_size>0xFFFFFFFF)
    2399           0 :                                         gf_bs_write_u64(bs, mdat_size);
    2400             :                                 else
    2401           1 :                                         gf_bs_write_u64(bs, 0);
    2402           1 :                                 gf_bs_del(bs);
    2403           1 :                                 movie->on_block_patch(movie->on_block_out_usr_data, data, 16, mdat_start, GF_FALSE);
    2404             :                         }
    2405             : 
    2406           1 :                         if (moov_bs) {
    2407             :                                 u8 *moov_data;
    2408             :                                 u32 moov_size;
    2409             : 
    2410           1 :                                 gf_bs_get_content(moov_bs, &moov_data, &moov_size);
    2411           1 :                                 gf_bs_del(moov_bs);
    2412             : 
    2413           1 :                                 movie->on_block_patch(movie->on_block_out_usr_data, moov_data, moov_size, mdat_start, GF_TRUE);
    2414           1 :                                 gf_free(moov_data);
    2415             :                         }
    2416             :                 } else {
    2417             :                         GF_BitStream *moov_bs = NULL;
    2418           2 :                         if ((movie->storageMode==GF_ISOM_STORE_STREAMABLE) || (movie->storageMode==GF_ISOM_STORE_FASTSTART) ) {
    2419           2 :                                 moov_bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
    2420             :                         }
    2421           2 :                         e = WriteFlat(&mw, 0, movie->editFileMap->bs, GF_FALSE, GF_FALSE, moov_bs);
    2422           2 :                         if (moov_bs) {
    2423             :                                 u8 *moov_data;
    2424             :                                 u32 moov_size;
    2425             : 
    2426           2 :                                 gf_bs_get_content(moov_bs, &moov_data, &moov_size);
    2427           2 :                                 gf_bs_del(moov_bs);
    2428           2 :                                 if (!e)
    2429           2 :                                         e = gf_bs_insert_data(movie->editFileMap->bs, moov_data, moov_size, movie->mdat->bsOffset);
    2430             :                                         
    2431           2 :                                 gf_free(moov_data);
    2432             :                         }
    2433             :                 }
    2434             :         } else {
    2435             :                 FILE *stream=NULL;
    2436             :                 Bool is_stdout = GF_FALSE;
    2437             :                 GF_BitStream *bs=NULL;
    2438             : 
    2439        1116 :                 if (!strcmp(movie->finalName, "_gpac_isobmff_redirect")) {
    2440         381 :                         if (!movie->on_block_out) {
    2441           0 :                                 GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISOBMFF] Missing output block callback, cannot write\n"));
    2442             :                                 return GF_BAD_PARAM;
    2443             :                         }
    2444         381 :                         bs = gf_bs_new_cbk(movie->on_block_out, movie->on_block_out_usr_data, movie->on_block_out_block_size);
    2445             :                         is_stdout = GF_TRUE;
    2446         735 :                 } else if (gf_isom_is_inplace_rewrite(movie)) {
    2447          56 :                         stream = gf_fopen(movie->fileName, "r+b");
    2448          56 :                         if (!stream)
    2449             :                                 return GF_IO_ERR;
    2450          56 :                         bs = gf_bs_from_file(stream, GF_BITSTREAM_WRITE);
    2451          56 :                         gf_bs_seek(bs, 0);
    2452             :                 } else {
    2453         679 :                         if (!strcmp(movie->finalName, "std"))
    2454             :                                 is_stdout = GF_TRUE;
    2455             : 
    2456             :                         //OK, we need a new bitstream
    2457         679 :                         stream = is_stdout ? stdout : gf_fopen(movie->finalName, "w+b");
    2458         679 :                         if (!stream)
    2459             :                                 return GF_IO_ERR;
    2460         679 :                         bs = gf_bs_from_file(stream, GF_BITSTREAM_WRITE);
    2461             :                 }
    2462        1116 :                 if (!bs) {
    2463           0 :                         if (!is_stdout)
    2464           0 :                                 gf_fclose(stream);
    2465             :                         return GF_OUT_OF_MEM;
    2466             :                 }
    2467             : 
    2468        1116 :                 if (movie->no_inplace_rewrite) {
    2469        1060 :                         switch (movie->storageMode) {
    2470           1 :                         case GF_ISOM_STORE_TIGHT:
    2471             :                         case GF_ISOM_STORE_INTERLEAVED:
    2472           1 :                                 e = WriteInterleaved(&mw, bs, 0);
    2473           1 :                                 break;
    2474        1055 :                         case GF_ISOM_STORE_DRIFT_INTERLEAVED:
    2475        1055 :                                 e = WriteInterleaved(&mw, bs, 1);
    2476        1055 :                                 break;
    2477           1 :                         case GF_ISOM_STORE_STREAMABLE:
    2478           1 :                                 e = WriteFlat(&mw, 1, bs, is_stdout, GF_FALSE, NULL);
    2479           1 :                                 break;
    2480           3 :                         default:
    2481           3 :                                 e = WriteFlat(&mw, 0, bs, is_stdout, GF_FALSE, NULL);
    2482           3 :                                 break;
    2483             :                         }
    2484             :                 } else {
    2485          56 :                         e = WriteInplace(&mw, bs);
    2486             :                 }
    2487             : 
    2488        1116 :                 gf_bs_del(bs);
    2489        1116 :                 if (!is_stdout)
    2490         735 :                         gf_fclose(stream);
    2491             :         }
    2492        1119 :         if (mw.buffer) gf_free(mw.buffer);
    2493        1119 :         if (mw.nb_done<mw.total_samples) {
    2494           3 :                 mw.nb_done = mw.total_samples;
    2495           3 :                 muxer_report_progress(&mw);
    2496             :         }
    2497             :         return e;
    2498             : }
    2499             : 
    2500             : 
    2501             : 
    2502             : #endif  /*!defined(GPAC_DISABLE_ISOM) && !defined(GPAC_DISABLE_ISOM_WRITE)*/

Generated by: LCOV version 1.13