| /* | |
| * GPAC - Multimedia Framework C SDK | |
| * | |
| * Authors: Jean Le Feuvre | |
| * Copyright (c) Telecom ParisTech 2000-2025 | |
| * All rights reserved | |
| * | |
| * This file is part of GPAC / MPEG-4 ObjectDescriptor 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/odf_dev.h> | |
| #include <gpac/constants.h> | |
| #include <gpac/avparse.h> | |
| #include <gpac/internal/media_dev.h> | |
| s32 gf_odf_size_field_size(u32 size_desc) | |
| { | |
| if (size_desc < 0x00000080) { | |
| return 1 + 1; | |
| } else if (size_desc < 0x00004000) { | |
| return 2 + 1; | |
| } else if (size_desc < 0x00200000) { | |
| return 3 + 1; | |
| } else if (size_desc < 0x10000000) { | |
| return 4 + 1; | |
| } else { | |
| return -1; | |
| } | |
| } | |
| GF_EXPORT | |
| GF_Err gf_odf_parse_descriptor(GF_BitStream *bs, GF_Descriptor **desc, u32 *desc_size) | |
| { | |
| u32 val, size, sizeHeader; | |
| u8 tag; | |
| GF_Err err; | |
| GF_Descriptor *newDesc; | |
| if (!bs) return GF_BAD_PARAM; | |
| *desc_size = 0; | |
| //tag | |
| tag = (u8) gf_bs_read_int(bs, 8); | |
| sizeHeader = 1; | |
| //size | |
| size = 0; | |
| do { | |
| val = gf_bs_read_int(bs, 8); | |
| sizeHeader++; | |
| if (sizeHeader > 5) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[ODF] Descriptor size on more than 4 bytes\n")); | |
| return GF_ODF_INVALID_DESCRIPTOR; | |
| } | |
| size <<= 7; | |
| size |= val & 0x7F; | |
| } while ( val & 0x80); | |
| *desc_size = size; | |
| if (gf_bs_available(bs) < size) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[ODF] Not enough bytes (%d) to read descriptor (size=%d)\n", gf_bs_available(bs), size)); | |
| return GF_ODF_INVALID_DESCRIPTOR; | |
| } | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("[ODF] Reading descriptor (tag %d size %d)\n", tag, size )); | |
| newDesc = gf_odf_create_descriptor(tag); | |
| if (! newDesc) { | |
| *desc = NULL; | |
| *desc_size = sizeHeader; | |
| if ( (tag >= GF_ODF_ISO_RES_BEGIN_TAG) && | |
| (tag <= GF_ODF_ISO_RES_END_TAG) ) { | |
| return GF_ODF_FORBIDDEN_DESCRIPTOR; | |
| } | |
| else if (!tag || (tag == 0xFF)) { | |
| return GF_ODF_INVALID_DESCRIPTOR; | |
| } | |
| #ifndef GPAC_MINIMAL_ODF | |
| return GF_OUT_OF_MEM; | |
| #else | |
| gf_bs_skip_bytes(bs, size); | |
| *desc_size = size + sizeHeader - gf_odf_size_field_size(*desc_size); | |
| return GF_OK; | |
| #endif | |
| } | |
| newDesc->tag = tag; | |
| err = gf_odf_read_descriptor(bs, newDesc, *desc_size); | |
| /*FFmpeg fix*/ | |
| if ((tag==GF_ODF_SLC_TAG) && (((GF_SLConfig*)newDesc)->predefined==2)) { | |
| if (*desc_size==3) { | |
| *desc_size = 1; | |
| err = GF_OK; | |
| } | |
| } | |
| //little trick to handle lazy bitstreams that encode | |
| //SizeOfInstance on a fix number of bytes | |
| //This nb of bytes is added in Read methods | |
| *desc_size += sizeHeader - gf_odf_size_field_size(*desc_size); | |
| *desc = newDesc; | |
| if (err) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[ODF] Error reading descriptor (tag %d size %d): %s\n", tag, size, gf_error_to_string(err) )); | |
| gf_odf_delete_descriptor(newDesc); | |
| *desc = NULL; | |
| } | |
| return err; | |
| } | |
| GF_Err gf_odf_delete_descriptor_list(GF_List *descList) | |
| { | |
| GF_Err e; | |
| GF_Descriptor*tmp; | |
| u32 i; | |
| //no error if NULL chain... | |
| if (! descList) return GF_OK; | |
| i=0; | |
| while ((tmp = (GF_Descriptor*)gf_list_enum(descList, &i))) { | |
| e = gf_odf_delete_descriptor(tmp); | |
| if (e) return e; | |
| } | |
| gf_list_del(descList); | |
| return GF_OK; | |
| } | |
| GF_Err gf_odf_write_base_descriptor(GF_BitStream *bs, u8 tag, u32 size) | |
| { | |
| u32 length; | |
| unsigned char vals[4]; | |
| if (!tag ) return GF_BAD_PARAM; | |
| length = size; | |
| vals[3] = (unsigned char) (length & 0x7f); | |
| length >>= 7; | |
| vals[2] = (unsigned char) ((length & 0x7f) | 0x80); | |
| length >>= 7; | |
| vals[1] = (unsigned char) ((length & 0x7f) | 0x80); | |
| length >>= 7; | |
| vals[0] = (unsigned char) ((length & 0x7f) | 0x80); | |
| gf_bs_write_int(bs, tag, 8); | |
| if (size < 0x00000080) { | |
| gf_bs_write_int(bs, vals[3], 8); | |
| } else if (size < 0x00004000) { | |
| gf_bs_write_int(bs, vals[2], 8); | |
| gf_bs_write_int(bs, vals[3], 8); | |
| } else if (size < 0x00200000) { | |
| gf_bs_write_int(bs, vals[1], 8); | |
| gf_bs_write_int(bs, vals[2], 8); | |
| gf_bs_write_int(bs, vals[3], 8); | |
| } else if (size < 0x10000000) { | |
| gf_bs_write_int(bs, vals[0], 8); | |
| gf_bs_write_int(bs, vals[1], 8); | |
| gf_bs_write_int(bs, vals[2], 8); | |
| gf_bs_write_int(bs, vals[3], 8); | |
| } else { | |
| return GF_ODF_INVALID_DESCRIPTOR; | |
| } | |
| return GF_OK; | |
| } | |
| GF_Err gf_odf_size_descriptor_list(GF_List *descList, u32 *outSize) | |
| { | |
| GF_Err e; | |
| u32 tmpSize, count, i; | |
| if (! descList) return GF_OK; | |
| count = gf_list_count(descList); | |
| for ( i = 0; i < count; i++ ) { | |
| GF_Descriptor *tmp = (GF_Descriptor*)gf_list_get(descList, i); | |
| if (tmp) { | |
| e = gf_odf_size_descriptor(tmp, &tmpSize); | |
| if (e) return e; | |
| if (tmpSize) *outSize += tmpSize + gf_odf_size_field_size(tmpSize); | |
| } | |
| } | |
| return GF_OK; | |
| } | |
| GF_Err gf_odf_write_descriptor_list(GF_BitStream *bs, GF_List *descList) | |
| { | |
| GF_Err e; | |
| u32 count, i; | |
| if (! descList) return GF_OK; | |
| count = gf_list_count(descList); | |
| for ( i = 0; i < count; i++ ) { | |
| GF_Descriptor *tmp = (GF_Descriptor*)gf_list_get(descList, i); | |
| if (tmp) { | |
| e = gf_odf_write_descriptor(bs, tmp); | |
| if (e) return e; | |
| } | |
| } | |
| return GF_OK; | |
| } | |
| GF_Err gf_odf_write_descriptor_list_filter(GF_BitStream *bs, GF_List *descList, u8 only_tag) | |
| { | |
| GF_Err e; | |
| u32 count, i; | |
| if (! descList) return GF_OK; | |
| count = gf_list_count(descList); | |
| for ( i = 0; i < count; i++ ) { | |
| GF_Descriptor *tmp = (GF_Descriptor*)gf_list_get(descList, i); | |
| if (tmp && (tmp->tag==only_tag) ) { | |
| e = gf_odf_write_descriptor(bs, tmp); | |
| if (e) return e; | |
| } | |
| } | |
| return GF_OK; | |
| } | |
| #ifndef GPAC_MINIMAL_ODF | |
| u32 gf_ipmpx_array_size(GF_BitStream *bs, u32 *array_size) | |
| { | |
| u32 val, size, io_size; | |
| io_size = size = 0; | |
| do { | |
| val = gf_bs_read_int(bs, 8); | |
| io_size ++; | |
| size <<= 7; | |
| size |= val & 0x7F; | |
| } while ( val & 0x80 ); | |
| *array_size = size; | |
| return io_size; | |
| } | |
| void gf_ipmpx_write_array(GF_BitStream *bs, u8 *data, u32 data_len) | |
| { | |
| u32 length; | |
| unsigned char vals[4]; | |
| if (!data || !data_len) return; | |
| length = data_len; | |
| vals[3] = (unsigned char) (length & 0x7f); | |
| length >>= 7; | |
| vals[2] = (unsigned char) ((length & 0x7f) | 0x80); | |
| length >>= 7; | |
| vals[1] = (unsigned char) ((length & 0x7f) | 0x80); | |
| length >>= 7; | |
| vals[0] = (unsigned char) ((length & 0x7f) | 0x80); | |
| if (data_len < 0x00000080) { | |
| gf_bs_write_int(bs, vals[3], 8); | |
| } else if (data_len < 0x00004000) { | |
| gf_bs_write_int(bs, vals[2], 8); | |
| gf_bs_write_int(bs, vals[3], 8); | |
| } else if (data_len < 0x00200000) { | |
| gf_bs_write_int(bs, vals[1], 8); | |
| gf_bs_write_int(bs, vals[2], 8); | |
| gf_bs_write_int(bs, vals[3], 8); | |
| } else if (data_len < 0x10000000) { | |
| gf_bs_write_int(bs, vals[0], 8); | |
| gf_bs_write_int(bs, vals[1], 8); | |
| gf_bs_write_int(bs, vals[2], 8); | |
| gf_bs_write_int(bs, vals[3], 8); | |
| } else { | |
| return; | |
| } | |
| gf_bs_write_data(bs, data, data_len); | |
| } | |
| #endif /*GPAC_MINIMAL_ODF*/ | |
| /*special authoring functions*/ | |
| GF_EXPORT | |
| GF_BIFSConfig *gf_odf_get_bifs_config(GF_DefaultDescriptor *dsi, u32 oti) | |
| { | |
| Bool hasSize, cmd_stream; | |
| GF_BitStream *bs; | |
| GF_BIFSConfig *cfg; | |
| if (oti>=GF_CODECID_BIFS_EXTENDED) return NULL; | |
| if (!dsi || !dsi->data || !dsi->dataLength ) { | |
| /* Hack for T-DMB non compliant streams (OnTimeTek ?) */ | |
| cfg = (GF_BIFSConfig *) gf_odf_desc_new(GF_ODF_BIFS_CFG_TAG); | |
| cfg->pixelMetrics = GF_TRUE; | |
| cfg->version = 1; | |
| return cfg; | |
| } | |
| bs = gf_bs_new(dsi->data, dsi->dataLength, GF_BITSTREAM_READ); | |
| cfg = (GF_BIFSConfig *) gf_odf_desc_new(GF_ODF_BIFS_CFG_TAG); | |
| if (oti==2) { | |
| /*3D Mesh Coding*/ | |
| gf_bs_read_int(bs, 1); | |
| /*PMF*/ | |
| gf_bs_read_int(bs, 1); | |
| } | |
| cfg->nodeIDbits = gf_bs_read_int(bs, 5); | |
| cfg->routeIDbits = gf_bs_read_int(bs, 5); | |
| if (oti==2) cfg->protoIDbits = gf_bs_read_int(bs, 5); | |
| cmd_stream = (Bool)gf_bs_read_int(bs, 1); | |
| if (!cmd_stream) { | |
| cfg->elementaryMasks = gf_list_new(); | |
| while (1) { | |
| GF_ElementaryMask* em = (GF_ElementaryMask* ) gf_odf_New_ElemMask(); | |
| em->node_id = gf_bs_read_int(bs, cfg->nodeIDbits); | |
| gf_list_add(cfg->elementaryMasks, em); | |
| /*this assumes only FDP, BDP and IFS2D (no elem mask)*/ | |
| if (gf_bs_read_int(bs, 1) == 0) break; | |
| } | |
| gf_bs_align(bs); | |
| if (gf_bs_get_size(bs) != gf_bs_get_position(bs)) { | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_CODEC, ("[ODF] Reading bifs config: shift in sizes (not supported)\n")); | |
| } | |
| } else { | |
| cfg->pixelMetrics = (Bool)gf_bs_read_int(bs, 1); | |
| hasSize = (Bool)gf_bs_read_int(bs, 1); | |
| if (hasSize) { | |
| cfg->pixelWidth = gf_bs_read_int(bs, 16); | |
| cfg->pixelHeight = gf_bs_read_int(bs, 16); | |
| } | |
| gf_bs_align(bs); | |
| if (gf_bs_get_size(bs) != gf_bs_get_position(bs)) | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_CODEC, ("[ODF] Reading bifs config: shift in sizes (invalid descriptor)\n")); | |
| } | |
| gf_bs_del(bs); | |
| return cfg; | |
| } | |
| /*special function for authoring - convert DSI to LASERConfig*/ | |
| GF_EXPORT | |
| GF_Err gf_odf_get_laser_config(GF_DefaultDescriptor *dsi, GF_LASERConfig *cfg) | |
| { | |
| u32 to_skip; | |
| GF_BitStream *bs; | |
| if (!cfg) return GF_BAD_PARAM; | |
| memset(cfg, 0, sizeof(GF_LASERConfig)); | |
| if (!dsi || !dsi->data || !dsi->dataLength) return GF_BAD_PARAM; | |
| bs = gf_bs_new(dsi->data, dsi->dataLength, GF_BITSTREAM_READ); | |
| memset(cfg, 0, sizeof(GF_LASERConfig)); | |
| cfg->tag = GF_ODF_LASER_CFG_TAG; | |
| cfg->profile = gf_bs_read_int(bs, 8); | |
| cfg->level = gf_bs_read_int(bs, 8); | |
| /*cfg->reserved = */gf_bs_read_int(bs, 3); | |
| cfg->pointsCodec = gf_bs_read_int(bs, 2); | |
| cfg->pathComponents = gf_bs_read_int(bs, 4); | |
| cfg->fullRequestHost = gf_bs_read_int(bs, 1); | |
| if (gf_bs_read_int(bs, 1)) cfg->time_resolution = gf_bs_read_int(bs, 16); | |
| else cfg->time_resolution = 1000; | |
| cfg->colorComponentBits = 1 + gf_bs_read_int(bs, 4); | |
| cfg->resolution = gf_bs_read_int(bs, 4); | |
| if (cfg->resolution>7) cfg->resolution -= 16; | |
| cfg->coord_bits = gf_bs_read_int(bs, 5); | |
| cfg->scale_bits_minus_coord_bits = gf_bs_read_int(bs, 4); | |
| cfg->newSceneIndicator = gf_bs_read_int(bs, 1); | |
| /*reserved2*/ gf_bs_read_int(bs, 3); | |
| cfg->extensionIDBits = gf_bs_read_int(bs, 4); | |
| /*hasExtConfig - we just ignore it*/ | |
| if (gf_bs_read_int(bs, 1)) { | |
| to_skip = gf_bs_read_vluimsbf5(bs); | |
| while (to_skip) { | |
| gf_bs_read_int(bs, 8); | |
| to_skip--; | |
| } | |
| } | |
| /*hasExtension - we just ignore it*/ | |
| if (gf_bs_read_int(bs, 1)) { | |
| to_skip = gf_bs_read_vluimsbf5(bs); | |
| while (to_skip) { | |
| gf_bs_read_int(bs, 8); | |
| to_skip--; | |
| } | |
| } | |
| gf_bs_del(bs); | |
| return GF_OK; | |
| } | |
| //unused | |
| #if 0 | |
| GF_Err gf_odf_get_ui_config(GF_DefaultDescriptor *dsi, GF_UIConfig *cfg) | |
| { | |
| u32 len, i; | |
| GF_BitStream *bs; | |
| if (!dsi || !dsi->data || !dsi->dataLength || !cfg) return GF_BAD_PARAM; | |
| memset(cfg, 0, sizeof(GF_UIConfig)); | |
| cfg->tag = GF_ODF_UI_CFG_TAG; | |
| bs = gf_bs_new(dsi->data, dsi->dataLength, GF_BITSTREAM_READ); | |
| len = gf_bs_read_int(bs, 8); | |
| cfg->deviceName = (char*)gf_malloc(sizeof(char) * (len+1)); | |
| for (i=0; i<len; i++) cfg->deviceName[i] = gf_bs_read_int(bs, 8); | |
| cfg->deviceName[i] = 0; | |
| if (!stricmp(cfg->deviceName, "StringSensor") && gf_bs_available(bs)) { | |
| cfg->termChar = gf_bs_read_int(bs, 8); | |
| cfg->delChar = gf_bs_read_int(bs, 8); | |
| } | |
| gf_bs_del(bs); | |
| return GF_OK; | |
| } | |
| #endif | |
| GF_EXPORT | |
| GF_Err gf_odf_encode_ui_config(GF_UIConfig *cfg, GF_DefaultDescriptor **out_dsi) | |
| { | |
| u32 i, len; | |
| GF_BitStream *bs; | |
| GF_DefaultDescriptor *dsi; | |
| if (!out_dsi || !cfg || (cfg->tag != GF_ODF_UI_CFG_TAG)) return GF_BAD_PARAM; | |
| *out_dsi = NULL; | |
| if (!cfg->deviceName) return GF_OK; | |
| bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); | |
| len = (u32) strlen(cfg->deviceName); | |
| gf_bs_write_int(bs, len, 8); | |
| for (i=0; i<len; i++) gf_bs_write_int(bs, cfg->deviceName[i], 8); | |
| if (!stricmp(cfg->deviceName, "StringSensor")) { | |
| /*fixme - this should be UTF-8 chars*/ | |
| if (cfg->delChar || cfg->termChar) { | |
| gf_bs_write_int(bs, cfg->termChar, 8); | |
| gf_bs_write_int(bs, cfg->delChar, 8); | |
| } | |
| } | |
| if (cfg->ui_data) gf_bs_write_data(bs, cfg->ui_data, cfg->ui_data_length); | |
| dsi = (GF_DefaultDescriptor *) gf_odf_desc_new(GF_ODF_DSI_TAG); | |
| gf_bs_get_content(bs, &dsi->data, &dsi->dataLength); | |
| gf_bs_del(bs); | |
| *out_dsi = dsi; | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_AVCConfig *gf_odf_avc_cfg_new() | |
| { | |
| GF_AVCConfig *cfg; | |
| GF_SAFEALLOC(cfg, GF_AVCConfig); | |
| if (!cfg) return NULL; | |
| cfg->sequenceParameterSets = gf_list_new(); | |
| cfg->pictureParameterSets = gf_list_new(); | |
| cfg->AVCLevelIndication = 1; | |
| cfg->chroma_format = 1; | |
| cfg->chroma_bit_depth = 8; | |
| cfg->luma_bit_depth = 8; | |
| return cfg; | |
| } | |
| GF_EXPORT | |
| void gf_odf_avc_cfg_del(GF_AVCConfig *cfg) | |
| { | |
| if (!cfg) return; | |
| while (gf_list_count(cfg->sequenceParameterSets)) { | |
| GF_NALUFFParam *sl = (GF_NALUFFParam *)gf_list_get(cfg->sequenceParameterSets, 0); | |
| gf_list_rem(cfg->sequenceParameterSets, 0); | |
| if (sl->data) gf_free(sl->data); | |
| gf_free(sl); | |
| } | |
| gf_list_del(cfg->sequenceParameterSets); | |
| cfg->sequenceParameterSets = NULL; | |
| while (gf_list_count(cfg->pictureParameterSets)) { | |
| GF_NALUFFParam *sl = (GF_NALUFFParam *)gf_list_get(cfg->pictureParameterSets, 0); | |
| gf_list_rem(cfg->pictureParameterSets, 0); | |
| if (sl->data) gf_free(sl->data); | |
| gf_free(sl); | |
| } | |
| gf_list_del(cfg->pictureParameterSets); | |
| cfg->pictureParameterSets = NULL; | |
| if (cfg->sequenceParameterSetExtensions) { | |
| while (gf_list_count(cfg->sequenceParameterSetExtensions)) { | |
| GF_NALUFFParam *sl = (GF_NALUFFParam *)gf_list_get(cfg->sequenceParameterSetExtensions, 0); | |
| gf_list_rem(cfg->sequenceParameterSetExtensions, 0); | |
| if (sl->data) gf_free(sl->data); | |
| gf_free(sl); | |
| } | |
| gf_list_del(cfg->sequenceParameterSetExtensions); | |
| cfg->sequenceParameterSetExtensions = NULL; | |
| } | |
| gf_free(cfg); | |
| } | |
| GF_EXPORT | |
| GF_Err gf_odf_avc_cfg_write_bs(GF_AVCConfig *cfg, GF_BitStream *bs) | |
| { | |
| u32 i, count; | |
| if (!cfg) return GF_BAD_PARAM; | |
| count = gf_list_count(cfg->sequenceParameterSets); | |
| if (!cfg->write_annex_b) { | |
| gf_bs_write_int(bs, cfg->configurationVersion, 8); | |
| gf_bs_write_int(bs, cfg->AVCProfileIndication , 8); | |
| gf_bs_write_int(bs, cfg->profile_compatibility, 8); | |
| gf_bs_write_int(bs, cfg->AVCLevelIndication, 8); | |
| gf_bs_write_int(bs, 0x3F, 6); | |
| gf_bs_write_int(bs, cfg->nal_unit_size - 1, 2); | |
| gf_bs_write_int(bs, 0x7, 3); | |
| gf_bs_write_int(bs, count, 5); | |
| } | |
| for (i=0; i<count; i++) { | |
| GF_NALUFFParam *sl = (GF_NALUFFParam *)gf_list_get(cfg->sequenceParameterSets, i); | |
| if (!cfg->write_annex_b) { | |
| gf_bs_write_u16(bs, sl->size); | |
| } else { | |
| gf_bs_write_u32(bs, 1); | |
| } | |
| gf_bs_write_data(bs, sl->data, sl->size); | |
| } | |
| count = gf_list_count(cfg->pictureParameterSets); | |
| if (!cfg->write_annex_b) { | |
| gf_bs_write_int(bs, count, 8); | |
| } | |
| for (i=0; i<count; i++) { | |
| GF_NALUFFParam *sl = (GF_NALUFFParam *)gf_list_get(cfg->pictureParameterSets, i); | |
| if (!cfg->write_annex_b) { | |
| gf_bs_write_u16(bs, sl->size); | |
| } else { | |
| gf_bs_write_u32(bs, 1); | |
| } | |
| gf_bs_write_data(bs, sl->data, sl->size); | |
| } | |
| if (gf_avcc_use_extensions(cfg->AVCProfileIndication)) { | |
| if (!cfg->write_annex_b) { | |
| gf_bs_write_int(bs, 0xFF, 6); | |
| gf_bs_write_int(bs, cfg->chroma_format, 2); | |
| gf_bs_write_int(bs, 0xFF, 5); | |
| gf_bs_write_int(bs, cfg->luma_bit_depth - 8, 3); | |
| gf_bs_write_int(bs, 0xFF, 5); | |
| gf_bs_write_int(bs, cfg->chroma_bit_depth - 8, 3); | |
| } | |
| count = cfg->sequenceParameterSetExtensions ? gf_list_count(cfg->sequenceParameterSetExtensions) : 0; | |
| if (!cfg->write_annex_b) { | |
| gf_bs_write_u8(bs, count); | |
| } | |
| for (i=0; i<count; i++) { | |
| GF_NALUFFParam *sl = (GF_NALUFFParam *) gf_list_get(cfg->sequenceParameterSetExtensions, i); | |
| if (!cfg->write_annex_b) { | |
| gf_bs_write_u16(bs, sl->size); | |
| } else { | |
| gf_bs_write_u32(bs, 1); | |
| } | |
| gf_bs_write_data(bs, sl->data, sl->size); | |
| } | |
| } | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_odf_avc_cfg_write(GF_AVCConfig *cfg, u8 **outData, u32 *outSize) | |
| { | |
| GF_BitStream *bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); | |
| gf_odf_avc_cfg_write_bs(cfg, bs); | |
| *outSize = 0; | |
| *outData = NULL; | |
| gf_bs_get_content(bs, outData, outSize); | |
| gf_bs_del(bs); | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_AVCConfig *gf_odf_avc_cfg_read(u8 *dsi, u32 dsi_size) | |
| { | |
| u32 i, count; | |
| GF_AVCConfig *avcc = gf_odf_avc_cfg_new(); | |
| GF_BitStream *bs = gf_bs_new(dsi, dsi_size, GF_BITSTREAM_READ); | |
| avcc->configurationVersion = gf_bs_read_int(bs, 8); | |
| avcc->AVCProfileIndication = gf_bs_read_int(bs, 8); | |
| avcc->profile_compatibility = gf_bs_read_int(bs, 8); | |
| avcc->AVCLevelIndication = gf_bs_read_int(bs, 8); | |
| gf_bs_read_int(bs, 6); | |
| avcc->nal_unit_size = 1 + gf_bs_read_int(bs, 2); | |
| gf_bs_read_int(bs, 3); | |
| count = gf_bs_read_int(bs, 5); | |
| for (i=0; i<count; i++) { | |
| GF_NALUFFParam *sl; | |
| u32 size = gf_bs_read_int(bs, 16); | |
| if ((size>gf_bs_available(bs)) || (size<2)) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[AVC] Wrong param set size %d\n", size)); | |
| gf_bs_del(bs); | |
| gf_odf_avc_cfg_del(avcc); | |
| return NULL; | |
| } | |
| GF_SAFEALLOC(sl, GF_NALUFFParam ); | |
| if (!sl) { | |
| gf_bs_del(bs); | |
| gf_odf_avc_cfg_del(avcc); | |
| return NULL; | |
| } | |
| sl->size = size; | |
| sl->data = (char*)gf_malloc(sizeof(char)*sl->size); | |
| if (!sl->data) { | |
| gf_bs_del(bs); | |
| gf_odf_avc_cfg_del(avcc); | |
| return NULL; | |
| } | |
| gf_bs_read_data(bs, sl->data, sl->size); | |
| gf_list_add(avcc->sequenceParameterSets, sl); | |
| } | |
| count = gf_bs_read_int(bs, 8); | |
| for (i=0; i<count; i++) { | |
| GF_NALUFFParam *sl; | |
| u32 size = gf_bs_read_int(bs, 16); | |
| if ((size>gf_bs_available(bs)) || (size<2)) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[AVC] Wrong param set size %d\n", size)); | |
| gf_bs_del(bs); | |
| gf_odf_avc_cfg_del(avcc); | |
| return NULL; | |
| } | |
| GF_SAFEALLOC(sl, GF_NALUFFParam ); | |
| if (!sl) { | |
| gf_bs_del(bs); | |
| gf_odf_avc_cfg_del(avcc); | |
| return NULL; | |
| } | |
| sl->size = size; | |
| sl->data = (char*)gf_malloc(sizeof(char)*sl->size); | |
| if (!sl->data) { | |
| gf_bs_del(bs); | |
| gf_odf_avc_cfg_del(avcc); | |
| return NULL; | |
| } | |
| gf_bs_read_data(bs, sl->data, sl->size); | |
| gf_list_add(avcc->pictureParameterSets, sl); | |
| } | |
| if (gf_avcc_use_extensions(avcc->AVCProfileIndication)) { | |
| gf_bs_read_int(bs, 6); | |
| avcc->chroma_format = gf_bs_read_int(bs, 2); | |
| gf_bs_read_int(bs, 5); | |
| avcc->luma_bit_depth = 8 + gf_bs_read_int(bs, 3); | |
| gf_bs_read_int(bs, 5); | |
| avcc->chroma_bit_depth = 8 + gf_bs_read_int(bs, 3); | |
| count = gf_bs_read_int(bs, 8); | |
| if (count) { | |
| avcc->sequenceParameterSetExtensions = gf_list_new(); | |
| for (i=0; i<count; i++) { | |
| GF_NALUFFParam *sl; | |
| u32 size = gf_bs_read_int(bs, 16); | |
| if ((size>gf_bs_available(bs)) || (size<2)) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[AVC] Wrong param set size %d\n", size)); | |
| gf_bs_del(bs); | |
| gf_odf_avc_cfg_del(avcc); | |
| return NULL; | |
| } | |
| GF_SAFEALLOC(sl, GF_NALUFFParam ); | |
| if (!sl) { | |
| gf_bs_del(bs); | |
| gf_odf_avc_cfg_del(avcc); | |
| return NULL; | |
| } | |
| sl->size = size; | |
| sl->data = (char*)gf_malloc(sizeof(char)*sl->size); | |
| if (!sl->data) { | |
| gf_bs_del(bs); | |
| gf_odf_avc_cfg_del(avcc); | |
| return NULL; | |
| } | |
| gf_bs_read_data(bs, sl->data, sl->size); | |
| gf_list_add(avcc->sequenceParameterSetExtensions, sl); | |
| } | |
| } | |
| } | |
| gf_bs_del(bs); | |
| return avcc; | |
| } | |
| GF_Descriptor *gf_odf_new_tx3g() | |
| { | |
| GF_TextSampleDescriptor *newDesc = (GF_TextSampleDescriptor*) gf_malloc(sizeof(GF_TextSampleDescriptor)); | |
| if (!newDesc) return NULL; | |
| memset(newDesc, 0, sizeof(GF_TextSampleDescriptor)); | |
| newDesc->tag = GF_ODF_TX3G_TAG; | |
| return (GF_Descriptor *) newDesc; | |
| } | |
| GF_Err gf_odf_del_tx3g(GF_TextSampleDescriptor *sd) | |
| { | |
| u32 i; | |
| for (i=0; i<sd->font_count; i++) | |
| if (sd->fonts[i].fontName) gf_free(sd->fonts[i].fontName); | |
| gf_free(sd->fonts); | |
| gf_free(sd); | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_TextSampleDescriptor *gf_odf_tx3g_read(u8 *dsi, u32 dsi_size) | |
| { | |
| #ifndef GPAC_DISABLE_ISOM | |
| u32 i; | |
| u32 gpp_read_rgba(GF_BitStream *bs); | |
| void gpp_read_style(GF_BitStream *bs, GF_StyleRecord *rec); | |
| void gpp_read_box(GF_BitStream *bs, GF_BoxRecord *rec); | |
| GF_TextSampleDescriptor *txtc = (GF_TextSampleDescriptor *) gf_odf_new_tx3g(); | |
| GF_BitStream *bs = gf_bs_new(dsi, dsi_size, GF_BITSTREAM_READ); | |
| txtc->displayFlags = gf_bs_read_int(bs, 32); | |
| txtc->horiz_justif = gf_bs_read_int(bs, 8); | |
| txtc->vert_justif = gf_bs_read_int(bs, 8); | |
| txtc->back_color = gpp_read_rgba(bs); | |
| gpp_read_box(bs, &txtc->default_pos); | |
| gpp_read_style(bs, &txtc->default_style); | |
| txtc->font_count = gf_bs_read_u16(bs); | |
| txtc->fonts = gf_malloc(sizeof(GF_FontRecord)*txtc->font_count); | |
| for (i=0; i<txtc->font_count; i++) { | |
| u8 len; | |
| txtc->fonts[i].fontID = gf_bs_read_u16(bs); | |
| len = gf_bs_read_u8(bs); | |
| txtc->fonts[i].fontName = gf_malloc(sizeof(char)*(len+1)); | |
| gf_bs_read_data(bs, txtc->fonts[i].fontName, len); | |
| txtc->fonts[i].fontName[len] = 0; | |
| } | |
| gf_bs_del(bs); | |
| return txtc; | |
| #else | |
| return NULL; | |
| #endif | |
| } | |
| GF_Err gf_odf_tx3g_write(GF_TextSampleDescriptor *a, u8 **outData, u32 *outSize) | |
| { | |
| #ifndef GPAC_DISABLE_ISOM | |
| u32 j; | |
| void gpp_write_rgba(GF_BitStream *bs, u32 col); | |
| void gpp_write_box(GF_BitStream *bs, GF_BoxRecord *rec); | |
| void gpp_write_style(GF_BitStream *bs, GF_StyleRecord *rec); | |
| GF_BitStream *bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); | |
| gf_bs_write_u32(bs, a->displayFlags); | |
| gf_bs_write_u8(bs, a->horiz_justif); | |
| gf_bs_write_u8(bs, a->vert_justif); | |
| gpp_write_rgba(bs, a->back_color); | |
| gpp_write_box(bs, &a->default_pos); | |
| gpp_write_style(bs, &a->default_style); | |
| gf_bs_write_u16(bs, a->font_count); | |
| for (j=0; j<a->font_count; j++) { | |
| gf_bs_write_u16(bs, a->fonts[j].fontID); | |
| if (a->fonts[j].fontName) { | |
| u32 len = (u32) strlen(a->fonts[j].fontName); | |
| gf_bs_write_u8(bs, len); | |
| gf_bs_write_data(bs, a->fonts[j].fontName, len); | |
| } else { | |
| gf_bs_write_u8(bs, 0); | |
| } | |
| } | |
| gf_bs_get_content(bs, outData, outSize); | |
| gf_bs_del(bs); | |
| return GF_OK; | |
| #else | |
| return GF_NOT_SUPPORTED; | |
| #endif | |
| } | |
| /*TextConfig*/ | |
| GF_Descriptor *gf_odf_new_text_cfg() | |
| { | |
| GF_TextConfig *newDesc = (GF_TextConfig*) gf_malloc(sizeof(GF_TextConfig)); | |
| if (!newDesc) return NULL; | |
| memset(newDesc, 0, sizeof(GF_TextConfig)); | |
| newDesc->tag = GF_ODF_TEXT_CFG_TAG; | |
| newDesc->sample_descriptions = gf_list_new(); | |
| newDesc->Base3GPPFormat = 0x10; | |
| newDesc->MPEGExtendedFormat = 0x10; | |
| newDesc->profileLevel = 0x10; | |
| newDesc->timescale = 1000; | |
| return (GF_Descriptor *) newDesc; | |
| } | |
| void ResetTextConfig(GF_TextConfig *desc) | |
| { | |
| GF_List *bck; | |
| while (gf_list_count(desc->sample_descriptions)) { | |
| GF_TextSampleDescriptor *sd = (GF_TextSampleDescriptor *)gf_list_get(desc->sample_descriptions, 0); | |
| gf_list_rem(desc->sample_descriptions, 0); | |
| gf_odf_del_tx3g(sd); | |
| } | |
| bck = desc->sample_descriptions; | |
| memset(desc, 0, sizeof(GF_TextConfig)); | |
| desc->tag = GF_ODF_TEXT_CFG_TAG; | |
| desc->sample_descriptions = bck; | |
| } | |
| GF_Err gf_odf_del_text_cfg(GF_TextConfig *desc) | |
| { | |
| ResetTextConfig(desc); | |
| gf_list_del(desc->sample_descriptions); | |
| gf_free(desc); | |
| return GF_OK; | |
| } | |
| /*we need box parsing*/ | |
| #include <gpac/internal/isomedia_dev.h> | |
| GF_EXPORT | |
| GF_Err gf_odf_get_text_config(u8 *data, u32 data_len, u32 codecid, GF_TextConfig *cfg) | |
| { | |
| u32 i; | |
| Bool has_alt_format; | |
| #ifndef GPAC_DISABLE_ISOM | |
| Bool has_sd; | |
| u32 j; | |
| #endif | |
| GF_Err e; | |
| GF_BitStream *bs; | |
| if (data || data_len || !cfg) return GF_BAD_PARAM; | |
| if (codecid != GF_CODECID_TEXT_MPEG4) return GF_NOT_SUPPORTED; | |
| /*reset*/ | |
| ResetTextConfig(cfg); | |
| bs = gf_bs_new(data, data_len, GF_BITSTREAM_READ); | |
| e = GF_OK; | |
| cfg->Base3GPPFormat = gf_bs_read_int(bs, 8); | |
| cfg->MPEGExtendedFormat = gf_bs_read_int(bs, 8); | |
| cfg->profileLevel = gf_bs_read_int(bs, 8); | |
| cfg->timescale = gf_bs_read_int(bs, 24); | |
| has_alt_format = (Bool)gf_bs_read_int(bs, 1); | |
| cfg->sampleDescriptionFlags = gf_bs_read_int(bs, 2); | |
| #ifndef GPAC_DISABLE_ISOM | |
| has_sd = (Bool)gf_bs_read_int(bs, 1); | |
| #else | |
| gf_bs_read_int(bs, 1); | |
| #endif | |
| cfg->has_vid_info = (Bool)gf_bs_read_int(bs, 1); | |
| gf_bs_read_int(bs, 3); | |
| cfg->layer = gf_bs_read_int(bs, 8); | |
| cfg->text_width = gf_bs_read_int(bs, 16); | |
| cfg->text_height = gf_bs_read_int(bs, 16); | |
| if (has_alt_format) { | |
| cfg->nb_compatible_formats = gf_bs_read_int(bs, 8); | |
| for (i=0; i<cfg->nb_compatible_formats; i++) cfg->compatible_formats[i] = gf_bs_read_int(bs, 8); | |
| } | |
| #ifndef GPAC_DISABLE_ISOM | |
| if (has_sd) { | |
| u8 sample_index; | |
| GF_TextSampleDescriptor *txdesc; | |
| GF_Tx3gSampleEntryBox *a; | |
| s64 avail; | |
| u32 nb_desc = gf_bs_read_int(bs, 8); | |
| /*parse TTU[5]s*/ | |
| avail = (s64) gf_bs_available(bs); | |
| for (i=0; i<nb_desc; i++) { | |
| sample_index = gf_bs_read_int(bs, 8); | |
| avail -= 1; | |
| e = gf_isom_box_parse((GF_Box **) &a, bs); | |
| if (e) goto exit; | |
| avail -= (s32) a->size; | |
| if (avail<0) { | |
| e = GF_NON_COMPLIANT_BITSTREAM; | |
| goto exit; | |
| } | |
| txdesc = (GF_TextSampleDescriptor *)gf_malloc(sizeof(GF_TextSampleDescriptor)); | |
| txdesc->sample_index = sample_index; | |
| txdesc->displayFlags = a->displayFlags; | |
| txdesc->back_color = a->back_color; | |
| txdesc->default_pos = a->default_box; | |
| txdesc->default_style = a->default_style; | |
| txdesc->vert_justif = a->vertical_justification; | |
| txdesc->horiz_justif = a->horizontal_justification; | |
| txdesc->font_count = a->font_table ? a->font_table->entry_count : 0; | |
| if (txdesc->font_count) { | |
| txdesc->fonts = (GF_FontRecord*)gf_malloc(sizeof(GF_FontRecord)*txdesc->font_count); | |
| for (j=0; j<txdesc->font_count; j++) { | |
| txdesc->fonts[j].fontID = a->font_table->fonts[j].fontID; | |
| txdesc->fonts[j].fontName = a->font_table->fonts[j].fontName ? gf_strdup(a->font_table->fonts[j].fontName) : NULL; | |
| } | |
| } | |
| gf_list_add(cfg->sample_descriptions, txdesc); | |
| gf_isom_box_del((GF_Box *)a); | |
| } | |
| } | |
| #endif | |
| if (cfg->has_vid_info) { | |
| cfg->video_width = gf_bs_read_int(bs, 16); | |
| cfg->video_height = gf_bs_read_int(bs, 16); | |
| cfg->horiz_offset = gf_bs_read_int(bs, 16); | |
| cfg->vert_offset = gf_bs_read_int(bs, 16); | |
| } | |
| #ifndef GPAC_DISABLE_ISOM | |
| exit: | |
| #endif | |
| gf_bs_del(bs); | |
| if (e) ResetTextConfig(cfg); | |
| return e; | |
| } | |
| GF_EXPORT | |
| GF_HEVCConfig *gf_odf_hevc_cfg_new() | |
| { | |
| GF_HEVCConfig *cfg; | |
| GF_SAFEALLOC(cfg, GF_HEVCConfig); | |
| if (!cfg) return NULL; | |
| cfg->param_array = gf_list_new(); | |
| cfg->nal_unit_size = 4; | |
| return cfg; | |
| } | |
| GF_EXPORT | |
| void gf_odf_hevc_cfg_del(GF_HEVCConfig *cfg) | |
| { | |
| if (!cfg) return; | |
| while (gf_list_count(cfg->param_array)) { | |
| GF_NALUFFParamArray *pa = (GF_NALUFFParamArray*)gf_list_get(cfg->param_array, 0); | |
| gf_list_rem(cfg->param_array, 0); | |
| while (gf_list_count(pa->nalus)) { | |
| GF_NALUFFParam *n = (GF_NALUFFParam*)gf_list_get(pa->nalus, 0); | |
| gf_list_rem(pa->nalus, 0); | |
| if (n->data) gf_free(n->data); | |
| gf_free(n); | |
| } | |
| gf_list_del(pa->nalus); | |
| gf_free(pa); | |
| } | |
| gf_list_del(cfg->param_array); | |
| gf_free(cfg); | |
| } | |
| GF_EXPORT | |
| GF_Err gf_odf_hevc_cfg_write_bs(GF_HEVCConfig *cfg, GF_BitStream *bs) | |
| { | |
| u32 i, count; | |
| count = gf_list_count(cfg->param_array); | |
| if (!cfg->write_annex_b) { | |
| gf_bs_write_int(bs, cfg->configurationVersion, 8); | |
| if (!cfg->is_lhvc) { | |
| gf_bs_write_int(bs, cfg->profile_space, 2); | |
| gf_bs_write_int(bs, cfg->tier_flag, 1); | |
| gf_bs_write_int(bs, cfg->profile_idc, 5); | |
| gf_bs_write_int(bs, cfg->general_profile_compatibility_flags, 32); | |
| gf_bs_write_int(bs, cfg->progressive_source_flag, 1); | |
| gf_bs_write_int(bs, cfg->interlaced_source_flag, 1); | |
| gf_bs_write_int(bs, cfg->non_packed_constraint_flag, 1); | |
| gf_bs_write_int(bs, cfg->frame_only_constraint_flag, 1); | |
| /*only lowest 44 bits used*/ | |
| gf_bs_write_long_int(bs, cfg->constraint_indicator_flags, 44); | |
| gf_bs_write_int(bs, cfg->level_idc, 8); | |
| } | |
| gf_bs_write_int(bs, 0xFF, 4); | |
| gf_bs_write_int(bs, cfg->min_spatial_segmentation_idc, 12); | |
| gf_bs_write_int(bs, 0xFF, 6); | |
| gf_bs_write_int(bs, cfg->parallelismType, 2); | |
| if (!cfg->is_lhvc) { | |
| gf_bs_write_int(bs, 0xFF, 6); | |
| gf_bs_write_int(bs, cfg->chromaFormat, 2); | |
| gf_bs_write_int(bs, 0xFF, 5); | |
| gf_bs_write_int(bs, cfg->luma_bit_depth-8, 3); | |
| gf_bs_write_int(bs, 0xFF, 5); | |
| gf_bs_write_int(bs, cfg->chroma_bit_depth-8, 3); | |
| gf_bs_write_int(bs, cfg->avgFrameRate, 16); | |
| gf_bs_write_int(bs, cfg->constantFrameRate, 2); | |
| } else { | |
| gf_bs_write_int(bs, 0xFF, 2); | |
| } | |
| gf_bs_write_int(bs, cfg->numTemporalLayers, 3); | |
| gf_bs_write_int(bs, cfg->temporalIdNested, 1); | |
| gf_bs_write_int(bs, cfg->nal_unit_size - 1, 2); | |
| gf_bs_write_int(bs, count, 8); | |
| } | |
| for (i=0; i<count; i++) { | |
| u32 nalucount, j; | |
| GF_NALUFFParamArray *ar = (GF_NALUFFParamArray*)gf_list_get(cfg->param_array, i); | |
| nalucount = gf_list_count(ar->nalus); | |
| if (!cfg->write_annex_b) { | |
| gf_bs_write_int(bs, ar->array_completeness, 1); | |
| gf_bs_write_int(bs, 0, 1); | |
| gf_bs_write_int(bs, ar->type, 6); | |
| gf_bs_write_int(bs, nalucount, 16); | |
| } | |
| for (j=0; j<nalucount; j++) { | |
| GF_NALUFFParam *sl = (GF_NALUFFParam *)gf_list_get(ar->nalus, j); | |
| if (!cfg->write_annex_b) { | |
| gf_bs_write_int(bs, sl->size, 16); | |
| } else { | |
| gf_bs_write_u32(bs, 1); | |
| } | |
| gf_bs_write_data(bs, sl->data, sl->size); | |
| } | |
| } | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_odf_hevc_cfg_write(GF_HEVCConfig *cfg, u8 **outData, u32 *outSize) | |
| { | |
| GF_Err e; | |
| GF_BitStream *bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); | |
| *outSize = 0; | |
| *outData = NULL; | |
| e = gf_odf_hevc_cfg_write_bs(cfg, bs); | |
| if (e==GF_OK) | |
| gf_bs_get_content(bs, outData, outSize); | |
| gf_bs_del(bs); | |
| return e; | |
| } | |
| GF_EXPORT | |
| GF_HEVCConfig *gf_odf_hevc_cfg_read_bs(GF_BitStream *bs, Bool is_lhvc) | |
| { | |
| u32 i, count; | |
| GF_HEVCConfig *cfg = gf_odf_hevc_cfg_new(); | |
| cfg->is_lhvc = is_lhvc; | |
| cfg->configurationVersion = gf_bs_read_int(bs, 8); | |
| if (!is_lhvc) { | |
| cfg->profile_space = gf_bs_read_int(bs, 2); | |
| cfg->tier_flag = gf_bs_read_int(bs, 1); | |
| cfg->profile_idc = gf_bs_read_int(bs, 5); | |
| cfg->general_profile_compatibility_flags = gf_bs_read_int(bs, 32); | |
| cfg->progressive_source_flag = gf_bs_read_int(bs, 1); | |
| cfg->interlaced_source_flag = gf_bs_read_int(bs, 1); | |
| cfg->non_packed_constraint_flag = gf_bs_read_int(bs, 1); | |
| cfg->frame_only_constraint_flag = gf_bs_read_int(bs, 1); | |
| /*only lowest 44 bits used*/ | |
| cfg->constraint_indicator_flags = gf_bs_read_long_int(bs, 44); | |
| cfg->level_idc = gf_bs_read_int(bs, 8); | |
| } | |
| gf_bs_read_int(bs, 4); //reserved | |
| cfg->min_spatial_segmentation_idc = gf_bs_read_int(bs, 12); | |
| gf_bs_read_int(bs, 6);//reserved | |
| cfg->parallelismType = gf_bs_read_int(bs, 2); | |
| if (!is_lhvc) { | |
| gf_bs_read_int(bs, 6); | |
| cfg->chromaFormat = gf_bs_read_int(bs, 2); | |
| gf_bs_read_int(bs, 5); | |
| cfg->luma_bit_depth = gf_bs_read_int(bs, 3) + 8; | |
| gf_bs_read_int(bs, 5); | |
| cfg->chroma_bit_depth = gf_bs_read_int(bs, 3) + 8; | |
| cfg->avgFrameRate = gf_bs_read_int(bs, 16); | |
| cfg->constantFrameRate = gf_bs_read_int(bs, 2); | |
| } else { | |
| gf_bs_read_int(bs, 2); //reserved | |
| } | |
| cfg->numTemporalLayers = gf_bs_read_int(bs, 3); | |
| cfg->temporalIdNested = gf_bs_read_int(bs, 1); | |
| cfg->nal_unit_size = 1 + gf_bs_read_int(bs, 2); | |
| count = gf_bs_read_int(bs, 8); | |
| for (i=0; i<count; i++) { | |
| u32 nalucount, j; | |
| GF_NALUFFParamArray *ar; | |
| GF_SAFEALLOC(ar, GF_NALUFFParamArray); | |
| if (!ar) { | |
| gf_odf_hevc_cfg_del(cfg); | |
| return NULL; | |
| } | |
| ar->nalus = gf_list_new(); | |
| gf_list_add(cfg->param_array, ar); | |
| ar->array_completeness = gf_bs_read_int(bs, 1); | |
| gf_bs_read_int(bs, 1); | |
| ar->type = gf_bs_read_int(bs, 6); | |
| nalucount = gf_bs_read_int(bs, 16); | |
| for (j=0; j<nalucount; j++) { | |
| GF_NALUFFParam *sl; | |
| u32 size = gf_bs_read_int(bs, 16); | |
| if ((size>gf_bs_available(bs)) || (size<2)) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[HEVC] Wrong param set size %d\n", size)); | |
| gf_odf_hevc_cfg_del(cfg); | |
| return NULL; | |
| } | |
| GF_SAFEALLOC(sl, GF_NALUFFParam ); | |
| if (!sl) { | |
| gf_odf_hevc_cfg_del(cfg); | |
| return NULL; | |
| } | |
| sl->size = size; | |
| sl->data = (char *)gf_malloc(sizeof(char) * sl->size); | |
| gf_bs_read_data(bs, sl->data, sl->size); | |
| gf_list_add(ar->nalus, sl); | |
| } | |
| } | |
| return cfg; | |
| } | |
| GF_EXPORT | |
| GF_HEVCConfig *gf_odf_hevc_cfg_read(u8 *dsi, u32 dsi_size, Bool is_lhvc) | |
| { | |
| GF_BitStream *bs = gf_bs_new(dsi, dsi_size, GF_BITSTREAM_READ); | |
| GF_HEVCConfig *cfg = gf_odf_hevc_cfg_read_bs(bs, is_lhvc); | |
| gf_bs_del(bs); | |
| return cfg; | |
| } | |
| GF_EXPORT | |
| GF_VVCConfig *gf_odf_vvc_cfg_new() | |
| { | |
| GF_VVCConfig *cfg; | |
| GF_SAFEALLOC(cfg, GF_VVCConfig); | |
| if (!cfg) return NULL; | |
| cfg->param_array = gf_list_new(); | |
| cfg->nal_unit_size = 4; | |
| cfg->chroma_format = 1; | |
| cfg->bit_depth = 8; | |
| return cfg; | |
| } | |
| GF_EXPORT | |
| void gf_odf_vvc_cfg_del(GF_VVCConfig *cfg) | |
| { | |
| if (!cfg) return; | |
| while (gf_list_count(cfg->param_array)) { | |
| GF_NALUFFParamArray *pa = (GF_NALUFFParamArray*)gf_list_get(cfg->param_array, 0); | |
| gf_list_rem(cfg->param_array, 0); | |
| while (gf_list_count(pa->nalus)) { | |
| GF_NALUFFParam *n = (GF_NALUFFParam*)gf_list_get(pa->nalus, 0); | |
| gf_list_rem(pa->nalus, 0); | |
| if (n->data) gf_free(n->data); | |
| gf_free(n); | |
| } | |
| gf_list_del(pa->nalus); | |
| gf_free(pa); | |
| } | |
| gf_list_del(cfg->param_array); | |
| if (cfg->general_constraint_info) | |
| gf_free(cfg->general_constraint_info); | |
| if (cfg->sub_profiles_idc) | |
| gf_free(cfg->sub_profiles_idc); | |
| gf_free(cfg); | |
| } | |
| GF_EXPORT | |
| GF_Err gf_odf_vvc_cfg_write_bs(GF_VVCConfig *cfg, GF_BitStream *bs) | |
| { | |
| u32 i, count; | |
| count = gf_list_count(cfg->param_array); | |
| if (!cfg->write_annex_b) { | |
| gf_bs_write_int(bs, 0xFF, 5); | |
| gf_bs_write_int(bs, cfg->nal_unit_size - 1, 2); | |
| gf_bs_write_int(bs, cfg->ptl_present, 1); | |
| if (cfg->ptl_present) { | |
| s32 idx; | |
| gf_bs_write_int(bs, cfg->ols_idx, 9); | |
| gf_bs_write_int(bs, cfg->numTemporalLayers, 3); | |
| gf_bs_write_int(bs, cfg->constantFrameRate, 2); | |
| gf_bs_write_int(bs, cfg->chroma_format, 2); | |
| gf_bs_write_int(bs, cfg->bit_depth - 8, 3); | |
| gf_bs_write_int(bs, 0xFF, 5); | |
| if (!cfg->general_constraint_info) | |
| cfg->num_constraint_info = 0; | |
| //write PTL | |
| gf_bs_write_int(bs, 0, 2); | |
| gf_bs_write_int(bs, cfg->num_constraint_info, 6); | |
| gf_bs_write_int(bs, cfg->general_profile_idc, 7); | |
| gf_bs_write_int(bs, cfg->general_tier_flag, 1); | |
| gf_bs_write_u8(bs, cfg->general_level_idc); | |
| gf_bs_write_int(bs, cfg->ptl_frame_only_constraint, 1); | |
| gf_bs_write_int(bs, cfg->ptl_multilayer_enabled, 1); | |
| if (cfg->num_constraint_info) { | |
| gf_bs_write_data(bs, cfg->general_constraint_info, cfg->num_constraint_info - 1); | |
| gf_bs_write_int(bs, cfg->general_constraint_info[cfg->num_constraint_info - 1], 6); | |
| } else { | |
| gf_bs_write_int(bs, 0, 6); | |
| } | |
| for (idx=cfg->numTemporalLayers-2; idx>=0; idx--) { | |
| u8 val = cfg->ptl_sublayer_present_mask & (1<<idx); | |
| gf_bs_write_int(bs, val ? 1 : 0, 1); | |
| } | |
| for (idx=cfg->numTemporalLayers; idx<=8 && cfg->numTemporalLayers>1; idx++) { | |
| gf_bs_write_int(bs, 0, 1); | |
| } | |
| for (idx=cfg->numTemporalLayers-2; idx>=0; idx--) { | |
| if (cfg->ptl_sublayer_present_mask & (1<<idx)) | |
| gf_bs_write_u8(bs, cfg->sublayer_level_idc[idx]); | |
| } | |
| if (!cfg->sub_profiles_idc) cfg->num_sub_profiles = 0; | |
| gf_bs_write_u8(bs, cfg->num_sub_profiles); | |
| for (idx=0; idx<cfg->num_sub_profiles; idx++) { | |
| gf_bs_write_u32(bs, cfg->sub_profiles_idc[idx]); | |
| } | |
| //end PTL | |
| gf_bs_write_u16(bs, cfg->maxPictureWidth); | |
| gf_bs_write_u16(bs, cfg->maxPictureHeight); | |
| gf_bs_write_u16(bs, cfg->avgFrameRate); | |
| } | |
| gf_bs_write_int(bs, count, 8); | |
| } | |
| for (i=0; i<count; i++) { | |
| u32 nalucount, j; | |
| GF_NALUFFParamArray *ar = (GF_NALUFFParamArray*)gf_list_get(cfg->param_array, i); | |
| nalucount = gf_list_count(ar->nalus); | |
| if (!cfg->write_annex_b) { | |
| gf_bs_write_int(bs, ar->array_completeness, 1); | |
| gf_bs_write_int(bs, 0, 2); | |
| gf_bs_write_int(bs, ar->type, 5); | |
| if ((ar->type != GF_VVC_NALU_DEC_PARAM) && (ar->type != GF_VVC_NALU_OPI)) | |
| gf_bs_write_int(bs, nalucount, 16); | |
| else | |
| nalucount = 1; | |
| } | |
| for (j=0; j<nalucount; j++) { | |
| GF_NALUFFParam *sl = (GF_NALUFFParam *)gf_list_get(ar->nalus, j); | |
| if (!sl) | |
| return GF_ISOM_INVALID_MEDIA; | |
| if (!cfg->write_annex_b) { | |
| gf_bs_write_int(bs, sl->size, 16); | |
| } else { | |
| gf_bs_write_u32(bs, 1); | |
| } | |
| gf_bs_write_data(bs, sl->data, sl->size); | |
| } | |
| } | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_odf_vvc_cfg_write(GF_VVCConfig *cfg, u8 **outData, u32 *outSize) | |
| { | |
| GF_Err e; | |
| GF_BitStream *bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); | |
| *outSize = 0; | |
| *outData = NULL; | |
| e = gf_odf_vvc_cfg_write_bs(cfg, bs); | |
| if (e==GF_OK) | |
| gf_bs_get_content(bs, outData, outSize); | |
| gf_bs_del(bs); | |
| return e; | |
| } | |
| GF_EXPORT | |
| GF_VVCConfig *gf_odf_vvc_cfg_read_bs(GF_BitStream *bs) | |
| { | |
| u32 i, count; | |
| GF_VVCConfig *cfg = gf_odf_vvc_cfg_new(); | |
| gf_bs_read_int(bs, 5); | |
| cfg->nal_unit_size = 1 + gf_bs_read_int(bs, 2); | |
| cfg->ptl_present = gf_bs_read_int(bs, 1); | |
| if (cfg->ptl_present) { | |
| s32 j; | |
| cfg->ols_idx = gf_bs_read_int(bs, 9); | |
| cfg->numTemporalLayers = gf_bs_read_int(bs, 3); | |
| cfg->constantFrameRate = gf_bs_read_int(bs, 2); | |
| cfg->chroma_format = gf_bs_read_int(bs, 2); | |
| cfg->bit_depth = 8 + gf_bs_read_int(bs, 3); | |
| gf_bs_read_int(bs, 5); | |
| //parse PTL | |
| gf_bs_read_int(bs, 2); | |
| cfg->num_constraint_info = gf_bs_read_int(bs, 6); | |
| cfg->general_profile_idc = gf_bs_read_int(bs, 7); | |
| cfg->general_tier_flag = gf_bs_read_int(bs, 1); | |
| cfg->general_level_idc = gf_bs_read_u8(bs); | |
| cfg->ptl_frame_only_constraint = gf_bs_read_int(bs, 1); | |
| cfg->ptl_multilayer_enabled = gf_bs_read_int(bs, 1); | |
| if (cfg->num_constraint_info) { | |
| cfg->general_constraint_info = gf_malloc(sizeof(u8)*cfg->num_constraint_info); | |
| if (!cfg->general_constraint_info) { | |
| gf_free(cfg); | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[VVC] alloc failed while parsing vvc config\n")); | |
| return NULL; | |
| } | |
| gf_bs_read_data(bs, cfg->general_constraint_info, cfg->num_constraint_info - 1); | |
| cfg->general_constraint_info[cfg->num_constraint_info-1] = gf_bs_read_int(bs, 6); | |
| } else { | |
| //forbidden in spec! | |
| gf_bs_read_int(bs, 6); | |
| } | |
| cfg->ptl_sublayer_present_mask = 0; | |
| for (j=cfg->numTemporalLayers-2; j>=0; j--) { | |
| u32 val = gf_bs_read_int(bs, 1); | |
| cfg->ptl_sublayer_present_mask |= val << j; | |
| } | |
| for (j=cfg->numTemporalLayers; j<=8 && cfg->numTemporalLayers>1; j++) { | |
| gf_bs_read_int(bs, 1); | |
| } | |
| for (j=cfg->numTemporalLayers-2; j>=0; j--) { | |
| if (cfg->ptl_sublayer_present_mask & (1<<j)) { | |
| cfg->sublayer_level_idc[j] = gf_bs_read_u8(bs); | |
| } | |
| } | |
| cfg->num_sub_profiles = gf_bs_read_u8(bs); | |
| if (cfg->num_sub_profiles) { | |
| cfg->sub_profiles_idc = gf_malloc(sizeof(u32)*cfg->num_sub_profiles); | |
| if (!cfg->sub_profiles_idc) { | |
| gf_free(cfg->general_constraint_info); | |
| gf_free(cfg); | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[VVC] alloc failed while parsing vvc config\n")); | |
| return NULL; | |
| } | |
| } | |
| for (i=0; i<cfg->num_sub_profiles; i++) { | |
| cfg->sub_profiles_idc[i] = gf_bs_read_u32(bs); | |
| } | |
| //end PTL | |
| cfg->maxPictureWidth = gf_bs_read_u16(bs); | |
| cfg->maxPictureHeight = gf_bs_read_u16(bs); | |
| cfg->avgFrameRate = gf_bs_read_u16(bs); | |
| } | |
| count = gf_bs_read_int(bs, 8); | |
| for (i=0; i<count; i++) { | |
| u32 nalucount, j; | |
| Bool valid = GF_FALSE; | |
| GF_NALUFFParamArray *ar; | |
| GF_SAFEALLOC(ar, GF_NALUFFParamArray); | |
| if (!ar) { | |
| gf_odf_vvc_cfg_del(cfg); | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[VVC] alloc failed while parsing vvc config\n")); | |
| return NULL; | |
| } | |
| ar->array_completeness = gf_bs_read_int(bs, 1); | |
| gf_bs_read_int(bs, 2); | |
| ar->type = gf_bs_read_int(bs, 5); | |
| switch (ar->type) { | |
| case GF_VVC_NALU_DEC_PARAM: | |
| case GF_VVC_NALU_OPI: | |
| case GF_VVC_NALU_VID_PARAM: | |
| case GF_VVC_NALU_SEQ_PARAM: | |
| case GF_VVC_NALU_PIC_PARAM: | |
| case GF_VVC_NALU_SEI_PREFIX: | |
| case GF_VVC_NALU_SEI_SUFFIX: | |
| valid = GF_TRUE; | |
| ar->nalus = gf_list_new(); | |
| gf_list_add(cfg->param_array, ar); | |
| break; | |
| default: | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[VVC] Invalid NALU type %d in vvcC - ignoring\n", ar->type)); | |
| gf_free(ar); | |
| break; | |
| } | |
| if (!valid || ((ar->type != GF_VVC_NALU_DEC_PARAM) && (ar->type != GF_VVC_NALU_OPI))) | |
| nalucount = gf_bs_read_int(bs, 16); | |
| else | |
| nalucount = 1; | |
| for (j=0; j<nalucount; j++) { | |
| GF_NALUFFParam *sl; | |
| u32 size = gf_bs_read_int(bs, 16); | |
| if ((size>gf_bs_available(bs)) || (size<2)) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[VVC] Wrong param set size %d\n", size)); | |
| gf_odf_vvc_cfg_del(cfg); | |
| return NULL; | |
| } | |
| if (!valid) { | |
| gf_bs_skip_bytes(bs, size); | |
| continue; | |
| } | |
| GF_SAFEALLOC(sl, GF_NALUFFParam ); | |
| if (!sl) { | |
| gf_odf_vvc_cfg_del(cfg); | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[VVC] alloc failed while parsing vvc config\n")); | |
| return NULL; | |
| } | |
| sl->size = size; | |
| sl->data = (char *)gf_malloc(sizeof(char) * sl->size); | |
| if (!sl->data) { | |
| gf_free(sl); | |
| gf_odf_vvc_cfg_del(cfg); | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[VVC] alloc failed while parsing vvc config\n")); | |
| return NULL; | |
| } | |
| gf_bs_read_data(bs, sl->data, sl->size); | |
| gf_list_add(ar->nalus, sl); | |
| } | |
| } | |
| return cfg; | |
| } | |
| GF_EXPORT | |
| GF_VVCConfig *gf_odf_vvc_cfg_read(u8 *dsi, u32 dsi_size) | |
| { | |
| GF_BitStream *bs = gf_bs_new(dsi, dsi_size, GF_BITSTREAM_READ); | |
| GF_VVCConfig *cfg = gf_odf_vvc_cfg_read_bs(bs); | |
| gf_bs_del(bs); | |
| return cfg; | |
| } | |
| GF_EXPORT | |
| GF_AV1Config *gf_odf_av1_cfg_new() | |
| { | |
| GF_AV1Config *cfg; | |
| GF_SAFEALLOC(cfg, GF_AV1Config); | |
| if (!cfg) return NULL; | |
| cfg->marker = 1; | |
| cfg->version = 1; | |
| cfg->initial_presentation_delay_minus_one = 0; | |
| cfg->obu_array = gf_list_new(); | |
| return cfg; | |
| } | |
| GF_EXPORT | |
| void gf_odf_av1_cfg_del(GF_AV1Config *cfg) | |
| { | |
| if (!cfg) return; | |
| while (gf_list_count(cfg->obu_array)) { | |
| GF_AV1_OBUArrayEntry *a = (GF_AV1_OBUArrayEntry*)gf_list_get(cfg->obu_array, 0); | |
| if (a->obu) gf_free(a->obu); | |
| gf_list_rem(cfg->obu_array, 0); | |
| gf_free(a); | |
| } | |
| gf_list_del(cfg->obu_array); | |
| gf_free(cfg); | |
| } | |
| GF_EXPORT | |
| GF_Err gf_odf_av1_cfg_write_bs(GF_AV1Config *cfg, GF_BitStream *bs) | |
| { | |
| u32 i = 0; | |
| gf_bs_write_int(bs, cfg->marker, 1); | |
| gf_assert(cfg->marker == 1); | |
| gf_bs_write_int(bs, cfg->version, 7); | |
| gf_assert(cfg->version == 1); | |
| gf_bs_write_int(bs, cfg->seq_profile, 3); | |
| gf_bs_write_int(bs, cfg->seq_level_idx_0, 5); | |
| gf_bs_write_int(bs, cfg->seq_tier_0, 1); | |
| gf_bs_write_int(bs, cfg->high_bitdepth, 1); | |
| gf_bs_write_int(bs, cfg->twelve_bit, 1); | |
| gf_bs_write_int(bs, cfg->monochrome, 1); | |
| gf_bs_write_int(bs, cfg->chroma_subsampling_x, 1); | |
| gf_bs_write_int(bs, cfg->chroma_subsampling_y, 1); | |
| gf_bs_write_int(bs, cfg->chroma_sample_position, 2); | |
| gf_bs_write_int(bs, 0, 3); /*reserved*/ | |
| gf_bs_write_int(bs, cfg->initial_presentation_delay_present, 1); | |
| gf_bs_write_int(bs, cfg->initial_presentation_delay_minus_one, 4); /*TODO: compute initial_presentation_delay_minus_one*/ | |
| for (i = 0; i < gf_list_count(cfg->obu_array); ++i) { | |
| GF_AV1_OBUArrayEntry *a = gf_list_get(cfg->obu_array, i); | |
| gf_bs_write_data(bs, a->obu, (u32)a->obu_length); //TODO: we are supposed to omit the size on the last OBU... | |
| } | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_odf_av1_cfg_write(GF_AV1Config *cfg, u8 **outData, u32 *outSize) { | |
| GF_Err e; | |
| GF_BitStream *bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); | |
| *outSize = 0; | |
| *outData = NULL; | |
| e = gf_odf_av1_cfg_write_bs(cfg, bs); | |
| if (e == GF_OK) | |
| gf_bs_get_content(bs, outData, outSize); | |
| gf_bs_del(bs); | |
| return e; | |
| } | |
| GF_EXPORT | |
| GF_VPConfig *gf_odf_vp_cfg_new() | |
| { | |
| GF_VPConfig *cfg; | |
| GF_SAFEALLOC(cfg, GF_VPConfig); | |
| if (!cfg) return NULL; | |
| cfg->codec_initdata_size = 0; | |
| cfg->codec_initdata = NULL; | |
| return cfg; | |
| } | |
| GF_EXPORT | |
| void gf_odf_vp_cfg_del(GF_VPConfig *cfg) | |
| { | |
| if (!cfg) return; | |
| if (cfg->codec_initdata) { | |
| gf_free(cfg->codec_initdata); | |
| cfg->codec_initdata = NULL; | |
| } | |
| gf_free(cfg); | |
| } | |
| GF_EXPORT | |
| GF_Err gf_odf_vp_cfg_write_bs(GF_VPConfig *cfg, GF_BitStream *bs, Bool is_v0) | |
| { | |
| gf_bs_write_int(bs, cfg->profile, 8); | |
| gf_bs_write_int(bs, cfg->level, 8); | |
| gf_bs_write_int(bs, cfg->bit_depth, 4); | |
| gf_bs_write_int(bs, cfg->chroma_subsampling, 3); | |
| gf_bs_write_int(bs, cfg->video_fullRange_flag, 1); | |
| gf_bs_write_int(bs, cfg->colour_primaries, 8); | |
| gf_bs_write_int(bs, cfg->transfer_characteristics, 8); | |
| gf_bs_write_int(bs, cfg->matrix_coefficients, 8); | |
| if (!is_v0) { | |
| if (cfg->codec_initdata_size) { | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[VPX] Invalid data in configuration: codec_initdata_size must be 0, was %d - ignoring\n", cfg->codec_initdata_size)); | |
| } | |
| gf_bs_write_int(bs, (u16)0, 16); | |
| } | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_odf_vp_cfg_write(GF_VPConfig *cfg, u8 **outData, u32 *outSize, Bool is_v0) | |
| { | |
| GF_Err e; | |
| GF_BitStream *bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); | |
| *outSize = 0; | |
| *outData = NULL; | |
| e = gf_odf_vp_cfg_write_bs(cfg, bs, is_v0); | |
| if (e==GF_OK) | |
| gf_bs_get_content(bs, outData, outSize); | |
| gf_bs_del(bs); | |
| return e; | |
| } | |
| GF_EXPORT | |
| GF_VPConfig *gf_odf_vp_cfg_read_bs(GF_BitStream *bs, Bool is_v0) | |
| { | |
| GF_VPConfig *cfg = gf_odf_vp_cfg_new(); | |
| cfg->profile = gf_bs_read_int(bs, 8); | |
| cfg->level = gf_bs_read_int(bs, 8); | |
| cfg->bit_depth = gf_bs_read_int(bs, 4); | |
| cfg->chroma_subsampling = gf_bs_read_int(bs, 3); | |
| cfg->video_fullRange_flag = gf_bs_read_int(bs, 1); | |
| cfg->colour_primaries = gf_bs_read_int(bs, 8); | |
| cfg->transfer_characteristics = gf_bs_read_int(bs, 8); | |
| cfg->matrix_coefficients = gf_bs_read_int(bs, 8); | |
| if (is_v0) | |
| return cfg; | |
| cfg->codec_initdata_size = gf_bs_read_int(bs, 16); | |
| // must be 0 according to spec | |
| if (cfg->codec_initdata_size) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[VPX] Invalid data in configuration: codec_initdata_size must be 0, was %d\n", cfg->codec_initdata_size)); | |
| gf_odf_vp_cfg_del(cfg); | |
| return NULL; | |
| } | |
| return cfg; | |
| } | |
| GF_EXPORT | |
| GF_VPConfig *gf_odf_vp_cfg_read(u8 *dsi, u32 dsi_size) | |
| { | |
| GF_BitStream *bs = gf_bs_new(dsi, dsi_size, GF_BITSTREAM_READ); | |
| GF_VPConfig *cfg = gf_odf_vp_cfg_read_bs(bs, GF_FALSE); | |
| gf_bs_del(bs); | |
| return cfg; | |
| } | |
| GF_EXPORT | |
| GF_AV1Config *gf_odf_av1_cfg_read_bs_size(GF_BitStream *bs, u32 size) | |
| { | |
| #ifndef GPAC_DISABLE_AV_PARSERS | |
| AV1State *av1_state; | |
| u8 reserved; | |
| GF_AV1Config *cfg; | |
| if (!size) size = (u32) gf_bs_available(bs); | |
| if (!size) return NULL; | |
| GF_SAFEALLOC(av1_state, AV1State); | |
| if (!av1_state) return NULL; | |
| cfg = gf_odf_av1_cfg_new(); | |
| gf_av1_init_state(av1_state); | |
| av1_state->config = cfg; | |
| cfg->marker = gf_bs_read_int(bs, 1); | |
| cfg->version = gf_bs_read_int(bs, 7); | |
| cfg->seq_profile = gf_bs_read_int(bs, 3); | |
| cfg->seq_level_idx_0 = gf_bs_read_int(bs, 5); | |
| cfg->seq_tier_0 = gf_bs_read_int(bs, 1); | |
| cfg->high_bitdepth = gf_bs_read_int(bs, 1); | |
| cfg->twelve_bit = gf_bs_read_int(bs, 1); | |
| cfg->monochrome = gf_bs_read_int(bs, 1); | |
| cfg->chroma_subsampling_x = gf_bs_read_int(bs, 1); | |
| cfg->chroma_subsampling_y = gf_bs_read_int(bs, 1); | |
| cfg->chroma_sample_position = gf_bs_read_int(bs, 2); | |
| reserved = gf_bs_read_int(bs, 3); | |
| cfg->initial_presentation_delay_present = gf_bs_read_int(bs, 1); | |
| if (cfg->initial_presentation_delay_present) { | |
| cfg->initial_presentation_delay_minus_one = gf_bs_read_int(bs, 4); | |
| } else { | |
| /*reserved = */gf_bs_read_int(bs, 4); | |
| cfg->initial_presentation_delay_minus_one = 0; | |
| } | |
| size -= 4; | |
| if (reserved != 0 || cfg->marker != 1 || cfg->version != 1) { | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_CODING, ("[AV1] av1C: wrong reserved %d / marker %d / version %d expecting 0 1 1\n", reserved, cfg->marker, cfg->version)); | |
| gf_odf_av1_cfg_del(cfg); | |
| gf_free(av1_state); | |
| return NULL; | |
| } | |
| while (size) { | |
| u64 pos, obu_size; | |
| ObuType obu_type; | |
| GF_AV1_OBUArrayEntry *a; | |
| pos = gf_bs_get_position(bs); | |
| obu_size = 0; | |
| if (gf_av1_parse_obu(bs, &obu_type, &obu_size, NULL, av1_state) != GF_OK) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[AV1] av1C: could not parse AV1 OBU at position "LLU". Leaving parsing.\n", pos)); | |
| break; | |
| } | |
| gf_assert(obu_size == gf_bs_get_position(bs) - pos); | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_CODING, ("[AV1] av1C: parsed AV1 OBU type=%u size="LLU" at position "LLU".\n", obu_type, obu_size, pos)); | |
| if (!av1_is_obu_header(obu_type)) { | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_CODING, ("[AV1] av1C: AV1 unexpected OBU type=%u size="LLU" found at position "LLU". Forwarding.\n", pos)); | |
| } | |
| GF_SAFEALLOC(a, GF_AV1_OBUArrayEntry); | |
| if (!a) break; | |
| a->obu = gf_malloc((size_t)obu_size); | |
| if (!a->obu) { | |
| gf_free(a); | |
| break; | |
| } | |
| gf_bs_seek(bs, pos); | |
| gf_bs_read_data(bs, (char *) a->obu, (u32)obu_size); | |
| a->obu_length = obu_size; | |
| a->obu_type = obu_type; | |
| gf_list_add(cfg->obu_array, a); | |
| if (size<obu_size) { | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[AV1] AV1 config misses %d bytes to fit the entire OBU\n", obu_size - size)); | |
| break; | |
| } | |
| size -= (u32) obu_size; | |
| } | |
| gf_av1_reset_state(av1_state, GF_TRUE); | |
| gf_bs_align(bs); | |
| gf_free(av1_state); | |
| return cfg; | |
| #else | |
| return NULL; | |
| #endif | |
| } | |
| GF_EXPORT | |
| GF_AV1Config *gf_odf_av1_cfg_read_bs(GF_BitStream *bs) | |
| { | |
| return gf_odf_av1_cfg_read_bs_size(bs, 0); | |
| } | |
| GF_EXPORT | |
| GF_AV1Config *gf_odf_av1_cfg_read(u8 *dsi, u32 dsi_size) | |
| { | |
| GF_BitStream *bs = gf_bs_new(dsi, dsi_size, GF_BITSTREAM_READ); | |
| GF_AV1Config *cfg = gf_odf_av1_cfg_read_bs(bs); | |
| gf_bs_del(bs); | |
| return cfg; | |
| } | |
| GF_EXPORT | |
| GF_AVS3VConfig *gf_odf_avs3v_cfg_new() | |
| { | |
| GF_AVS3VConfig *cfg; | |
| GF_SAFEALLOC(cfg, GF_AVS3VConfig); | |
| if (!cfg) return NULL; | |
| return cfg; | |
| } | |
| GF_EXPORT | |
| void gf_odf_avs3v_cfg_del(GF_AVS3VConfig *cfg) | |
| { | |
| if (!cfg) return; | |
| if (cfg->sequence_header) gf_free(cfg->sequence_header); | |
| gf_free(cfg); | |
| } | |
| GF_EXPORT | |
| GF_Err gf_odf_avs3v_cfg_write_bs(GF_AVS3VConfig *cfg, GF_BitStream *bs) | |
| { | |
| gf_bs_write_int(bs, cfg->configurationVersion, 8); | |
| gf_bs_write_int(bs, cfg->sequence_header_length, 16); | |
| gf_bs_write_data(bs, cfg->sequence_header, cfg->sequence_header_length); | |
| gf_bs_write_int(bs, 0xFF, 6); //reserved | |
| gf_bs_write_int(bs, cfg->library_dependency_idc, 2); | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_odf_avs3v_cfg_write(GF_AVS3VConfig *cfg, u8 **outData, u32 *outSize) | |
| { | |
| GF_Err e; | |
| GF_BitStream *bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); | |
| *outSize = 0; | |
| *outData = NULL; | |
| e = gf_odf_avs3v_cfg_write_bs(cfg, bs); | |
| if (e==GF_OK) | |
| gf_bs_get_content(bs, outData, outSize); | |
| gf_bs_del(bs); | |
| return e; | |
| } | |
| GF_EXPORT | |
| GF_AVS3VConfig *gf_odf_avs3v_cfg_read_bs(GF_BitStream *bs) | |
| { | |
| GF_AVS3VConfig *cfg = gf_odf_avs3v_cfg_new(); | |
| cfg->configurationVersion = gf_bs_read_int(bs, 8); | |
| cfg->sequence_header_length = gf_bs_read_int(bs, 16); | |
| cfg->sequence_header = gf_malloc(cfg->sequence_header_length); | |
| if (!cfg->sequence_header) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[AVS3 Video] Sequence header allocation failed\n")); | |
| gf_odf_avs3v_cfg_del(cfg); | |
| return NULL; | |
| } | |
| gf_bs_read_data(bs, (char *)cfg->sequence_header, (u32)cfg->sequence_header_length); | |
| gf_bs_read_int(bs, 6); //reserved | |
| cfg->library_dependency_idc = gf_bs_read_int(bs, 2); // 6 bits reserved at '1' + 2 bits | |
| return cfg; | |
| } | |
| GF_EXPORT | |
| GF_AVS3VConfig *gf_odf_avs3v_cfg_read(u8 *dsi, u32 dsi_size) | |
| { | |
| GF_BitStream *bs = gf_bs_new(dsi, dsi_size, GF_BITSTREAM_READ); | |
| GF_AVS3VConfig *cfg = gf_odf_avs3v_cfg_read_bs(bs); | |
| gf_bs_del(bs); | |
| return cfg; | |
| } | |
| GF_EXPORT | |
| GF_DOVIDecoderConfigurationRecord *gf_odf_dovi_cfg_read_bs(GF_BitStream *bs) | |
| { | |
| GF_DOVIDecoderConfigurationRecord *cfg; | |
| GF_SAFEALLOC(cfg, GF_DOVIDecoderConfigurationRecord); | |
| cfg->dv_version_major = gf_bs_read_u8(bs); | |
| cfg->dv_version_minor = gf_bs_read_u8(bs); | |
| cfg->dv_profile = gf_bs_read_int(bs, 7); | |
| cfg->dv_level = gf_bs_read_int(bs, 6); | |
| cfg->rpu_present_flag = gf_bs_read_int(bs, 1); | |
| cfg->el_present_flag = gf_bs_read_int(bs, 1); | |
| cfg->bl_present_flag = gf_bs_read_int(bs, 1); | |
| cfg->dv_bl_signal_compatibility_id = gf_bs_read_int(bs, 4); | |
| if (gf_bs_read_int(bs, 28)) { | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[DOVI] Configuration reserved bits are not zero\n")); | |
| } | |
| for (u32 i=0; i<4; i++) { | |
| if (gf_bs_read_u32(bs)) { | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[DOVII] Configuration reserved bits are not zero\n")); | |
| } | |
| } | |
| return cfg; | |
| } | |
| GF_EXPORT | |
| void gf_odf_dovi_cfg_del(GF_DOVIDecoderConfigurationRecord *cfg) | |
| { | |
| gf_free(cfg); | |
| } | |
| GF_EXPORT | |
| GF_Err gf_odf_dovi_cfg_write_bs(GF_DOVIDecoderConfigurationRecord *cfg, GF_BitStream *bs) | |
| { | |
| gf_bs_write_u8(bs, cfg->dv_version_major); | |
| gf_bs_write_u8(bs, cfg->dv_version_minor); | |
| gf_bs_write_int(bs, cfg->dv_profile, 7); | |
| gf_bs_write_int(bs, cfg->dv_level, 6); | |
| gf_bs_write_int(bs, cfg->rpu_present_flag, 1); | |
| gf_bs_write_int(bs, cfg->el_present_flag, 1); | |
| gf_bs_write_int(bs, cfg->bl_present_flag, 1); | |
| gf_bs_write_int(bs, cfg->dv_bl_signal_compatibility_id, 4); | |
| gf_bs_write_int(bs, 0, 28); | |
| gf_bs_write_u32(bs, 0); | |
| gf_bs_write_u32(bs, 0); | |
| gf_bs_write_u32(bs, 0); | |
| gf_bs_write_u32(bs, 0); | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_odf_ac3_cfg_write_bs(GF_AC3Config *cfg, GF_BitStream *bs) | |
| { | |
| if (!cfg || !bs) return GF_BAD_PARAM; | |
| if (cfg->is_ec3) { | |
| u32 i; | |
| gf_bs_write_int(bs, cfg->brcode, 13); | |
| gf_bs_write_int(bs, cfg->nb_streams - 1, 3); | |
| for (i=0; i<cfg->nb_streams; i++) { | |
| gf_bs_write_int(bs, cfg->streams[i].fscod, 2); | |
| gf_bs_write_int(bs, cfg->streams[i].bsid, 5); | |
| gf_bs_write_int(bs, cfg->streams[i].bsmod, 5); | |
| gf_bs_write_int(bs, cfg->streams[i].acmod, 3); | |
| gf_bs_write_int(bs, cfg->streams[i].lfon, 1); | |
| gf_bs_write_int(bs, 0, 3); | |
| gf_bs_write_int(bs, cfg->streams[i].nb_dep_sub, 4); | |
| if (cfg->streams[i].nb_dep_sub) { | |
| gf_bs_write_int(bs, cfg->streams[i].chan_loc, 9); | |
| } else { | |
| gf_bs_write_int(bs, 0, 1); | |
| } | |
| } | |
| } else { | |
| gf_bs_write_int(bs, cfg->streams[0].fscod, 2); | |
| gf_bs_write_int(bs, cfg->streams[0].bsid, 5); | |
| gf_bs_write_int(bs, cfg->streams[0].bsmod, 3); | |
| gf_bs_write_int(bs, cfg->streams[0].acmod, 3); | |
| gf_bs_write_int(bs, cfg->streams[0].lfon, 1); | |
| gf_bs_write_int(bs, cfg->brcode, 5); | |
| gf_bs_write_int(bs, 0, 5); | |
| } | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_odf_ac3_cfg_write(GF_AC3Config *cfg, u8 **data, u32 *size) | |
| { | |
| GF_BitStream *bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); | |
| GF_Err e = gf_odf_ac3_cfg_write_bs(cfg, bs); | |
| if (cfg->is_ec3 && (cfg->atmos_ec3_ext || cfg->complexity_index_type)) { | |
| gf_bs_write_int(bs, 0, 7); | |
| gf_bs_write_int(bs, cfg->atmos_ec3_ext, 1); | |
| gf_bs_write_u8(bs, cfg->complexity_index_type); | |
| } | |
| gf_bs_get_content(bs, data, size); | |
| gf_bs_del(bs); | |
| return e; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_odf_ac3_cfg_parse_bs(GF_BitStream *bs, Bool is_ec3, GF_AC3Config *cfg) | |
| { | |
| if (!cfg || !bs) return GF_BAD_PARAM; | |
| memset(cfg, 0, sizeof(GF_AC3Config)); | |
| cfg->is_ec3 = is_ec3; | |
| if (is_ec3) { | |
| u32 j; | |
| cfg->is_ec3 = 1; | |
| cfg->brcode = gf_bs_read_int(bs, 13); | |
| cfg->nb_streams = 1 + gf_bs_read_int(bs, 3); | |
| for (j=0; j<cfg->nb_streams; j++) { | |
| cfg->streams[j].fscod = gf_bs_read_int(bs, 2); | |
| cfg->streams[j].bsid = gf_bs_read_int(bs, 5); | |
| gf_bs_read_int(bs, 1); | |
| cfg->streams[j].asvc = gf_bs_read_int(bs, 1); | |
| cfg->streams[j].bsmod = gf_bs_read_int(bs, 3); | |
| cfg->streams[j].acmod = gf_bs_read_int(bs, 3); | |
| cfg->streams[j].lfon = gf_bs_read_int(bs, 1); | |
| gf_bs_read_int(bs, 3); | |
| cfg->streams[j].nb_dep_sub = gf_bs_read_int(bs, 4); | |
| if (cfg->streams[j].nb_dep_sub) { | |
| cfg->streams[j].chan_loc = gf_bs_read_int(bs, 9); | |
| } else { | |
| gf_bs_read_int(bs, 1); | |
| } | |
| } | |
| } else { | |
| cfg->nb_streams = 1; | |
| cfg->streams[0].fscod = gf_bs_read_int(bs, 2); | |
| cfg->streams[0].bsid = gf_bs_read_int(bs, 5); | |
| cfg->streams[0].bsmod = gf_bs_read_int(bs, 3); | |
| cfg->streams[0].acmod = gf_bs_read_int(bs, 3); | |
| cfg->streams[0].lfon = gf_bs_read_int(bs, 1); | |
| cfg->brcode = gf_bs_read_int(bs, 5); | |
| gf_bs_read_int(bs, 5); | |
| } | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_odf_ac3_cfg_parse(u8 *dsi, u32 dsi_len, Bool is_ec3, GF_AC3Config *cfg) | |
| { | |
| GF_BitStream *bs; | |
| GF_Err e; | |
| if (!cfg || !dsi) return GF_BAD_PARAM; | |
| bs = gf_bs_new(dsi, dsi_len, GF_BITSTREAM_READ); | |
| e = gf_odf_ac3_cfg_parse_bs(bs, is_ec3, cfg); | |
| if (is_ec3 && gf_bs_available(bs)>=2) { | |
| gf_bs_read_int(bs, 7); | |
| cfg->atmos_ec3_ext = gf_bs_read_int(bs, 1); | |
| cfg->complexity_index_type = gf_bs_read_u8(bs); | |
| } | |
| gf_bs_del(bs); | |
| return e; | |
| } | |
| #define GF_AC4_SSS(bs, value, nbits, size, mode) { \ | |
| if (mode == GF_AC4_DESCMODE_PARSE) value = gf_bs_read_int(bs, nbits); \ | |
| else if (mode == GF_AC4_DESCMODE_WRITE) gf_bs_write_int(bs, value, nbits); \ | |
| else if (mode == GF_AC4_DESCMODE_GETSIZE) *size += nbits;} | |
| #define GF_AC4_ALLIGN(bs, size, mode) { \ | |
| if (mode == GF_AC4_DESCMODE_PARSE) gf_bs_align(bs); \ | |
| else if (mode == GF_AC4_DESCMODE_WRITE) gf_bs_align(bs); \ | |
| else if (mode == GF_AC4_DESCMODE_GETSIZE && (*size % 8 != 0)) *size = ((*size / 8) + 1) * 8;} | |
| GF_Err gf_odf_ac4_cfg_alternative_info(GF_AC4AlternativeInfo *info, GF_BitStream *bs, u64 *size, u8 desc_mode) | |
| { | |
| u32 i; | |
| GF_AC4_SSS(bs, info->name_len, 16, size, desc_mode); | |
| if (info->name_len >= GF_ARRAY_LENGTH(info->presentation_name)) | |
| return GF_ISOM_INVALID_MEDIA; | |
| for (i = 0; i < info->name_len; i++) { | |
| GF_AC4_SSS(bs, info->presentation_name[i], 8, size, desc_mode); | |
| } | |
| GF_AC4_SSS(bs, info->n_targets, 5, size, desc_mode); | |
| if (info->n_targets >= MIN(GF_ARRAY_LENGTH(info->target_md_compat), GF_ARRAY_LENGTH(info->target_device_category))) | |
| return GF_ISOM_INVALID_MEDIA; | |
| for (i = 0; i < info->n_targets; i++) { | |
| GF_AC4_SSS(bs, info->target_md_compat[i], 3, size, desc_mode); | |
| GF_AC4_SSS(bs, info->target_device_category[i], 8, size, desc_mode); | |
| } | |
| return GF_OK; | |
| } | |
| GF_Err gf_odf_ac4_cfg_substream_dsi(GF_AC4SubStream *s, GF_BitStream *bs, u8 b_channel_coded, u64 *size, u8 desc_mode) | |
| { | |
| u32 zero_val = 0; | |
| GF_AC4_SSS(bs, s->dsi_sf_multiplier, 2, size, desc_mode); | |
| GF_AC4_SSS(bs, s->b_substream_bitrate_indicator, 1, size, desc_mode); | |
| if (s->b_substream_bitrate_indicator == 1) { | |
| GF_AC4_SSS(bs, s->substream_bitrate_indicator, 5, size, desc_mode); | |
| } | |
| if (b_channel_coded == 1) { | |
| GF_AC4_SSS(bs, zero_val, 6, size, desc_mode); //reserved zero | |
| GF_AC4_SSS(bs, s->dsi_substream_channel_groups, 18, size, desc_mode); | |
| } else { | |
| GF_AC4_SSS(bs, s->b_ajoc, 1, size, desc_mode); | |
| if (s->b_ajoc == 1) { | |
| GF_AC4_SSS(bs, s->b_static_dmx, 1, size, desc_mode); | |
| if (s->b_static_dmx == 0) { | |
| GF_AC4_SSS(bs, s->n_dmx_objects_minus1, 4, size, desc_mode); | |
| } | |
| GF_AC4_SSS(bs, s->n_umx_objects_minus1, 6, size, desc_mode); | |
| } | |
| GF_AC4_SSS(bs, s->b_substream_contains_bed_objects, 1, size, desc_mode); | |
| GF_AC4_SSS(bs, s->b_substream_contains_dynamic_objects, 1, size, desc_mode); | |
| GF_AC4_SSS(bs, s->b_substream_contains_ISF_objects, 1, size, desc_mode); | |
| GF_AC4_SSS(bs, zero_val, 1, size, desc_mode); //reserved bit | |
| } | |
| return GF_OK; | |
| } | |
| GF_Err gf_odf_ac4_cfg_content_type(GF_AC4SubStreamGroupV1 *g, GF_BitStream *bs, u64 *size, u8 desc_mode) | |
| { | |
| u32 i; | |
| GF_AC4_SSS(bs, g->b_content_type, 1, size, desc_mode); | |
| if (g->b_content_type == 1){ | |
| GF_AC4_SSS(bs, g->content_classifier, 3, size, desc_mode); | |
| GF_AC4_SSS(bs, g->b_language_indicator, 1, size, desc_mode); | |
| if (g->b_language_indicator == 1){ | |
| GF_AC4_SSS(bs, g->n_language_tag_bytes, 6, size, desc_mode); | |
| for (i = 0; i < g->n_language_tag_bytes; i++ ){ | |
| GF_AC4_SSS(bs, g->language_tag_bytes[i], 8, size, desc_mode); | |
| } | |
| } | |
| } | |
| return GF_OK; | |
| } | |
| GF_Err gf_odf_ac4_cfg_substream_group_dsi(GF_AC4SubStreamGroupV1 *g, GF_BitStream *bs, u64 *size, u8 desc_mode) | |
| { | |
| u32 i; | |
| GF_AC4SubStream *s; | |
| if (!g) | |
| return GF_BAD_PARAM; | |
| GF_AC4_SSS(bs, g->b_substreams_present, 1, size, desc_mode); | |
| GF_AC4_SSS(bs, g->b_hsf_ext, 1, size, desc_mode); | |
| GF_AC4_SSS(bs, g->b_channel_coded, 1, size, desc_mode); | |
| GF_AC4_SSS(bs, g->n_lf_substreams, 8, size, desc_mode); | |
| if (desc_mode == GF_AC4_DESCMODE_PARSE) { | |
| g->substreams = gf_list_new(); | |
| } | |
| for (i = 0; i < g->n_lf_substreams; i++ ){ | |
| if (desc_mode == GF_AC4_DESCMODE_PARSE) { | |
| GF_SAFEALLOC(s, GF_AC4SubStream); | |
| } else { // write or get_size | |
| s = (GF_AC4SubStream*)gf_list_get(g->substreams, i); | |
| } | |
| gf_odf_ac4_cfg_substream_dsi(s, bs, g->b_channel_coded, size, desc_mode); | |
| if (desc_mode == GF_AC4_DESCMODE_PARSE) { | |
| gf_list_add(g->substreams, s); | |
| } | |
| } | |
| gf_odf_ac4_cfg_content_type(g, bs, size, desc_mode); | |
| return GF_OK; | |
| } | |
| GF_Err gf_odf_ac4_cfg_bitrate_dsi(GF_AC4BitrateDsi *bitr, GF_BitStream *bs, u64 *size, u8 desc_mode) | |
| { | |
| GF_AC4_SSS(bs, bitr->bit_rate_mode, 2, size, desc_mode); | |
| GF_AC4_SSS(bs, bitr->bit_rate, 32, size, desc_mode); | |
| GF_AC4_SSS(bs, bitr->bit_rate_precision, 32, size, desc_mode); | |
| return GF_OK; | |
| } | |
| GF_Err gf_odf_ac4_cfg_presentation_v1_dsi(GF_AC4PresentationV1 *p, GF_BitStream *bs, u64 *size, u8 desc_mode) | |
| { | |
| GF_AC4SubStreamGroupV1 *g; | |
| u32 i, zero_val = 0; | |
| GF_AC4_SSS(bs, p->presentation_config, 5, size, desc_mode); | |
| if (p->presentation_config == 0x06) { | |
| p->b_add_emdf_substreams = 1; | |
| } else { | |
| GF_AC4_SSS(bs, p->md_compat, 3, size, desc_mode); | |
| GF_AC4_SSS(bs, p->b_presentation_id, 1, size, desc_mode); | |
| if (p->b_presentation_id) { | |
| GF_AC4_SSS(bs, p->presentation_id, 5, size, desc_mode); | |
| } | |
| GF_AC4_SSS(bs, p->dsi_frame_rate_multiply_info, 2, size, desc_mode); | |
| GF_AC4_SSS(bs, p->dsi_frame_rate_fraction_info, 2, size, desc_mode); | |
| GF_AC4_SSS(bs, p->presentation_emdf_version, 5, size, desc_mode); | |
| GF_AC4_SSS(bs, p->presentation_key_id, 10, size, desc_mode); | |
| GF_AC4_SSS(bs, p->b_presentation_channel_coded, 1, size, desc_mode); | |
| if (p->b_presentation_channel_coded) { | |
| GF_AC4_SSS(bs, p->dsi_presentation_ch_mode, 5, size, desc_mode); | |
| if (p->dsi_presentation_ch_mode >= 11 && p->dsi_presentation_ch_mode <= 14) { | |
| GF_AC4_SSS(bs, p->pres_b_4_back_channels_present, 1, size, desc_mode); | |
| GF_AC4_SSS(bs, p->pres_top_channel_pairs, 2, size, desc_mode); | |
| } | |
| GF_AC4_SSS(bs, zero_val, 6, size, desc_mode); // reserved zero | |
| GF_AC4_SSS(bs, p->presentation_v1_channel_groups, 18, size, desc_mode); | |
| } | |
| GF_AC4_SSS(bs, p->b_presentation_core_differs, 1, size, desc_mode); | |
| if (p->b_presentation_core_differs) { | |
| GF_AC4_SSS(bs, p->b_presentation_core_channel_coded, 1, size, desc_mode); | |
| if (p->b_presentation_core_channel_coded) { | |
| GF_AC4_SSS(bs, p->dsi_presentation_channel_mode_core, 2, size, desc_mode); | |
| } | |
| } | |
| GF_AC4_SSS(bs, p->b_presentation_filter, 1, size, desc_mode); | |
| if (p->b_presentation_filter) { | |
| GF_AC4_SSS(bs, p->b_enable_presentation, 1, size, desc_mode); | |
| GF_AC4_SSS(bs, p->n_filter_bytes, 8, size, desc_mode); | |
| for (i = 0; i < p->n_filter_bytes; i++) { | |
| GF_AC4_SSS(bs, zero_val, 8, size, desc_mode); // filter_data | |
| } | |
| } | |
| // calloc memory for substream_groups | |
| if (desc_mode == GF_AC4_DESCMODE_PARSE) { | |
| p->substream_groups = gf_list_new(); | |
| } | |
| if (p->presentation_config == 0x1f) { | |
| if (desc_mode == GF_AC4_DESCMODE_PARSE) { | |
| GF_SAFEALLOC(g, GF_AC4SubStreamGroupV1); | |
| gf_list_add(p->substream_groups, g); | |
| p->n_substream_groups = 1; | |
| } else { // write or get_size | |
| g = (GF_AC4SubStreamGroupV1*)gf_list_get(p->substream_groups, 0); | |
| } | |
| if (g) | |
| gf_odf_ac4_cfg_substream_group_dsi(g, bs, size, desc_mode); | |
| } | |
| else { | |
| GF_AC4_SSS(bs, p->b_multi_pid, 1, size, desc_mode); | |
| if (p->presentation_config >= 0 && p->presentation_config <= 2) { | |
| p->n_substream_groups = 2; | |
| } | |
| if (p->presentation_config == 3 || p->presentation_config == 4) { | |
| p->n_substream_groups = 3; | |
| } | |
| if (p->presentation_config == 5) { | |
| // n_substream_groups_minus2 | |
| if (desc_mode == GF_AC4_DESCMODE_PARSE) | |
| p->n_substream_groups = gf_bs_read_int(bs, 3) + 2; | |
| else if (desc_mode == GF_AC4_DESCMODE_WRITE) | |
| gf_bs_write_int(bs, p->n_substream_groups - 2, 3); | |
| else if(desc_mode == GF_AC4_DESCMODE_GETSIZE) | |
| *size += 3; | |
| } | |
| for (i = 0; i < p->n_substream_groups; i++) { | |
| if (desc_mode == GF_AC4_DESCMODE_PARSE) { | |
| GF_SAFEALLOC(g, GF_AC4SubStreamGroupV1); | |
| gf_list_add(p->substream_groups, g); | |
| } else { // write or get_size | |
| g = (GF_AC4SubStreamGroupV1*)gf_list_get(p->substream_groups, i); | |
| } | |
| gf_odf_ac4_cfg_substream_group_dsi(g, bs, size, desc_mode); | |
| } | |
| if (p->presentation_config > 5) { | |
| GF_AC4_SSS(bs, p->n_skip_bytes, 7, size, desc_mode); | |
| for (i = 0; i < p->n_skip_bytes; i++) { | |
| GF_AC4_SSS(bs, zero_val, 8, size, desc_mode); // skip_data | |
| } | |
| } | |
| } | |
| GF_AC4_SSS(bs, p->b_pre_virtualized, 1, size, desc_mode); | |
| GF_AC4_SSS(bs, p->b_add_emdf_substreams, 1, size, desc_mode); | |
| } | |
| if (p->b_add_emdf_substreams) { | |
| GF_AC4_SSS(bs, p->n_add_emdf_substreams, 7, size, desc_mode); | |
| for (i = 0; i < p->n_add_emdf_substreams && i < GF_ARRAY_LENGTH(p->substream_emdf_version) && i < GF_ARRAY_LENGTH(p->substream_key_id); i++) { | |
| GF_AC4_SSS(bs, p->substream_emdf_version[i], 5, size, desc_mode); | |
| GF_AC4_SSS(bs, p->substream_key_id[i], 10, size, desc_mode); | |
| } | |
| } | |
| GF_AC4_SSS(bs, p->b_presentation_bitrate_info, 1, size, desc_mode); | |
| if (p->b_presentation_bitrate_info) { | |
| gf_odf_ac4_cfg_bitrate_dsi(&(p->ac4_bitrate_dsi), bs, size, desc_mode); | |
| } | |
| GF_AC4_SSS(bs, p->b_alternative, 1, size, desc_mode); | |
| if (p->b_alternative) { | |
| GF_AC4_ALLIGN(bs, size, desc_mode); | |
| gf_odf_ac4_cfg_alternative_info(&(p->alternative_info), bs, size, desc_mode); | |
| } | |
| GF_AC4_ALLIGN(bs, size, desc_mode); | |
| /* | |
| * TODO: Not implement, need the information from ac4_substream. | |
| * Currently just set the value to 1 according to Dolby's internal discussion. | |
| */ | |
| GF_AC4_SSS(bs, p->de_indicator, 1, size, desc_mode); | |
| GF_AC4_SSS(bs, p->immersive_audio_indicator, 1, size, desc_mode); | |
| GF_AC4_SSS(bs, zero_val, 4, size, desc_mode); | |
| if (p->presentation_id > 31) { | |
| p->b_extended_presentation_id = 1; | |
| p->extended_presentation_id = p->presentation_id; | |
| } | |
| GF_AC4_SSS(bs, p->b_extended_presentation_id, 1, size, desc_mode); | |
| if (p->b_extended_presentation_id) { | |
| GF_AC4_SSS(bs, p->extended_presentation_id, 9, size, desc_mode); | |
| } | |
| else { | |
| GF_AC4_SSS(bs, zero_val, 1, size, desc_mode); | |
| } | |
| return GF_OK; | |
| } | |
| static void gf_odf_ac4_presentation_deep_copy(GF_AC4PresentationV1 *pres_dst, GF_AC4PresentationV1 *pres_src); | |
| GF_Err gf_odf_ac4_cfg_dsi_v1(GF_AC4StreamInfo *dsi, GF_BitStream *bs, u64 *size, u8 desc_mode) | |
| { | |
| u32 i, j, add_pres_bytes, presentation_bytes, skip_bytes, ims_pres_num = 0, legacy_pres_num = 0; | |
| u32 pres_bytes = 0, t_size_bytes = 0; | |
| GF_AC4PresentationV1* p = NULL, *imsp = NULL; | |
| u64 pos, t_size_bits = 0; | |
| u8 *t_data = NULL; | |
| GF_BitStream *t_bs; | |
| if (!dsi) | |
| return GF_BAD_PARAM; | |
| GF_AC4_SSS(bs, dsi->ac4_dsi_version, 3, size, desc_mode); | |
| GF_AC4_SSS(bs, dsi->bitstream_version, 7, size, desc_mode); | |
| GF_AC4_SSS(bs, dsi->fs_index, 1, size, desc_mode); | |
| GF_AC4_SSS(bs, dsi->frame_rate_index, 4, size, desc_mode); | |
| if (desc_mode == GF_AC4_DESCMODE_WRITE) { | |
| // check whether legacy presentations are added in the presentations | |
| for (i = 0; i < dsi->n_presentations; i++) { | |
| p = gf_list_get(dsi->presentations, i); | |
| if (!p) continue; | |
| if (p->presentation_version == 1) { | |
| legacy_pres_num += 1; | |
| } else if (p->presentation_version == 2) { | |
| ims_pres_num += 1; | |
| } | |
| } | |
| // In WRITE mode, modify n_presentations for IMS content and add legacy presentations | |
| // For more information, please read Dolby AC-4 Online Delivery Kit - Signaling immersive stereo content | |
| if (legacy_pres_num == 0 && ims_pres_num > 0) { | |
| GF_LOG(GF_LOG_INFO, GF_LOG_APP, ("[AC4] This is a Dolby AC-4 bitstreams signal immersive stereo content.\n")); | |
| for (i = 0; i < dsi->n_presentations; i++) { | |
| p = gf_list_get(dsi->presentations, i); | |
| if (!p) continue; | |
| if (p->presentation_version == 2) { | |
| GF_SAFEALLOC(imsp, GF_AC4PresentationV1); | |
| gf_odf_ac4_presentation_deep_copy(imsp, p); | |
| imsp->presentation_version = 1; | |
| imsp->b_pre_virtualized = 0; | |
| imsp->immersive_audio_indicator = 0; | |
| gf_list_add(dsi->presentations, imsp); | |
| } | |
| } | |
| dsi->n_presentations += ims_pres_num; | |
| } | |
| } | |
| GF_AC4_SSS(bs, dsi->n_presentations, 9, size, desc_mode); | |
| if (dsi->bitstream_version > 1) { | |
| GF_AC4_SSS(bs, dsi->b_program_id, 1, size, desc_mode); | |
| if (dsi->b_program_id) { | |
| GF_AC4_SSS(bs, dsi->short_program_id, 16, size, desc_mode); | |
| GF_AC4_SSS(bs, dsi->b_uuid, 1, size, desc_mode); | |
| if (dsi->b_uuid) { | |
| for (i = 0; i < 16; i++) | |
| GF_AC4_SSS(bs, dsi->program_uuid[i], 8, size, desc_mode); | |
| } | |
| } | |
| } | |
| // ac4_bitrate_dsi | |
| gf_odf_ac4_cfg_bitrate_dsi(&dsi->ac4_bitrate_dsi, bs, size, desc_mode); | |
| GF_AC4_ALLIGN(bs, size, desc_mode); | |
| if (desc_mode == GF_AC4_DESCMODE_PARSE) { | |
| dsi->presentations = gf_list_new(); | |
| } | |
| for (i = 0; i < dsi->n_presentations; i++) { | |
| if (desc_mode == GF_AC4_DESCMODE_PARSE) { | |
| GF_SAFEALLOC(p, GF_AC4PresentationV1); | |
| gf_list_add(dsi->presentations, p); | |
| p->presentation_version = gf_bs_read_int(bs, 8); | |
| pres_bytes = gf_bs_read_int(bs, 8); | |
| if (pres_bytes == 255) { | |
| add_pres_bytes = gf_bs_read_int(bs, 16); | |
| pres_bytes += add_pres_bytes; | |
| } | |
| pos = gf_bs_get_position(bs); | |
| if (p->presentation_version == 0) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_APP, ("[AC4] Don't support presentation_version 0.\n")); | |
| } else if (p->presentation_version == 1) { | |
| gf_odf_ac4_cfg_presentation_v1_dsi(p, bs, size, desc_mode); | |
| } else if (p->presentation_version == 2) { | |
| gf_odf_ac4_cfg_presentation_v1_dsi(p, bs, size, desc_mode); | |
| } | |
| presentation_bytes = (u32) (gf_bs_get_position(bs) - pos); | |
| skip_bytes = pres_bytes - presentation_bytes; | |
| for (j = 0; j < skip_bytes && gf_bs_available(bs); j++) { | |
| gf_bs_read_int(bs, 8); | |
| } | |
| } | |
| else if (desc_mode == GF_AC4_DESCMODE_WRITE) { | |
| p = gf_list_get(dsi->presentations, i); | |
| if (!p) continue; | |
| t_bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); | |
| if (p->presentation_version == 0) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_APP, ("[AC4] Don't support presentation_version 0.\n")); | |
| } else if (p->presentation_version == 1 || p->presentation_version == 2) { | |
| gf_odf_ac4_cfg_presentation_v1_dsi(p, t_bs, size, desc_mode); | |
| } | |
| // write into output bitstream | |
| gf_bs_write_int(bs, p->presentation_version, 8); | |
| presentation_bytes = (u32) gf_bs_get_position(t_bs); | |
| if (presentation_bytes < 255) { | |
| gf_bs_write_int(bs, presentation_bytes, 8); | |
| } else { | |
| gf_bs_write_int(bs, 255, 8); | |
| gf_bs_write_int(bs, presentation_bytes - 255, 16); | |
| } | |
| gf_bs_get_content(t_bs, &t_data, &t_size_bytes); | |
| gf_bs_write_data(bs, t_data, t_size_bytes); | |
| gf_bs_del(t_bs); | |
| gf_free(t_data); | |
| } | |
| else if (desc_mode == GF_AC4_DESCMODE_GETSIZE) { | |
| p = gf_list_get(dsi->presentations, i); | |
| t_size_bits = 0; // t_size in bits | |
| if (p->presentation_version == 0) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_APP, ("[AC4] Don't support presentation_version 0.\n")); | |
| return GF_OK; | |
| } else if (p->presentation_version == 1 || p->presentation_version == 2) { | |
| gf_odf_ac4_cfg_presentation_v1_dsi(p, bs, &t_size_bits, desc_mode); | |
| } | |
| if (t_size_bits < 255 * 8) { | |
| *size += (8 + 8 + t_size_bits); | |
| } else { | |
| *size += (8 + 8 + 16 + t_size_bits); | |
| } | |
| } | |
| } | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_odf_ac4_cfg_write_bs(GF_AC4Config *cfg, GF_BitStream *bs) | |
| { | |
| if (!cfg || !bs) return GF_BAD_PARAM; | |
| GF_AC4StreamInfo* dsi = &cfg->stream; | |
| if (dsi->ac4_dsi_version == 0) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_APP, ("[AC4] Don't support ac4_dsi_version 0.\n")); | |
| return GF_OK; | |
| } | |
| GF_Err e = gf_odf_ac4_cfg_dsi_v1(dsi, bs, NULL, GF_AC4_DESCMODE_WRITE); | |
| return e; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_odf_ac4_cfg_write(GF_AC4Config *cfg, u8 **data, u32 *size) | |
| { | |
| GF_BitStream *bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); | |
| GF_Err e = gf_odf_ac4_cfg_write_bs(cfg, bs); | |
| gf_bs_get_content(bs, data, size); | |
| gf_bs_del(bs); | |
| return e; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_odf_ac4_cfg_parse_bs(GF_BitStream *bs, GF_AC4Config *cfg) | |
| { | |
| GF_AC4StreamInfo* dsi = &cfg->stream; | |
| u64 pos = gf_bs_get_position(bs); | |
| dsi->ac4_dsi_version = gf_bs_read_int(bs, 3); | |
| if (dsi->ac4_dsi_version == 0) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_APP, ("[AC4] Don't support ac4_dsi_version 0.\n")); | |
| gf_bs_seek(bs, pos); | |
| return GF_OK; | |
| } | |
| gf_bs_seek(bs, pos); | |
| GF_Err e = gf_odf_ac4_cfg_dsi_v1(dsi, bs, NULL, GF_AC4_DESCMODE_PARSE); | |
| if (e) return e; | |
| cfg->sample_rate = dsi->fs_index ? 48000 : 44100; | |
| return e; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_odf_ac4_cfg_parse(u8 *dsi, u32 dsi_len, GF_AC4Config *cfg) | |
| { | |
| GF_BitStream *bs; | |
| GF_Err e; | |
| if (!cfg || !dsi) return GF_BAD_PARAM; | |
| bs = gf_bs_new(dsi, dsi_len, GF_BITSTREAM_READ); | |
| e = gf_odf_ac4_cfg_parse_bs(bs, cfg); | |
| gf_bs_del(bs); | |
| return e; | |
| } | |
| GF_EXPORT | |
| u64 gf_odf_ac4_cfg_size(GF_AC4Config *cfg) | |
| { | |
| GF_AC4StreamInfo* dsi = &cfg->stream; | |
| u64 size = 0; | |
| if (dsi->ac4_dsi_version == 0) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_APP, ("[AC4] Don't support ac4_dsi_version 0.\n")); | |
| return 0; | |
| } else { | |
| gf_odf_ac4_cfg_dsi_v1(dsi, NULL, &size, GF_AC4_DESCMODE_GETSIZE); | |
| } | |
| return size / 8; | |
| } | |
| GF_EXPORT | |
| void gf_odf_ac4_cfg_deep_copy(GF_AC4Config *dst, GF_AC4Config *src) | |
| { | |
| u32 i; | |
| GF_List *presentations_src = src->stream.presentations; | |
| GF_AC4PresentationV1 *pres_dst, *pres_src; | |
| if (!dst || !src) { | |
| return; | |
| } | |
| memcpy(dst, src, sizeof(GF_AC4Config)); | |
| if (!src->stream.presentations) { | |
| return; | |
| } | |
| dst->stream.presentations = gf_list_new(); | |
| for (i = 0; i < gf_list_count(presentations_src); i++) { | |
| pres_src = gf_list_get(presentations_src, i); | |
| GF_SAFEALLOC(pres_dst, GF_AC4PresentationV1); | |
| gf_odf_ac4_presentation_deep_copy(pres_dst, pres_src); | |
| gf_list_add(dst->stream.presentations, pres_dst); | |
| } | |
| } | |
| static void gf_odf_ac4_presentation_deep_copy(GF_AC4PresentationV1 *pres_dst, GF_AC4PresentationV1 *pres_src) | |
| { | |
| u32 j, s; | |
| GF_AC4SubStreamGroupV1 *group_dst, *group_src; | |
| GF_AC4SubStream *subs_dst, *subs_src; | |
| if (!pres_dst || !pres_src) { | |
| return; | |
| } | |
| memcpy(pres_dst, pres_src, sizeof(GF_AC4PresentationV1)); | |
| if (!pres_src->substream_groups) { | |
| return; | |
| } | |
| pres_dst->substream_groups = gf_list_new(); | |
| for (j = 0; j < gf_list_count(pres_src->substream_groups); j++) { | |
| group_src = gf_list_get(pres_src->substream_groups, j); | |
| if (!group_src) { | |
| continue; | |
| } | |
| GF_SAFEALLOC(group_dst, GF_AC4SubStreamGroupV1); | |
| memcpy(group_dst, group_src, sizeof(GF_AC4SubStreamGroupV1)); | |
| gf_list_add(pres_dst->substream_groups, group_dst); | |
| if (!group_src->substreams) { | |
| continue; | |
| } | |
| group_dst->substreams = gf_list_new(); | |
| for (s = 0; s < gf_list_count(group_src->substreams); s++) { | |
| subs_src = gf_list_get(group_src->substreams, s); | |
| if (!subs_src) { | |
| continue; | |
| } | |
| GF_SAFEALLOC(subs_dst, GF_AC4SubStream); | |
| memcpy(subs_dst, subs_src, sizeof(GF_AC4SubStream)); | |
| gf_list_add(group_dst->substreams, subs_dst); | |
| } | |
| } | |
| if (!pres_src->substreams) { | |
| return; | |
| } | |
| pres_dst->substreams = gf_list_new(); | |
| for (j = 0; j < gf_list_count(pres_src->substreams); j++) { | |
| subs_src = gf_list_get(pres_src->substreams, j); | |
| if (!subs_src) { | |
| continue; | |
| } | |
| GF_SAFEALLOC(subs_dst, GF_AC4SubStream); | |
| memcpy(subs_dst, subs_src, sizeof(GF_AC4SubStream)); | |
| gf_list_add(pres_dst->substreams, subs_dst); | |
| } | |
| } | |
| GF_EXPORT | |
| void gf_odf_ac4_cfg_clean_list(GF_AC4Config *cfg) | |
| { | |
| GF_AC4PresentationV1 *pres; | |
| GF_AC4SubStreamGroupV1 *group; | |
| GF_AC4SubStream *subs; | |
| if (!cfg) { | |
| return; | |
| } | |
| if (cfg->stream.presentations) { | |
| GF_List* groups_to_del = gf_list_new(); | |
| GF_List* substreams_to_del = gf_list_new(); | |
| while ( (pres = gf_list_pop_back(cfg->stream.presentations)) ) { | |
| if (pres->substream_groups) { | |
| while ( (group = gf_list_pop_back(pres->substream_groups)) ) { | |
| if (group && gf_list_find(groups_to_del, group) < 0) { | |
| gf_list_add(groups_to_del, group); | |
| } | |
| } | |
| gf_list_del(pres->substream_groups); | |
| } | |
| if (pres->substreams) { | |
| while ( (subs = gf_list_pop_back(pres->substreams)) ) { | |
| if (subs && gf_list_find(substreams_to_del, subs) < 0) { | |
| gf_list_add(substreams_to_del, subs); | |
| } | |
| } | |
| gf_list_del(pres->substreams); | |
| } | |
| gf_free(pres); | |
| } | |
| gf_list_del(cfg->stream.presentations); | |
| cfg->stream.presentations = NULL; | |
| while ( (group = gf_list_pop_back(groups_to_del)) ) { | |
| if (group->substreams) { | |
| while ( (subs = gf_list_pop_back(group->substreams)) ) { | |
| if (subs && gf_list_find(substreams_to_del, subs) < 0) { | |
| gf_list_add(substreams_to_del, subs); | |
| } | |
| } | |
| gf_list_del(group->substreams); | |
| } | |
| gf_free(group); | |
| } | |
| gf_list_del(groups_to_del); | |
| while ( (subs = gf_list_pop_back(substreams_to_del)) ) { | |
| gf_free(subs); | |
| } | |
| gf_list_del(substreams_to_del); | |
| } | |
| } | |
| GF_EXPORT | |
| void gf_odf_ac4_cfg_del(GF_AC4Config *cfg) | |
| { | |
| if (!cfg) return; | |
| gf_odf_ac4_cfg_clean_list(cfg); | |
| gf_free(cfg); | |
| } | |
| GF_EXPORT | |
| GF_Err gf_odf_opus_cfg_parse_bs(GF_BitStream *bs, GF_OpusConfig *cfg) | |
| { | |
| memset(cfg, 0, sizeof(GF_OpusConfig)); | |
| cfg->version = gf_bs_read_u8(bs); | |
| cfg->OutputChannelCount = gf_bs_read_u8(bs); | |
| cfg->PreSkip = gf_bs_read_u16_le(bs); | |
| cfg->InputSampleRate = gf_bs_read_u32_le(bs); | |
| cfg->OutputGain = gf_bs_read_u16_le(bs); | |
| cfg->ChannelMappingFamily = gf_bs_read_u8(bs); | |
| if (cfg->ChannelMappingFamily) { | |
| cfg->StreamCount = gf_bs_read_u8(bs); | |
| cfg->CoupledCount = gf_bs_read_u8(bs); | |
| gf_bs_read_data(bs, (char *) cfg->ChannelMapping, cfg->OutputChannelCount); | |
| } | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_odf_opus_cfg_parse(u8 *dsi, u32 dsi_len, GF_OpusConfig *cfg) | |
| { | |
| GF_BitStream *bs; | |
| GF_Err e; | |
| if (!cfg || !dsi) return GF_BAD_PARAM; | |
| bs = gf_bs_new(dsi, dsi_len, GF_BITSTREAM_READ); | |
| e = gf_odf_opus_cfg_parse_bs(bs, cfg); | |
| gf_bs_del(bs); | |
| return e; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_odf_opus_cfg_write_bs(GF_OpusConfig *cfg, GF_BitStream *bs) | |
| { | |
| if (!cfg || !bs) return GF_BAD_PARAM; | |
| gf_bs_write_u8(bs, cfg->version); | |
| gf_bs_write_u8(bs, cfg->OutputChannelCount); | |
| gf_bs_write_u16_le(bs, cfg->PreSkip); | |
| gf_bs_write_u32_le(bs, cfg->InputSampleRate); | |
| gf_bs_write_u16_le(bs, cfg->OutputGain); | |
| gf_bs_write_u8(bs, cfg->ChannelMappingFamily); | |
| if (cfg->ChannelMappingFamily) { | |
| gf_bs_write_u8(bs, cfg->StreamCount); | |
| gf_bs_write_u8(bs, cfg->CoupledCount); | |
| gf_bs_write_data(bs, (char *) cfg->ChannelMapping, cfg->OutputChannelCount); | |
| } | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_odf_opus_cfg_write(GF_OpusConfig *cfg, u8 **data, u32 *size) | |
| { | |
| GF_BitStream *bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); | |
| GF_Err e = gf_odf_opus_cfg_write_bs(cfg, bs); | |
| gf_bs_get_content(bs, data, size); | |
| gf_bs_del(bs); | |
| return e; | |
| } | |
| GF_EXPORT | |
| GF_IAConfig *gf_odf_iamf_cfg_new() | |
| { | |
| GF_IAConfig *cfg = NULL; | |
| GF_SAFEALLOC(cfg, GF_IAConfig); | |
| if (!cfg) return NULL; | |
| cfg->configurationVersion = 1; | |
| cfg->configOBUs_size = 0; | |
| cfg->configOBUs = gf_list_new(); | |
| if (!cfg->configOBUs) { | |
| gf_free(cfg); | |
| return NULL; | |
| } | |
| return cfg; | |
| } | |
| GF_EXPORT | |
| GF_IAConfig *gf_odf_iamf_cfg_read_bs_size(GF_BitStream *bs, u32 size) { | |
| #ifndef GPAC_DISABLE_AV_PARSERS | |
| IAMFState *state = NULL; | |
| GF_IAConfig *cfg = NULL; | |
| u8 leb128_size; | |
| if (!size) size = (u32) gf_bs_available(bs); | |
| if (!size) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[IAMF] Unknown IAConfigurationBox size to read\n")); | |
| return NULL; | |
| } | |
| GF_SAFEALLOC(state, IAMFState); | |
| if (!state) return NULL; | |
| cfg = gf_odf_iamf_cfg_new(); | |
| gf_iamf_init_state(state); | |
| cfg->configurationVersion = gf_bs_read_u8(bs); | |
| if (cfg->configurationVersion != 1) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[IAMF] Unknown configurationVersion %d\n", cfg->configurationVersion)); | |
| gf_odf_iamf_cfg_del(cfg); | |
| gf_free(state); | |
| return NULL; | |
| } | |
| size--; | |
| cfg->configOBUs_size = (u32)gf_av1_leb128_read(bs, &leb128_size); | |
| size -= leb128_size; | |
| while(size) { | |
| u64 pos, obu_size; | |
| IamfObuType obu_type; | |
| GF_IamfObu *config_obu; | |
| pos = gf_bs_get_position(bs); | |
| obu_size = 0; | |
| if (gf_iamf_parse_obu(bs, &obu_type, &obu_size, state) != GF_OK) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[IAMF] could not parse configOBUs at position "LLU". Leaving parsing.\n", pos)); | |
| break; | |
| } | |
| gf_assert(obu_size == gf_bs_get_position(bs) - pos); | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_CODING, ("[IAMF] parsed OBU type=%u size="LLU" at position "LLU".\n", obu_type, obu_size, pos)); | |
| GF_SAFEALLOC(config_obu, GF_IamfObu); | |
| if (!config_obu) break; | |
| config_obu->raw_obu_bytes = gf_malloc((size_t)obu_size); | |
| if (!config_obu->raw_obu_bytes) { | |
| gf_free(config_obu); | |
| break; | |
| } | |
| gf_bs_seek(bs, pos); | |
| gf_bs_read_data(bs, (char *)config_obu->raw_obu_bytes, (u32)obu_size); | |
| config_obu->obu_length = obu_size; | |
| config_obu->obu_type = obu_type; | |
| gf_list_add(cfg->configOBUs, config_obu); | |
| if (size < obu_size) { | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[IAMF] IAMF config misses %d bytes to fit the entire OBU\n", obu_size - size)); | |
| break; | |
| } | |
| size -= (u32) obu_size; | |
| } | |
| gf_iamf_reset_state(state, GF_TRUE); | |
| gf_bs_align(bs); | |
| gf_free(state); | |
| return cfg; | |
| #else | |
| return NULL; | |
| #endif | |
| } | |
| GF_EXPORT | |
| GF_IAConfig *gf_odf_iamf_cfg_read_bs(GF_BitStream *bs) { | |
| return gf_odf_iamf_cfg_read_bs_size(bs, 0); | |
| } | |
| GF_EXPORT | |
| GF_IAConfig *gf_odf_iamf_cfg_read(u8 *dsi, u32 dsi_size) | |
| { | |
| GF_BitStream *bs = gf_bs_new(dsi, dsi_size, GF_BITSTREAM_READ); | |
| GF_IAConfig *cfg = gf_odf_iamf_cfg_read_bs(bs); | |
| gf_bs_del(bs); | |
| return cfg; | |
| } | |
| GF_EXPORT | |
| void gf_odf_iamf_cfg_del(GF_IAConfig *cfg) | |
| { | |
| if (!cfg) return; | |
| while (gf_list_count(cfg->configOBUs)) { | |
| GF_IamfObu *configOBU = (GF_IamfObu*)gf_list_get(cfg->configOBUs, 0); | |
| if (configOBU->raw_obu_bytes) gf_free(configOBU->raw_obu_bytes); | |
| gf_list_rem(cfg->configOBUs, 0); | |
| gf_free(configOBU); | |
| } | |
| gf_list_del(cfg->configOBUs); | |
| gf_free(cfg); | |
| } | |
| GF_EXPORT | |
| GF_Err gf_odf_iamf_cfg_write_obus(GF_IAConfig *cfg, GF_BitStream *bs) | |
| { | |
| u32 i; | |
| if (!cfg || !bs) return GF_BAD_PARAM; | |
| #ifndef GPAC_DISABLE_AV_PARSERS | |
| for (i = 0; i < gf_list_count(cfg->configOBUs); ++i) { | |
| GF_IamfObu *configOBU = gf_list_get(cfg->configOBUs, i); | |
| gf_bs_write_data(bs, configOBU->raw_obu_bytes, (u32)configOBU->obu_length); | |
| } | |
| #endif | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_odf_iamf_cfg_write_bs(GF_IAConfig *cfg, GF_BitStream *bs) | |
| { | |
| if (!cfg || !bs) return GF_BAD_PARAM; | |
| #ifndef GPAC_DISABLE_AV_PARSERS | |
| gf_bs_write_u8(bs, cfg->configurationVersion); | |
| gf_av1_leb128_write(bs, cfg->configOBUs_size); | |
| return gf_odf_iamf_cfg_write_obus(cfg, bs); | |
| #endif | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_odf_iamf_cfg_write(GF_IAConfig *cfg, u8 **outData, u32 *outSize) { | |
| GF_Err e; | |
| GF_BitStream *bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); | |
| *outSize = 0; | |
| *outData = NULL; | |
| e = gf_odf_iamf_cfg_write_bs(cfg, bs); | |
| if (e == GF_OK) | |
| gf_bs_get_content(bs, outData, outSize); | |
| gf_bs_del(bs); | |
| return e; | |
| } | |
| GF_EXPORT | |
| u32 gf_odf_iamf_cfg_size(GF_IAConfig *cfg) | |
| { | |
| if (!cfg) return 0; | |
| #ifndef GPAC_DISABLE_AV_PARSERS | |
| u32 cfg_size = 1; // configurationVersion | |
| cfg_size += gf_av1_leb128_size(cfg->configOBUs_size); | |
| cfg_size += cfg->configOBUs_size; | |
| return cfg_size; | |
| #else | |
| return 0; | |
| #endif | |
| } | |
| char* gf_odf_preselection_obtain_str(GF_BitStream *bs, u32 size) | |
| { | |
| char *data, *ptr; | |
| u32 i = 0; | |
| GF_SAFE_ALLOC_N(data, (size) + 1, char); | |
| while (gf_bs_available(bs) && i < (size)) { | |
| data[i] = (char) gf_bs_read_u8(bs); | |
| if (data[i] == '\0') break; | |
| i++; | |
| } | |
| data[(size)] = '\0'; | |
| ptr = gf_strdup(data); | |
| gf_free(data); | |
| return ptr; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_odf_preselection_cfg_parse_bs(GF_BitStream *bs, GF_List *cfg_list) | |
| { | |
| GF_Err e = GF_OK; | |
| GF_PreselectionConfig *cfg; | |
| GF_Box *child; | |
| GF_PreselectionGroupBox *prsl; | |
| GF_AudioRenderingIndicationBox *ardi; | |
| GF_ExtendedLanguageBox *elng; | |
| GF_UserDataBox *udta; | |
| GF_DialogueProcessingBox *diap; | |
| GF_LabelBox *labl; | |
| GF_KindBox *kind; | |
| u32 size = 0, type = 0; | |
| if (!cfg_list) return GF_BAD_PARAM; | |
| while(gf_bs_available(bs)) { | |
| size = gf_bs_read_u32(bs); | |
| type = gf_bs_read_u32(bs); | |
| if (type == GF_4CC('p', 'r', 's', 'l') && size >= 8) { | |
| prsl = (GF_PreselectionGroupBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_PRSL); | |
| prsl->size = size - 8; | |
| prsl->type = type; | |
| e = gf_isom_box_read((GF_Box *)prsl, bs); | |
| if (e) { | |
| gf_isom_box_del((GF_Box *)prsl); | |
| return e; | |
| } | |
| GF_SAFEALLOC(cfg, GF_PreselectionConfig); | |
| if (!cfg) { | |
| gf_isom_box_del((GF_Box *)prsl); | |
| return GF_OUT_OF_MEM; | |
| } | |
| cfg->flags = prsl->flags; | |
| cfg->group_id = prsl->group_id; | |
| if ((prsl->flags & GF_ISOM_PRESELECTION_TAG_PRESENT) && prsl->preselection_tag) { | |
| cfg->preselection_tag = gf_strdup(prsl->preselection_tag); | |
| } | |
| if (prsl->flags & GF_ISOM_SELECTION_PRIORITY_PRESENT) { | |
| cfg->selection_priority = prsl->selection_priority; | |
| } | |
| if ((prsl->flags & GF_ISOM_INTERLEAVING_TAG_PRESENT) && prsl->interleaving_tag) { | |
| cfg->interleaving_tag = gf_strdup(prsl->interleaving_tag); | |
| } | |
| for (u32 i = 0 ; i < gf_list_count(prsl->child_boxes); i++) { | |
| child = (GF_Box*)gf_list_get(prsl->child_boxes, i); | |
| if (!child) continue; | |
| if (child->type == GF_ISOM_BOX_TYPE_ARDI) { | |
| ardi = (GF_AudioRenderingIndicationBox *) child; | |
| cfg->audio_rendering_indication = ardi->audio_rendering_indication; | |
| } | |
| else if (child->type == GF_ISOM_BOX_TYPE_ELNG) { | |
| elng = (GF_ExtendedLanguageBox *) child; | |
| cfg->extended_language = gf_strdup(elng->extended_language); | |
| } | |
| else if (child->type == GF_ISOM_BOX_TYPE_UDTA) { | |
| GF_UserDataMap *map; | |
| u32 i = 0; | |
| udta = (GF_UserDataBox *) child; | |
| while ((map = (GF_UserDataMap *)gf_list_enum(udta->recordList, &i))) { | |
| if (map->boxType == GF_ISOM_BOX_TYPE_DIAP) { | |
| diap = (GF_DialogueProcessingBox *) gf_list_get(map->boxes, 0); | |
| if (diap) { | |
| cfg->dialog_gain_present = GF_TRUE; | |
| cfg->dialog_gain = diap->dialog_gain; | |
| } | |
| } | |
| } | |
| } | |
| else if (child->type == GF_ISOM_BOX_TYPE_LABL) { | |
| labl = (GF_LabelBox *) child; | |
| GF_Label *label; | |
| GF_SAFEALLOC(label, GF_Label); | |
| if (!label) { | |
| gf_isom_box_del((GF_Box *)prsl); | |
| return GF_OUT_OF_MEM; | |
| } | |
| label->is_group_label = labl->flags & GF_ISOM_IS_GROUP_LABEL ? GF_TRUE : GF_FALSE; | |
| label->label_id = labl->label_id; | |
| label->language = gf_strdup(labl->language); | |
| label->label = gf_strdup(labl->label); | |
| if (!cfg->labels) { | |
| cfg->labels = gf_list_new(); | |
| if (!cfg->labels) { | |
| gf_isom_box_del((GF_Box *)prsl); | |
| return GF_OUT_OF_MEM; | |
| } | |
| } | |
| gf_list_add(cfg->labels, label); | |
| } | |
| else if (child->type == GF_ISOM_BOX_TYPE_KIND) { | |
| kind = (GF_KindBox *) child; | |
| GF_Kind *k; | |
| GF_SAFEALLOC(k, GF_Kind); | |
| if (!k) { | |
| gf_isom_box_del((GF_Box *)prsl); | |
| return GF_OUT_OF_MEM; | |
| } | |
| k->value = kind->value ? gf_strdup(kind->value) : NULL; | |
| k->schemeURI = gf_strdup(kind->schemeURI); | |
| if (!cfg->kinds) { | |
| cfg->kinds = gf_list_new(); | |
| if (!cfg->kinds) { | |
| gf_isom_box_del((GF_Box *)prsl); | |
| return GF_OUT_OF_MEM; | |
| } | |
| } | |
| gf_list_add(cfg->kinds, k); | |
| } | |
| } | |
| gf_list_add(cfg_list, cfg); | |
| gf_isom_box_del((GF_Box *)prsl); | |
| } | |
| else { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_APP, ("[Preselection] Failed to parse the preselection.\n")); | |
| return GF_NOT_SUPPORTED; | |
| } | |
| } | |
| return e; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_odf_preselection_cfg_parse(u8 *data, u32 data_len, GF_List *cfg_list) | |
| { | |
| GF_BitStream *bs; | |
| GF_Err e; | |
| if (!cfg_list || !data) return GF_BAD_PARAM; | |
| bs = gf_bs_new(data, data_len, GF_BITSTREAM_READ); | |
| e = gf_odf_preselection_cfg_parse_bs(bs, cfg_list); | |
| gf_bs_del(bs); | |
| return e; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_odf_preselection_cfg_write(GF_List *cfg_list, GF_BitStream *bs) | |
| { | |
| GF_Err e = GF_OK; | |
| GF_PreselectionConfig *cfg; | |
| GF_PreselectionGroupBox *prsl; | |
| GF_AudioRenderingIndicationBox *ardi; | |
| GF_ExtendedLanguageBox *elng; | |
| GF_UserDataBox *udta; | |
| GF_DialogueProcessingBox *diap; | |
| GF_LabelBox *labl; | |
| GF_KindBox *kind; | |
| if (!cfg_list || !bs) return GF_BAD_PARAM; | |
| for (u32 i=0; i<gf_list_count(cfg_list); i++) { | |
| cfg = (GF_PreselectionConfig*)gf_list_get(cfg_list, i); | |
| if (!cfg) return GF_BAD_PARAM; | |
| prsl = (GF_PreselectionGroupBox*)gf_isom_box_new(GF_ISOM_BOX_TYPE_PRSL); | |
| if (!prsl) return GF_OUT_OF_MEM; | |
| prsl->flags = cfg->flags; | |
| prsl->group_id = cfg->group_id; | |
| // use 0 as entity ID | |
| prsl->entity_id_count = 1; | |
| prsl->entity_ids = gf_malloc(prsl->entity_id_count * sizeof(u32)); | |
| if (!prsl->entity_ids) return GF_OUT_OF_MEM; | |
| prsl->entity_ids[0] = 0; | |
| if ((cfg->flags & GF_ISOM_PRESELECTION_TAG_PRESENT) && cfg->preselection_tag) { | |
| prsl->preselection_tag = gf_strdup(cfg->preselection_tag); | |
| } | |
| if (cfg->flags & GF_ISOM_SELECTION_PRIORITY_PRESENT) { | |
| prsl->selection_priority = cfg->selection_priority; | |
| } | |
| if ((cfg->flags & GF_ISOM_INTERLEAVING_TAG_PRESENT) && cfg->interleaving_tag) { | |
| prsl->interleaving_tag = gf_strdup(cfg->interleaving_tag); | |
| } | |
| ardi = (GF_AudioRenderingIndicationBox *) gf_isom_box_new_parent(&prsl->child_boxes, GF_ISOM_BOX_TYPE_ARDI); | |
| if (!ardi) return GF_OUT_OF_MEM; | |
| ardi->audio_rendering_indication = cfg->audio_rendering_indication; | |
| if (cfg->extended_language) { | |
| elng = (GF_ExtendedLanguageBox *) gf_isom_box_new_parent(&prsl->child_boxes, GF_ISOM_BOX_TYPE_ELNG); | |
| if (!elng) return GF_OUT_OF_MEM; | |
| elng->extended_language = gf_strdup(cfg->extended_language); | |
| } | |
| if (cfg->dialog_gain_present) { | |
| udta = (GF_UserDataBox *) gf_isom_box_new_parent(&prsl->child_boxes, GF_ISOM_BOX_TYPE_UDTA); | |
| if (!udta) return GF_OUT_OF_MEM; | |
| diap = (GF_DialogueProcessingBox *) gf_isom_box_new_parent(&udta->child_boxes, GF_ISOM_BOX_TYPE_DIAP); | |
| if (!diap) return GF_OUT_OF_MEM; | |
| diap->dialog_gain = cfg->dialog_gain; | |
| } | |
| if (cfg->labels) { | |
| for (u32 j = 0; j < gf_list_count(cfg->labels); j++) { | |
| GF_Label *label = (GF_Label*)gf_list_get(cfg->labels, j); | |
| if (!label) continue; | |
| labl = (GF_LabelBox *) gf_isom_box_new_parent(&prsl->child_boxes, GF_ISOM_BOX_TYPE_LABL); | |
| if (!labl) return GF_OUT_OF_MEM; | |
| labl->label_id = label->label_id; | |
| labl->language = gf_strdup(label->language); | |
| labl->label = gf_strdup(label->label); | |
| } | |
| } | |
| if (cfg->kinds) { | |
| for (u32 j = 0; j < gf_list_count(cfg->kinds); j++) { | |
| GF_Kind *k = (GF_Kind*)gf_list_get(cfg->kinds, j); | |
| if (!k) continue; | |
| kind = (GF_KindBox *) gf_isom_box_new_parent(&prsl->child_boxes, GF_ISOM_BOX_TYPE_KIND); | |
| if (!kind) return GF_OUT_OF_MEM; | |
| kind->value = k->value ? gf_strdup(k->value) : NULL; | |
| kind->schemeURI = gf_strdup(k->schemeURI); | |
| } | |
| } | |
| e = gf_isom_box_size((GF_Box *)prsl); | |
| if (e) return e; | |
| e = gf_isom_box_write((GF_Box *)prsl, bs); | |
| if (e) return e; | |
| gf_isom_box_del((GF_Box *)prsl); | |
| } | |
| return e; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_odf_preselection_cfg_del(GF_List *cfg_list) | |
| { | |
| GF_PreselectionConfig *p; | |
| GF_Label *l; | |
| GF_Kind *k; | |
| if (!cfg_list) | |
| return GF_BAD_PARAM; | |
| for (u32 i = 0; i < gf_list_count(cfg_list); i++) { | |
| p = (GF_PreselectionConfig*)gf_list_get(cfg_list, i); | |
| if (!p) continue; | |
| if (p->labels) { | |
| for (u32 j = 0; j < gf_list_count(p->labels); j++) { | |
| l = gf_list_get(p->labels, j); | |
| if (!l) continue; | |
| if (l->label) gf_free(l->label); | |
| if (l->language) gf_free(l->language); | |
| gf_free(l); | |
| } | |
| gf_list_del(p->labels); | |
| } | |
| if (p->kinds) { | |
| for (u32 j = 0; j < gf_list_count(p->kinds); j++) { | |
| k = gf_list_get(p->kinds, j); | |
| if (!k) continue; | |
| if (k->schemeURI) gf_free(k->schemeURI); | |
| if (k->value) gf_free(k->value); | |
| gf_free(k); | |
| } | |
| gf_list_del(p->kinds); | |
| } | |
| if (p->preselection_tag) gf_free(p->preselection_tag); | |
| if (p->interleaving_tag) gf_free(p->interleaving_tag); | |
| if (p->extended_language) gf_free(p->extended_language); | |
| gf_free(p); | |
| } | |
| gf_list_del(cfg_list); | |
| return GF_OK; | |
| } |