| /* | |
| * GPAC Multimedia Framework | |
| * | |
| * Authors: Cyril Concolato - Jean le Feuvre | |
| * Copyright (c) Telecom ParisTech 2005-2026 | |
| * All rights reserved | |
| * | |
| * This file is part of GPAC / ISO Media File Format sub-project | |
| * | |
| * GPAC is free software; you can redistribute it and/or modify | |
| * it under the terms of the GNU Lesser General Public License as published by | |
| * the Free Software Foundation; either version 2, or (at your option) | |
| * any later version. | |
| * | |
| * GPAC is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; see the file COPYING. If not, write to | |
| * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | |
| * | |
| */ | |
| #include <gpac/internal/isomedia_dev.h> | |
| #ifndef GPAC_DISABLE_ISOM | |
| GF_MetaBox *gf_isom_get_meta(GF_ISOFile *file, Bool root_meta, u32 track_num) | |
| { | |
| GF_TrackBox *tk = NULL; | |
| if (!file) return NULL; | |
| if (root_meta) return file->meta; | |
| if (!track_num) return file->moov ? file->moov->meta : NULL; | |
| if (file->moov) | |
| tk = (GF_TrackBox*)gf_list_get(file->moov->trackList, track_num-1); | |
| return tk ? tk->meta : NULL; | |
| } | |
| GF_EXPORT | |
| u32 gf_isom_get_meta_type(GF_ISOFile *file, Bool root_meta, u32 track_num) | |
| { | |
| GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); | |
| if (!meta) return 0; | |
| if (!meta->handler) return 0; | |
| return meta->handler->handlerType; | |
| } | |
| GF_EXPORT | |
| u32 gf_isom_has_meta_xml(GF_ISOFile *file, Bool root_meta, u32 track_num) | |
| { | |
| u32 i, count; | |
| GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); | |
| if (!meta) return 0; | |
| count = gf_list_count(meta->child_boxes); | |
| for (i=0; i<count; i++) { | |
| GF_Box *a = (GF_Box *)gf_list_get(meta->child_boxes, i); | |
| if (a->type == GF_ISOM_BOX_TYPE_XML) return 1; | |
| if (a->type == GF_ISOM_BOX_TYPE_BXML) return 2; | |
| } | |
| return 0; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_isom_extract_meta_xml(GF_ISOFile *file, Bool root_meta, u32 track_num, char *outName, Bool *is_binary) | |
| { | |
| u32 i, count; | |
| FILE *didfile; | |
| GF_Err e=GF_OK; | |
| GF_XMLBox *xml = NULL; | |
| GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); | |
| if (!meta) return GF_BAD_PARAM; | |
| /*Find XMLBox*/ | |
| count = gf_list_count(meta->child_boxes); | |
| for (i = 0; i <count; i++) { | |
| GF_Box *a = (GF_Box *)gf_list_get(meta->child_boxes, i); | |
| if ((a->type == GF_ISOM_BOX_TYPE_XML) || (a->type == GF_ISOM_BOX_TYPE_BXML) ) { | |
| xml = (GF_XMLBox *)a; | |
| break; | |
| } | |
| } | |
| if (!xml || !xml->xml) return GF_BAD_PARAM; | |
| didfile = gf_fopen(outName, "wb"); | |
| if (!didfile) return GF_IO_ERR; | |
| u32 len = (u32) strlen(xml->xml); | |
| if (gf_fwrite(xml->xml, len, didfile)!=len) e = GF_IO_ERR; | |
| gf_fclose(didfile); | |
| if (is_binary) *is_binary = (xml->type==GF_ISOM_BOX_TYPE_BXML) ? 1 : 0; | |
| return e; | |
| } | |
| #if 0 //unused | |
| GF_XMLBox *gf_isom_get_meta_xml(GF_ISOFile *file, Bool root_meta, u32 track_num, Bool *is_binary) | |
| { | |
| u32 i, count; | |
| GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); | |
| if (!meta) return NULL; | |
| /*Find XMLBox*/ | |
| count = gf_list_count(meta->child_boxes); | |
| for (i = 0; i <count; i++) { | |
| GF_Box *a = (GF_Box *)gf_list_get(meta->child_boxes, i); | |
| if (a->type == GF_ISOM_BOX_TYPE_XML) { | |
| *is_binary = 0; | |
| return (GF_XMLBox *)a; | |
| } else if (a->type == GF_ISOM_BOX_TYPE_BXML) { | |
| *is_binary = 1; | |
| return (GF_XMLBox *)a; | |
| } | |
| } | |
| return NULL; | |
| } | |
| #endif | |
| GF_EXPORT | |
| u32 gf_isom_get_meta_item_count(GF_ISOFile *file, Bool root_meta, u32 track_num) | |
| { | |
| GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); | |
| if (!meta || !meta->item_infos || !meta->item_locations) return 0; | |
| return gf_list_count(meta->item_infos->item_infos); | |
| } | |
| GF_EXPORT | |
| GF_Err gf_isom_get_meta_item_info(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 item_num, | |
| u32 *itemID, u32 *type, u32 *protection_scheme, u32 *protection_scheme_version, Bool *is_self_reference, | |
| const char **item_name, const char **item_mime_type, const char **item_encoding, | |
| const char **item_url, const char **item_urn) | |
| { | |
| GF_ItemInfoEntryBox *iinf; | |
| u32 i, count; | |
| GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); | |
| if (!meta || !meta->item_infos || !meta->item_locations) return GF_BAD_PARAM; | |
| iinf = (GF_ItemInfoEntryBox *)gf_list_get(meta->item_infos->item_infos, item_num-1); | |
| if (!iinf) return GF_BAD_PARAM; | |
| if (itemID) (*itemID) = iinf->item_ID; | |
| if (item_name) (*item_name) = iinf->item_name; | |
| if (item_mime_type) (*item_mime_type) = iinf->content_type; | |
| if (item_encoding) (*item_encoding) = iinf->content_encoding; | |
| if (is_self_reference) *is_self_reference = 0; | |
| if (type) *type = iinf->item_type; | |
| if (item_url) (*item_url) = NULL; | |
| if (item_urn) (*item_urn) = NULL; | |
| if (iinf->item_protection_index) { | |
| GF_ProtectionSchemeInfoBox *sinf; | |
| if (!meta->protections) return GF_ISOM_INVALID_FILE; | |
| sinf = gf_list_get(meta->protections->protection_information, iinf->item_protection_index-1); | |
| if (!sinf) return GF_ISOM_INVALID_FILE; | |
| if (sinf->scheme_type) { | |
| if (protection_scheme) *protection_scheme = sinf->scheme_type->scheme_type; | |
| if (protection_scheme_version) *protection_scheme_version = sinf->scheme_type->scheme_version; | |
| } else { | |
| if (protection_scheme) *protection_scheme = GF_4CC('u','k','n','w'); | |
| if (protection_scheme_version) *protection_scheme_version = 0; | |
| } | |
| } else { | |
| if (protection_scheme) *protection_scheme = 0; | |
| if (protection_scheme_version) *protection_scheme_version = 0; | |
| } | |
| count = gf_list_count(meta->item_locations->location_entries); | |
| for (i=0; i<count; i++) { | |
| GF_ItemLocationEntry *iloc = (GF_ItemLocationEntry *)gf_list_get(meta->item_locations->location_entries, i); | |
| if (iloc->item_ID==iinf->item_ID) { | |
| if (iloc->data_reference_index) { | |
| if (!meta->file_locations || !meta->file_locations->dref) | |
| return GF_ISOM_INVALID_FILE; | |
| GF_Box *a = (GF_Box *)gf_list_get(meta->file_locations->dref->child_boxes, iloc->data_reference_index-1); | |
| if (!a) return GF_ISOM_INVALID_FILE; | |
| if (a->type==GF_ISOM_BOX_TYPE_URL) { | |
| if (item_url) (*item_url) = ((GF_DataEntryURLBox*)a)->location; | |
| } else if (a->type==GF_ISOM_BOX_TYPE_URN) { | |
| if (item_url) (*item_url) = ((GF_DataEntryURNBox*)a)->location; | |
| if (item_urn) (*item_urn) = ((GF_DataEntryURNBox*)a)->nameURN; | |
| } | |
| break; | |
| } else if (is_self_reference && !iloc->base_offset) { | |
| GF_ItemExtentEntry *entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0); | |
| if (!entry) return GF_ISOM_INVALID_FILE; | |
| if (!entry->extent_length | |
| #ifndef GPAC_DISABLE_ISOM_WRITE | |
| && !entry->original_extent_offset | |
| #endif | |
| ) | |
| *is_self_reference = 1; | |
| } | |
| } | |
| } | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| u32 gf_isom_get_meta_item_flags(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 item_num) | |
| { | |
| GF_ItemInfoEntryBox *iinf; | |
| GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); | |
| if (!meta || !meta->item_infos || !meta->item_locations) return 0; | |
| iinf = (GF_ItemInfoEntryBox *)gf_list_get(meta->item_infos->item_infos, item_num-1); | |
| if (!iinf) return 0; | |
| return iinf->flags; | |
| } | |
| GF_EXPORT | |
| u32 gf_isom_get_meta_item_by_id(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 item_ID) | |
| { | |
| u32 i, count; | |
| GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); | |
| if (!meta || !meta->item_infos || !meta->item_locations) return 0; | |
| count = gf_list_count(meta->item_infos->item_infos); | |
| for (i=0; i<count; i++) { | |
| GF_ItemInfoEntryBox *iinf = (GF_ItemInfoEntryBox *)gf_list_get(meta->item_infos->item_infos, i); | |
| if (iinf->item_ID==item_ID) return i+1; | |
| } | |
| return 0; | |
| } | |
| static GF_Err gf_isom_extract_meta_item_intern(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 item_id, const char *dump_file_name, u8 **out_data, u32 *out_size, u32 *out_alloc_size, const char **out_mime, Bool use_annex_b) | |
| { | |
| GF_BitStream *item_bs; | |
| char szPath[1024]; | |
| FILE *resource = NULL; | |
| u32 i, count; | |
| GF_Err e; | |
| GF_ItemLocationEntry *location_entry; | |
| u32 item_num; | |
| u32 item_type = 0; | |
| u32 nalu_size_length = 0; | |
| u64 idat_offset = 0; | |
| char *item_name = NULL; | |
| GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); | |
| if (!meta || !meta->item_infos || !meta->item_locations) return GF_BAD_PARAM; | |
| if (out_mime) *out_mime = NULL; | |
| item_num = gf_isom_get_meta_item_by_id(file, root_meta, track_num, item_id); | |
| if (item_num) { | |
| GF_ItemInfoEntryBox *item_entry = (GF_ItemInfoEntryBox *)gf_list_get(meta->item_infos->item_infos, item_num-1); | |
| item_name = item_entry->item_name; | |
| if (out_mime) *out_mime = item_entry->content_type; | |
| item_type = item_entry->item_type; | |
| } | |
| location_entry = NULL; | |
| count = gf_list_count(meta->item_locations->location_entries); | |
| for (i=0; i<count; i++) { | |
| location_entry = (GF_ItemLocationEntry *)gf_list_get(meta->item_locations->location_entries, i); | |
| if (location_entry->item_ID == item_id) break; | |
| location_entry = NULL; | |
| } | |
| if (!location_entry) return GF_BAD_PARAM; | |
| /* offsets are expressed from the start of the idat box instead of the start of the file */ | |
| if (location_entry->construction_method == 1) { | |
| Bool found = GF_FALSE; | |
| count = gf_list_count(meta->child_boxes); | |
| for (i = 0; i <count; i++) { | |
| GF_Box *a = (GF_Box *)gf_list_get(meta->child_boxes, i); | |
| if (a->type == GF_ISOM_BOX_TYPE_IDAT) { | |
| GF_MediaDataBox *p = (GF_MediaDataBox *)a; | |
| idat_offset = p->bsOffset; | |
| found = GF_TRUE; | |
| break; | |
| } | |
| } | |
| if (!found) { | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[IsoMedia] Item %d references an inexistant idat box\n", item_num)); | |
| return GF_BAD_PARAM; | |
| } | |
| } | |
| else if (location_entry->construction_method == 2) { | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[IsoMedia] Item %d use from-item construction method, not supported - patch welcome\n", item_num)); | |
| return GF_NOT_SUPPORTED; | |
| } | |
| /* when construction_method==1, data_reference_index is ignored */ | |
| /*FIXME*/ | |
| else if (location_entry->data_reference_index) { | |
| char *item_url = NULL, *item_urn = NULL; | |
| if (!meta || !meta->file_locations || !meta->file_locations->dref || !meta->file_locations->dref->child_boxes) | |
| return GF_ISOM_INVALID_FILE; | |
| GF_FullBox *a = (GF_FullBox *)gf_list_get(meta->file_locations->dref->child_boxes, location_entry->data_reference_index-1); | |
| if (!a) return GF_ISOM_INVALID_FILE; | |
| if (a->type==GF_ISOM_BOX_TYPE_URL) { | |
| if (!(a->flags & 1)) { | |
| item_url = ((GF_DataEntryURLBox*)a)->location; | |
| } | |
| } else if (a->type==GF_ISOM_BOX_TYPE_URN) { | |
| item_url = ((GF_DataEntryURNBox*)a)->location; | |
| item_urn = ((GF_DataEntryURNBox*)a)->nameURN; | |
| } | |
| if (item_url || item_urn) { | |
| GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[IsoMedia] Item already outside the ISO file at URL: %s, URN: %s\n", (item_url?item_url:"N/A"), (item_urn?item_urn:"N/A") )); | |
| return GF_OK; | |
| } | |
| if (a->type==GF_ISOM_BOX_TYPE_IMDT) { | |
| Bool found = GF_FALSE; | |
| u32 imda_id = ((GF_DataEntryURLBox*)a)->imda_ref_id; | |
| count = gf_list_count(file->TopBoxes); | |
| for (i=0; i<count; i++) { | |
| GF_MediaDataBox *imda = (GF_MediaDataBox *)gf_list_get(file->TopBoxes, i); | |
| if (imda->type != GF_ISOM_BOX_TYPE_MDAT) continue; | |
| if (!imda->is_imda) continue; | |
| if (imda->imda_id != imda_id) continue; | |
| idat_offset = imda->bsOffset; | |
| found = GF_TRUE; | |
| break; | |
| } | |
| if (!found) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[IsoMedia] Item %d references an inexistant imda box of id %u\n", item_num, imda_id)); | |
| return GF_BAD_PARAM; | |
| } | |
| } | |
| } | |
| /*don't extract self-reference item*/ | |
| count = gf_list_count(location_entry->extent_entries); | |
| if (!location_entry->base_offset && (count==1)) { | |
| GF_ItemExtentEntry *extent_entry = (GF_ItemExtentEntry *)gf_list_get(location_entry->extent_entries, 0); | |
| if (!extent_entry->extent_length | |
| #ifndef GPAC_DISABLE_ISOM_WRITE | |
| && !extent_entry->original_extent_offset | |
| #endif | |
| ) return GF_BAD_PARAM; | |
| } | |
| item_bs = NULL; | |
| if (out_data) { | |
| item_bs = gf_bs_new(*out_data, *out_size, GF_BITSTREAM_WRITE_DYN); | |
| } else if (dump_file_name) { | |
| gf_strlcpy(szPath, dump_file_name, sizeof(szPath)); | |
| resource = gf_fopen(szPath, "wb"); | |
| item_bs = gf_bs_from_file(resource, GF_BITSTREAM_WRITE); | |
| } else { | |
| if (item_name && strlen(item_name) > 0) gf_strlcpy(szPath, item_name, sizeof(szPath)); | |
| else snprintf(szPath, sizeof(szPath), "item_id%02d", item_id); | |
| resource = gf_fopen(szPath, "wb"); | |
| item_bs = gf_bs_from_file(resource, GF_BITSTREAM_WRITE); | |
| } | |
| if (!item_bs) | |
| return GF_BAD_PARAM; | |
| if ((item_type == GF_ISOM_SUBTYPE_HVC1) || (item_type == GF_ISOM_SUBTYPE_AVC_H264) || (item_type == GF_ISOM_SUBTYPE_VVC1) ) { | |
| u32 j, nb_assoc; | |
| GF_HEVCConfigurationBox *hvcc = NULL; | |
| GF_AVCConfigurationBox *avcc = NULL; | |
| GF_VVCConfigurationBox *vvcc = NULL; | |
| if (!meta->item_props || !meta->item_props->property_container || !meta->item_props->property_association) { | |
| if (item_bs) gf_bs_del(item_bs); | |
| return GF_NON_COMPLIANT_BITSTREAM; | |
| } | |
| nb_assoc = gf_list_count(meta->item_props->property_association->entries); | |
| for (i=0; i<nb_assoc; i++) { | |
| GF_ItemPropertyAssociationEntry *ent = gf_list_get(meta->item_props->property_association->entries, i); | |
| if (ent->item_id!=item_id) continue; | |
| for (j=0; j<ent->nb_associations; j++) { | |
| u32 idx = ent->associations[j].index; | |
| if (! idx) continue; | |
| hvcc = gf_list_get(meta->item_props->property_container->child_boxes, idx - 1); | |
| if (!hvcc) { | |
| if (item_bs) gf_bs_del(item_bs); | |
| return GF_NON_COMPLIANT_BITSTREAM; | |
| } | |
| if (hvcc->type == GF_ISOM_BOX_TYPE_HVCC) | |
| break; | |
| if (hvcc->type == GF_ISOM_BOX_TYPE_AVCC) { | |
| avcc = (GF_AVCConfigurationBox *) hvcc; | |
| hvcc = NULL; | |
| break; | |
| } | |
| if (hvcc->type == GF_ISOM_BOX_TYPE_VVCC) { | |
| vvcc = (GF_VVCConfigurationBox *) hvcc; | |
| hvcc = NULL; | |
| break; | |
| } | |
| hvcc = NULL; | |
| } | |
| if (avcc || hvcc || vvcc) break; | |
| } | |
| if (hvcc) { | |
| if (! hvcc->config) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("Missing HEVC config in hvcC\n")); | |
| } else { | |
| if (use_annex_b) { | |
| hvcc->config->write_annex_b = GF_TRUE; | |
| gf_odf_hevc_cfg_write_bs(hvcc->config, item_bs); | |
| hvcc->config->write_annex_b = GF_FALSE; | |
| } | |
| nalu_size_length = hvcc->config->nal_unit_size; | |
| } | |
| } else if (avcc) { | |
| if (! avcc->config) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("Missing AVC config in avcC\n")); | |
| } else { | |
| if (use_annex_b) { | |
| avcc->config->write_annex_b = GF_TRUE; | |
| gf_odf_avc_cfg_write_bs(avcc->config, item_bs); | |
| avcc->config->write_annex_b = GF_FALSE; | |
| } | |
| nalu_size_length = avcc->config->nal_unit_size; | |
| } | |
| } else if (vvcc) { | |
| if (! vvcc->config) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("Missing VVC config in vvcC\n")); | |
| } else { | |
| if (use_annex_b) { | |
| vvcc->config->write_annex_b = GF_TRUE; | |
| gf_odf_vvc_cfg_write_bs(vvcc->config, item_bs); | |
| vvcc->config->write_annex_b = GF_FALSE; | |
| } | |
| nalu_size_length = vvcc->config->nal_unit_size; | |
| } | |
| } | |
| } | |
| e = GF_OK; | |
| for (i=0; i<count; i++) { | |
| char buf_cache[4096]; | |
| u64 remain; | |
| GF_ItemExtentEntry *extent_entry = (GF_ItemExtentEntry *)gf_list_get(location_entry->extent_entries, i); | |
| gf_bs_seek(file->movieFileMap->bs, idat_offset + location_entry->base_offset + extent_entry->extent_offset); | |
| remain = extent_entry->extent_length; | |
| while (remain) { | |
| if (nalu_size_length) { | |
| if (remain < nalu_size_length) { | |
| e = GF_ISOM_INVALID_FILE; | |
| break; | |
| } | |
| u32 nal_size = gf_bs_read_int(file->movieFileMap->bs, 8*nalu_size_length); | |
| if (remain - nalu_size_length < nal_size) { | |
| e = GF_ISOM_INVALID_FILE; | |
| break; | |
| } | |
| if (use_annex_b) | |
| gf_bs_write_u32(item_bs, 1); | |
| else | |
| gf_bs_write_int(item_bs, nal_size, 8*nalu_size_length); | |
| remain -= nalu_size_length + nal_size; | |
| while (nal_size) { | |
| u32 cache_size = (nal_size>4096) ? 4096 : (u32) nal_size; | |
| gf_bs_read_data(file->movieFileMap->bs, buf_cache, cache_size); | |
| gf_bs_write_data(item_bs, buf_cache, cache_size); | |
| nal_size -= cache_size; | |
| } | |
| } else { | |
| u32 cache_size = (remain>4096) ? 4096 : (u32) remain; | |
| gf_bs_read_data(file->movieFileMap->bs, buf_cache, cache_size); | |
| gf_bs_write_data(item_bs, buf_cache, cache_size); | |
| remain -= cache_size; | |
| } | |
| if (gf_bs_is_overflow(file->movieFileMap->bs)) { | |
| e = GF_ISOM_INVALID_FILE; | |
| break; | |
| } | |
| } | |
| } | |
| if (out_data) { | |
| gf_bs_get_content_no_truncate(item_bs, out_data, out_size, out_alloc_size); | |
| } | |
| gf_bs_del(item_bs); | |
| if (resource) { | |
| gf_fclose(resource); | |
| } | |
| return e; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_isom_extract_meta_item(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 item_id, const char *dump_file_name) | |
| { | |
| return gf_isom_extract_meta_item_intern(file, root_meta, track_num, item_id, dump_file_name, NULL, NULL, NULL, NULL, GF_TRUE); | |
| } | |
| GF_EXPORT | |
| GF_Err gf_isom_extract_meta_item_mem(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 item_id, u8 **out_data, u32 *out_size, u32 *out_alloc_size, const char **out_mime, Bool use_annex_b) | |
| { | |
| return gf_isom_extract_meta_item_intern(file, root_meta, track_num, item_id, NULL, out_data, out_size, out_alloc_size, out_mime, use_annex_b); | |
| } | |
| GF_EXPORT | |
| GF_Err gf_isom_extract_meta_item_get_cenc_info(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 item_id, Bool *is_protected, | |
| u32 *skip_byte_block, u32 *crypt_byte_block, const u8 **key_info, u32 *key_info_size, u32 *aux_info_type_param, | |
| u8 **cenc_sai_data, u32 *cenc_sai_data_size, u32 *cenc_sai_alloc_size) | |
| { | |
| u32 count, i; | |
| u32 j, sai_item_id; | |
| Bool found = GF_FALSE; | |
| GF_ItemPropertyAssociationBox *ipma = NULL; | |
| GF_ItemPropertyContainerBox *ipco = NULL; | |
| GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); | |
| if (!meta) return GF_BAD_PARAM; | |
| ipma = meta->item_props->property_association; | |
| ipco = meta->item_props->property_container; | |
| count = gf_list_count(ipma->entries); | |
| for (i = 0; i < count; i++) { | |
| GF_ItemPropertyAssociationEntry *entry = (GF_ItemPropertyAssociationEntry *)gf_list_get(ipma->entries, i); | |
| if (entry->item_id != item_id) continue; | |
| for (j = 0; j < entry->nb_associations; j++) { | |
| GF_ItemEncryptionPropertyBox *ienc; | |
| u32 index = entry->associations[j].index; | |
| ienc = index ? (GF_ItemEncryptionPropertyBox *)gf_list_get(ipco->child_boxes, index - 1) : NULL; | |
| if (!ienc) continue; | |
| if (ienc->type!=GF_ISOM_BOX_TYPE_IENC) continue; | |
| if (ienc->key_info_size<19) return GF_ISOM_INVALID_FILE; | |
| *skip_byte_block = ienc->skip_byte_block; | |
| *crypt_byte_block = ienc->crypt_byte_block; | |
| *key_info = ienc->key_info; | |
| *key_info_size = ienc->key_info_size; | |
| found = GF_TRUE; | |
| break; | |
| } | |
| if (found) break; | |
| } | |
| if (!found) { | |
| *is_protected = GF_FALSE; | |
| return GF_OK; | |
| } | |
| *is_protected = GF_TRUE; | |
| sai_item_id = 0; | |
| //look for item reference | |
| if (!meta->item_refs) | |
| return GF_ISOM_INVALID_FILE; | |
| count = gf_list_count(meta->item_refs->references); | |
| for (i=0; i<count; i++) { | |
| GF_ItemReferenceTypeBox *iref = gf_list_get(meta->item_refs->references, i); | |
| if (iref->reference_type!=GF_ISOM_REF_AUXR) continue; | |
| sai_item_id = iref->to_item_IDs[0]; | |
| break; | |
| } | |
| if (!sai_item_id) return GF_ISOM_INVALID_FILE; | |
| if (aux_info_type_param) { | |
| count = gf_list_count(ipma->entries); | |
| found = GF_FALSE; | |
| for (i = 0; i < count; i++) { | |
| GF_ItemPropertyAssociationEntry *entry = (GF_ItemPropertyAssociationEntry *)gf_list_get(ipma->entries, i); | |
| if (entry->item_id != sai_item_id) continue; | |
| for (j = 0; j < entry->nb_associations; j++) { | |
| GF_AuxiliaryInfoPropertyBox *iaux; | |
| u32 index = entry->associations[j].index; | |
| iaux = index ? (GF_AuxiliaryInfoPropertyBox *)gf_list_get(ipco->child_boxes, index - 1) : NULL; | |
| if (!iaux) continue; | |
| if (iaux->type!=GF_ISOM_BOX_TYPE_IAUX) continue; | |
| switch (iaux->aux_info_type) { | |
| case GF_ISOM_CENC_SCHEME: | |
| case GF_ISOM_CENS_SCHEME: | |
| case GF_ISOM_CBC_SCHEME: | |
| case GF_ISOM_CBCS_SCHEME: | |
| case 0: | |
| break; | |
| default: | |
| continue; | |
| } | |
| *aux_info_type_param = iaux->aux_info_parameter; | |
| found = GF_TRUE; | |
| break; | |
| } | |
| if (found) break; | |
| } | |
| } | |
| if (!cenc_sai_data) | |
| return GF_OK; | |
| return gf_isom_extract_meta_item_mem(file, root_meta, track_num, sai_item_id, cenc_sai_data, cenc_sai_data_size, cenc_sai_alloc_size, NULL, GF_FALSE); | |
| } | |
| GF_EXPORT | |
| u32 gf_isom_get_meta_primary_item_id(GF_ISOFile *file, Bool root_meta, u32 track_num) | |
| { | |
| GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); | |
| if (!meta || !meta->primary_resource) return 0; | |
| return meta->primary_resource->item_ID; | |
| } | |
| #ifndef GPAC_DISABLE_ISOM_WRITE | |
| GF_EXPORT | |
| GF_Err gf_isom_set_meta_type(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 metaType) | |
| { | |
| char szName[40]; | |
| GF_MetaBox *meta; | |
| GF_Err e = gf_isom_can_access_movie(file, GF_ISOM_OPEN_WRITE); | |
| if (e) return e; | |
| meta = gf_isom_get_meta(file, root_meta, track_num); | |
| if (!meta) { | |
| if (!metaType) return GF_OK; | |
| meta = (GF_MetaBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_META); | |
| if (!meta) return GF_OUT_OF_MEM; | |
| if (root_meta) { | |
| file->meta = meta; | |
| gf_list_add(file->TopBoxes, meta); | |
| } else { | |
| e = gf_isom_insert_moov(file); | |
| if (e) return e; | |
| if (!track_num) { | |
| file->moov->meta = meta; | |
| if (!file->moov->child_boxes) file->moov->child_boxes = gf_list_new(); | |
| gf_list_add(file->moov->child_boxes, meta); | |
| } else { | |
| GF_TrackBox *tk = (GF_TrackBox *)gf_list_get(file->moov->trackList, track_num-1); | |
| if (!tk) { | |
| gf_isom_box_del((GF_Box *)meta); | |
| return GF_BAD_PARAM; | |
| } | |
| if (!tk->child_boxes) tk->child_boxes = gf_list_new(); | |
| gf_list_add(tk->child_boxes, meta); | |
| tk->meta = meta; | |
| } | |
| } | |
| } else if (!metaType) { | |
| if (root_meta) { | |
| gf_list_del_item(file->TopBoxes, meta); | |
| gf_isom_box_del((GF_Box *)file->meta); | |
| file->meta = NULL; | |
| } else if (file->moov) { | |
| if (!track_num) { | |
| gf_isom_box_del_parent(&file->moov->child_boxes, (GF_Box *)file->moov->meta); | |
| file->moov->meta = NULL; | |
| } else { | |
| GF_TrackBox *tk = (GF_TrackBox *)gf_list_get(file->moov->trackList, track_num-1); | |
| if (!tk) return GF_BAD_PARAM; | |
| gf_isom_box_del_parent(&tk->child_boxes, (GF_Box *)tk->meta); | |
| tk->meta = NULL; | |
| } | |
| } | |
| return GF_OK; | |
| } | |
| if (!meta->handler) { | |
| meta->handler = (GF_HandlerBox *)gf_isom_box_new_parent(&meta->child_boxes, GF_ISOM_BOX_TYPE_HDLR); | |
| if (!meta->handler) return GF_OUT_OF_MEM; | |
| } | |
| if (meta->handler->nameUTF8) gf_free(meta->handler->nameUTF8); | |
| meta->handler->handlerType = metaType; | |
| sprintf(szName, "GPAC %s Handler", gf_4cc_to_str(metaType)); | |
| meta->handler->nameUTF8 = gf_strdup(szName); | |
| if (!meta->handler->nameUTF8) return GF_OUT_OF_MEM; | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_isom_remove_meta_xml(GF_ISOFile *file, Bool root_meta, u32 track_num) | |
| { | |
| u32 i; | |
| GF_Box *a; | |
| GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); | |
| if (!meta) return GF_BAD_PARAM; | |
| i=0; | |
| while ((a = (GF_Box*)gf_list_enum(meta->child_boxes, &i))) { | |
| switch (a->type) { | |
| case GF_ISOM_BOX_TYPE_XML: | |
| case GF_ISOM_BOX_TYPE_BXML: | |
| gf_isom_box_del_parent(&meta->child_boxes, a); | |
| i--; | |
| return GF_OK; | |
| } | |
| } | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_isom_set_meta_xml(GF_ISOFile *file, Bool root_meta, u32 track_num, char *XMLFileName, unsigned char *data, u32 data_size, Bool IsBinaryXML) | |
| { | |
| GF_Err e; | |
| GF_XMLBox *xml; | |
| GF_MetaBox *meta; | |
| u32 length; | |
| if (!XMLFileName && !data) | |
| return GF_BAD_PARAM; | |
| e = gf_isom_can_access_movie(file, GF_ISOM_OPEN_WRITE); | |
| if (e) return e; | |
| meta = gf_isom_get_meta(file, root_meta, track_num); | |
| if (!meta) return GF_BAD_PARAM; | |
| e = gf_isom_remove_meta_xml(file, root_meta, track_num); | |
| if (e) return e; | |
| xml = (GF_XMLBox *)gf_isom_box_new_parent(&meta->child_boxes, GF_ISOM_BOX_TYPE_XML); | |
| if (!xml) return GF_OUT_OF_MEM; | |
| if (IsBinaryXML) xml->type = GF_ISOM_BOX_TYPE_BXML; | |
| if (XMLFileName) { | |
| /*assume 32bit max size = 4Go should be sufficient for a DID!!*/ | |
| return gf_file_load_data(XMLFileName, (u8 **) &xml->xml, &length); | |
| } | |
| /*assume 32bit max size = 4Go should be sufficient for a DID!!*/ | |
| xml->xml = (char*)gf_malloc(sizeof(unsigned char)*data_size); | |
| if (!xml->xml) return GF_OUT_OF_MEM; | |
| memcpy(xml->xml, data, sizeof(unsigned char)*data_size); | |
| return GF_OK; | |
| } | |
| #endif | |
| GF_EXPORT | |
| GF_Err gf_isom_get_meta_image_props(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 item_id, GF_ImageItemProperties *prop, GF_List *unmapped_props) { | |
| u32 count, i, inum; | |
| u32 j; | |
| GF_ItemPropertyAssociationBox *ipma = NULL; | |
| GF_ItemPropertyContainerBox *ipco = NULL; | |
| GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); | |
| if (!meta) return GF_BAD_PARAM; | |
| memset(prop, 0, sizeof(GF_ImageItemProperties)); | |
| if (!meta->item_props) return GF_OK; | |
| ipma = meta->item_props->property_association; | |
| ipco = meta->item_props->property_container; | |
| inum = gf_isom_get_meta_item_by_id(file, root_meta, track_num, item_id); | |
| i = gf_isom_get_meta_item_flags(file, root_meta, track_num, inum); | |
| if (i & 0x1) | |
| prop->hidden = GF_TRUE; | |
| count = (ipma && ipma->entries) ? gf_list_count(ipma->entries) : 0; | |
| for (i = 0; i < count; i++) { | |
| GF_ItemPropertyAssociationEntry *entry = (GF_ItemPropertyAssociationEntry *)gf_list_get(ipma->entries, i); | |
| if (entry->item_id != item_id) continue; | |
| if (entry->nb_associations && !ipco) return GF_ISOM_INVALID_FILE; | |
| for (j = 0; j < entry->nb_associations; j++) { | |
| GF_Box *b; | |
| u32 index = entry->associations[j].index; | |
| b = index ? (GF_Box *)gf_list_get(ipco->child_boxes, index - 1) : NULL; | |
| if (!b) continue; | |
| switch (b->type) { | |
| case GF_ISOM_BOX_TYPE_ISPE: | |
| { | |
| GF_ImageSpatialExtentsPropertyBox *ispe = (GF_ImageSpatialExtentsPropertyBox *)b; | |
| prop->width = ispe->image_width; | |
| prop->height = ispe->image_height; | |
| } | |
| break; | |
| case GF_ISOM_BOX_TYPE_RLOC: | |
| { | |
| GF_RelativeLocationPropertyBox *rloc = (GF_RelativeLocationPropertyBox *)b; | |
| prop->hOffset = rloc->horizontal_offset; | |
| prop->vOffset = rloc->vertical_offset; | |
| } | |
| break; | |
| case GF_ISOM_BOX_TYPE_PASP: | |
| { | |
| GF_PixelAspectRatioBox *pasp = (GF_PixelAspectRatioBox *)b; | |
| prop->hSpacing = pasp->hSpacing; | |
| prop->vSpacing = pasp->vSpacing; | |
| } | |
| break; | |
| case GF_ISOM_BOX_TYPE_PIXI: | |
| { | |
| u32 k; | |
| GF_PixelInformationPropertyBox *pixi = (GF_PixelInformationPropertyBox *)b; | |
| if (pixi->num_channels > 3) { | |
| return GF_BAD_PARAM; | |
| } | |
| prop->num_channels = pixi->num_channels; | |
| for(k = 0; k < 3; k++) { | |
| if (k < pixi->num_channels) { | |
| prop->bits_per_channel[k] = pixi->bits_per_channel[k]; | |
| } else { | |
| prop->bits_per_channel[k] = 0; | |
| } | |
| } | |
| } | |
| break; | |
| case GF_ISOM_BOX_TYPE_IROT: | |
| { | |
| GF_ImageRotationBox *irot = (GF_ImageRotationBox *)b; | |
| prop->angle = irot->angle * 90; | |
| } | |
| break; | |
| case GF_ISOM_BOX_TYPE_IMIR: | |
| { | |
| GF_ImageMirrorBox *imir = (GF_ImageMirrorBox *)b; | |
| prop->mirror = imir->axis+1; | |
| } | |
| break; | |
| case GF_ISOM_BOX_TYPE_ILCE: | |
| { | |
| GF_FieldInterlaceTypeBox *ilce = (GF_FieldInterlaceTypeBox *)b; | |
| prop->interlace_type = ilce->interlace_type; | |
| } | |
| break; | |
| case GF_ISOM_BOX_TYPE_CLAP: | |
| { | |
| GF_CleanApertureBox *clap = (GF_CleanApertureBox *)b; | |
| prop->clap_hden = clap->cleanApertureHeightD; | |
| prop->clap_hnum = clap->cleanApertureHeightN; | |
| prop->clap_wden = clap->cleanApertureWidthD; | |
| prop->clap_wnum = clap->cleanApertureWidthN; | |
| prop->clap_hoden = clap->horizOffD; | |
| prop->clap_honum = clap->horizOffN; | |
| prop->clap_voden = clap->vertOffD; | |
| prop->clap_vonum = clap->vertOffN; | |
| } | |
| break; | |
| case GF_ISOM_BOX_TYPE_A1LX: | |
| { | |
| GF_AV1LayeredImageIndexingPropertyBox *a1lx = (GF_AV1LayeredImageIndexingPropertyBox *)b; | |
| memcpy(prop->av1_layer_size, a1lx->layer_size, sizeof(prop->av1_layer_size)); | |
| } | |
| break; | |
| case GF_ISOM_BOX_TYPE_A1OP: | |
| { | |
| GF_AV1OperatingPointSelectorPropertyBox *a1op = (GF_AV1OperatingPointSelectorPropertyBox *)b; | |
| prop->av1_op_index = a1op->op_index; | |
| } | |
| case GF_ISOM_BOX_TYPE_HVCC: | |
| case GF_ISOM_BOX_TYPE_AVCC: | |
| case GF_ISOM_BOX_TYPE_AV1C: | |
| case GF_ISOM_BOX_TYPE_VVCC: | |
| case GF_ISOM_BOX_TYPE_J2KH: | |
| if (!prop->config) | |
| prop->config = b; | |
| break; | |
| default: | |
| if (unmapped_props) { | |
| gf_list_add(unmapped_props, b); | |
| } | |
| break; | |
| } | |
| } | |
| } | |
| return GF_OK; | |
| } | |
| #ifndef GPAC_DISABLE_ISOM_WRITE | |
| static s32 meta_find_prop(GF_ItemPropertyContainerBox *boxes, GF_ImageItemProperties *prop) { | |
| u32 i; | |
| u32 count; | |
| count = gf_list_count(boxes->child_boxes); | |
| for (i = 0; i < count; i++) { | |
| GF_Box *b = (GF_Box *)gf_list_get(boxes->child_boxes, i); | |
| switch(b->type) { | |
| case GF_ISOM_BOX_TYPE_ISPE: | |
| { | |
| GF_ImageSpatialExtentsPropertyBox *ispe = (GF_ImageSpatialExtentsPropertyBox *)b; | |
| if ((prop->width || prop->height) && ispe->image_width == prop->width && ispe->image_height == prop->height) { | |
| return i; | |
| } | |
| } | |
| break; | |
| case GF_ISOM_BOX_TYPE_RLOC: | |
| { | |
| GF_RelativeLocationPropertyBox *rloc = (GF_RelativeLocationPropertyBox *)b; | |
| if ((prop->hOffset || prop->vOffset) && rloc->horizontal_offset == prop->hOffset && rloc->vertical_offset == prop->vOffset) { | |
| return i; | |
| } | |
| } | |
| break; | |
| case GF_ISOM_BOX_TYPE_PASP: | |
| { | |
| GF_PixelAspectRatioBox *pasp = (GF_PixelAspectRatioBox *)b; | |
| if ((prop->hSpacing || prop->vSpacing) && pasp->hSpacing == prop->hSpacing && pasp->vSpacing == prop->vSpacing) { | |
| return i; | |
| } | |
| } | |
| break; | |
| case GF_ISOM_BOX_TYPE_IROT: | |
| { | |
| GF_ImageRotationBox *irot = (GF_ImageRotationBox *)b; | |
| if (prop->angle && irot->angle*90 == prop->angle) { | |
| return i; | |
| } | |
| } | |
| break; | |
| case GF_ISOM_BOX_TYPE_IMIR: | |
| { | |
| GF_ImageMirrorBox *imir = (GF_ImageMirrorBox *)b; | |
| if (prop->mirror && imir->axis == prop->mirror-1) { | |
| return i; | |
| } | |
| } | |
| break; | |
| case GF_ISOM_BOX_TYPE_ILCE: | |
| { | |
| GF_FieldInterlaceTypeBox *ilce = (GF_FieldInterlaceTypeBox *)b; | |
| if (ilce->interlace_type == prop->interlace_type) { | |
| return i; | |
| } | |
| } | |
| break; | |
| case GF_ISOM_BOX_TYPE_CLAP: | |
| { | |
| GF_CleanApertureBox *clap = (GF_CleanApertureBox *)b; | |
| if (prop->clap_hden == clap->cleanApertureHeightD && | |
| prop->clap_hnum == clap->cleanApertureHeightN && | |
| prop->clap_wden == clap->cleanApertureWidthD && | |
| prop->clap_wnum == clap->cleanApertureWidthN && | |
| prop->clap_hoden == clap->horizOffD && | |
| prop->clap_honum == clap->horizOffN && | |
| prop->clap_voden == clap->vertOffD && | |
| prop->clap_vonum == clap->vertOffN) { | |
| return i; | |
| } | |
| } | |
| break; | |
| case GF_ISOM_BOX_TYPE_PIXI: | |
| { | |
| GF_PixelInformationPropertyBox *pixi = (GF_PixelInformationPropertyBox *)b; | |
| if (prop->num_channels && pixi->num_channels == prop->num_channels) { | |
| int j; | |
| for (j = 0; j < pixi->num_channels; j++) { | |
| if (pixi->bits_per_channel[j] != prop->bits_per_channel[j]) { | |
| break; | |
| } | |
| } | |
| return i; | |
| } | |
| } | |
| break; | |
| case GF_ISOM_BOX_TYPE_IENC: | |
| { | |
| GF_ItemEncryptionPropertyBox *ienc = (GF_ItemEncryptionPropertyBox *)b; | |
| if (prop->cenc_info | |
| && (prop->cenc_info->skip_byte_block == ienc->skip_byte_block) | |
| && (prop->cenc_info->crypt_byte_block == ienc->crypt_byte_block) | |
| && (prop->cenc_info->key_info_size == ienc->key_info_size) | |
| && prop->cenc_info->key_info && ienc->key_info | |
| && !memcmp(prop->cenc_info->key_info, ienc->key_info, ienc->key_info_size) | |
| ) { | |
| return i; | |
| } | |
| } | |
| break; | |
| case GF_ISOM_BOX_TYPE_A1LX: | |
| { | |
| GF_AV1LayeredImageIndexingPropertyBox *a1lx = (GF_AV1LayeredImageIndexingPropertyBox *)b; | |
| if (memcmp(prop->av1_layer_size, a1lx->layer_size, sizeof(prop->av1_layer_size)) == 0) { | |
| return i; | |
| } | |
| } | |
| break; | |
| case GF_ISOM_BOX_TYPE_A1OP: | |
| { | |
| GF_AV1OperatingPointSelectorPropertyBox *a1op = (GF_AV1OperatingPointSelectorPropertyBox *)b; | |
| if (prop->av1_op_index == a1op->op_index) { | |
| return i; | |
| } | |
| } | |
| break; | |
| default: | |
| if (prop->config) { | |
| if (gf_isom_box_equal(prop->config, b)) { | |
| return i; | |
| } | |
| } | |
| break; | |
| } | |
| } | |
| return -1; | |
| } | |
| static GF_Err meta_add_item_property_association(GF_ItemPropertyAssociationBox *ipma, u32 item_ID, u32 prop_index, Bool essential) { | |
| u32 i, count, insert_pos; | |
| GF_ItemPropertyAssociationEntry *found_entry = NULL; | |
| count = gf_list_count(ipma->entries); | |
| insert_pos = 0; | |
| for (i = 0; i < count; i++) { | |
| found_entry = (GF_ItemPropertyAssociationEntry *)gf_list_get(ipma->entries, i); | |
| if (found_entry->item_id == item_ID) break; | |
| // item ids must appear in increasing order | |
| if (item_ID > found_entry->item_id) ++insert_pos; | |
| found_entry = NULL; | |
| } | |
| if (!found_entry) { | |
| GF_SAFEALLOC(found_entry, GF_ItemPropertyAssociationEntry); | |
| if (!found_entry) return GF_OUT_OF_MEM; | |
| gf_list_insert(ipma->entries, found_entry, insert_pos); | |
| found_entry->item_id = item_ID; | |
| } | |
| found_entry->associations = gf_realloc(found_entry->associations, sizeof(GF_ItemPropertyAssociationSlot) * (found_entry->nb_associations+1)); | |
| if (!found_entry->associations) return GF_OUT_OF_MEM; | |
| found_entry->associations[found_entry->nb_associations].essential = essential; | |
| found_entry->associations[found_entry->nb_associations].index = prop_index; | |
| found_entry->nb_associations++; | |
| return GF_OK; | |
| } | |
| static GF_Err meta_process_image_properties(GF_MetaBox *meta, u32 item_ID, u32 item_type, GF_ImageItemProperties *image_props) | |
| { | |
| GF_ImageItemProperties searchprop; | |
| GF_ItemPropertyAssociationBox *ipma; | |
| GF_ItemPropertyContainerBox *ipco; | |
| s32 prop_index; | |
| GF_Err e; | |
| memset(&searchprop, 0, sizeof(GF_ImageItemProperties)); | |
| if (!meta->item_props) { | |
| meta->item_props = (GF_ItemPropertiesBox *)gf_isom_box_new_parent(&meta->child_boxes, GF_ISOM_BOX_TYPE_IPRP); | |
| if (!meta->item_props) return GF_OUT_OF_MEM; | |
| meta->item_props->property_container = (GF_ItemPropertyContainerBox *)gf_isom_box_new_parent(&meta->item_props->child_boxes, GF_ISOM_BOX_TYPE_IPCO); | |
| if (!meta->item_props->property_container) return GF_OUT_OF_MEM; | |
| ipco = meta->item_props->property_container; | |
| ipma = (GF_ItemPropertyAssociationBox *)gf_isom_box_new_parent(&meta->item_props->child_boxes, GF_ISOM_BOX_TYPE_IPMA); | |
| if (!ipma) return GF_OUT_OF_MEM; | |
| meta->item_props->property_association = ipma; | |
| } else { | |
| ipco = meta->item_props->property_container; | |
| ipma = meta->item_props->property_association; | |
| } | |
| if (!ipco->child_boxes) | |
| ipco->child_boxes = gf_list_new(); | |
| if (strlen(image_props->iccPath) > 0) { | |
| u32 size; | |
| u8 *data; | |
| e = gf_file_load_data(image_props->iccPath, &data, &size); | |
| if (e) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Error opening ICC colour profile file at %s\n", &image_props->iccPath)); | |
| } else { | |
| GF_ColourInformationBox *colr = (GF_ColourInformationBox *)gf_isom_box_new_parent(&ipco->child_boxes, GF_ISOM_BOX_TYPE_COLR); | |
| if (!colr) return GF_OUT_OF_MEM; | |
| GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[iso file] reading ICC colour profile from file %s\n", &image_props->iccPath)); | |
| colr->colour_type = GF_ISOM_SUBTYPE_PROF; | |
| colr->opaque_size = size; | |
| colr->opaque = data; | |
| prop_index = gf_list_count(ipco->child_boxes) - 1; | |
| meta_add_item_property_association(ipma, item_ID, prop_index + 1, GF_FALSE); | |
| } | |
| } | |
| if (image_props->width || image_props->height) { | |
| Bool essential=GF_FALSE; | |
| searchprop.width = image_props->width; | |
| searchprop.height = image_props->height; | |
| prop_index = meta_find_prop(ipco, &searchprop); | |
| if (prop_index < 0) { | |
| GF_ImageSpatialExtentsPropertyBox *ispe = (GF_ImageSpatialExtentsPropertyBox *)gf_isom_box_new_parent(&ipco->child_boxes, GF_ISOM_BOX_TYPE_ISPE); | |
| if (!ispe) return GF_OUT_OF_MEM; | |
| ispe->image_width = image_props->width; | |
| ispe->image_height = image_props->height; | |
| prop_index = gf_list_count(ipco->child_boxes) - 1; | |
| } | |
| if (item_type==GF_ISOM_ITEM_TYPE_UNCI) essential=GF_TRUE; | |
| e = meta_add_item_property_association(ipma, item_ID, prop_index + 1, essential); | |
| if (e) return e; | |
| searchprop.width = 0; | |
| searchprop.height = 0; | |
| } | |
| if (image_props->hOffset || image_props->vOffset) { | |
| searchprop.hOffset = image_props->hOffset; | |
| searchprop.vOffset = image_props->vOffset; | |
| prop_index = meta_find_prop(ipco, &searchprop); | |
| if (prop_index < 0) { | |
| GF_RelativeLocationPropertyBox *rloc = (GF_RelativeLocationPropertyBox *)gf_isom_box_new_parent(&ipco->child_boxes, GF_ISOM_BOX_TYPE_RLOC); | |
| if (!rloc) return GF_OUT_OF_MEM; | |
| rloc->horizontal_offset = image_props->hOffset; | |
| rloc->vertical_offset = image_props->vOffset; | |
| prop_index = gf_list_count(ipco->child_boxes) - 1; | |
| } | |
| e = meta_add_item_property_association(ipma, item_ID, prop_index + 1, GF_TRUE); | |
| if (e) return e; | |
| searchprop.hOffset = 0; | |
| searchprop.vOffset = 0; | |
| } | |
| if (image_props->hSpacing || image_props->vSpacing) { | |
| searchprop.hSpacing = image_props->hSpacing; | |
| searchprop.vSpacing = image_props->vSpacing; | |
| prop_index = meta_find_prop(ipco, &searchprop); | |
| if (prop_index < 0) { | |
| GF_PixelAspectRatioBox *pasp = (GF_PixelAspectRatioBox *)gf_isom_box_new_parent(&ipco->child_boxes, GF_ISOM_BOX_TYPE_PASP); | |
| if (!pasp) return GF_OUT_OF_MEM; | |
| pasp->hSpacing = image_props->hSpacing; | |
| pasp->vSpacing = image_props->vSpacing; | |
| prop_index = gf_list_count(ipco->child_boxes) - 1; | |
| } | |
| e = meta_add_item_property_association(ipma, item_ID, prop_index + 1, GF_FALSE); | |
| if (e) return e; | |
| searchprop.hSpacing = 0; | |
| searchprop.vSpacing = 0; | |
| } | |
| if (image_props->clap_wnum || image_props->clap_wden || image_props->clap_hnum || image_props->clap_hden || image_props->clap_honum || image_props->clap_hoden || image_props->clap_vonum || image_props->clap_voden) { | |
| searchprop.clap_wnum = image_props->clap_wnum; | |
| searchprop.clap_wden = image_props->clap_wden; | |
| searchprop.clap_hnum = image_props->clap_hnum; | |
| searchprop.clap_hden = image_props->clap_hden; | |
| searchprop.clap_honum = image_props->clap_honum; | |
| searchprop.clap_hoden = image_props->clap_hoden; | |
| searchprop.clap_vonum = image_props->clap_vonum; | |
| searchprop.clap_voden = image_props->clap_voden; | |
| prop_index = meta_find_prop(ipco, &searchprop); | |
| if (prop_index < 0) { | |
| GF_CleanApertureBox *clap = (GF_CleanApertureBox *)gf_isom_box_new_parent(&ipco->child_boxes, GF_ISOM_BOX_TYPE_CLAP); | |
| if (!clap) return GF_OUT_OF_MEM; | |
| clap->cleanApertureHeightD = image_props->clap_hden; | |
| clap->cleanApertureHeightN = image_props->clap_hnum; | |
| clap->cleanApertureWidthD = image_props->clap_wden; | |
| clap->cleanApertureWidthN = image_props->clap_wnum; | |
| clap->horizOffD = image_props->clap_hoden; | |
| clap->horizOffN = image_props->clap_honum; | |
| clap->vertOffD = image_props->clap_voden; | |
| clap->vertOffN = image_props->clap_vonum; | |
| prop_index = gf_list_count(ipco->child_boxes) - 1; | |
| } | |
| e = meta_add_item_property_association(ipma, item_ID, prop_index + 1, GF_TRUE); | |
| if (e) return e; | |
| searchprop.clap_wnum = searchprop.clap_wden = searchprop.clap_hnum = searchprop.clap_hden = searchprop.clap_honum = searchprop.clap_hoden = searchprop.clap_vonum = searchprop.clap_voden = 0; | |
| } | |
| if (image_props->angle) { | |
| searchprop.angle = image_props->angle; | |
| prop_index = meta_find_prop(ipco, &searchprop); | |
| if (prop_index < 0) { | |
| GF_ImageRotationBox *irot = (GF_ImageRotationBox *)gf_isom_box_new_parent(&ipco->child_boxes, GF_ISOM_BOX_TYPE_IROT); | |
| if (!irot) return GF_OUT_OF_MEM; | |
| irot->angle = image_props->angle/90; | |
| prop_index = gf_list_count(ipco->child_boxes) - 1; | |
| } | |
| e = meta_add_item_property_association(ipma, item_ID, prop_index + 1, GF_TRUE); | |
| if (e) return e; | |
| searchprop.angle = 0; | |
| } | |
| if (image_props->mirror) { | |
| searchprop.mirror = image_props->mirror; | |
| prop_index = meta_find_prop(ipco, &searchprop); | |
| if (prop_index < 0) { | |
| GF_ImageMirrorBox *imir = (GF_ImageMirrorBox *)gf_isom_box_new_parent(&ipco->child_boxes, GF_ISOM_BOX_TYPE_IMIR); | |
| if (!imir) return GF_OUT_OF_MEM; | |
| imir->axis = image_props->mirror-1; | |
| prop_index = gf_list_count(ipco->child_boxes) - 1; | |
| } | |
| e = meta_add_item_property_association(ipma, item_ID, prop_index + 1, GF_TRUE); | |
| if (e) return e; | |
| searchprop.mirror = 0; | |
| } | |
| if (image_props->config) { | |
| searchprop.config = image_props->config; | |
| prop_index = meta_find_prop(ipco, &searchprop); | |
| if (prop_index < 0) { | |
| #ifndef GPAC_DISABLE_ISOM_WRITE | |
| gf_list_add(ipco->child_boxes, gf_isom_clone_config_box(image_props->config)); | |
| prop_index = gf_list_count(ipco->child_boxes) - 1; | |
| #else | |
| return GF_NOT_SUPPORTED; | |
| #endif | |
| } | |
| e = meta_add_item_property_association(ipma, item_ID, prop_index + 1, GF_TRUE); | |
| if (e) return e; | |
| searchprop.config = NULL; | |
| } | |
| if (image_props->config_ba && image_props->config_ba_size) { | |
| GF_Box *b; | |
| GF_SAFEALLOC(b, GF_Box); | |
| b->child_boxes = gf_list_new(); | |
| b->size = image_props->config_ba_size; | |
| GF_BitStream *bs = gf_bs_new(image_props->config_ba, image_props->config_ba_size, GF_BITSTREAM_READ); | |
| e = gf_isom_box_array_read(b, bs); | |
| gf_bs_del(bs); | |
| if (e) { | |
| gf_isom_box_array_del(b->child_boxes); | |
| gf_free(b); | |
| return e; | |
| } | |
| while (gf_list_count(b->child_boxes)) { | |
| GF_Box *a = gf_list_pop_front(b->child_boxes); | |
| u32 type = a->type; | |
| if (a->type==GF_ISOM_BOX_TYPE_UNKNOWN) | |
| type = ((GF_UnknownBox*)a)->original_4cc; | |
| GF_Box *exists = gf_isom_box_find_child(ipco->child_boxes, type); | |
| if (exists) { | |
| if (gf_isom_box_equal(a, exists)) { | |
| prop_index = gf_list_find(ipco->child_boxes, exists); | |
| gf_isom_box_del_parent(&b->child_boxes, a); | |
| } else { | |
| exists = NULL; | |
| } | |
| } | |
| if (!exists) { | |
| gf_list_add(ipco->child_boxes, a); | |
| prop_index = gf_list_count(ipco->child_boxes) - 1; | |
| } | |
| //mark all as essential | |
| e = meta_add_item_property_association(ipma, item_ID, prop_index + 1, GF_TRUE); | |
| if (e) return e; | |
| } | |
| gf_list_del(b->child_boxes); | |
| gf_free(b); | |
| } | |
| if (image_props->alpha) { | |
| searchprop.alpha = image_props->alpha; | |
| prop_index = meta_find_prop(ipco, &searchprop); | |
| if (prop_index < 0) { | |
| GF_AuxiliaryTypePropertyBox *auxC = (GF_AuxiliaryTypePropertyBox *)gf_isom_box_new_parent(&ipco->child_boxes, GF_ISOM_BOX_TYPE_AUXC); | |
| if (!auxC) return GF_OUT_OF_MEM; | |
| //always use mpegB code points (2nd edition of HEIF recommends it) | |
| if (gf_opts_get_bool("core", "heif-hevc-urn")) { | |
| u32 cfg_type = image_props->config ? ((GF_Box *) image_props->config)->type : 0; | |
| switch (cfg_type) { | |
| case GF_ISOM_BOX_TYPE_HVCC: | |
| case GF_ISOM_BOX_TYPE_LHVC: | |
| auxC->aux_urn = gf_strdup("urn:mpeg:hevc:2015:auxid:1"); | |
| break; | |
| default: | |
| auxC->aux_urn = gf_strdup("urn:mpeg:mpegB:cicp:systems:auxiliary:alpha"); | |
| break; | |
| } | |
| } else { | |
| auxC->aux_urn = gf_strdup("urn:mpeg:mpegB:cicp:systems:auxiliary:alpha"); | |
| } | |
| prop_index = gf_list_count(ipco->child_boxes) - 1; | |
| } | |
| e = meta_add_item_property_association(ipma, item_ID, prop_index + 1, GF_TRUE); | |
| if (e) return e; | |
| searchprop.alpha = GF_FALSE; | |
| if (image_props->num_channels) image_props->num_channels = 1; | |
| } | |
| if (image_props->depth) { | |
| searchprop.depth = image_props->depth; | |
| prop_index = meta_find_prop(ipco, &searchprop); | |
| if (prop_index < 0) { | |
| GF_AuxiliaryTypePropertyBox *auxC = (GF_AuxiliaryTypePropertyBox *)gf_isom_box_new_parent(&ipco->child_boxes, GF_ISOM_BOX_TYPE_AUXC); | |
| if (!auxC) return GF_OUT_OF_MEM; | |
| //always use mpegB code points (2nd edition of HEIF recommends it) | |
| if (gf_opts_get_bool("core", "heif-hevc-urn")) { | |
| u32 cfg_type = image_props->config ? ((GF_Box *) image_props->config)->type : 0; | |
| switch (cfg_type) { | |
| case GF_ISOM_BOX_TYPE_HVCC: | |
| case GF_ISOM_BOX_TYPE_LHVC: | |
| auxC->aux_urn = gf_strdup("urn:mpeg:hevc:2015:auxid:2"); | |
| break; | |
| default: | |
| auxC->aux_urn = gf_strdup("urn:mpeg:mpegB:cicp:systems:auxiliary:depth"); | |
| break; | |
| } | |
| } else { | |
| auxC->aux_urn = gf_strdup("urn:mpeg:mpegB:cicp:systems:auxiliary:depth"); | |
| } | |
| prop_index = gf_list_count(ipco->child_boxes) - 1; | |
| } | |
| e = meta_add_item_property_association(ipma, item_ID, prop_index + 1, GF_TRUE); | |
| if (e) return e; | |
| searchprop.depth = GF_FALSE; | |
| if (image_props->num_channels) image_props->num_channels = 1; | |
| } | |
| if (image_props->num_channels) { | |
| u32 k; | |
| searchprop.num_channels = image_props->num_channels; | |
| for (k=0; k<3; k++) { | |
| searchprop.bits_per_channel[k] = image_props->bits_per_channel[k]; | |
| } | |
| prop_index = meta_find_prop(ipco, &searchprop); | |
| if (prop_index < 0) { | |
| GF_PixelInformationPropertyBox *pixi = (GF_PixelInformationPropertyBox *)gf_isom_box_new_parent(&ipco->child_boxes, GF_ISOM_BOX_TYPE_PIXI); | |
| if (!pixi) return GF_OUT_OF_MEM; | |
| pixi->num_channels = image_props->num_channels; | |
| pixi->bits_per_channel = gf_malloc(pixi->num_channels); | |
| if (!pixi->bits_per_channel) return GF_OUT_OF_MEM; | |
| for (k=0; k<pixi->num_channels; k++) { | |
| pixi->bits_per_channel[k] = image_props->bits_per_channel[k]; | |
| } | |
| prop_index = gf_list_count(ipco->child_boxes) - 1; | |
| } | |
| e = meta_add_item_property_association(ipma, item_ID, prop_index + 1, GF_TRUE); | |
| if (e) return e; | |
| searchprop.num_channels = 0; | |
| } | |
| if ((image_props->av1_layer_size[0] != image_props->av1_layer_size[1]) || | |
| (image_props->av1_layer_size[1] != image_props->av1_layer_size[2]) || | |
| (image_props->av1_layer_size[0] != image_props->av1_layer_size[2])) { | |
| memcpy(searchprop.av1_layer_size, image_props->av1_layer_size, sizeof(searchprop.av1_layer_size)); | |
| prop_index = meta_find_prop(ipco, &searchprop); | |
| if (prop_index < 0) { | |
| GF_AV1LayeredImageIndexingPropertyBox *a1lx = (GF_AV1LayeredImageIndexingPropertyBox *)gf_isom_box_new_parent(&ipco->child_boxes, GF_ISOM_BOX_TYPE_A1LX); | |
| if (!a1lx) return GF_OUT_OF_MEM; | |
| a1lx->large_size = 1; | |
| memcpy(a1lx->layer_size, image_props->av1_layer_size, sizeof(a1lx->layer_size)); | |
| prop_index = gf_list_count(ipco->child_boxes) - 1; | |
| } | |
| e = meta_add_item_property_association(ipma, item_ID, prop_index + 1, GF_FALSE); | |
| if (e) return e; | |
| memset(searchprop.av1_layer_size, 0, sizeof(searchprop.av1_layer_size)); | |
| } | |
| if (image_props->av1_op_index) { | |
| searchprop.av1_op_index = image_props->av1_op_index; | |
| prop_index = meta_find_prop(ipco, &searchprop); | |
| if (prop_index < 0) { | |
| GF_AV1OperatingPointSelectorPropertyBox *a1op = (GF_AV1OperatingPointSelectorPropertyBox *)gf_isom_box_new_parent(&ipco->child_boxes, GF_ISOM_BOX_TYPE_A1OP); | |
| if (!a1op) return GF_OUT_OF_MEM; | |
| a1op->op_index = image_props->av1_op_index; | |
| prop_index = gf_list_count(ipco->child_boxes) - 1; | |
| } | |
| e = meta_add_item_property_association(ipma, item_ID, prop_index + 1, GF_TRUE); | |
| if (e) return e; | |
| searchprop.av1_op_index = 0; | |
| } | |
| if (image_props->aux_urn) { | |
| GF_AuxiliaryTypePropertyBox *auxC = (GF_AuxiliaryTypePropertyBox *)gf_isom_box_new_parent(&ipco->child_boxes, GF_ISOM_BOX_TYPE_AUXC); | |
| if (!auxC) return GF_OUT_OF_MEM; | |
| auxC->aux_urn = gf_strdup(image_props->aux_urn); | |
| prop_index = gf_list_count(ipco->child_boxes) - 1; | |
| if (image_props->aux_data && image_props->aux_data_len) { | |
| auxC->data_size = image_props->aux_data_len; | |
| auxC->data = gf_malloc(sizeof(u8) * image_props->aux_data_len); | |
| if (!auxC->data) return GF_OUT_OF_MEM; | |
| memcpy(auxC->data, image_props->aux_data, sizeof(u8) * image_props->aux_data_len); | |
| } | |
| e = meta_add_item_property_association(ipma, item_ID, prop_index + 1, GF_TRUE); | |
| if (e) return e; | |
| } | |
| if (image_props->interlace_type) { | |
| searchprop.interlace_type = image_props->interlace_type; | |
| prop_index = meta_find_prop(ipco, &searchprop); | |
| if (prop_index < 0) { | |
| GF_FieldInterlaceTypeBox *ilce = (GF_FieldInterlaceTypeBox *)gf_isom_box_new_parent(&ipco->child_boxes, GF_ISOM_BOX_TYPE_ILCE); | |
| if (!ilce) return GF_OUT_OF_MEM; | |
| ilce->interlace_type = image_props->interlace_type; | |
| prop_index = gf_list_count(ipco->child_boxes) - 1; | |
| } | |
| e = meta_add_item_property_association(ipma, item_ID, prop_index + 1, GF_TRUE); | |
| if (e) return e; | |
| searchprop.interlace_type = 0; | |
| } | |
| if (image_props->cenc_info) { | |
| GF_ItemEncryptionPropertyBox *ienc = NULL; | |
| if (!gf_cenc_validate_key_info(image_props->cenc_info->key_info, image_props->cenc_info->key_info_size)) | |
| return GF_BAD_PARAM; | |
| searchprop.cenc_info = image_props->cenc_info; | |
| prop_index = meta_find_prop(ipco, &searchprop); | |
| if (prop_index < 0) { | |
| ienc = (GF_ItemEncryptionPropertyBox *)gf_isom_box_new_parent(&ipco->child_boxes, GF_ISOM_BOX_TYPE_IENC); | |
| if (!ienc) return GF_OUT_OF_MEM; | |
| ienc->skip_byte_block = image_props->cenc_info->skip_byte_block; | |
| ienc->crypt_byte_block = image_props->cenc_info->crypt_byte_block; | |
| ienc->key_info_size = image_props->cenc_info->key_info_size; | |
| ienc->key_info = gf_malloc(sizeof(u8) * image_props->cenc_info->key_info_size); | |
| if (!ienc->key_info) { | |
| gf_free(ienc); | |
| return GF_OUT_OF_MEM; | |
| } | |
| memcpy(ienc->key_info, image_props->cenc_info->key_info, image_props->cenc_info->key_info_size); | |
| prop_index = gf_list_count(ipco->child_boxes) - 1; | |
| } | |
| //add property | |
| e = meta_add_item_property_association(ipma, item_ID, prop_index + 1, GF_TRUE); | |
| if (e) return e; | |
| searchprop.cenc_info = NULL; | |
| } | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_isom_meta_get_next_item_id(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 *item_id) | |
| { | |
| GF_MetaBox *meta; | |
| u32 lastItemID = 0; | |
| if (!file || !item_id) return GF_BAD_PARAM; | |
| meta = gf_isom_get_meta(file, root_meta, track_num); | |
| if (!meta) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("Missing meta box")); | |
| return GF_BAD_PARAM; | |
| } | |
| 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; | |
| } | |
| *item_id = lastItemID+1; | |
| } | |
| else { | |
| *item_id = lastItemID + 1; | |
| } | |
| if (meta->groups_list) { | |
| u32 i; | |
| u32 groups_count = gf_list_count(meta->groups_list->child_boxes); | |
| for (i = 0; i < groups_count; i++) { | |
| GF_EntityToGroupTypeBox *g = (GF_EntityToGroupTypeBox *)gf_list_get(meta->groups_list->child_boxes, i); | |
| if (g->group_id > lastItemID) lastItemID = g->group_id; | |
| } | |
| *item_id = lastItemID+1; | |
| } | |
| 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 *io_item_id, u32 item_type, const char *mime_type, const char *content_encoding, | |
| GF_ImageItemProperties *image_props, | |
| const char *URL, const char *URN, | |
| char *data, u32 data_len, GF_List *item_extent_refs, u32 tk_id, u32 sample_num) | |
| { | |
| u32 i; | |
| GF_Err e; | |
| GF_ItemLocationEntry *location_entry; | |
| GF_ItemInfoEntryBox *infe; | |
| GF_MetaBox *meta; | |
| u32 lastItemID = 0; | |
| u32 item_id = io_item_id ? *io_item_id : 0; | |
| e = gf_isom_can_access_movie(file, GF_ISOM_OPEN_WRITE); | |
| if (e) return e; | |
| meta = gf_isom_get_meta(file, root_meta, track_num); | |
| if (!meta) { | |
| if (track_num) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("Trying to add item, but missing meta box in track %u\n", track_num)); | |
| } else { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("Trying to add item, but missing meta box in %s\n", root_meta ? "file" : "movie box")); | |
| } | |
| return GF_BAD_PARAM; | |
| } | |
| e = FlushCaptureMode(file); | |
| if (e) return e; | |
| /*check file exists */ | |
| if (resource_path) { | |
| FILE *src = gf_fopen(resource_path, "rb"); | |
| if (!src) return GF_URL_ERROR; | |
| gf_fclose(src); | |
| } | |
| if (meta->item_infos) { | |
| u32 item_count = gf_list_count(meta->item_infos->item_infos); | |
| for (i = 0; i < item_count; i++) { | |
| GF_ItemInfoEntryBox *iinf_e= (GF_ItemInfoEntryBox *)gf_list_get(meta->item_infos->item_infos, i); | |
| if (iinf_e->item_ID > lastItemID) lastItemID = iinf_e->item_ID; | |
| if (item_id == iinf_e->item_ID) { | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[IsoMedia] Item with ID %d already exists, ignoring ID\n", item_id)); | |
| item_id = 0; | |
| } | |
| } | |
| } else { | |
| meta->item_infos = (GF_ItemInfoBox *)gf_isom_box_new_parent(&meta->child_boxes, GF_ISOM_BOX_TYPE_IINF); | |
| } | |
| /*Creation an ItemLocation Box if it does not exist*/ | |
| if (!meta->item_locations) | |
| meta->item_locations = (GF_ItemLocationBox *)gf_isom_box_new_parent(&meta->child_boxes, GF_ISOM_BOX_TYPE_ILOC); | |
| infe = (GF_ItemInfoEntryBox *)gf_isom_box_new_parent(&meta->item_infos->child_boxes, GF_ISOM_BOX_TYPE_INFE); | |
| if (!infe) return GF_OUT_OF_MEM; | |
| if (item_id) { | |
| infe->item_ID = item_id; | |
| } else { | |
| infe->item_ID = ++lastItemID; | |
| } | |
| if (io_item_id) *io_item_id = infe->item_ID; | |
| if (tk_id && sample_num) { | |
| data_len = gf_isom_get_sample_size(file, tk_id, sample_num); | |
| if (item_name) | |
| infe->item_name = gf_strdup(item_name); | |
| } | |
| else if (!tk_id && sample_num) { | |
| data_len = 0; | |
| if (item_name) | |
| infe->item_name = gf_strdup(item_name); | |
| } | |
| /*get relative name*/ | |
| else if (item_name) { | |
| infe->item_name = gf_strdup(item_name); | |
| file->no_inplace_rewrite = GF_TRUE; | |
| } else if (resource_path) { | |
| //don't write an item name if not set | |
| //infe->item_name = gf_strdup(gf_file_basename( resource_path )); | |
| file->no_inplace_rewrite = GF_TRUE; | |
| } | |
| infe->item_type = item_type; | |
| 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_parent(&meta->item_infos->child_boxes, (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 *)gf_isom_box_new(GF_ISOM_BOX_TYPE_MDAT); | |
| if (!file->mdat) return GF_OUT_OF_MEM; | |
| gf_list_add(file->TopBoxes, file->mdat); | |
| } | |
| gf_list_add(meta->item_locations->location_entries, location_entry); | |
| location_entry->item_ID = infe->item_ID; | |
| e = gf_list_add(meta->item_infos->item_infos, infe); | |
| if (e) return e; | |
| if (image_props) { | |
| if (image_props->hidden) { | |
| infe->flags = 0x1; | |
| } | |
| e = meta_process_image_properties(meta, infe->item_ID, item_type, image_props); | |
| if (e) return e; | |
| if (image_props->cenc_info) { | |
| GF_ProtectionSchemeInfoBox *sinf; | |
| u32 cenc_item_id = infe->item_ID + 1; | |
| //create a new auxi item | |
| e = gf_isom_add_meta_item_memory(file, root_meta, track_num, NULL, &cenc_item_id, GF_ISOM_ITEM_TYPE_AUXI, NULL, NULL, NULL, (u8 *) image_props->cenc_info->sai_data, image_props->cenc_info->sai_data_size, NULL); | |
| if (e) return e; | |
| //add item reference | |
| e = gf_isom_meta_add_item_ref(file, root_meta, track_num, infe->item_ID, cenc_item_id, GF_ISOM_REF_AUXR, NULL); | |
| if (e) return e; | |
| //multikey, we MUST have a 'iaux' prop with aux_info_type_param=1 associated | |
| if (image_props->cenc_info->key_info[0]) { | |
| GF_ItemPropertyContainerBox *ipco = meta->item_props->property_container; | |
| GF_ItemPropertyAssociationBox *ipma = meta->item_props->property_association; | |
| u32 k, pcount = gf_list_count(ipco->child_boxes); | |
| s32 prop_index = -1; | |
| for (k=0; k<pcount; k++) { | |
| GF_AuxiliaryInfoPropertyBox *b = (GF_AuxiliaryInfoPropertyBox*)gf_list_get(ipco->child_boxes, k); | |
| if (b->type != GF_ISOM_BOX_TYPE_IAUX) continue; | |
| switch (b->aux_info_type) { | |
| case GF_ISOM_CENC_SCHEME: | |
| case GF_ISOM_CENS_SCHEME: | |
| case GF_ISOM_CBC_SCHEME: | |
| case GF_ISOM_CBCS_SCHEME: | |
| case 0: | |
| break; | |
| default: | |
| continue; | |
| } | |
| if (b->aux_info_parameter!=1) continue; | |
| prop_index = k; | |
| break; | |
| } | |
| if (prop_index < 0) { | |
| GF_AuxiliaryInfoPropertyBox *iaux = (GF_AuxiliaryInfoPropertyBox *)gf_isom_box_new_parent(&ipco->child_boxes, GF_ISOM_BOX_TYPE_IAUX); | |
| if (!iaux) return GF_OUT_OF_MEM; | |
| iaux->aux_info_parameter = 1; | |
| prop_index = gf_list_count(ipco->child_boxes) - 1; | |
| } | |
| //add property | |
| e = meta_add_item_property_association(ipma, cenc_item_id, prop_index + 1, GF_TRUE); | |
| if (e) return e; | |
| } | |
| //look for scheme in ipro | |
| if (!meta->protections) { | |
| meta->protections = (GF_ItemProtectionBox *) gf_isom_box_new_parent(&meta->child_boxes, GF_ISOM_BOX_TYPE_IPRO); | |
| if (!meta->protections) return GF_OUT_OF_MEM; | |
| } | |
| sinf = NULL; | |
| for (i=0; i<gf_list_count(meta->protections->protection_information); i++) { | |
| sinf = gf_list_get(meta->protections->protection_information, i); | |
| if (sinf->scheme_type && (sinf->scheme_type->scheme_type==image_props->cenc_info->scheme_type) | |
| && (sinf->scheme_type->scheme_version==image_props->cenc_info->scheme_version)) | |
| break; | |
| sinf = NULL; | |
| } | |
| if (!sinf) { | |
| sinf = (GF_ProtectionSchemeInfoBox *)gf_isom_box_new_parent(&meta->protections->child_boxes, GF_ISOM_BOX_TYPE_SINF); | |
| if (!sinf) return GF_OUT_OF_MEM; | |
| gf_list_add(meta->protections->protection_information, sinf); | |
| sinf->scheme_type = (GF_SchemeTypeBox *)gf_isom_box_new_parent(&sinf->child_boxes, GF_ISOM_BOX_TYPE_SCHM); | |
| if (!sinf->scheme_type) return GF_OUT_OF_MEM; | |
| sinf->scheme_type->scheme_type = image_props->cenc_info->scheme_type; | |
| sinf->scheme_type->scheme_version = image_props->cenc_info->scheme_version; | |
| } | |
| infe->item_protection_index = 1 + gf_list_find(meta->protections->protection_information, sinf); | |
| } | |
| } | |
| /*0: the current file*/ | |
| location_entry->data_reference_index = 0; | |
| if (self_reference) { | |
| GF_ItemExtentEntry *entry; | |
| GF_SAFEALLOC(entry, GF_ItemExtentEntry); | |
| if (!entry) return GF_OUT_OF_MEM; | |
| 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_parent(&meta->child_boxes, GF_ISOM_BOX_TYPE_DINF); | |
| if (!meta->file_locations) return GF_OUT_OF_MEM; | |
| } | |
| if (!meta->file_locations->dref) { | |
| meta->file_locations->dref = (GF_DataReferenceBox *) gf_isom_box_new_parent(&meta->file_locations->child_boxes, GF_ISOM_BOX_TYPE_DREF); | |
| if (!meta->file_locations->dref) return GF_OUT_OF_MEM; | |
| } | |
| e = Media_FindDataRef(meta->file_locations->dref, (char *) URL, (char *) URN, &dataRefIndex); | |
| if (e) return e; | |
| if (!dataRefIndex) { | |
| e = Media_CreateDataRef(file, meta->file_locations->dref, (char *) URL, (char *) URN, &dataRefIndex); | |
| if (e) return e; | |
| } | |
| location_entry->data_reference_index = dataRefIndex; | |
| } | |
| if (item_extent_refs && gf_list_count(item_extent_refs)) { | |
| u32 refs_count; | |
| location_entry->construction_method = 2; | |
| meta->item_locations->index_size = 4; | |
| refs_count = gf_list_count(item_extent_refs); | |
| for (i = 0; i < refs_count; i++) { | |
| u32 *item_index; | |
| GF_ItemExtentEntry *entry; | |
| GF_SAFEALLOC(entry, GF_ItemExtentEntry); | |
| if (!entry) return GF_OUT_OF_MEM; | |
| gf_list_add(location_entry->extent_entries, entry); | |
| item_index = (u32 *)gf_list_get(item_extent_refs, i); | |
| gf_isom_meta_add_item_ref(file, root_meta, track_num, infe->item_ID, *item_index, GF_ISOM_REF_ILOC, &(entry->extent_index)); | |
| } | |
| } | |
| else if (tk_id && sample_num) { | |
| if ((file->openMode == GF_ISOM_OPEN_WRITE) || (file->openMode == GF_ISOM_OPEN_EDIT)) { | |
| GF_ItemExtentEntry *entry; | |
| GF_SAFEALLOC(entry, GF_ItemExtentEntry); | |
| if (!entry) return GF_OUT_OF_MEM; | |
| entry->extent_length = data_len; | |
| location_entry->base_offset = 0; | |
| GF_ISOSample *samp = gf_isom_get_sample_info(file, tk_id, sample_num, NULL, &entry->extent_offset); | |
| if (samp) gf_isom_sample_del(&samp); | |
| gf_list_add(location_entry->extent_entries, entry); | |
| if (data_len>0xFFFFFFFF) meta->item_locations->length_size = 8; | |
| else if (! meta->item_locations->base_offset_size) meta->item_locations->length_size = 4; | |
| //for in-place rewrite + add-image | |
| if (file->openMode == GF_ISOM_OPEN_EDIT) { | |
| location_entry->base_offset = 0; | |
| infe->tk_id = tk_id; | |
| infe->sample_num = sample_num; | |
| infe->data_len = data_len; | |
| } | |
| } else { | |
| infe->tk_id = tk_id; | |
| infe->sample_num = sample_num; | |
| infe->data_len = data_len; | |
| file->no_inplace_rewrite = GF_TRUE; | |
| } | |
| meta->use_item_sample_sharing = 1; | |
| } | |
| else if (!tk_id && sample_num) { | |
| GF_ItemExtentEntry *entry; | |
| GF_SAFEALLOC(entry, GF_ItemExtentEntry); | |
| if (!entry) return GF_OUT_OF_MEM; | |
| entry->extent_length = data_len; | |
| gf_list_add(location_entry->extent_entries, entry); | |
| infe->ref_it_id = sample_num; | |
| infe->data_len = data_len; | |
| meta->use_item_item_sharing = 1; | |
| } | |
| else if (data || resource_path){ | |
| /*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); | |
| if (!entry) return GF_OUT_OF_MEM; | |
| 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; | |
| entry->extent_length = gf_fsize(src); | |
| remain = entry->extent_length; | |
| while (remain) { | |
| u32 size_cache = (remain > 4096) ? 4096 : (u32)remain; | |
| size_t read = gf_fread(cache_data, 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); | |
| if (!infe->full_path) return GF_OUT_OF_MEM; | |
| memcpy(infe->full_path, data, sizeof(char) * data_len); | |
| infe->data_len = data_len; | |
| } | |
| else if (resource_path) { | |
| infe->full_path = gf_strdup(resource_path); | |
| infe->data_len = 0; | |
| } | |
| } | |
| } | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_isom_add_meta_item(GF_ISOFile *file, Bool root_meta, u32 track_num, Bool self_reference, char *resource_path, const char *item_name, u32 item_id, u32 item_type, | |
| const char *mime_type, const char *content_encoding, const char *URL, const char *URN, | |
| GF_ImageItemProperties *image_props) | |
| { | |
| return gf_isom_add_meta_item_extended(file, root_meta, track_num, self_reference, resource_path, item_name, &item_id, item_type, mime_type, content_encoding, image_props, URL, URN, NULL, 0, NULL, 0, 0); | |
| } | |
| GF_EXPORT | |
| GF_Err gf_isom_add_meta_item2(GF_ISOFile *file, Bool root_meta, u32 track_num, Bool self_reference, char *resource_path, const char *item_name, u32 *io_item_id, u32 item_type, | |
| const char *mime_type, const char *content_encoding, const char *URL, const char *URN, | |
| GF_ImageItemProperties *image_props) | |
| { | |
| return gf_isom_add_meta_item_extended(file, root_meta, track_num, self_reference, resource_path, item_name, io_item_id, item_type, mime_type, content_encoding, image_props, URL, URN, NULL, 0, NULL, 0, 0); | |
| } | |
| GF_EXPORT | |
| GF_Err gf_isom_add_meta_item_memory(GF_ISOFile *file, Bool root_meta, u32 track_num, const char *item_name, u32 *item_id, u32 item_type, const char *mime_type, const char *content_encoding, GF_ImageItemProperties *image_props, char *data, u32 data_len, GF_List *item_extent_refs) | |
| { | |
| return gf_isom_add_meta_item_extended(file, root_meta, track_num, GF_FALSE, NULL, item_name, item_id, item_type, mime_type, content_encoding, image_props, NULL, NULL, data, data_len, item_extent_refs, 0, 0); | |
| } | |
| GF_EXPORT | |
| GF_Err gf_isom_add_meta_item_sample_ref(GF_ISOFile *file, Bool root_meta, u32 track_num, const char *item_name, u32 *item_id, u32 item_type, const char *mime_type, const char *content_encoding, GF_ImageItemProperties *image_props, u32 tk_id, u32 sample_num) | |
| { | |
| return gf_isom_add_meta_item_extended(file, root_meta, track_num, GF_FALSE, NULL, item_name, item_id, item_type, mime_type, content_encoding, image_props, NULL, NULL, NULL, 0, NULL, tk_id, sample_num); | |
| } | |
| static Bool meta_use_item_prop_idx(GF_MetaBox *meta, u32 prop_idx) | |
| { | |
| u32 k, i, count = gf_list_count(meta->item_props->property_association->entries); | |
| for (i=0; i<count; i++) { | |
| GF_ItemPropertyAssociationEntry *pa_ent = gf_list_get(meta->item_props->property_association->entries, i); | |
| for (k=0; k<pa_ent->nb_associations; k++) { | |
| if (pa_ent->associations[k].index == prop_idx) return GF_TRUE; | |
| } | |
| } | |
| return GF_FALSE; | |
| } | |
| static void meta_shift_item_prop_idx(GF_MetaBox *meta, u32 prop_idx) | |
| { | |
| u32 k, i, count = gf_list_count(meta->item_props->property_association->entries); | |
| for (i=0; i<count; i++) { | |
| GF_ItemPropertyAssociationEntry *pa_ent = gf_list_get(meta->item_props->property_association->entries, i); | |
| for (k=0; k<pa_ent->nb_associations; k++) { | |
| if (pa_ent->associations[k].index > prop_idx) { | |
| pa_ent->associations[k].index--; | |
| } | |
| } | |
| } | |
| } | |
| static void meta_cleanup_associations(GF_MetaBox *meta) | |
| { | |
| u32 i, count = gf_list_count(meta->item_props->property_container->child_boxes); | |
| for (i=0; i<count; i++) { | |
| u32 prop_idx = i+1; | |
| if (meta_use_item_prop_idx(meta, prop_idx)) continue; | |
| meta_shift_item_prop_idx(meta, prop_idx); | |
| GF_Box *prop = gf_list_get(meta->item_props->property_container->child_boxes, i); | |
| gf_isom_box_del_parent(&meta->item_props->property_container->child_boxes, prop); | |
| i--; | |
| count--; | |
| } | |
| } | |
| static void meta_cleanup_item_association(GF_MetaBox *meta, GF_ItemPropertyAssociationEntry *pa_ent, const char *keep_props) | |
| { | |
| u32 i; | |
| GF_ItemPropertyContainerBox *ipco = meta->item_props->property_container; | |
| for (i=0; i<pa_ent->nb_associations; i++) { | |
| GF_Box *prop; | |
| Bool do_rem = GF_TRUE; | |
| if (!pa_ent->associations[i].index) continue; | |
| prop = gf_list_get(ipco->child_boxes, pa_ent->associations[i].index-1); | |
| if (prop && keep_props) { | |
| const char *p4cc = gf_4cc_to_str(prop->type); | |
| if (keep_props && strstr(keep_props, p4cc)) | |
| do_rem = GF_FALSE; | |
| } | |
| if (do_rem) { | |
| if (i+1<pa_ent->nb_associations) { | |
| memmove(&pa_ent->associations[i], &pa_ent->associations[i+1], sizeof(GF_ItemPropertyAssociationSlot)*(pa_ent->nb_associations-i-1)); | |
| } | |
| pa_ent->nb_associations--; | |
| i--; | |
| } | |
| } | |
| } | |
| GF_EXPORT | |
| GF_Err gf_isom_remove_meta_item(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 item_id, Bool keep_refs, const char *keep_props) | |
| { | |
| GF_ItemInfoEntryBox *iinf; | |
| u32 i, count; | |
| GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); | |
| u32 item_num; | |
| if (!meta || !meta->item_infos || !meta->item_locations) return GF_BAD_PARAM; | |
| item_num = gf_isom_get_meta_item_by_id(file, root_meta, track_num, item_id); | |
| if (!item_num) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("Error: No item with ID %d in file, cannot remove\n")); | |
| return GF_NOT_FOUND; | |
| } | |
| iinf = (GF_ItemInfoEntryBox *)gf_list_get(meta->item_infos->item_infos, item_num-1); | |
| gf_list_rem(meta->item_infos->item_infos, item_num-1); | |
| count = gf_list_count(meta->item_locations->location_entries); | |
| for (i=0; i<count; i++) { | |
| GF_ItemLocationEntry *iloc = (GF_ItemLocationEntry *)gf_list_get(meta->item_locations->location_entries, i); | |
| if (iloc->item_ID==iinf->item_ID) { | |
| /*TODO: remove data ref...*/ | |
| if (iloc->data_reference_index) { } | |
| gf_list_rem(meta->item_locations->location_entries, i); | |
| iloc_entry_del(iloc); | |
| break; | |
| } | |
| } | |
| if (meta->item_props && meta->item_props->property_association) { | |
| Bool cleanup_associations = GF_FALSE; | |
| GF_ItemPropertyAssociationBox *ipma = meta->item_props->property_association; | |
| count = gf_list_count(ipma->entries); | |
| for (i=0; i<count; i++) { | |
| GF_ItemPropertyAssociationEntry *pa_ent = gf_list_get(ipma->entries, i); | |
| if (pa_ent->item_id == iinf->item_ID) { | |
| meta_cleanup_item_association(meta, pa_ent, keep_props); | |
| if (!pa_ent->nb_associations) { | |
| gf_free(pa_ent->associations); | |
| gf_free(pa_ent); | |
| gf_list_rem(ipma->entries, i); | |
| } | |
| cleanup_associations = GF_TRUE; | |
| break; | |
| } | |
| } | |
| if (cleanup_associations) { | |
| meta_cleanup_associations(meta); | |
| } | |
| } | |
| if (!keep_refs && meta->item_refs) { | |
| count = gf_list_count(meta->item_refs->references); | |
| for (i=0; i<count; i++) { | |
| GF_ItemReferenceTypeBox *iref = gf_list_get(meta->item_refs->references, i); | |
| Bool do_delete = GF_FALSE; | |
| if (iref->from_item_id == item_id) { | |
| do_delete = GF_FALSE; | |
| } else { | |
| u32 k; | |
| for (k=0; k<iref->reference_count; k++) { | |
| if (iref->to_item_IDs[k] != item_id) continue; | |
| if (k+1<iref->reference_count) | |
| memmove(&iref->to_item_IDs[k], &iref->to_item_IDs[k+1], sizeof(u32)*(iref->reference_count-k-1)); | |
| iref->reference_count--; | |
| k--; | |
| } | |
| if (!iref->reference_count) { | |
| do_delete = GF_TRUE; | |
| } | |
| } | |
| if (do_delete) { | |
| gf_isom_box_del_parent(&meta->item_refs->child_boxes, (GF_Box *) iref); | |
| gf_list_rem(meta->item_refs->references, i); | |
| i--; | |
| count--; | |
| } | |
| } | |
| } | |
| //rewrite item sharing ids | |
| if (meta->use_item_item_sharing) { | |
| u32 new_ref_id = 0; | |
| count = gf_list_count(meta->item_infos->item_infos); | |
| for (i=0; i<count; i++) { | |
| GF_ItemInfoEntryBox *iinf2 = (GF_ItemInfoEntryBox *) gf_list_get(meta->item_infos->item_infos, i); | |
| if (iinf2->ref_it_id != iinf->item_ID) continue; | |
| if (new_ref_id) { | |
| iinf2->ref_it_id = new_ref_id; | |
| } else { | |
| new_ref_id = iinf2->item_ID; | |
| iinf2->ref_it_id = 0; | |
| if (iinf->tk_id) { | |
| iinf2->tk_id = iinf->tk_id; | |
| iinf2->sample_num = iinf->sample_num; | |
| iinf2->data_len = iinf->data_len; | |
| } | |
| } | |
| } | |
| } | |
| gf_isom_box_del_parent(&meta->item_infos->child_boxes, (GF_Box *)iinf); | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_isom_set_meta_primary_item(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 item_id) | |
| { | |
| GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); | |
| if (!meta || !meta->item_infos || !meta->item_locations) return GF_BAD_PARAM; | |
| /*either one or the other*/ | |
| if (gf_isom_has_meta_xml(file, root_meta, track_num)) return GF_BAD_PARAM; | |
| if (meta->primary_resource) gf_isom_box_del_parent(&meta->child_boxes, (GF_Box*)meta->primary_resource); | |
| meta->primary_resource = (GF_PrimaryItemBox*) gf_isom_box_new_parent(&meta->child_boxes, GF_ISOM_BOX_TYPE_PITM); | |
| if (!meta->primary_resource) return GF_OUT_OF_MEM; | |
| meta->primary_resource->item_ID = item_id; | |
| return GF_OK; | |
| } | |
| #endif /*GPAC_DISABLE_ISOM_WRITE*/ | |
| GF_EXPORT | |
| GF_Err gf_isom_meta_add_item_ref(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 from_id, u32 to_id, u32 type, u64 *ref_index) | |
| { | |
| u32 i, count; | |
| s32 index = -1; | |
| GF_ItemReferenceTypeBox *ref; | |
| GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); | |
| if (!meta) return GF_BAD_PARAM; | |
| if (!meta->item_refs) { | |
| meta->item_refs = (GF_ItemReferenceBox *)gf_isom_box_new_parent(&meta->child_boxes, GF_ISOM_BOX_TYPE_IREF); | |
| if (!meta->item_refs) return GF_OUT_OF_MEM; | |
| } | |
| count = gf_list_count(meta->item_refs->references); | |
| for (i = 0; i < count; i++) { | |
| ref = (GF_ItemReferenceTypeBox *)gf_list_get(meta->item_refs->references, i); | |
| if (ref->from_item_id == from_id && ref->reference_type == type) { | |
| index = i; | |
| break; | |
| } | |
| } | |
| if (index < 0) { | |
| ref = (GF_ItemReferenceTypeBox *)gf_isom_box_new_parent(&meta->item_refs->child_boxes, GF_ISOM_BOX_TYPE_REFI); | |
| if (!ref) return GF_OUT_OF_MEM; | |
| gf_list_add(meta->item_refs->references, ref); | |
| ref->reference_type = type; | |
| ref->from_item_id = from_id; | |
| } | |
| else { | |
| for (i = 0; i < ref->reference_count; i++) { | |
| if (ref->to_item_IDs[i] == to_id) { | |
| return GF_OK; | |
| } | |
| } | |
| } | |
| ref->to_item_IDs = (u32 *)gf_realloc(ref->to_item_IDs, (ref->reference_count + 1) * sizeof(u32)); | |
| if (!ref->to_item_IDs) return GF_OUT_OF_MEM; | |
| ref->to_item_IDs[ref->reference_count] = to_id; | |
| ref->reference_count++; | |
| if (ref_index) { | |
| *ref_index = ref->reference_count; | |
| } | |
| return GF_OK; | |
| } | |
| void gf_isom_meta_restore_items_ref(GF_ISOFile *movie, GF_MetaBox *meta) | |
| { | |
| u32 i, nb_items, nb_tracks; | |
| if (!meta->item_locations || !meta->item_infos) return; | |
| nb_tracks = movie->moov ? gf_list_count(movie->moov->trackList) : 0; | |
| nb_items = gf_list_count(meta->item_locations->location_entries); | |
| for (i=0; i<nb_items; i++) { | |
| u32 j; | |
| u64 item_offset; | |
| GF_ItemExtentEntry *entry; | |
| GF_ItemLocationEntry *iloc = (GF_ItemLocationEntry *)gf_list_get(meta->item_locations->location_entries, i); | |
| /*get item info*/ | |
| GF_ItemInfoEntryBox *iinf = NULL; | |
| j=0; | |
| while ((iinf = (GF_ItemInfoEntryBox *)gf_list_enum(meta->item_infos->item_infos, &j))) { | |
| if (iinf->item_ID==iloc->item_ID) break; | |
| iinf = NULL; | |
| } | |
| if (!iinf) continue; | |
| if (iinf->ref_it_id) continue; | |
| if (gf_list_count(iloc->extent_entries) != 1) continue; | |
| entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0); | |
| if (!entry) continue; | |
| item_offset = iloc->base_offset + entry->extent_offset; | |
| for (j=0;j<nb_tracks; j++) { | |
| GF_TrackBox *trak; | |
| GF_SampleSizeBox *stsz; | |
| u32 k; | |
| trak = gf_list_get(movie->moov->trackList, j); | |
| if (! gf_isom_is_video_handler_type(trak->Media->handler->handlerType)) | |
| continue; | |
| stsz = trak->Media->information->sampleTable->SampleSize; | |
| if (!stsz || !stsz->sampleCount) continue; | |
| for (k=0; k<stsz->sampleCount; k++) { | |
| GF_Err e; | |
| u32 chunk, di, samp_size; | |
| u64 samp_offset; | |
| samp_size = stsz->sampleSize ? stsz->sampleSize : stsz->sizes[k]; | |
| if (samp_size != entry->extent_length) | |
| continue; | |
| e = stbl_GetSampleInfos(trak->Media->information->sampleTable, k+1, &samp_offset, &chunk, &di, NULL); | |
| if (e) continue; | |
| if (samp_offset == item_offset) { | |
| iinf->tk_id = trak->Header->trackID; | |
| iinf->sample_num = k+1; | |
| iinf->data_len = (u32) entry->extent_length; | |
| meta->use_item_sample_sharing = 1; | |
| break; | |
| } | |
| } | |
| if (iinf->tk_id) break; | |
| } | |
| //restore all item refs | |
| for (j=i+1;j<nb_items; j++) { | |
| u32 k; | |
| u64 item_offset2; | |
| GF_ItemExtentEntry *entry2; | |
| GF_ItemLocationEntry *iloc2 = (GF_ItemLocationEntry *)gf_list_get(meta->item_locations->location_entries, j); | |
| /*get item info*/ | |
| GF_ItemInfoEntryBox *iinf2 = NULL; | |
| k=0; | |
| while ((iinf2 = (GF_ItemInfoEntryBox *)gf_list_enum(meta->item_infos->item_infos, &k))) { | |
| if (iinf2->item_ID==iloc2->item_ID) break; | |
| iinf2 = NULL; | |
| } | |
| if (!iinf2) continue; | |
| if (gf_list_count(iloc2->extent_entries) != 1) continue; | |
| entry2 = (GF_ItemExtentEntry *)gf_list_get(iloc2->extent_entries, 0); | |
| if (!entry2) continue; | |
| item_offset2 = iloc2->base_offset + entry2->extent_offset; | |
| if (item_offset == item_offset2) { | |
| iinf2->ref_it_id = iinf->item_ID; | |
| meta->use_item_item_sharing = 1; | |
| } | |
| } | |
| } | |
| } | |
| #ifndef GPAC_DISABLE_ISOM_WRITE | |
| GF_EXPORT | |
| GF_Err gf_isom_meta_add_item_group(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 item_id, u32 group_id, u32 group_type) | |
| { | |
| u32 i, count; | |
| GF_EntityToGroupTypeBox *group; | |
| s32 index = -1; | |
| GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); | |
| if (!meta) return GF_BAD_PARAM; | |
| if (!group_type) return GF_BAD_PARAM; | |
| if (!group_id) { | |
| GF_Err e = gf_isom_meta_get_next_item_id(file, root_meta, track_num, &group_id); | |
| if (e != GF_OK) return e; | |
| } | |
| if (!meta->groups_list) { | |
| meta->groups_list = (GF_GroupListBox *)gf_isom_box_new_parent(&meta->child_boxes, GF_ISOM_BOX_TYPE_GRPL); | |
| if (!meta->groups_list) return GF_OUT_OF_MEM; | |
| meta->groups_list->child_boxes = gf_list_new(); | |
| if (!meta->groups_list->child_boxes) return GF_OUT_OF_MEM; | |
| } | |
| count = gf_list_count(meta->groups_list->child_boxes); | |
| for (i = 0; i < count; i++) { | |
| group = (GF_EntityToGroupTypeBox *)gf_list_get(meta->groups_list->child_boxes, i); | |
| if (group->grouping_type == group_type && group->group_id == group_id) { | |
| index = i; | |
| break; | |
| } | |
| } | |
| if (index < 0) { | |
| group = (GF_EntityToGroupTypeBox *)gf_isom_box_new_parent(&meta->groups_list->child_boxes, GF_ISOM_BOX_TYPE_GRPT); | |
| if (!group) return GF_OUT_OF_MEM; | |
| group->grouping_type = group_type; | |
| group->group_id = group_id; | |
| group->entity_ids = NULL; | |
| group->entity_id_count = 0; | |
| } else { | |
| group = (GF_EntityToGroupTypeBox *)gf_list_get(meta->groups_list->child_boxes, index); | |
| } | |
| group->entity_ids = (u32 *)gf_realloc(group->entity_ids, (group->entity_id_count + 1) * sizeof(u32)); | |
| if (!group->entity_ids) return GF_OUT_OF_MEM; | |
| group->entity_ids[group->entity_id_count] = item_id; | |
| group->entity_id_count++; | |
| return GF_OK; | |
| } | |
| #endif // GPAC_DISABLE_ISOM_WRITE | |
| GF_EXPORT | |
| u32 gf_isom_meta_get_item_ref_count(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 from_id, u32 type) | |
| { | |
| u32 i, count; | |
| GF_ItemReferenceTypeBox *ref; | |
| GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); | |
| if (!meta || !type || !from_id) return 0; | |
| if (!meta->item_refs) return 0; | |
| count = gf_list_count(meta->item_refs->references); | |
| for (i = 0; i < count; i++) { | |
| ref = (GF_ItemReferenceTypeBox *)gf_list_get(meta->item_refs->references, i); | |
| if (ref->from_item_id == from_id && ref->reference_type == type) { | |
| return ref->reference_count; | |
| } | |
| } | |
| return 0; | |
| } | |
| GF_EXPORT | |
| u32 gf_isom_meta_item_has_ref(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 to_id, u32 type) | |
| { | |
| u32 i, j, count, nb_refs=0; | |
| GF_ItemReferenceTypeBox *ref; | |
| GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); | |
| if (!meta || !type || !to_id) return 0; | |
| if (!meta->item_refs) return 0; | |
| count = gf_list_count(meta->item_refs->references); | |
| for (i = 0; i < count; i++) { | |
| ref = (GF_ItemReferenceTypeBox *)gf_list_get(meta->item_refs->references, i); | |
| if (ref->reference_type != type) continue; | |
| for (j=0; j<ref->reference_count; j++) { | |
| if (ref->to_item_IDs[j] == to_id) { | |
| nb_refs++; | |
| break; | |
| } | |
| } | |
| } | |
| return nb_refs; | |
| } | |
| GF_EXPORT | |
| u32 gf_isom_meta_get_item_ref_id(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 from_id, u32 type, u32 ref_idx) | |
| { | |
| u32 i, count; | |
| GF_ItemReferenceTypeBox *ref; | |
| GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); | |
| if (!meta || !type || !from_id || !ref_idx) return 0; | |
| if (!meta->item_refs) return 0; | |
| count = gf_list_count(meta->item_refs->references); | |
| for (i = 0; i < count; i++) { | |
| ref = (GF_ItemReferenceTypeBox *)gf_list_get(meta->item_refs->references, i); | |
| if (ref->from_item_id == from_id && ref->reference_type == type) { | |
| if (ref_idx>ref->reference_count) return 0; | |
| return ref->to_item_IDs[ref_idx-1]; | |
| } | |
| } | |
| return 0; | |
| } | |
| #endif /*GPAC_DISABLE_ISOM*/ |