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