M4Err Media_GetSample(MediaAtom *mdia, u32 sampleNumber, M4Sample **samp, u32 *sampleDescriptionIndex, Bool no_data, u64 *out_offset) { M4Err e; u32 bytesRead; u32 dataRefIndex, chunkNumber; u64 offset, new_size; u8 isEdited; SampleEntryAtom *entry; (*sampleDescriptionIndex) = 0; if (!mdia || !mdia->information->sampleTable) return M4BadParam; //OK, here we go.... if (sampleNumber > mdia->information->sampleTable->SampleSize->sampleCount) return M4BadParam; //get the DTS e = stbl_GetSampleDTS(mdia->information->sampleTable->TimeToSample, sampleNumber, &(*samp)->DTS); if (e) return e; //the CTS offset if (mdia->information->sampleTable->CompositionOffset) { e = stbl_GetSampleCTS(mdia->information->sampleTable->CompositionOffset , sampleNumber, &(*samp)->CTS_Offset); if (e) return e; } else { (*samp)->CTS_Offset = 0; } //the size e = stbl_GetSampleSize(mdia->information->sampleTable->SampleSize, sampleNumber, &(*samp)->dataLength); if (e) return e; //the RAP if (mdia->information->sampleTable->SyncSample) { e = stbl_GetSampleRAP(mdia->information->sampleTable->SyncSample, sampleNumber, &(*samp)->IsRAP, NULL, NULL); if (e) return e; } else { //if no SyncSample, all samples are sync (cf spec) (*samp)->IsRAP = 1; } /*get sync shadow*/ if (Media_IsSampleSyncShadow(mdia->information->sampleTable->ShadowSync, sampleNumber)) (*samp)->IsRAP = 2; //the data info e = stbl_GetSampleInfos(mdia->information->sampleTable, sampleNumber, &offset, &chunkNumber, sampleDescriptionIndex, &isEdited); if (e) return e; //then get the DataRef e = Media_GetSampleDesc(mdia, *sampleDescriptionIndex, &entry, &dataRefIndex); if (e) return e; // Open the data handler - check our mode, don't reopen in read only if this is //the same entry. In other modes we have no choice because the main data map is //divided into the original and the edition files if (mdia->mediaTrack->moov->mov->openMode == M4_OPEN_READ) { //same as last call in read mode if (!mdia->information->dataHandler || (mdia->information->dataEntryIndex != dataRefIndex)) { e = DataMap_Open(mdia, dataRefIndex, isEdited); if (e) return e; } } else { e = DataMap_Open(mdia, dataRefIndex, isEdited); if (e) return e; } if (out_offset) *out_offset = offset; if (no_data) return M4OK; /*and finally get the data, include padding if needed*/ (*samp)->data = (char *) malloc(sizeof(char) * ( (*samp)->dataLength + mdia->mediaTrack->padding_bytes) ); if (mdia->mediaTrack->padding_bytes) memset((*samp)->data + (*samp)->dataLength, 0, sizeof(char) * mdia->mediaTrack->padding_bytes); //check if we can get the sample (make sure we have enougth data...) new_size = BS_GetSize(mdia->information->dataHandler->bs); if (offset + (*samp)->dataLength > new_size) { //always refresh the size to avoid wrong info on http/ftp new_size = BS_GetRefreshedSize(mdia->information->dataHandler->bs); if (offset + (*samp)->dataLength > new_size) { mdia->BytesMissing = offset + (*samp)->dataLength - new_size; return M4UncompleteFile; } } bytesRead = DataMap_GetData(mdia->information->dataHandler, (*samp)->data, (*samp)->dataLength, offset); //if bytesRead != sampleSize, we have an IO err if (bytesRead < (*samp)->dataLength) { return M4IOErr; } mdia->BytesMissing = 0; //finally rewrite the sample if this is an OD Access Unit if (mdia->handler->handlerType == M4_ODMediaType) { e = Media_RewriteODFrame(mdia, *samp); if (e) return e; } else if (mdia->mediaTrack->moov->mov->convert_streaming_text && (mdia->handler->handlerType == M4_TimedTextMediaType) ) { u32 dur; if (sampleNumber == mdia->information->sampleTable->SampleSize->sampleCount) { dur = (u32) mdia->mediaHeader->duration - (*samp)->DTS; } else { stbl_GetSampleDTS(mdia->information->sampleTable->TimeToSample, sampleNumber+1, &dur); dur -= (*samp)->DTS; } e = M4_RewriteTextSample(*samp, *sampleDescriptionIndex, dur); if (e) return e; } return M4OK; }
GF_Err Media_GetSample(GF_MediaBox *mdia, u32 sampleNumber, GF_ISOSample **samp, u32 *sIDX, Bool no_data, u64 *out_offset) { GF_Err e; u32 bytesRead; u32 dataRefIndex, chunkNumber; u64 offset, new_size; u8 isEdited; GF_SampleEntryBox *entry; if (!mdia || !mdia->information->sampleTable) return GF_BAD_PARAM; //OK, here we go.... if (sampleNumber > mdia->information->sampleTable->SampleSize->sampleCount) return GF_BAD_PARAM; //get the DTS e = stbl_GetSampleDTS(mdia->information->sampleTable->TimeToSample, sampleNumber, &(*samp)->DTS); if (e) return e; //the CTS offset if (mdia->information->sampleTable->CompositionOffset) { e = stbl_GetSampleCTS(mdia->information->sampleTable->CompositionOffset , sampleNumber, &(*samp)->CTS_Offset); if (e) return e; } else { (*samp)->CTS_Offset = 0; } //the size e = stbl_GetSampleSize(mdia->information->sampleTable->SampleSize, sampleNumber, &(*samp)->dataLength); if (e) return e; //the RAP if (mdia->information->sampleTable->SyncSample) { e = stbl_GetSampleRAP(mdia->information->sampleTable->SyncSample, sampleNumber, &(*samp)->IsRAP, NULL, NULL); if (e) return e; } else { //if no SyncSample, all samples are sync (cf spec) (*samp)->IsRAP = 1; } /*overwrite sync sample with sample dep if any*/ if (mdia->information->sampleTable->SampleDep) { u32 dependsOn, dependedOn, redundant; e = stbl_GetSampleDepType(mdia->information->sampleTable->SampleDep, sampleNumber, &dependsOn, &dependedOn, &redundant); if (!e) { if (dependsOn==1) (*samp)->IsRAP = 0; else if (dependsOn==2) (*samp)->IsRAP = 1; /*if not depended upon and redundant, mark as carousel sample*/ if ((dependedOn==2) && (redundant==1)) (*samp)->IsRAP = 2; /*TODO FIXME - we must enhance the IsRAP semantics to carry disposable info ... */ } } /*get sync shadow*/ if (Media_IsSampleSyncShadow(mdia->information->sampleTable->ShadowSync, sampleNumber)) (*samp)->IsRAP = 2; //the data info if (!sIDX && !no_data) return GF_BAD_PARAM; if (!sIDX && !out_offset) return GF_OK; (*sIDX) = 0; e = stbl_GetSampleInfos(mdia->information->sampleTable, sampleNumber, &offset, &chunkNumber, sIDX, &isEdited); if (e) return e; //then get the DataRef e = Media_GetSampleDesc(mdia, *sIDX, &entry, &dataRefIndex); if (e) return e; // Open the data handler - check our mode, don't reopen in read only if this is //the same entry. In other modes we have no choice because the main data map is //divided into the original and the edition files if (mdia->mediaTrack->moov->mov->openMode == GF_ISOM_OPEN_READ) { //same as last call in read mode if (!mdia->information->dataHandler) { e = gf_isom_datamap_open(mdia, dataRefIndex, isEdited); if (e) return e; } if (mdia->information->dataEntryIndex != dataRefIndex) mdia->information->dataEntryIndex = dataRefIndex; } else { e = gf_isom_datamap_open(mdia, dataRefIndex, isEdited); if (e) return e; } if (out_offset) *out_offset = offset; if (no_data) return GF_OK; /*and finally get the data, include padding if needed*/ (*samp)->data = (char *) gf_malloc(sizeof(char) * ( (*samp)->dataLength + mdia->mediaTrack->padding_bytes) ); if (mdia->mediaTrack->padding_bytes) memset((*samp)->data + (*samp)->dataLength, 0, sizeof(char) * mdia->mediaTrack->padding_bytes); //check if we can get the sample (make sure we have enougth data...) new_size = gf_bs_get_size(mdia->information->dataHandler->bs); if (offset + (*samp)->dataLength > new_size) { //always refresh the size to avoid wrong info on http/ftp new_size = gf_bs_get_refreshed_size(mdia->information->dataHandler->bs); if (offset + (*samp)->dataLength > new_size) { mdia->BytesMissing = offset + (*samp)->dataLength - new_size; return GF_ISOM_INCOMPLETE_FILE; } } bytesRead = gf_isom_datamap_get_data(mdia->information->dataHandler, (*samp)->data, (*samp)->dataLength, offset); //if bytesRead != sampleSize, we have an IO err if (bytesRead < (*samp)->dataLength) { return GF_IO_ERR; } mdia->BytesMissing = 0; //finally rewrite the sample if this is an OD Access Unit if (mdia->handler->handlerType == GF_ISOM_MEDIA_OD) { e = Media_RewriteODFrame(mdia, *samp); if (e) return e; } /*FIXME: we don NOT rewrite sample if we have a encrypted track*/ else if (gf_isom_is_nalu_based_entry(mdia, entry) && !gf_isom_is_track_encrypted(mdia->mediaTrack->moov->mov, gf_isom_get_tracknum_from_id(mdia->mediaTrack->moov, mdia->mediaTrack->Header->trackID)) ) { e = gf_isom_nalu_sample_rewrite(mdia, *samp, sampleNumber, (GF_MPEGVisualSampleEntryBox *)entry); if (e) return e; } else if (mdia->mediaTrack->moov->mov->convert_streaming_text && ((mdia->handler->handlerType == GF_ISOM_MEDIA_TEXT) || (mdia->handler->handlerType == GF_ISOM_MEDIA_SUBT)) && (entry->type == GF_ISOM_BOX_TYPE_TX3G || entry->type == GF_ISOM_BOX_TYPE_TEXT) ) { u64 dur; if (sampleNumber == mdia->information->sampleTable->SampleSize->sampleCount) { dur = mdia->mediaHeader->duration - (*samp)->DTS; } else { stbl_GetSampleDTS(mdia->information->sampleTable->TimeToSample, sampleNumber+1, &dur); dur -= (*samp)->DTS; } e = gf_isom_rewrite_text_sample(*samp, *sIDX, (u32) dur); if (e) return e; } return GF_OK; }
//This functions unpack the offset for easy editing, eg each sample //is contained in one chunk... M4Err stbl_UnpackOffsets(SampleTableAtom *stbl) { M4Err e; u8 isEdited; u32 i, chunkNumber, sampleDescIndex; u64 dataOffset; stscEntry *ent; ChunkOffsetAtom *stco_tmp; ChunkLargeOffsetAtom *co64_tmp; SampleToChunkAtom *stsc_tmp; if (!stbl) return M4InvalidMP4File; //we should have none of the mandatory atoms (allowed in the spec) if (!stbl->ChunkOffset && !stbl->SampleDescription && !stbl->SampleSize && !stbl->SampleToChunk && !stbl->TimeToSample) return M4OK; //or all the mandatory ones ... if (!stbl->ChunkOffset || !stbl->SampleDescription || !stbl->SampleSize || !stbl->SampleToChunk || !stbl->TimeToSample) return M4InvalidMP4File; //do we need to unpack? Not if we have only one sample per chunk. if (stbl->SampleSize->sampleCount == ChainGetCount(stbl->SampleToChunk->entryList)) return M4OK; //create a new SampleToChunk table stsc_tmp = (SampleToChunkAtom *) CreateAtom(SampleToChunkAtomType); //check the offset type and create a new table... if (stbl->ChunkOffset->type == ChunkOffsetAtomType) { co64_tmp = NULL; stco_tmp = (ChunkOffsetAtom *) CreateAtom(ChunkOffsetAtomType); stco_tmp->entryCount = stbl->SampleSize->sampleCount; stco_tmp->offsets = (u32*)malloc(stco_tmp->entryCount * sizeof(u32)); } else if (stbl->ChunkOffset->type == ChunkLargeOffsetAtomType) { stco_tmp = NULL; co64_tmp = (ChunkLargeOffsetAtom *) CreateAtom(ChunkLargeOffsetAtomType); co64_tmp->entryCount = stbl->SampleSize->sampleCount; co64_tmp->offsets = (u64*)malloc(co64_tmp->entryCount * sizeof(u64)); } else { return M4InvalidMP4File; } ent = NULL; //OK write our two tables... for (i = 0; i < stbl->SampleSize->sampleCount; i++) { //get the data info for the sample e = stbl_GetSampleInfos(stbl, i+1, &dataOffset, &chunkNumber, &sampleDescIndex, &isEdited); if (e) goto err_exit; ent = (stscEntry*)malloc(sizeof(stscEntry)); ent->isEdited = 0; ent->sampleDescriptionIndex = sampleDescIndex; //here's the trick: each sample is in ONE chunk ent->firstChunk = i+1; ent->nextChunk = i+2; ent->samplesPerChunk = 1; e = ChainAddEntry(stsc_tmp->entryList, ent); if (e) goto err_exit; if (stco_tmp) { stco_tmp->offsets[i] = (u32) dataOffset; } else { co64_tmp->offsets[i] = dataOffset; } } //close the list if (ent) ent->nextChunk = 0; //done, remove our previous tables DelAtom(stbl->ChunkOffset); DelAtom((Atom *)stbl->SampleToChunk); //and set these ones... if (stco_tmp) { stbl->ChunkOffset = (Atom *)stco_tmp; } else { stbl->ChunkOffset = (Atom *)co64_tmp; } stbl->SampleToChunk = stsc_tmp; stbl->SampleToChunk->currentEntry = (stscEntry*)ChainGetEntry(stbl->SampleToChunk->entryList, 0); stbl->SampleToChunk->currentIndex = 0; stbl->SampleToChunk->currentChunk = 0; stbl->SampleToChunk->firstSampleInCurrentChunk = 0; return M4OK; err_exit: if (stco_tmp) DelAtom((Atom *) stco_tmp); if (co64_tmp) DelAtom((Atom *) co64_tmp); if (stsc_tmp) DelAtom((Atom *) stsc_tmp); return e; }
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; //this is used to emulate the write ... u64 offset, sampOffset, size, mdatSize; u32 count; GF_ISOFile *movie = mw->movie; 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(stdout, "error writing to disk: only %d bytes written\n", res); i++; fprintf(stdout, "writing blank block: %.02f done - %d/%d \r", (100.0*i)/count , i, count); } gf_free(blank); } mdatSize = 4096*1024; mdatSize *= 1024; #endif /*write meta content first - WE DON'T support fragmentation of resources in ISOM atm*/ 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); e = GF_OK; curGroupID = 1; //we emulate a write from this offset... offset = StartOffset; writeGroup = 1; tracksDone = 0; #ifdef TEST_LARGE_FILES offset += mdatSize; #endif count = gf_list_count(writers); //browse each groups while (1) { /*the max DTS the chunk of the current writer*/ u64 chunkLastDTS = 0; /*the timescale related to the max DTS*/ u32 chunkLastScale = 0; writeGroup = 1; //proceed a group while (writeGroup) { curWriter = NULL; for (i=0 ; i < count; i++) { tmp = (TrackWriter*)gf_list_get(writers, i); //is it done writing ? if (tmp->isDone) continue; //is it in our group ?? if (tmp->mdia->information->sampleTable->groupID != curGroupID) continue; //write till this chunk is full on this track... while (1) { //To Check: are empty sample tables allowed ??? if (tmp->sampleNumber > tmp->mdia->information->sampleTable->SampleSize->sampleCount) { tmp->isDone = 1; tracksDone ++; break; } //OK, get the current sample in this track stbl_GetSampleDTS(tmp->mdia->information->sampleTable->TimeToSample, tmp->sampleNumber, &DTS); //can this sample fit in our chunk ? if ( ( (DTS - tmp->DTSprev) + tmp->chunkDur) * movie->moov->mvhd->timeScale > movie->interleavingTime * tmp->timeScale /*drfit check: reject sample if outside our check window*/ || (drift_inter && chunkLastDTS && ( ((u64)tmp->DTSprev*chunkLastScale) > ((u64)chunkLastDTS*tmp->timeScale)) ) ) { //in case the sample is longer than InterleaveTime if (!tmp->chunkDur) { forceNewChunk = 1; } else { //this one is full. go to next one (exit the loop) tmp->chunkDur = 0; break; } } else { forceNewChunk = tmp->chunkDur ? 0 : 1; } //OK, we can write this track curWriter = tmp; //small check for first 2 samples (DTS = 0 :) 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; //do we actually write, or do we emulate ? if (Emulation) { //update our offsets... 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; //we have a DataRef, so use the offset idicated in sampleToChunk //and ChunkOffset tables... e = stbl_SetChunkAndOffset(curWriter->mdia->information->sampleTable, curWriter->sampleNumber, descIndex, curWriter->stsc, &curWriter->stco, sampOffset, forceNewChunk); if (e) return e; } } else { //this is no game, we're writing .... if (Media_IsSelfContained(curWriter->mdia, descIndex) ) { e = WriteSample(mw, sampSize, sampOffset, isEdited, bs); if (e) return e; } } //ok, the sample is done if (curWriter->sampleNumber == curWriter->mdia->information->sampleTable->SampleSize->sampleCount) { curWriter->isDone = 1; //one more track done... tracksDone ++; break; } else { curWriter->sampleNumber ++; } } /*record chunk end-time & track timescale for drift-controled interleaving*/ if (drift_inter && curWriter) { chunkLastScale = curWriter->timeScale; chunkLastDTS = curWriter->DTSprev; /*add one interleave window drift - since the "maxDTS" is the previously written one, we will have the following cases: - sample doesn't fit: post-pone and force new chunk - sample time larger than previous chunk time + interleave: post-pone and force new chunk - otherwise store and track becomes current reference this ensures a proper drift regulation (max DTS diff is less than the interleaving window) */ chunkLastDTS += curWriter->timeScale * movie->interleavingTime / movie->moov->mvhd->timeScale; } } //no sample found, we're done with this group if (!curWriter) { writeGroup = 0; continue; } } //if all our track are done, break if (tracksDone == gf_list_count(writers)) break; //go to next group curGroupID ++; } if (movie->mdat) movie->mdat->dataSize = mdatSize; return GF_OK; }
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; //this is used to emulate the write ... u64 offset, totSize, sampOffset; GF_ISOFile *movie = mw->movie; e = GF_OK; totSize = 0; curGroupID = 1; prevWriter = NULL; //we emulate a write from this offset... offset = StartOffset; writeGroup = 1; tracksDone = 0; //browse each groups while (1) { writeGroup = 1; //proceed a group while (writeGroup) { //first get the appropriated sample for the min time in this group curWriter = NULL; DTStmp = (u64) -1; TStmp = 0; curTrackPriority = (u16) -1; i=0; while ((tmp = (TrackWriter*)gf_list_enum(writers, &i))) { //is it done writing ? //is it in our group ?? if (tmp->isDone || tmp->mdia->information->sampleTable->groupID != curGroupID) continue; //OK, get the current sample in this track 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; } //no sample found, we're done with this group if (!curWriter) { //we're done with the group curTrackPriority = 0; writeGroup = 0; continue; } //To Check: are empty sample tables allowed ??? 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; //do we actually write, or do we emulate ? if (Emulation) { //are we in the same track ??? If not, force a new chunk when adding this sample if (curWriter != prevWriter) { forceNewChunk = 1; } else { forceNewChunk = 0; } //update our offsets... 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 { if (curWriter->prev_offset != sampOffset) forceNewChunk = 1; curWriter->prev_offset = sampOffset + sampSize; //we have a DataRef, so use the offset idicated in sampleToChunk //and ChunkOffset tables... e = stbl_SetChunkAndOffset(curWriter->mdia->information->sampleTable, curWriter->sampleNumber, descIndex, curWriter->stsc, &curWriter->stco, sampOffset, 0); if (e) return e; } } else { //this is no game, we're writing .... if (Media_IsSelfContained(curWriter->mdia, descIndex) ) { e = WriteSample(mw, sampSize, sampOffset, isEdited, bs); if (e) return e; } } //ok, the sample is done if (curWriter->sampleNumber == curWriter->mdia->information->sampleTable->SampleSize->sampleCount) { curWriter->isDone = 1; //one more track done... tracksDone ++; } else { curWriter->sampleNumber ++; } prevWriter = curWriter; } //if all our track are done, break if (tracksDone == gf_list_count(writers)) break; //go to next group curGroupID ++; } movie->mdat->dataSize = totSize; return GF_OK; }
//this function writes track by track in the order of tracks inside the moov... 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; /*write meta content first - WE DON'T support fragmentation of resources in ISOM atm*/ 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) { //To Check: are empty sample tables allowed ??? 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; //update our chunks. force = 0; if (movie->openMode == GF_ISOM_OPEN_WRITE) { offset = sampOffset; if (predOffset != offset) force = 1; } //update our global offset... 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; //we have a DataRef, so use the offset idicated in sampleToChunk and ChunkOffset tables... e = stbl_SetChunkAndOffset(writer->mdia->information->sampleTable, writer->sampleNumber, descIndex, writer->stsc, &writer->stco, sampOffset, force); if (e) return e; } //we write the sample if not emulation if (!Emulation) { if (Media_IsSelfContained(writer->mdia, descIndex) ) { e = WriteSample(mw, sampSize, sampOffset, isEdited, bs); if (e) return e; } } //ok, the track is done if (writer->sampleNumber == writer->mdia->information->sampleTable->SampleSize->sampleCount) { writer->isDone = 1; } else { writer->sampleNumber ++; } } } //set the mdatSize... movie->mdat->dataSize = mdatSize; return GF_OK; }