GF_Err UpdateSample(GF_MediaBox *mdia, u32 sampleNumber, u32 size, u32 CTS, u64 offset, u8 isRap) { u32 i; GF_SampleTableBox *stbl = mdia->information->sampleTable; //set size, offset, RAP, CTS ... stbl_SetSampleSize(stbl->SampleSize, sampleNumber, size); stbl_SetChunkOffset(mdia, sampleNumber, offset); //do we have a CTS? if (stbl->CompositionOffset) { stbl_SetSampleCTS(stbl, sampleNumber, CTS); } else { //do we need one ?? if (CTS) { stbl->CompositionOffset = (GF_CompositionOffsetBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_CTTS); stbl_AddCTS(stbl, sampleNumber, CTS); } } //do we have a sync ??? if (stbl->SyncSample) { stbl_SetSampleRAP(stbl->SyncSample, sampleNumber, isRap); } else { //do we need one if (! isRap) { stbl->SyncSample = (GF_SyncSampleBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSS); //what a pain: all the sample we had have to be sync ... for (i=0; i<stbl->SampleSize->sampleCount; i++) { if (i+1 != sampleNumber) stbl_AddRAP(stbl->SyncSample, i+1); } } } if (isRap==2) { stbl_SetRedundant(stbl, sampleNumber); } return GF_OK; }
GF_Err gf_isom_new_xml_metadata_description(GF_ISOFile *movie, u32 trackNumber, const char *_namespace, const char *schema_loc, const char *content_encoding, u32 *outDescriptionIndex) { GF_TrackBox *trak; GF_Err e; u32 dataRefIndex; GF_MetaDataSampleEntryBox *metad; char *URLname = NULL; char *URNname = NULL; e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); if (e) return e; trak = gf_isom_get_track_from_file(movie, trackNumber); if (!trak || !trak->Media || !_namespace) return GF_BAD_PARAM; switch (trak->Media->handler->handlerType) { case GF_ISOM_MEDIA_MPEG_SUBT: case GF_ISOM_MEDIA_META: case GF_ISOM_MEDIA_TEXT: break; default: return GF_BAD_PARAM; } //get or create the data ref e = Media_FindDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); if (e) return e; if (!dataRefIndex) { e = Media_CreateDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); if (e) return e; } if (!movie->keep_utc) trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); metad = (GF_MetaDataSampleEntryBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_METX); if (!metad) return GF_OUT_OF_MEM; metad->dataReferenceIndex = dataRefIndex; metad->xml_namespace = gf_strdup(_namespace); if (content_encoding) metad->content_encoding = gf_strdup(content_encoding); if (schema_loc) metad->xml_schema_loc = gf_strdup(schema_loc); e = gf_list_add(trak->Media->information->sampleTable->SampleDescription->other_boxes, metad); if (outDescriptionIndex) *outDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->other_boxes); return e; }
GF_Err gf_isom_new_generic_subtitle_description(GF_ISOFile *movie, u32 trackNumber, char *content_encoding, char *xml_schema_loc, char *mime_type_or_namespace, Bool is_xml, char *URLname, char *URNname, u32 *outDescriptionIndex) { GF_TrackBox *trak; GF_Err e; u32 dataRefIndex; GF_MetaDataSampleEntryBox *metasd; e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); if (e) return e; trak = gf_isom_get_track_from_file(movie, trackNumber); if (!trak || !trak->Media) return GF_BAD_PARAM; switch (trak->Media->handler->handlerType) { case GF_ISOM_MEDIA_SUBM: break; default: return GF_BAD_PARAM; } //get or create the data ref e = Media_FindDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); if (e) return e; if (!dataRefIndex) { e = Media_CreateDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); if (e) return e; } trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); metasd = (GF_MetaDataSampleEntryBox *) gf_isom_box_new((is_xml ? GF_ISOM_BOX_TYPE_METX : GF_ISOM_BOX_TYPE_METT)); metasd->dataReferenceIndex = dataRefIndex; gf_list_add(trak->Media->information->sampleTable->SampleDescription->other_boxes, metasd); if (outDescriptionIndex) *outDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->other_boxes); metasd->content_encoding = gf_strdup(content_encoding); metasd->xml_schema_loc = gf_strdup(xml_schema_loc); metasd->mime_type_or_namespace = gf_strdup(mime_type_or_namespace); return e; }
GF_Err gf_isom_text_set_highlight_color(GF_TextSample *samp, u8 r, u8 g, u8 b, u8 a) { if (!samp) return GF_BAD_PARAM; if (!samp->highlight_color) { samp->highlight_color = (GF_TextHighlightColorBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_HCLR); if (!samp->highlight_color) return GF_OUT_OF_MEM; } samp->highlight_color->hil_color = a; samp->highlight_color->hil_color <<= 8; samp->highlight_color->hil_color = r; samp->highlight_color->hil_color <<= 8; samp->highlight_color->hil_color = g; samp->highlight_color->hil_color <<= 8; samp->highlight_color->hil_color = b; return GF_OK; }
//add an SDP line to the SDP container at the track level (media-specific SDP info) GF_EXPORT GF_Err gf_isom_sdp_add_track_line(GF_ISOFile *the_file, u32 trackNumber, const char *text) { GF_TrackBox *trak; GF_UserDataMap *map; GF_HintTrackInfoBox *hnti; GF_SDPBox *sdp; GF_Err e; char *buf; trak = gf_isom_get_track_from_file(the_file, trackNumber); if (!trak) return GF_BAD_PARAM; //currently, only RTP hinting supports SDP if (!CheckHintFormat(trak, GF_ISOM_HINT_RTP)) return GF_BAD_PARAM; map = udta_getEntry(trak->udta, GF_ISOM_BOX_TYPE_HNTI, NULL); if (!map) return GF_ISOM_INVALID_FILE; //we should have only one HNTI in the UDTA if (gf_list_count(map->other_boxes) != 1) return GF_ISOM_INVALID_FILE; hnti = (GF_HintTrackInfoBox *)gf_list_get(map->other_boxes, 0); if (!hnti->SDP) { e = hnti_AddBox(hnti, gf_isom_box_new(GF_ISOM_BOX_TYPE_SDP)); if (e) return e; } sdp = (GF_SDPBox *) hnti->SDP; if (!sdp->sdpText) { sdp->sdpText = (char *)gf_malloc(sizeof(char) * (strlen(text) + 3)); strcpy(sdp->sdpText, text); strcat(sdp->sdpText, "\r\n"); return GF_OK; } buf = (char *)gf_malloc(sizeof(char) * (strlen(sdp->sdpText) + strlen(text) + 3)); strcpy(buf, sdp->sdpText); strcat(buf, text); strcat(buf, "\r\n"); gf_free(sdp->sdpText); ReorderSDP(buf, GF_FALSE); sdp->sdpText = buf; return GF_OK; }
GF_ISOSample *gf_isom_webvtt_to_sample(void *s) { GF_Err e = GF_OK; GF_ISOSample *res; GF_BitStream *bs; u32 i; GF_WebVTTCue *cue; GF_WebVTTSample *samp = (GF_WebVTTSample *)s; if (!samp) return NULL; bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); if (gf_list_count(samp->cues)) { i=0; while ((cue = (GF_WebVTTCue *)gf_list_enum(samp->cues, &i))) { e = webvtt_write_cue(bs, cue); if (e) break; } if (e) { gf_bs_del(bs); return NULL; } } else { GF_Box *cuebox = (GF_Box *)gf_isom_box_new(GF_ISOM_BOX_TYPE_VTTE); e = gf_isom_box_size((GF_Box *)cuebox); if (!e) e = gf_isom_box_write((GF_Box *)cuebox, bs); gf_isom_box_del((GF_Box *)cuebox); if (e) { gf_bs_del(bs); return NULL; } } res = gf_isom_sample_new(); if (!res) { gf_bs_del(bs); return NULL; } gf_bs_get_content(bs, &res->data, &res->dataLength); gf_bs_del(bs); res->IsRAP = RAP; return res; }
GF_Err gf_isom_set_ipod_compatible(GF_ISOFile *the_file, u32 trackNumber) { static u8 ipod_ext[][16] = { { 0x6B, 0x68, 0x40, 0xF2, 0x5F, 0x24, 0x4F, 0xC5, 0xBA, 0x39, 0xA5, 0x1B, 0xCF, 0x03, 0x23, 0xF3} }; GF_TrackBox *trak; GF_Err e; GF_MPEGVisualSampleEntryBox *entry; e = CanAccessMovie(the_file, GF_ISOM_OPEN_WRITE); if (e) return e; trak = gf_isom_get_track_from_file(the_file, trackNumber); if (!trak || !trak->Media) return GF_BAD_PARAM; entry = (GF_MPEGVisualSampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, 0); if (!entry) return GF_OK; if (entry->type != GF_ISOM_BOX_TYPE_AVC1) return GF_OK; if (!entry->ipod_ext) entry->ipod_ext = (GF_UnknownUUIDBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_UUID); memcpy(entry->ipod_ext->uuid, ipod_ext, sizeof(u8)*16); entry->ipod_ext->dataSize = 0; return GF_OK; }
GF_Err gf_isom_update_stxt_description(GF_ISOFile *movie, u32 trackNumber, const char *encoding, const char *config, u32 DescriptionIndex) { GF_TrackBox *trak; GF_Err e; GF_MetaDataSampleEntryBox *sample_entry; e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); if (e) return e; trak = gf_isom_get_track_from_file(movie, trackNumber); if (!trak || !trak->Media || !DescriptionIndex) return GF_BAD_PARAM; sample_entry = (GF_MetaDataSampleEntryBox *)gf_list_get(trak->Media->information->sampleTable->SampleDescription->other_boxes, DescriptionIndex-1); if (!sample_entry) return GF_BAD_PARAM; if (sample_entry->type != GF_ISOM_BOX_TYPE_METT && sample_entry->type != GF_ISOM_BOX_TYPE_SBTT && sample_entry->type != GF_ISOM_BOX_TYPE_STXT) { return GF_BAD_PARAM; } if (!sample_entry->config) sample_entry->config = (GF_TextConfigBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_TXTC); if (!movie->keep_utc) trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); if (sample_entry->config->config) { gf_free(sample_entry->config->config); } sample_entry->config->config = gf_strdup(config); if (sample_entry->content_encoding) { gf_free(sample_entry->content_encoding); } if (encoding) { sample_entry->content_encoding = gf_strdup(encoding); } return e; }
GF_HintSample *gf_isom_hint_sample_new(u32 ProtocolType) { GF_HintSample *tmp; switch (ProtocolType) { case GF_ISOM_BOX_TYPE_RTP_STSD: case GF_ISOM_BOX_TYPE_SRTP_STSD: case GF_ISOM_BOX_TYPE_RRTP_STSD: case GF_ISOM_BOX_TYPE_RTCP_STSD: break; case GF_ISOM_BOX_TYPE_FDP_STSD: return (GF_HintSample *) gf_isom_box_new(GF_ISOM_BOX_TYPE_FDSA); break; default: return NULL; } GF_SAFEALLOC(tmp, GF_HintSample); if (!tmp) return NULL; tmp->packetTable = gf_list_new(); tmp->hint_subtype = ProtocolType; return tmp; }
GF_Err gf_isom_hevc_config_update(GF_ISOFile *the_file, u32 trackNumber, u32 DescriptionIndex, GF_HEVCConfig *cfg) { u32 i, array_incomplete; GF_TrackBox *trak; GF_Err e; GF_MPEGVisualSampleEntryBox *entry; e = CanAccessMovie(the_file, GF_ISOM_OPEN_WRITE); if (e) return e; trak = gf_isom_get_track_from_file(the_file, trackNumber); if (!trak || !trak->Media || !cfg || !DescriptionIndex) return GF_BAD_PARAM; entry = (GF_MPEGVisualSampleEntryBox *)gf_list_get(trak->Media->information->sampleTable->SampleDescription->other_boxes, DescriptionIndex-1); if (!entry) return GF_BAD_PARAM; switch (entry->type) { case GF_ISOM_BOX_TYPE_HVC1: case GF_ISOM_BOX_TYPE_HEV1: break; default: return GF_BAD_PARAM; } if (!entry->hevc_config) entry->hevc_config = (GF_HEVCConfigurationBox*)gf_isom_box_new(GF_ISOM_BOX_TYPE_HVCC); if (entry->hevc_config->config) gf_odf_hevc_cfg_del(entry->hevc_config->config); entry->hevc_config->config = HEVC_DuplicateConfig(cfg); array_incomplete = 0; for (i=0; i<gf_list_count(entry->hevc_config->config->param_array); i++) { GF_HEVCParamArray *ar = gf_list_get(entry->hevc_config->config->param_array, i); if (!ar->array_completeness) { array_incomplete = 1; break; } } entry->type = array_incomplete ? GF_ISOM_BOX_TYPE_HEV1 : GF_ISOM_BOX_TYPE_HVC1; HEVC_RewriteESDescriptor(entry); return GF_OK; }
GF_Err gf_isom_new_webvtt_description(GF_ISOFile *movie, u32 trackNumber, GF_TextSampleDescriptor *desc, char *URLname, char *URNname, u32 *outDescriptionIndex) { GF_TrackBox *trak; GF_Err e; u32 dataRefIndex; GF_WebVTTSampleEntryBox *wvtt; e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); if (e) return e; trak = gf_isom_get_track_from_file(movie, trackNumber); if (!trak || !trak->Media) return GF_BAD_PARAM; switch (trak->Media->handler->handlerType) { case GF_ISOM_MEDIA_TEXT: case GF_ISOM_MEDIA_SUBT: case GF_ISOM_MEDIA_MPEG_SUBT: break; default: return GF_BAD_PARAM; } //get or create the data ref e = Media_FindDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); if (e) return e; if (!dataRefIndex) { e = Media_CreateDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); if (e) return e; } if (!movie->keep_utc) trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); wvtt = (GF_WebVTTSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_WVTT); wvtt->dataReferenceIndex = dataRefIndex; gf_list_add(trak->Media->information->sampleTable->SampleDescription->other_boxes, wvtt); if (outDescriptionIndex) *outDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->other_boxes); return e; }
static GF_Err webvtt_write_cue(GF_BitStream *bs, GF_WebVTTCue *cue) { GF_Err e; GF_VTTCueBox *cuebox; if (!cue) return GF_OK; cuebox = (GF_VTTCueBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_VTCC_CUE); if (cue->id) { cuebox->id = (GF_StringBox *)boxstring_new_with_data(GF_ISOM_BOX_TYPE_IDEN, cue->id); } if (cue->settings) { cuebox->settings = (GF_StringBox *)boxstring_new_with_data(GF_ISOM_BOX_TYPE_STTG, cue->settings); } if (cue->text) { cuebox->payload = (GF_StringBox *)boxstring_new_with_data(GF_ISOM_BOX_TYPE_PAYL, cue->text); } /* TODO: check if a time box should be written */ e = gf_isom_box_size((GF_Box *)cuebox); if (!e) e = gf_isom_box_write((GF_Box *)cuebox, bs); gf_isom_box_del((GF_Box *)cuebox); return e; }
GF_EXPORT GF_Err gf_isom_3gp_config_new(GF_ISOFile *the_file, u32 trackNumber, GF_3GPConfig *cfg, char *URLname, char *URNname, u32 *outDescriptionIndex) { GF_TrackBox *trak; GF_Err e; u32 dataRefIndex; u32 cfg_type; e = CanAccessMovie(the_file, GF_ISOM_OPEN_WRITE); if (e) return e; trak = gf_isom_get_track_from_file(the_file, trackNumber); if (!trak || !trak->Media || !cfg) return GF_BAD_PARAM; switch (cfg->type) { case GF_ISOM_SUBTYPE_3GP_AMR: case GF_ISOM_SUBTYPE_3GP_AMR_WB: if (trak->Media->handler->handlerType!=GF_ISOM_MEDIA_AUDIO) return GF_BAD_PARAM; cfg_type = GF_ISOM_BOX_TYPE_DAMR; break; case GF_ISOM_SUBTYPE_3GP_EVRC: if (trak->Media->handler->handlerType!=GF_ISOM_MEDIA_AUDIO) return GF_BAD_PARAM; cfg_type = GF_ISOM_BOX_TYPE_DEVC; break; case GF_ISOM_SUBTYPE_3GP_QCELP: if (trak->Media->handler->handlerType!=GF_ISOM_MEDIA_AUDIO) return GF_BAD_PARAM; cfg_type = GF_ISOM_BOX_TYPE_DQCP; break; case GF_ISOM_SUBTYPE_3GP_SMV: if (trak->Media->handler->handlerType!=GF_ISOM_MEDIA_AUDIO) return GF_BAD_PARAM; cfg_type = GF_ISOM_BOX_TYPE_DSMV; break; case GF_ISOM_SUBTYPE_3GP_H263: if (trak->Media->handler->handlerType!=GF_ISOM_MEDIA_VISUAL) return GF_BAD_PARAM; cfg_type = GF_ISOM_BOX_TYPE_D263; break; case 0: return GF_BAD_PARAM; default: return GF_NOT_SUPPORTED; } //get or create the data ref e = Media_FindDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); if (e) return e; if (!dataRefIndex) { e = Media_CreateDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); if (e) return e; } trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); switch (cfg->type) { case GF_ISOM_SUBTYPE_3GP_AMR: case GF_ISOM_SUBTYPE_3GP_AMR_WB: case GF_ISOM_SUBTYPE_3GP_EVRC: case GF_ISOM_SUBTYPE_3GP_QCELP: case GF_ISOM_SUBTYPE_3GP_SMV: { GF_3GPPAudioSampleEntryBox *entry = (GF_3GPPAudioSampleEntryBox *) gf_isom_box_new(cfg->type); if (!entry) return GF_OUT_OF_MEM; entry->info = (GF_3GPPConfigBox *) gf_isom_box_new(cfg_type); if (!entry->info) { gf_isom_box_del((GF_Box *) entry); return GF_OUT_OF_MEM; } memcpy(&entry->info->cfg, cfg, sizeof(GF_3GPConfig)); entry->samplerate_hi = trak->Media->mediaHeader->timeScale; entry->dataReferenceIndex = dataRefIndex; e = gf_list_add(trak->Media->information->sampleTable->SampleDescription->other_boxes, entry); *outDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->other_boxes); } break; case GF_ISOM_SUBTYPE_3GP_H263: { GF_3GPPVisualSampleEntryBox *entry = (GF_3GPPVisualSampleEntryBox *) gf_isom_box_new(cfg->type); if (!entry) return GF_OUT_OF_MEM; entry->info = (GF_3GPPConfigBox *) gf_isom_box_new(cfg_type); if (!entry->info) { gf_isom_box_del((GF_Box *) entry); return GF_OUT_OF_MEM; } memcpy(&entry->info->cfg, cfg, sizeof(GF_3GPConfig)); entry->dataReferenceIndex = dataRefIndex; e = gf_list_add(trak->Media->information->sampleTable->SampleDescription->other_boxes, entry); *outDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->other_boxes); } break; } 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; }
static GF_Err gf_isom_avc_config_update_ex(GF_ISOFile *the_file, u32 trackNumber, u32 DescriptionIndex, GF_AVCConfig *cfg, u32 op_type) { GF_TrackBox *trak; GF_Err e; GF_MPEGVisualSampleEntryBox *entry; e = CanAccessMovie(the_file, GF_ISOM_OPEN_WRITE); if (e) return e; trak = gf_isom_get_track_from_file(the_file, trackNumber); if (!trak || !trak->Media || !DescriptionIndex) return GF_BAD_PARAM; entry = (GF_MPEGVisualSampleEntryBox *)gf_list_get(trak->Media->information->sampleTable->SampleDescription->other_boxes, DescriptionIndex-1); if (!entry) return GF_BAD_PARAM; switch (entry->type) { case GF_ISOM_BOX_TYPE_AVC1: case GF_ISOM_BOX_TYPE_AVC2: case GF_ISOM_BOX_TYPE_AVC3: case GF_ISOM_BOX_TYPE_AVC4: case GF_ISOM_BOX_TYPE_SVC1: break; default: return GF_BAD_PARAM; } switch (op_type) { /*AVCC replacement*/ case 0: if (!cfg) return GF_BAD_PARAM; if (!entry->avc_config) entry->avc_config = (GF_AVCConfigurationBox*)gf_isom_box_new(GF_ISOM_BOX_TYPE_AVCC); if (entry->avc_config->config) gf_odf_avc_cfg_del(entry->avc_config->config); entry->avc_config->config = AVC_DuplicateConfig(cfg); entry->type = GF_ISOM_BOX_TYPE_AVC1; break; /*SVCC replacement*/ case 1: if (!cfg) return GF_BAD_PARAM; if (!entry->svc_config) entry->svc_config = (GF_AVCConfigurationBox*)gf_isom_box_new(GF_ISOM_BOX_TYPE_SVCC); if (entry->svc_config->config) gf_odf_avc_cfg_del(entry->svc_config->config); entry->svc_config->config = AVC_DuplicateConfig(cfg); entry->type = GF_ISOM_BOX_TYPE_AVC1; break; /*SVCC replacement and AVC removal*/ case 2: if (!cfg) return GF_BAD_PARAM; if (entry->avc_config) { gf_isom_box_del((GF_Box*)entry->avc_config); entry->avc_config = NULL; } if (!entry->svc_config) entry->svc_config = (GF_AVCConfigurationBox*)gf_isom_box_new(GF_ISOM_BOX_TYPE_SVCC); if (entry->svc_config->config) gf_odf_avc_cfg_del(entry->svc_config->config); entry->svc_config->config = AVC_DuplicateConfig(cfg); entry->type = GF_ISOM_BOX_TYPE_SVC1; break; /*AVCC removal and switch to avc3*/ case 3: if (!entry->avc_config || !entry->avc_config->config) return GF_BAD_PARAM; if (entry->svc_config) { gf_isom_box_del((GF_Box*)entry->svc_config); entry->svc_config = NULL; } while (gf_list_count(entry->avc_config->config->sequenceParameterSets)) { GF_AVCConfigSlot *sl = gf_list_get(entry->avc_config->config->sequenceParameterSets, 0); gf_list_rem(entry->avc_config->config->sequenceParameterSets, 0); if (sl->data) gf_free(sl->data); gf_free(sl); } while (gf_list_count(entry->avc_config->config->pictureParameterSets)) { GF_AVCConfigSlot *sl = gf_list_get(entry->avc_config->config->pictureParameterSets, 0); gf_list_rem(entry->avc_config->config->pictureParameterSets, 0); if (sl->data) gf_free(sl->data); gf_free(sl); } if (entry->type == GF_ISOM_BOX_TYPE_AVC1) entry->type = GF_ISOM_BOX_TYPE_AVC3; else if (entry->type == GF_ISOM_BOX_TYPE_AVC2) entry->type = GF_ISOM_BOX_TYPE_AVC4; break; } AVC_RewriteESDescriptor(entry); return GF_OK; }
GF_Err Media_AddSample(GF_MediaBox *mdia, u64 data_offset, GF_ISOSample *sample, u32 StreamDescIndex, u32 syncShadowNumber) { GF_Err e; GF_SampleTableBox *stbl; u32 sampleNumber, i; if (!mdia || !sample) return GF_BAD_PARAM; stbl = mdia->information->sampleTable; //get a valid sampleNumber for this new guy e = stbl_AddDTS(stbl, sample->DTS, &sampleNumber, mdia->mediaHeader->timeScale); if (e) return e; //add size e = stbl_AddSize(stbl->SampleSize, sampleNumber, sample->dataLength); if (e) return e; //adds CTS offset if (sample->CTS_Offset) { //if we don't have a CTS table, add it... if (!stbl->CompositionOffset) stbl->CompositionOffset = (GF_CompositionOffsetBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_CTTS); //then add our CTS (the prev samples with no CTS offset will be automatically added... e = stbl_AddCTS(stbl, sampleNumber, sample->CTS_Offset); if (e) return e; } else if (stbl->CompositionOffset) { e = stbl_AddCTS(stbl, sampleNumber, sample->CTS_Offset); if (e) return e; } //The first non sync sample we see must create a syncTable if (sample->IsRAP) { //insert it only if we have a sync table if (stbl->SyncSample) { e = stbl_AddRAP(stbl->SyncSample, sampleNumber); if (e) return e; } } else { //non-sync sample. Create a SyncSample table if needed if (!stbl->SyncSample) { stbl->SyncSample = (GF_SyncSampleBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSS); //all the prev samples are sync for (i=0; i<stbl->SampleSize->sampleCount; i++) { if (i+1 != sampleNumber) { e = stbl_AddRAP(stbl->SyncSample, i+1); if (e) return e; } } } } if (sample->IsRAP==2) { e = stbl_AddRedundant(stbl, sampleNumber); if (e) return e; } //and update the chunks e = stbl_AddChunkOffset(mdia, sampleNumber, StreamDescIndex, data_offset); if (e) return e; if (!syncShadowNumber) return GF_OK; if (!stbl->ShadowSync) stbl->ShadowSync = (GF_ShadowSyncBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSH); return stbl_AddShadow(mdia->information->sampleTable->ShadowSync, sampleNumber, syncShadowNumber); }
GF_Err AVC_HEVC_UpdateESD(GF_MPEGVisualSampleEntryBox *avc, GF_ESD *esd) { if (!avc->bitrate) avc->bitrate = (GF_MPEG4BitRateBox*)gf_isom_box_new(GF_ISOM_BOX_TYPE_BTRT); if (avc->descr) gf_isom_box_del((GF_Box *) avc->descr); avc->descr = NULL; avc->bitrate->avgBitrate = esd->decoderConfig->avgBitrate; avc->bitrate->maxBitrate = esd->decoderConfig->maxBitrate; avc->bitrate->bufferSizeDB = esd->decoderConfig->bufferSizeDB; if (gf_list_count(esd->IPIDataSet) || gf_list_count(esd->IPMPDescriptorPointers) || esd->langDesc || gf_list_count(esd->extensionDescriptors) || esd->ipiPtr || esd->qos || esd->RegDescriptor) { avc->descr = (GF_MPEG4ExtensionDescriptorsBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_M4DS); if (esd->RegDescriptor) { gf_list_add(avc->descr->descriptors, esd->RegDescriptor); esd->RegDescriptor = NULL; } if (esd->qos) { gf_list_add(avc->descr->descriptors, esd->qos); esd->qos = NULL; } if (esd->ipiPtr) { gf_list_add(avc->descr->descriptors, esd->ipiPtr); esd->ipiPtr= NULL; } while (gf_list_count(esd->IPIDataSet)) { GF_Descriptor *desc = (GF_Descriptor *)gf_list_get(esd->IPIDataSet, 0); gf_list_rem(esd->IPIDataSet, 0); gf_list_add(avc->descr->descriptors, desc); } while (gf_list_count(esd->IPMPDescriptorPointers)) { GF_Descriptor *desc = (GF_Descriptor *)gf_list_get(esd->IPMPDescriptorPointers, 0); gf_list_rem(esd->IPMPDescriptorPointers, 0); gf_list_add(avc->descr->descriptors, desc); } if (esd->langDesc) { gf_list_add(avc->descr->descriptors, esd->langDesc); esd->langDesc = NULL; } while (gf_list_count(esd->extensionDescriptors)) { GF_Descriptor *desc = (GF_Descriptor *)gf_list_get(esd->extensionDescriptors, 0); gf_list_rem(esd->extensionDescriptors, 0); gf_list_add(avc->descr->descriptors, desc); } } /*update GF_AVCConfig*/ if (!avc->svc_config) { if (esd->decoderConfig->objectTypeIndication==GPAC_OTI_VIDEO_HEVC) { if (!avc->hevc_config) avc->hevc_config = (GF_HEVCConfigurationBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_HVCC); if (esd->decoderConfig->decoderSpecificInfo && esd->decoderConfig->decoderSpecificInfo->data) { if (avc->hevc_config->config) gf_odf_hevc_cfg_del(avc->hevc_config->config); avc->hevc_config->config = gf_odf_hevc_cfg_read(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength); } } else { if (!avc->avc_config) avc->avc_config = (GF_AVCConfigurationBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_AVCC); if (esd->decoderConfig->decoderSpecificInfo && esd->decoderConfig->decoderSpecificInfo->data) { if (avc->avc_config->config) gf_odf_avc_cfg_del(avc->avc_config->config); avc->avc_config->config = gf_odf_avc_cfg_read(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength); } } } gf_odf_desc_del((GF_Descriptor *)esd); if (avc->hevc_config) { HEVC_RewriteESDescriptor(avc); } else { AVC_RewriteESDescriptor(avc); } return GF_OK; }
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; }
GF_Err MergeTrack(GF_TrackBox *trak, GF_TrackFragmentBox *traf, u64 moof_offset, Bool is_first_merge) { u32 i, j, chunk_size; u64 base_offset, data_offset; u32 def_duration, DescIndex, def_size, def_flags; u32 duration, size, flags, cts_offset, prev_trun_data_offset; u8 pad, sync; u16 degr; GF_TrackFragmentRunBox *trun; GF_TrunEntry *ent; void stbl_AppendTime(GF_SampleTableBox *stbl, u32 duration); void stbl_AppendSize(GF_SampleTableBox *stbl, u32 size); void stbl_AppendChunk(GF_SampleTableBox *stbl, u64 offset); void stbl_AppendSampleToChunk(GF_SampleTableBox *stbl, u32 DescIndex, u32 samplesInChunk); void stbl_AppendCTSOffset(GF_SampleTableBox *stbl, u32 CTSOffset); void stbl_AppendRAP(GF_SampleTableBox *stbl, u8 isRap); void stbl_AppendPadding(GF_SampleTableBox *stbl, u8 padding); void stbl_AppendDegradation(GF_SampleTableBox *stbl, u16 DegradationPriority); if (trak->Header->trackID != traf->tfhd->trackID) return GF_OK; //setup all our defaults DescIndex = (traf->tfhd->flags & GF_ISOM_TRAF_SAMPLE_DESC) ? traf->tfhd->sample_desc_index : traf->trex->def_sample_desc_index; def_duration = (traf->tfhd->flags & GF_ISOM_TRAF_SAMPLE_DUR) ? traf->tfhd->def_sample_duration : traf->trex->def_sample_duration; def_size = (traf->tfhd->flags & GF_ISOM_TRAF_SAMPLE_SIZE) ? traf->tfhd->def_sample_size : traf->trex->def_sample_size; def_flags = (traf->tfhd->flags & GF_ISOM_TRAF_SAMPLE_FLAGS) ? traf->tfhd->def_sample_flags : traf->trex->def_sample_flags; //locate base offset base_offset = (traf->tfhd->flags & GF_ISOM_TRAF_BASE_OFFSET) ? traf->tfhd->base_data_offset : moof_offset; chunk_size = 0; prev_trun_data_offset = 0; /*in playback mode*/ if (traf->tfdt && is_first_merge) { #ifndef GPAC_DISABLE_LOG if (trak->sample_count_at_seg_start && (trak->dts_at_seg_start != traf->tfdt->baseMediaDecodeTime)) { s32 drift = (s32) ((s64)trak->dts_at_seg_start - (s64) traf->tfdt->baseMediaDecodeTime); if (drift<0) drift = -drift; if (drift > 1) { GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso file] Error: TFDT timing "LLD" different from track cumulated timing "LLD" - using tfdt\n", traf->tfdt->baseMediaDecodeTime, trak->dts_at_seg_start )); } } #endif trak->dts_at_seg_start = traf->tfdt->baseMediaDecodeTime; } i=0; while ((trun = (GF_TrackFragmentRunBox *)gf_list_enum(traf->TrackRuns, &i))) { //merge the run for (j=0; j<trun->sample_count; j++) { ent = (GF_TrunEntry*)gf_list_get(trun->entries, j); size = def_size; duration = def_duration; flags = def_flags; if (ent) { if (trun->flags & GF_ISOM_TRUN_DURATION) duration = ent->Duration; if (trun->flags & GF_ISOM_TRUN_SIZE) size = ent->size; if (trun->flags & GF_ISOM_TRUN_FLAGS) { flags = ent->flags; } else if (!j && (trun->flags & GF_ISOM_TRUN_FIRST_FLAG)) { flags = trun->first_sample_flags; } } //add size first stbl_AppendSize(trak->Media->information->sampleTable, size); //then TS stbl_AppendTime(trak->Media->information->sampleTable, duration); //add chunk on first sample if (!j) { data_offset = base_offset; //aggregated offset if base-data-offset-present is not set AND if default-base-is-moof is not set if (!(traf->tfhd->flags & GF_ISOM_TRAF_BASE_OFFSET) && !(traf->tfhd->flags & GF_ISOM_MOOF_BASE_OFFSET) ) data_offset += chunk_size; if (trun->flags & GF_ISOM_TRUN_DATA_OFFSET) { data_offset += trun->data_offset; /*reset chunk size since data is now relative to this trun*/ chunk_size = 0; /*remember this data offset for following trun*/ prev_trun_data_offset = trun->data_offset; } else { /*data offset is previous chunk size plus previous offset of the trun*/ data_offset += prev_trun_data_offset; } stbl_AppendChunk(trak->Media->information->sampleTable, data_offset); //then sampleToChunk stbl_AppendSampleToChunk(trak->Media->information->sampleTable, DescIndex, trun->sample_count); } chunk_size += size; //CTS cts_offset = (trun->flags & GF_ISOM_TRUN_CTS_OFFSET) ? ent->CTS_Offset : 0; stbl_AppendCTSOffset(trak->Media->information->sampleTable, cts_offset); //flags sync = GF_ISOM_GET_FRAG_SYNC(flags); stbl_AppendRAP(trak->Media->information->sampleTable, sync); pad = GF_ISOM_GET_FRAG_PAD(flags); if (pad) stbl_AppendPadding(trak->Media->information->sampleTable, pad); degr = GF_ISOM_GET_FRAG_DEG(flags); if (degr) stbl_AppendDegradation(trak->Media->information->sampleTable, degr); } } /*merge sample groups*/ if (traf->sampleGroups) { GF_List *groups; GF_List *groupDescs; if (!trak->Media->information->sampleTable->sampleGroups) trak->Media->information->sampleTable->sampleGroups = gf_list_new(); if (!trak->Media->information->sampleTable->sampleGroupsDescription) trak->Media->information->sampleTable->sampleGroupsDescription = gf_list_new(); groupDescs = trak->Media->information->sampleTable->sampleGroupsDescription; for (i=0; i<gf_list_count(traf->sampleGroupsDescription); i++) { GF_SampleGroupDescriptionBox *new_sgdesc = NULL; GF_SampleGroupDescriptionBox *sgdesc = gf_list_get(traf->sampleGroupsDescription, i); for (j=0; j<gf_list_count(groupDescs); j++) { new_sgdesc = gf_list_get(groupDescs, j); if (new_sgdesc->grouping_type==sgdesc->grouping_type) break; new_sgdesc = NULL; } /*new description, move it to our sample table*/ if (!new_sgdesc) { gf_list_add(groupDescs, sgdesc); gf_list_rem(traf->sampleGroupsDescription, i); i--; } /*merge descriptions*/ else { u32 idx = gf_list_count(new_sgdesc->group_descriptions); for (j=idx; j<gf_list_count(sgdesc->group_descriptions); j++) { void *ptr = gf_list_get(sgdesc->group_descriptions, j); if (ptr) { gf_list_add(new_sgdesc->group_descriptions, ptr); gf_list_rem(sgdesc->group_descriptions, j); j--; } } } } groups = trak->Media->information->sampleTable->sampleGroups; for (i=0; i<gf_list_count(traf->sampleGroups); i++) { GF_SampleGroupBox *stbl_group = NULL; GF_SampleGroupBox *frag_group = gf_list_get(traf->sampleGroups, i); for (j=0; j<gf_list_count(groups); j++) { stbl_group = gf_list_get(groups, j); if ((frag_group->grouping_type==stbl_group->grouping_type) && (frag_group->grouping_type_parameter==stbl_group->grouping_type_parameter)) break; stbl_group = NULL; } if (!stbl_group) { stbl_group = (GF_SampleGroupBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_SBGP); stbl_group->grouping_type = frag_group->grouping_type; stbl_group->grouping_type_parameter = frag_group->grouping_type_parameter; stbl_group->version = frag_group->version; gf_list_add(groups, stbl_group); } if (frag_group->entry_count && stbl_group->entry_count && (frag_group->sample_entries[0].group_description_index==stbl_group->sample_entries[stbl_group->entry_count-1].group_description_index) ) { stbl_group->sample_entries[stbl_group->entry_count - 1].sample_count += frag_group->sample_entries[0].sample_count; if (frag_group->entry_count>1) { stbl_group->sample_entries = gf_realloc(stbl_group->sample_entries, sizeof(GF_SampleGroupEntry) * (stbl_group->entry_count + frag_group->entry_count - 1)); memcpy(&stbl_group->sample_entries[stbl_group->entry_count], &frag_group->sample_entries[1], sizeof(GF_SampleGroupEntry) * (frag_group->entry_count - 1)); stbl_group->entry_count += frag_group->entry_count - 1; } } else { stbl_group->sample_entries = gf_realloc(stbl_group->sample_entries, sizeof(GF_SampleGroupEntry) * (stbl_group->entry_count + frag_group->entry_count)); memcpy(&stbl_group->sample_entries[stbl_group->entry_count], &frag_group->sample_entries[0], sizeof(GF_SampleGroupEntry) * frag_group->entry_count); stbl_group->entry_count += frag_group->entry_count; } } } return GF_OK; }
GF_Err gf_isom_set_oma_protection(GF_ISOFile *the_file, u32 trackNumber, u32 desc_index, char *contentID, char *kms_URI, u32 encryption_type, u64 plainTextLength, char *textual_headers, u32 textual_headers_len, Bool selective_encryption, u32 KI_length, u32 IV_length) { u32 original_format; GF_Err e; GF_SampleEntryBox *sea; GF_TrackBox *trak = gf_isom_get_track_from_file(the_file, trackNumber); if (!trak) return GF_BAD_PARAM; e = Media_GetSampleDesc(trak->Media, desc_index, &sea, NULL); if (e) return e; /* Replacing the Media Type */ switch (sea->type) { case GF_ISOM_BOX_TYPE_MP4A: case GF_ISOM_BOX_TYPE_DAMR: case GF_ISOM_BOX_TYPE_DEVC: case GF_ISOM_BOX_TYPE_DQCP: case GF_ISOM_BOX_TYPE_DSMV: original_format = sea->type; sea->type = GF_ISOM_BOX_TYPE_ENCA; break; case GF_ISOM_BOX_TYPE_MP4V: case GF_ISOM_BOX_TYPE_AVC1: case GF_ISOM_BOX_TYPE_D263: original_format = sea->type; sea->type = GF_ISOM_BOX_TYPE_ENCV; break; case GF_ISOM_BOX_TYPE_MP4S: original_format = sea->type; sea->type = GF_ISOM_BOX_TYPE_ENCS; break; default: return GF_BAD_PARAM; } sea->protection_info = (GF_ProtectionInfoBox *)sinf_New(); sea->protection_info->scheme_type = (GF_SchemeTypeBox *)schm_New(); sea->protection_info->scheme_type->scheme_type = GF_4CC('o','d','k','m'); sea->protection_info->scheme_type->scheme_version = 0x00000200; sea->protection_info->original_format = (GF_OriginalFormatBox *)frma_New(); sea->protection_info->original_format->data_format = original_format; sea->protection_info->info = (GF_SchemeInformationBox *)schi_New(); sea->protection_info->info->okms = (GF_OMADRMKMSBox *)odkm_New(); sea->protection_info->info->okms->fmt = (GF_OMADRMAUFormatBox*)gf_isom_box_new(GF_ISOM_BOX_TYPE_ODAF); sea->protection_info->info->okms->fmt->selective_encryption = selective_encryption; sea->protection_info->info->okms->fmt->key_indicator_length = KI_length; sea->protection_info->info->okms->fmt->IV_length = IV_length; sea->protection_info->info->okms->hdr = (GF_OMADRMCommonHeaderBox*)ohdr_New(); sea->protection_info->info->okms->hdr->EncryptionMethod = encryption_type; sea->protection_info->info->okms->hdr->PaddingScheme = (encryption_type==0x01) ? 1 : 0; sea->protection_info->info->okms->hdr->PlaintextLength = plainTextLength; if (contentID) sea->protection_info->info->okms->hdr->ContentID = strdup(contentID); if (kms_URI) sea->protection_info->info->okms->hdr->RightsIssuerURL = strdup(kms_URI); if (textual_headers) { sea->protection_info->info->okms->hdr->TextualHeaders = malloc(sizeof(char)*textual_headers_len); memcpy(sea->protection_info->info->okms->hdr->TextualHeaders, textual_headers, sizeof(char)*textual_headers_len); sea->protection_info->info->okms->hdr->TextualHeadersLen = textual_headers_len; } return GF_OK; }
GF_Err LSR_UpdateESD(GF_LASeRSampleEntryBox *lsr, GF_ESD *esd) { GF_BitRateBox *btrt = gf_isom_sample_entry_get_bitrate((GF_SampleEntryBox *)lsr, GF_TRUE); if (lsr->descr) gf_isom_box_del((GF_Box *) lsr->descr); lsr->descr = NULL; btrt->avgBitrate = esd->decoderConfig->avgBitrate; btrt->maxBitrate = esd->decoderConfig->maxBitrate; btrt->bufferSizeDB = esd->decoderConfig->bufferSizeDB; if (gf_list_count(esd->IPIDataSet) || gf_list_count(esd->IPMPDescriptorPointers) || esd->langDesc || gf_list_count(esd->extensionDescriptors) || esd->ipiPtr || esd->qos || esd->RegDescriptor) { lsr->descr = (GF_MPEG4ExtensionDescriptorsBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_M4DS); if (esd->RegDescriptor) { gf_list_add(lsr->descr->descriptors, esd->RegDescriptor); esd->RegDescriptor = NULL; } if (esd->qos) { gf_list_add(lsr->descr->descriptors, esd->qos); esd->qos = NULL; } if (esd->ipiPtr) { gf_list_add(lsr->descr->descriptors, esd->ipiPtr); esd->ipiPtr= NULL; } while (gf_list_count(esd->IPIDataSet)) { GF_Descriptor *desc = (GF_Descriptor *)gf_list_get(esd->IPIDataSet, 0); gf_list_rem(esd->IPIDataSet, 0); gf_list_add(lsr->descr->descriptors, desc); } while (gf_list_count(esd->IPMPDescriptorPointers)) { GF_Descriptor *desc = (GF_Descriptor *)gf_list_get(esd->IPMPDescriptorPointers, 0); gf_list_rem(esd->IPMPDescriptorPointers, 0); gf_list_add(lsr->descr->descriptors, desc); } if (esd->langDesc) { gf_list_add(lsr->descr->descriptors, esd->langDesc); esd->langDesc = NULL; } while (gf_list_count(esd->extensionDescriptors)) { GF_Descriptor *desc = (GF_Descriptor *)gf_list_get(esd->extensionDescriptors, 0); gf_list_rem(esd->extensionDescriptors, 0); gf_list_add(lsr->descr->descriptors, desc); } } /*update GF_AVCConfig*/ if (!lsr->lsr_config) lsr->lsr_config = (GF_LASERConfigurationBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_LSRC); if (esd->decoderConfig->decoderSpecificInfo && esd->decoderConfig->decoderSpecificInfo->data) { lsr->lsr_config->hdr = gf_realloc(lsr->lsr_config->hdr, sizeof(char) * esd->decoderConfig->decoderSpecificInfo->dataLength); lsr->lsr_config->hdr_size = esd->decoderConfig->decoderSpecificInfo->dataLength; memcpy(lsr->lsr_config->hdr, esd->decoderConfig->decoderSpecificInfo->data, sizeof(char)*esd->decoderConfig->decoderSpecificInfo->dataLength); } gf_odf_desc_del((GF_Descriptor *)esd); return GF_OK; }
GF_Err gf_isom_add_meta_item_extended(GF_ISOFile *file, Bool root_meta, u32 track_num, Bool self_reference, char *resource_path, const char *item_name, u32 item_id, const char *mime_type, const char *content_encoding, GF_ImageItemProperties *image_props, const char *URL, const char *URN, char *data, u32 data_len) { GF_Err e; GF_ItemLocationEntry *location_entry; GF_ItemInfoEntryBox *infe; GF_MetaBox *meta; u32 lastItemID = 0; if (!self_reference && !item_name && !resource_path) return GF_BAD_PARAM; e = CanAccessMovie(file, GF_ISOM_OPEN_WRITE); if (e) return e; meta = gf_isom_get_meta(file, root_meta, track_num); if (!meta) { GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("Trying to add item, but missing meta box")); return GF_BAD_PARAM; } e = FlushCaptureMode(file); if (e) return e; /*check file exists */ if (!URN && !URL && !self_reference && !data) { FILE *src = gf_fopen(resource_path, "rb"); if (!src) return GF_URL_ERROR; gf_fclose(src); } if (meta->item_infos) { u32 i; u32 item_count = gf_list_count(meta->item_infos->item_infos); for (i = 0; i < item_count; i++) { GF_ItemInfoEntryBox *e= (GF_ItemInfoEntryBox *)gf_list_get(meta->item_infos->item_infos, i); if (e->item_ID > lastItemID) lastItemID = e->item_ID; if (item_id == e->item_ID) { GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[IsoMedia] Item with id %d already exists, ignoring\n", item_id)); item_id = 0; } } } infe = (GF_ItemInfoEntryBox *)infe_New(); if (item_id) { infe->item_ID = item_id; } else { infe->item_ID = ++lastItemID; } /*get relative name*/ if (item_name) { infe->item_name = gf_strdup(item_name); } else if (resource_path) { if (strrchr(resource_path, GF_PATH_SEPARATOR)) { infe->item_name = gf_strdup(strrchr(resource_path, GF_PATH_SEPARATOR) + 1); } else { infe->item_name = gf_strdup(resource_path); } } if (mime_type) { infe->content_type = gf_strdup(mime_type); } else { infe->content_type = gf_strdup("application/octet-stream"); } if (content_encoding) infe->content_encoding = gf_strdup(content_encoding); /*Creation of the ItemLocation */ location_entry = (GF_ItemLocationEntry*)gf_malloc(sizeof(GF_ItemLocationEntry)); if (!location_entry) { gf_isom_box_del((GF_Box *)infe); return GF_OUT_OF_MEM; } memset(location_entry, 0, sizeof(GF_ItemLocationEntry)); location_entry->extent_entries = gf_list_new(); /*Creates an mdat if it does not exist*/ if (!file->mdat) { file->mdat = (GF_MediaDataBox *)mdat_New(); gf_list_add(file->TopBoxes, file->mdat); } /*Creation an ItemLocation Box if it does not exist*/ if (!meta->item_locations) meta->item_locations = (GF_ItemLocationBox *)iloc_New(); gf_list_add(meta->item_locations->location_entries, location_entry); location_entry->item_ID = infe->item_ID; if (!meta->item_infos) meta->item_infos = (GF_ItemInfoBox *) iinf_New(); e = gf_list_add(meta->item_infos->item_infos, infe); if (e) return e; if (image_props) { meta_process_image_properties(meta, infe->item_ID, image_props); } /*0: the current file*/ location_entry->data_reference_index = 0; if (self_reference) { GF_ItemExtentEntry *entry; GF_SAFEALLOC(entry, GF_ItemExtentEntry); gf_list_add(location_entry->extent_entries, entry); if (!infe->item_name) infe->item_name = gf_strdup(""); return GF_OK; } /*file not copied, just referenced*/ if (URL || URN) { u32 dataRefIndex; if (!meta->file_locations) meta->file_locations = (GF_DataInformationBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_DINF); if (!meta->file_locations->dref) meta->file_locations->dref = (GF_DataReferenceBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_DREF); e = Media_FindDataRef(meta->file_locations->dref, (char *) URL, (char *) URN, &dataRefIndex); if (e) return e; if (!dataRefIndex) { e = Media_CreateDataRef(meta->file_locations->dref, (char *) URL, (char *) URN, &dataRefIndex); if (e) return e; } location_entry->data_reference_index = dataRefIndex; } /*capture mode, write to disk*/ if ((file->openMode == GF_ISOM_OPEN_WRITE) && !location_entry->data_reference_index) { FILE *src; GF_ItemExtentEntry *entry; GF_SAFEALLOC(entry, GF_ItemExtentEntry); location_entry->base_offset = gf_bs_get_position(file->editFileMap->bs); /*update base offset size*/ if (location_entry->base_offset>0xFFFFFFFF) meta->item_locations->base_offset_size = 8; else if (location_entry->base_offset && !meta->item_locations->base_offset_size) meta->item_locations->base_offset_size = 4; entry->extent_length = 0; entry->extent_offset = 0; gf_list_add(location_entry->extent_entries, entry); if (data) { gf_bs_write_data(file->editFileMap->bs, data, data_len); /*update length size*/ if (entry->extent_length>0xFFFFFFFF) meta->item_locations->length_size = 8; else if (entry->extent_length && !meta->item_locations->length_size) meta->item_locations->length_size = 4; } else if (resource_path) { src = gf_fopen(resource_path, "rb"); if (src) { char cache_data[4096]; u64 remain; gf_fseek(src, 0, SEEK_END); entry->extent_length = gf_ftell(src); gf_fseek(src, 0, SEEK_SET); remain = entry->extent_length; while (remain) { u32 size_cache = (remain>4096) ? 4096 : (u32) remain; size_t read = fread(cache_data, 1, size_cache, src); if (read==(size_t)-1) break; gf_bs_write_data(file->editFileMap->bs, cache_data, (u32) read); remain -= (u32) read; } gf_fclose(src); /*update length size*/ if (entry->extent_length>0xFFFFFFFF) meta->item_locations->length_size = 8; else if (entry->extent_length && !meta->item_locations->length_size) meta->item_locations->length_size = 4; } } } /*store full path for info*/ else if (!location_entry->data_reference_index) { if (data) { infe->full_path = (char *)gf_malloc(sizeof(char) * data_len); memcpy(infe->full_path, data, sizeof(char) * data_len); infe->data_len = data_len; } else { infe->full_path = gf_strdup(resource_path); infe->data_len = 0; } } return GF_OK; }
static GF_Err ShiftOffset(GF_ISOFile *file, GF_List *writers, u64 offset) { u32 i, j, k, l, last; TrackWriter *writer; GF_StscEntry *ent; GF_ChunkOffsetBox *stco; GF_ChunkLargeOffsetBox *co64; if (file->meta) ShiftMetaOffset(file->meta, offset); if (file->moov && file->moov->meta) ShiftMetaOffset(file->moov->meta, offset); i=0; while ((writer = (TrackWriter *)gf_list_enum(writers, &i))) { if (writer->mdia->mediaTrack->meta) ShiftMetaOffset(writer->mdia->mediaTrack->meta, offset); //we have to proceed entry by entry in case a part of the media is not self-contained... for (j=0; j<writer->stsc->nb_entries; j++) { ent = &writer->stsc->entries[j]; if (!Media_IsSelfContained(writer->mdia, ent->sampleDescriptionIndex)) continue; //OK, get the chunk(s) number(s) and "shift" its (their) offset(s). if (writer->stco->type == GF_ISOM_BOX_TYPE_STCO) { stco = (GF_ChunkOffsetBox *) writer->stco; //be carefull for the last entry, nextChunk is set to 0 in edit mode... last = ent->nextChunk ? ent->nextChunk : stco->nb_entries + 1; for (k = ent->firstChunk; k < last; k++) { if (stco->offsets[k-1] + offset > 0xFFFFFFFF) { //too bad, rewrite the table.... co64 = (GF_ChunkLargeOffsetBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_CO64); if (!co64) return GF_OUT_OF_MEM; co64->nb_entries = stco->nb_entries; co64->offsets = (u64*)gf_malloc(co64->nb_entries * sizeof(u64)); if (!co64) { gf_isom_box_del((GF_Box *)co64); return GF_OUT_OF_MEM; } //duplicate the table for (l = 0; l < co64->nb_entries; l++) { co64->offsets[l] = (u64) stco->offsets[l]; if (l + 1 == k) co64->offsets[l] += offset; } //and replace our box gf_isom_box_del(writer->stco); writer->stco = (GF_Box *)co64; } else { stco->offsets[k-1] += (u32) offset; } } } else { co64 = (GF_ChunkLargeOffsetBox *) writer->stco; //be carefull for the last entry ... last = ent->nextChunk ? ent->nextChunk : co64->nb_entries + 1; for (k = ent->firstChunk; k < last; k++) { co64->offsets[k-1] += offset; } } } } return GF_OK; }
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_new_stxt_description(GF_ISOFile *movie, u32 trackNumber, u32 type, const char *mime, const char *encoding, const char * config, u32 *outDescriptionIndex) { GF_TrackBox *trak; GF_Err e; u32 dataRefIndex; GF_MetaDataSampleEntryBox *sample_entry; char *URLname = NULL; char *URNname = NULL; e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); if (e) return e; trak = gf_isom_get_track_from_file(movie, trackNumber); if (!trak || !trak->Media) return GF_BAD_PARAM; switch (trak->Media->handler->handlerType) { case GF_ISOM_MEDIA_MPEG_SUBT: case GF_ISOM_MEDIA_META: case GF_ISOM_MEDIA_SCENE: case GF_ISOM_MEDIA_TEXT: case GF_ISOM_MEDIA_SUBT: break; default: return GF_BAD_PARAM; } switch (type) { case GF_ISOM_SUBTYPE_SBTT: case GF_ISOM_SUBTYPE_STXT: case GF_ISOM_SUBTYPE_METT: break; default: GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("SampleEntry shall be either Metadata, Subtitle or SimpleText. Abort.\n")); return GF_BAD_PARAM; } if (!mime) { GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("Text (Metadata, Subtitle or SimpleText) SampleEntry: mime is mandatory. Using text/plain.\n")); mime = "text/plain"; } //get or create the data ref e = Media_FindDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); if (e) return e; if (!dataRefIndex) { e = Media_CreateDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); if (e) return e; } if (!movie->keep_utc) trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); sample_entry = (GF_MetaDataSampleEntryBox *) gf_isom_box_new(type); sample_entry->dataReferenceIndex = dataRefIndex; gf_list_add(trak->Media->information->sampleTable->SampleDescription->other_boxes, sample_entry); if (outDescriptionIndex) *outDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->other_boxes); sample_entry->mime_type = gf_strdup(mime); if (encoding) sample_entry->content_encoding = gf_strdup(encoding); if (config) { sample_entry->config = (GF_TextConfigBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_TXTC); sample_entry->config->config = gf_strdup(config); } return e; }
GF_Err NewMedia(GF_MediaBox **mdia, u32 MediaType, u32 TimeScale) { GF_MediaHeaderBox *mdhd; GF_Box *mediaInfo; GF_HandlerBox *hdlr; GF_MediaInformationBox *minf; GF_DataInformationBox *dinf; GF_SampleTableBox *stbl; GF_DataReferenceBox *dref; char *str; GF_Err e; if (*mdia || !mdia) return GF_BAD_PARAM; e = GF_OK; minf = NULL; mdhd = NULL; hdlr = NULL; dinf = NULL; stbl = NULL; dref = NULL; mediaInfo = NULL; //first create the media *mdia = (GF_MediaBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MDIA); mdhd = (GF_MediaHeaderBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MDHD); //"handler name" is for debugging purposes. Let's stick our name here ;) switch (MediaType) { case GF_ISOM_MEDIA_VISUAL: mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_VMHD); str = "GPAC ISO Video Handler"; break; case GF_ISOM_MEDIA_AUDIO: mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_SMHD); str = "GPAC ISO Audio Handler"; break; case GF_ISOM_MEDIA_HINT: mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_HMHD); str = "GPAC ISO Hint Handler"; break; case GF_ISOM_MEDIA_META: mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD); str = "GPAC Timed MetaData Handler"; break; case GF_ISOM_MEDIA_OD: mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD); str = "GPAC MPEG-4 OD Handler"; break; case GF_ISOM_MEDIA_OCR: mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD); str = "GPAC MPEG-4 OCR Handler"; break; case GF_ISOM_MEDIA_SCENE: mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD); str = "GPAC MPEG-4 Scene Description Handler"; break; case GF_ISOM_MEDIA_MPEG7: mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD); str = "GPAC MPEG-4 MPEG-7 Handler"; break; case GF_ISOM_MEDIA_OCI: mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD); str = "GPAC MPEG-4 OCI Handler"; break; case GF_ISOM_MEDIA_IPMP: mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD); str = "GPAC MPEG-4 IPMP Handler"; break; case GF_ISOM_MEDIA_MPEGJ: mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD); str = "GPAC MPEG-4 MPEG-J Handler"; break; case GF_ISOM_MEDIA_TEXT: case GF_ISOM_MEDIA_SUBT: mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD); str = "GPAC Streaming Text Handler"; break; case GF_ISOM_MEDIA_DIMS: mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_VMHD); MediaType = GF_ISOM_MEDIA_SCENE; str = "GPAC DIMS Handler"; break; default: mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD); str = "GPAC IsoMedia Handler"; break; } hdlr = (GF_HandlerBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_HDLR); minf = (GF_MediaInformationBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MINF); mdhd->timeScale = TimeScale; hdlr->handlerType = MediaType; hdlr->nameUTF8 = gf_strdup(str); //first set-up the sample table... stbl = (GF_SampleTableBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STBL); dinf = (GF_DataInformationBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_DINF); stbl->SampleDescription = (GF_SampleDescriptionBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSD); stbl->ChunkOffset = gf_isom_box_new(GF_ISOM_BOX_TYPE_STCO); //by default create a regular table stbl->SampleSize = (GF_SampleSizeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSZ); stbl->SampleToChunk = (GF_SampleToChunkBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSC); stbl->TimeToSample = (GF_TimeToSampleBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STTS); //Create a data reference WITHOUT DATA ENTRY (we don't know anything yet about the media Data) dref = (GF_DataReferenceBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_DREF); e = dinf_AddBox((GF_Box*)dinf, (GF_Box *)dref); if (e) goto err_exit; e = minf_AddBox((GF_Box*)minf, (GF_Box *) mediaInfo); if (e) goto err_exit; e = minf_AddBox((GF_Box*)minf, (GF_Box *) stbl); if (e) goto err_exit; e = minf_AddBox((GF_Box*)minf, (GF_Box *) dinf); if (e) goto err_exit; e = mdia_AddBox((GF_Box*)*mdia, (GF_Box *) mdhd); if (e) goto err_exit; e = mdia_AddBox((GF_Box*)*mdia, (GF_Box *) minf); if (e) goto err_exit; e = mdia_AddBox((GF_Box*)*mdia, (GF_Box *) hdlr); if (e) goto err_exit; return GF_OK; err_exit: if (mdhd) gf_isom_box_del((GF_Box *)mdhd); if (minf) gf_isom_box_del((GF_Box *)minf); if (hdlr) { if (hdlr->nameUTF8) gf_free(hdlr->nameUTF8); gf_isom_box_del((GF_Box *)hdlr); } return e; }
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 /*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); #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) { return GF_ISOM_INVALID_FILE; } return e; } else { return e; } switch (a->type) { /*MOOV box*/ case GF_ISOM_BOX_TYPE_MOOV: if (mov->moov) 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) 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); 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); 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: if (((GF_SegmentTypeBox *)a)->majorBrand == GF_4CC('i', 's', 's', 's') || ((GF_SegmentTypeBox *)a)->majorBrand == GF_4CC('i', 'm', 's', 's')) mov->is_index_segment = 1; 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: ((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) { gf_list_add(mov->TopBoxes, a); } 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); if (e) { GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Error merging fragment: %s\n", gf_error_to_string(e) )); } } break; #endif case GF_4CC('j','P',' ',' '): { GF_UnknownBox *box = (GF_UnknownBox*)a; u8 *c = 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; default: totSize += a->size; e = gf_list_add(mov->TopBoxes, a); break; } #ifndef GPAC_DISABLE_ISOM_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_INVALID_FILE; /*we MUST have movie header*/ if (mov->moov && !mov->moov->mvhd) return GF_ISOM_INVALID_FILE; /*we MUST have meta handler*/ if (mov->meta && !mov->meta->handler) 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 } #endif /*GPAC_DISABLE_ISOM_WRITE*/ 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; }
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 if (mov->is_smooth) { trak = gf_list_get(mov->moov->trackList, 0); traf->trex = (GF_TrackExtendsBox*)gf_list_get(mov->moov->mvex->TrackExList, 0); assert(traf->trex); traf->trex->trackID = trak->Header->trackID = traf->tfhd->trackID; } 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 ? traf->tfhd->trackID : 0)); 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 *)gf_isom_box_new(GF_ISOM_BOX_TYPE_PSSH); 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; }