| /* | |
| * GPAC - Multimedia Framework C SDK | |
| * | |
| * Authors: Jean Le Feuvre | |
| * Copyright (c) Telecom ParisTech 2000-2012 | |
| * 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> | |
| #ifndef GPAC_MINIMAL_ODF | |
| //from OD | |
| GF_Err gf_odf_write_descriptor_list(GF_BitStream *bs, GF_List *descList); | |
| GF_Err gf_odf_size_descriptor_list(GF_List *descList, u32 *outSize); | |
| GF_Err gf_odf_delete_descriptor(GF_Descriptor *desc); | |
| GF_Err gf_odf_parse_descriptor(GF_BitStream *bs, GF_Descriptor **desc, u32 *desc_size); | |
| s32 gf_odf_size_field_size(u32 size_desc); | |
| //max size of an OCI event | |
| #define OCI_MAX_EVENT_SIZE 1<<28 - 1 | |
| #define MAX_OCIEVENT_ID 0x7FFF | |
| struct __tag_oci_event | |
| { | |
| u16 EventID; | |
| u8 AbsoluteTimeFlag; | |
| char StartingTime[4]; | |
| char duration[4]; | |
| GF_List *OCIDescriptors; | |
| }; | |
| struct __tag_oci_codec | |
| { | |
| //events | |
| GF_List *OCIEvents; | |
| //version, should always be one | |
| u8 Version; | |
| //encoder or decoder | |
| u8 Mode; | |
| }; | |
| GF_EXPORT | |
| OCIEvent *gf_oci_event_new(u16 EventID) | |
| { | |
| OCIEvent *tmp; | |
| if (EventID > MAX_OCIEVENT_ID) return NULL; | |
| tmp = (OCIEvent *)gf_malloc(sizeof(OCIEvent)); | |
| if (!tmp) return NULL; | |
| memset(tmp, 0, sizeof(OCIEvent)); | |
| tmp->EventID = EventID; | |
| tmp->OCIDescriptors = gf_list_new(); | |
| return tmp; | |
| } | |
| GF_EXPORT | |
| void gf_oci_event_del(OCIEvent *event) | |
| { | |
| if (!event) return; | |
| while (gf_list_count(event->OCIDescriptors)) { | |
| GF_Descriptor *desc = (GF_Descriptor *)gf_list_get(event->OCIDescriptors, 0); | |
| gf_list_rem(event->OCIDescriptors, 0); | |
| gf_odf_delete_descriptor(desc); | |
| } | |
| gf_list_del(event->OCIDescriptors); | |
| gf_free(event); | |
| } | |
| GF_EXPORT | |
| GF_Err gf_oci_event_set_start_time(OCIEvent *event, u8 Hours, u8 Minutes, u8 Seconds, u8 HundredSeconds, u8 IsAbsoluteTime) | |
| { | |
| if (!event || (Hours >= 100) || (Minutes >= 100) || (Seconds >= 100) || (HundredSeconds >= 100) ) | |
| return GF_BAD_PARAM; | |
| event->AbsoluteTimeFlag = IsAbsoluteTime; | |
| event->StartingTime[0] = Hours; | |
| event->StartingTime[1] = Minutes; | |
| event->StartingTime[2] = Seconds; | |
| event->StartingTime[3] = HundredSeconds; | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_oci_event_set_duration(OCIEvent *event, u8 Hours, u8 Minutes, u8 Seconds, u8 HundredSeconds) | |
| { | |
| if (!event || (Hours >= 100) || (Minutes >= 100) || (Seconds >= 100) || (HundredSeconds >= 100) ) | |
| return GF_BAD_PARAM; | |
| event->duration[0] = Hours; | |
| event->duration[1] = Minutes; | |
| event->duration[2] = Seconds; | |
| event->duration[3] = HundredSeconds; | |
| return GF_OK; | |
| } | |
| u8 OCI_IsOCIDesc(GF_Descriptor *oci_desc) | |
| { | |
| if (oci_desc->tag < GF_ODF_OCI_BEGIN_TAG) return 0; | |
| if (oci_desc->tag > GF_ODF_OCI_END_TAG) return 0; | |
| return 1; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_oci_event_add_desc(OCIEvent *event, GF_Descriptor *oci_desc) | |
| { | |
| if (!event || !oci_desc) return GF_BAD_PARAM; | |
| if (!OCI_IsOCIDesc(oci_desc)) return GF_ODF_INVALID_DESCRIPTOR; | |
| gf_list_add(event->OCIDescriptors, oci_desc); | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_oci_event_get_id(OCIEvent *event, u16 *ID) | |
| { | |
| if (!event || !ID) return GF_BAD_PARAM; | |
| *ID = event->EventID; | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_oci_event_get_start_time(OCIEvent *event, u8 *Hours, u8 *Minutes, u8 *Seconds, u8 *HundredSeconds, u8 *IsAbsoluteTime) | |
| { | |
| if (!event || !Hours || !Minutes || !Seconds || !HundredSeconds || !IsAbsoluteTime) | |
| return GF_BAD_PARAM; | |
| *IsAbsoluteTime = event->AbsoluteTimeFlag; | |
| *Hours = event->StartingTime[0]; | |
| *Minutes = event->StartingTime[1]; | |
| *Seconds = event->StartingTime[2]; | |
| *HundredSeconds = event->StartingTime[3]; | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_oci_event_get_duration(OCIEvent *event, u8 *Hours, u8 *Minutes, u8 *Seconds, u8 *HundredSeconds) | |
| { | |
| if (!event || !Hours || !Minutes || !Seconds || !HundredSeconds) | |
| return GF_BAD_PARAM; | |
| *Hours = event->duration[0]; | |
| *Minutes = event->duration[1]; | |
| *Seconds = event->duration[2]; | |
| *HundredSeconds = event->duration[3]; | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| u32 gf_oci_event_get_desc_count(OCIEvent *event) | |
| { | |
| if (!event) return 0; | |
| return gf_list_count(event->OCIDescriptors); | |
| } | |
| GF_EXPORT | |
| GF_Descriptor *gf_oci_event_get_desc(OCIEvent *event, u32 DescIndex) | |
| { | |
| if (!event || DescIndex >= gf_list_count(event->OCIDescriptors) ) return NULL; | |
| return (GF_Descriptor *) gf_list_get(event->OCIDescriptors, DescIndex); | |
| } | |
| GF_EXPORT | |
| GF_Err gf_oci_event_rem_desc(OCIEvent *event, u32 DescIndex) | |
| { | |
| if (!event || DescIndex >= gf_list_count(event->OCIDescriptors) ) return GF_BAD_PARAM; | |
| return gf_list_rem(event->OCIDescriptors, DescIndex); | |
| } | |
| //construction / destruction | |
| GF_EXPORT | |
| OCICodec *gf_oci_codec_new(u8 IsEncoder, u8 Version) | |
| { | |
| OCICodec *tmp; | |
| if (Version != 0x01) return NULL; | |
| tmp = (OCICodec *)gf_malloc(sizeof(OCICodec)); | |
| if (!tmp) return NULL; | |
| tmp->Mode = IsEncoder ? 1 : 0; | |
| tmp->Version = 0x01; | |
| tmp->OCIEvents = gf_list_new(); | |
| return tmp; | |
| } | |
| GF_EXPORT | |
| void gf_oci_codec_del(OCICodec *codec) | |
| { | |
| if (!codec) return; | |
| while (gf_list_count(codec->OCIEvents)) { | |
| OCIEvent *ev = (OCIEvent *)gf_list_get(codec->OCIEvents, 0); | |
| gf_oci_event_del(ev); | |
| gf_list_rem(codec->OCIEvents, 0); | |
| } | |
| gf_list_del(codec->OCIEvents); | |
| gf_free(codec); | |
| } | |
| GF_EXPORT | |
| GF_Err gf_oci_codec_add_event(OCICodec *codec, OCIEvent *event) | |
| { | |
| if (!codec || !codec->Mode || !event) return GF_BAD_PARAM; | |
| return gf_list_add(codec->OCIEvents, event); | |
| } | |
| GF_Err WriteSevenBitLength(GF_BitStream *bs, u32 size) | |
| { | |
| u32 length; | |
| unsigned char vals[4]; | |
| if (!bs || !size) 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); | |
| 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_EXPORT | |
| GF_Err gf_oci_codec_encode(OCICodec *codec, u8 **outAU, u32 *au_length) | |
| { | |
| GF_BitStream *bs; | |
| u32 i, size, desc_size; | |
| GF_Err e; | |
| OCIEvent *ev; | |
| if (!codec || !codec->Mode || *outAU) return GF_BAD_PARAM; | |
| bs = NULL; | |
| size = 0; | |
| //get the size of each event | |
| i=0; | |
| while ((ev = (OCIEvent *)gf_list_enum(codec->OCIEvents, &i))) { | |
| //fixed size header | |
| size += 10; | |
| e = gf_odf_size_descriptor_list(codec->OCIEvents, &desc_size); | |
| if (e) goto err_exit; | |
| size += desc_size; | |
| } | |
| //encode | |
| bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); | |
| e = WriteSevenBitLength(bs, size); | |
| if (e) goto err_exit; | |
| //get one event, write it and delete it | |
| while (gf_list_count(codec->OCIEvents)) { | |
| ev = (OCIEvent *)gf_list_get(codec->OCIEvents, 0); | |
| gf_list_rem(codec->OCIEvents, 0); | |
| gf_bs_write_int(bs, ev->EventID, 15); | |
| gf_bs_write_int(bs, ev->AbsoluteTimeFlag, 1); | |
| gf_bs_write_data(bs, ev->StartingTime, 4); | |
| gf_bs_write_data(bs, ev->duration, 4); | |
| e = gf_odf_write_descriptor_list(bs, ev->OCIDescriptors); | |
| gf_oci_event_del(ev); | |
| if (e) goto err_exit; | |
| //OCI Event is aligned | |
| gf_bs_align(bs); | |
| } | |
| gf_bs_get_content(bs, outAU, au_length); | |
| gf_bs_del(bs); | |
| return GF_OK; | |
| err_exit: | |
| if (bs) gf_bs_del(bs); | |
| //delete everything | |
| while (gf_list_count(codec->OCIEvents)) { | |
| ev = (OCIEvent *)gf_list_get(codec->OCIEvents, 0); | |
| gf_list_rem(codec->OCIEvents, 0); | |
| gf_oci_event_del(ev); | |
| } | |
| return e; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_oci_codec_decode(OCICodec *codec, u8 *au, u32 au_length) | |
| { | |
| OCIEvent *ev; | |
| GF_BitStream *bs; | |
| u32 size, hdrS, desc_size, tot_size, tmp_size, val; | |
| GF_Descriptor *tmp; | |
| GF_Err e; | |
| //must be decoder | |
| if (!codec || codec->Mode || !au) return GF_BAD_PARAM; | |
| bs = gf_bs_new(au, au_length, GF_BITSTREAM_READ); | |
| ev = 0; | |
| tot_size = 0; | |
| while (tot_size < au_length) { | |
| //create an event | |
| ev = gf_oci_event_new(0); | |
| if (!ev) { | |
| e = GF_OUT_OF_MEM; | |
| goto err_exit; | |
| } | |
| //FIX IM1 | |
| gf_bs_read_int(bs, 8); | |
| size = 0; | |
| //get its size | |
| hdrS = 0; | |
| do { | |
| val = gf_bs_read_int(bs, 8); | |
| hdrS += 1; | |
| size <<= 7; | |
| size |= val & 0x7F; | |
| } while ( val & 0x80 ); | |
| //parse event vars | |
| ev->EventID = gf_bs_read_int(bs, 15); | |
| ev->AbsoluteTimeFlag = gf_bs_read_int(bs, 1); | |
| gf_bs_read_data(bs, ev->StartingTime, 4); | |
| gf_bs_read_data(bs, ev->duration, 4); | |
| desc_size = 0; | |
| //parse descriptor list | |
| while (desc_size < size - 10) { | |
| e = gf_odf_parse_descriptor(bs, &tmp, &tmp_size); | |
| //RE-FIX IM1 | |
| if (e || !tmp) goto err_exit; | |
| if (!OCI_IsOCIDesc(tmp)) { | |
| gf_odf_delete_descriptor(tmp); | |
| e = GF_ODF_INVALID_DESCRIPTOR; | |
| goto err_exit; | |
| } | |
| gf_list_add(ev->OCIDescriptors, tmp); | |
| desc_size += tmp_size + gf_odf_size_field_size(tmp_size); | |
| } | |
| if (desc_size != size - 10) { | |
| e = GF_CORRUPTED_DATA; | |
| goto err_exit; | |
| } | |
| gf_list_add(codec->OCIEvents, ev); | |
| //FIX IM1 | |
| size += 1; | |
| tot_size += size + hdrS; | |
| ev = NULL; | |
| } | |
| if (tot_size != au_length) { | |
| e = GF_CORRUPTED_DATA; | |
| goto err_exit; | |
| } | |
| gf_bs_del(bs); | |
| return GF_OK; | |
| err_exit: | |
| gf_bs_del(bs); | |
| if (ev) gf_oci_event_del(ev); | |
| //delete everything | |
| while (gf_list_count(codec->OCIEvents)) { | |
| ev = (OCIEvent *)gf_list_get(codec->OCIEvents, 0); | |
| gf_list_rem(codec->OCIEvents, 0); | |
| gf_oci_event_del(ev); | |
| } | |
| return e; | |
| } | |
| GF_EXPORT | |
| OCIEvent *gf_oci_codec_get_event(OCICodec *codec) | |
| { | |
| OCIEvent *ev; | |
| if (!codec ||codec->Mode) return NULL; | |
| ev = (OCIEvent *)gf_list_get(codec->OCIEvents, 0); | |
| gf_list_rem(codec->OCIEvents, 0); | |
| return ev; | |
| } | |
| #endif /*GPAC_MINIMAL_ODF*/ |