1 /*
2 *			GPAC - Multimedia Framework C SDK
3 *
4 *			Authors: Jean Le Feuvre
5 *			Copyright (c) Telecom ParisTech 2000-2012
6 *					All rights reserved
7 *
8 *  This file is part of GPAC / ISO Media File Format sub-project
9 *
10 *  GPAC is free software; you can redistribute it and/or modify
11 *  it under the terms of the GNU Lesser General Public License as published by
12 *  the Free Software Foundation; either version 2, or (at your option)
13 *  any later version.
14 *
15 *  GPAC is distributed in the hope that it will be useful,
16 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 *  GNU Lesser General Public License for more details.
19 *
20 *  You should have received a copy of the GNU Lesser General Public
21 *  License along with this library; see the file COPYING.  If not, write to
22 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23 *
24 */
25 
26 #include <gpac/internal/isomedia_dev.h>
27 
28 #if !defined(GPAC_DISABLE_ISOM) && !defined(GPAC_DISABLE_ISOM_WRITE)
29 
30 #define GPAC_ISOM_CPRT_NOTICE "IsoMedia File Produced with GPAC"
31 #define GPAC_ISOM_CPRT_NOTICE_VERSION GPAC_ISOM_CPRT_NOTICE" "GPAC_FULL_VERSION
32 
gf_isom_insert_copyright(GF_ISOFile * movie)33 static GF_Err gf_isom_insert_copyright(GF_ISOFile *movie)
34 {
35 	u32 i;
36 	GF_Box *a;
37 	GF_FreeSpaceBox *_free;
38 	i = 0;
39 	while ((a = (GF_Box *)gf_list_enum(movie->TopBoxes, &i))) {
40 		if (a->type == GF_ISOM_BOX_TYPE_FREE) {
41 			_free = (GF_FreeSpaceBox *)a;
42 			if (_free->dataSize) {
43 				if (!strcmp(_free->data, GPAC_ISOM_CPRT_NOTICE_VERSION)) return GF_OK;
44 				if (strstr(_free->data, GPAC_ISOM_CPRT_NOTICE)) {
45 					gf_free(_free->data);
46 					_free->data = gf_strdup(movie->drop_date_version_info ? GPAC_ISOM_CPRT_NOTICE : GPAC_ISOM_CPRT_NOTICE_VERSION);
47 					_free->dataSize = 1 + (u32)strlen(_free->data);
48 					return GF_OK;
49 				}
50 			}
51 		}
52 	}
53 	a = gf_isom_box_new(GF_ISOM_BOX_TYPE_FREE);
54 	if (!a) return GF_OUT_OF_MEM;
55 	_free = (GF_FreeSpaceBox *)a;
56 	_free->data = gf_strdup(movie->drop_date_version_info ? GPAC_ISOM_CPRT_NOTICE : GPAC_ISOM_CPRT_NOTICE_VERSION);
57 	_free->dataSize = (u32)strlen(_free->data) + 1;
58 	if (!_free->data) return GF_OUT_OF_MEM;
59 	return gf_list_add(movie->TopBoxes, _free);
60 }
61 
62 typedef struct
63 {
64 	/*the curent sample of this track*/
65 	u32 sampleNumber;
66 	/*timeScale of the media (for interleaving)*/
67 	u32 timeScale;
68 	/*this is for generic, time-based interleaving. Expressed in Media TimeScale*/
69 	u32 chunkDur;
70 	u64 DTSprev;
71 	u8 isDone;
72 	u64 prev_offset;
73 	GF_MediaBox *mdia;
74 	/*each writer has a sampleToChunck and ChunkOffset tables
75 	these tables are filled during emulation mode and then will	replace the table in the GF_SampleTableBox*/
76 	GF_SampleToChunkBox *stsc;
77 	/*we don't know if it's a large offset or not*/
78 	GF_Box *stco;
79 } TrackWriter;
80 
81 typedef struct
82 {
83 	char *buffer;
84 	u32 size;
85 	GF_ISOFile *movie;
86 	u32 total_samples, nb_done;
87 } MovieWriter;
88 
CleanWriters(GF_List * writers)89 void CleanWriters(GF_List *writers)
90 {
91 	TrackWriter *writer;
92 	while (gf_list_count(writers)) {
93 		writer = (TrackWriter*)gf_list_get(writers, 0);
94 		gf_isom_box_del(writer->stco);
95 		gf_isom_box_del((GF_Box *)writer->stsc);
96 		gf_free(writer);
97 		gf_list_rem(writers, 0);
98 	}
99 }
100 
ResetWriters(GF_List * writers)101 void ResetWriters(GF_List *writers)
102 {
103 	u32 i;
104 	TrackWriter *writer;
105 	i = 0;
106 	while ((writer = (TrackWriter *)gf_list_enum(writers, &i))) {
107 		writer->isDone = 0;
108 		writer->chunkDur = 0;
109 		writer->DTSprev = 0;
110 		writer->sampleNumber = 1;
111 		gf_isom_box_del((GF_Box *)writer->stsc);
112 		writer->stsc = (GF_SampleToChunkBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_STSC);
113 		if (writer->stco->type == GF_ISOM_BOX_TYPE_STCO) {
114 			gf_free(((GF_ChunkOffsetBox *)writer->stco)->offsets);
115 			((GF_ChunkOffsetBox *)writer->stco)->offsets = NULL;
116 			((GF_ChunkOffsetBox *)writer->stco)->nb_entries = 0;
117 			((GF_ChunkOffsetBox *)writer->stco)->alloc_size = 0;
118 		}
119 		else {
120 			gf_free(((GF_ChunkLargeOffsetBox *)writer->stco)->offsets);
121 			((GF_ChunkLargeOffsetBox *)writer->stco)->offsets = NULL;
122 			((GF_ChunkLargeOffsetBox *)writer->stco)->nb_entries = 0;
123 			((GF_ChunkLargeOffsetBox *)writer->stco)->alloc_size = 0;
124 		}
125 	}
126 }
127 
SetupWriters(MovieWriter * mw,GF_List * writers,u8 interleaving)128 GF_Err SetupWriters(MovieWriter *mw, GF_List *writers, u8 interleaving)
129 {
130 	u32 i, trackCount;
131 	TrackWriter *writer;
132 	GF_TrackBox *trak;
133 	GF_ISOFile *movie = mw->movie;
134 
135 	mw->total_samples = mw->nb_done = 0;
136 	if (!movie->moov) return GF_OK;
137 
138 	trackCount = gf_list_count(movie->moov->trackList);
139 	for (i = 0; i < trackCount; i++) {
140 		trak = gf_isom_get_track(movie->moov, i + 1);
141 
142 		GF_SAFEALLOC(writer, TrackWriter);
143 		if (!writer) goto exit;
144 		writer->sampleNumber = 1;
145 		writer->mdia = trak->Media;
146 		writer->timeScale = trak->Media->mediaHeader->timeScale;
147 		writer->isDone = 0;
148 		writer->DTSprev = 0;
149 		writer->chunkDur = 0;
150 		writer->stsc = (GF_SampleToChunkBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_STSC);
151 		if (trak->Media->information->sampleTable->ChunkOffset->type == GF_ISOM_BOX_TYPE_STCO) {
152 			writer->stco = gf_isom_box_new(GF_ISOM_BOX_TYPE_STCO);
153 		}
154 		else {
155 			writer->stco = gf_isom_box_new(GF_ISOM_BOX_TYPE_CO64);
156 		}
157 		/*stops from chunk escape*/
158 		if (interleaving) writer->mdia->information->sampleTable->MaxSamplePerChunk = 0;
159 		/*for progress, assume only one descIndex*/
160 		if (Media_IsSelfContained(writer->mdia, 1)) mw->total_samples += trak->Media->information->sampleTable->SampleSize->sampleCount;
161 		/*optimization for interleaving: put audio last (this can be overriden by priorities)*/
162 		if (movie->storageMode != GF_ISOM_STORE_INTERLEAVED) {
163 			gf_list_add(writers, writer);
164 		}
165 		else {
166 			if (writer->mdia->information->InfoHeader && writer->mdia->information->InfoHeader->type == GF_ISOM_BOX_TYPE_SMHD) {
167 				gf_list_add(writers, writer);
168 			}
169 			else {
170 				gf_list_insert(writers, writer, 0);
171 			}
172 		}
173 	}
174 	return GF_OK;
175 
176 exit:
177 	CleanWriters(writers);
178 	return GF_OUT_OF_MEM;
179 }
180 
181 
ShiftMetaOffset(GF_MetaBox * meta,u64 offset)182 static void ShiftMetaOffset(GF_MetaBox *meta, u64 offset)
183 {
184 	u32 i, count;
185 	if (!meta->item_locations) return;
186 
187 	count = gf_list_count(meta->item_locations->location_entries);
188 	for (i = 0; i<count; i++) {
189 		GF_ItemLocationEntry *iloc = (GF_ItemLocationEntry *)gf_list_get(meta->item_locations->location_entries, i);
190 		if (iloc->data_reference_index) continue;
191 		if (iloc->construction_method == 2) continue;
192 		if (!iloc->base_offset) {
193 			GF_ItemExtentEntry *entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0);
194 			if (entry && !entry->extent_length && !entry->original_extent_offset && (gf_list_count(iloc->extent_entries) == 1))
195 				continue;
196 		}
197 
198 		iloc->base_offset += offset;
199 	}
200 }
201 
ShiftOffset(GF_ISOFile * file,GF_List * writers,u64 offset)202 static GF_Err ShiftOffset(GF_ISOFile *file, GF_List *writers, u64 offset)
203 {
204 	u32 i, j, k, l, last;
205 	TrackWriter *writer;
206 	GF_StscEntry *ent;
207 
208 	if (file->meta) ShiftMetaOffset(file->meta, offset);
209 	if (file->moov && file->moov->meta) ShiftMetaOffset(file->moov->meta, offset);
210 
211 	i = 0;
212 	while ((writer = (TrackWriter *)gf_list_enum(writers, &i))) {
213 		if (writer->mdia->mediaTrack->meta) ShiftMetaOffset(writer->mdia->mediaTrack->meta, offset);
214 
215 		//we have to proceed entry by entry in case a part of the media is not self-contained...
216 		for (j = 0; j<writer->stsc->nb_entries; j++) {
217 			ent = &writer->stsc->entries[j];
218 			if (!Media_IsSelfContained(writer->mdia, ent->sampleDescriptionIndex)) continue;
219 
220 			//OK, get the chunk(s) number(s) and "shift" its (their) offset(s).
221 			if (writer->stco->type == GF_ISOM_BOX_TYPE_STCO) {
222 				GF_ChunkLargeOffsetBox *new_stco64 = NULL;
223 				GF_ChunkOffsetBox *stco = (GF_ChunkOffsetBox *)writer->stco;
224 
225 				//be carefull for the last entry, nextChunk is set to 0 in edit mode...
226 				last = ent->nextChunk ? ent->nextChunk : stco->nb_entries + 1;
227 				for (k = ent->firstChunk; k < last; k++) {
228 
229 					//we need to rewrite the table: only allocate co64 if not done previously and convert all offsets
230 					//to co64. Then (whether co64 was created or not) adjust the offset
231 					//Do not reassign table until we are done with the current sampleToChunk processing
232 					//since we have a test on stco->offsets[k-1], we need to keep stco untouched
233 					if (new_stco64 || file->force_co64 || (stco->offsets[k - 1] + offset > 0xFFFFFFFF)) {
234 						if (!new_stco64) {
235 							new_stco64 = (GF_ChunkLargeOffsetBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_CO64);
236 							if (!new_stco64) return GF_OUT_OF_MEM;
237 							new_stco64->nb_entries = stco->nb_entries;
238 							new_stco64->offsets = (u64 *)gf_malloc(new_stco64->nb_entries * sizeof(u64));
239 							if (!new_stco64->offsets) return GF_OUT_OF_MEM;
240 							//copy over the stco table
241 							for (l = 0; l < new_stco64->nb_entries; l++) {
242 								new_stco64->offsets[l] = (u64)stco->offsets[l];
243 							}
244 						}
245 						new_stco64->offsets[k - 1] += offset;
246 					}
247 					else {
248 						stco->offsets[k - 1] += (u32)offset;
249 					}
250 				}
251 				if (new_stco64) {
252 					//done with this sampleToChunk entry, replace the box if we moved to co64
253 					gf_isom_box_del(writer->stco);
254 					writer->stco = (GF_Box *)new_stco64;
255 					new_stco64 = NULL;
256 				}
257 			}
258 			else {
259 				GF_ChunkLargeOffsetBox *stco64 = (GF_ChunkLargeOffsetBox *)writer->stco;
260 				//be carefull for the last entry ...
261 				last = ent->nextChunk ? ent->nextChunk : stco64->nb_entries + 1;
262 				for (k = ent->firstChunk; k < last; k++) {
263 					stco64->offsets[k - 1] += offset;
264 				}
265 			}
266 		}
267 	}
268 
269 	return GF_OK;
270 
271 }
272 
273 
274 //replace the chunk and offset tables...
WriteMoovAndMeta(GF_ISOFile * movie,GF_List * writers,GF_BitStream * bs)275 static GF_Err WriteMoovAndMeta(GF_ISOFile *movie, GF_List *writers, GF_BitStream *bs)
276 {
277 	u32 i;
278 	TrackWriter *writer;
279 	GF_Err e;
280 	GF_Box *stco;
281 	GF_SampleToChunkBox *stsc;
282 
283 	if (movie->meta) {
284 		//write the moov box...
285 		e = gf_isom_box_size((GF_Box *)movie->meta);
286 		if (e) return e;
287 		e = gf_isom_box_write((GF_Box *)movie->meta, bs);
288 		if (e) return e;
289 	}
290 
291 	if (movie->moov) {
292 		//switch all our tables
293 		i = 0;
294 		while ((writer = (TrackWriter*)gf_list_enum(writers, &i))) {
295 			//don't delete them !!!
296 			stsc = writer->mdia->information->sampleTable->SampleToChunk;
297 			stco = writer->mdia->information->sampleTable->ChunkOffset;
298 			writer->mdia->information->sampleTable->SampleToChunk = writer->stsc;
299 			writer->mdia->information->sampleTable->ChunkOffset = writer->stco;
300 			writer->stco = stco;
301 			writer->stsc = stsc;
302 		}
303 		//write the moov box...
304 		e = gf_isom_box_size((GF_Box *)movie->moov);
305 		if (e) return e;
306 		e = gf_isom_box_write((GF_Box *)movie->moov, bs);
307 		//and re-switch our table. We have to do it that way because it is
308 		//needed when the moov is written first
309 		i = 0;
310 		while ((writer = (TrackWriter*)gf_list_enum(writers, &i))) {
311 			//don't delete them !!!
312 			stsc = writer->stsc;
313 			stco = writer->stco;
314 			writer->stsc = writer->mdia->information->sampleTable->SampleToChunk;
315 			writer->stco = writer->mdia->information->sampleTable->ChunkOffset;
316 			writer->mdia->information->sampleTable->SampleToChunk = stsc;
317 			writer->mdia->information->sampleTable->ChunkOffset = stco;
318 		}
319 		if (e) return e;
320 	}
321 	return GF_OK;
322 }
323 
324 //compute the size of the moov as it will be written.
GetMoovAndMetaSize(GF_ISOFile * movie,GF_List * writers)325 u64 GetMoovAndMetaSize(GF_ISOFile *movie, GF_List *writers)
326 {
327 	u32 i;
328 	u64 size;
329 	TrackWriter *writer;
330 
331 	size = 0;
332 	if (movie->moov) {
333 		gf_isom_box_size((GF_Box *)movie->moov);
334 		size = movie->moov->size;
335 		if (size > 0xFFFFFFFF) size += 8;
336 
337 		i = 0;
338 		while ((writer = (TrackWriter*)gf_list_enum(writers, &i))) {
339 			size -= writer->mdia->information->sampleTable->ChunkOffset->size;
340 			size -= writer->mdia->information->sampleTable->SampleToChunk->size;
341 			gf_isom_box_size((GF_Box *)writer->stsc);
342 			gf_isom_box_size(writer->stco);
343 			size += writer->stsc->size;
344 			size += writer->stco->size;
345 		}
346 	}
347 	if (movie->meta) {
348 		u64 msize;
349 		gf_isom_box_size((GF_Box *)movie->meta);
350 		msize = movie->meta->size;
351 		if (msize > 0xFFFFFFFF) msize += 8;
352 		size += msize;
353 	}
354 	return size;
355 }
356 
357 //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)358 GF_Err WriteSample(MovieWriter *mw, u32 size, u64 offset, u8 isEdited, GF_BitStream *bs)
359 {
360 	GF_DataMap *map;
361 	u32 bytes;
362 
363 	if (!size) return GF_OK;
364 
365 	if (size>mw->size) {
366 		mw->buffer = (char*)gf_realloc(mw->buffer, size);
367 		mw->size = size;
368 	}
369 
370 	if (!mw->buffer) return GF_OUT_OF_MEM;
371 
372 	if (isEdited) {
373 		map = mw->movie->editFileMap;
374 	}
375 	else {
376 		map = mw->movie->movieFileMap;
377 	}
378 	//get the payload...
379 	bytes = gf_isom_datamap_get_data(map, mw->buffer, size, offset);
380 	if (bytes != size)
381 		return GF_IO_ERR;
382 	//write it to our stream...
383 	bytes = gf_bs_write_data(bs, mw->buffer, size);
384 	if (bytes != size)
385 		return GF_IO_ERR;
386 
387 	mw->nb_done++;
388 	gf_set_progress("ISO File Writing", mw->nb_done, mw->total_samples);
389 	return GF_OK;
390 }
391 
392 
DoWriteMeta(GF_ISOFile * file,GF_MetaBox * meta,GF_BitStream * bs,Bool Emulation,u64 baseOffset,u64 * mdatSize)393 GF_Err DoWriteMeta(GF_ISOFile *file, GF_MetaBox *meta, GF_BitStream *bs, Bool Emulation, u64 baseOffset, u64 *mdatSize)
394 {
395 	GF_ItemExtentEntry *entry;
396 	u64 maxExtendOffset, maxExtendSize;
397 	u32 i, j, count;
398 
399 	maxExtendOffset = 0;
400 	maxExtendSize = 0;
401 	*mdatSize = 0;
402 	if (!meta->item_locations) return GF_OK;
403 
404 	count = gf_list_count(meta->item_locations->location_entries);
405 	for (i = 0; i<count; i++) {
406 		u64 it_size;
407 		GF_ItemLocationEntry *iloc = (GF_ItemLocationEntry *)gf_list_get(meta->item_locations->location_entries, i);
408 		/*get item info*/
409 		GF_ItemInfoEntryBox *iinf = NULL;
410 		j = 0;
411 		while ((iinf = (GF_ItemInfoEntryBox *)gf_list_enum(meta->item_infos->item_infos, &j))) {
412 			if (iinf->item_ID == iloc->item_ID) break;
413 			iinf = NULL;
414 		}
415 
416 		if (!iloc->base_offset && (gf_list_count(iloc->extent_entries) == 1)) {
417 			entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0);
418 			if (!entry->extent_length && !entry->original_extent_offset && !entry->extent_index) {
419 				entry->extent_offset = 0;
420 				continue;
421 			}
422 		}
423 
424 		it_size = 0;
425 		/*for self contained only*/
426 		if (!iloc->data_reference_index) {
427 			if (iloc->construction_method != 2) {
428 				iloc->base_offset = baseOffset;
429 			}
430 
431 			/*new resource*/
432 			if (iinf && iinf->full_path) {
433 				FILE *src = NULL;
434 
435 				if (!iinf->data_len) {
436 					src = gf_fopen(iinf->full_path, "rb");
437 					if (!src) continue;
438 					gf_fseek(src, 0, SEEK_END);
439 					it_size = gf_ftell(src);
440 					gf_fseek(src, 0, SEEK_SET);
441 				}
442 				else {
443 					it_size = iinf->data_len;
444 				}
445 				if (maxExtendSize<it_size) maxExtendSize = it_size;
446 
447 				if (!gf_list_count(iloc->extent_entries)) {
448 					GF_SAFEALLOC(entry, GF_ItemExtentEntry);
449 					gf_list_add(iloc->extent_entries, entry);
450 				}
451 				entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0);
452 				entry->extent_offset = 0;
453 				entry->extent_length = it_size;
454 
455 				/*OK write to mdat*/
456 				if (!Emulation) {
457 					if (src) {
458 						char cache_data[4096];
459 						u64 remain = entry->extent_length;
460 						while (remain) {
461 							u32 size_cache = (remain>4096) ? 4096 : (u32)remain;
462 							size_t read = fread(cache_data, sizeof(char), size_cache, src);
463 							if (read == (size_t)-1) break;
464 							gf_bs_write_data(bs, cache_data, (u32)read);
465 							remain -= (u32)read;
466 						}
467 					}
468 					else {
469 						gf_bs_write_data(bs, iinf->full_path, iinf->data_len);
470 					}
471 				}
472 				if (src) gf_fclose(src);
473 			}
474 			else if (gf_list_count(iloc->extent_entries)) {
475 				u32 j;
476 				j = 0;
477 				while ((entry = (GF_ItemExtentEntry *)gf_list_enum(iloc->extent_entries, &j))) {
478 					if (entry->extent_index) continue;
479 					if (j && (maxExtendOffset<it_size)) maxExtendOffset = it_size;
480 					/*compute new offset*/
481 					entry->extent_offset = baseOffset + it_size;
482 
483 					it_size += entry->extent_length;
484 					if (maxExtendSize<entry->extent_length) maxExtendSize = entry->extent_length;
485 
486 					/*Reading from the input file*/
487 					if (!Emulation) {
488 						char cache_data[4096];
489 						u64 remain = entry->extent_length;
490 						gf_bs_seek(file->movieFileMap->bs, entry->original_extent_offset + iloc->original_base_offset);
491 						while (remain) {
492 							u32 size_cache = (remain>4096) ? 4096 : (u32)remain;
493 							gf_bs_read_data(file->movieFileMap->bs, cache_data, size_cache);
494 							/*Writing to the output file*/
495 							gf_bs_write_data(bs, cache_data, size_cache);
496 							remain -= size_cache;
497 						}
498 					}
499 				}
500 			}
501 			baseOffset += it_size;
502 			*mdatSize += it_size;
503 		}
504 		else {
505 			/*we MUST have at least one extent for the dref data*/
506 			if (!gf_list_count(iloc->extent_entries)) {
507 				GF_SAFEALLOC(entry, GF_ItemExtentEntry);
508 				gf_list_add(iloc->extent_entries, entry);
509 			}
510 			entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0);
511 			entry->extent_offset = 0;
512 			/*0 means full length of referenced file*/
513 			entry->extent_length = 0;
514 		}
515 	}
516 	/*update offset & size length fields*/
517 	if (baseOffset>0xFFFFFFFF) meta->item_locations->base_offset_size = 8;
518 	else if (baseOffset) meta->item_locations->base_offset_size = 4;
519 
520 	if (maxExtendSize>0xFFFFFFFF) meta->item_locations->length_size = 8;
521 	else if (maxExtendSize) meta->item_locations->length_size = 4;
522 
523 	if (maxExtendOffset>0xFFFFFFFF) meta->item_locations->offset_size = 8;
524 	else if (maxExtendOffset) meta->item_locations->offset_size = 4;
525 	return GF_OK;
526 }
527 
528 //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)529 GF_Err DoWrite(MovieWriter *mw, GF_List *writers, GF_BitStream *bs, u8 Emulation, u64 StartOffset)
530 {
531 	u32 i;
532 	GF_Err e;
533 	TrackWriter *writer;
534 	u64 offset, sampOffset, predOffset;
535 	u32 chunkNumber, descIndex, sampSize;
536 	u8 isEdited, force;
537 	u64 size, mdatSize = 0;
538 	GF_ISOFile *movie = mw->movie;
539 
540 	/*write meta content first - WE DON'T support fragmentation of resources in ISOM atm*/
541 	if (movie->openMode != GF_ISOM_OPEN_WRITE) {
542 		if (movie->meta) {
543 			e = DoWriteMeta(movie, movie->meta, bs, Emulation, StartOffset, &size);
544 			if (e) return e;
545 			mdatSize += size;
546 			StartOffset += size;
547 		}
548 		if (movie->moov && movie->moov->meta) {
549 			e = DoWriteMeta(movie, movie->meta, bs, Emulation, StartOffset, &size);
550 			if (e) return e;
551 			mdatSize += size;
552 			StartOffset += size;
553 		}
554 		i = 0;
555 		while ((writer = (TrackWriter*)gf_list_enum(writers, &i))) {
556 			if (writer->mdia->mediaTrack->meta) {
557 				e = DoWriteMeta(movie, movie->meta, bs, Emulation, StartOffset, &size);
558 				if (e) return e;
559 				mdatSize += size;
560 				StartOffset += size;
561 			}
562 		}
563 	}
564 
565 	offset = StartOffset;
566 	predOffset = 0;
567 	i = 0;
568 	while ((writer = (TrackWriter*)gf_list_enum(writers, &i))) {
569 		while (!writer->isDone) {
570 			//To Check: are empty sample tables allowed ???
571 			if (writer->sampleNumber > writer->mdia->information->sampleTable->SampleSize->sampleCount) {
572 				writer->isDone = 1;
573 				continue;
574 			}
575 			e = stbl_GetSampleInfos(writer->mdia->information->sampleTable, writer->sampleNumber, &sampOffset, &chunkNumber, &descIndex, &isEdited);
576 			if (e) return e;
577 			e = stbl_GetSampleSize(writer->mdia->information->sampleTable->SampleSize, writer->sampleNumber, &sampSize);
578 			if (e) return e;
579 
580 			//update our chunks.
581 			force = 0;
582 			if (movie->openMode == GF_ISOM_OPEN_WRITE) {
583 				offset = sampOffset;
584 				if (predOffset != offset)
585 					force = 1;
586 			}
587 			//update our global offset...
588 			if (Media_IsSelfContained(writer->mdia, descIndex)) {
589 				e = stbl_SetChunkAndOffset(writer->mdia->information->sampleTable, writer->sampleNumber, descIndex, writer->stsc, &writer->stco, offset, force);
590 				if (e) return e;
591 				if (movie->openMode == GF_ISOM_OPEN_WRITE) {
592 					predOffset = sampOffset + sampSize;
593 				}
594 				else {
595 					offset += sampSize;
596 					mdatSize += sampSize;
597 				}
598 			}
599 			else {
600 				if (predOffset != offset) force = 1;
601 				predOffset = sampOffset + sampSize;
602 				//we have a DataRef, so use the offset idicated in sampleToChunk and ChunkOffset tables...
603 				e = stbl_SetChunkAndOffset(writer->mdia->information->sampleTable, writer->sampleNumber, descIndex, writer->stsc, &writer->stco, sampOffset, force);
604 				if (e) return e;
605 			}
606 			//we write the sample if not emulation
607 			if (!Emulation) {
608 				if (Media_IsSelfContained(writer->mdia, descIndex)) {
609 					e = WriteSample(mw, sampSize, sampOffset, isEdited, bs);
610 					if (e) return e;
611 				}
612 			}
613 			//ok, the track is done
614 			if (writer->sampleNumber == writer->mdia->information->sampleTable->SampleSize->sampleCount) {
615 				writer->isDone = 1;
616 			}
617 			else {
618 				writer->sampleNumber++;
619 			}
620 		}
621 	}
622 	//set the mdatSize...
623 	movie->mdat->dataSize = mdatSize;
624 	return GF_OK;
625 }
626 
627 
628 //write the file track by track, with moov box before or after the mdat
WriteFlat(MovieWriter * mw,u8 moovFirst,GF_BitStream * bs)629 GF_Err WriteFlat(MovieWriter *mw, u8 moovFirst, GF_BitStream *bs)
630 {
631 	GF_Err e;
632 	u32 i;
633 	u64 offset, finalOffset, totSize, begin, firstSize, finalSize;
634 	GF_Box *a;
635 	GF_List *writers = gf_list_new();
636 	GF_ISOFile *movie = mw->movie;
637 
638 	begin = totSize = 0;
639 
640 	//first setup the writers
641 	e = SetupWriters(mw, writers, 0);
642 	if (e) goto exit;
643 
644 	if (!moovFirst) {
645 		if (movie->openMode == GF_ISOM_OPEN_WRITE) {
646 			begin = 0;
647 			totSize = gf_isom_datamap_get_offset(movie->editFileMap);
648 			/*start boxes have not been written yet, do it*/
649 			if (!totSize) {
650 				if (movie->is_jp2) {
651 					gf_bs_write_u32(movie->editFileMap->bs, 12);
652 					gf_bs_write_u32(movie->editFileMap->bs, GF_4CC('j', 'P', ' ', ' '));
653 					gf_bs_write_u32(movie->editFileMap->bs, 0x0D0A870A);
654 					totSize += 12;
655 					begin += 12;
656 				}
657 				if (movie->brand) {
658 					e = gf_isom_box_size((GF_Box *)movie->brand);
659 					if (e) goto exit;
660 					e = gf_isom_box_write((GF_Box *)movie->brand, movie->editFileMap->bs);
661 					if (e) goto exit;
662 					totSize += movie->brand->size;
663 					begin += movie->brand->size;
664 				}
665 				if (movie->pdin) {
666 					e = gf_isom_box_size((GF_Box *)movie->pdin);
667 					if (e) goto exit;
668 					e = gf_isom_box_write((GF_Box *)movie->pdin, movie->editFileMap->bs);
669 					if (e) goto exit;
670 					totSize += movie->pdin->size;
671 					begin += movie->pdin->size;
672 				}
673 			}
674 			else {
675 				if (movie->is_jp2) begin += 12;
676 				if (movie->brand) begin += movie->brand->size;
677 				if (movie->pdin) begin += movie->pdin->size;
678 			}
679 			totSize -= begin;
680 		}
681 		else {
682 			if (movie->is_jp2) {
683 				gf_bs_write_u32(bs, 12);
684 				gf_bs_write_u32(bs, GF_4CC('j', 'P', ' ', ' '));
685 				gf_bs_write_u32(bs, 0x0D0A870A);
686 			}
687 			if (movie->brand) {
688 				e = gf_isom_box_size((GF_Box *)movie->brand);
689 				if (e) goto exit;
690 				e = gf_isom_box_write((GF_Box *)movie->brand, bs);
691 				if (e) goto exit;
692 			}
693 			/*then progressive download*/
694 			if (movie->pdin) {
695 				e = gf_isom_box_size((GF_Box *)movie->pdin);
696 				if (e) goto exit;
697 				e = gf_isom_box_write((GF_Box *)movie->pdin, bs);
698 				if (e) goto exit;
699 			}
700 		}
701 
702 		//if the moov is at the end, write directly
703 		i = 0;
704 		while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
705 			switch (a->type) {
706 				/*written by hand*/
707 			case GF_ISOM_BOX_TYPE_MOOV:
708 			case GF_ISOM_BOX_TYPE_META:
709 			case GF_ISOM_BOX_TYPE_FTYP:
710 			case GF_ISOM_BOX_TYPE_PDIN:
711 #ifndef GPAC_DISABLE_ISOM_ADOBE
712 			case GF_ISOM_BOX_TYPE_AFRA:
713 			case GF_ISOM_BOX_TYPE_ABST:
714 #endif
715 				break;
716 			case GF_ISOM_BOX_TYPE_MDAT:
717 				//in case we're capturing
718 				if (movie->openMode == GF_ISOM_OPEN_WRITE) {
719 					//emulate a write to recreate our tables (media data already written)
720 					e = DoWrite(mw, writers, bs, 1, begin);
721 					if (e) goto exit;
722 					continue;
723 				}
724 				//to avoid computing the size each time write always 4 + 4 + 8 bytes before
725 				begin = gf_bs_get_position(bs);
726 				gf_bs_write_u64(bs, 0);
727 				gf_bs_write_u64(bs, 0);
728 				e = DoWrite(mw, writers, bs, 0, gf_bs_get_position(bs));
729 				if (e) goto exit;
730 				totSize = gf_bs_get_position(bs) - begin;
731 				break;
732 			default:
733 				e = gf_isom_box_size(a);
734 				if (e) goto exit;
735 				e = gf_isom_box_write(a, bs);
736 				if (e) goto exit;
737 				break;
738 			}
739 		}
740 
741 		//OK, write the movie box.
742 		e = WriteMoovAndMeta(movie, writers, bs);
743 		if (e) goto exit;
744 
745 #ifndef GPAC_DISABLE_ISOM_ADOBE
746 		i = 0;
747 		while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
748 			switch (a->type) {
749 			case GF_ISOM_BOX_TYPE_AFRA:
750 			case GF_ISOM_BOX_TYPE_ABST:
751 				e = gf_isom_box_size(a);
752 				if (e) goto exit;
753 				e = gf_isom_box_write(a, bs);
754 				if (e) goto exit;
755 				break;
756 			}
757 		}
758 #endif
759 
760 		/*if data has been written, update mdat size*/
761 		if (totSize) {
762 			offset = gf_bs_get_position(bs);
763 			e = gf_bs_seek(bs, begin);
764 			if (e) goto exit;
765 			if (totSize > 0xFFFFFFFF) {
766 				gf_bs_write_u32(bs, 1);
767 			}
768 			else {
769 				gf_bs_write_u32(bs, (u32)totSize);
770 			}
771 			gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_MDAT);
772 			if (totSize > 0xFFFFFFFF) gf_bs_write_u64(bs, totSize);
773 			e = gf_bs_seek(bs, offset);
774 		}
775 		movie->mdat->size = totSize;
776 		goto exit;
777 	}
778 
779 	//nope, we have to write the moov first. The pb is that
780 	//1 - we don't know its size till the mdat is written
781 	//2 - we don't know the ofset at which the mdat will start...
782 	//3 - once the mdat is written, the chunkOffset table can have changed...
783 
784 	if (movie->is_jp2) {
785 		gf_bs_write_u32(bs, 12);
786 		gf_bs_write_u32(bs, GF_4CC('j', 'P', ' ', ' '));
787 		gf_bs_write_u32(bs, 0x0D0A870A);
788 	}
789 	if (movie->brand) {
790 		e = gf_isom_box_size((GF_Box *)movie->brand);
791 		if (e) goto exit;
792 		e = gf_isom_box_write((GF_Box *)movie->brand, bs);
793 		if (e) goto exit;
794 	}
795 	/*then progressive dnload*/
796 	if (movie->pdin) {
797 		e = gf_isom_box_size((GF_Box *)movie->pdin);
798 		if (e) goto exit;
799 		e = gf_isom_box_write((GF_Box *)movie->pdin, bs);
800 		if (e) goto exit;
801 	}
802 	//What we will do is first emulate the write from the beginning...
803 	//note: this will set the size of the mdat
804 	e = DoWrite(mw, writers, bs, 1, gf_bs_get_position(bs));
805 	if (e) goto exit;
806 
807 	firstSize = GetMoovAndMetaSize(movie, writers);
808 	//offset = (firstSize > 0xFFFFFFFF ? firstSize + 8 : firstSize) + 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0);
809 	offset = firstSize + 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0);
810 	e = ShiftOffset(movie, writers, offset);
811 	if (e) goto exit;
812 	//get the size and see if it has changed (eg, we moved to 64 bit offsets)
813 	finalSize = GetMoovAndMetaSize(movie, writers);
814 	if (firstSize != finalSize) {
815 		finalOffset = finalSize + 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0);
816 		//OK, now we're sure about the final size.
817 		//we don't need to re-emulate, as the only thing that changed is the offset
818 		//so just shift the offset
819 		e = ShiftOffset(movie, writers, finalOffset - offset);
820 		if (e) goto exit;
821 	}
822 	//now write our stuff
823 	e = WriteMoovAndMeta(movie, writers, bs);
824 	if (e) goto exit;
825 	e = gf_isom_box_size((GF_Box *)movie->mdat);
826 	if (e) goto exit;
827 	e = gf_isom_box_write((GF_Box *)movie->mdat, bs);
828 	if (e) goto exit;
829 
830 	//we don't need the offset as the moov is already written...
831 	ResetWriters(writers);
832 	e = DoWrite(mw, writers, bs, 0, 0);
833 	if (e) goto exit;
834 	//then the rest
835 	i = 0;
836 	while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
837 		switch (a->type) {
838 		case GF_ISOM_BOX_TYPE_MOOV:
839 		case GF_ISOM_BOX_TYPE_META:
840 		case GF_ISOM_BOX_TYPE_FTYP:
841 		case GF_ISOM_BOX_TYPE_PDIN:
842 		case GF_ISOM_BOX_TYPE_MDAT:
843 			break;
844 		default:
845 			e = gf_isom_box_size(a);
846 			if (e) goto exit;
847 			e = gf_isom_box_write(a, bs);
848 			if (e) goto exit;
849 		}
850 	}
851 
852 exit:
853 	CleanWriters(writers);
854 	gf_list_del(writers);
855 	return e;
856 }
857 
DoFullInterleave(MovieWriter * mw,GF_List * writers,GF_BitStream * bs,u8 Emulation,u64 StartOffset)858 GF_Err DoFullInterleave(MovieWriter *mw, GF_List *writers, GF_BitStream *bs, u8 Emulation, u64 StartOffset)
859 {
860 
861 	u32 i, tracksDone;
862 	TrackWriter *tmp, *curWriter, *prevWriter;
863 	GF_Err e;
864 	u64 DTS, DTStmp, TStmp;
865 	s64 res;
866 	u32 descIndex, sampSize, chunkNumber;
867 	u16 curGroupID, curTrackPriority;
868 	u8 forceNewChunk, writeGroup, isEdited;
869 	//this is used to emulate the write ...
870 	u64 offset, totSize, sampOffset;
871 	GF_ISOFile *movie = mw->movie;
872 
873 	totSize = 0;
874 	curGroupID = 1;
875 
876 	prevWriter = NULL;
877 	//we emulate a write from this offset...
878 	offset = StartOffset;
879 	tracksDone = 0;
880 
881 	//browse each groups
882 	while (1) {
883 		writeGroup = 1;
884 
885 		//proceed a group
886 		while (writeGroup) {
887 			//first get the appropriated sample for the min time in this group
888 			curWriter = NULL;
889 			DTStmp = (u64)-1;
890 			TStmp = 0;
891 			curTrackPriority = (u16)-1;
892 
893 			i = 0;
894 			while ((tmp = (TrackWriter*)gf_list_enum(writers, &i))) {
895 
896 				//is it done writing ?
897 				//is it in our group ??
898 				if (tmp->isDone || tmp->mdia->information->sampleTable->groupID != curGroupID) continue;
899 
900 				//OK, get the current sample in this track
901 				stbl_GetSampleDTS(tmp->mdia->information->sampleTable->TimeToSample, tmp->sampleNumber, &DTS);
902 				res = TStmp ? DTStmp * tmp->timeScale - DTS * TStmp : 0;
903 				if (res < 0) continue;
904 				if ((!res) && curTrackPriority <= tmp->mdia->information->sampleTable->trackPriority) continue;
905 				curWriter = tmp;
906 				curTrackPriority = tmp->mdia->information->sampleTable->trackPriority;
907 				DTStmp = DTS;
908 				TStmp = tmp->timeScale;
909 			}
910 			//no sample found, we're done with this group
911 			if (!curWriter) {
912 				//we're done with the group
913 				writeGroup = 0;
914 				continue;
915 			}
916 			//To Check: are empty sample tables allowed ???
917 			if (curWriter->sampleNumber > curWriter->mdia->information->sampleTable->SampleSize->sampleCount) {
918 				curWriter->isDone = 1;
919 				tracksDone++;
920 				continue;
921 			}
922 
923 			e = stbl_GetSampleInfos(curWriter->mdia->information->sampleTable, curWriter->sampleNumber, &sampOffset, &chunkNumber, &descIndex, &isEdited);
924 			if (e) return e;
925 			e = stbl_GetSampleSize(curWriter->mdia->information->sampleTable->SampleSize, curWriter->sampleNumber, &sampSize);
926 			if (e) return e;
927 
928 			//do we actually write, or do we emulate ?
929 			if (Emulation) {
930 				//are we in the same track ??? If not, force a new chunk when adding this sample
931 				if (curWriter != prevWriter) {
932 					forceNewChunk = 1;
933 				}
934 				else {
935 					forceNewChunk = 0;
936 				}
937 				//update our offsets...
938 				if (Media_IsSelfContained(curWriter->mdia, descIndex)) {
939 					e = stbl_SetChunkAndOffset(curWriter->mdia->information->sampleTable, curWriter->sampleNumber, descIndex, curWriter->stsc, &curWriter->stco, offset, forceNewChunk);
940 					if (e) return e;
941 					offset += sampSize;
942 					totSize += sampSize;
943 				}
944 				else {
945 					//					if (curWriter->prev_offset != sampOffset) forceNewChunk = 1;
946 					curWriter->prev_offset = sampOffset + sampSize;
947 
948 					//we have a DataRef, so use the offset idicated in sampleToChunk
949 					//and ChunkOffset tables...
950 					e = stbl_SetChunkAndOffset(curWriter->mdia->information->sampleTable, curWriter->sampleNumber, descIndex, curWriter->stsc, &curWriter->stco, sampOffset, 0);
951 					if (e) return e;
952 				}
953 			}
954 			else {
955 				//this is no game, we're writing ....
956 				if (Media_IsSelfContained(curWriter->mdia, descIndex)) {
957 					e = WriteSample(mw, sampSize, sampOffset, isEdited, bs);
958 					if (e) return e;
959 				}
960 			}
961 			//ok, the sample is done
962 			if (curWriter->sampleNumber == curWriter->mdia->information->sampleTable->SampleSize->sampleCount) {
963 				curWriter->isDone = 1;
964 				//one more track done...
965 				tracksDone++;
966 			}
967 			else {
968 				curWriter->sampleNumber++;
969 			}
970 			prevWriter = curWriter;
971 		}
972 		//if all our track are done, break
973 		if (tracksDone == gf_list_count(writers)) break;
974 		//go to next group
975 		curGroupID++;
976 	}
977 	movie->mdat->dataSize = totSize;
978 	return GF_OK;
979 }
980 
981 /*uncomment the following to easily test large file generation. This will prepend 4096*1MByte of 0 before the media data*/
982 //#define TEST_LARGE_FILES
983 
DoInterleave(MovieWriter * mw,GF_List * writers,GF_BitStream * bs,u8 Emulation,u64 StartOffset,Bool drift_inter)984 GF_Err DoInterleave(MovieWriter *mw, GF_List *writers, GF_BitStream *bs, u8 Emulation, u64 StartOffset, Bool drift_inter)
985 {
986 	u32 i, tracksDone;
987 	TrackWriter *tmp, *curWriter;
988 	GF_Err e;
989 	u32 descIndex, sampSize, chunkNumber;
990 	u64 DTS;
991 	u16 curGroupID;
992 	u8 forceNewChunk, writeGroup, isEdited;
993 	//this is used to emulate the write ...
994 	u64 offset, sampOffset, size, mdatSize;
995 	u32 count;
996 	GF_ISOFile *movie = mw->movie;
997 	if (!movie->moov || !movie->moov->mvhd) return GF_NON_COMPLIANT_BITSTREAM;
998 
999 	mdatSize = 0;
1000 
1001 #ifdef TEST_LARGE_FILES
1002 	if (!Emulation) {
1003 		char *blank;
1004 		u32 count, i;
1005 		i = count = 0;
1006 		blank = gf_malloc(sizeof(char) * 1024 * 1024);
1007 		memset(blank, 0, sizeof(char) * 1024 * 1024);
1008 		count = 4096;
1009 		memset(blank, 0, sizeof(char) * 1024 * 1024);
1010 		while (i<count) {
1011 			u32 res = gf_bs_write_data(bs, blank, 1024 * 1024);
1012 			if (res != 1024 * 1024) fprintf(stderr, "error writing to disk: only %d bytes written\n", res);
1013 			i++;
1014 			fprintf(stderr, "writing blank block: %.02f done - %d/%d \r", (100.0*i) / count, i, count);
1015 		}
1016 		gf_free(blank);
1017 	}
1018 	mdatSize = 4096 * 1024;
1019 	mdatSize *= 1024;
1020 #endif
1021 
1022 	/*write meta content first - WE DON'T support fragmentation of resources in ISOM atm*/
1023 	if (movie->meta) {
1024 		e = DoWriteMeta(movie, movie->meta, bs, Emulation, StartOffset, &size);
1025 		if (e) return e;
1026 		mdatSize += size;
1027 		StartOffset += (u32)size;
1028 	}
1029 	if (movie->moov && movie->moov->meta) {
1030 		e = DoWriteMeta(movie, movie->moov->meta, bs, Emulation, StartOffset, &size);
1031 		if (e) return e;
1032 		mdatSize += size;
1033 		StartOffset += (u32)size;
1034 	}
1035 	i = 0;
1036 	while ((tmp = (TrackWriter*)gf_list_enum(writers, &i))) {
1037 		if (tmp->mdia->mediaTrack->meta) {
1038 			e = DoWriteMeta(movie, tmp->mdia->mediaTrack->meta, bs, Emulation, StartOffset, &size);
1039 			if (e) return e;
1040 			mdatSize += size;
1041 			StartOffset += (u32)size;
1042 		}
1043 	}
1044 
1045 
1046 
1047 	if (movie->storageMode == GF_ISOM_STORE_TIGHT)
1048 		return DoFullInterleave(mw, writers, bs, Emulation, StartOffset);
1049 
1050 	curGroupID = 1;
1051 	//we emulate a write from this offset...
1052 	offset = StartOffset;
1053 	tracksDone = 0;
1054 
1055 #ifdef TEST_LARGE_FILES
1056 	offset += mdatSize;
1057 #endif
1058 
1059 	count = gf_list_count(writers);
1060 	//browse each groups
1061 	while (1) {
1062 		/*the max DTS the chunk of the current writer*/
1063 		u64 chunkLastDTS = 0;
1064 		/*the timescale related to the max DTS*/
1065 		u32 chunkLastScale = 0;
1066 
1067 		writeGroup = 1;
1068 
1069 		//proceed a group
1070 		while (writeGroup) {
1071 			curWriter = NULL;
1072 			for (i = 0; i < count; i++) {
1073 				tmp = (TrackWriter*)gf_list_get(writers, i);
1074 
1075 				//is it done writing ?
1076 				if (tmp->isDone) continue;
1077 
1078 				//is it in our group ??
1079 				if (tmp->mdia->information->sampleTable->groupID != curGroupID) continue;
1080 
1081 				//write till this chunk is full on this track...
1082 				while (1) {
1083 					//To Check: are empty sample tables allowed ???
1084 					if (tmp->sampleNumber > tmp->mdia->information->sampleTable->SampleSize->sampleCount) {
1085 						tmp->isDone = 1;
1086 						tracksDone++;
1087 						break;
1088 					}
1089 
1090 					//OK, get the current sample in this track
1091 					stbl_GetSampleDTS(tmp->mdia->information->sampleTable->TimeToSample, tmp->sampleNumber, &DTS);
1092 
1093 					//can this sample fit in our chunk ?
1094 					if (((DTS - tmp->DTSprev) + tmp->chunkDur) *  movie->moov->mvhd->timeScale > movie->interleavingTime * tmp->timeScale
1095 						/*drfit check: reject sample if outside our check window*/
1096 						|| (drift_inter && chunkLastDTS && (((u64)tmp->DTSprev*chunkLastScale) > ((u64)chunkLastDTS*tmp->timeScale)))
1097 						) {
1098 						//in case the sample is longer than InterleaveTime
1099 						if (!tmp->chunkDur) {
1100 							forceNewChunk = 1;
1101 						}
1102 						else {
1103 							//this one is full. go to next one (exit the loop)
1104 							tmp->chunkDur = 0;
1105 							break;
1106 						}
1107 					}
1108 					else {
1109 						forceNewChunk = tmp->chunkDur ? 0 : 1;
1110 					}
1111 					//OK, we can write this track
1112 					curWriter = tmp;
1113 
1114 					//small check for first 2 samples (DTS = 0 :)
1115 					if (tmp->sampleNumber == 2 && !tmp->chunkDur) forceNewChunk = 0;
1116 
1117 					tmp->chunkDur += (u32)(DTS - tmp->DTSprev);
1118 					tmp->DTSprev = DTS;
1119 
1120 					e = stbl_GetSampleInfos(curWriter->mdia->information->sampleTable, curWriter->sampleNumber, &sampOffset, &chunkNumber, &descIndex, &isEdited);
1121 					if (e) return e;
1122 					e = stbl_GetSampleSize(curWriter->mdia->information->sampleTable->SampleSize, curWriter->sampleNumber, &sampSize);
1123 					if (e) return e;
1124 
1125 					//do we actually write, or do we emulate ?
1126 					if (Emulation) {
1127 						//update our offsets...
1128 						if (Media_IsSelfContained(curWriter->mdia, descIndex)) {
1129 							e = stbl_SetChunkAndOffset(curWriter->mdia->information->sampleTable, curWriter->sampleNumber, descIndex, curWriter->stsc, &curWriter->stco, offset, forceNewChunk);
1130 							if (e) return e;
1131 							offset += sampSize;
1132 							mdatSize += sampSize;
1133 						}
1134 						else {
1135 							if (curWriter->prev_offset != sampOffset) forceNewChunk = 1;
1136 							curWriter->prev_offset = sampOffset + sampSize;
1137 
1138 							//we have a DataRef, so use the offset idicated in sampleToChunk
1139 							//and ChunkOffset tables...
1140 							e = stbl_SetChunkAndOffset(curWriter->mdia->information->sampleTable, curWriter->sampleNumber, descIndex, curWriter->stsc, &curWriter->stco, sampOffset, forceNewChunk);
1141 							if (e) return e;
1142 						}
1143 					}
1144 					else {
1145 						//this is no game, we're writing ....
1146 						if (Media_IsSelfContained(curWriter->mdia, descIndex)) {
1147 							e = WriteSample(mw, sampSize, sampOffset, isEdited, bs);
1148 							if (e) return e;
1149 						}
1150 					}
1151 					//ok, the sample is done
1152 					if (curWriter->sampleNumber == curWriter->mdia->information->sampleTable->SampleSize->sampleCount) {
1153 						curWriter->isDone = 1;
1154 						//one more track done...
1155 						tracksDone++;
1156 						break;
1157 					}
1158 					else {
1159 						curWriter->sampleNumber++;
1160 					}
1161 				}
1162 				/*record chunk end-time & track timescale for drift-controled interleaving*/
1163 				if (drift_inter && curWriter) {
1164 					chunkLastScale = curWriter->timeScale;
1165 					chunkLastDTS = curWriter->DTSprev;
1166 					/*add one interleave window drift - since the "maxDTS" is the previously written one, we will
1167 					have the following cases:
1168 					- sample doesn't fit: post-pone and force new chunk
1169 					- sample time larger than previous chunk time + interleave: post-pone and force new chunk
1170 					- otherwise store and track becomes current reference
1171 
1172 					this ensures a proper drift regulation (max DTS diff is less than the interleaving window)
1173 					*/
1174 					chunkLastDTS += curWriter->timeScale * movie->interleavingTime / movie->moov->mvhd->timeScale;
1175 				}
1176 			}
1177 			//no sample found, we're done with this group
1178 			if (!curWriter) {
1179 				writeGroup = 0;
1180 				continue;
1181 			}
1182 		}
1183 		//if all our track are done, break
1184 		if (tracksDone == gf_list_count(writers)) break;
1185 		//go to next group
1186 		curGroupID++;
1187 	}
1188 	if (movie->mdat) movie->mdat->dataSize = mdatSize;
1189 	return GF_OK;
1190 }
1191 
1192 
WriteInterleaved(MovieWriter * mw,GF_BitStream * bs,Bool drift_inter)1193 static GF_Err WriteInterleaved(MovieWriter *mw, GF_BitStream *bs, Bool drift_inter)
1194 {
1195 	GF_Err e;
1196 	u32 i;
1197 	GF_Box *a;
1198 	u64 firstSize, finalSize, offset, finalOffset;
1199 	GF_List *writers = gf_list_new();
1200 	GF_ISOFile *movie = mw->movie;
1201 
1202 	//first setup the writers
1203 	e = SetupWriters(mw, writers, 1);
1204 	if (e) goto exit;
1205 
1206 
1207 	if (movie->is_jp2) {
1208 		gf_bs_write_u32(bs, 12);
1209 		gf_bs_write_u32(bs, GF_4CC('j', 'P', ' ', ' '));
1210 		gf_bs_write_u32(bs, 0x0D0A870A);
1211 	}
1212 	if (movie->brand) {
1213 		e = gf_isom_box_size((GF_Box *)movie->brand);
1214 		if (e) goto exit;
1215 		e = gf_isom_box_write((GF_Box *)movie->brand, bs);
1216 		if (e) goto exit;
1217 	}
1218 	if (movie->pdin) {
1219 		e = gf_isom_box_size((GF_Box *)movie->pdin);
1220 		if (e) goto exit;
1221 		e = gf_isom_box_write((GF_Box *)movie->pdin, bs);
1222 		if (e) goto exit;
1223 	}
1224 
1225 	e = DoInterleave(mw, writers, bs, 1, gf_bs_get_position(bs), drift_inter);
1226 	if (e) goto exit;
1227 
1228 	firstSize = GetMoovAndMetaSize(movie, writers);
1229 	offset = firstSize;
1230 	if (movie->mdat && movie->mdat->dataSize) offset += 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0);
1231 	e = ShiftOffset(movie, writers, offset);
1232 	if (e) goto exit;
1233 	//get the size and see if it has changed (eg, we moved to 64 bit offsets)
1234 	finalSize = GetMoovAndMetaSize(movie, writers);
1235 	if (firstSize != finalSize) {
1236 		finalOffset = finalSize;
1237 		if (movie->mdat && movie->mdat->dataSize) finalOffset += 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0);
1238 		//OK, now we're sure about the final size -> shift the offsets
1239 		//we don't need to re-emulate, as the only thing that changed is the offset
1240 		//so just shift the offset
1241 		e = ShiftOffset(movie, writers, finalOffset - offset);
1242 		if (e) goto exit;
1243 		/*firstSize = */GetMoovAndMetaSize(movie, writers);
1244 	}
1245 	//now write our stuff
1246 	e = WriteMoovAndMeta(movie, writers, bs);
1247 	if (e) goto exit;
1248 
1249 	/*we have 8 extra bytes for large size (not computed in gf_isom_box_size) */
1250 	if (movie->mdat && movie->mdat->dataSize) {
1251 		if (movie->mdat->dataSize > 0xFFFFFFFF) movie->mdat->dataSize += 8;
1252 		e = gf_isom_box_size((GF_Box *)movie->mdat);
1253 		if (e) goto exit;
1254 		e = gf_isom_box_write((GF_Box *)movie->mdat, bs);
1255 		if (e) goto exit;
1256 	}
1257 
1258 	//we don't need the offset as we are writing...
1259 	ResetWriters(writers);
1260 	e = DoInterleave(mw, writers, bs, 0, 0, drift_inter);
1261 	if (e) goto exit;
1262 
1263 	//then the rest
1264 	i = 0;
1265 	while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
1266 		switch (a->type) {
1267 		case GF_ISOM_BOX_TYPE_MOOV:
1268 		case GF_ISOM_BOX_TYPE_META:
1269 		case GF_ISOM_BOX_TYPE_FTYP:
1270 		case GF_ISOM_BOX_TYPE_PDIN:
1271 		case GF_ISOM_BOX_TYPE_MDAT:
1272 			break;
1273 		default:
1274 			e = gf_isom_box_size(a);
1275 			if (e) goto exit;
1276 			e = gf_isom_box_write(a, bs);
1277 			if (e) goto exit;
1278 		}
1279 	}
1280 
1281 exit:
1282 	CleanWriters(writers);
1283 	gf_list_del(writers);
1284 	return e;
1285 }
1286 
1287 extern u32 default_write_buffering_size;
1288 
WriteToFile(GF_ISOFile * movie)1289 GF_Err WriteToFile(GF_ISOFile *movie)
1290 {
1291 	FILE *stream;
1292 	GF_BitStream *bs;
1293 	MovieWriter mw;
1294 	GF_Err e = GF_OK;
1295 	if (!movie) return GF_BAD_PARAM;
1296 
1297 	if (movie->openMode == GF_ISOM_OPEN_READ) return GF_BAD_PARAM;
1298 
1299 	e = gf_isom_insert_copyright(movie);
1300 	if (e) return e;
1301 
1302 	memset(&mw, 0, sizeof(mw));
1303 	mw.movie = movie;
1304 
1305 	if (movie->drop_date_version_info) {
1306 		u32 i;
1307 		GF_TrackBox *trak;
1308 		movie->moov->mvhd->creationTime = 0;
1309 		movie->moov->mvhd->modificationTime = 0;
1310 		i = 0;
1311 		while ((trak = gf_list_enum(movie->moov->trackList, &i))) {
1312 			trak->Header->creationTime = 0;
1313 			trak->Header->modificationTime = 0;
1314 			if (trak->Media->handler->nameUTF8 && strstr(trak->Media->handler->nameUTF8, "@GPAC")) {
1315 				gf_free(trak->Media->handler->nameUTF8);
1316 				trak->Media->handler->nameUTF8 = gf_strdup("MediaHandler");
1317 			}
1318 			trak->Media->mediaHeader->creationTime = 0;
1319 			trak->Media->mediaHeader->modificationTime = 0;
1320 		}
1321 	}
1322 
1323 	//capture mode: we don't need a new bitstream
1324 	if (movie->openMode == GF_ISOM_OPEN_WRITE) {
1325 		e = WriteFlat(&mw, 0, movie->editFileMap->bs);
1326 	}
1327 	else {
1328 		u32 buffer_size = movie->editFileMap ? gf_bs_get_output_buffering(movie->editFileMap->bs) : 0;
1329 		Bool is_stdout = 0;
1330 		if (!strcmp(movie->finalName, "std"))
1331 			is_stdout = 1;
1332 
1333 		//OK, we need a new bitstream
1334 		stream = is_stdout ? stdout : gf_fopen(movie->finalName, "w+b");
1335 		if (!stream)
1336 			return GF_IO_ERR;
1337 		bs = gf_bs_from_file(stream, GF_BITSTREAM_WRITE);
1338 		if (!bs) {
1339 			if (!is_stdout)
1340 				gf_fclose(stream);
1341 			return GF_OUT_OF_MEM;
1342 		}
1343 
1344 		if (buffer_size) {
1345 			gf_bs_set_output_buffering(bs, buffer_size);
1346 		}
1347 
1348 		if (!movie->moov) {
1349 			/* in case of file with only a meta box, we force a flat storage */
1350 			movie->storageMode = GF_ISOM_STORE_FLAT;
1351 		}
1352 
1353 		switch (movie->storageMode) {
1354 		case GF_ISOM_STORE_TIGHT:
1355 		case GF_ISOM_STORE_INTERLEAVED:
1356 			e = WriteInterleaved(&mw, bs, 0);
1357 			break;
1358 		case GF_ISOM_STORE_DRIFT_INTERLEAVED:
1359 			e = WriteInterleaved(&mw, bs, 1);
1360 			break;
1361 		case GF_ISOM_STORE_STREAMABLE:
1362 			e = WriteFlat(&mw, 1, bs);
1363 			break;
1364 		default:
1365 			e = WriteFlat(&mw, 0, bs);
1366 			break;
1367 		}
1368 
1369 		gf_bs_del(bs);
1370 		if (!is_stdout)
1371 			gf_fclose(stream);
1372 	}
1373 	if (mw.buffer) gf_free(mw.buffer);
1374 	if (mw.nb_done<mw.total_samples) {
1375 		gf_set_progress("ISO File Writing", mw.total_samples, mw.total_samples);
1376 	}
1377 	return e;
1378 }
1379 
1380 
1381 
1382 #endif	/*!defined(GPAC_DISABLE_ISOM) && !defined(GPAC_DISABLE_ISOM_WRITE)*/
1383