1 
2 /*
3  *			GPAC - Multimedia Framework C SDK
4  *
5  *			Authors: Jean Le Feuvre
6  *			Copyright (c) Telecom ParisTech 2000-2020
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 
gf_isom_insert_copyright(GF_ISOFile * movie)36 static GF_Err gf_isom_insert_copyright(GF_ISOFile *movie)
37 {
38 	u32 i;
39 	GF_Box *a;
40 	GF_FreeSpaceBox *_free;
41 	i=0;
42 	while ((a = (GF_Box *)gf_list_enum(movie->TopBoxes, &i))) {
43 		if (a->type == GF_ISOM_BOX_TYPE_FREE) {
44 			_free = (GF_FreeSpaceBox *)a;
45 			if (_free->dataSize) {
46 				if (!strcmp(_free->data, GPAC_ISOM_CPRT_NOTICE_VERSION)) return GF_OK;
47 				if (strstr(_free->data, GPAC_ISOM_CPRT_NOTICE)) {
48 					gf_free(_free->data);
49 					_free->data = gf_strdup(gf_sys_is_test_mode() ? GPAC_ISOM_CPRT_NOTICE : GPAC_ISOM_CPRT_NOTICE_VERSION);
50 					_free->dataSize = 1 + (u32) strlen(_free->data);
51 					return GF_OK;
52 				}
53 			}
54 		}
55 	}
56 	a = gf_isom_box_new(GF_ISOM_BOX_TYPE_FREE);
57 	if (!a) return GF_OUT_OF_MEM;
58 	_free = (GF_FreeSpaceBox *)a;
59 	_free->data = gf_strdup(gf_sys_is_test_mode() ? GPAC_ISOM_CPRT_NOTICE : GPAC_ISOM_CPRT_NOTICE_VERSION);
60 	_free->dataSize = (u32) strlen(_free->data) + 1;
61 	if (!_free->data) return GF_OUT_OF_MEM;
62 	return gf_list_add(movie->TopBoxes, _free);
63 }
64 
65 typedef struct
66 {
67 	/*the curent sample of this track*/
68 	u32 sampleNumber;
69 	/*timeScale of the media (for interleaving)*/
70 	u32 timeScale;
71 	/*this is for generic, time-based interleaving. Expressed in Media TimeScale*/
72 	u64 chunkDur;
73 	u32 chunkSize;
74 	u32 constant_size, constant_dur;
75 
76 	u64 DTSprev;
77 	u8 isDone;
78 	u64 prev_offset;
79 	GF_MediaBox *mdia;
80 	GF_SampleTableBox *stbl;
81 
82 	u32 all_dref_mode;
83 
84 	/*each writer has a sampleToChunck and ChunkOffset tables
85 	these tables are filled during emulation mode and then will	replace the table in the GF_SampleTableBox*/
86 	GF_SampleToChunkBox *stsc;
87 	/*we don't know if it's a large offset or not*/
88 	GF_Box *stco;
89 	//track uses a box requiring seeking into the moov during write, we cannot dispatch blocks
90 	Bool prevent_dispatch;
91 } TrackWriter;
92 
93 typedef struct
94 {
95 	char *buffer;
96 	u32 alloc_size;
97 	GF_ISOFile *movie;
98 	u32 total_samples, nb_done;
99 } MovieWriter;
100 
CleanWriters(GF_List * writers)101 void CleanWriters(GF_List *writers)
102 {
103 	while (gf_list_count(writers)) {
104 		TrackWriter *writer = (TrackWriter*)gf_list_get(writers, 0);
105 		gf_isom_box_del(writer->stco);
106 		gf_isom_box_del((GF_Box *)writer->stsc);
107 		gf_free(writer);
108 		gf_list_rem(writers, 0);
109 	}
110 }
111 
ResetWriters(GF_List * writers)112 GF_Err ResetWriters(GF_List *writers)
113 {
114 	u32 i;
115 	TrackWriter *writer;
116 	i=0;
117 	while ((writer = (TrackWriter *)gf_list_enum(writers, &i))) {
118 		writer->isDone = 0;
119 		writer->chunkDur = 0;
120 		writer->chunkSize = 0;
121 		writer->DTSprev = 0;
122 		writer->sampleNumber = 1;
123 		gf_isom_box_del((GF_Box *)writer->stsc);
124 		writer->stsc = (GF_SampleToChunkBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSC);
125 		if (!writer->stsc) return GF_OUT_OF_MEM;
126 		if (writer->stco->type == GF_ISOM_BOX_TYPE_STCO) {
127 			gf_free(((GF_ChunkOffsetBox *)writer->stco)->offsets);
128 			((GF_ChunkOffsetBox *)writer->stco)->offsets = NULL;
129 			((GF_ChunkOffsetBox *)writer->stco)->nb_entries = 0;
130 			((GF_ChunkOffsetBox *)writer->stco)->alloc_size = 0;
131 		} else {
132 			gf_free(((GF_ChunkLargeOffsetBox *)writer->stco)->offsets);
133 			((GF_ChunkLargeOffsetBox *)writer->stco)->offsets = NULL;
134 			((GF_ChunkLargeOffsetBox *)writer->stco)->nb_entries = 0;
135 			((GF_ChunkLargeOffsetBox *)writer->stco)->alloc_size = 0;
136 		}
137 	}
138 	return GF_OK;
139 }
140 
SetupWriters(MovieWriter * mw,GF_List * writers,u8 interleaving)141 GF_Err SetupWriters(MovieWriter *mw, GF_List *writers, u8 interleaving)
142 {
143 	u32 i, trackCount;
144 	TrackWriter *writer;
145 	GF_TrackBox *trak;
146 	GF_ISOFile *movie = mw->movie;
147 
148 	mw->total_samples = mw->nb_done = 0;
149 	if (!movie->moov) return GF_OK;
150 
151 	trackCount = gf_list_count(movie->moov->trackList);
152 	for (i = 0; i < trackCount; i++) {
153 		trak = gf_isom_get_track(movie->moov, i+1);
154 
155 		GF_SAFEALLOC(writer, TrackWriter);
156 		if (!writer) goto exit;
157 		writer->sampleNumber = 1;
158 		writer->mdia = trak->Media;
159 		writer->stbl = trak->Media->information->sampleTable;
160 		writer->timeScale = trak->Media->mediaHeader->timeScale;
161 		writer->all_dref_mode = Media_SelfContainedType(writer->mdia);
162 
163 		if (trak->sample_encryption)
164 			writer->prevent_dispatch = GF_TRUE;
165 
166 		writer->isDone = 0;
167 		writer->DTSprev = 0;
168 		writer->chunkDur = 0;
169 		writer->chunkSize = 0;
170 		writer->constant_size = writer->constant_dur = 0;
171 		if (writer->stbl->SampleSize->sampleSize)
172 			writer->constant_size = writer->stbl->SampleSize->sampleSize;
173 		if (writer->stbl->TimeToSample->nb_entries==1) {
174 			writer->constant_dur = writer->stbl->TimeToSample->entries[0].sampleDelta;
175 			if (writer->constant_dur>1) writer->constant_dur = 0;
176 		}
177 		if (!writer->constant_dur || !writer->constant_size || (writer->constant_size>=10))
178 			writer->constant_size = writer->constant_dur = 0;
179 
180 		writer->stsc = (GF_SampleToChunkBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSC);
181 		if (!writer->stsc) return GF_OUT_OF_MEM;
182 		if (writer->stbl->ChunkOffset->type == GF_ISOM_BOX_TYPE_STCO) {
183 			writer->stco = gf_isom_box_new(GF_ISOM_BOX_TYPE_STCO);
184 		} else {
185 			writer->stco = gf_isom_box_new(GF_ISOM_BOX_TYPE_CO64);
186 		}
187 		if (!writer->stco) return GF_OUT_OF_MEM;
188 		/*stops from chunk escape*/
189 		if (interleaving) writer->stbl->MaxSamplePerChunk = 0;
190 		/*for progress, assume only one descIndex*/
191 		if (Media_IsSelfContained(writer->mdia, 1))
192 			mw->total_samples += writer->stbl->SampleSize->sampleCount;
193 		/*optimization for interleaving: put audio last (this can be overriden by priorities)*/
194 		if (movie->storageMode != GF_ISOM_STORE_INTERLEAVED) {
195 			gf_list_add(writers, writer);
196 		} else {
197 			if (writer->mdia->information->InfoHeader && writer->mdia->information->InfoHeader->type == GF_ISOM_BOX_TYPE_SMHD) {
198 				gf_list_add(writers, writer);
199 			} else {
200 				gf_list_insert(writers, writer, 0);
201 			}
202 		}
203 		if (movie->sample_groups_in_traf && trak->Media->information->sampleTable)
204 			trak->Media->information->sampleTable->skip_sample_groups = GF_TRUE;
205 	}
206 	return GF_OK;
207 
208 exit:
209 	CleanWriters(writers);
210 	return GF_OUT_OF_MEM;
211 }
212 
213 
ShiftMetaOffset(GF_MetaBox * meta,u64 offset)214 static void ShiftMetaOffset(GF_MetaBox *meta, u64 offset)
215 {
216 	u32 i, count;
217 	if (!meta->item_locations) return;
218 
219 	count = gf_list_count(meta->item_locations->location_entries);
220 	for (i=0; i<count; i++) {
221 		GF_ItemLocationEntry *iloc = (GF_ItemLocationEntry *)gf_list_get(meta->item_locations->location_entries, i);
222 		if (iloc->data_reference_index) continue;
223 		if (iloc->construction_method == 2) continue;
224 		if (!iloc->base_offset) {
225 			GF_ItemExtentEntry *entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0);
226 			if (entry && !entry->extent_length && !entry->original_extent_offset && (gf_list_count(iloc->extent_entries)==1) )
227 				continue;
228 		}
229 
230 		iloc->base_offset += offset;
231 	}
232 }
233 
ShiftOffset(GF_ISOFile * file,GF_List * writers,u64 offset)234 static GF_Err ShiftOffset(GF_ISOFile *file, GF_List *writers, u64 offset)
235 {
236 	u32 i, j, k, l, last;
237 	TrackWriter *writer;
238 	GF_StscEntry *ent;
239 
240 	if (file->meta) ShiftMetaOffset(file->meta, offset);
241 	if (file->moov && file->moov->meta) ShiftMetaOffset(file->moov->meta, offset);
242 
243 	i=0;
244 	while ((writer = (TrackWriter *)gf_list_enum(writers, &i))) {
245 		if (writer->mdia->mediaTrack->meta) ShiftMetaOffset(writer->mdia->mediaTrack->meta, offset);
246 
247 		//we have to proceed entry by entry in case a part of the media is not self-contained...
248 		for (j=0; j<writer->stsc->nb_entries; j++) {
249 			ent = &writer->stsc->entries[j];
250 			if ((writer->all_dref_mode==ISOM_DREF_EXT) || !Media_IsSelfContained(writer->mdia, ent->sampleDescriptionIndex))
251 				continue;
252 
253 			//OK, get the chunk(s) number(s) and "shift" its (their) offset(s).
254 			if (writer->stco->type == GF_ISOM_BOX_TYPE_STCO) {
255 				GF_ChunkLargeOffsetBox *new_stco64 = NULL;
256 				GF_ChunkOffsetBox *stco = (GF_ChunkOffsetBox *) writer->stco;
257 
258 				//be carefull for the last entry, nextChunk is set to 0 in edit mode...
259 				last = ent->nextChunk ? ent->nextChunk : stco->nb_entries + 1;
260 				for (k = ent->firstChunk; k < last; k++) {
261 
262 					//we need to rewrite the table: only allocate co64 if not done previously and convert all offsets
263 					//to co64. Then (whether co64 was created or not) adjust the offset
264 					//Do not reassign table until we are done with the current sampleToChunk processing
265 					//since we have a test on stco->offsets[k-1], we need to keep stco untouched
266 					if (new_stco64 || file->force_co64 || (stco->offsets[k-1] + offset > 0xFFFFFFFF)) {
267 						if (!new_stco64) {
268 							new_stco64 = (GF_ChunkLargeOffsetBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_CO64);
269 							if (!new_stco64) return GF_OUT_OF_MEM;
270 							new_stco64->nb_entries = stco->nb_entries;
271 							new_stco64->offsets = (u64 *) gf_malloc(new_stco64->nb_entries * sizeof(u64));
272 							if (!new_stco64->offsets) return GF_OUT_OF_MEM;
273 							//copy over the stco table
274 							for (l = 0; l < new_stco64->nb_entries; l++) {
275 								new_stco64->offsets[l] = (u64) stco->offsets[l];
276 							}
277 						}
278 						new_stco64->offsets[k-1] += offset;
279 					} else {
280 						stco->offsets[k-1] += (u32) offset;
281 					}
282 				}
283 				if (new_stco64) {
284 					//done with this sampleToChunk entry, replace the box if we moved to co64
285 					gf_isom_box_del(writer->stco);
286 					writer->stco = (GF_Box *)new_stco64;
287 					new_stco64 = NULL;
288 				}
289 			} else {
290 				GF_ChunkLargeOffsetBox *stco64 = (GF_ChunkLargeOffsetBox *) writer->stco;
291 				//be carefull for the last entry ...
292 				last = ent->nextChunk ? ent->nextChunk : stco64->nb_entries + 1;
293 				for (k = ent->firstChunk; k < last; k++) {
294 					stco64->offsets[k-1] += offset;
295 				}
296 			}
297 		}
298 	}
299 
300 	return GF_OK;
301 
302 }
303 
304 #define COMP_BOX_COST_BYTES		8
305 
gf_isom_write_compressed_box(GF_ISOFile * mov,GF_Box * root_box,u32 repl_type,GF_BitStream * bs,u32 * box_csize)306 GF_Err gf_isom_write_compressed_box(GF_ISOFile *mov, GF_Box *root_box, u32 repl_type, GF_BitStream *bs, u32 *box_csize)
307 {
308 #ifdef GPAC_DISABLE_ZLIB
309 	return GF_NOT_SUPPORTED;
310 #else
311 	GF_Err e;
312 	GF_BitStream *comp_bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
313 	e = gf_isom_box_write(root_box, comp_bs);
314 
315 	if (!e) {
316 		u8 *box_data;
317 		u32 box_size, comp_size;
318 
319 		if (box_csize)
320 			*box_csize = (u32) root_box->size;
321 
322 		gf_bs_get_content(comp_bs, &box_data, &box_size);
323 		gf_gz_compress_payload_ex(&box_data, box_size, &comp_size, 8, GF_TRUE);
324 		if (mov->force_compress || (comp_size + COMP_BOX_COST_BYTES < box_size)) {
325 			if (bs) {
326 				gf_bs_write_u32(bs, comp_size+8);
327 				gf_bs_write_u32(bs, repl_type);
328 				gf_bs_write_data(bs, box_data, comp_size);
329 			}
330 			if (box_csize)
331 				*box_csize = comp_size + COMP_BOX_COST_BYTES;
332 		} else if (bs) {
333 			gf_bs_write_data(bs, box_data, box_size);
334 		}
335 		gf_free(box_data);
336 	}
337 	gf_bs_del(comp_bs);
338 	return e;
339 #endif /*GPAC_DISABLE_ZLIB*/
340 }
341 
342 //replace the chunk and offset tables...
WriteMoovAndMeta(GF_ISOFile * movie,GF_List * writers,GF_BitStream * bs)343 static GF_Err WriteMoovAndMeta(GF_ISOFile *movie, GF_List *writers, GF_BitStream *bs)
344 {
345 	u32 i;
346 	TrackWriter *writer;
347 	GF_Err e;
348 	GF_Box *stco;
349 	GF_SampleToChunkBox *stsc;
350 
351 	if (movie->meta) {
352 		//write the moov box...
353 		e = gf_isom_box_size((GF_Box *)movie->meta);
354 		if (e) return e;
355 		e = gf_isom_box_write((GF_Box *)movie->meta, bs);
356 		if (e) return e;
357 	}
358 
359 	if (movie->moov) {
360 		Bool prevent_dispatch = GF_FALSE;
361 		//switch all our tables
362 		i=0;
363 		while ((writer = (TrackWriter*)gf_list_enum(writers, &i))) {
364 			//don't delete them !!!
365 			stsc = writer->stbl->SampleToChunk;
366 			stco = writer->stbl->ChunkOffset;
367 			s32 stsc_pos = gf_list_del_item(writer->stbl->child_boxes, stsc);
368 			s32 stco_pos = gf_list_del_item(writer->stbl->child_boxes, stco);
369 			writer->stbl->SampleToChunk = writer->stsc;
370 			writer->stbl->ChunkOffset = writer->stco;
371 			gf_list_insert(writer->stbl->child_boxes, writer->stsc, stsc_pos);
372 			gf_list_insert(writer->stbl->child_boxes, writer->stco, stco_pos);
373 			writer->stco = stco;
374 			writer->stsc = stsc;
375 			if (writer->prevent_dispatch)
376 				prevent_dispatch = GF_TRUE;
377 		}
378 		if (prevent_dispatch) {
379 			gf_bs_prevent_dispatch(bs, GF_TRUE);
380 		}
381 		//write the moov box...
382 		e = gf_isom_box_size((GF_Box *)movie->moov);
383 		if (e) return e;
384 
385 		if ((movie->compress_mode==GF_ISO_COMP_ALL) || (movie->compress_mode==GF_ISO_COMP_MOOV)) {
386 			e = gf_isom_write_compressed_box(movie, (GF_Box *) movie->moov, GF_4CC('!', 'm', 'o', 'v'), bs, NULL);
387 		} else {
388 			e = gf_isom_box_write((GF_Box *)movie->moov, bs);
389 		}
390 
391 		if (prevent_dispatch) {
392 			gf_bs_prevent_dispatch(bs, GF_FALSE);
393 		}
394 
395 		//and re-switch our table. We have to do it that way because it is
396 		//needed when the moov is written first
397 		i=0;
398 		while ((writer = (TrackWriter*)gf_list_enum(writers, &i))) {
399 			//don't delete them !!!
400 			stsc = writer->stsc;
401 			stco = writer->stco;
402 			writer->stsc = writer->stbl->SampleToChunk;
403 			writer->stco = writer->stbl->ChunkOffset;
404 			s32 stsc_pos = gf_list_del_item(writer->stbl->child_boxes, writer->stsc);
405 			s32 stco_pos = gf_list_del_item(writer->stbl->child_boxes, writer->stco);
406 
407 			writer->stbl->SampleToChunk = stsc;
408 			writer->stbl->ChunkOffset = stco;
409 			gf_list_insert(writer->stbl->child_boxes, stsc, stsc_pos);
410 			gf_list_insert(writer->stbl->child_boxes, stco, stco_pos);
411 		}
412 		if (e) return e;
413 	}
414 	return GF_OK;
415 }
416 
417 //compute the size of the moov as it will be written.
GetMoovAndMetaSize(GF_ISOFile * movie,GF_List * writers)418 u64 GetMoovAndMetaSize(GF_ISOFile *movie, GF_List *writers)
419 {
420 	u32 i;
421 	u64 size;
422 
423 	size = 0;
424 	if (movie->moov) {
425 		TrackWriter *writer;
426 		gf_isom_box_size((GF_Box *)movie->moov);
427 		size = movie->moov->size;
428 		if (size > 0xFFFFFFFF) size += 8;
429 
430 		i=0;
431 		while ((writer = (TrackWriter*)gf_list_enum(writers, &i))) {
432 			size -= writer->stbl->ChunkOffset->size;
433 			size -= writer->stbl->SampleToChunk->size;
434 			gf_isom_box_size((GF_Box *)writer->stsc);
435 			gf_isom_box_size(writer->stco);
436 			size += writer->stsc->size;
437 			size += writer->stco->size;
438 		}
439 	}
440 	if (movie->meta) {
441 		u64 msize;
442 		gf_isom_box_size((GF_Box *)movie->meta);
443 		msize = movie->meta->size;
444 		if (msize > 0xFFFFFFFF) msize += 8;
445 		size += msize;
446 	}
447 	return size;
448 }
449 
muxer_report_progress(MovieWriter * mw)450 static void muxer_report_progress(MovieWriter *mw)
451 {
452 	if (mw->movie->progress_cbk) {
453 		mw->movie->progress_cbk(mw->movie->progress_cbk_udta, mw->nb_done, mw->total_samples);
454 	} else {
455 		gf_set_progress("ISO File Writing", mw->nb_done, mw->total_samples);
456 	}
457 }
458 
459 //Write a sample to the file - this is only called for self-contained media
WriteSample(MovieWriter * mw,u32 size,u64 offset,u8 isEdited,GF_BitStream * bs,u32 nb_samp)460 GF_Err WriteSample(MovieWriter *mw, u32 size, u64 offset, u8 isEdited, GF_BitStream *bs, u32 nb_samp)
461 {
462 	GF_DataMap *map;
463 	u32 bytes;
464 
465 	if (!size) return GF_OK;
466 
467 	if (size>mw->alloc_size) {
468 		mw->buffer = (char*)gf_realloc(mw->buffer, size);
469 		mw->alloc_size = size;
470 	}
471 
472 	if (!mw->buffer) return GF_OUT_OF_MEM;
473 
474 	if (isEdited) {
475 		map = mw->movie->editFileMap;
476 	} else {
477 		map = mw->movie->movieFileMap;
478 	}
479 	//get the payload...
480 	bytes = gf_isom_datamap_get_data(map, mw->buffer, size, offset);
481 	if (bytes != size)
482 		return GF_IO_ERR;
483 	//write it to our stream...
484 	bytes = gf_bs_write_data(bs, mw->buffer, size);
485 	if (bytes != size)
486 		return GF_IO_ERR;
487 
488 	mw->nb_done+=nb_samp;
489 	muxer_report_progress(mw);
490 	return GF_OK;
491 }
492 
493 //flush as much as possible from current chunk for constand size and duration (typically raw audio)
494 // We don't want to write samples outside of the current source chunk
495 //since the next chunk might be edited (different bitstream object), which would complexify WriteSample code
496 //not flushing the chunk will work, but result in very slow writing of raw audio
update_writer_constant_dur(GF_ISOFile * movie,TrackWriter * tkw,GF_StscEntry * stsc_ent,u32 * nb_samp,u32 * samp_size,Bool is_flat)497 void update_writer_constant_dur(GF_ISOFile *movie, TrackWriter *tkw, GF_StscEntry *stsc_ent, u32 *nb_samp, u32 *samp_size, Bool is_flat)
498 {
499 	u64 chunk_dur;
500 	u32 nb_in_run;
501 	u32 samp_idx_in_chunk, nb_samp_left_in_src_chunk;
502 	if (!tkw->constant_dur) return;
503 
504 	samp_idx_in_chunk = tkw->sampleNumber - tkw->stbl->SampleToChunk->firstSampleInCurrentChunk;
505 	nb_samp_left_in_src_chunk = stsc_ent->samplesPerChunk - samp_idx_in_chunk;
506 
507 	if (nb_samp_left_in_src_chunk<=1) return;
508 
509 	if (is_flat) {
510 		nb_in_run = nb_samp_left_in_src_chunk;
511 	} else {
512 
513 		chunk_dur = movie->interleavingTime * tkw->timeScale;
514 		if (movie->moov && movie->moov->mvhd && movie->moov->mvhd->timeScale)
515 			chunk_dur /= movie->moov->mvhd->timeScale;
516 
517 		chunk_dur -= tkw->chunkDur;
518 
519 		if (chunk_dur <= tkw->chunkDur) return;
520 		chunk_dur -= tkw->constant_dur;
521 
522 		nb_in_run = (u32) (chunk_dur / tkw->constant_dur);
523 
524 		if (nb_in_run > nb_samp_left_in_src_chunk) {
525 			nb_in_run = nb_samp_left_in_src_chunk;
526 		}
527 	}
528 	if (tkw->sampleNumber + nb_in_run >= tkw->stbl->SampleSize->sampleCount) {
529 		nb_in_run = tkw->stbl->SampleSize->sampleCount - tkw->sampleNumber;
530 	}
531 
532 	chunk_dur = nb_in_run * tkw->constant_dur;
533 
534 	tkw->chunkDur += (u32) chunk_dur - tkw->constant_dur; //because tkw->chunkDur already include duration of first sample of chunk
535 	tkw->DTSprev += chunk_dur - tkw->constant_dur; //because nb_samp += nb_in_run-1
536 
537 	*nb_samp = nb_in_run;
538 	*samp_size = nb_in_run * tkw->constant_size;
539 }
540 
DoWriteMeta(GF_ISOFile * file,GF_MetaBox * meta,GF_BitStream * bs,Bool Emulation,u64 baseOffset,u64 * mdatSize)541 GF_Err DoWriteMeta(GF_ISOFile *file, GF_MetaBox *meta, GF_BitStream *bs, Bool Emulation, u64 baseOffset, u64 *mdatSize)
542 {
543 	GF_ItemExtentEntry *entry;
544 	u64 maxExtendOffset, maxExtendSize;
545 	u32 i, j, count;
546 
547 	maxExtendOffset = 0;
548 	maxExtendSize = 0;
549 	*mdatSize = 0;
550 	if (!meta->item_locations) return GF_OK;
551 
552 	count = gf_list_count(meta->item_locations->location_entries);
553 	for (i=0; i<count; i++) {
554 		u64 it_size;
555 		GF_ItemLocationEntry *iloc = (GF_ItemLocationEntry *)gf_list_get(meta->item_locations->location_entries, i);
556 		/*get item info*/
557 		GF_ItemInfoEntryBox *iinf = NULL;
558 		j=0;
559 		while ((iinf = (GF_ItemInfoEntryBox *)gf_list_enum(meta->item_infos->item_infos, &j))) {
560 			if (iinf->item_ID==iloc->item_ID) break;
561 			iinf = NULL;
562 		}
563 
564 		if (!iloc->base_offset && (gf_list_count(iloc->extent_entries)==1)) {
565 			entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0);
566 			if (!entry->extent_length && !entry->original_extent_offset && !entry->extent_index) {
567 				entry->extent_offset = 0;
568 				continue;
569 			}
570 		}
571 
572 		it_size = 0;
573 		/*for self contained only*/
574 		if (!iloc->data_reference_index) {
575 			if (iloc->construction_method != 2) {
576 				iloc->base_offset = baseOffset;
577 			}
578 
579 			/*new resource*/
580 			if (iinf && iinf->full_path) {
581 				FILE *src=NULL;
582 
583 				if (!iinf->data_len) {
584 					src = gf_fopen(iinf->full_path, "rb");
585 					if (!src) continue;
586 					it_size = gf_fsize(src);
587 				} else {
588 					it_size = iinf->data_len;
589 				}
590 				if (maxExtendSize<it_size) maxExtendSize = it_size;
591 
592 				if (!gf_list_count(iloc->extent_entries)) {
593 					GF_SAFEALLOC(entry, GF_ItemExtentEntry);
594 					if (!entry) return GF_OUT_OF_MEM;
595 					gf_list_add(iloc->extent_entries, entry);
596 				}
597 				entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0);
598 				entry->extent_offset = 0;
599 				entry->extent_length = it_size;
600 
601 				/*OK write to mdat*/
602 				if (!Emulation) {
603 					if (src) {
604 						char cache_data[4096];
605 						u64 remain = entry->extent_length;
606 						while (remain) {
607 							u32 size_cache = (remain>4096) ? 4096 : (u32) remain;
608 							size_t read = gf_fread(cache_data, size_cache, src);
609 							if (read ==(size_t) -1) break;
610 							gf_bs_write_data(bs, cache_data, (u32) read);
611 							remain -= (u32) read;
612 						}
613 					} else {
614 						gf_bs_write_data(bs, iinf->full_path, iinf->data_len);
615 					}
616 				}
617 				if (src) gf_fclose(src);
618 			}
619 			else if (gf_list_count(iloc->extent_entries)) {
620 				j=0;
621 				while ((entry = (GF_ItemExtentEntry *)gf_list_enum(iloc->extent_entries, &j))) {
622 					if (entry->extent_index) continue;
623 					if (j && (maxExtendOffset<it_size) ) maxExtendOffset = it_size;
624 					/*compute new offset*/
625 					entry->extent_offset = baseOffset + it_size;
626 
627 					it_size += entry->extent_length;
628 					if (maxExtendSize<entry->extent_length) maxExtendSize = entry->extent_length;
629 
630 					/*Reading from the input file*/
631 					if (!Emulation) {
632 						char cache_data[4096];
633 						u64 remain = entry->extent_length;
634 						gf_bs_seek(file->movieFileMap->bs, entry->original_extent_offset + iloc->original_base_offset);
635 						while (remain) {
636 							u32 size_cache = (remain>4096) ? 4096 : (u32) remain;
637 							gf_bs_read_data(file->movieFileMap->bs, cache_data, size_cache);
638 							/*Writing to the output file*/
639 							gf_bs_write_data(bs, cache_data, size_cache);
640 							remain -= size_cache;
641 						}
642 					}
643 				}
644 			}
645 			baseOffset += it_size;
646 			*mdatSize += it_size;
647 		} else {
648 			/*we MUST have at least one extent for the dref data*/
649 			if (!gf_list_count(iloc->extent_entries)) {
650 				GF_SAFEALLOC(entry, GF_ItemExtentEntry);
651 				if (!entry) return GF_OUT_OF_MEM;
652 				gf_list_add(iloc->extent_entries, entry);
653 			}
654 			entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0);
655 			entry->extent_offset = 0;
656 			/*0 means full length of referenced file*/
657 			entry->extent_length = 0;
658 		}
659 	}
660 	/*update offset & size length fields*/
661 	if (baseOffset>0xFFFFFFFF) meta->item_locations->base_offset_size = 8;
662 	else if (baseOffset) meta->item_locations->base_offset_size = 4;
663 
664 	if (maxExtendSize>0xFFFFFFFF) meta->item_locations->length_size = 8;
665 	else if (maxExtendSize) meta->item_locations->length_size = 4;
666 
667 	if (maxExtendOffset>0xFFFFFFFF) meta->item_locations->offset_size = 8;
668 	else if (maxExtendOffset) meta->item_locations->offset_size = 4;
669 	return GF_OK;
670 }
671 
672 //this function writes track by track in the order of tracks inside the moov...
DoWrite(MovieWriter * mw,GF_List * writers,GF_BitStream * bs,u8 Emulation,u64 StartOffset)673 GF_Err DoWrite(MovieWriter *mw, GF_List *writers, GF_BitStream *bs, u8 Emulation, u64 StartOffset)
674 {
675 	u32 i;
676 	GF_Err e;
677 	TrackWriter *writer;
678 	u64 offset, sampOffset, predOffset;
679 	u32 chunkNumber, descIndex, sampSize;
680 	Bool force;
681 	GF_StscEntry *stsc_ent;
682 	u64 size, mdatSize = 0;
683 	GF_ISOFile *movie = mw->movie;
684 
685 	/*write meta content first - WE DON'T support fragmentation of resources in ISOM atm*/
686 	if (movie->openMode != GF_ISOM_OPEN_WRITE) {
687 		if (movie->meta) {
688 			e = DoWriteMeta(movie, movie->meta, bs, Emulation, StartOffset, &size);
689 			if (e) return e;
690 			mdatSize += size;
691 			StartOffset += size;
692 		}
693 		if (movie->moov && movie->moov->meta) {
694 			e = DoWriteMeta(movie, movie->meta, bs, Emulation, StartOffset, &size);
695 			if (e) return e;
696 			mdatSize += size;
697 			StartOffset += size;
698 		}
699 		i=0;
700 		while ((writer = (TrackWriter*)gf_list_enum(writers, &i))) {
701 			if (writer->mdia->mediaTrack->meta) {
702 				e = DoWriteMeta(movie, movie->meta, bs, Emulation, StartOffset, &size);
703 				if (e) return e;
704 				mdatSize += size;
705 				StartOffset += size;
706 			}
707 		}
708 	}
709 
710 	offset = StartOffset;
711 	predOffset = 0;
712 	i=0;
713 	while ((writer = (TrackWriter*)gf_list_enum(writers, &i))) {
714 		while (!writer->isDone) {
715 			Bool self_contained;
716 			u32 nb_samp=1;
717 			//To Check: are empty sample tables allowed ???
718 			if (writer->sampleNumber > writer->stbl->SampleSize->sampleCount) {
719 				writer->isDone = 1;
720 				continue;
721 			}
722 			e = stbl_GetSampleInfos(writer->stbl, writer->sampleNumber, &sampOffset, &chunkNumber, &descIndex, &stsc_ent);
723 			if (e) return e;
724 			e = stbl_GetSampleSize(writer->stbl->SampleSize, writer->sampleNumber, &sampSize);
725 			if (e) return e;
726 
727 			update_writer_constant_dur(movie, writer, stsc_ent, &nb_samp, &sampSize, GF_TRUE);
728 
729 			//update our chunks.
730 			force = 0;
731 			if (movie->openMode == GF_ISOM_OPEN_WRITE) {
732 				offset = sampOffset;
733 				if (predOffset != offset)
734 					force = 1;
735 			}
736 
737 			if (writer->stbl->MaxChunkSize && (writer->chunkSize + sampSize > writer->stbl->MaxChunkSize)) {
738 				writer->chunkSize = 0;
739 				force = 1;
740 			}
741 			writer->chunkSize += sampSize;
742 
743 			self_contained = ((writer->all_dref_mode==ISOM_DREF_SELF) || Media_IsSelfContained(writer->mdia, descIndex) ) ? GF_TRUE : GF_FALSE;
744 
745 			//update our global offset...
746 			if (self_contained) {
747 				e = stbl_SetChunkAndOffset(writer->stbl, writer->sampleNumber, descIndex, writer->stsc, &writer->stco, offset, force, nb_samp);
748 				if (e) return e;
749 				if (movie->openMode == GF_ISOM_OPEN_WRITE) {
750 					predOffset = sampOffset + sampSize;
751 				} else {
752 					offset += sampSize;
753 					mdatSize += sampSize;
754 				}
755 			} else {
756 				if (predOffset != offset) force = 1;
757 				predOffset = sampOffset + sampSize;
758 				//we have a DataRef, so use the offset idicated in sampleToChunk and ChunkOffset tables...
759 				e = stbl_SetChunkAndOffset(writer->stbl, writer->sampleNumber, descIndex, writer->stsc, &writer->stco, sampOffset, force, nb_samp);
760 				if (e) return e;
761 			}
762 			//we write the sample if not emulation
763 			if (!Emulation) {
764 				if (self_contained) {
765 					e = WriteSample(mw, sampSize, sampOffset, stsc_ent->isEdited, bs, 1);
766 					if (e) return e;
767 				}
768 			}
769 			//ok, the track is done
770 			if (writer->sampleNumber >= writer->stbl->SampleSize->sampleCount) {
771 				writer->isDone = 1;
772 			} else {
773 				writer->sampleNumber += nb_samp;
774 			}
775 		}
776 	}
777 	//set the mdatSize...
778 	movie->mdat->dataSize = mdatSize;
779 	return GF_OK;
780 }
781 
782 
783 //write the file track by track, with moov box before or after the mdat
WriteFlat(MovieWriter * mw,u8 moovFirst,GF_BitStream * bs,Bool non_seakable,Bool for_fragments,GF_BitStream * moov_bs)784 static GF_Err WriteFlat(MovieWriter *mw, u8 moovFirst, GF_BitStream *bs, Bool non_seakable, Bool for_fragments, GF_BitStream *moov_bs)
785 {
786 	GF_Err e;
787 	u32 i;
788 	u64 offset, finalOffset, totSize, begin, firstSize, finalSize;
789 	GF_Box *a, *cprt_box=NULL;
790 	GF_List *writers = gf_list_new();
791 	GF_ISOFile *movie = mw->movie;
792 	s32 moov_meta_pos=-1;
793 	begin = totSize = 0;
794 
795 	//first setup the writers
796 	e = SetupWriters(mw, writers, 0);
797 	if (e) goto exit;
798 
799 	if (!moovFirst) {
800 		if ((movie->openMode == GF_ISOM_OPEN_WRITE) && !non_seakable) {
801 			begin = 0;
802 			totSize = gf_isom_datamap_get_offset(movie->editFileMap);
803 			/*start boxes have not been written yet, do it*/
804 			if (!totSize) {
805 				if (movie->is_jp2) {
806 					gf_bs_write_u32(movie->editFileMap->bs, 12);
807 					gf_bs_write_u32(movie->editFileMap->bs, GF_ISOM_BOX_TYPE_JP);
808 					gf_bs_write_u32(movie->editFileMap->bs, 0x0D0A870A);
809 					totSize += 12;
810 					begin += 12;
811 				}
812 				if (movie->brand) {
813 					e = gf_isom_box_size((GF_Box *)movie->brand);
814 					if (e) goto exit;
815 					e = gf_isom_box_write((GF_Box *)movie->brand, movie->editFileMap->bs);
816 					if (e) goto exit;
817 					totSize += movie->brand->size;
818 					begin += movie->brand->size;
819 				}
820 				if (movie->pdin) {
821 					e = gf_isom_box_size((GF_Box *)movie->pdin);
822 					if (e) goto exit;
823 					e = gf_isom_box_write((GF_Box *)movie->pdin, movie->editFileMap->bs);
824 					if (e) goto exit;
825 					totSize += movie->pdin->size;
826 					begin += movie->pdin->size;
827 				}
828 			} else {
829 				if (movie->is_jp2) begin += 12;
830 				if (movie->brand) begin += movie->brand->size;
831 				if (movie->pdin) begin += movie->pdin->size;
832 			}
833 			totSize -= begin;
834 		} else if (!non_seakable || for_fragments) {
835 			if (movie->is_jp2) {
836 				gf_bs_write_u32(bs, 12);
837 				gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_JP);
838 				gf_bs_write_u32(bs, 0x0D0A870A);
839 			}
840 			if (movie->brand) {
841 				e = gf_isom_box_size((GF_Box *)movie->brand);
842 				if (e) goto exit;
843 				e = gf_isom_box_write((GF_Box *)movie->brand, bs);
844 				if (e) goto exit;
845 			}
846 			/*then progressive download*/
847 			if (movie->pdin) {
848 				e = gf_isom_box_size((GF_Box *)movie->pdin);
849 				if (e) goto exit;
850 				e = gf_isom_box_write((GF_Box *)movie->pdin, bs);
851 				if (e) goto exit;
852 			}
853 		}
854 
855 		//if the moov is at the end, write directly
856 		i=0;
857 		while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
858 			switch (a->type) {
859 			/*written by hand*/
860 			case GF_ISOM_BOX_TYPE_MOOV:
861 			case GF_ISOM_BOX_TYPE_META:
862 				moov_meta_pos = i-1;
863 			case GF_ISOM_BOX_TYPE_FTYP:
864 			case GF_ISOM_BOX_TYPE_PDIN:
865 #ifndef GPAC_DISABLE_ISOM_ADOBE
866 			case GF_ISOM_BOX_TYPE_AFRA:
867 			case GF_ISOM_BOX_TYPE_ABST:
868 #endif
869 				break;
870 			case GF_ISOM_BOX_TYPE_MDAT:
871 				//in case we're capturing
872 				if (movie->openMode == GF_ISOM_OPEN_WRITE) {
873 					//emulate a write to recreate our tables (media data already written)
874 					e = DoWrite(mw, writers, bs, 1, begin);
875 					if (e) goto exit;
876 					continue;
877 				}
878 				if (non_seakable) {
879 					begin = gf_bs_get_position(bs);
880 					//do a sim pass to get the true mdat size
881 					e = DoWrite(mw, writers, bs, 1, begin);
882 					if (e) goto exit;
883 
884 					if (movie->mdat->dataSize > 0xFFFFFFFF) {
885 						gf_bs_write_u32(bs, 1);
886 					} else {
887 						gf_bs_write_u32(bs, (u32) movie->mdat->dataSize + 8);
888 					}
889 					gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_MDAT);
890 					if (movie->mdat->dataSize > 0xFFFFFFFF) gf_bs_write_u64(bs, movie->mdat->dataSize + 8 + 8);
891 					//reset writers and write samples
892 					ResetWriters(writers);
893 					e = DoWrite(mw, writers, bs, 0, gf_bs_get_position(bs));
894 					if (e) goto exit;
895 					movie->mdat->size = movie->mdat->dataSize;
896 					totSize = 0;
897 				} else {
898 					//to avoid computing the size each time write always 4 + 4 + 8 bytes before
899 					begin = gf_bs_get_position(bs);
900 					gf_bs_write_u64(bs, 0);
901 					gf_bs_write_u64(bs, 0);
902 					e = DoWrite(mw, writers, bs, 0, gf_bs_get_position(bs));
903 					if (e) goto exit;
904 					totSize = gf_bs_get_position(bs) - begin;
905 				}
906 				break;
907 
908 			case GF_ISOM_BOX_TYPE_FREE:
909 				//for backward compat with old arch, keep copyright before moov
910 				if (((GF_FreeSpaceBox*)a)->dataSize>4) {
911 					GF_FreeSpaceBox *fr = (GF_FreeSpaceBox*) a;
912 					if ((fr->dataSize>20) && !strncmp(fr->data, "IsoMedia File", 13)) {
913 						e = gf_isom_box_size(a);
914 						if (e) goto exit;
915 						e = gf_isom_box_write(a, bs);
916 						if (e) goto exit;
917 						cprt_box = a;
918 						break;
919 					}
920 				}
921 			default:
922 				if (moov_meta_pos < 0) {
923 					e = gf_isom_box_size(a);
924 					if (e) goto exit;
925 					e = gf_isom_box_write(a, bs);
926 					if (e) goto exit;
927 				}
928 				break;
929 			}
930 		}
931 
932 		if (moov_bs) {
933 			e = DoWrite(mw, writers, bs, 1, movie->mdat->bsOffset);
934 			if (e) goto exit;
935 
936 			firstSize = GetMoovAndMetaSize(movie, writers);
937 
938 			offset = firstSize;
939 			e = ShiftOffset(movie, writers, offset);
940 			if (e) goto exit;
941 			//get the size and see if it has changed (eg, we moved to 64 bit offsets)
942 			finalSize = GetMoovAndMetaSize(movie, writers);
943 			if (firstSize != finalSize) {
944 				finalOffset = finalSize;
945 				//OK, now we're sure about the final size.
946 				//we don't need to re-emulate, as the only thing that changed is the offset
947 				//so just shift the offset
948 				e = ShiftOffset(movie, writers, finalOffset - offset);
949 				if (e) goto exit;
950 			}
951 		}
952 		//OK, write the movie box.
953 		e = WriteMoovAndMeta(movie, writers, moov_bs ? moov_bs : bs);
954 		if (e) goto exit;
955 
956 #ifndef GPAC_DISABLE_ISOM_ADOBE
957 		i=0;
958 		while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
959 			switch (a->type) {
960 			case GF_ISOM_BOX_TYPE_AFRA:
961 			case GF_ISOM_BOX_TYPE_ABST:
962 				e = gf_isom_box_size(a);
963 				if (e) goto exit;
964 				e = gf_isom_box_write(a, bs);
965 				if (e) goto exit;
966 				break;
967 			}
968 		}
969 #endif
970 
971 		/*if data has been written, update mdat size*/
972 		if (totSize) {
973 			offset = gf_bs_get_position(bs);
974 			e = gf_bs_seek(bs, begin);
975 			if (e) goto exit;
976 			if (totSize > 0xFFFFFFFF) {
977 				gf_bs_write_u32(bs, 1);
978 			} else {
979 				gf_bs_write_u32(bs, (u32) totSize);
980 			}
981 			gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_MDAT);
982 			if (totSize > 0xFFFFFFFF) gf_bs_write_u64(bs, totSize);
983 			e = gf_bs_seek(bs, offset);
984 			movie->mdat->size = totSize;
985 		}
986 
987 		//then the rest
988 		i = (u32) (moov_meta_pos + 1);
989 		while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
990 			if (a==cprt_box) continue;
991 
992 			switch (a->type) {
993 			case GF_ISOM_BOX_TYPE_MOOV:
994 			case GF_ISOM_BOX_TYPE_META:
995 			case GF_ISOM_BOX_TYPE_FTYP:
996 			case GF_ISOM_BOX_TYPE_PDIN:
997 			case GF_ISOM_BOX_TYPE_MDAT:
998 				break;
999 			default:
1000 				e = gf_isom_box_size(a);
1001 				if (e) goto exit;
1002 				e = gf_isom_box_write(a, bs);
1003 				if (e) goto exit;
1004 			}
1005 		}
1006 		goto exit;
1007 	}
1008 
1009 	//nope, we have to write the moov first. The pb is that
1010 	//1 - we don't know its size till the mdat is written
1011 	//2 - we don't know the ofset at which the mdat will start...
1012 	//3 - once the mdat is written, the chunkOffset table can have changed...
1013 
1014 	if (movie->is_jp2) {
1015 		gf_bs_write_u32(bs, 12);
1016 		gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_JP);
1017 		gf_bs_write_u32(bs, 0x0D0A870A);
1018 	}
1019 	if (movie->brand) {
1020 		e = gf_isom_box_size((GF_Box *)movie->brand);
1021 		if (e) goto exit;
1022 		e = gf_isom_box_write((GF_Box *)movie->brand, bs);
1023 		if (e) goto exit;
1024 	}
1025 	/*then progressive dnload*/
1026 	if (movie->pdin) {
1027 		e = gf_isom_box_size((GF_Box *)movie->pdin);
1028 		if (e) goto exit;
1029 		e = gf_isom_box_write((GF_Box *)movie->pdin, bs);
1030 		if (e) goto exit;
1031 	}
1032 
1033 	//write all boxes before moov
1034 	i=0;
1035 	while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
1036 		switch (a->type) {
1037 		case GF_ISOM_BOX_TYPE_MOOV:
1038 		case GF_ISOM_BOX_TYPE_META:
1039 			moov_meta_pos = i-1;
1040 			break;
1041 		case GF_ISOM_BOX_TYPE_FTYP:
1042 		case GF_ISOM_BOX_TYPE_PDIN:
1043 		case GF_ISOM_BOX_TYPE_MDAT:
1044 			break;
1045 		//for backward compat with old arch keep out copyright after moov
1046 		case GF_ISOM_BOX_TYPE_FREE:
1047 			if (((GF_FreeSpaceBox*)a)->dataSize>4) {
1048 				GF_FreeSpaceBox *fr = (GF_FreeSpaceBox*) a;
1049 				if ((fr->dataSize>20) && !strncmp(fr->data, "IsoMedia File", 13)) {
1050 					cprt_box = a;
1051 					break;
1052 				}
1053 			}
1054 		default:
1055 			if (moov_meta_pos<0) {
1056 				e = gf_isom_box_size(a);
1057 				if (e) goto exit;
1058 				e = gf_isom_box_write(a, bs);
1059 				if (e) goto exit;
1060 			}
1061 			break;
1062 		}
1063 	}
1064 
1065 	//What we will do is first emulate the write from the beginning...
1066 	//note: this will set the size of the mdat
1067 	e = DoWrite(mw, writers, bs, 1, gf_bs_get_position(bs));
1068 	if (e) goto exit;
1069 
1070 	firstSize = GetMoovAndMetaSize(movie, writers);
1071 	//offset = (firstSize > 0xFFFFFFFF ? firstSize + 8 : firstSize) + 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0);
1072 	offset = firstSize + 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0);
1073 	e = ShiftOffset(movie, writers, offset);
1074 	if (e) goto exit;
1075 	//get the size and see if it has changed (eg, we moved to 64 bit offsets)
1076 	finalSize = GetMoovAndMetaSize(movie, writers);
1077 	if (firstSize != finalSize) {
1078 		finalOffset = finalSize + 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0);
1079 		//OK, now we're sure about the final size.
1080 		//we don't need to re-emulate, as the only thing that changed is the offset
1081 		//so just shift the offset
1082 		e = ShiftOffset(movie, writers, finalOffset - offset);
1083 		if (e) goto exit;
1084 	}
1085 	//now write our stuff
1086 	e = WriteMoovAndMeta(movie, writers, bs);
1087 	if (e) goto exit;
1088 	e = gf_isom_box_size((GF_Box *)movie->mdat);
1089 	if (e) goto exit;
1090 	e = gf_isom_box_write((GF_Box *)movie->mdat, bs);
1091 	if (e) goto exit;
1092 
1093 	//we don't need the offset as the moov is already written...
1094 	ResetWriters(writers);
1095 	e = DoWrite(mw, writers, bs, 0, 0);
1096 	if (e) goto exit;
1097 
1098 	//then the rest
1099 	i=0;
1100 	while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
1101 		if ((i-1<= (u32) moov_meta_pos) && (a!=cprt_box)) continue;
1102 		switch (a->type) {
1103 		case GF_ISOM_BOX_TYPE_MOOV:
1104 		case GF_ISOM_BOX_TYPE_META:
1105 		case GF_ISOM_BOX_TYPE_FTYP:
1106 		case GF_ISOM_BOX_TYPE_PDIN:
1107 		case GF_ISOM_BOX_TYPE_MDAT:
1108 			break;
1109 		default:
1110 			e = gf_isom_box_size(a);
1111 			if (e) goto exit;
1112 			e = gf_isom_box_write(a, bs);
1113 			if (e) goto exit;
1114 		}
1115 	}
1116 
1117 exit:
1118 	CleanWriters(writers);
1119 	gf_list_del(writers);
1120 	return e;
1121 }
1122 
DoFullInterleave(MovieWriter * mw,GF_List * writers,GF_BitStream * bs,u8 Emulation,u64 StartOffset)1123 GF_Err DoFullInterleave(MovieWriter *mw, GF_List *writers, GF_BitStream *bs, u8 Emulation, u64 StartOffset)
1124 {
1125 
1126 	u32 i, tracksDone;
1127 	TrackWriter *tmp, *curWriter, *prevWriter;
1128 	GF_Err e;
1129 	u64 DTS, DTStmp, TStmp;
1130 	s64 res;
1131 	u32 descIndex, sampSize, chunkNumber;
1132 	u16 curGroupID, curTrackPriority;
1133 	Bool forceNewChunk, writeGroup;
1134 	GF_StscEntry *stsc_ent;
1135 	//this is used to emulate the write ...
1136 	u64 offset, totSize, sampOffset;
1137 	GF_ISOFile *movie = mw->movie;
1138 
1139 	totSize = 0;
1140 	curGroupID = 1;
1141 
1142 	prevWriter = NULL;
1143 	//we emulate a write from this offset...
1144 	offset = StartOffset;
1145 	tracksDone = 0;
1146 
1147 	//browse each groups
1148 	while (1) {
1149 		writeGroup = 1;
1150 
1151 		//proceed a group
1152 		while (writeGroup) {
1153 			u32 nb_samp = 1;
1154 			Bool self_contained, chunked_forced=GF_FALSE;
1155 			//first get the appropriated sample for the min time in this group
1156 			curWriter = NULL;
1157 			DTStmp = (u64) -1;
1158 			TStmp = 0;
1159 			curTrackPriority = (u16) -1;
1160 
1161 			i=0;
1162 			while ((tmp = (TrackWriter*)gf_list_enum(writers, &i))) {
1163 
1164 				//is it done writing ?
1165 				//is it in our group ??
1166 				if (tmp->isDone || tmp->stbl->groupID != curGroupID) continue;
1167 
1168 				//OK, get the current sample in this track
1169 				stbl_GetSampleDTS(tmp->stbl->TimeToSample, tmp->sampleNumber, &DTS);
1170 				res = TStmp ? DTStmp * tmp->timeScale - DTS * TStmp : 0;
1171 				if (res < 0) continue;
1172 				if ((!res) && curTrackPriority <= tmp->stbl->trackPriority) continue;
1173 				curWriter = tmp;
1174 				curTrackPriority = tmp->stbl->trackPriority;
1175 				DTStmp = DTS;
1176 				TStmp = tmp->timeScale;
1177 			}
1178 			//no sample found, we're done with this group
1179 			if (!curWriter) {
1180 				//we're done with the group
1181 				writeGroup = 0;
1182 				continue;
1183 			}
1184 			//To Check: are empty sample tables allowed ???
1185 			if (curWriter->sampleNumber > curWriter->stbl->SampleSize->sampleCount) {
1186 				curWriter->isDone = 1;
1187 				tracksDone ++;
1188 				continue;
1189 			}
1190 
1191 			e = stbl_GetSampleInfos(curWriter->stbl, curWriter->sampleNumber, &sampOffset, &chunkNumber, &descIndex, &stsc_ent);
1192 			if (e) return e;
1193 			e = stbl_GetSampleSize(curWriter->stbl->SampleSize, curWriter->sampleNumber, &sampSize);
1194 			if (e) return e;
1195 
1196 			update_writer_constant_dur(movie, curWriter, stsc_ent, &nb_samp, &sampSize, GF_FALSE);
1197 
1198 			if (curWriter->stbl->MaxChunkSize && (curWriter->chunkSize + sampSize > curWriter->stbl->MaxChunkSize)) {
1199 				curWriter->chunkSize = 0;
1200 				chunked_forced = forceNewChunk = 1;
1201 			}
1202 			curWriter->chunkSize += sampSize;
1203 
1204 			self_contained = ((curWriter->all_dref_mode==ISOM_DREF_SELF) || Media_IsSelfContained(curWriter->mdia, descIndex) ) ? GF_TRUE : GF_FALSE;
1205 
1206 			//do we actually write, or do we emulate ?
1207 			if (Emulation) {
1208 				//are we in the same track ??? If not, force a new chunk when adding this sample
1209 				if (!chunked_forced) {
1210 					if (curWriter != prevWriter) {
1211 						forceNewChunk = 1;
1212 					} else {
1213 						forceNewChunk = 0;
1214 					}
1215 				}
1216 				//update our offsets...
1217 				if (self_contained) {
1218 					e = stbl_SetChunkAndOffset(curWriter->stbl, curWriter->sampleNumber, descIndex, curWriter->stsc, &curWriter->stco, offset, forceNewChunk, nb_samp);
1219 					if (e) return e;
1220 					offset += sampSize;
1221 					totSize += sampSize;
1222 				} else {
1223 //					if (curWriter->prev_offset != sampOffset) forceNewChunk = 1;
1224 					curWriter->prev_offset = sampOffset + sampSize;
1225 
1226 					//we have a DataRef, so use the offset idicated in sampleToChunk
1227 					//and ChunkOffset tables...
1228 					e = stbl_SetChunkAndOffset(curWriter->stbl, curWriter->sampleNumber, descIndex, curWriter->stsc, &curWriter->stco, sampOffset, chunked_forced, nb_samp);
1229 					if (e) return e;
1230 				}
1231 			} else {
1232 				//this is no game, we're writing ....
1233 				if (self_contained) {
1234 					e = WriteSample(mw, sampSize, sampOffset, stsc_ent->isEdited, bs, 1);
1235 					if (e) return e;
1236 				}
1237 			}
1238 			//ok, the sample is done
1239 			if (curWriter->sampleNumber == curWriter->stbl->SampleSize->sampleCount) {
1240 				curWriter->isDone = 1;
1241 				//one more track done...
1242 				tracksDone ++;
1243 			} else {
1244 				curWriter->sampleNumber += nb_samp;
1245 			}
1246 			prevWriter = curWriter;
1247 		}
1248 		//if all our track are done, break
1249 		if (tracksDone == gf_list_count(writers)) break;
1250 		//go to next group
1251 		curGroupID ++;
1252 	}
1253 	if (movie->mdat)
1254 		movie->mdat->dataSize = totSize;
1255 	return GF_OK;
1256 }
1257 
1258 
1259 
1260 /*uncomment the following to easily test large file generation. This will prepend 4096*1MByte of 0 before the media data*/
1261 //#define TEST_LARGE_FILES
1262 
DoInterleave(MovieWriter * mw,GF_List * writers,GF_BitStream * bs,u8 Emulation,u64 StartOffset,Bool drift_inter)1263 GF_Err DoInterleave(MovieWriter *mw, GF_List *writers, GF_BitStream *bs, u8 Emulation, u64 StartOffset, Bool drift_inter)
1264 {
1265 	u32 i, tracksDone;
1266 	TrackWriter *tmp, *curWriter;
1267 	GF_Err e;
1268 	u32 descIndex, sampSize, chunkNumber;
1269 	u64 DTS;
1270 	u32 moov_timescale;
1271 	u16 curGroupID;
1272 	Bool forceNewChunk, writeGroup;
1273 	GF_StscEntry *stsc_ent;
1274 	//this is used to emulate the write ...
1275 	u64 offset, sampOffset, size, mdatSize;
1276 	u32 count;
1277 	GF_ISOFile *movie = mw->movie;
1278 
1279 	mdatSize = 0;
1280 
1281 #ifdef TEST_LARGE_FILES
1282 	if (!Emulation) {
1283 		char *blank;
1284 		u32 count, i;
1285 		i = count = 0;
1286 		blank = gf_malloc(sizeof(char)*1024*1024);
1287 		memset(blank, 0, sizeof(char)*1024*1024);
1288 		count = 4096;
1289 		memset(blank, 0, sizeof(char)*1024*1024);
1290 		while (i<count) {
1291 			u32 res = gf_bs_write_data(bs, blank, 1024*1024);
1292 			if (res != 1024*1024) {
1293 				GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("error writing to disk: only %d bytes written\n", res));
1294 			}
1295 			i++;
1296 			GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("writing blank block: %.02f done - %d/%d \r", (100.0*i)/count , i, count));
1297 		}
1298 		gf_free(blank);
1299 	}
1300 	mdatSize = 4096*1024;
1301 	mdatSize *= 1024;
1302 #endif
1303 
1304 	/*write meta content first - WE DON'T support fragmentation of resources in ISOM atm*/
1305 	if (movie->meta) {
1306 		e = DoWriteMeta(movie, movie->meta, bs, Emulation, StartOffset, &size);
1307 		if (e) return e;
1308 		mdatSize += size;
1309 		StartOffset += (u32) size;
1310 	}
1311 	if (movie->moov) {
1312 		if (movie->moov->meta) {
1313 			e = DoWriteMeta(movie, movie->moov->meta, bs, Emulation, StartOffset, &size);
1314 			if (e) return e;
1315 			mdatSize += size;
1316 			StartOffset += (u32) size;
1317 		}
1318 		i=0;
1319 		while ((tmp = (TrackWriter*)gf_list_enum(writers, &i))) {
1320 			if (tmp->mdia->mediaTrack->meta) {
1321 				e = DoWriteMeta(movie, tmp->mdia->mediaTrack->meta, bs, Emulation, StartOffset, &size);
1322 				if (e) return e;
1323 				mdatSize += size;
1324 				StartOffset += (u32) size;
1325 			}
1326 		}
1327 	}
1328 
1329 
1330 	if (movie->storageMode == GF_ISOM_STORE_TIGHT)
1331 		return DoFullInterleave(mw, writers, bs, Emulation, StartOffset);
1332 
1333 	curGroupID = 1;
1334 	//we emulate a write from this offset...
1335 	offset = StartOffset;
1336 	tracksDone = 0;
1337 
1338 #ifdef TEST_LARGE_FILES
1339 	offset += mdatSize;
1340 #endif
1341 
1342 	moov_timescale = movie->moov && movie->moov->mvhd ? movie->moov->mvhd->timeScale : 1000;
1343 
1344 	count = gf_list_count(writers);
1345 	//browse each groups
1346 	while (1) {
1347 		/*the max DTS the chunk of the current writer*/
1348 		u64 chunkLastDTS = 0;
1349 		/*the timescale related to the max DTS*/
1350 		u32 chunkLastScale = 0;
1351 
1352 		writeGroup = 1;
1353 
1354 		//proceed a group
1355 		while (writeGroup) {
1356 			curWriter = NULL;
1357 			for (i=0 ; i < count; i++) {
1358 				tmp = (TrackWriter*)gf_list_get(writers, i);
1359 
1360 				//is it done writing ?
1361 				if (tmp->isDone) continue;
1362 
1363 				//is it in our group ??
1364 				if (tmp->stbl->groupID != curGroupID) continue;
1365 
1366 				//write till this chunk is full on this track...
1367 				while (1) {
1368 					Bool self_contained;
1369 					u32 nb_samp = 1;
1370 					u32 sample_dur;
1371 					u64 chunk_prev_dur;
1372 					//To Check: are empty sample tables allowed ???
1373 					if (tmp->sampleNumber > tmp->stbl->SampleSize->sampleCount) {
1374 						tmp->isDone = 1;
1375 						tracksDone ++;
1376 						break;
1377 					}
1378 
1379 					//OK, get the current sample in this track
1380 					stbl_GetSampleDTS_and_Duration(tmp->stbl->TimeToSample, tmp->sampleNumber, &DTS, &sample_dur);
1381 
1382 					//can this sample fit in our chunk ?
1383 					if ( ( (DTS - tmp->DTSprev) + tmp->chunkDur) * moov_timescale > movie->interleavingTime * tmp->timeScale
1384 					        /*drift check: reject sample if outside our check window*/
1385 					        || (drift_inter && chunkLastDTS && ( ((u64)tmp->DTSprev*chunkLastScale) > ((u64)chunkLastDTS*tmp->timeScale)) )
1386 					   ) {
1387 						//in case the sample is longer than InterleaveTime
1388 						if (!tmp->chunkDur) {
1389 							forceNewChunk = 1;
1390 						} else {
1391 							//this one is full. go to next one (exit the loop)
1392 							tmp->chunkDur = 0;
1393 							//forceNewChunk = 0;
1394 							break;
1395 						}
1396 					} else {
1397 						forceNewChunk = tmp->chunkDur ? 0 : 1;
1398 					}
1399 					//OK, we can write this track
1400 					curWriter = tmp;
1401 
1402 					//small check for first 2 samples (DTS = 0)
1403 					//only in the old mode can chunkdur be 0 for dts 0
1404 					if (tmp->sampleNumber == 2 && !tmp->chunkDur && gf_sys_old_arch_compat() ) {
1405 						forceNewChunk = 0;
1406 					}
1407 
1408 					chunk_prev_dur = tmp->chunkDur;
1409 					//FIXME we do not apply patch in test mode for now since this breaks all our hashes, remove this
1410 					//once we move to filters permanently
1411 					if (!gf_sys_old_arch_compat()) {
1412 						tmp->chunkDur += sample_dur;
1413 					} else {
1414 						//old style, compute based on DTS diff
1415 						tmp->chunkDur += (u32) (DTS - tmp->DTSprev);
1416 					}
1417 					tmp->DTSprev = DTS;
1418 
1419 					e = stbl_GetSampleInfos(curWriter->stbl, curWriter->sampleNumber, &sampOffset, &chunkNumber, &descIndex, &stsc_ent);
1420 					if (e)
1421 						return e;
1422 					e = stbl_GetSampleSize(curWriter->stbl->SampleSize, curWriter->sampleNumber, &sampSize);
1423 					if (e)
1424 						return e;
1425 
1426 					self_contained = ((curWriter->all_dref_mode==ISOM_DREF_SELF) || Media_IsSelfContained(curWriter->mdia, descIndex)) ? GF_TRUE : GF_FALSE;
1427 
1428 					update_writer_constant_dur(movie, curWriter, stsc_ent, &nb_samp, &sampSize, GF_FALSE);
1429 
1430 					if (curWriter->stbl->MaxChunkSize && (curWriter->chunkSize + sampSize > curWriter->stbl->MaxChunkSize)) {
1431 						curWriter->chunkSize = 0;
1432 						tmp->chunkDur -= chunk_prev_dur;
1433 						forceNewChunk = 1;
1434 					}
1435 					curWriter->chunkSize += sampSize;
1436 
1437 					//do we actually write, or do we emulate ?
1438 					if (Emulation) {
1439 						//update our offsets...
1440 						if (self_contained) {
1441 							e = stbl_SetChunkAndOffset(curWriter->stbl, curWriter->sampleNumber, descIndex, curWriter->stsc, &curWriter->stco, offset, forceNewChunk, nb_samp);
1442 							if (e)
1443 								return e;
1444 							offset += sampSize;
1445 							mdatSize += sampSize;
1446 						} else {
1447 							if (curWriter->prev_offset != sampOffset) forceNewChunk = 1;
1448 							curWriter->prev_offset = sampOffset + sampSize;
1449 
1450 							//we have a DataRef, so use the offset idicated in sampleToChunk
1451 							//and ChunkOffset tables...
1452 							e = stbl_SetChunkAndOffset(curWriter->stbl, curWriter->sampleNumber, descIndex, curWriter->stsc, &curWriter->stco, sampOffset, forceNewChunk, nb_samp);
1453 							if (e) return e;
1454 						}
1455 					} else {
1456 						//we're writing ....
1457 						if (self_contained) {
1458 							e = WriteSample(mw, sampSize, sampOffset, stsc_ent->isEdited, bs, nb_samp);
1459 							if (e)
1460 								return e;
1461 						}
1462 					}
1463 					//ok, the sample is done
1464 					if (curWriter->sampleNumber >= curWriter->stbl->SampleSize->sampleCount) {
1465 						curWriter->isDone = 1;
1466 						//one more track done...
1467 						tracksDone ++;
1468 						break;
1469 					} else {
1470 						curWriter->sampleNumber += nb_samp;
1471 					}
1472 				}
1473 				/*record chunk end-time & track timescale for drift-controled interleaving*/
1474 				if (drift_inter && curWriter) {
1475 					chunkLastScale = curWriter->timeScale;
1476 					chunkLastDTS = curWriter->DTSprev;
1477 					/*add one interleave window drift - since the "maxDTS" is the previously written one, we will
1478 					have the following cases:
1479 					- sample doesn't fit: post-pone and force new chunk
1480 					- sample time larger than previous chunk time + interleave: post-pone and force new chunk
1481 					- otherwise store and track becomes current reference
1482 
1483 					this ensures a proper drift regulation (max DTS diff is less than the interleaving window)
1484 					*/
1485 					chunkLastDTS += curWriter->timeScale * movie->interleavingTime / moov_timescale;
1486 				}
1487 			}
1488 			//no sample found, we're done with this group
1489 			if (!curWriter) {
1490 				writeGroup = 0;
1491 				continue;
1492 			}
1493 		}
1494 		//if all our track are done, break
1495 		if (tracksDone == gf_list_count(writers)) break;
1496 		//go to next group
1497 		curGroupID ++;
1498 	}
1499 	if (movie->mdat) movie->mdat->dataSize = mdatSize;
1500 	return GF_OK;
1501 }
1502 
1503 
WriteInterleaved(MovieWriter * mw,GF_BitStream * bs,Bool drift_inter)1504 static GF_Err WriteInterleaved(MovieWriter *mw, GF_BitStream *bs, Bool drift_inter)
1505 {
1506 	GF_Err e;
1507 	u32 i;
1508 	s32 moov_meta_pos=-1;
1509 	GF_Box *a, *cprt_box=NULL;
1510 	u64 firstSize, finalSize, offset, finalOffset;
1511 	GF_List *writers = gf_list_new();
1512 	GF_ISOFile *movie = mw->movie;
1513 
1514 	//first setup the writers
1515 	e = SetupWriters(mw, writers, 1);
1516 	if (e) goto exit;
1517 
1518 
1519 	if (movie->is_jp2) {
1520 		gf_bs_write_u32(bs, 12);
1521 		gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_JP);
1522 		gf_bs_write_u32(bs, 0x0D0A870A);
1523 	}
1524 	if (movie->brand) {
1525 		e = gf_isom_box_size((GF_Box *)movie->brand);
1526 		if (e) goto exit;
1527 		e = gf_isom_box_write((GF_Box *)movie->brand, bs);
1528 		if (e) goto exit;
1529 	}
1530 	if (movie->pdin) {
1531 		e = gf_isom_box_size((GF_Box *)movie->pdin);
1532 		if (e) goto exit;
1533 		e = gf_isom_box_write((GF_Box *)movie->pdin, bs);
1534 		if (e) goto exit;
1535 	}
1536 
1537 	//write all boxes before moov
1538 	i=0;
1539 	while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
1540 		switch (a->type) {
1541 		case GF_ISOM_BOX_TYPE_MOOV:
1542 		case GF_ISOM_BOX_TYPE_META:
1543 			moov_meta_pos = i-1;
1544 			break;
1545 		case GF_ISOM_BOX_TYPE_FTYP:
1546 		case GF_ISOM_BOX_TYPE_PDIN:
1547 		case GF_ISOM_BOX_TYPE_MDAT:
1548 			break;
1549 		case GF_ISOM_BOX_TYPE_FREE:
1550 			//for backward compat with old arch, keep copyright before moov
1551 			if (((GF_FreeSpaceBox*)a)->dataSize>4) {
1552 				GF_FreeSpaceBox *fr = (GF_FreeSpaceBox*) a;
1553 				if ((fr->dataSize>20) && !strncmp(fr->data, "IsoMedia File", 13)) {
1554 					cprt_box = a;
1555 					break;
1556 				}
1557 			}
1558 		default:
1559 			if (moov_meta_pos<0) {
1560 				e = gf_isom_box_size(a);
1561 				if (e) goto exit;
1562 				e = gf_isom_box_write(a, bs);
1563 				if (e) goto exit;
1564 			}
1565 			break;
1566 		}
1567 	}
1568 
1569 	e = DoInterleave(mw, writers, bs, 1, gf_bs_get_position(bs), drift_inter);
1570 	if (e) goto exit;
1571 
1572 	firstSize = GetMoovAndMetaSize(movie, writers);
1573 	offset = firstSize;
1574 	if (movie->mdat && movie->mdat->dataSize) offset += 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0);
1575 	e = ShiftOffset(movie, writers, offset);
1576 	if (e) goto exit;
1577 	//get the size and see if it has changed (eg, we moved to 64 bit offsets)
1578 	finalSize = GetMoovAndMetaSize(movie, writers);
1579 	if (firstSize != finalSize) {
1580 		finalOffset = finalSize;
1581 		if (movie->mdat && movie->mdat->dataSize) finalOffset += 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0);
1582 		//OK, now we're sure about the final size -> shift the offsets
1583 		//we don't need to re-emulate, as the only thing that changed is the offset
1584 		//so just shift the offset
1585 		e = ShiftOffset(movie, writers, finalOffset - offset);
1586 		if (e) goto exit;
1587 		/*firstSize = */GetMoovAndMetaSize(movie, writers);
1588 	}
1589 	//now write our stuff
1590 	e = WriteMoovAndMeta(movie, writers, bs);
1591 	if (e) goto exit;
1592 
1593 	/*we have 8 extra bytes for large size (not computed in gf_isom_box_size) */
1594 	if (movie->mdat && movie->mdat->dataSize) {
1595 		if (movie->mdat->dataSize > 0xFFFFFFFF) movie->mdat->dataSize += 8;
1596 		e = gf_isom_box_size((GF_Box *)movie->mdat);
1597 		if (e) goto exit;
1598 		e = gf_isom_box_write((GF_Box *)movie->mdat, bs);
1599 		if (e) goto exit;
1600 	}
1601 
1602 	//we don't need the offset as we are writing...
1603 	ResetWriters(writers);
1604 	e = DoInterleave(mw, writers, bs, 0, 0, drift_inter);
1605 	if (e) goto exit;
1606 
1607 
1608 	//then the rest
1609 	i=0;
1610 	while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
1611 		if ((i-1 < (u32) moov_meta_pos) && (a != cprt_box))
1612 			continue;
1613 		switch (a->type) {
1614 		case GF_ISOM_BOX_TYPE_MOOV:
1615 		case GF_ISOM_BOX_TYPE_META:
1616 		case GF_ISOM_BOX_TYPE_FTYP:
1617 		case GF_ISOM_BOX_TYPE_PDIN:
1618 		case GF_ISOM_BOX_TYPE_MDAT:
1619 			break;
1620 		default:
1621 			e = gf_isom_box_size(a);
1622 			if (e) goto exit;
1623 			e = gf_isom_box_write(a, bs);
1624 			if (e) goto exit;
1625 		}
1626 	}
1627 
1628 exit:
1629 	CleanWriters(writers);
1630 	gf_list_del(writers);
1631 	return e;
1632 }
1633 
1634 extern u32 default_write_buffering_size;
1635 GF_Err gf_isom_flush_chunk(GF_TrackBox *trak, Bool is_final);
1636 
WriteToFile(GF_ISOFile * movie,Bool for_fragments)1637 GF_Err WriteToFile(GF_ISOFile *movie, Bool for_fragments)
1638 {
1639 	MovieWriter mw;
1640 	GF_Err e = GF_OK;
1641 	if (!movie) return GF_BAD_PARAM;
1642 
1643 	if (movie->openMode == GF_ISOM_OPEN_READ) return GF_BAD_PARAM;
1644 
1645 	e = gf_isom_insert_copyright(movie);
1646 	if (e) return e;
1647 
1648 	memset(&mw, 0, sizeof(mw));
1649 	mw.movie = movie;
1650 
1651 
1652 	if (movie->moov) {
1653 		u32 i;
1654 		GF_TrackBox *trak;
1655 		if (gf_sys_is_test_mode()) {
1656 			movie->moov->mvhd->creationTime = 0;
1657 			movie->moov->mvhd->modificationTime = 0;
1658 		}
1659 		i=0;
1660 		while ( (trak = gf_list_enum(movie->moov->trackList, &i))) {
1661 			if (gf_sys_is_test_mode()) {
1662 				trak->Header->creationTime = 0;
1663 				trak->Header->modificationTime = 0;
1664 				if (trak->Media->handler->nameUTF8 && strstr(trak->Media->handler->nameUTF8, "@GPAC")) {
1665 					gf_free(trak->Media->handler->nameUTF8);
1666 					trak->Media->handler->nameUTF8 = gf_strdup("MediaHandler");
1667 				}
1668 				trak->Media->mediaHeader->creationTime = 0;
1669 				trak->Media->mediaHeader->modificationTime = 0;
1670 			}
1671 			if (trak->chunk_cache) {
1672 				gf_isom_flush_chunk(trak, GF_TRUE);
1673 			}
1674 		}
1675 	}
1676 	//capture mode: we don't need a new bitstream
1677 	if (movie->openMode == GF_ISOM_OPEN_WRITE) {
1678 		if (!strcmp(movie->fileName, "_gpac_isobmff_redirect")) {
1679 			GF_BitStream *bs, *moov_bs=NULL;
1680 			u64 mdat_end = gf_bs_get_position(movie->editFileMap->bs);
1681 			u64 mdat_start = movie->mdat->bsOffset;
1682 			u64 mdat_size = mdat_end - mdat_start;
1683 
1684 			if (for_fragments) {
1685 				if (!movie->on_block_out) {
1686 					GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISOBMFF] Missing output block callback, cannot write\n"));
1687 					return GF_BAD_PARAM;
1688 				}
1689 				bs = gf_bs_new_cbk(movie->on_block_out, movie->on_block_out_usr_data, movie->on_block_out_block_size);
1690 				e = WriteFlat(&mw, 0, bs, GF_TRUE, GF_TRUE, NULL);
1691 				movie->fragmented_file_pos = gf_bs_get_position(bs);
1692 				gf_bs_del(bs);
1693 				return e;
1694 			}
1695 			//seek at end in case we had a read of the file
1696 			gf_bs_seek(movie->editFileMap->bs, gf_bs_get_size(movie->editFileMap->bs) );
1697 
1698 			if ((movie->storageMode==GF_ISOM_STORE_FASTSTART) && mdat_start && mdat_size) {
1699 				moov_bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
1700 			}
1701 			//write as non-seekable
1702 			e = WriteFlat(&mw, 0, movie->editFileMap->bs, GF_TRUE, GF_FALSE, moov_bs);
1703 
1704 			movie->fragmented_file_pos = gf_bs_get_position(movie->editFileMap->bs);
1705 
1706 			if (mdat_start && mdat_size) {
1707 				u8 data[16];
1708 				if (!movie->on_block_out) {
1709 					GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISOBMFF] Missing output block patch callback, cannot patch mdat size in flat storage\n"));
1710 					return GF_BAD_PARAM;
1711 				}
1712 
1713 				//create a patch packet for mdat covering out 16 bytes (cf FlushCapture)
1714 				bs = gf_bs_new(data, 16, GF_BITSTREAM_WRITE);
1715 				gf_bs_write_u32(bs, (mdat_size>0xFFFFFFFF) ? 1 : (u32) mdat_size);
1716 				gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_MDAT);
1717 				if  (mdat_size>0xFFFFFFFF)
1718 					gf_bs_write_u64(bs, mdat_size);
1719 				else
1720 					gf_bs_write_u64(bs, 0);
1721 				gf_bs_del(bs);
1722 				movie->on_block_patch(movie->on_block_out_usr_data, data, 16, mdat_start, GF_FALSE);
1723 			}
1724 
1725 			if (moov_bs) {
1726 				u8 *moov_data;
1727 				u32 moov_size;
1728 
1729 				gf_bs_get_content(moov_bs, &moov_data, &moov_size);
1730 				gf_bs_del(moov_bs);
1731 
1732 				movie->on_block_patch(movie->on_block_out_usr_data, moov_data, moov_size, mdat_start, GF_TRUE);
1733 				gf_free(moov_data);
1734 			}
1735 		} else {
1736 			GF_BitStream *moov_bs = NULL;
1737 			if ((movie->storageMode==GF_ISOM_STORE_STREAMABLE) || (movie->storageMode==GF_ISOM_STORE_FASTSTART) ) {
1738 				moov_bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
1739 			}
1740 			e = WriteFlat(&mw, 0, movie->editFileMap->bs, GF_FALSE, GF_FALSE, moov_bs);
1741 			if (moov_bs) {
1742 				u8 *moov_data;
1743 				u32 moov_size;
1744 
1745 				gf_bs_get_content(moov_bs, &moov_data, &moov_size);
1746 				gf_bs_del(moov_bs);
1747 				if (!e)
1748 					e = gf_bs_insert_data(movie->editFileMap->bs, moov_data, moov_size, movie->mdat->bsOffset);
1749 
1750 				gf_free(moov_data);
1751 			}
1752 		}
1753 	} else {
1754 		FILE *stream=NULL;
1755 		Bool is_stdout = GF_FALSE;
1756 		GF_BitStream *bs=NULL;
1757 		if (!strcmp(movie->finalName, "_gpac_isobmff_redirect")) {
1758 			if (!movie->on_block_out) {
1759 				GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISOBMFF] Missing output block callback, cannot write\n"));
1760 				return GF_BAD_PARAM;
1761 			}
1762 			bs = gf_bs_new_cbk(movie->on_block_out, movie->on_block_out_usr_data, movie->on_block_out_block_size);
1763 			is_stdout = GF_TRUE;
1764 		} else {
1765 			if (!strcmp(movie->finalName, "std"))
1766 				is_stdout = GF_TRUE;
1767 
1768 			//OK, we need a new bitstream
1769 			stream = is_stdout ? stdout : gf_fopen(movie->finalName, "w+b");
1770 			if (!stream)
1771 				return GF_IO_ERR;
1772 			bs = gf_bs_from_file(stream, GF_BITSTREAM_WRITE);
1773 		}
1774 		if (!bs) {
1775 			if (!is_stdout)
1776 				gf_fclose(stream);
1777 			return GF_OUT_OF_MEM;
1778 		}
1779 
1780 		switch (movie->storageMode) {
1781 		case GF_ISOM_STORE_TIGHT:
1782 		case GF_ISOM_STORE_INTERLEAVED:
1783 			e = WriteInterleaved(&mw, bs, 0);
1784 			break;
1785 		case GF_ISOM_STORE_DRIFT_INTERLEAVED:
1786 			e = WriteInterleaved(&mw, bs, 1);
1787 			break;
1788 		case GF_ISOM_STORE_STREAMABLE:
1789 			e = WriteFlat(&mw, 1, bs, is_stdout, GF_FALSE, NULL);
1790 			break;
1791 		default:
1792 			e = WriteFlat(&mw, 0, bs, is_stdout, GF_FALSE, NULL);
1793 			break;
1794 		}
1795 
1796 		gf_bs_del(bs);
1797 		if (!is_stdout)
1798 			gf_fclose(stream);
1799 	}
1800 	if (mw.buffer) gf_free(mw.buffer);
1801 	if (mw.nb_done<mw.total_samples) {
1802 		mw.nb_done = mw.total_samples;
1803 		muxer_report_progress(&mw);
1804 	}
1805 	return e;
1806 }
1807 
1808 
1809 
1810 #endif	/*!defined(GPAC_DISABLE_ISOM) && !defined(GPAC_DISABLE_ISOM_WRITE)*/
1811