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