GF_EXPORT GF_Err gf_isom_reset_hint_reader(GF_ISOFile *the_file, u32 trackNumber, u32 sample_start, u32 ts_offset, u32 sn_offset, u32 ssrc) { GF_Err e; GF_TrackBox *trak; GF_HintSampleEntryBox *entry; trak = gf_isom_get_track_from_file(the_file, trackNumber); if (!trak) return GF_BAD_PARAM; if (!sample_start) return GF_BAD_PARAM; if (sample_start>=trak->Media->information->sampleTable->SampleSize->sampleCount) return GF_BAD_PARAM; e = Media_GetSampleDesc(trak->Media, 1, (GF_SampleEntryBox **) &entry, NULL); if (e) return e; switch (entry->type) { case GF_ISOM_BOX_TYPE_RTP_STSD: break; default: return GF_NOT_SUPPORTED; } entry->hint_ref = NULL; e = Track_FindRef(trak, GF_ISOM_REF_HINT, &entry->hint_ref); if (e) return e; entry->cur_sample = sample_start; entry->pck_sn = 1 + sn_offset; entry->ssrc = ssrc; entry->ts_offset = ts_offset; if (entry->hint_sample) gf_isom_hint_sample_del(entry->hint_sample); entry->hint_sample = NULL; return GF_OK; }
M4Err M4H_AddStreamDescriptionData(M4File *the_file, u32 trackNumber, u32 SourceTrackID, u32 StreamDescriptionIndex, u16 DataLength, u32 offsetInDescription, u8 AtBegin) { TrackAtom *trak; HintSampleEntryAtom *entry; u32 count; u16 refIndex; HintPacket *pck; StreamDescDTE *dte; M4Err e; TrackReferenceTypeAtom *hint; M4Err reftype_AddRefTrack(TrackReferenceTypeAtom *ref, u32 trackID, u16 *outRefIndex); trak = GetTrackFromFile(the_file, trackNumber); if (!trak || !IsHintTrack(trak)) return M4BadParam; e = Media_GetSampleDesc(trak->Media, trak->Media->information->sampleTable->currentEntryIndex, (SampleEntryAtom **) &entry, &count); if (e) return e; if (!entry->w_sample) return M4BadParam; count = ChainGetCount(entry->w_sample->packetTable); if (!count) return M4BadParam; pck = ChainGetEntry(entry->w_sample->packetTable, count - 1); dte = (StreamDescDTE *) NewDTE(3); dte->byteOffset = offsetInDescription; dte->dataLength = DataLength; dte->streamDescIndex = StreamDescriptionIndex; if (SourceTrackID == trak->Header->trackID) { dte->trackRefIndex = -1; } else { //get (or set) the track reference index e = Track_FindRef(trak, M4_HintTrack_Ref, &hint); if (e) return e; e = reftype_AddRefTrack(hint, SourceTrackID, &refIndex); if (e) return e; //WARNING: IN QT, MUST BE 0-based !!! dte->trackRefIndex = (u8) (refIndex - 1); } return AddDTE_HintPacket(entry->w_sample->HintType, pck, (GenericDTE *)dte, AtBegin); }
GF_Err gf_isom_hint_sample_description_data(GF_ISOFile *the_file, u32 trackNumber, u32 SourceTrackID, u32 StreamDescriptionIndex, u16 DataLength, u32 offsetInDescription, u8 AtBegin) { GF_TrackBox *trak; GF_HintSampleEntryBox *entry; u32 count; u16 refIndex; GF_HintPacket *pck; GF_StreamDescDTE *dte; GF_Err e; GF_TrackReferenceTypeBox *hint; trak = gf_isom_get_track_from_file(the_file, trackNumber); if (!trak || !IsHintTrack(trak)) return GF_BAD_PARAM; e = Media_GetSampleDesc(trak->Media, trak->Media->information->sampleTable->currentEntryIndex, (GF_SampleEntryBox **) &entry, &count); if (e) return e; if (!entry->hint_sample) return GF_BAD_PARAM; count = gf_list_count(entry->hint_sample->packetTable); if (!count) return GF_BAD_PARAM; pck = (GF_HintPacket *)gf_list_get(entry->hint_sample->packetTable, count - 1); dte = (GF_StreamDescDTE *) NewDTE(3); dte->byteOffset = offsetInDescription; dte->dataLength = DataLength; dte->streamDescIndex = StreamDescriptionIndex; if (SourceTrackID == trak->Header->trackID) { dte->trackRefIndex = (s8) -1; } else { //get (or set) the track reference index e = Track_FindRef(trak, GF_ISOM_REF_HINT, &hint); if (e) return e; e = reftype_AddRefTrack(hint, SourceTrackID, &refIndex); if (e) return e; //WARNING: IN QT, MUST BE 0-based !!! dte->trackRefIndex = (u8) (refIndex - 1); } return gf_isom_hint_pck_add_dte(entry->hint_sample->HintType, pck, (GF_GenericDTE *)dte, AtBegin); }
GF_Err gf_isom_setup_hint_track(GF_ISOFile *movie, u32 trackNumber, u32 HintType) { GF_Err e; GF_TrackBox *trak; GF_TrackReferenceBox *tref; GF_TrackReferenceTypeBox *dpnd; GF_HintMediaHeaderBox *hmhd; //UDTA related ... GF_UserDataBox *udta; //what do we support switch (HintType) { case GF_ISOM_HINT_RTP: break; default: return GF_NOT_SUPPORTED; } e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); if (e) return e; trak = gf_isom_get_track_from_file(movie, trackNumber); if (!trak) return gf_isom_last_error(movie); //check we have a hint ... if ( !IsHintTrack(trak)) { return GF_BAD_PARAM; } hmhd = (GF_HintMediaHeaderBox *)trak->Media->information->InfoHeader; //make sure the subtype was not already defined if (hmhd->subType) return GF_BAD_PARAM; //store the HintTrack format for later use... hmhd->subType = HintType; //hint tracks always have a tref and everything ... if (!trak->References) { 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; //do we have a hint reference on this trak ??? e = Track_FindRef(trak, GF_ISOM_BOX_TYPE_HINT, &dpnd); if (e) return e; //if yes, return false (existing hint track...) if (dpnd) return GF_BAD_PARAM; //create our dep dpnd = (GF_TrackReferenceTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_REFT); dpnd->reference_type = GF_ISOM_BOX_TYPE_HINT; e = tref_AddBox((GF_Box*)tref, (GF_Box *) dpnd); if (e) return e; //for RTP, we need to do some UDTA-related stuff... if (HintType != GF_ISOM_HINT_RTP) return GF_OK; if (!trak->udta) { //create one udta = (GF_UserDataBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_UDTA); e = trak_AddBox((GF_Box*)trak, (GF_Box *) udta); if (e) return e; } udta = trak->udta; //HNTI e = udta_AddBox(udta, gf_isom_box_new(GF_ISOM_BOX_TYPE_HNTI)); if (e) return e; /* //NAME e = udta_AddBox(udta, gf_isom_box_new(GF_ISOM_BOX_TYPE_NAME)); if (e) return e; //HINF return udta_AddBox(udta, gf_isom_box_new(GF_ISOM_BOX_TYPE_HINF)); */ return GF_OK; }
GF_Err gf_isom_hint_sample_data(GF_ISOFile *the_file, u32 trackNumber, u32 SourceTrackID, u32 SampleNumber, u16 DataLength, u32 offsetInSample, char *extra_data, u8 AtBegin) { GF_TrackBox *trak; GF_HintSampleEntryBox *entry; u32 count; u16 refIndex; GF_HintPacket *pck; GF_SampleDTE *dte; GF_Err e; GF_TrackReferenceTypeBox *hint; trak = gf_isom_get_track_from_file(the_file, trackNumber); if (!trak || !IsHintTrack(trak)) return GF_BAD_PARAM; e = Media_GetSampleDesc(trak->Media, trak->Media->information->sampleTable->currentEntryIndex, (GF_SampleEntryBox **) &entry, &count); if (e) return e; if (!entry->hint_sample) return GF_BAD_PARAM; count = gf_list_count(entry->hint_sample->packetTable); if (!count) return GF_BAD_PARAM; pck = (GF_HintPacket *)gf_list_get(entry->hint_sample->packetTable, count - 1); dte = (GF_SampleDTE *) NewDTE(2); dte->dataLength = DataLength; dte->sampleNumber = SampleNumber; dte->byteOffset = offsetInSample; //we're getting data from another track if (SourceTrackID != trak->Header->trackID) { //get (or set) the track reference index e = Track_FindRef(trak, GF_ISOM_REF_HINT, &hint); if (e) return e; e = reftype_AddRefTrack(hint, SourceTrackID, &refIndex); if (e) return e; //WARNING: IN QT, MUST BE 0-based !!! dte->trackRefIndex = (u8) (refIndex - 1); } else { //we're in the hint track dte->trackRefIndex = (s8) -1; //basic check... if (SampleNumber > trak->Media->information->sampleTable->SampleSize->sampleCount + 1) { DelDTE((GF_GenericDTE *)dte); return GF_BAD_PARAM; } //are we in the current sample ?? if (!SampleNumber || (SampleNumber == trak->Media->information->sampleTable->SampleSize->sampleCount + 1)) { //we adding some stuff in the current sample ... dte->byteOffset += entry->hint_sample->dataLength; entry->hint_sample->AdditionalData = (char*)gf_realloc(entry->hint_sample->AdditionalData, sizeof(char) * (entry->hint_sample->dataLength + DataLength)); if (AtBegin) { if (entry->hint_sample->dataLength) memmove(entry->hint_sample->AdditionalData + DataLength, entry->hint_sample->AdditionalData, entry->hint_sample->dataLength); memcpy(entry->hint_sample->AdditionalData, extra_data, DataLength); /*offset existing DTE*/ gf_isom_hint_pck_offset(entry->hint_sample->HintType, pck, DataLength, SampleNumber); } else { memcpy(entry->hint_sample->AdditionalData + entry->hint_sample->dataLength, extra_data, DataLength); } entry->hint_sample->dataLength += DataLength; //and set the sample number ... dte->sampleNumber = trak->Media->information->sampleTable->SampleSize->sampleCount + 1; } } //OK, add the entry return gf_isom_hint_pck_add_dte(entry->hint_sample->HintType, pck, (GF_GenericDTE *)dte, AtBegin); }
// Rewrite the good dependancies when an OD AU is extracted from the file static u32 Media_FindOD_ID(GF_MediaBox *mdia, GF_ISOSample *sample, u32 track_id) { GF_Err e; GF_ODCodec *ODdecode; GF_ODCom *com; u32 the_od_id; GF_ODUpdate *odU; GF_ESD *esd; GF_Descriptor *desc; GF_TrackReferenceTypeBox *mpod; u32 i, j; if (!mdia || !sample || !sample->data || !sample->dataLength) return 0; mpod = NULL; e = Track_FindRef(mdia->mediaTrack, GF_ISOM_BOX_TYPE_MPOD, &mpod); if (e) return 0; //no references, nothing to do... if (!mpod) return 0; the_od_id = 0; ODdecode = gf_odf_codec_new(); if (!ODdecode) return 0; e = gf_odf_codec_set_au(ODdecode, sample->data, sample->dataLength); if (e) goto err_exit; e = gf_odf_codec_decode(ODdecode); if (e) goto err_exit; while (1) { GF_List *esd_list = NULL; com = gf_odf_codec_get_com(ODdecode); if (!com) break; if (com->tag != GF_ODF_OD_UPDATE_TAG) continue; odU = (GF_ODUpdate *) com; i=0; while ((desc = (GF_Descriptor*)gf_list_enum(odU->objectDescriptors, &i))) { switch (desc->tag) { case GF_ODF_OD_TAG: case GF_ODF_IOD_TAG: esd_list = ((GF_ObjectDescriptor *)desc)->ESDescriptors; break; default: continue; } j=0; while ((esd = (GF_ESD*)gf_list_enum( esd_list, &j))){ if (esd->ESID==track_id) { the_od_id = ((GF_IsomObjectDescriptor*)desc)->objectDescriptorID; break; } } if (the_od_id) break; } gf_odf_com_del((GF_ODCom **)&odU); if (the_od_id) break; } err_exit: gf_odf_codec_del(ODdecode); if (e) return 0; return the_od_id; }
// Rewrite the good dependancies when an OD AU is extracted from the file GF_Err Media_RewriteODFrame(GF_MediaBox *mdia, GF_ISOSample *sample) { GF_Err e; GF_ODCodec *ODdecode; GF_ODCodec *ODencode; GF_ODCom *com; //the commands we proceed GF_ESDUpdate *esdU, *esdU2; GF_ESDRemove *esdR, *esdR2; GF_ODUpdate *odU, *odU2; //the desc they contain GF_ObjectDescriptor *od; GF_IsomObjectDescriptor *isom_od; GF_ESD *esd; GF_ES_ID_Ref *ref; GF_Descriptor *desc; GF_TrackReferenceTypeBox *mpod; u32 i, j, skipped; if (!mdia || !sample || !sample->data || !sample->dataLength) return GF_BAD_PARAM; mpod = NULL; e = Track_FindRef(mdia->mediaTrack, GF_ISOM_BOX_TYPE_MPOD, &mpod); if (e) return e; //no references, nothing to do... if (!mpod) return GF_OK; ODdecode = gf_odf_codec_new(); if (!ODdecode) return GF_OUT_OF_MEM; ODencode = gf_odf_codec_new(); if (!ODencode) { gf_odf_codec_del(ODdecode); return GF_OUT_OF_MEM; } e = gf_odf_codec_set_au(ODdecode, sample->data, sample->dataLength); if (e) goto err_exit; e = gf_odf_codec_decode(ODdecode); if (e) goto err_exit; while (1) { com = gf_odf_codec_get_com(ODdecode); if (!com) break; //we only need to rewrite commands with ESDs inside: ESDUpdate and ODUpdate switch (com->tag) { case GF_ODF_OD_UPDATE_TAG: odU = (GF_ODUpdate *) com; odU2 = (GF_ODUpdate *) gf_odf_com_new(GF_ODF_OD_UPDATE_TAG); i=0; while ((desc = (GF_Descriptor*)gf_list_enum(odU->objectDescriptors, &i))) { switch (desc->tag) { case GF_ODF_OD_TAG: case GF_ODF_ISOM_OD_TAG: //IOD can be used in OD streams case GF_ODF_ISOM_IOD_TAG: break; default: return GF_ISOM_INVALID_FILE; } e = gf_odf_desc_copy(desc, (GF_Descriptor **)&isom_od); if (e) goto err_exit; //create our OD... if (desc->tag == GF_ODF_ISOM_IOD_TAG) { od = (GF_ObjectDescriptor *) gf_malloc(sizeof(GF_InitialObjectDescriptor)); } else { od = (GF_ObjectDescriptor *) gf_malloc(sizeof(GF_ObjectDescriptor)); } if (!od) { e = GF_OUT_OF_MEM; goto err_exit; } od->ESDescriptors = gf_list_new(); //and duplicate... od->objectDescriptorID = isom_od->objectDescriptorID; od->tag = GF_ODF_OD_TAG; od->URLString = isom_od->URLString; isom_od->URLString = NULL; od->extensionDescriptors = isom_od->extensionDescriptors; isom_od->extensionDescriptors = NULL; od->IPMP_Descriptors = isom_od->IPMP_Descriptors; isom_od->IPMP_Descriptors = NULL; od->OCIDescriptors = isom_od->OCIDescriptors; isom_od->OCIDescriptors = NULL; //init as IOD if (isom_od->tag == GF_ODF_ISOM_IOD_TAG) { ((GF_InitialObjectDescriptor *)od)->audio_profileAndLevel = ((GF_IsomInitialObjectDescriptor *)isom_od)->audio_profileAndLevel; ((GF_InitialObjectDescriptor *)od)->inlineProfileFlag = ((GF_IsomInitialObjectDescriptor *)isom_od)->inlineProfileFlag; ((GF_InitialObjectDescriptor *)od)->graphics_profileAndLevel = ((GF_IsomInitialObjectDescriptor *)isom_od)->graphics_profileAndLevel; ((GF_InitialObjectDescriptor *)od)->OD_profileAndLevel = ((GF_IsomInitialObjectDescriptor *)isom_od)->OD_profileAndLevel; ((GF_InitialObjectDescriptor *)od)->scene_profileAndLevel = ((GF_IsomInitialObjectDescriptor *)isom_od)->scene_profileAndLevel; ((GF_InitialObjectDescriptor *)od)->visual_profileAndLevel = ((GF_IsomInitialObjectDescriptor *)isom_od)->visual_profileAndLevel; ((GF_InitialObjectDescriptor *)od)->IPMPToolList = ((GF_IsomInitialObjectDescriptor *)isom_od)->IPMPToolList; ((GF_IsomInitialObjectDescriptor *)isom_od)->IPMPToolList = NULL; } //then rewrite the ESDesc j=0; while ((ref = (GF_ES_ID_Ref*)gf_list_enum(isom_od->ES_ID_RefDescriptors, &j))){ //if the ref index is not valid, skip this desc... if (!mpod->trackIDs || gf_isom_get_track_from_id(mdia->mediaTrack->moov, mpod->trackIDs[ref->trackRef - 1]) == NULL) continue; //OK, get the esd e = GetESDForTime(mdia->mediaTrack->moov, mpod->trackIDs[ref->trackRef - 1], sample->DTS, &esd); if (!e) e = gf_odf_desc_add_desc((GF_Descriptor *) od, (GF_Descriptor *) esd); if (e) { gf_odf_desc_del((GF_Descriptor *)od); gf_odf_com_del((GF_ODCom **)&odU2); gf_odf_desc_del((GF_Descriptor *)isom_od); gf_odf_com_del((GF_ODCom **)&odU); goto err_exit; } } //delete our desc gf_odf_desc_del((GF_Descriptor *)isom_od); gf_list_add(odU2->objectDescriptors, od); } //clean a bit gf_odf_com_del((GF_ODCom **)&odU); gf_odf_codec_add_com(ODencode, (GF_ODCom *)odU2); break; case GF_ODF_ESD_UPDATE_TAG: esdU = (GF_ESDUpdate *) com; esdU2 = (GF_ESDUpdate *) gf_odf_com_new(GF_ODF_ESD_UPDATE_TAG); esdU2->ODID = esdU->ODID; i=0; while ((ref = (GF_ES_ID_Ref*)gf_list_enum(esdU->ESDescriptors, &i))) { //if the ref index is not valid, skip this desc... if (gf_isom_get_track_from_id(mdia->mediaTrack->moov, mpod->trackIDs[ref->trackRef - 1]) == NULL) continue; //OK, get the esd e = GetESDForTime(mdia->mediaTrack->moov, mpod->trackIDs[ref->trackRef - 1], sample->DTS, &esd); if (e) goto err_exit; gf_list_add(esdU2->ESDescriptors, esd); } gf_odf_com_del((GF_ODCom **)&esdU); gf_odf_codec_add_com(ODencode, (GF_ODCom *)esdU2); break; //brand new case: the ESRemove follows the same principle according to the spec... case GF_ODF_ESD_REMOVE_REF_TAG: //both commands have the same structure, only the tags change esdR = (GF_ESDRemove *) com; esdR2 = (GF_ESDRemove *) gf_odf_com_new(GF_ODF_ESD_REMOVE_TAG); esdR2->ODID = esdR->ODID; esdR2->NbESDs = esdR->NbESDs; //alloc our stuff esdR2->ES_ID = (unsigned short*)gf_malloc(sizeof(u32) * esdR->NbESDs); if (!esdR2->ES_ID) { e = GF_OUT_OF_MEM; goto err_exit; } skipped = 0; //get the ES_ID in the mpod indicated in the ES_ID[] for (i = 0; i < esdR->NbESDs; i++) { //if the ref index is not valid, remove this desc... if (gf_isom_get_track_from_id(mdia->mediaTrack->moov, mpod->trackIDs[esdR->ES_ID[i] - 1]) == NULL) { skipped ++; } else { //the command in the file has the ref index of the trackID in the mpod esdR2->ES_ID[i - skipped] = mpod->trackIDs[esdR->ES_ID[i] - 1]; } } //gf_realloc... if (skipped && (skipped != esdR2->NbESDs) ) { esdR2->NbESDs -= skipped; esdR2->ES_ID = (unsigned short*)gf_realloc(esdR2->ES_ID, sizeof(u32) * esdR2->NbESDs); } gf_odf_com_del((GF_ODCom **)&esdR); gf_odf_codec_add_com(ODencode, (GF_ODCom *)esdR2); break; default: e = gf_odf_codec_add_com(ODencode, com); if (e) goto err_exit; } } //encode our new AU e = gf_odf_codec_encode(ODencode, 1); if (e) goto err_exit; //and set the buffer in the sample gf_free(sample->data); sample->data = NULL; sample->dataLength = 0; e = gf_odf_codec_get_au(ODencode, &sample->data, &sample->dataLength); err_exit: gf_odf_codec_del(ODdecode); gf_odf_codec_del(ODencode); return e; }
// Update the dependancies when an OD AU is inserted in the file GF_Err Media_ParseODFrame(GF_MediaBox *mdia, GF_ISOSample *sample, GF_ISOSample **od_samp) { GF_TrackReferenceBox *tref; GF_TrackReferenceTypeBox *mpod; GF_Err e; GF_ODCom *com; GF_ODCodec *ODencode; GF_ODCodec *ODdecode; u32 i, j; //the commands we proceed GF_ESDUpdate *esdU, *esdU2; GF_ESDRemove *esdR, *esdR2; GF_ODUpdate *odU, *odU2; //the desc they contain GF_ObjectDescriptor *od; GF_IsomObjectDescriptor *isom_od; GF_ESD *esd; GF_ES_ID_Ref *ref; GF_Descriptor *desc; *od_samp = NULL; if (!mdia || !sample || !sample->data || !sample->dataLength) return GF_BAD_PARAM; //First find the references, and create them if none tref = mdia->mediaTrack->References; if (!tref) { tref = (GF_TrackReferenceBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_TREF); e = trak_AddBox((GF_Box*)mdia->mediaTrack, (GF_Box *) tref); if (e) return e; } //then find the OD reference, and create it if none e = Track_FindRef(mdia->mediaTrack, GF_ISOM_BOX_TYPE_MPOD, &mpod); if (e) return e; if (!mpod) { mpod = (GF_TrackReferenceTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_REFT); mpod->reference_type = GF_ISOM_BOX_TYPE_MPOD; e = tref_AddBox((GF_Box*)tref, (GF_Box *)mpod); if (e) return e; } //OK, create our codecs ODencode = gf_odf_codec_new(); if (!ODencode) return GF_OUT_OF_MEM; ODdecode = gf_odf_codec_new(); if (!ODdecode) return GF_OUT_OF_MEM; e = gf_odf_codec_set_au(ODdecode, sample->data, sample->dataLength); if (e) goto err_exit; e = gf_odf_codec_decode(ODdecode); if (e) goto err_exit; while (1) { com = gf_odf_codec_get_com(ODdecode); if (!com) break; //check our commands switch (com->tag) { //Rewrite OD Update case GF_ODF_OD_UPDATE_TAG: //duplicate our command odU = (GF_ODUpdate *) com; odU2 = (GF_ODUpdate *) gf_odf_com_new(GF_ODF_OD_UPDATE_TAG); i=0; while ((desc = (GF_Descriptor*)gf_list_enum(odU->objectDescriptors, &i))) { //both OD and IODs are accepted switch (desc->tag) { case GF_ODF_OD_TAG: case GF_ODF_IOD_TAG: break; default: e = GF_ODF_INVALID_DESCRIPTOR; goto err_exit; } //get the esd e = gf_odf_desc_copy(desc, (GF_Descriptor **)&od); if (e) goto err_exit; if (desc->tag == GF_ODF_OD_TAG) { isom_od = (GF_IsomObjectDescriptor *) gf_malloc(sizeof(GF_IsomObjectDescriptor)); isom_od->tag = GF_ODF_ISOM_OD_TAG; } else { isom_od = (GF_IsomObjectDescriptor *) gf_malloc(sizeof(GF_IsomInitialObjectDescriptor)); isom_od->tag = GF_ODF_ISOM_IOD_TAG; //copy PL ((GF_IsomInitialObjectDescriptor *)isom_od)->inlineProfileFlag = ((GF_InitialObjectDescriptor *)od)->inlineProfileFlag; ((GF_IsomInitialObjectDescriptor *)isom_od)->graphics_profileAndLevel = ((GF_InitialObjectDescriptor *)od)->graphics_profileAndLevel; ((GF_IsomInitialObjectDescriptor *)isom_od)->audio_profileAndLevel = ((GF_InitialObjectDescriptor *)od)->audio_profileAndLevel; ((GF_IsomInitialObjectDescriptor *)isom_od)->OD_profileAndLevel = ((GF_InitialObjectDescriptor *)od)->OD_profileAndLevel; ((GF_IsomInitialObjectDescriptor *)isom_od)->scene_profileAndLevel = ((GF_InitialObjectDescriptor *)od)->scene_profileAndLevel; ((GF_IsomInitialObjectDescriptor *)isom_od)->visual_profileAndLevel = ((GF_InitialObjectDescriptor *)od)->visual_profileAndLevel; ((GF_IsomInitialObjectDescriptor *)isom_od)->IPMPToolList = ((GF_InitialObjectDescriptor *)od)->IPMPToolList; ((GF_InitialObjectDescriptor *)od)->IPMPToolList = NULL; } //in OD stream only ref desc are accepted isom_od->ES_ID_RefDescriptors = gf_list_new(); isom_od->ES_ID_IncDescriptors = NULL; //TO DO: check that a given sampleDescription exists isom_od->extensionDescriptors = od->extensionDescriptors; od->extensionDescriptors = NULL; isom_od->IPMP_Descriptors = od->IPMP_Descriptors; od->IPMP_Descriptors = NULL; isom_od->OCIDescriptors = od->OCIDescriptors; od->OCIDescriptors = NULL; isom_od->URLString = od->URLString; od->URLString = NULL; isom_od->objectDescriptorID = od->objectDescriptorID; j=0; while ((esd = (GF_ESD*)gf_list_enum(od->ESDescriptors, &j))) { ref = (GF_ES_ID_Ref *) gf_odf_desc_new(GF_ODF_ESD_REF_TAG); //1 to 1 mapping trackID and ESID. Add this track to MPOD //if track does not exist, this will be remove while reading the OD stream e = reftype_AddRefTrack(mpod, esd->ESID, &ref->trackRef); e = gf_odf_desc_add_desc((GF_Descriptor *)isom_od, (GF_Descriptor *)ref); if (e) goto err_exit; } //delete our desc gf_odf_desc_del((GF_Descriptor *)od); //and add the new one to our command gf_list_add(odU2->objectDescriptors, isom_od); } //delete the command gf_odf_com_del((GF_ODCom **)&odU); //and add the new one to the codec gf_odf_codec_add_com(ODencode, (GF_ODCom *)odU2); break; //Rewrite ESD Update case GF_ODF_ESD_UPDATE_TAG: esdU = (GF_ESDUpdate *) com; esdU2 = (GF_ESDUpdate *) gf_odf_com_new(GF_ODF_ESD_UPDATE_TAG); esdU2->ODID = esdU->ODID; i=0; while ((esd = (GF_ESD*)gf_list_enum(esdU->ESDescriptors, &i))) { ref = (GF_ES_ID_Ref *) gf_odf_desc_new(GF_ODF_ESD_REF_TAG); //1 to 1 mapping trackID and ESID e = reftype_AddRefTrack(mpod, esd->ESID, &ref->trackRef); e = gf_list_add(esdU2->ESDescriptors, ref); if (e) goto err_exit; } gf_odf_com_del((GF_ODCom **)&esdU); gf_odf_codec_add_com(ODencode, (GF_ODCom *)esdU2); break; //Brand new case: the ESRemove has to be rewritten too according to the specs... case GF_ODF_ESD_REMOVE_TAG: esdR = (GF_ESDRemove *) com; esdR2 = (GF_ESDRemove *) gf_odf_com_new(GF_ODF_ESD_REMOVE_TAG); //change the tag for the file format esdR2->tag = GF_ODF_ESD_REMOVE_REF_TAG; esdR2->ODID = esdR->ODID; esdR2->NbESDs = esdR->NbESDs; if (esdR->NbESDs) { //alloc our stuff esdR2->ES_ID = (unsigned short*)gf_malloc(sizeof(u32) * esdR->NbESDs); if (!esdR2->ES_ID) { e = GF_OUT_OF_MEM; goto err_exit; } for (i = 0; i < esdR->NbESDs; i++) { //1 to 1 mapping trackID and ESID e = reftype_AddRefTrack(mpod, esdR->ES_ID[i], &esdR2->ES_ID[i]); if (e) goto err_exit; } } gf_odf_com_del(&com); gf_odf_codec_add_com(ODencode, (GF_ODCom *)esdR2); break; //Add the command as is default: e = gf_odf_codec_add_com(ODencode, com); if (e) goto err_exit; } } //encode our new AU e = gf_odf_codec_encode(ODencode, 1); if (e) goto err_exit; //and set the buffer in the sample *od_samp = gf_isom_sample_new(); (*od_samp)->CTS_Offset = sample->CTS_Offset; (*od_samp)->DTS = sample->DTS; (*od_samp)->IsRAP = sample->IsRAP; e = gf_odf_codec_get_au(ODencode, & (*od_samp)->data, & (*od_samp)->dataLength); if (e) { gf_isom_sample_del(od_samp); *od_samp = NULL; } err_exit: gf_odf_codec_del(ODencode); gf_odf_codec_del(ODdecode); return e; }
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; }
// Update the dependancies when an OD AU is inserted in the file M4Err Media_ParseODFrame(MediaAtom *mdia, M4Sample *sample) { TrackReferenceAtom *tref; TrackReferenceTypeAtom *mpod; M4Err e; ODCommand *com; LPODCODEC ODencode; LPODCODEC ODdecode; u32 i, j; //the commands we proceed ESDescriptorUpdate *esdU, *esdU2; ESDescriptorRemove *esdR, *esdR2; ObjectDescriptorUpdate *odU, *odU2; //the desc they contain ObjectDescriptor *od; M4F_ObjectDescriptor *m4_od; ESDescriptor *esd; ES_ID_Ref *ref; Descriptor *desc; M4Err reftype_AddRefTrack(TrackReferenceTypeAtom *ref, u32 trackID, u16 *outRefIndex); M4Err trak_AddAtom(TrackAtom *ptr, Atom *a); M4Err tref_AddAtom(TrackReferenceAtom *ptr, Atom *a); if (!mdia || !sample || !sample->data || !sample->dataLength) return M4BadParam; //First find the references, and create them if none tref = mdia->mediaTrack->References; if (!tref) { tref = (TrackReferenceAtom *) CreateAtom(TrackReferenceAtomType); e = trak_AddAtom(mdia->mediaTrack, (Atom *) tref); if (e) return e; } //then find the OD reference, and create it if none e = Track_FindRef(mdia->mediaTrack, ODTrackReferenceAtomType, &mpod); if (e) return e; if (!mpod) { mpod = (TrackReferenceTypeAtom *) CreateAtom(ODTrackReferenceAtomType); e = tref_AddAtom(tref, (Atom *)mpod); if (e) return e; } //OK, create our codecs ODencode = OD_NewCodec(OD_WRITE); if (!ODencode) return M4OutOfMem; ODdecode = OD_NewCodec(OD_READ); if (!ODdecode) return M4OutOfMem; e = OD_SetBuffer(ODdecode, sample->data, sample->dataLength); if (e) goto err_exit; e = OD_DecodeAU(ODdecode); if (e) goto err_exit; while (1) { com = OD_GetCommand(ODdecode); if (!com) break; //check our commands switch (com->tag) { //Rewrite OD Update case ODUpdate_Tag: //duplicate our command odU = (ObjectDescriptorUpdate *) com; odU2 = (ObjectDescriptorUpdate *) OD_NewCommand(ODUpdate_Tag); for (i = 0; i < ChainGetCount(odU->objectDescriptors); i++) { desc = (Descriptor*)ChainGetEntry(odU->objectDescriptors, i); //both OD and IODs are accepted switch (desc->tag) { case ObjectDescriptor_Tag: case InitialObjectDescriptor_Tag: break; default: e = M4InvalidDescriptor; goto err_exit; } //get the esd e = OD_DuplicateDescriptor(desc, (Descriptor **)&od); if (e) goto err_exit; if (desc->tag == ObjectDescriptor_Tag) { m4_od = (M4F_ObjectDescriptor *) malloc(sizeof(M4F_ObjectDescriptor)); m4_od->tag = MP4_OD_Tag; } else { m4_od = (M4F_ObjectDescriptor *) malloc(sizeof(M4F_InitialObjectDescriptor)); m4_od->tag = MP4_IOD_Tag; //copy PL ((M4F_InitialObjectDescriptor *)m4_od)->inlineProfileFlag = ((InitialObjectDescriptor *)od)->inlineProfileFlag; ((M4F_InitialObjectDescriptor *)m4_od)->graphics_profileAndLevel = ((InitialObjectDescriptor *)od)->graphics_profileAndLevel; ((M4F_InitialObjectDescriptor *)m4_od)->audio_profileAndLevel = ((InitialObjectDescriptor *)od)->audio_profileAndLevel; ((M4F_InitialObjectDescriptor *)m4_od)->OD_profileAndLevel = ((InitialObjectDescriptor *)od)->OD_profileAndLevel; ((M4F_InitialObjectDescriptor *)m4_od)->scene_profileAndLevel = ((InitialObjectDescriptor *)od)->scene_profileAndLevel; ((M4F_InitialObjectDescriptor *)m4_od)->visual_profileAndLevel = ((InitialObjectDescriptor *)od)->visual_profileAndLevel; } //in OD stream only ref desc are accepted m4_od->ES_ID_RefDescriptors = NewChain(); m4_od->ES_ID_IncDescriptors = NULL; //TO DO: check that a given sampleDescription exists m4_od->extensionDescriptors = od->extensionDescriptors; od->extensionDescriptors = NULL; m4_od->IPMPDescriptorPointers = od->IPMPDescriptorPointers; od->IPMPDescriptorPointers = NULL; m4_od->OCIDescriptors = od->OCIDescriptors; od->OCIDescriptors = NULL; m4_od->URLString = od->URLString; od->URLString = NULL; m4_od->objectDescriptorID = od->objectDescriptorID; for (j = 0; j < ChainGetCount(od->ESDescriptors); j++) { esd =(ESDescriptor*)ChainGetEntry(od->ESDescriptors, j); ref = (ES_ID_Ref *) OD_NewDescriptor(ES_ID_RefTag); //1 to 1 mapping trackID and ESID. Add this track to MPOD //if track does not exist, this will be remove while reading the OD stream e = reftype_AddRefTrack(mpod, esd->ESID, &ref->trackRef); e = OD_AddDescToDesc((Descriptor *)m4_od, (Descriptor *)ref); if (e) goto err_exit; } //delete our desc OD_DeleteDescriptor((Descriptor **)&od); //and add the new one to our command ChainAddEntry(odU2->objectDescriptors, m4_od); } //delete the command OD_DeleteCommand((ODCommand **)&odU); //and add the new one to the codec OD_AddCommand(ODencode, (ODCommand *)odU2); break; //Rewrite ESD Update case ESDUpdate_Tag: esdU = (ESDescriptorUpdate *) com; esdU2 = (ESDescriptorUpdate *) OD_NewCommand(ESDUpdate_Tag); esdU2->ODID = esdU->ODID; for (i = 0; i< ChainGetCount(esdU->ESDescriptors); i++) { esd = (ESDescriptor*)ChainGetEntry(esdU->ESDescriptors, i); ref = (ES_ID_Ref *) OD_NewDescriptor(ES_ID_RefTag); //1 to 1 mapping trackID and ESID e = reftype_AddRefTrack(mpod, esd->ESID, &ref->trackRef); e = ChainAddEntry(esdU2->ESDescriptors, ref); if (e) goto err_exit; } OD_DeleteCommand((ODCommand **)&esdU); OD_AddCommand(ODencode, (ODCommand *)esdU2); break; //Brand new case: the ESRemove has to be rewritten too according to the specs... case ESDRemove_Tag: esdR = (ESDescriptorRemove *) com; esdR2 = (ESDescriptorRemove *) OD_NewCommand(ESDRemove_Tag); //change the tag for the file format esdR2->tag = ESDRemove_Ref_Tag; esdR2->ODID = esdR->ODID; esdR2->NbESDs = esdR->NbESDs; //alloc our stuff esdR2->ES_ID = (unsigned short*)malloc(sizeof(u32) * esdR->NbESDs); if (!esdR2->ES_ID) { e = M4OutOfMem; goto err_exit; } for (i = 0; i < esdR->NbESDs; i++) { //1 to 1 mapping trackID and ESID e = reftype_AddRefTrack(mpod, esdR->ES_ID[i], &esdR2->ES_ID[i]); if (e) goto err_exit; } OD_DeleteCommand(&com); OD_AddCommand(ODencode, (ODCommand *)esdR2); break; //Add the command as is default: e = OD_AddCommand(ODencode, com); if (e) goto err_exit; } } //encode our new AU e = OD_EncodeAU(ODencode); if (e) goto err_exit; //and set the buffer in the sample free(sample->data); sample->data = NULL; sample->dataLength = 0; e = OD_GetEncodedAU(ODencode, &sample->data, &sample->dataLength); err_exit: OD_DeleteCodec(ODencode); OD_DeleteCodec(ODdecode); return e; }
M4Err M4H_SetupHintTrack(M4File *the_file, u32 trackNumber, u32 HintType) { M4Err e; TrackAtom *trak; TrackReferenceAtom *tref; TrackReferenceTypeAtom *dpnd; HintMediaHeaderAtom *hmhd; //UDTA related ... UserDataAtom *udta; M4Err tref_AddAtom(TrackReferenceAtom *ptr, Atom *a); M4Err trak_AddAtom(TrackAtom *ptr, Atom *a); M4Err moov_AddAtom(MovieAtom *ptr, Atom *a); M4Err udta_AddAtom(UserDataAtom *ptr, Atom *a); //what do we support switch (HintType) { case M4_Hint_RTP: break; default: return M4NotSupported; } trak = GetTrackFromFile(the_file, trackNumber); if (!trak) return M4_GetLastError(the_file); if (((M4Movie *)the_file)->openMode != M4_OPEN_EDIT) return M4InvalidMP4Mode; //check we have a hint ... if ( !IsHintTrack(trak)) { return M4BadParam; } hmhd = (HintMediaHeaderAtom *)trak->Media->information->InfoHeader; //make sure the subtype was not already defined if (hmhd->subType) return M4HintPresent; //store the HintTrack format for later use... hmhd->subType = HintType; //hint tracks always have a tref and everything ... if (!trak->References) { if (!trak->References) { tref = (TrackReferenceAtom *) CreateAtom(TrackReferenceAtomType); e = trak_AddAtom(trak, (Atom *)tref); if (e) return e; } } tref = trak->References; //do we have a hint reference on this trak ??? e = Track_FindRef(trak, HintTrackReferenceAtomType, &dpnd); if (e) return e; //if yes, return false (existing hint track...) if (dpnd) return M4HintPresent; //create our dep dpnd = (TrackReferenceTypeAtom *) CreateAtom(HintTrackReferenceAtomType); e = tref_AddAtom(tref, (Atom *) dpnd); if (e) return e; //for RTP, we need to do some UDTA-related stuff... if (HintType != M4_Hint_RTP) return M4OK; if (!trak->udta) { //create one udta = (UserDataAtom *) CreateAtom(UserDataAtomType); e = trak_AddAtom(trak, (Atom *) udta); if (e) return e; } udta = trak->udta; //end-of-udta marker for Darwin... // if (!udta->voidAtom) { // e = udta_AddAtom(udta, CreateAtom(VoidAtomType)); // if (e) return e; // } //HNTI e = udta_AddAtom(udta, CreateAtom(HintTrackInfoAtomType)); if (e) return e; /* //NAME e = udta_AddAtom(udta, CreateAtom(nameAtomType)); if (e) return e; //HINF return udta_AddAtom(udta, CreateAtom(HintInfoAtomType)); */ return M4OK; }
M4Err M4H_AddSampleData(M4File *the_file, u32 trackNumber, u32 SourceTrackID, u32 SampleNumber, u16 DataLength, u32 offsetInSample, char *extra_data, u8 AtBegin) { TrackAtom *trak; HintSampleEntryAtom *entry; u32 count; u16 refIndex; HintPacket *pck; SampleDTE *dte; M4Err e; TrackReferenceTypeAtom *hint; M4Err reftype_AddRefTrack(TrackReferenceTypeAtom *ref, u32 trackID, u16 *outRefIndex); trak = GetTrackFromFile(the_file, trackNumber); if (!trak || !IsHintTrack(trak)) return M4BadParam; e = Media_GetSampleDesc(trak->Media, trak->Media->information->sampleTable->currentEntryIndex, (SampleEntryAtom **) &entry, &count); if (e) return e; if (!entry->w_sample) return M4BadParam; count = ChainGetCount(entry->w_sample->packetTable); if (!count) return M4BadParam; pck = ChainGetEntry(entry->w_sample->packetTable, count - 1); dte = (SampleDTE *) NewDTE(2); dte->dataLength = DataLength; dte->sampleNumber = SampleNumber; dte->byteOffset = offsetInSample; //we're getting data from another track if (SourceTrackID != trak->Header->trackID) { //get (or set) the track reference index e = Track_FindRef(trak, M4_HintTrack_Ref, &hint); if (e) return e; e = reftype_AddRefTrack(hint, SourceTrackID, &refIndex); if (e) return e; //WARNING: IN QT, MUST BE 0-based !!! dte->trackRefIndex = (u8) (refIndex - 1); } else { //we're in the hint track dte->trackRefIndex = -1; //basic check... if (SampleNumber > trak->Media->information->sampleTable->SampleSize->sampleCount + 1) { DelDTE((GenericDTE *)dte); return M4BadParam; } //are we in the current sample ?? if (!SampleNumber || (SampleNumber == trak->Media->information->sampleTable->SampleSize->sampleCount + 1)) { //we adding some stuff in the current sample ... dte->byteOffset += entry->w_sample->dataLength; if (entry->w_sample->AdditionalData) { entry->w_sample->AdditionalData = realloc(entry->w_sample->AdditionalData, sizeof(char) * (entry->w_sample->dataLength + DataLength)); memcpy(entry->w_sample->AdditionalData + entry->w_sample->dataLength, extra_data, DataLength); entry->w_sample->dataLength += DataLength; } else { entry->w_sample->AdditionalData = malloc(sizeof(char) * DataLength); memcpy(entry->w_sample->AdditionalData, extra_data, DataLength); entry->w_sample->dataLength = DataLength; } //and set the sample number ... dte->sampleNumber = trak->Media->information->sampleTable->SampleSize->sampleCount + 1; } } //OK, add the entry return AddDTE_HintPacket(entry->w_sample->HintType, pck, (GenericDTE *)dte, AtBegin); }
//extraction of the ESD from the track M4Err GetESD(MovieAtom *moov, u32 trackID, u32 StreamDescIndex, ESDescriptor **outESD) { M4Err e; ESDescriptor *esd; SampleTableAtom *stbl; TrackAtom *trak, *OCRTrack; TrackReferenceTypeAtom *dpnd; SLConfigDescriptor *slc; MPEGSampleEntryAtom *entry; dpnd = NULL; *outESD = NULL; trak = GetTrack(moov, GetTrackNumberByID(moov, trackID)); if (!trak) return M4InvalidMP4File; e = Media_GetESD(trak->Media, StreamDescIndex, &esd, 0); if (e) return e; e = Media_GetSampleDesc(trak->Media, StreamDescIndex, (SampleEntryAtom **) &entry, NULL); if (e) return e; //set the ID esd->ESID = trackID; //find stream dependencies e = Track_FindRef(trak, StreamDependenceAtomType , &dpnd); if (e) return e; if (dpnd) { //ONLY ONE STREAM DEPENDENCY IS ALLOWED if (dpnd->trackIDCount != 1) return M4InvalidMP4Media; //fix the spec: where is the index located ?? esd->dependsOnESID = dpnd->trackIDs[0]; } else { esd->dependsOnESID = 0; } //OK, get the OCR (in a REAL MP4File, OCR is 0 in ESD and is specified through track reference dpnd = NULL; OCRTrack = NULL; //find OCR dependencies e = Track_FindRef(trak, OCRReferenceAtomType, &dpnd); if (e) return e; if (dpnd) { if (dpnd->trackIDCount != 1) return M4InvalidMP4Media; esd->OCRESID = dpnd->trackIDs[0]; OCRTrack = GetTrackFromID(trak->moov, dpnd->trackIDs[0]); while (OCRTrack) { /*if we have a dependency on a track that doesn't have OCR dep, remove that dependency*/ e = Track_FindRef(OCRTrack, OCRReferenceAtomType, &dpnd); if (e || !dpnd || !dpnd->trackIDCount) { OCRTrack = NULL; goto default_sync; } /*this is explicit desync*/ if (dpnd && ((dpnd->trackIDs[0]==0) || (dpnd->trackIDs[0]==OCRTrack->Header->trackID))) break; /*loop in OCRs, break it*/ if (esd->ESID == OCRTrack->Header->trackID) { OCRTrack = NULL; goto default_sync; } /*check next*/ OCRTrack = GetTrackFromID(trak->moov, dpnd->trackIDs[0]); } if (!OCRTrack) goto default_sync; } else { default_sync: /*all tracks are sync'ed by default*/ if (trak->moov->mov->es_id_default_sync<0) { if (esd->OCRESID) trak->moov->mov->es_id_default_sync = esd->OCRESID; else trak->moov->mov->es_id_default_sync = esd->ESID; } if (trak->moov->mov->es_id_default_sync) esd->OCRESID = (u16) trak->moov->mov->es_id_default_sync; /*cf ESD writer*/ if (esd->OCRESID == esd->ESID) esd->OCRESID = 0; } //update the IPI stuff if needed if (esd->ipiPtr != NULL) { dpnd = NULL; e = Track_FindRef(trak, IPIReferenceAtomType, &dpnd); if (e) return e; if (dpnd) { if (esd->ipiPtr->tag != IPI_DescPtr_Tag) return M4InvalidMP4File; //OK, retrieve the ID: the IPI_ES_Id is currently the ref track esd->ipiPtr->IPI_ES_Id = dpnd->trackIDs[esd->ipiPtr->IPI_ES_Id - 1]; //and change the tag esd->ipiPtr->tag = IPIPtr_Tag; } else { return M4InvalidMP4File; } } /*normally all files shall be stored with predefined=SLPredef_MP4, but of course some are broken (philips) so we just check the ESD_URL. If set, use the given cfg, otherwise always rewrite it*/ if (esd->URLString != NULL) { *outESD = esd; return M4OK; } //if we are in publishing mode and we have an SLConfig specified, use it as is switch (entry->type) { case MPEGVisualSampleEntryAtomType: slc = ((MPEGVisualSampleEntryAtom *)entry)->slc; break; case MPEGAudioSampleEntryAtomType: slc = ((MPEGAudioSampleEntryAtom *)entry)->slc; break; case MPEGSampleEntryAtomType: slc = entry->slc; break; default: slc = NULL; break; } if (slc) { OD_DeleteDescriptor((Descriptor **)&esd->slConfig); OD_DuplicateDescriptor((Descriptor *)slc, (Descriptor **)&esd->slConfig); *outESD = esd; return M4OK; } //otherwise use the regular mapping //this is a desc for a media in the file, let's rewrite some param esd->slConfig->timestampLength = 32; esd->slConfig->timestampResolution = trak->Media->mediaHeader->timeScale; //NO OCR from MP4File streams (eg, constant OC Res one) esd->slConfig->OCRLength = 0; if (OCRTrack) { esd->slConfig->OCRResolution = OCRTrack->Media->mediaHeader->timeScale; } else { esd->slConfig->OCRResolution = 0; } stbl = trak->Media->information->sampleTable; // a little optimization here: if all our samples are sync, //set the RAPOnly to true... for external users... if (! stbl->SyncSample) { esd->slConfig->useRandomAccessUnitsOnlyFlag = 1; esd->slConfig->useRandomAccessPointFlag = 0; } else { esd->slConfig->useRandomAccessUnitsOnlyFlag = 0; //signal we are NOT using sync points if no info is present in the table esd->slConfig->useRandomAccessPointFlag = stbl->SyncSample->entryCount ? 1 : 0; } //do we have DegradationPriority ? if (stbl->DegradationPriority) { esd->slConfig->degradationPriorityLength = 15; } else { esd->slConfig->degradationPriorityLength = 0; } //paddingBits if (stbl->PaddingBits) { esd->slConfig->usePaddingFlag = 1; } //change to support reflecting OD streams esd->slConfig->useAccessUnitEndFlag = 1; esd->slConfig->useAccessUnitStartFlag = 1; //signal we do have padding flag (since we only use logical SL packet //the user can decide whether to use the info or not esd->slConfig->usePaddingFlag = stbl->PaddingBits ? 1 : 0; //same with degradation priority esd->slConfig->degradationPriorityLength = stbl->DegradationPriority ? 32 : 0; //this new SL will be OUT OF THE FILE. Let's set its predefined to 0 esd->slConfig->predefined = 0; *outESD = esd; return M4OK; }
//extraction of the ESD from the track GF_Err GetESD(GF_MovieBox *moov, u32 trackID, u32 StreamDescIndex, GF_ESD **outESD) { GF_Err e; GF_ESD *esd; u32 track_num = 0; GF_SampleTableBox *stbl; GF_TrackBox *trak, *OCRTrack; GF_TrackReferenceTypeBox *dpnd; GF_SLConfig *slc; GF_MPEGSampleEntryBox *entry; track_num = gf_isom_get_tracknum_from_id(moov, trackID); dpnd = NULL; *outESD = NULL; trak = gf_isom_get_track(moov, track_num); if (!trak) return GF_ISOM_INVALID_FILE; e = Media_GetESD(trak->Media, StreamDescIndex, &esd, 0); if (e) return e; e = Media_GetSampleDesc(trak->Media, StreamDescIndex, (GF_SampleEntryBox **) &entry, NULL); if (e) return e; //set the ID esd->ESID = trackID; //find stream dependencies e = Track_FindRef(trak, GF_ISOM_BOX_TYPE_DPND , &dpnd); if (e) return e; if (dpnd) { //ONLY ONE STREAM DEPENDENCY IS ALLOWED if (dpnd->trackIDCount != 1) return GF_ISOM_INVALID_MEDIA; //fix the spec: where is the index located ?? esd->dependsOnESID = dpnd->trackIDs[0]; } else { esd->dependsOnESID = 0; } if (trak->udta) { GF_UserDataMap *map; u32 i = 0; while ((map = (GF_UserDataMap*)gf_list_enum(trak->udta->recordList, &i))) { if (map->boxType == GF_4CC('A','U','X','V')) { GF_Descriptor *d = gf_odf_desc_new(GF_ODF_AUX_VIDEO_DATA); gf_list_add(esd->extensionDescriptors, d); break; } } } //OK, get the OCR (in a REAL MP4File, OCR is 0 in ESD and is specified through track reference dpnd = NULL; OCRTrack = NULL; //find OCR dependencies e = Track_FindRef(trak, GF_ISOM_BOX_TYPE_SYNC, &dpnd); if (e) return e; if (dpnd) { if (dpnd->trackIDCount != 1) return GF_ISOM_INVALID_MEDIA; esd->OCRESID = dpnd->trackIDs[0]; OCRTrack = gf_isom_get_track_from_id(trak->moov, dpnd->trackIDs[0]); while (OCRTrack) { /*if we have a dependency on a track that doesn't have OCR dep, remove that dependency*/ e = Track_FindRef(OCRTrack, GF_ISOM_BOX_TYPE_SYNC, &dpnd); if (e || !dpnd || !dpnd->trackIDCount) { OCRTrack = NULL; goto default_sync; } /*this is explicit desync*/ if (dpnd && ((dpnd->trackIDs[0]==0) || (dpnd->trackIDs[0]==OCRTrack->Header->trackID))) break; /*loop in OCRs, break it*/ if (esd->ESID == OCRTrack->Header->trackID) { OCRTrack = NULL; goto default_sync; } /*check next*/ OCRTrack = gf_isom_get_track_from_id(trak->moov, dpnd->trackIDs[0]); } if (!OCRTrack) goto default_sync; } else { default_sync: /*all tracks are sync'ed by default*/ if (trak->moov->mov->es_id_default_sync<0) { if (esd->OCRESID) trak->moov->mov->es_id_default_sync = esd->OCRESID; else trak->moov->mov->es_id_default_sync = esd->ESID; } if (trak->moov->mov->es_id_default_sync) esd->OCRESID = (u16) trak->moov->mov->es_id_default_sync; /*cf ESD writer*/ if (esd->OCRESID == esd->ESID) esd->OCRESID = 0; } //update the IPI stuff if needed if (esd->ipiPtr != NULL) { dpnd = NULL; e = Track_FindRef(trak, GF_ISOM_BOX_TYPE_IPIR, &dpnd); if (e) return e; if (dpnd) { if (esd->ipiPtr->tag != GF_ODF_ISOM_IPI_PTR_TAG) return GF_ISOM_INVALID_FILE; //OK, retrieve the ID: the IPI_ES_Id is currently the ref track esd->ipiPtr->IPI_ES_Id = dpnd->trackIDs[esd->ipiPtr->IPI_ES_Id - 1]; //and change the tag esd->ipiPtr->tag = GF_ODF_IPI_PTR_TAG; } else { return GF_ISOM_INVALID_FILE; } } if ((trak->Media->mediaHeader->packedLanguage[0] != 'u') || (trak->Media->mediaHeader->packedLanguage[1] != 'n') || (trak->Media->mediaHeader->packedLanguage[2] != 'd') ) { if (!esd->langDesc) esd->langDesc = (GF_Language *) gf_odf_desc_new(GF_ODF_LANG_TAG); esd->langDesc->langCode = trak->Media->mediaHeader->packedLanguage[0]; esd->langDesc->langCode <<= 8; esd->langDesc->langCode |= trak->Media->mediaHeader->packedLanguage[1]; esd->langDesc->langCode <<= 8; esd->langDesc->langCode |= trak->Media->mediaHeader->packedLanguage[2]; } { u16 rvc_predefined; char *rvc_cfg_data; const char *mime_type; u32 rvc_cfg_size; e = gf_isom_get_rvc_config(moov->mov, track_num, 1, &rvc_predefined, &rvc_cfg_data, &rvc_cfg_size, &mime_type); if (e==GF_OK) { if (rvc_predefined) { esd->decoderConfig->predefined_rvc_config = rvc_predefined; } else { esd->decoderConfig->rvc_config = (GF_DefaultDescriptor *) gf_odf_desc_new(GF_ODF_DSI_TAG); if (mime_type && !strcmp(mime_type, "application/rvc-config+xml+gz") ) { #if !defined(GPAC_DISABLE_CORE_TOOLS) && !defined(GPAC_DISABLE_ZLIB) gf_gz_decompress_payload(rvc_cfg_data, rvc_cfg_size, &esd->decoderConfig->rvc_config->data, &esd->decoderConfig->rvc_config->dataLength); gf_free(rvc_cfg_data); #endif } else { esd->decoderConfig->rvc_config->data = rvc_cfg_data; esd->decoderConfig->rvc_config->dataLength = rvc_cfg_size; } } } } /*normally all files shall be stored with predefined=SLPredef_MP4, but of course some are broken (philips) so we just check the ESD_URL. If set, use the given cfg, otherwise always rewrite it*/ if (esd->URLString != NULL) { *outESD = esd; return GF_OK; } //if we are in publishing mode and we have an SLConfig specified, use it as is switch (entry->type) { case GF_ISOM_BOX_TYPE_MP4V: slc = ((GF_MPEGVisualSampleEntryBox *)entry)->slc; break; case GF_ISOM_BOX_TYPE_MP4A: slc = ((GF_MPEGAudioSampleEntryBox *)entry)->slc; break; case GF_ISOM_BOX_TYPE_MP4S: slc = entry->slc; break; default: slc = NULL; break; } if (slc) { gf_odf_desc_del((GF_Descriptor *)esd->slConfig); gf_odf_desc_copy((GF_Descriptor *)slc, (GF_Descriptor **)&esd->slConfig); *outESD = esd; return GF_OK; } //otherwise use the regular mapping //this is a desc for a media in the file, let's rewrite some param esd->slConfig->timestampLength = 32; esd->slConfig->timestampResolution = trak->Media->mediaHeader->timeScale; //NO OCR from MP4File streams (eg, constant OC Res one) esd->slConfig->OCRLength = 0; esd->slConfig->OCRResolution = 0; // if (OCRTrack) esd->slConfig->OCRResolution = OCRTrack->Media->mediaHeader->timeScale; stbl = trak->Media->information->sampleTable; // a little optimization here: if all our samples are sync, //set the RAPOnly to true... for external users... if (! stbl->SyncSample) { esd->slConfig->hasRandomAccessUnitsOnlyFlag = 1; esd->slConfig->useRandomAccessPointFlag = 0; } else { esd->slConfig->hasRandomAccessUnitsOnlyFlag = 0; //signal we are NOT using sync points if no info is present in the table esd->slConfig->useRandomAccessPointFlag = stbl->SyncSample->nb_entries ? 1 : 0; } //do we have DegradationPriority ? if (stbl->DegradationPriority) { esd->slConfig->degradationPriorityLength = 15; } else { esd->slConfig->degradationPriorityLength = 0; } //paddingBits if (stbl->PaddingBits) { esd->slConfig->usePaddingFlag = 1; } //change to support reflecting OD streams esd->slConfig->useAccessUnitEndFlag = 1; esd->slConfig->useAccessUnitStartFlag = 1; //signal we do have padding flag (since we only use logical SL packet //the user can decide whether to use the info or not esd->slConfig->usePaddingFlag = stbl->PaddingBits ? 1 : 0; //same with degradation priority esd->slConfig->degradationPriorityLength = stbl->DegradationPriority ? 32 : 0; //this new SL will be OUT OF THE FILE. Let's set its predefined to 0 esd->slConfig->predefined = 0; *outESD = esd; return GF_OK; }
// Rewrite the good dependancies when an OD AU is extracted from the file M4Err Media_RewriteODFrame(MediaAtom *mdia, M4Sample *sample) { M4Err e; LPODCODEC ODdecode; LPODCODEC ODencode; ODCommand *com; //the commands we proceed ESDescriptorUpdate *esdU, *esdU2; ESDescriptorRemove *esdR, *esdR2; ObjectDescriptorUpdate *odU, *odU2; //the desc they contain ObjectDescriptor *od; M4F_ObjectDescriptor *m4_od; ESDescriptor *esd; ES_ID_Ref *ref; Descriptor *desc; TrackReferenceTypeAtom *mpod; u32 i, j, skipped; if (!mdia || !sample || !sample->data || !sample->dataLength) return M4BadParam; mpod = NULL; e = Track_FindRef(mdia->mediaTrack, ODTrackReferenceAtomType, &mpod); if (e) return e; //no references, nothing to do... if (!mpod) return M4OK; ODdecode = OD_NewCodec(OD_READ); if (!ODdecode) return M4OutOfMem; ODencode = OD_NewCodec(OD_WRITE); if (!ODencode) { OD_DeleteCodec(ODdecode); return M4OutOfMem; } e = OD_SetBuffer(ODdecode, sample->data, sample->dataLength); if (e) goto err_exit; e = OD_DecodeAU(ODdecode); if (e) goto err_exit; while (1) { com = OD_GetCommand(ODdecode); if (!com) break; //we only need to rewrite commands with ESDs inside: ESDUpdate and ODUpdate switch (com->tag) { case ODUpdate_Tag: odU = (ObjectDescriptorUpdate *) com; odU2 = (ObjectDescriptorUpdate *) OD_NewCommand(ODUpdate_Tag); for (i = 0; i< ChainGetCount(odU->objectDescriptors); i++) { desc = (Descriptor*)ChainGetEntry(odU->objectDescriptors, i); switch (desc->tag) { case ObjectDescriptor_Tag: case MP4_OD_Tag: //IOD can be used in OD streams case MP4_IOD_Tag: break; default: return M4InvalidMP4File; } e = OD_DuplicateDescriptor(desc, (Descriptor **)&m4_od); if (e) goto err_exit; //create our OD... if (desc->tag == MP4_IOD_Tag) { od = (ObjectDescriptor *) malloc(sizeof(InitialObjectDescriptor)); } else { od = (ObjectDescriptor *) malloc(sizeof(ObjectDescriptor)); } if (!od) { e = M4OutOfMem; goto err_exit; } od->ESDescriptors = NewChain(); //and duplicate... od->objectDescriptorID = m4_od->objectDescriptorID; od->tag = ObjectDescriptor_Tag; od->URLString = m4_od->URLString; m4_od->URLString = NULL; od->extensionDescriptors = m4_od->extensionDescriptors; m4_od->extensionDescriptors = NULL; od->IPMPDescriptorPointers = m4_od->IPMPDescriptorPointers; m4_od->IPMPDescriptorPointers = NULL; od->OCIDescriptors = m4_od->OCIDescriptors; m4_od->OCIDescriptors = NULL; //init as IOD if (m4_od->tag == MP4_IOD_Tag) { ((InitialObjectDescriptor *)od)->audio_profileAndLevel = ((M4F_InitialObjectDescriptor *)m4_od)->audio_profileAndLevel; ((InitialObjectDescriptor *)od)->inlineProfileFlag = ((M4F_InitialObjectDescriptor *)m4_od)->inlineProfileFlag; ((InitialObjectDescriptor *)od)->graphics_profileAndLevel = ((M4F_InitialObjectDescriptor *)m4_od)->graphics_profileAndLevel; ((InitialObjectDescriptor *)od)->OD_profileAndLevel = ((M4F_InitialObjectDescriptor *)m4_od)->OD_profileAndLevel; ((InitialObjectDescriptor *)od)->scene_profileAndLevel = ((M4F_InitialObjectDescriptor *)m4_od)->scene_profileAndLevel; ((InitialObjectDescriptor *)od)->visual_profileAndLevel = ((M4F_InitialObjectDescriptor *)m4_od)->visual_profileAndLevel; } //then rewrite the ESDesc for (j = 0; j < ChainGetCount(m4_od->ES_ID_RefDescriptors); j++) { ref = (ES_ID_Ref*)ChainGetEntry(m4_od->ES_ID_RefDescriptors, j); //if the ref index is not valid, skip this desc... if (!mpod->trackIDs || GetTrackFromID(mdia->mediaTrack->moov, mpod->trackIDs[ref->trackRef - 1]) == NULL) continue; //OK, get the esd e = GetESDForTime(mdia->mediaTrack->moov, mpod->trackIDs[ref->trackRef - 1], sample->DTS, &esd); if (!e) e = OD_AddDescToDesc((Descriptor *) od, (Descriptor *) esd); if (e) { OD_DeleteDescriptor((Descriptor **)&od); OD_DeleteCommand((ODCommand **)&odU2); OD_DeleteDescriptor((Descriptor **)&m4_od); OD_DeleteCommand((ODCommand **)&odU); goto err_exit; } } //delete our desc OD_DeleteDescriptor((Descriptor **)&m4_od); ChainAddEntry(odU2->objectDescriptors, od); } //clean a bit OD_DeleteCommand((ODCommand **)&odU); //if no desc in the command, remove it if (ChainGetCount(odU2->objectDescriptors)) { OD_AddCommand(ODencode, (ODCommand *)odU2); } else { OD_DeleteCommand((ODCommand **)&odU2); } break; case ESDUpdate_Tag: esdU = (ESDescriptorUpdate *) com; esdU2 = (ESDescriptorUpdate *) OD_NewCommand(ESDUpdate_Tag); esdU2->ODID = esdU->ODID; for (i = 0; i< ChainGetCount(esdU->ESDescriptors); i++) { ref = (ES_ID_Ref*)ChainGetEntry(esdU->ESDescriptors, i); //if the ref index is not valid, skip this desc... if (GetTrackFromID(mdia->mediaTrack->moov, mpod->trackIDs[ref->trackRef - 1]) == NULL) continue; //OK, get the esd e = GetESDForTime(mdia->mediaTrack->moov, mpod->trackIDs[ref->trackRef - 1], sample->DTS, &esd); if (e) goto err_exit; ChainAddEntry(esdU2->ESDescriptors, esd); } OD_DeleteCommand((ODCommand **)&esdU); //if our command is not empty, add it. Otherwise delete it... if (ChainGetCount(esdU2->ESDescriptors)) { OD_AddCommand(ODencode, (ODCommand *)esdU2); } else { OD_DeleteCommand((ODCommand **)&esdU2); } break; //brand new case: the ESRemove follows the same principle according to the spec... case ESDRemove_Ref_Tag: //both commands have the same structure, only the tags change esdR = (ESDescriptorRemove *) com; esdR2 = (ESDescriptorRemove *) OD_NewCommand(ESDRemove_Tag); esdR2->ODID = esdR->ODID; esdR2->NbESDs = esdR->NbESDs; //alloc our stuff esdR2->ES_ID = (unsigned short*)malloc(sizeof(u32) * esdR->NbESDs); if (!esdR2->ES_ID) { e = M4OutOfMem; goto err_exit; } skipped = 0; //get the ES_ID in the mpod indicated in the ES_ID[] for (i = 0; i < esdR->NbESDs; i++) { //if the ref index is not valid, remove this desc... if (GetTrackFromID(mdia->mediaTrack->moov, mpod->trackIDs[esdR->ES_ID[i] - 1]) == NULL) { skipped ++; } else { //the command in the file has the ref index of the trackID in the mpod esdR2->ES_ID[i - skipped] = mpod->trackIDs[esdR->ES_ID[i] - 1]; } } //realloc... if (skipped && (skipped != esdR2->NbESDs) ) { esdR2->NbESDs -= skipped; esdR2->ES_ID = (unsigned short*)realloc(esdR2->ES_ID, sizeof(u32) * esdR2->NbESDs); } OD_DeleteCommand((ODCommand **)&esdR); //if empty, remove it otherwise add it if (skipped == esdR2->NbESDs) { OD_DeleteCommand((ODCommand **)&esdR2); } else { OD_AddCommand(ODencode, (ODCommand *)esdR2); } break; default: e = OD_AddCommand(ODencode, com); if (e) goto err_exit; } } //encode our new AU e = OD_EncodeAU(ODencode); if (e) goto err_exit; //and set the buffer in the sample free(sample->data); sample->data = NULL; sample->dataLength = 0; e = OD_GetEncodedAU(ODencode, &sample->data, &sample->dataLength); err_exit: OD_DeleteCodec(ODdecode); OD_DeleteCodec(ODencode); return e; }
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; }
/* Rewrite mode: * mode = 0: playback * mode = 1: streaming */ GF_Err gf_isom_nalu_sample_rewrite(GF_MediaBox *mdia, GF_ISOSample *sample, u32 sampleNumber, GF_MPEGVisualSampleEntryBox *entry) { Bool is_hevc = 0; GF_Err e = GF_OK; GF_ISOSample *ref_samp; GF_BitStream *src_bs, *ref_bs, *dst_bs; u64 offset; u32 ref_nalu_size, data_offset, data_length, copy_size, nal_size, max_size, di, nal_unit_size_field, cur_extract_mode, extractor_mode; Bool rewrite_ps, rewrite_start_codes; u8 ref_track_ID, ref_track_num; s8 sample_offset, nal_type; u32 nal_hdr; char *buffer; GF_ISOFile *file = mdia->mediaTrack->moov->mov; src_bs = ref_bs = dst_bs = NULL; ref_samp = NULL; buffer = NULL; rewrite_ps = (mdia->mediaTrack->extractor_mode & GF_ISOM_NALU_EXTRACT_INBAND_PS_FLAG) ? 1 : 0; if (! sample->IsRAP) rewrite_ps = 0; rewrite_start_codes = (mdia->mediaTrack->extractor_mode & GF_ISOM_NALU_EXTRACT_ANNEXB_FLAG) ? 1 : 0; extractor_mode = mdia->mediaTrack->extractor_mode&0x0000FFFF; if (extractor_mode == GF_ISOM_NALU_EXTRACT_INSPECT) { if (!rewrite_ps && !rewrite_start_codes) return GF_OK; } if (!entry) return GF_BAD_PARAM; nal_unit_size_field = 0; /*if svc rewrire*/ if (entry->svc_config && entry->svc_config->config) nal_unit_size_field = entry->svc_config->config->nal_unit_size; /*if mvc rewrire*/ /*otherwise do nothing*/ else if (!rewrite_ps && !rewrite_start_codes) { return GF_OK; } if (!nal_unit_size_field) { if (entry->avc_config) nal_unit_size_field = entry->avc_config->config->nal_unit_size; else if (entry->hevc_config) { nal_unit_size_field = entry->hevc_config->config->nal_unit_size; is_hevc = 1; } } if (!nal_unit_size_field) return GF_ISOM_INVALID_FILE; dst_bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); src_bs = gf_bs_new(sample->data, sample->dataLength, GF_BITSTREAM_READ); max_size = 4096; /*rewrite start code with NALU delim*/ if (rewrite_start_codes) { gf_bs_write_int(dst_bs, 1, 32); if (is_hevc) { gf_bs_write_int(dst_bs, 0, 1); gf_bs_write_int(dst_bs, GF_HEVC_NALU_ACCESS_UNIT, 6); gf_bs_write_int(dst_bs, 0, 9); /*pic-type - by default we signal all slice types possible*/ gf_bs_write_int(dst_bs, 2, 3); gf_bs_write_int(dst_bs, 0, 5); } else { gf_bs_write_int(dst_bs, (sample->data[0] & 0x60) | GF_AVC_NALU_ACCESS_UNIT, 8); gf_bs_write_int(dst_bs, 0xF0 , 8); /*7 "all supported NALUs" (=111) + rbsp trailing (10000)*/; } } if (rewrite_ps) { if (is_hevc) { u32 i, count; count = gf_list_count(entry->hevc_config->config->param_array); for (i=0; i<count; i++) { GF_HEVCParamArray *ar = gf_list_get(entry->hevc_config->config->param_array, i); rewrite_nalus_list(ar->nalus, dst_bs, rewrite_start_codes, nal_unit_size_field); } /*little optimization if we are not asked to start codes: copy over the sample*/ if (!rewrite_start_codes) { gf_bs_write_data(dst_bs, sample->data, sample->dataLength); gf_free(sample->data); sample->data = NULL; gf_bs_get_content(dst_bs, &sample->data, &sample->dataLength); gf_bs_del(src_bs); gf_bs_del(dst_bs); return GF_OK; } } else { /*this is an SVC track: get all SPS/PPS from this track down to the base layer and rewrite them*/ if (mdia->mediaTrack->has_base_layer) { u32 j; GF_List *nalu_sps = gf_list_new(); GF_List *nalu_pps = gf_list_new(); GF_TrackReferenceTypeBox *dpnd = NULL; Track_FindRef(mdia->mediaTrack, GF_ISOM_REF_SCAL, &dpnd); #if 0 /*get all upper layers with SCAL reference to this track*/ for (j = 0; j < gf_isom_get_track_count(file); j++) { if (gf_isom_has_track_reference(file, j+1, GF_ISOM_REF_SCAL, mdia->mediaTrack->Header->trackID)) { u32 tkID; GF_TrackBox *base_track; GF_MPEGVisualSampleEntryBox *base_entry; gf_isom_get_reference_ID(file, j+1, GF_ISOM_REF_SCAL, 1, &tkID); base_track = GetTrackbyID(mdia->mediaTrack->moov, tkID); base_entry = base_track ? gf_list_get(base_track->Media->information->sampleTable->SampleDescription->other_boxes, 0) : NULL; if (base_entry) merge_nalus(base_entry, nalu_sps, nalu_pps); } } #endif merge_nalus(entry, nalu_sps, nalu_pps); if (dpnd) { for (j=0; j<dpnd->trackIDCount; j++) { GF_TrackBox *base_track = GetTrackbyID(mdia->mediaTrack->moov, dpnd->trackIDs[j]); GF_MPEGVisualSampleEntryBox *base_entry = base_track ? gf_list_get(base_track->Media->information->sampleTable->SampleDescription->other_boxes, 0) : NULL; if (base_entry) merge_nalus(base_entry, nalu_sps, nalu_pps); } } //rewrite nalus rewrite_nalus_list(nalu_sps, dst_bs, rewrite_start_codes, nal_unit_size_field); rewrite_nalus_list(nalu_pps, dst_bs, rewrite_start_codes, nal_unit_size_field); gf_list_del(nalu_sps); gf_list_del(nalu_pps); } else { if (entry->avc_config) { rewrite_nalus_list(entry->avc_config->config->sequenceParameterSets, dst_bs, rewrite_start_codes, nal_unit_size_field); rewrite_nalus_list(entry->avc_config->config->pictureParameterSets, dst_bs, rewrite_start_codes, nal_unit_size_field); rewrite_nalus_list(entry->avc_config->config->sequenceParameterSetExtensions, dst_bs, rewrite_start_codes, nal_unit_size_field); } /*add svc config */ if (entry->svc_config) { rewrite_nalus_list(entry->svc_config->config->sequenceParameterSets, dst_bs, rewrite_start_codes, nal_unit_size_field); rewrite_nalus_list(entry->svc_config->config->pictureParameterSets, dst_bs, rewrite_start_codes, nal_unit_size_field); } /*little optimization if we are not asked to rewrite extractors or start codes: copy over the sample*/ if (!entry->svc_config && !rewrite_start_codes) { gf_bs_write_data(dst_bs, sample->data, sample->dataLength); gf_free(sample->data); sample->data = NULL; gf_bs_get_content(dst_bs, &sample->data, &sample->dataLength); gf_bs_del(src_bs); gf_bs_del(dst_bs); return GF_OK; } } } } buffer = (char *)gf_malloc(sizeof(char)*max_size); while (gf_bs_available(src_bs)) { nal_size = gf_bs_read_int(src_bs, 8*nal_unit_size_field); if (nal_size>max_size) { buffer = (char*) gf_realloc(buffer, sizeof(char)*nal_size); max_size = nal_size; } if (is_hevc) { nal_hdr = gf_bs_read_u16(src_bs); nal_type = (nal_hdr&0x7E00) >> 9; } else { nal_hdr = gf_bs_read_u8(src_bs); nal_type = nal_hdr & 0x1F; } if (is_hevc) { /*we already wrote this stuff*/ if (nal_type==GF_HEVC_NALU_ACCESS_UNIT) { gf_bs_skip_bytes(src_bs, nal_size-2); continue; } /*rewrite nal*/ gf_bs_read_data(src_bs, buffer, nal_size-2); if (rewrite_start_codes) gf_bs_write_u32(dst_bs, 1); else gf_bs_write_int(dst_bs, nal_size, 8*nal_unit_size_field); gf_bs_write_u16(dst_bs, nal_hdr); gf_bs_write_data(dst_bs, buffer, nal_size-2); continue; } /*we already wrote this stuff*/ if (nal_type==GF_AVC_NALU_ACCESS_UNIT) { gf_bs_skip_bytes(src_bs, nal_size-1); continue; } //extractor if (nal_type == 31) { switch (extractor_mode) { case 0: gf_bs_read_int(src_bs, 24); //3 bytes of NALUHeader in extractor ref_track_ID = gf_bs_read_u8(src_bs); sample_offset = (s8) gf_bs_read_int(src_bs, 8); data_offset = gf_bs_read_u32(src_bs); data_length = gf_bs_read_u32(src_bs); ref_track_num = gf_isom_get_track_by_id(file, ref_track_ID); if (!ref_track_num) { e = GF_BAD_PARAM; goto exit; } cur_extract_mode = gf_isom_get_nalu_extract_mode(file, ref_track_num); gf_isom_set_nalu_extract_mode(file, ref_track_num, GF_ISOM_NALU_EXTRACT_INSPECT); ref_samp = gf_isom_get_sample(file, ref_track_num, sampleNumber+sample_offset, &di); if (!ref_samp) { e = GF_IO_ERR; goto exit; } ref_bs = gf_bs_new(ref_samp->data, ref_samp->dataLength, GF_BITSTREAM_READ); offset = 0; while (gf_bs_available(ref_bs)) { if (gf_bs_get_position(ref_bs) < data_offset) { ref_nalu_size = gf_bs_read_int(ref_bs, 8*nal_unit_size_field); offset += ref_nalu_size + nal_unit_size_field; if ((offset > data_offset) || (offset >= gf_bs_get_size(ref_bs))) { e = GF_BAD_PARAM; goto exit; } e = gf_bs_seek(ref_bs, offset); if (e) goto exit; continue; } ref_nalu_size = gf_bs_read_int(ref_bs, 8*nal_unit_size_field); copy_size = data_length ? data_length : ref_nalu_size; assert(copy_size <= ref_nalu_size); nal_hdr = gf_bs_read_u8(ref_bs); //rewrite NAL type if ((copy_size-1)>max_size) { buffer = (char*)gf_realloc(buffer, sizeof(char)*(copy_size-1)); max_size = copy_size-1; } gf_bs_read_data(ref_bs, buffer, copy_size-1); if (rewrite_start_codes) gf_bs_write_u32(dst_bs, 1); else gf_bs_write_int(dst_bs, copy_size, 8*nal_unit_size_field); gf_bs_write_u8(dst_bs, nal_hdr); gf_bs_write_data(dst_bs, buffer, copy_size-1); } gf_isom_sample_del(&ref_samp); ref_samp = NULL; gf_bs_del(ref_bs); ref_bs = NULL; gf_isom_set_nalu_extract_mode(file, ref_track_num, cur_extract_mode); break; default: //skip to end of this NALU gf_bs_skip_bytes(src_bs, nal_size-1); continue; } } else { gf_bs_read_data(src_bs, buffer, nal_size-1); if (rewrite_start_codes) gf_bs_write_u32(dst_bs, 1); else gf_bs_write_int(dst_bs, nal_size, 8*nal_unit_size_field); gf_bs_write_u8(dst_bs, nal_hdr); gf_bs_write_data(dst_bs, buffer, nal_size-1); } }