GF_Err MergeFragment(GF_MovieFragmentBox *moof, GF_ISOFile *mov) { u32 i, j; u64 MaxDur; GF_TrackFragmentBox *traf; GF_TrackBox *trak; MaxDur = 0; //we shall have a MOOV and its MVEX BEFORE any MOOF if (!mov->moov || !mov->moov->mvex) return GF_ISOM_INVALID_FILE; //and all fragments must be continous if (mov->NextMoofNumber && (mov->NextMoofNumber >= moof->mfhd->sequence_number)) return GF_ISOM_INVALID_FILE; i=0; while ((traf = (GF_TrackFragmentBox*)gf_list_enum(moof->TrackList, &i))) { if (!traf->tfhd) { trak = NULL; traf->trex = NULL; } else { trak = gf_isom_get_track_from_id(mov->moov, traf->tfhd->trackID); j=0; while ((traf->trex = (GF_TrackExtendsBox*)gf_list_enum(mov->moov->mvex->TrackExList, &j))) { if (traf->trex->trackID == traf->tfhd->trackID) break; traf->trex = NULL; } } if (!trak || !traf->trex) return GF_ISOM_INVALID_FILE; MergeTrack(trak, traf, mov->current_top_box_start, !mov->first_moof_merged); //update trak duration SetTrackDuration(trak); if (trak->Header->duration > MaxDur) MaxDur = trak->Header->duration; } mov->first_moof_merged = 1; mov->NextMoofNumber = moof->mfhd->sequence_number; //update movie duration if (mov->moov->mvhd->duration < MaxDur) mov->moov->mvhd->duration = MaxDur; return GF_OK; }
GF_Err MergeFragment(GF_MovieFragmentBox *moof, GF_ISOFile *mov) { GF_Err e; u32 i, j; u64 MaxDur; GF_TrackFragmentBox *traf; GF_TrackBox *trak; MaxDur = 0; //we shall have a MOOV and its MVEX BEFORE any MOOF if (!mov->moov || !mov->moov->mvex) { GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Error: %s not received before merging fragment\n", mov->moov ? "mvex" : "moov" )); return GF_ISOM_INVALID_FILE; } //and all fragments must be continous - we do not throw an error as we may still want to be able to concatenate dependent representations in DASH and //we will likely a-have R1(moofSN 1, 3, 5, 7) plus R2(moofSN 2, 4, 6, 8) if (mov->NextMoofNumber && (mov->NextMoofNumber >= moof->mfhd->sequence_number)) { GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso file] Warning: wrong sequence number: got %d but last one was %d\n", moof->mfhd->sequence_number, mov->NextMoofNumber)); // return GF_ISOM_INVALID_FILE; } i=0; while ((traf = (GF_TrackFragmentBox*)gf_list_enum(moof->TrackList, &i))) { if (!traf->tfhd) { trak = NULL; traf->trex = NULL; } else { trak = gf_isom_get_track_from_id(mov->moov, traf->tfhd->trackID); j=0; while ((traf->trex = (GF_TrackExtendsBox*)gf_list_enum(mov->moov->mvex->TrackExList, &j))) { if (traf->trex->trackID == traf->tfhd->trackID) break; traf->trex = NULL; } } if (!trak || !traf->trex) { GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Error: Cannot find fragment track with ID %d\n", traf->tfhd->trackID)); return GF_ISOM_INVALID_FILE; } e = MergeTrack(trak, traf, mov->current_top_box_start, !trak->first_traf_merged); if (e) return e; trak->present_in_scalable_segment = 1; //update trak duration SetTrackDuration(trak); if (trak->Header->duration > MaxDur) MaxDur = trak->Header->duration; trak->first_traf_merged = 1; } if (moof->other_boxes) { GF_Box *a; i = 0; while ((a = (GF_Box *)gf_list_enum(moof->other_boxes, &i))) { if (a->type == GF_ISOM_BOX_TYPE_PSSH) { GF_ProtectionSystemHeaderBox *pssh = (GF_ProtectionSystemHeaderBox *)pssh_New(); memmove(pssh->SystemID, ((GF_ProtectionSystemHeaderBox *)a)->SystemID, 16); pssh->KID_count = ((GF_ProtectionSystemHeaderBox *)a)->KID_count; pssh->KIDs = (bin128 *)gf_malloc(pssh->KID_count*sizeof(bin128)); memmove(pssh->KIDs, ((GF_ProtectionSystemHeaderBox *)a)->KIDs, pssh->KID_count*sizeof(bin128)); pssh->private_data_size = ((GF_ProtectionSystemHeaderBox *)a)->private_data_size; pssh->private_data = (u8 *)gf_malloc(pssh->private_data_size*sizeof(char)); memmove(pssh->private_data, ((GF_ProtectionSystemHeaderBox *)a)->private_data, pssh->private_data_size); if (!mov->moov->other_boxes) mov->moov->other_boxes = gf_list_new(); gf_list_add(mov->moov->other_boxes, pssh); } } } mov->NextMoofNumber = moof->mfhd->sequence_number; //update movie duration if (mov->moov->mvhd->duration < MaxDur) mov->moov->mvhd->duration = MaxDur; return GF_OK; }
GF_Err gf_isom_parse_movie_boxes(GF_ISOFile *mov, u64 *bytesMissing, Bool progressive_mode) { GF_Box *a; u64 totSize; GF_Err e = GF_OK; totSize = 0; #ifndef GPAC_DISABLE_ISOM_FRAGMENTS if (mov->single_moof_mode && mov->single_moof_state == 2) { return e; } /*restart from where we stopped last*/ totSize = mov->current_top_box_start; gf_bs_seek(mov->movieFileMap->bs, mov->current_top_box_start); #endif /*while we have some data, parse our boxes*/ while (gf_bs_available(mov->movieFileMap->bs)) { *bytesMissing = 0; #ifndef GPAC_DISABLE_ISOM_FRAGMENTS mov->current_top_box_start = gf_bs_get_position(mov->movieFileMap->bs); GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[iso file] Current top box start before parsing %d\n", mov->current_top_box_start)); #endif e = gf_isom_parse_root_box(&a, mov->movieFileMap->bs, bytesMissing, progressive_mode); if (e >= 0) { e = GF_OK; } else if (e == GF_ISOM_INCOMPLETE_FILE) { /*our mdat is uncomplete, only valid for READ ONLY files...*/ if (mov->openMode != GF_ISOM_OPEN_READ) { GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Incomplete MDAT while file is not read-only\n")); return GF_ISOM_INVALID_FILE; } return e; } else { return e; } switch (a->type) { /*MOOV box*/ case GF_ISOM_BOX_TYPE_MOOV: if (mov->moov) { GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Duplicate MOOV detected!\n")); return GF_ISOM_INVALID_FILE; } mov->moov = (GF_MovieBox *)a; /*set our pointer to the movie*/ mov->moov->mov = mov; #ifndef GPAC_DISABLE_ISOM_FRAGMENTS if (mov->moov->mvex) mov->moov->mvex->mov = mov; #endif e = gf_list_add(mov->TopBoxes, a); if (e) { return e; } totSize += a->size; break; /*META box*/ case GF_ISOM_BOX_TYPE_META: if (mov->meta) { GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Duplicate META detected!\n")); return GF_ISOM_INVALID_FILE; } mov->meta = (GF_MetaBox *)a; e = gf_list_add(mov->TopBoxes, a); if (e) { return e; } totSize += a->size; break; /*we only keep the MDAT in READ for dump purposes*/ case GF_ISOM_BOX_TYPE_MDAT: totSize += a->size; if (mov->openMode == GF_ISOM_OPEN_READ) { if (!mov->mdat) { mov->mdat = (GF_MediaDataBox *) a; e = gf_list_add(mov->TopBoxes, mov->mdat); if (e) { return e; } } #ifndef GPAC_DISABLE_ISOM_FRAGMENTS else if (mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG) gf_list_add(mov->TopBoxes, a); #endif else gf_isom_box_del(a); } /*if we don't have any MDAT yet, create one (edit-write mode) We only work with one mdat, but we're puting it at the place of the first mdat found when opening a file for editing*/ else if (!mov->mdat && (mov->openMode != GF_ISOM_OPEN_READ) && (mov->openMode != GF_ISOM_OPEN_CAT_FRAGMENTS)) { gf_isom_box_del(a); mov->mdat = (GF_MediaDataBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MDAT); e = gf_list_add(mov->TopBoxes, mov->mdat); if (e) { return e; } } else { gf_isom_box_del(a); } break; case GF_ISOM_BOX_TYPE_FTYP: /*ONE AND ONLY ONE FTYP*/ if (mov->brand) { gf_isom_box_del(a); GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Duplicate FTYP detected!\n")); return GF_ISOM_INVALID_FILE; } mov->brand = (GF_FileTypeBox *)a; totSize += a->size; e = gf_list_add(mov->TopBoxes, a); break; case GF_ISOM_BOX_TYPE_PDIN: /*ONE AND ONLY ONE PDIN*/ if (mov->pdin) { gf_isom_box_del(a); GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Duplicate PDIN detected!\n")); return GF_ISOM_INVALID_FILE; } mov->pdin = (GF_ProgressiveDownloadBox *) a; totSize += a->size; e = gf_list_add(mov->TopBoxes, a); break; #ifndef GPAC_DISABLE_ISOM_FRAGMENTS case GF_ISOM_BOX_TYPE_STYP: { u32 brand = ((GF_SegmentTypeBox *)a)->majorBrand; switch (brand) { case GF_4CC('s', 'i', 's', 'x'): case GF_4CC('r', 'i', 's', 'x'): case GF_4CC('s', 's', 's', 's'): mov->is_index_segment = GF_TRUE; break; default: break; } } /*fall-through*/ case GF_ISOM_BOX_TYPE_SIDX: totSize += a->size; if (mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG) { e = gf_list_add(mov->TopBoxes, a); } else { gf_isom_box_del(a); } break; case GF_ISOM_BOX_TYPE_MOOF: if (!mov->moov) { GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso file] Movie fragment but no moov (yet) - possibly broken parsing!\n")); } if (mov->single_moof_mode) { mov->single_moof_state++; if (mov->single_moof_state > 1) { gf_isom_box_del(a); return GF_OK; } } ((GF_MovieFragmentBox *)a)->mov = mov; totSize += a->size; mov->moof = (GF_MovieFragmentBox *) a; /*read & debug: store at root level*/ if (mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG) { u32 k; gf_list_add(mov->TopBoxes, a); /*also update pointers to trex for debug*/ if (mov->moov) { for (k=0; k<gf_list_count(mov->moof->TrackList); k++) { GF_TrackFragmentBox *traf = gf_list_get(mov->moof->TrackList, k); if (traf->tfhd) { GF_TrackBox *trak = gf_isom_get_track_from_id(mov->moov, traf->tfhd->trackID); u32 j=0; while ((traf->trex = (GF_TrackExtendsBox*)gf_list_enum(mov->moov->mvex->TrackExList, &j))) { if (traf->trex->trackID == traf->tfhd->trackID) { if (!traf->trex->track) traf->trex->track = trak; break; } traf->trex = NULL; } } //we should only parse senc/psec when no saiz/saio is present, otherwise we fetch the info directly if (traf->trex && traf->trex->track && (traf->piff_sample_encryption || traf->sample_encryption)) { GF_TrackBox *trak = GetTrackbyID(mov->moov, traf->tfhd->trackID); e = senc_Parse(mov->movieFileMap->bs, trak, traf, traf->piff_sample_encryption ? (GF_SampleEncryptionBox *) traf->piff_sample_encryption : traf->sample_encryption); } } } } else if (mov->openMode==GF_ISOM_OPEN_CAT_FRAGMENTS) { mov->NextMoofNumber = mov->moof->mfhd->sequence_number+1; mov->moof = NULL; gf_isom_box_del(a); } else { /*merge all info*/ e = MergeFragment((GF_MovieFragmentBox *)a, mov); gf_isom_box_del(a); } break; #endif case GF_4CC('j','P',' ',' '): { GF_UnknownBox *box = (GF_UnknownBox*)a; u8 *c = (u8 *) box->data; if ((box->dataSize==4) && (GF_4CC(c[0],c[1],c[2],c[3])==(u32)0x0D0A870A)) mov->is_jp2 = 1; gf_isom_box_del(a); } break; case GF_ISOM_BOX_TYPE_PRFT: #ifndef GPAC_DISABLE_ISOM_FRAGMENTS if (!(mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG)) { //keep the last one read if (mov->last_producer_ref_time) gf_isom_box_del(a); else mov->last_producer_ref_time = (GF_ProducerReferenceTimeBox *)a; break; } #endif //fallthrough default: totSize += a->size; e = gf_list_add(mov->TopBoxes, a); break; } #ifndef GPAC_DISABLE_ISOM_FRAGMENTS /*remember where we left, in case we append an entire number of movie fragments*/ mov->current_top_box_start = gf_bs_get_position(mov->movieFileMap->bs); #endif } /*we need at least moov or meta*/ if (!mov->moov && !mov->meta #ifndef GPAC_DISABLE_ISOM_FRAGMENTS && !mov->moof && !mov->is_index_segment #endif ) { return GF_ISOM_INCOMPLETE_FILE; } /*we MUST have movie header*/ if (mov->moov && !mov->moov->mvhd) { GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Missing MVHD in MOOV!\n")); return GF_ISOM_INVALID_FILE; } /*we MUST have meta handler*/ if (mov->meta && !mov->meta->handler) { GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Missing handler in META!\n")); return GF_ISOM_INVALID_FILE; } #ifndef GPAC_DISABLE_ISOM_WRITE if (mov->moov) { /*set the default interleaving time*/ mov->interleavingTime = mov->moov->mvhd->timeScale; #ifndef GPAC_DISABLE_ISOM_FRAGMENTS /*in edit mode with successfully loaded fragments, delete all fragment signaling since file is no longer fragmented*/ if ((mov->openMode > GF_ISOM_OPEN_READ) && (mov->openMode != GF_ISOM_OPEN_CAT_FRAGMENTS) && mov->moov->mvex) { gf_isom_box_del((GF_Box *)mov->moov->mvex); mov->moov->mvex = NULL; } #endif } //create a default mdat if none was found if (!mov->mdat && (mov->openMode != GF_ISOM_OPEN_READ) && (mov->openMode != GF_ISOM_OPEN_CAT_FRAGMENTS)) { mov->mdat = (GF_MediaDataBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MDAT); e = gf_list_add(mov->TopBoxes, mov->mdat); if (e) return e; } #endif /*GPAC_DISABLE_ISOM_WRITE*/ return GF_OK; }
//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 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; }
GF_EXPORT GF_Err gf_isom_next_hint_packet(GF_ISOFile *the_file, u32 trackNumber, char **pck_data, u32 *pck_size, Bool *disposable, Bool *repeated, u32 *trans_ts, u32 *sample_num) { GF_RTPPacket *pck; GF_Err e; GF_BitStream *bs; GF_TrackBox *trak, *ref_trak; GF_HintSampleEntryBox *entry; u32 i, count, ts; s32 cts_off; *pck_data = NULL; *pck_size = 0; if (trans_ts) *trans_ts = 0; if (disposable) *disposable = 0; if (repeated) *repeated = 0; if (sample_num) *sample_num = 0; trak = gf_isom_get_track_from_file(the_file, trackNumber); if (!trak) 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: case GF_ISOM_BOX_TYPE_SRTP_STSD: case GF_ISOM_BOX_TYPE_RRTP_STSD: break; default: return GF_NOT_SUPPORTED; } if (!entry->hint_sample) { e = gf_isom_load_next_hint_sample(the_file, trackNumber, trak, entry); if (e) return e; } pck = (GF_RTPPacket *)gf_list_get(entry->hint_sample->packetTable, 0); gf_list_rem(entry->hint_sample->packetTable, 0); if (!pck) return GF_BAD_PARAM; bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); /*write RTP header*/ gf_bs_write_int(bs, 2, 2); /*version*/ gf_bs_write_int(bs, pck->P_bit, 1); /*P bit*/ gf_bs_write_int(bs, pck->X_bit, 1); /*X bit*/ gf_bs_write_int(bs, 0, 4); /*CSRC count*/ gf_bs_write_int(bs, pck->M_bit, 1); /*M bit*/ gf_bs_write_int(bs, pck->payloadType, 7); /*payt*/ gf_bs_write_u16(bs, entry->pck_sn); /*seq num*/ entry->pck_sn++; /*look for CTS offset in TLV*/ cts_off = 0; count = gf_list_count(pck->TLV); for (i=0; i<count; i++) { GF_RTPOBox *rtpo = (GF_RTPOBox *)gf_list_get(pck->TLV, i); if (rtpo->type == GF_ISOM_BOX_TYPE_RTPO) { cts_off = rtpo->timeOffset; break; } } /*TS - TODO check TS wrapping*/ ts = (u32) (entry->hint_sample->TransmissionTime + pck->relativeTransTime + entry->ts_offset + cts_off); gf_bs_write_u32(bs, ts ); gf_bs_write_u32(bs, entry->ssrc); /*SSRC*/ /*then build all data*/ count = gf_list_count(pck->DataTable); for (i=0; i<count; i++) { GF_GenericDTE *dte = (GF_GenericDTE *)gf_list_get(pck->DataTable, i); switch (dte->source) { /*empty*/ case 0: break; /*immediate data*/ case 1: gf_bs_write_data(bs, ((GF_ImmediateDTE *)dte)->data, ((GF_ImmediateDTE *)dte)->dataLength); break; /*sample data*/ case 2: { GF_ISOSample *samp; GF_SampleDTE *sdte = (GF_SampleDTE *)dte; /*get track if not this one*/ if (sdte->trackRefIndex != (s8) -1) { if (!entry->hint_ref || !entry->hint_ref->trackIDs) { gf_isom_hint_rtp_del(pck); gf_bs_del(bs); return GF_ISOM_INVALID_FILE; } ref_trak = gf_isom_get_track_from_id(trak->moov, entry->hint_ref->trackIDs[(u32)sdte->trackRefIndex]); } else { ref_trak = trak; } samp = gf_isom_get_data_sample(entry->hint_sample, ref_trak, sdte->sampleNumber); if (!samp) { gf_isom_hint_rtp_del(pck); gf_bs_del(bs); return GF_IO_ERR; } gf_bs_write_data(bs, samp->data + sdte->byteOffset, sdte->dataLength); } break; /*sample desc data - currently NOT SUPPORTED !!!*/ case 3: break; } } if (trans_ts) *trans_ts = ts; if (disposable) *disposable = pck->B_bit; if (repeated) *repeated = pck->R_bit; if (sample_num) *sample_num = entry->cur_sample-1; gf_bs_get_content(bs, pck_data, pck_size); gf_bs_del(bs); gf_isom_hint_rtp_del(pck); if (!gf_list_count(entry->hint_sample->packetTable)) { gf_isom_hint_sample_del(entry->hint_sample); entry->hint_sample = NULL; } return GF_OK; }