This source file includes following definitions.
- gf_isom_insert_copyright
- CleanWriters
- ResetWriters
- SetupWriters
- ShiftMetaOffset
- ShiftOffset
- WriteMoovAndMeta
- GetMoovAndMetaSize
- WriteSample
- DoWriteMeta
- DoWrite
- WriteFlat
- DoFullInterleave
- DoInterleave
- WriteInterleaved
- WriteToFile
#include <gpac/internal/isomedia_dev.h>
#if !defined(GPAC_DISABLE_ISOM) && !defined(GPAC_DISABLE_ISOM_WRITE)
#define GPAC_ISOM_CPRT_NOTICE "IsoMedia File Produced with GPAC"
#define GPAC_ISOM_CPRT_NOTICE_VERSION GPAC_ISOM_CPRT_NOTICE" "GPAC_FULL_VERSION
static GF_Err gf_isom_insert_copyright(GF_ISOFile *movie)
{
u32 i;
GF_Box *a;
GF_FreeSpaceBox *_free;
i=0;
while ((a = (GF_Box *)gf_list_enum(movie->TopBoxes, &i))) {
if (a->type == GF_ISOM_BOX_TYPE_FREE) {
_free = (GF_FreeSpaceBox *)a;
if (_free->dataSize) {
if (!strcmp(_free->data, GPAC_ISOM_CPRT_NOTICE_VERSION)) return GF_OK;
if (strstr(_free->data, GPAC_ISOM_CPRT_NOTICE)) {
gf_free(_free->data);
_free->data = gf_strdup(movie->drop_date_version_info ? GPAC_ISOM_CPRT_NOTICE : GPAC_ISOM_CPRT_NOTICE_VERSION);
_free->dataSize = 1 + (u32) strlen(_free->data);
return GF_OK;
}
}
}
}
a = gf_isom_box_new(GF_ISOM_BOX_TYPE_FREE);
if (!a) return GF_OUT_OF_MEM;
_free = (GF_FreeSpaceBox *)a;
_free->data = gf_strdup(movie->drop_date_version_info ? GPAC_ISOM_CPRT_NOTICE : GPAC_ISOM_CPRT_NOTICE_VERSION);
_free->dataSize = (u32) strlen(_free->data) + 1;
if (!_free->data) return GF_OUT_OF_MEM;
return gf_list_add(movie->TopBoxes, _free);
}
typedef struct
{
u32 sampleNumber;
u32 timeScale;
u32 chunkDur;
u64 DTSprev;
u8 isDone;
u64 prev_offset;
GF_MediaBox *mdia;
GF_SampleToChunkBox *stsc;
GF_Box *stco;
} TrackWriter;
typedef struct
{
char *buffer;
u32 size;
GF_ISOFile *movie;
u32 total_samples, nb_done;
} MovieWriter;
void CleanWriters(GF_List *writers)
{
TrackWriter *writer;
while (gf_list_count(writers)) {
writer = (TrackWriter*)gf_list_get(writers, 0);
gf_isom_box_del(writer->stco);
gf_isom_box_del((GF_Box *)writer->stsc);
gf_free(writer);
gf_list_rem(writers, 0);
}
}
void ResetWriters(GF_List *writers)
{
u32 i;
TrackWriter *writer;
i=0;
while ((writer = (TrackWriter *)gf_list_enum(writers, &i))) {
writer->isDone = 0;
writer->chunkDur = 0;
writer->DTSprev = 0;
writer->sampleNumber = 1;
gf_isom_box_del((GF_Box *)writer->stsc);
writer->stsc = (GF_SampleToChunkBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSC);
if (writer->stco->type == GF_ISOM_BOX_TYPE_STCO) {
gf_free(((GF_ChunkOffsetBox *)writer->stco)->offsets);
((GF_ChunkOffsetBox *)writer->stco)->offsets = NULL;
((GF_ChunkOffsetBox *)writer->stco)->nb_entries = 0;
((GF_ChunkOffsetBox *)writer->stco)->alloc_size = 0;
} else {
gf_free(((GF_ChunkLargeOffsetBox *)writer->stco)->offsets);
((GF_ChunkLargeOffsetBox *)writer->stco)->offsets = NULL;
((GF_ChunkLargeOffsetBox *)writer->stco)->nb_entries = 0;
((GF_ChunkLargeOffsetBox *)writer->stco)->alloc_size = 0;
}
}
}
GF_Err SetupWriters(MovieWriter *mw, GF_List *writers, u8 interleaving)
{
u32 i, trackCount;
TrackWriter *writer;
GF_TrackBox *trak;
GF_ISOFile *movie = mw->movie;
mw->total_samples = mw->nb_done = 0;
if (!movie->moov) return GF_OK;
trackCount = gf_list_count(movie->moov->trackList);
for (i = 0; i < trackCount; i++) {
trak = gf_isom_get_track(movie->moov, i+1);
GF_SAFEALLOC(writer, TrackWriter);
if (!writer) goto exit;
writer->sampleNumber = 1;
writer->mdia = trak->Media;
writer->timeScale = trak->Media->mediaHeader->timeScale;
writer->isDone = 0;
writer->DTSprev = 0;
writer->chunkDur = 0;
writer->stsc = (GF_SampleToChunkBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSC);
if (trak->Media->information->sampleTable->ChunkOffset->type == GF_ISOM_BOX_TYPE_STCO) {
writer->stco = gf_isom_box_new(GF_ISOM_BOX_TYPE_STCO);
} else {
writer->stco = gf_isom_box_new(GF_ISOM_BOX_TYPE_CO64);
}
if (interleaving) writer->mdia->information->sampleTable->MaxSamplePerChunk = 0;
if (Media_IsSelfContained(writer->mdia, 1)) mw->total_samples += trak->Media->information->sampleTable->SampleSize->sampleCount;
if (movie->storageMode != GF_ISOM_STORE_INTERLEAVED) {
gf_list_add(writers, writer);
} else {
if (writer->mdia->information->InfoHeader && writer->mdia->information->InfoHeader->type == GF_ISOM_BOX_TYPE_SMHD) {
gf_list_add(writers, writer);
} else {
gf_list_insert(writers, writer, 0);
}
}
}
return GF_OK;
exit:
CleanWriters(writers);
return GF_OUT_OF_MEM;
}
static void ShiftMetaOffset(GF_MetaBox *meta, u64 offset)
{
u32 i, count;
if (!meta->item_locations) return;
count = gf_list_count(meta->item_locations->location_entries);
for (i=0; i<count; i++) {
GF_ItemLocationEntry *iloc = (GF_ItemLocationEntry *)gf_list_get(meta->item_locations->location_entries, i);
if (iloc->data_reference_index) continue;
if (iloc->construction_method == 2) continue;
if (!iloc->base_offset) {
GF_ItemExtentEntry *entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0);
if (entry && !entry->extent_length && !entry->original_extent_offset && (gf_list_count(iloc->extent_entries)==1) )
continue;
}
iloc->base_offset += offset;
}
}
static GF_Err ShiftOffset(GF_ISOFile *file, GF_List *writers, u64 offset)
{
u32 i, j, k, l, last;
TrackWriter *writer;
GF_StscEntry *ent;
if (file->meta) ShiftMetaOffset(file->meta, offset);
if (file->moov && file->moov->meta) ShiftMetaOffset(file->moov->meta, offset);
i=0;
while ((writer = (TrackWriter *)gf_list_enum(writers, &i))) {
if (writer->mdia->mediaTrack->meta) ShiftMetaOffset(writer->mdia->mediaTrack->meta, offset);
for (j=0; j<writer->stsc->nb_entries; j++) {
ent = &writer->stsc->entries[j];
if (!Media_IsSelfContained(writer->mdia, ent->sampleDescriptionIndex)) continue;
if (writer->stco->type == GF_ISOM_BOX_TYPE_STCO) {
GF_ChunkLargeOffsetBox *new_stco64 = NULL;
GF_ChunkOffsetBox *stco = (GF_ChunkOffsetBox *) writer->stco;
last = ent->nextChunk ? ent->nextChunk : stco->nb_entries + 1;
for (k = ent->firstChunk; k < last; k++) {
if (new_stco64 || file->force_co64 || (stco->offsets[k-1] + offset > 0xFFFFFFFF)) {
if (!new_stco64) {
new_stco64 = (GF_ChunkLargeOffsetBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_CO64);
if (!new_stco64) return GF_OUT_OF_MEM;
new_stco64->nb_entries = stco->nb_entries;
new_stco64->offsets = (u64 *) gf_malloc(new_stco64->nb_entries * sizeof(u64));
if (!new_stco64->offsets) return GF_OUT_OF_MEM;
for (l = 0; l < new_stco64->nb_entries; l++) {
new_stco64->offsets[l] = (u64) stco->offsets[l];
}
}
new_stco64->offsets[k-1] += offset;
} else {
stco->offsets[k-1] += (u32) offset;
}
}
if (new_stco64) {
gf_isom_box_del(writer->stco);
writer->stco = (GF_Box *)new_stco64;
new_stco64 = NULL;
}
} else {
GF_ChunkLargeOffsetBox *stco64 = (GF_ChunkLargeOffsetBox *) writer->stco;
last = ent->nextChunk ? ent->nextChunk : stco64->nb_entries + 1;
for (k = ent->firstChunk; k < last; k++) {
stco64->offsets[k-1] += offset;
}
}
}
}
return GF_OK;
}
static GF_Err WriteMoovAndMeta(GF_ISOFile *movie, GF_List *writers, GF_BitStream *bs)
{
u32 i;
TrackWriter *writer;
GF_Err e;
GF_Box *stco;
GF_SampleToChunkBox *stsc;
if (movie->meta) {
e = gf_isom_box_size((GF_Box *)movie->meta);
if (e) return e;
e = gf_isom_box_write((GF_Box *)movie->meta, bs);
if (e) return e;
}
if (movie->moov) {
i=0;
while ((writer = (TrackWriter*)gf_list_enum(writers, &i))) {
stsc = writer->mdia->information->sampleTable->SampleToChunk;
stco = writer->mdia->information->sampleTable->ChunkOffset;
writer->mdia->information->sampleTable->SampleToChunk = writer->stsc;
writer->mdia->information->sampleTable->ChunkOffset = writer->stco;
writer->stco = stco;
writer->stsc = stsc;
}
e = gf_isom_box_size((GF_Box *)movie->moov);
if (e) return e;
e = gf_isom_box_write((GF_Box *)movie->moov, bs);
i=0;
while ((writer = (TrackWriter*)gf_list_enum(writers, &i))) {
stsc = writer->stsc;
stco = writer->stco;
writer->stsc = writer->mdia->information->sampleTable->SampleToChunk;
writer->stco = writer->mdia->information->sampleTable->ChunkOffset;
writer->mdia->information->sampleTable->SampleToChunk = stsc;
writer->mdia->information->sampleTable->ChunkOffset = stco;
}
if (e) return e;
}
return GF_OK;
}
u64 GetMoovAndMetaSize(GF_ISOFile *movie, GF_List *writers)
{
u32 i;
u64 size;
TrackWriter *writer;
size = 0;
if (movie->moov) {
gf_isom_box_size((GF_Box *)movie->moov);
size = movie->moov->size;
if (size > 0xFFFFFFFF) size += 8;
i=0;
while ((writer = (TrackWriter*)gf_list_enum(writers, &i))) {
size -= writer->mdia->information->sampleTable->ChunkOffset->size;
size -= writer->mdia->information->sampleTable->SampleToChunk->size;
gf_isom_box_size((GF_Box *)writer->stsc);
gf_isom_box_size(writer->stco);
size += writer->stsc->size;
size += writer->stco->size;
}
}
if (movie->meta) {
u64 msize;
gf_isom_box_size((GF_Box *)movie->meta);
msize = movie->meta->size;
if (msize > 0xFFFFFFFF) msize += 8;
size += msize;
}
return size;
}
GF_Err WriteSample(MovieWriter *mw, u32 size, u64 offset, u8 isEdited, GF_BitStream *bs)
{
GF_DataMap *map;
u32 bytes;
if (!size) return GF_OK;
if (size>mw->size) {
mw->buffer = (char*)gf_realloc(mw->buffer, size);
mw->size = size;
}
if (!mw->buffer) return GF_OUT_OF_MEM;
if (isEdited) {
map = mw->movie->editFileMap;
} else {
map = mw->movie->movieFileMap;
}
bytes = gf_isom_datamap_get_data(map, mw->buffer, size, offset);
if (bytes != size)
return GF_IO_ERR;
bytes = gf_bs_write_data(bs, mw->buffer, size);
if (bytes != size)
return GF_IO_ERR;
mw->nb_done++;
gf_set_progress("ISO File Writing", mw->nb_done, mw->total_samples);
return GF_OK;
}
GF_Err DoWriteMeta(GF_ISOFile *file, GF_MetaBox *meta, GF_BitStream *bs, Bool Emulation, u64 baseOffset, u64 *mdatSize)
{
GF_ItemExtentEntry *entry;
u64 maxExtendOffset, maxExtendSize;
u32 i, j, count;
maxExtendOffset = 0;
maxExtendSize = 0;
*mdatSize = 0;
if (!meta->item_locations) return GF_OK;
count = gf_list_count(meta->item_locations->location_entries);
for (i=0; i<count; i++) {
u64 it_size;
GF_ItemLocationEntry *iloc = (GF_ItemLocationEntry *)gf_list_get(meta->item_locations->location_entries, i);
GF_ItemInfoEntryBox *iinf = NULL;
j=0;
while ((iinf = (GF_ItemInfoEntryBox *)gf_list_enum(meta->item_infos->item_infos, &j))) {
if (iinf->item_ID==iloc->item_ID) break;
iinf = NULL;
}
if (!iloc->base_offset && (gf_list_count(iloc->extent_entries)==1)) {
entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0);
if (!entry->extent_length && !entry->original_extent_offset && !entry->extent_index) {
entry->extent_offset = 0;
continue;
}
}
it_size = 0;
if (!iloc->data_reference_index) {
if (iloc->construction_method != 2) {
iloc->base_offset = baseOffset;
}
if (iinf && iinf->full_path) {
FILE *src=NULL;
if (!iinf->data_len) {
src = gf_fopen(iinf->full_path, "rb");
if (!src) continue;
gf_fseek(src, 0, SEEK_END);
it_size = gf_ftell(src);
gf_fseek(src, 0, SEEK_SET);
} else {
it_size = iinf->data_len;
}
if (maxExtendSize<it_size) maxExtendSize = it_size;
if (!gf_list_count(iloc->extent_entries)) {
GF_SAFEALLOC(entry, GF_ItemExtentEntry);
gf_list_add(iloc->extent_entries, entry);
}
entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0);
entry->extent_offset = 0;
entry->extent_length = it_size;
if (!Emulation) {
if (src) {
char cache_data[4096];
u64 remain = entry->extent_length;
while (remain) {
u32 size_cache = (remain>4096) ? 4096 : (u32) remain;
size_t read = fread(cache_data, sizeof(char), size_cache, src);
if (read ==(size_t) -1) break;
gf_bs_write_data(bs, cache_data, (u32) read);
remain -= (u32) read;
}
} else {
gf_bs_write_data(bs, iinf->full_path, iinf->data_len);
}
}
if (src) gf_fclose(src);
}
else if (gf_list_count(iloc->extent_entries)) {
u32 j;
j=0;
while ((entry = (GF_ItemExtentEntry *)gf_list_enum(iloc->extent_entries, &j))) {
if (entry->extent_index) continue;
if (j && (maxExtendOffset<it_size) ) maxExtendOffset = it_size;
entry->extent_offset = baseOffset + it_size;
it_size += entry->extent_length;
if (maxExtendSize<entry->extent_length) maxExtendSize = entry->extent_length;
if (!Emulation) {
char cache_data[4096];
u64 remain = entry->extent_length;
gf_bs_seek(file->movieFileMap->bs, entry->original_extent_offset + iloc->original_base_offset);
while (remain) {
u32 size_cache = (remain>4096) ? 4096 : (u32) remain;
gf_bs_read_data(file->movieFileMap->bs, cache_data, size_cache);
gf_bs_write_data(bs, cache_data, size_cache);
remain -= size_cache;
}
}
}
}
baseOffset += it_size;
*mdatSize += it_size;
} else {
if (!gf_list_count(iloc->extent_entries)) {
GF_SAFEALLOC(entry, GF_ItemExtentEntry);
gf_list_add(iloc->extent_entries, entry);
}
entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0);
entry->extent_offset = 0;
entry->extent_length = 0;
}
}
if (baseOffset>0xFFFFFFFF) meta->item_locations->base_offset_size = 8;
else if (baseOffset) meta->item_locations->base_offset_size = 4;
if (maxExtendSize>0xFFFFFFFF) meta->item_locations->length_size = 8;
else if (maxExtendSize) meta->item_locations->length_size = 4;
if (maxExtendOffset>0xFFFFFFFF) meta->item_locations->offset_size = 8;
else if (maxExtendOffset) meta->item_locations->offset_size = 4;
return GF_OK;
}
GF_Err DoWrite(MovieWriter *mw, GF_List *writers, GF_BitStream *bs, u8 Emulation, u64 StartOffset)
{
u32 i;
GF_Err e;
TrackWriter *writer;
u64 offset, sampOffset, predOffset;
u32 chunkNumber, descIndex, sampSize;
u8 isEdited, force;
u64 size, mdatSize = 0;
GF_ISOFile *movie = mw->movie;
if (movie->openMode != GF_ISOM_OPEN_WRITE) {
if (movie->meta) {
e = DoWriteMeta(movie, movie->meta, bs, Emulation, StartOffset, &size);
if (e) return e;
mdatSize += size;
StartOffset += size;
}
if (movie->moov && movie->moov->meta) {
e = DoWriteMeta(movie, movie->meta, bs, Emulation, StartOffset, &size);
if (e) return e;
mdatSize += size;
StartOffset += size;
}
i=0;
while ((writer = (TrackWriter*)gf_list_enum(writers, &i))) {
if (writer->mdia->mediaTrack->meta) {
e = DoWriteMeta(movie, movie->meta, bs, Emulation, StartOffset, &size);
if (e) return e;
mdatSize += size;
StartOffset += size;
}
}
}
offset = StartOffset;
predOffset = 0;
i=0;
while ((writer = (TrackWriter*)gf_list_enum(writers, &i))) {
while (!writer->isDone) {
if (writer->sampleNumber > writer->mdia->information->sampleTable->SampleSize->sampleCount) {
writer->isDone = 1;
continue;
}
e = stbl_GetSampleInfos(writer->mdia->information->sampleTable, writer->sampleNumber, &sampOffset, &chunkNumber, &descIndex, &isEdited);
if (e) return e;
e = stbl_GetSampleSize(writer->mdia->information->sampleTable->SampleSize, writer->sampleNumber, &sampSize);
if (e) return e;
force = 0;
if (movie->openMode == GF_ISOM_OPEN_WRITE) {
offset = sampOffset;
if (predOffset != offset)
force = 1;
}
if (Media_IsSelfContained(writer->mdia, descIndex) ) {
e = stbl_SetChunkAndOffset(writer->mdia->information->sampleTable, writer->sampleNumber, descIndex, writer->stsc, &writer->stco, offset, force);
if (e) return e;
if (movie->openMode == GF_ISOM_OPEN_WRITE) {
predOffset = sampOffset + sampSize;
} else {
offset += sampSize;
mdatSize += sampSize;
}
} else {
if (predOffset != offset) force = 1;
predOffset = sampOffset + sampSize;
e = stbl_SetChunkAndOffset(writer->mdia->information->sampleTable, writer->sampleNumber, descIndex, writer->stsc, &writer->stco, sampOffset, force);
if (e) return e;
}
if (!Emulation) {
if (Media_IsSelfContained(writer->mdia, descIndex) ) {
e = WriteSample(mw, sampSize, sampOffset, isEdited, bs);
if (e) return e;
}
}
if (writer->sampleNumber == writer->mdia->information->sampleTable->SampleSize->sampleCount) {
writer->isDone = 1;
} else {
writer->sampleNumber ++;
}
}
}
movie->mdat->dataSize = mdatSize;
return GF_OK;
}
GF_Err WriteFlat(MovieWriter *mw, u8 moovFirst, GF_BitStream *bs)
{
GF_Err e;
u32 i;
u64 offset, finalOffset, totSize, begin, firstSize, finalSize;
GF_Box *a;
GF_List *writers = gf_list_new();
GF_ISOFile *movie = mw->movie;
begin = totSize = 0;
e = SetupWriters(mw, writers, 0);
if (e) goto exit;
if (!moovFirst) {
if (movie->openMode == GF_ISOM_OPEN_WRITE) {
begin = 0;
totSize = gf_isom_datamap_get_offset(movie->editFileMap);
if (!totSize) {
if (movie->is_jp2) {
gf_bs_write_u32(movie->editFileMap->bs, 12);
gf_bs_write_u32(movie->editFileMap->bs, GF_4CC('j','P',' ',' '));
gf_bs_write_u32(movie->editFileMap->bs, 0x0D0A870A);
totSize += 12;
begin += 12;
}
if (movie->brand) {
e = gf_isom_box_size((GF_Box *)movie->brand);
if (e) goto exit;
e = gf_isom_box_write((GF_Box *)movie->brand, movie->editFileMap->bs);
if (e) goto exit;
totSize += movie->brand->size;
begin += movie->brand->size;
}
if (movie->pdin) {
e = gf_isom_box_size((GF_Box *)movie->pdin);
if (e) goto exit;
e = gf_isom_box_write((GF_Box *)movie->pdin, movie->editFileMap->bs);
if (e) goto exit;
totSize += movie->pdin->size;
begin += movie->pdin->size;
}
} else {
if (movie->is_jp2) begin += 12;
if (movie->brand) begin += movie->brand->size;
if (movie->pdin) begin += movie->pdin->size;
}
totSize -= begin;
} else {
if (movie->is_jp2) {
gf_bs_write_u32(bs, 12);
gf_bs_write_u32(bs, GF_4CC('j','P',' ',' '));
gf_bs_write_u32(bs, 0x0D0A870A);
}
if (movie->brand) {
e = gf_isom_box_size((GF_Box *)movie->brand);
if (e) goto exit;
e = gf_isom_box_write((GF_Box *)movie->brand, bs);
if (e) goto exit;
}
if (movie->pdin) {
e = gf_isom_box_size((GF_Box *)movie->pdin);
if (e) goto exit;
e = gf_isom_box_write((GF_Box *)movie->pdin, bs);
if (e) goto exit;
}
}
i=0;
while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
switch (a->type) {
case GF_ISOM_BOX_TYPE_MOOV:
case GF_ISOM_BOX_TYPE_META:
case GF_ISOM_BOX_TYPE_FTYP:
case GF_ISOM_BOX_TYPE_PDIN:
#ifndef GPAC_DISABLE_ISOM_ADOBE
case GF_ISOM_BOX_TYPE_AFRA:
case GF_ISOM_BOX_TYPE_ABST:
#endif
break;
case GF_ISOM_BOX_TYPE_MDAT:
if (movie->openMode == GF_ISOM_OPEN_WRITE) {
e = DoWrite(mw, writers, bs, 1, begin);
if (e) goto exit;
continue;
}
begin = gf_bs_get_position(bs);
gf_bs_write_u64(bs, 0);
gf_bs_write_u64(bs, 0);
e = DoWrite(mw, writers, bs, 0, gf_bs_get_position(bs));
if (e) goto exit;
totSize = gf_bs_get_position(bs) - begin;
break;
default:
e = gf_isom_box_size(a);
if (e) goto exit;
e = gf_isom_box_write(a, bs);
if (e) goto exit;
break;
}
}
e = WriteMoovAndMeta(movie, writers, bs);
if (e) goto exit;
#ifndef GPAC_DISABLE_ISOM_ADOBE
i=0;
while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
switch (a->type) {
case GF_ISOM_BOX_TYPE_AFRA:
case GF_ISOM_BOX_TYPE_ABST:
e = gf_isom_box_size(a);
if (e) goto exit;
e = gf_isom_box_write(a, bs);
if (e) goto exit;
break;
}
}
#endif
if (totSize) {
offset = gf_bs_get_position(bs);
e = gf_bs_seek(bs, begin);
if (e) goto exit;
if (totSize > 0xFFFFFFFF) {
gf_bs_write_u32(bs, 1);
} else {
gf_bs_write_u32(bs, (u32) totSize);
}
gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_MDAT);
if (totSize > 0xFFFFFFFF) gf_bs_write_u64(bs, totSize);
e = gf_bs_seek(bs, offset);
}
movie->mdat->size = totSize;
goto exit;
}
if (movie->is_jp2) {
gf_bs_write_u32(bs, 12);
gf_bs_write_u32(bs, GF_4CC('j','P',' ',' '));
gf_bs_write_u32(bs, 0x0D0A870A);
}
if (movie->brand) {
e = gf_isom_box_size((GF_Box *)movie->brand);
if (e) goto exit;
e = gf_isom_box_write((GF_Box *)movie->brand, bs);
if (e) goto exit;
}
if (movie->pdin) {
e = gf_isom_box_size((GF_Box *)movie->pdin);
if (e) goto exit;
e = gf_isom_box_write((GF_Box *)movie->pdin, bs);
if (e) goto exit;
}
e = DoWrite(mw, writers, bs, 1, gf_bs_get_position(bs));
if (e) goto exit;
firstSize = GetMoovAndMetaSize(movie, writers);
offset = firstSize + 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0);
e = ShiftOffset(movie, writers, offset);
if (e) goto exit;
finalSize = GetMoovAndMetaSize(movie, writers);
if (firstSize != finalSize) {
finalOffset = finalSize + 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0);
e = ShiftOffset(movie, writers, finalOffset - offset);
if (e) goto exit;
}
e = WriteMoovAndMeta(movie, writers, bs);
if (e) goto exit;
e = gf_isom_box_size((GF_Box *)movie->mdat);
if (e) goto exit;
e = gf_isom_box_write((GF_Box *)movie->mdat, bs);
if (e) goto exit;
ResetWriters(writers);
e = DoWrite(mw, writers, bs, 0, 0);
if (e) goto exit;
i=0;
while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
switch (a->type) {
case GF_ISOM_BOX_TYPE_MOOV:
case GF_ISOM_BOX_TYPE_META:
case GF_ISOM_BOX_TYPE_FTYP:
case GF_ISOM_BOX_TYPE_PDIN:
case GF_ISOM_BOX_TYPE_MDAT:
break;
default:
e = gf_isom_box_size(a);
if (e) goto exit;
e = gf_isom_box_write(a, bs);
if (e) goto exit;
}
}
exit:
CleanWriters(writers);
gf_list_del(writers);
return e;
}
GF_Err DoFullInterleave(MovieWriter *mw, GF_List *writers, GF_BitStream *bs, u8 Emulation, u64 StartOffset)
{
u32 i, tracksDone;
TrackWriter *tmp, *curWriter, *prevWriter;
GF_Err e;
u64 DTS, DTStmp, TStmp;
s64 res;
u32 descIndex, sampSize, chunkNumber;
u16 curGroupID, curTrackPriority;
u8 forceNewChunk, writeGroup, isEdited;
u64 offset, totSize, sampOffset;
GF_ISOFile *movie = mw->movie;
totSize = 0;
curGroupID = 1;
prevWriter = NULL;
offset = StartOffset;
tracksDone = 0;
while (1) {
writeGroup = 1;
while (writeGroup) {
curWriter = NULL;
DTStmp = (u64) -1;
TStmp = 0;
curTrackPriority = (u16) -1;
i=0;
while ((tmp = (TrackWriter*)gf_list_enum(writers, &i))) {
if (tmp->isDone || tmp->mdia->information->sampleTable->groupID != curGroupID) continue;
stbl_GetSampleDTS(tmp->mdia->information->sampleTable->TimeToSample, tmp->sampleNumber, &DTS);
res = TStmp ? DTStmp * tmp->timeScale - DTS * TStmp : 0;
if (res < 0) continue;
if ((!res) && curTrackPriority <= tmp->mdia->information->sampleTable->trackPriority) continue;
curWriter = tmp;
curTrackPriority = tmp->mdia->information->sampleTable->trackPriority;
DTStmp = DTS;
TStmp = tmp->timeScale;
}
if (!curWriter) {
writeGroup = 0;
continue;
}
if (curWriter->sampleNumber > curWriter->mdia->information->sampleTable->SampleSize->sampleCount) {
curWriter->isDone = 1;
tracksDone ++;
continue;
}
e = stbl_GetSampleInfos(curWriter->mdia->information->sampleTable, curWriter->sampleNumber, &sampOffset, &chunkNumber, &descIndex, &isEdited);
if (e) return e;
e = stbl_GetSampleSize(curWriter->mdia->information->sampleTable->SampleSize, curWriter->sampleNumber, &sampSize);
if (e) return e;
if (Emulation) {
if (curWriter != prevWriter) {
forceNewChunk = 1;
} else {
forceNewChunk = 0;
}
if (Media_IsSelfContained(curWriter->mdia, descIndex) ) {
e = stbl_SetChunkAndOffset(curWriter->mdia->information->sampleTable, curWriter->sampleNumber, descIndex, curWriter->stsc, &curWriter->stco, offset, forceNewChunk);
if (e) return e;
offset += sampSize;
totSize += sampSize;
} else {
curWriter->prev_offset = sampOffset + sampSize;
e = stbl_SetChunkAndOffset(curWriter->mdia->information->sampleTable, curWriter->sampleNumber, descIndex, curWriter->stsc, &curWriter->stco, sampOffset, 0);
if (e) return e;
}
} else {
if (Media_IsSelfContained(curWriter->mdia, descIndex) ) {
e = WriteSample(mw, sampSize, sampOffset, isEdited, bs);
if (e) return e;
}
}
if (curWriter->sampleNumber == curWriter->mdia->information->sampleTable->SampleSize->sampleCount) {
curWriter->isDone = 1;
tracksDone ++;
} else {
curWriter->sampleNumber ++;
}
prevWriter = curWriter;
}
if (tracksDone == gf_list_count(writers)) break;
curGroupID ++;
}
movie->mdat->dataSize = totSize;
return GF_OK;
}
GF_Err DoInterleave(MovieWriter *mw, GF_List *writers, GF_BitStream *bs, u8 Emulation, u64 StartOffset, Bool drift_inter)
{
u32 i, tracksDone;
TrackWriter *tmp, *curWriter;
GF_Err e;
u32 descIndex, sampSize, chunkNumber;
u64 DTS;
u16 curGroupID;
u8 forceNewChunk, writeGroup, isEdited;
u64 offset, sampOffset, size, mdatSize;
u32 count;
GF_ISOFile *movie = mw->movie;
if (!movie->moov || !movie->moov->mvhd) return GF_NON_COMPLIANT_BITSTREAM;
mdatSize = 0;
#ifdef TEST_LARGE_FILES
if (!Emulation) {
char *blank;
u32 count, i;
i = count = 0;
blank = gf_malloc(sizeof(char)*1024*1024);
memset(blank, 0, sizeof(char)*1024*1024);
count = 4096;
memset(blank, 0, sizeof(char)*1024*1024);
while (i<count) {
u32 res = gf_bs_write_data(bs, blank, 1024*1024);
if (res != 1024*1024) fprintf(stderr, "error writing to disk: only %d bytes written\n", res);
i++;
fprintf(stderr, "writing blank block: %.02f done - %d/%d \r", (100.0*i)/count , i, count);
}
gf_free(blank);
}
mdatSize = 4096*1024;
mdatSize *= 1024;
#endif
if (movie->meta) {
e = DoWriteMeta(movie, movie->meta, bs, Emulation, StartOffset, &size);
if (e) return e;
mdatSize += size;
StartOffset += (u32) size;
}
if (movie->moov && movie->moov->meta) {
e = DoWriteMeta(movie, movie->moov->meta, bs, Emulation, StartOffset, &size);
if (e) return e;
mdatSize += size;
StartOffset += (u32) size;
}
i=0;
while ((tmp = (TrackWriter*)gf_list_enum(writers, &i))) {
if (tmp->mdia->mediaTrack->meta) {
e = DoWriteMeta(movie, tmp->mdia->mediaTrack->meta, bs, Emulation, StartOffset, &size);
if (e) return e;
mdatSize += size;
StartOffset += (u32) size;
}
}
if (movie->storageMode == GF_ISOM_STORE_TIGHT)
return DoFullInterleave(mw, writers, bs, Emulation, StartOffset);
curGroupID = 1;
offset = StartOffset;
tracksDone = 0;
#ifdef TEST_LARGE_FILES
offset += mdatSize;
#endif
count = gf_list_count(writers);
while (1) {
u64 chunkLastDTS = 0;
u32 chunkLastScale = 0;
writeGroup = 1;
while (writeGroup) {
curWriter = NULL;
for (i=0 ; i < count; i++) {
tmp = (TrackWriter*)gf_list_get(writers, i);
if (tmp->isDone) continue;
if (tmp->mdia->information->sampleTable->groupID != curGroupID) continue;
while (1) {
if (tmp->sampleNumber > tmp->mdia->information->sampleTable->SampleSize->sampleCount) {
tmp->isDone = 1;
tracksDone ++;
break;
}
stbl_GetSampleDTS(tmp->mdia->information->sampleTable->TimeToSample, tmp->sampleNumber, &DTS);
if ( ( (DTS - tmp->DTSprev) + tmp->chunkDur) * movie->moov->mvhd->timeScale > movie->interleavingTime * tmp->timeScale
|| (drift_inter && chunkLastDTS && ( ((u64)tmp->DTSprev*chunkLastScale) > ((u64)chunkLastDTS*tmp->timeScale)) )
) {
if (!tmp->chunkDur) {
forceNewChunk = 1;
} else {
tmp->chunkDur = 0;
break;
}
} else {
forceNewChunk = tmp->chunkDur ? 0 : 1;
}
curWriter = tmp;
if (tmp->sampleNumber == 2 && !tmp->chunkDur) forceNewChunk = 0;
tmp->chunkDur += (u32) (DTS - tmp->DTSprev);
tmp->DTSprev = DTS;
e = stbl_GetSampleInfos(curWriter->mdia->information->sampleTable, curWriter->sampleNumber, &sampOffset, &chunkNumber, &descIndex, &isEdited);
if (e) return e;
e = stbl_GetSampleSize(curWriter->mdia->information->sampleTable->SampleSize, curWriter->sampleNumber, &sampSize);
if (e) return e;
if (Emulation) {
if (Media_IsSelfContained(curWriter->mdia, descIndex) ) {
e = stbl_SetChunkAndOffset(curWriter->mdia->information->sampleTable, curWriter->sampleNumber, descIndex, curWriter->stsc, &curWriter->stco, offset, forceNewChunk);
if (e) return e;
offset += sampSize;
mdatSize += sampSize;
} else {
if (curWriter->prev_offset != sampOffset) forceNewChunk = 1;
curWriter->prev_offset = sampOffset + sampSize;
e = stbl_SetChunkAndOffset(curWriter->mdia->information->sampleTable, curWriter->sampleNumber, descIndex, curWriter->stsc, &curWriter->stco, sampOffset, forceNewChunk);
if (e) return e;
}
} else {
if (Media_IsSelfContained(curWriter->mdia, descIndex) ) {
e = WriteSample(mw, sampSize, sampOffset, isEdited, bs);
if (e) return e;
}
}
if (curWriter->sampleNumber == curWriter->mdia->information->sampleTable->SampleSize->sampleCount) {
curWriter->isDone = 1;
tracksDone ++;
break;
} else {
curWriter->sampleNumber ++;
}
}
if (drift_inter && curWriter) {
chunkLastScale = curWriter->timeScale;
chunkLastDTS = curWriter->DTSprev;
chunkLastDTS += curWriter->timeScale * movie->interleavingTime / movie->moov->mvhd->timeScale;
}
}
if (!curWriter) {
writeGroup = 0;
continue;
}
}
if (tracksDone == gf_list_count(writers)) break;
curGroupID ++;
}
if (movie->mdat) movie->mdat->dataSize = mdatSize;
return GF_OK;
}
static GF_Err WriteInterleaved(MovieWriter *mw, GF_BitStream *bs, Bool drift_inter)
{
GF_Err e;
u32 i;
GF_Box *a;
u64 firstSize, finalSize, offset, finalOffset;
GF_List *writers = gf_list_new();
GF_ISOFile *movie = mw->movie;
e = SetupWriters(mw, writers, 1);
if (e) goto exit;
if (movie->is_jp2) {
gf_bs_write_u32(bs, 12);
gf_bs_write_u32(bs, GF_4CC('j','P',' ',' '));
gf_bs_write_u32(bs, 0x0D0A870A);
}
if (movie->brand) {
e = gf_isom_box_size((GF_Box *)movie->brand);
if (e) goto exit;
e = gf_isom_box_write((GF_Box *)movie->brand, bs);
if (e) goto exit;
}
if (movie->pdin) {
e = gf_isom_box_size((GF_Box *)movie->pdin);
if (e) goto exit;
e = gf_isom_box_write((GF_Box *)movie->pdin, bs);
if (e) goto exit;
}
e = DoInterleave(mw, writers, bs, 1, gf_bs_get_position(bs), drift_inter);
if (e) goto exit;
firstSize = GetMoovAndMetaSize(movie, writers);
offset = firstSize;
if (movie->mdat && movie->mdat->dataSize) offset += 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0);
e = ShiftOffset(movie, writers, offset);
if (e) goto exit;
finalSize = GetMoovAndMetaSize(movie, writers);
if (firstSize != finalSize) {
finalOffset = finalSize;
if (movie->mdat && movie->mdat->dataSize) finalOffset += 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0);
e = ShiftOffset(movie, writers, finalOffset - offset);
if (e) goto exit;
GetMoovAndMetaSize(movie, writers);
}
e = WriteMoovAndMeta(movie, writers, bs);
if (e) goto exit;
if (movie->mdat && movie->mdat->dataSize) {
if (movie->mdat->dataSize > 0xFFFFFFFF) movie->mdat->dataSize += 8;
e = gf_isom_box_size((GF_Box *)movie->mdat);
if (e) goto exit;
e = gf_isom_box_write((GF_Box *)movie->mdat, bs);
if (e) goto exit;
}
ResetWriters(writers);
e = DoInterleave(mw, writers, bs, 0, 0, drift_inter);
if (e) goto exit;
i=0;
while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
switch (a->type) {
case GF_ISOM_BOX_TYPE_MOOV:
case GF_ISOM_BOX_TYPE_META:
case GF_ISOM_BOX_TYPE_FTYP:
case GF_ISOM_BOX_TYPE_PDIN:
case GF_ISOM_BOX_TYPE_MDAT:
break;
default:
e = gf_isom_box_size(a);
if (e) goto exit;
e = gf_isom_box_write(a, bs);
if (e) goto exit;
}
}
exit:
CleanWriters(writers);
gf_list_del(writers);
return e;
}
extern u32 default_write_buffering_size;
GF_Err WriteToFile(GF_ISOFile *movie)
{
FILE *stream;
GF_BitStream *bs;
MovieWriter mw;
GF_Err e = GF_OK;
if (!movie) return GF_BAD_PARAM;
if (movie->openMode == GF_ISOM_OPEN_READ) return GF_BAD_PARAM;
e = gf_isom_insert_copyright(movie);
if (e) return e;
memset(&mw, 0, sizeof(mw));
mw.movie = movie;
if (movie->drop_date_version_info && movie->moov) {
u32 i;
GF_TrackBox *trak;
movie->moov->mvhd->creationTime = 0;
movie->moov->mvhd->modificationTime = 0;
i=0;
while ( (trak = gf_list_enum(movie->moov->trackList, &i))) {
trak->Header->creationTime = 0;
trak->Header->modificationTime = 0;
if (trak->Media->handler->nameUTF8 && strstr(trak->Media->handler->nameUTF8, "@GPAC")) {
gf_free(trak->Media->handler->nameUTF8);
trak->Media->handler->nameUTF8 = gf_strdup("MediaHandler");
}
trak->Media->mediaHeader->creationTime = 0;
trak->Media->mediaHeader->modificationTime = 0;
}
}
if (movie->openMode == GF_ISOM_OPEN_WRITE) {
e = WriteFlat(&mw, 0, movie->editFileMap->bs);
} else {
u32 buffer_size = movie->editFileMap ? gf_bs_get_output_buffering(movie->editFileMap->bs) : 0;
Bool is_stdout = 0;
if (!strcmp(movie->finalName, "std"))
is_stdout = 1;
stream = is_stdout ? stdout : gf_fopen(movie->finalName, "w+b");
if (!stream)
return GF_IO_ERR;
bs = gf_bs_from_file(stream, GF_BITSTREAM_WRITE);
if (!bs) {
if (!is_stdout)
gf_fclose(stream);
return GF_OUT_OF_MEM;
}
if (buffer_size) {
gf_bs_set_output_buffering(bs, buffer_size);
}
if (!movie->moov) {
movie->storageMode = GF_ISOM_STORE_FLAT;
}
switch (movie->storageMode) {
case GF_ISOM_STORE_TIGHT:
case GF_ISOM_STORE_INTERLEAVED:
e = WriteInterleaved(&mw, bs, 0);
break;
case GF_ISOM_STORE_DRIFT_INTERLEAVED:
e = WriteInterleaved(&mw, bs, 1);
break;
case GF_ISOM_STORE_STREAMABLE:
e = WriteFlat(&mw, 1, bs);
break;
default:
e = WriteFlat(&mw, 0, bs);
break;
}
gf_bs_del(bs);
if (!is_stdout)
gf_fclose(stream);
}
if (mw.buffer) gf_free(mw.buffer);
if (mw.nb_done<mw.total_samples) {
gf_set_progress("ISO File Writing", mw.total_samples, mw.total_samples);
}
return e;
}
#endif