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