text: /*
   *			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>
  
  
  //
  //	Constructor
  //
  GF_Descriptor *gf_odf_new_slc(u8 predef)
  {
  	GF_SLConfig *newDesc = (GF_SLConfig *) gf_malloc(sizeof(GF_SLConfig));
  	if (!newDesc) return NULL;
  	memset(newDesc, 0, sizeof(GF_SLConfig));
  	newDesc->tag = GF_ODF_SLC_TAG;
  	newDesc->predefined = predef;
  	if (predef) gf_odf_slc_set_pref(newDesc);
  	newDesc->useTimestampsFlag = 1;
  
  	return (GF_Descriptor *)newDesc;
  }
  
  //
  //	Destructor
  //
  GF_Err gf_odf_del_slc(GF_SLConfig *sl)
  {
  	if (!sl) return GF_BAD_PARAM;
  	gf_free(sl);
  	return GF_OK;
  }
  
  //
  //	Set the SL to the ISO predefined value
  //
  GF_EXPORT
  GF_Err gf_odf_slc_set_pref(GF_SLConfig *sl)
  {
  	if (! sl) return GF_BAD_PARAM;
  
  	switch (sl->predefined) {
  
  	case SLPredef_MP4:
  		sl->useAccessUnitStartFlag = 0;
  		sl->useAccessUnitEndFlag = 0;
  		//each packet is an AU, and we need RAP signaling
  		sl->useRandomAccessPointFlag = 1;
  		sl->hasRandomAccessUnitsOnlyFlag = 0;
  		sl->usePaddingFlag = 0;
  		//in MP4 file, we use TimeStamps
  		sl->useTimestampsFlag = 1;
  		sl->useIdleFlag = 0;
  		sl->durationFlag = 0;
  		sl->timestampLength = 0;
  		sl->OCRLength = 0;
  		sl->AULength = 0;
  		sl->instantBitrateLength = 0;
  		sl->degradationPriorityLength = 0;
  		sl->AUSeqNumLength = 0;
  		sl->packetSeqNumLength = 0;
  		break;
  
  	case SLPredef_Null:
  		sl->useAccessUnitStartFlag = 0;
  		sl->useAccessUnitEndFlag = 0;
  		sl->useRandomAccessPointFlag = 0;
  		sl->hasRandomAccessUnitsOnlyFlag = 0;
  		sl->usePaddingFlag = 0;
  		sl->useTimestampsFlag = 0;
  		sl->useIdleFlag = 0;
  		sl->AULength = 0;
  		sl->degradationPriorityLength = 0;
  		sl->AUSeqNumLength = 0;
  		sl->packetSeqNumLength = 0;
  
  		//for MPEG4 IP
  		sl->timestampResolution = 1000;
  		sl->timestampLength = 32;
  		break;
  	case SLPredef_SkipSL:
  		sl->predefined = SLPredef_SkipSL;
  		break;
  	/*handle all unknown predefined as predef-null*/
  	default:
  		sl->useAccessUnitStartFlag = 0;
  		sl->useAccessUnitEndFlag = 0;
  		sl->useRandomAccessPointFlag = 0;
  		sl->hasRandomAccessUnitsOnlyFlag = 0;
  		sl->usePaddingFlag = 0;
  		sl->useTimestampsFlag = 1;
  		sl->useIdleFlag = 0;
  		sl->AULength = 0;
  		sl->degradationPriorityLength = 0;
  		sl->AUSeqNumLength = 0;
  		sl->packetSeqNumLength = 0;
  
  		sl->timestampResolution = 1000;
  		sl->timestampLength = 32;
  		break;
  	}
  
  	return GF_OK;
  }
  
  u32 SLIsPredefined(GF_SLConfig *sl)
  {
  	if (!sl) return 0;
  	if (sl->predefined) return sl->predefined;
  
  	if (!sl->useAccessUnitStartFlag
  	        &&  !sl->useAccessUnitEndFlag
  	        && !sl->usePaddingFlag
  	        && sl->useTimestampsFlag
  	        && !sl->useIdleFlag
  	        && !sl->durationFlag
  	        && !sl->timestampLength
  	        && !sl->OCRLength
  	        && !sl->AULength
  	        && !sl->instantBitrateLength
  	        && !sl->degradationPriorityLength
  	        && !sl->AUSeqNumLength
  	        && !sl->packetSeqNumLength)
  		return SLPredef_MP4;
  
  	return 0;
  }
  
  //this function gets the real amount of bytes needed to store the timeStamp
  static u32 GetTSbytesLen(GF_SLConfig *sl)
  {
  	u32 TSlen, TSbytes;
  	if (! sl) return 0;
  
  	TSlen = sl->timestampLength * 2;
  	TSbytes = TSlen / 8;
  	TSlen = TSlen % 8;
  	if (TSlen) TSbytes += 1;
  	return TSbytes;
  }
  
  //
  //		Reader
  //
  GF_Err gf_odf_read_slc(GF_BitStream *bs, GF_SLConfig *sl, u32 DescSize)
  {
  	GF_Err e;
  	u32 nbBytes = 0;
  
  	if (!sl) return GF_BAD_PARAM;
  
  	//APPLE fix
  	if (!DescSize) {
  		sl->predefined = SLPredef_MP4;
  		return gf_odf_slc_set_pref(sl);
  	}
  
  	sl->predefined = gf_bs_read_int(bs, 8);
  	nbBytes += 1;
  
  	/*MPEG4 IP fix*/
  	if (!sl->predefined && nbBytes==DescSize) {
  		sl->predefined = SLPredef_Null;
  		gf_odf_slc_set_pref(sl);
  		return GF_OK;
  	}
  
  	if (sl->predefined) {
  		//predefined configuration
  		e = gf_odf_slc_set_pref(sl);
  		if (e) return e;
  	} else {
  		sl->useAccessUnitStartFlag = gf_bs_read_int(bs, 1);
  		sl->useAccessUnitEndFlag = gf_bs_read_int(bs, 1);
  		sl->useRandomAccessPointFlag = gf_bs_read_int(bs, 1);
  		sl->hasRandomAccessUnitsOnlyFlag = gf_bs_read_int(bs, 1);
  		sl->usePaddingFlag = gf_bs_read_int(bs, 1);
  		sl->useTimestampsFlag = gf_bs_read_int(bs, 1);
  		sl->useIdleFlag = gf_bs_read_int(bs, 1);
  		sl->durationFlag = gf_bs_read_int(bs, 1);
  		sl->timestampResolution = gf_bs_read_int(bs, 32);
  		sl->OCRResolution = gf_bs_read_int(bs, 32);
  
  		sl->timestampLength = gf_bs_read_int(bs, 8);
  		if (sl->timestampLength > 64) return GF_ODF_INVALID_DESCRIPTOR;
  
  		sl->OCRLength = gf_bs_read_int(bs, 8);
  		if (sl->OCRLength > 64) return GF_ODF_INVALID_DESCRIPTOR;
  
  		sl->AULength = gf_bs_read_int(bs, 8);
  		if (sl->AULength > 32) return GF_ODF_INVALID_DESCRIPTOR;
  
  		sl->instantBitrateLength = gf_bs_read_int(bs, 8);
  		sl->degradationPriorityLength = gf_bs_read_int(bs, 4);
  		sl->AUSeqNumLength = gf_bs_read_int(bs, 5);
  		if (sl->AUSeqNumLength > 16) return GF_ODF_INVALID_DESCRIPTOR;
  		sl->packetSeqNumLength = gf_bs_read_int(bs, 5);
  		if (sl->packetSeqNumLength > 16) return GF_ODF_INVALID_DESCRIPTOR;
  
  		/*reserved = */gf_bs_read_int(bs, 2);
  		nbBytes += 15;
  	}
  
  	if (sl->durationFlag) {
  		sl->timeScale = gf_bs_read_int(bs, 32);
  		sl->AUDuration = gf_bs_read_int(bs, 16);
  		sl->CUDuration = gf_bs_read_int(bs, 16);
  		nbBytes += 8;
  	}
  	if (! sl->useTimestampsFlag) {
  		sl->startDTS = gf_bs_read_long_int(bs, sl->timestampLength);
  		sl->startCTS = gf_bs_read_long_int(bs, sl->timestampLength);
  		nbBytes += GetTSbytesLen(sl);
  	}
  
  	if (DescSize != nbBytes) return GF_ODF_INVALID_DESCRIPTOR;
  	return GF_OK;
  }
  
  
  //
  //	Size
  //
  GF_Err gf_odf_size_slc(GF_SLConfig *sl, u32 *outSize)
  {
  	if (! sl) return GF_BAD_PARAM;
  
  	*outSize = 1;
  	if (! sl->predefined)	*outSize += 15;
  	if (sl->durationFlag)	*outSize += 8;
  	if (! sl->useTimestampsFlag) *outSize += GetTSbytesLen(sl);
  	return GF_OK;
  }
  
  //
  //	Writer
  //
  GF_Err gf_odf_write_slc(GF_BitStream *bs, GF_SLConfig *sl)
  {
  	GF_Err e;
  	u32 size;
  	if (! sl) return GF_BAD_PARAM;
  
  	e = gf_odf_size_descriptor((GF_Descriptor *)sl, &size);
  	if (e) return e;
  	e = gf_odf_write_base_descriptor(bs, sl->tag, size);
  	if (e) return e;
  
  	gf_bs_write_int(bs, sl->predefined, 8);
  	if (! sl->predefined) {
  		gf_bs_write_int(bs, sl->useAccessUnitStartFlag, 1);
  		gf_bs_write_int(bs, sl->useAccessUnitEndFlag, 1);
  		gf_bs_write_int(bs, sl->useRandomAccessPointFlag, 1);
  		gf_bs_write_int(bs, sl->hasRandomAccessUnitsOnlyFlag, 1);
  		gf_bs_write_int(bs, sl->usePaddingFlag, 1);
  		gf_bs_write_int(bs, sl->useTimestampsFlag, 1);
  		gf_bs_write_int(bs, sl->useIdleFlag, 1);
  		gf_bs_write_int(bs, sl->durationFlag, 1);
  		gf_bs_write_int(bs, sl->timestampResolution, 32);
  		gf_bs_write_int(bs, sl->OCRResolution, 32);
  		gf_bs_write_int(bs, sl->timestampLength, 8);
  		gf_bs_write_int(bs, sl->OCRLength, 8);
  		gf_bs_write_int(bs, sl->AULength, 8);
  		gf_bs_write_int(bs, sl->instantBitrateLength, 8);
  		gf_bs_write_int(bs, sl->degradationPriorityLength, 4);
  		gf_bs_write_int(bs, sl->AUSeqNumLength, 5);
  		gf_bs_write_int(bs, sl->packetSeqNumLength, 5);
  		gf_bs_write_int(bs, 3, 2);	//reserved: 0b11 == 3
  	}
  	if (sl->durationFlag) {
  		gf_bs_write_int(bs, sl->timeScale, 32);
  		gf_bs_write_int(bs, sl->AUDuration, 16);
  		gf_bs_write_int(bs, sl->CUDuration, 16);
  	}
  	if (! sl->useTimestampsFlag) {
  		gf_bs_write_long_int(bs, sl->startDTS, sl->timestampLength);
  		gf_bs_write_long_int(bs, sl->startCTS, sl->timestampLength);
  	}
  
  	return GF_OK;
  }
  
  
  /*allocates and writes the SL-PDU (Header + PDU) given the SLConfig and the GF_SLHeader
  for this PDU. AUs must be split in PDUs by another process if needed (packetizer).*/
  GF_EXPORT
  void gf_sl_packetize(GF_SLConfig* slConfig,
                       GF_SLHeader *Header,
                       char *PDU,
                       u32 size,
                       char **outPacket,
                       u32 *OutSize)
  {
  	GF_BitStream *bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
  	*OutSize = 0;
  	if (!bs) return;
  
  	if (slConfig->useAccessUnitStartFlag) gf_bs_write_int(bs, Header->accessUnitStartFlag, 1);
  	if (slConfig->useAccessUnitEndFlag) gf_bs_write_int(bs, Header->accessUnitEndFlag, 1);
  	if (slConfig->OCRLength > 0) gf_bs_write_int(bs, Header->OCRflag, 1);
  	if (slConfig->useIdleFlag) gf_bs_write_int(bs, Header->idleFlag, 1);
  	if (slConfig->usePaddingFlag) {
  		gf_bs_write_int(bs, Header->paddingFlag, 1);
  		if (Header->paddingFlag) gf_bs_write_int(bs, Header->paddingBits, 3);
  	}
  	if (! Header->idleFlag && (! Header->paddingFlag || Header->paddingBits != 0)) {
  		if (slConfig->packetSeqNumLength > 0) gf_bs_write_int(bs, Header->packetSequenceNumber, slConfig->packetSeqNumLength);
  		if (slConfig->degradationPriorityLength > 0) {
  			gf_bs_write_int(bs, Header->degradationPriorityFlag, 1);
  			if (Header->degradationPriorityFlag) gf_bs_write_int(bs, Header->degradationPriority, slConfig->degradationPriorityLength);
  		}
  		if (Header->OCRflag) gf_bs_write_long_int(bs, Header->objectClockReference, slConfig->OCRLength);
  		if (Header->accessUnitStartFlag) {
  			if (slConfig->useRandomAccessPointFlag) gf_bs_write_int(bs, Header->randomAccessPointFlag, 1);
  			if (slConfig->AUSeqNumLength > 0) gf_bs_write_int(bs, Header->AU_sequenceNumber, slConfig->AUSeqNumLength);
  			if (slConfig->useTimestampsFlag) {
  				gf_bs_write_int(bs, Header->decodingTimeStampFlag, 1);
  				gf_bs_write_int(bs, Header->compositionTimeStampFlag, 1);
  			}
  			if (slConfig->instantBitrateLength > 0) gf_bs_write_int(bs, Header->instantBitrateFlag, 1);
  			if (Header->decodingTimeStampFlag) gf_bs_write_long_int(bs, Header->decodingTimeStamp, slConfig->timestampLength);
  			if (Header->compositionTimeStampFlag) gf_bs_write_long_int(bs, Header->compositionTimeStamp, slConfig->timestampLength);
  			if (slConfig->AULength > 0) gf_bs_write_int(bs, Header->accessUnitLength, slConfig->AULength);
  			if (Header->instantBitrateFlag) gf_bs_write_int(bs, Header->instantBitrate, slConfig->instantBitrateLength);
  		}
  	}
  	/*done with the Header, Alin*/
  	gf_bs_align(bs);
  
  	/*write the PDU - already byte aligned with stuffing (paddingBits in SL Header)*/
  	if (PDU && size)
  		gf_bs_write_data(bs, PDU, size);
  
  	gf_bs_align(bs);
  	gf_bs_get_content(bs, outPacket, OutSize);
  	gf_bs_del(bs);
  }
  
  /*allocates and writes the SL-PDU (Header + PDU) given the SLConfig and the GF_SLHeader
  for this PDU. AUs must be split in PDUs by another process if needed (packetizer).*/
  GF_EXPORT
  u32 gf_sl_get_header_size(GF_SLConfig* slConfig, GF_SLHeader *Header)
  {
  	u32 nb_bits = 0;
  
  	if (slConfig->useAccessUnitStartFlag) nb_bits++;
  	if (slConfig->useAccessUnitEndFlag) nb_bits++;
  	if (slConfig->OCRLength > 0) nb_bits++;
  	if (slConfig->useIdleFlag) nb_bits++;
  	if (slConfig->usePaddingFlag) {
  		nb_bits++;
  		if (Header->paddingFlag) nb_bits+=3;
  	}
  	if (!Header->idleFlag && (! Header->paddingFlag || Header->paddingBits != 0)) {
  		if (slConfig->packetSeqNumLength > 0) nb_bits += slConfig->packetSeqNumLength;
  		if (slConfig->degradationPriorityLength > 0) {
  			nb_bits++;
  			if (Header->degradationPriorityFlag) nb_bits += slConfig->degradationPriorityLength;
  		}
  		if (Header->OCRflag) nb_bits += slConfig->OCRLength;
  		if (Header->accessUnitStartFlag) {
  			if (slConfig->useRandomAccessPointFlag) nb_bits++;
  			if (slConfig->AUSeqNumLength > 0) nb_bits += slConfig->AUSeqNumLength;
  			if (slConfig->useTimestampsFlag) {
  				nb_bits++;
  				nb_bits++;
  			}
  			if (slConfig->instantBitrateLength > 0) nb_bits++;
  			if (Header->decodingTimeStampFlag) nb_bits += slConfig->timestampLength;
  			if (Header->compositionTimeStampFlag) nb_bits += slConfig->timestampLength;
  			if (slConfig->AULength > 0) nb_bits += slConfig->AULength;
  			if (Header->instantBitrateFlag) nb_bits += slConfig->instantBitrateLength;
  		}
  	}
  	while (nb_bits%8) nb_bits++;
  	return nb_bits/8;
  }
  
  
  GF_EXPORT
  void gf_sl_depacketize (GF_SLConfig *slConfig, GF_SLHeader *Header, const char *PDU, u32 PDULength, u32 *HeaderLen)
  {
  	GF_BitStream *bs;
  	*HeaderLen = 0;
  	if (!Header) return;
  	//reset the input header
  	memset(Header, 0, sizeof(GF_SLHeader));
  
  	bs = gf_bs_new(PDU, PDULength, GF_BITSTREAM_READ);
  	if (!bs) return;
  
  	if (slConfig->useAccessUnitStartFlag) Header->accessUnitStartFlag = gf_bs_read_int(bs, 1);
  	if (slConfig->useAccessUnitEndFlag) Header->accessUnitEndFlag = gf_bs_read_int(bs, 1);
  	if ( !slConfig->useAccessUnitStartFlag && !slConfig->useAccessUnitEndFlag) {
  		Header->accessUnitStartFlag = 1;
  		Header->accessUnitEndFlag = 1;
  	}
  	if (slConfig->OCRLength > 0) Header->OCRflag = gf_bs_read_int(bs, 1);
  	if (slConfig->useIdleFlag) Header->idleFlag = gf_bs_read_int(bs, 1);
  	if (slConfig->usePaddingFlag) {
  		Header->paddingFlag = gf_bs_read_int(bs, 1);
  		if (Header->paddingFlag) Header->paddingBits = gf_bs_read_int(bs, 3);
  	}
  	if (!Header->paddingFlag || (Header->paddingBits != 0)) {
  
  		if (slConfig->packetSeqNumLength > 0) Header->packetSequenceNumber = gf_bs_read_int(bs, slConfig->packetSeqNumLength);
  		if (slConfig->degradationPriorityLength > 0) {
  			Header->degradationPriorityFlag = gf_bs_read_int(bs, 1);
  			if (Header->degradationPriorityFlag) Header->degradationPriority = gf_bs_read_int(bs, slConfig->degradationPriorityLength);
  		}
  		if (Header->OCRflag) Header->objectClockReference = gf_bs_read_long_int(bs, slConfig->OCRLength);
  		if (Header->accessUnitStartFlag) {
  			if (slConfig->useRandomAccessPointFlag) Header->randomAccessPointFlag = gf_bs_read_int(bs, 1);
  			if (slConfig->AUSeqNumLength > 0) Header->AU_sequenceNumber = gf_bs_read_int(bs, slConfig->AUSeqNumLength);
  			if (slConfig->useTimestampsFlag) {
  				Header->decodingTimeStampFlag = gf_bs_read_int(bs, 1);
  				Header->compositionTimeStampFlag = gf_bs_read_int(bs, 1);
  			}
  			if (slConfig->instantBitrateLength > 0) Header->instantBitrateFlag = gf_bs_read_int(bs, 1);
  			if (Header->decodingTimeStampFlag) Header->decodingTimeStamp = gf_bs_read_long_int(bs, slConfig->timestampLength);
  			if (Header->compositionTimeStampFlag) Header->compositionTimeStamp = gf_bs_read_long_int(bs, slConfig->timestampLength);
  			if (slConfig->AULength > 0) Header->accessUnitLength = gf_bs_read_int(bs, slConfig->AULength);
  			if (Header->instantBitrateFlag) Header->instantBitrate = gf_bs_read_int(bs, slConfig->instantBitrateLength);
  		}
  	}
  	gf_bs_align(bs);
  	*HeaderLen = (u32) gf_bs_get_position(bs);
  	gf_bs_del(bs);
  }
  