//Get the total media duration based on the TimeToSample table M4Err Media_SetDuration(TrackAtom *trak) { ESDescriptor *esd; u32 DTS, DTSprev; u32 nbSamp = trak->Media->information->sampleTable->SampleSize->sampleCount; //we need to check how many samples we have. // == 1 -> last sample duration == default duration // > 1 -> last sample duration == prev sample duration switch (nbSamp) { case 0: trak->Media->mediaHeader->duration = 0; //we have an URL stream, set duration to its max if (!Track_IsMPEG4Stream(trak->Media->handler->handlerType)) { Media_GetESD(trak->Media, 1, &esd, 1); if (esd && esd->URLString) trak->Media->mediaHeader->duration = -1; } return M4OK; case 1: trak->Media->mediaHeader->duration = trak->Media->mediaHeader->timeScale; return M4OK; default: //we assume a constant frame rate for the media and assume the last sample //will be hold the same time as the prev one stbl_GetSampleDTS(trak->Media->information->sampleTable->TimeToSample, nbSamp, &DTS); stbl_GetSampleDTS(trak->Media->information->sampleTable->TimeToSample, nbSamp-1, &DTSprev); trak->Media->mediaHeader->duration = DTS + (DTS - DTSprev); return M4OK; } }
GF_Err Track_SetStreamDescriptor(GF_TrackBox *trak, u32 StreamDescriptionIndex, u32 DataReferenceIndex, GF_ESD *esd, u32 *outStreamIndex) { GF_Err e; GF_MPEGSampleEntryBox *entry; GF_MPEGVisualSampleEntryBox *entry_v; GF_MPEGAudioSampleEntryBox *entry_a; GF_TrackReferenceBox *tref; GF_TrackReferenceTypeBox *dpnd; u16 tmpRef; entry = NULL; tref = NULL; if (!trak || !esd || (!outStreamIndex && !DataReferenceIndex) ) return GF_BAD_PARAM; if (!Track_IsMPEG4Stream(trak->Media->handler->handlerType)) return GF_ISOM_INVALID_MEDIA; esd->ESID = 0; //set SL to predefined if no url if (esd->URLString == NULL) { if (!esd->slConfig) esd->slConfig = (GF_SLConfig*) gf_odf_desc_new(GF_ODF_SLC_TAG); esd->slConfig->predefined = SLPredef_MP4; esd->slConfig->durationFlag = 0; esd->slConfig->useTimestampsFlag = 1; } //get the REF box if needed if (esd->dependsOnESID || esd->OCRESID ) { if (!trak->References) { tref = (GF_TrackReferenceBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_TREF); e = trak_AddBox((GF_Box*)trak, (GF_Box *)tref); if (e) return e; } tref = trak->References; } //Update Stream dependancies e = Track_FindRef(trak, GF_ISOM_REF_DECODE, &dpnd); if (e) return e; if (!dpnd && esd->dependsOnESID) { dpnd = (GF_TrackReferenceTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_REFT); dpnd->reference_type = GF_ISOM_BOX_TYPE_DPND; e = tref_AddBox((GF_Box*)tref, (GF_Box *) dpnd); if (e) return e; e = reftype_AddRefTrack(dpnd, esd->dependsOnESID, NULL); if (e) return e; } else if (dpnd && !esd->dependsOnESID) { Track_RemoveRef(trak, GF_ISOM_BOX_TYPE_DPND); } esd->dependsOnESID = 0; //Update GF_Clock dependancies e = Track_FindRef(trak, GF_ISOM_REF_OCR, &dpnd); if (e) return e; if (!dpnd && esd->OCRESID) { dpnd = (GF_TrackReferenceTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_REFT); dpnd->reference_type = GF_ISOM_BOX_TYPE_SYNC; e = tref_AddBox((GF_Box*)tref, (GF_Box *) dpnd); if (e) return e; e = reftype_AddRefTrack(dpnd, esd->OCRESID, NULL); if (e) return e; } else if (dpnd && !esd->OCRESID) { Track_RemoveRef(trak, GF_ISOM_BOX_TYPE_SYNC); } else if (dpnd && esd->OCRESID) { if (dpnd->trackIDCount != 1) return GF_ISOM_INVALID_MEDIA; dpnd->trackIDs[0] = esd->OCRESID; } esd->OCRESID = 0; //brand new case: we have to change the IPI desc if (esd->ipiPtr) { e = Track_FindRef(trak, GF_ISOM_REF_IPI, &dpnd); if (e) return e; if (!dpnd) { tmpRef = 0; dpnd = (GF_TrackReferenceTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_REFT); dpnd->reference_type = GF_ISOM_BOX_TYPE_IPIR; e = tref_AddBox((GF_Box*)tref, (GF_Box *) dpnd); if (e) return e; e = reftype_AddRefTrack(dpnd, esd->ipiPtr->IPI_ES_Id, &tmpRef); if (e) return e; //and replace the tag and value... esd->ipiPtr->IPI_ES_Id = tmpRef; esd->ipiPtr->tag = GF_ODF_ISOM_IPI_PTR_TAG; } else { //Watch out! ONLY ONE IPI dependancy is allowed per stream if (dpnd->trackIDCount != 1) return GF_ISOM_INVALID_MEDIA; //if an existing one is there, what shall we do ??? //donno, erase it dpnd->trackIDs[0] = esd->ipiPtr->IPI_ES_Id; //and replace the tag and value... esd->ipiPtr->IPI_ES_Id = 1; esd->ipiPtr->tag = GF_ODF_ISOM_IPI_PTR_TAG; } } /*don't store the lang desc in ESD, use the media header language info*/ if (esd->langDesc) { trak->Media->mediaHeader->packedLanguage[0] = (esd->langDesc->langCode>>16)&0xFF; trak->Media->mediaHeader->packedLanguage[1] = (esd->langDesc->langCode>>8)&0xFF; trak->Media->mediaHeader->packedLanguage[2] = (esd->langDesc->langCode)&0xFF; gf_odf_desc_del((GF_Descriptor *)esd->langDesc); esd->langDesc = NULL; }
//Get the total media duration based on the TimeToSample table GF_Err Media_SetDuration(GF_TrackBox *trak) { GF_ESD *esd; u64 DTS; GF_SttsEntry *ent; u32 nbSamp = trak->Media->information->sampleTable->SampleSize->sampleCount; //we need to check how many samples we have. // == 1 -> last sample duration == default duration // > 1 -> last sample duration == prev sample duration switch (nbSamp) { case 0: trak->Media->mediaHeader->duration = 0; if (Track_IsMPEG4Stream(trak->Media->handler->handlerType)) { Media_GetESD(trak->Media, 1, &esd, 1); if (esd && esd->URLString) trak->Media->mediaHeader->duration = (u64) -1; } return GF_OK; // case 1: // trak->Media->mediaHeader->duration = trak->Media->mediaHeader->timeScale; // return GF_OK; default: //we assume a constant frame rate for the media and assume the last sample //will be hold the same time as the prev one stbl_GetSampleDTS(trak->Media->information->sampleTable->TimeToSample, nbSamp, &DTS); ent = &trak->Media->information->sampleTable->TimeToSample->entries[trak->Media->information->sampleTable->TimeToSample->nb_entries-1]; trak->Media->mediaHeader->duration = DTS; #if 1 trak->Media->mediaHeader->duration += ent->sampleDelta; #else if (!ent) { u64 DTSprev; stbl_GetSampleDTS(trak->Media->information->sampleTable->TimeToSample, nbSamp-1, &DTSprev); trak->Media->mediaHeader->duration += (DTS - DTSprev); } else { #ifndef GPAC_DISABLE_ISOM_WRITE if (trak->moov->mov->editFileMap && trak->Media->information->sampleTable->CompositionOffset) { u32 count, i; u64 max_ts; GF_DttsEntry *cts_ent; GF_CompositionOffsetBox *ctts = trak->Media->information->sampleTable->CompositionOffset; if (ctts->w_LastSampleNumber==nbSamp) { count = gf_list_count(ctts->entryList); max_ts = trak->Media->mediaHeader->duration; while (count) { count -= 1; cts_ent = gf_list_get(ctts->entryList, count); if (nbSamp<cts_ent->sampleCount) break; for (i=0; i<cts_ent->sampleCount; i++) { stbl_GetSampleDTS(trak->Media->information->sampleTable->TimeToSample, nbSamp-i, &DTS); if ((s32) cts_ent->decodingOffset < 0) max_ts = DTS; else max_ts = DTS + cts_ent->decodingOffset; if (max_ts>=trak->Media->mediaHeader->duration) { trak->Media->mediaHeader->duration = max_ts; } else { break; } } if (max_ts<trak->Media->mediaHeader->duration) { break; } nbSamp-=cts_ent->sampleCount; } } } #endif /*GPAC_DISABLE_ISOM_WRITE*/ trak->Media->mediaHeader->duration += ent->sampleDelta; } #endif return GF_OK; } }
M4Err Track_SetStreamDescriptor(TrackAtom *trak, u32 StreamDescriptionIndex, u32 DataReferenceIndex, ESDescriptor *esd, u32 *outStreamIndex) { M4Err e; MPEGSampleEntryAtom *entry; MPEGVisualSampleEntryAtom *entry_v; MPEGAudioSampleEntryAtom *entry_a; TrackReferenceAtom *tref; TrackReferenceTypeAtom *dpnd; u16 tmpRef; M4Err reftype_AddRefTrack(TrackReferenceTypeAtom *ref, u32 trackID, u16 *outRefIndex); M4Err tref_AddAtom(TrackReferenceAtom *ptr, Atom *a); M4Err stsd_AddAtom(SampleDescriptionAtom *ptr, Atom *a); M4Err trak_AddAtom(TrackAtom *ptr, Atom *a); entry = NULL; tref = NULL; if (!trak || !esd || (!outStreamIndex && !DataReferenceIndex) ) return M4BadParam; if (!Track_IsMPEG4Stream(trak->Media->handler->handlerType)) return M4InvalidMP4Media; esd->ESID = 0; //set SL to predefined if no url if (esd->URLString == NULL) { esd->slConfig->predefined = SLPredef_MP4; esd->slConfig->durationFlag = 0; esd->slConfig->useTimestampsFlag = 1; } //get the REF atom if needed if (esd->dependsOnESID || esd->OCRESID ) { if (!trak->References) { tref = (TrackReferenceAtom *) CreateAtom(TrackReferenceAtomType); e = trak_AddAtom(trak, (Atom *)tref); if (e) return e; } tref = trak->References; } //Update Stream dependancies e = Track_FindRef(trak, M4_StreamDependence_Ref, &dpnd); if (e) return e; if (!dpnd && esd->dependsOnESID) { dpnd = (TrackReferenceTypeAtom *) CreateAtom(StreamDependenceAtomType); e = tref_AddAtom(tref, (Atom *) dpnd); if (e) return e; e = reftype_AddRefTrack(dpnd, esd->dependsOnESID, NULL); if (e) return e; } else if (dpnd && !esd->dependsOnESID) { Track_RemoveRef(trak, StreamDependenceAtomType); } esd->dependsOnESID = 0; //Update Clock dependancies e = Track_FindRef(trak, M4_OCR_Ref, &dpnd); if (e) return e; if (!dpnd && esd->OCRESID) { dpnd = (TrackReferenceTypeAtom *) CreateAtom(OCRReferenceAtomType); e = tref_AddAtom(tref, (Atom *) dpnd); if (e) return e; e = reftype_AddRefTrack(dpnd, esd->OCRESID, NULL); if (e) return e; } else if (dpnd && !esd->OCRESID) { Track_RemoveRef(trak, OCRReferenceAtomType); } else if (dpnd && esd->OCRESID) { if (dpnd->trackIDCount != 1) return M4InvalidMP4Media; dpnd->trackIDs[0] = esd->OCRESID; } esd->OCRESID = 0; //brand new case: we have to change the IPI desc if (esd->ipiPtr) { e = Track_FindRef(trak, M4_IPI_Ref, &dpnd); if (e) return e; if (!dpnd) { tmpRef = 0; dpnd = (TrackReferenceTypeAtom *) CreateAtom(IPIReferenceAtomType); e = tref_AddAtom(tref, (Atom *) dpnd); if (e) return e; e = reftype_AddRefTrack(dpnd, esd->ipiPtr->IPI_ES_Id, &tmpRef); if (e) return e; //and replace the tag and value... esd->ipiPtr->IPI_ES_Id = tmpRef; esd->ipiPtr->tag = IPI_DescPtr_Tag; } else { //Watch out! ONLY ONE IPI dependancy is allowed per stream if (dpnd->trackIDCount != 1) return M4InvalidMP4Media; //if an existing one is there, what shall we do ??? //donno, erase it dpnd->trackIDs[0] = esd->ipiPtr->IPI_ES_Id; //and replace the tag and value... esd->ipiPtr->IPI_ES_Id = 1; esd->ipiPtr->tag = IPI_DescPtr_Tag; } } //we have a streamDescritpionIndex, use it if (StreamDescriptionIndex) { entry = (MPEGSampleEntryAtom*)ChainGetEntry(trak->Media->information->sampleTable->SampleDescription->atomList, StreamDescriptionIndex - 1); if (!entry) return M4InvalidMP4File; switch (entry->type) { case MPEGSampleEntryAtomType: //OK, delete the previous ESD e = OD_DeleteDescriptor((Descriptor **) &entry->esd->desc); if (e) return e; entry->esd->desc = esd; break; case MPEGVisualSampleEntryAtomType: entry_v = (MPEGVisualSampleEntryAtom*) entry; //OK, delete the previous ESD e = OD_DeleteDescriptor((Descriptor **) &entry_v->esd->desc); if (e) return e; entry_v->esd->desc = esd; break; case MPEGAudioSampleEntryAtomType: entry_a = (MPEGAudioSampleEntryAtom*) entry; //OK, delete the previous ESD e = OD_DeleteDescriptor((Descriptor **) &entry_a->esd->desc); if (e) return e; entry_a->esd->desc = esd; break; } } else { //need to check we're not in URL mode where only ONE description is allowed... StreamDescriptionIndex = ChainGetCount(trak->Media->information->sampleTable->SampleDescription->atomList); if (StreamDescriptionIndex) { entry = (MPEGSampleEntryAtom*)ChainGetEntry(trak->Media->information->sampleTable->SampleDescription->atomList, StreamDescriptionIndex - 1); if (!entry) return M4InvalidMP4File; if (entry->esd->desc->URLString) return M4BadParam; } //OK, check the handler and create the entry switch (trak->Media->handler->handlerType) { case M4_VisualMediaType: entry_v = (MPEGVisualSampleEntryAtom *) CreateAtom(MPEGVisualSampleEntryAtomType); if (!entry_v) return M4OutOfMem; //create an esdAtom entry_v->esd = (ESDAtom *) CreateAtom(ESDAtomType); entry_v->esd->desc = esd; //type cast possible now entry = (MPEGSampleEntryAtom*) entry_v; break; case M4_AudioMediaType: entry_a = (MPEGAudioSampleEntryAtom *) CreateAtom(MPEGAudioSampleEntryAtomType); entry_a->samplerate_hi = trak->Media->mediaHeader->timeScale; if (!entry_a) return M4OutOfMem; //create an esdAtom entry_a->esd = (ESDAtom *) CreateAtom(ESDAtomType); entry_a->esd->desc = esd; //type cast possible now entry = (MPEGSampleEntryAtom*) entry_a; break; case M4_ODMediaType: case M4_OCRMediaType: case M4_BIFSMediaType: case M4_MPEG7MediaType: case M4_OCIMediaType: case M4_IPMPMediaType: case M4_MPEGJMediaType: entry = (MPEGSampleEntryAtom *) CreateAtom(MPEGSampleEntryAtomType); //create an esdAtom entry->esd = (ESDAtom *) CreateAtom(ESDAtomType); entry->esd->desc = esd; break; } entry->dataReferenceIndex = DataReferenceIndex; //and add the entry to our table... e = stsd_AddAtom(trak->Media->information->sampleTable->SampleDescription, (Atom *) entry); if (e) return e; if(outStreamIndex) *outStreamIndex = ChainGetCount(trak->Media->information->sampleTable->SampleDescription->atomList); } return M4OK; }