/*
 *			GPAC - MPEG-4 Systems C Development Kit
 *
 *			Copyright (c) Jean Le Feuvre 2000-2005 
 *					All rights reserved
 *
 *  This file is part of GPAC / Authoring Tools sub-project
 *
 *  GPAC is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU 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 General Public License for more details.
 *   
 *  You should have received a copy of the GNU General Public License
 *  along with GNU Make; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
 *
 */

#include <gpac/intern/m4_author_dev.h>

void InitSL_RTP(SLConfigDescriptor *slc);


u32 GetNumBitsForMaxValue(u32 MaxValue)
{
	u32 i = 0;
	//1<<i <=> 2^i for i<32 which is always our case
	while ( (u32) ( (1 << i) - 1)  < MaxValue) i++;
	return i;
}





M4RTPBuilder *NewRTPBuilder(u32 rtp_payt, SLConfigDescriptor *slc, u32 flags,
				void *cbk_obj, 
				void (*OnNewPacket)(void *cbk, RTPHeader *header),
				void (*OnPacketDone)(void *cbk, RTPHeader *header),
				void (*OnDataReference)(void *cbk, u32 payload_size, u32 offset_from_orig),
				void (*OnData)(void *cbk, char *data, u32 data_size, Bool is_head)
				)
{
	M4RTPBuilder *tmp;
	if (!rtp_payt || !cbk_obj | !OnPacketDone) return NULL;
	
	tmp = malloc(sizeof(M4RTPBuilder));
	if (!tmp) return NULL;
	memset(tmp, 0, sizeof(M4RTPBuilder));
	if (slc) {
		memcpy(&tmp->sl_config, slc, sizeof(SLConfigDescriptor));
	} else {
		//default
		InitSL_RTP(&tmp->sl_config);
	}
	tmp->OnNewPacket = OnNewPacket;
	tmp->OnDataReference = OnDataReference;
	tmp->OnData = OnData;
	tmp->cbk_obj = cbk_obj;
	tmp->OnPacketDone = OnPacketDone;
	tmp->rtp_payt = rtp_payt;
	tmp->flags = flags;
	//default init
	tmp->sl_header.AU_sequenceNumber = 1;
	tmp->sl_header.packetSequenceNumber = 1;

	//we assume we start on a new AU
	tmp->sl_header.accessUnitStartFlag = 1;	
	return tmp;
}

void M4RTP_DeleteBuilder(M4RTPBuilder *builder)
{
	if (!builder) return;

	if (builder->payload) DeleteBitStream(builder->payload);
	if (builder->pck_hdr) DeleteBitStream(builder->pck_hdr);
	free(builder);
}

M4Err M4RTP_ProcessData(M4RTPBuilder *builder, char *data, u32 data_size, u8 IsAUEnd, u32 FullAUSize, u32 duration, u8 descIndex)
{
	if (!builder) return M4BadParam;

	switch (builder->rtp_payt) {
	case RTP_PAYT_MPEG4:
		return M4RTP_ProcessMPEG4(builder, data, data_size, IsAUEnd, FullAUSize);
	case RTP_PAYT_MPEG12:
		return M4RTP_ProcessMPEG12(builder, data, data_size, IsAUEnd, FullAUSize);
	case RTP_PAYT_H263:
		return M4RTP_ProcessH263(builder, data, data_size, IsAUEnd, FullAUSize);
	case RTP_PAYT_AMR:
	case RTP_PAYT_AMR_WB:
		return M4RTP_ProcessAMR(builder, data, data_size, IsAUEnd, FullAUSize);
	case RTP_PAYT_3GPP_TEXT:
		return M4RTP_ProcessText(builder, data, data_size, IsAUEnd, FullAUSize, duration, descIndex);
	case RTP_PAYT_H264_AVC:
		return M4RTP_ProcessAVC(builder, data, data_size, IsAUEnd, FullAUSize);
	case RTP_PAYT_QCELP:
		return M4RTP_ProcessQCELP(builder, data, data_size, IsAUEnd, FullAUSize);
	case RTP_PAYT_EVRC_SMV:
		return M4RTP_ProcessEVRC_SMV(builder, data, data_size, IsAUEnd, FullAUSize);
	case RTP_PAYT_LATM: 
		return M4RTP_ProcessLATM(builder, data, data_size, IsAUEnd, FullAUSize); 
 	default:
		return M4BadParam;
	}
}


//Compute the #params of the slMap
void M4RTP_InitBuilder(M4RTPBuilder *builder, u8 PayloadType, u32 PathMTU, u32 max_ptime, 
					   u32 StreamType, u32 OTI, u32 PL_ID,
					   u32 avgSize, u32 maxSize, 
					   u32 avgTS, u32 maxDTS,
					   u8 IV_length, u8 KI_length,
					   char *pref_mode) 
{
	u32 k, totDelta, ismacrypt_flags;

	memset(&builder->slMap, 0, sizeof(RTPSLMap));
	builder->Path_MTU = PathMTU;
	builder->PayloadType = PayloadType;
	builder->slMap.StreamType = StreamType;
	builder->slMap.ObjectTypeIndication = OTI;
	builder->slMap.PL_ID = PL_ID;
	builder->max_ptime = max_ptime;
	if (pref_mode) strcpy(builder->slMap.mode, pref_mode);


	//some cst vars
	builder->rtp_header.Version = 2;
	builder->rtp_header.PayloadType = builder->PayloadType;

	/*our max config is with 1 packet only (SingleSL)*/
	builder->first_sl_in_rtp = 1;
	/*no AUX data*/
	builder->slMap.AuxiliaryDataSizeLength= 0;


	/*just compute max aggregation size*/
	switch (builder->rtp_payt) {
	case RTP_PAYT_QCELP:
	case RTP_PAYT_EVRC_SMV:
	case RTP_PAYT_AMR:
	case RTP_PAYT_AMR_WB:
	{
		u32 nb_pck = 1;
		u32 block_size = 160;
		/*compute max frames per packet - if no avg size, use max size per codec*/
		if (builder->flags & M4HF_UseMulti) {
			if (builder->rtp_payt == RTP_PAYT_QCELP) {
				if (!avgSize) avgSize = 35;
				nb_pck = (PathMTU-1) / avgSize;	/*one-byte header*/
				if (nb_pck>10) nb_pck=10;	/*cf RFC2658*/
			} else if (builder->rtp_payt == RTP_PAYT_EVRC_SMV) {
				if (!avgSize) avgSize = 23;
				nb_pck = (PathMTU) / avgSize;
				if (nb_pck>32) nb_pck=32;	/*cf RFC3558*/
			} else if (builder->rtp_payt == RTP_PAYT_AMR_WB) {
				if (!avgSize) avgSize = 61;
				nb_pck = (PathMTU-1) / avgSize;
				block_size = 320;
			} else {
				if (!avgSize) avgSize = 32;
				nb_pck = (PathMTU-1) / avgSize;
			}
			if (max_ptime) {
				u32 max_pck = max_ptime / block_size;
				if (nb_pck > max_pck) nb_pck = max_pck;
			}
		}
		if (nb_pck<=1) {
			builder->flags &= ~(M4HF_UseMulti|M4HF_UseInterleaving);
			builder->auh_size = 1;
		} else {
			builder->auh_size = nb_pck;
		}
		/*remove all MPEG-4 and ISMA flags */
		builder->flags &= 0x07;
	}
		return;
	case RTP_PAYT_LATM: 
	case RTP_PAYT_MPEG4:
		break;
	default:
		/*remove all MPEG-4 and ISMA flags */
		builder->flags &= 0x07;
		/*disable aggregation for visual streams*/
		if (StreamType==M4ST_VISUAL) builder->flags &= ~M4HF_UseMulti;
		else if (avgSize && (PathMTU <= avgSize) ) builder->flags &= ~M4HF_UseMulti;
		return;
	} 

	builder->slMap.IV_length = IV_length;
	builder->slMap.KI_length = KI_length;

	ismacrypt_flags = 0;
	if (builder->flags & M4HF_SelectiveEncryption) ismacrypt_flags |= M4HF_SelectiveEncryption;
	if (builder->flags & M4HF_KeyIndicatorPerAU) ismacrypt_flags |= M4HF_KeyIndicatorPerAU;

	/*mode setup*/
	if (!strnicmp(builder->slMap.mode, "AAC", 3)) {
		builder->flags = M4HF_UseMulti | M4HF_SignalSize | M4HF_SignalIDX | ismacrypt_flags;
		if (builder->flags & M4HF_UseInterleaving) builder->slMap.ConstantDuration = avgTS;

		/*AAC LBR*/
		if (maxSize < 63) {
			builder->slMap.PL_ID = 14;
			strcpy(builder->slMap.mode, "AAC-lbr");
			builder->slMap.IndexLength = builder->slMap.IndexDeltaLength = 2;
			builder->slMap.SizeLength = 6;
		}
		/*AAC HBR*/
		else {
			builder->slMap.PL_ID = 16;
			strcpy(builder->slMap.mode, "AAC-hbr");
			builder->slMap.IndexLength = builder->slMap.IndexDeltaLength = 3;
			builder->slMap.SizeLength = 13;
		}
		goto check_header;
	}
	if (!strnicmp(builder->slMap.mode, "CELP", 4)) {
		builder->slMap.PL_ID = 14;
		/*CELP-cbr*/
		if (maxSize == avgSize) {
			/*reset flags (interleaving forbidden)*/
			builder->flags = M4HF_UseMulti | ismacrypt_flags;
			strcpy(builder->slMap.mode, "CELP-cbr");
			builder->slMap.ConstantSize = avgSize;
			builder->slMap.ConstantDuration = avgTS;
		}
		/*CELP VBR*/
		else {
			strcpy(builder->slMap.mode, "CELP-vbr");
			builder->slMap.IndexLength = builder->slMap.IndexDeltaLength = 2;
			builder->slMap.SizeLength = 6;
			if (builder->flags & M4HF_UseInterleaving) builder->slMap.ConstantDuration = avgTS;
			builder->flags = M4HF_UseMulti | M4HF_SignalSize | M4HF_SignalIDX | ismacrypt_flags;
		}
		goto check_header;
	}

	/*generic setup by flags*/
	
	/*size*/
	if (builder->flags & M4HF_SignalSize) {
		if (avgSize==maxSize) {
			builder->slMap.SizeLength = 0;
			builder->slMap.ConstantSize = maxSize;
		} else {
			builder->slMap.SizeLength = GetNumBitsForMaxValue(maxSize ? maxSize : PathMTU);
			builder->slMap.ConstantSize = 0;
		}
	} else {
		builder->slMap.SizeLength = 0;
		if (builder->flags & M4HF_UseMulti)
			builder->slMap.ConstantSize = (avgSize==maxSize) ? maxSize : 0;
		else
			builder->slMap.ConstantSize = 0;
	}

	/*single SL per RTP*/
	if (!(builder->flags & M4HF_UseMulti)) {
		if ( builder->sl_config.AUSeqNumLength && (builder->flags & M4HF_SignalIDX)) {
			builder->slMap.IndexLength = builder->sl_config.AUSeqNumLength;
		} else {
			builder->slMap.IndexLength = 0;
		}
		/*one packet per RTP so no delta*/
		builder->slMap.IndexDeltaLength = 0;
		builder->slMap.IV_delta_length = 0;

		/*CTS Delta is always 0 since we have one SL packet per RTP*/
		builder->slMap.CTSDeltaLength = 0;

		/*DTS Delta depends on the video type*/
		if ((builder->flags & M4HF_SignalTS) && maxDTS ) 
			builder->slMap.DTSDeltaLength = GetNumBitsForMaxValue(maxDTS);
		else
			builder->slMap.DTSDeltaLength = 0;

		/*RAP*/
		if (builder->sl_config.useRandomAccessPointFlag && (builder->flags & M4HF_SignalRAP)) {
			builder->slMap.RandomAccessIndication = 1;
		} else {
			builder->slMap.RandomAccessIndication = 0;
		}
		/*TODO: stream state*/
		goto check_header;
	}

	/*this is the avg samples we can store per RTP packet*/
	k = PathMTU / avgSize;
	if (k<=1) {
		builder->flags &= ~M4HF_UseMulti;
		/*keep TS signaling for B-frames (eg never default to M4V-ES when B-frames are present)*/
		//builder->flags &= ~M4HF_SignalTS;
		builder->flags &= ~M4HF_SignalSize;
		builder->flags &= ~M4HF_SignalIDX;
		builder->flags &= ~M4HF_UseInterleaving;
		builder->flags &= ~M4HF_KeyIndicatorPerAU;
		M4RTP_InitBuilder(builder, PayloadType, PathMTU, max_ptime, StreamType, OTI, PL_ID, avgSize, maxSize, avgTS, maxDTS, IV_length, KI_length, pref_mode);
		return;
	}

	/*multiple SL per RTP - check if we have to send TS*/
	builder->slMap.ConstantDuration = builder->sl_config.CUDuration;
	if (!builder->slMap.ConstantDuration) {
		builder->flags |= M4HF_SignalTS;
	}
	/*if we have a constant duration and are not writting TSs, make sure we write AU IDX when interleaving*/
	else if (! (builder->flags & M4HF_SignalTS) && (builder->flags & M4HF_UseInterleaving)) {
		builder->flags |= M4HF_SignalIDX;
	}

	if (builder->flags & M4HF_SignalTS) {
		/*compute CTS delta*/
		totDelta = k*avgTS;
		builder->slMap.CTSDeltaLength = GetNumBitsForMaxValue(k*avgTS);

		/*compute DTS delta. Delta is ALWAYS from the CTS of the same sample*/ 
		if (maxDTS) 
			builder->slMap.DTSDeltaLength = GetNumBitsForMaxValue(maxDTS);
		else
			builder->slMap.DTSDeltaLength = 0;
	}

	if ((builder->flags & M4HF_SignalIDX) && builder->sl_config.AUSeqNumLength) {
		builder->slMap.IndexLength = builder->sl_config.AUSeqNumLength;
		/*and k-1 AUs in Delta*/
		builder->slMap.IndexDeltaLength = (builder->flags & M4HF_UseInterleaving) ? GetNumBitsForMaxValue(k-1) : 0;
	}

	/*RAP*/
	if (builder->sl_config.useRandomAccessPointFlag && (builder->flags & M4HF_SignalRAP)) {
		builder->slMap.RandomAccessIndication = 1;
	} else {
		builder->slMap.RandomAccessIndication = 0;
	}

check_header:

	/*IV delta only if interleaving (otherwise reconstruction from IV is trivial)*/
	if (IV_length && (builder->flags & M4HF_UseInterleaving)) {
		builder->slMap.IV_delta_length = GetNumBitsForMaxValue(maxSize);
	}
	/*ISMACryp video mode*/
	if ((builder->slMap.StreamType==M4ST_VISUAL) && (builder->slMap.ObjectTypeIndication==0x20)
		&& (builder->flags & M4HF_SignalRAP) && builder->slMap.IV_length 
		&& !(builder->flags & M4HF_SignalIDX) && !(builder->flags & M4HF_SignalSize)
		/*shall have SignalTS*/
		&& (builder->flags & M4HF_SignalTS) && !(builder->flags & M4HF_UseMulti)
	) {
		strcpy(builder->slMap.mode, "mpeg4-video");
	}

	/*check if we use AU header or not*/
	if (!builder->slMap.SizeLength 
		&& !builder->slMap.IndexLength 
		&& !builder->slMap.IndexDeltaLength
		&& !builder->slMap.DTSDeltaLength  
		&& !builder->slMap.CTSDeltaLength
		&& !builder->slMap.RandomAccessIndication
		&& !builder->slMap.IV_length
		&& !builder->slMap.KI_length
	) {
		builder->has_AU_header= 0;
	} else {
		builder->has_AU_header = 1;
	}
}

void M4RTP_SetCrypInfo(M4RTPBuilder *builder, u64 IV, char *key_indicator, Bool is_encrypted)
{
	if (!key_indicator) {
		if (builder->key_indicator) {
			/*force flush if no provision for keyIndicator per AU*/
			builder->force_flush = (builder->flags & M4HF_KeyIndicatorPerAU) ? 0 : 1;
			free(builder->key_indicator);
			builder->key_indicator = NULL;
		}
	} else if (!builder->key_indicator
		||
		memcmp(builder->key_indicator, key_indicator, sizeof(char)*builder->slMap.KI_length)
	) {
		/*force flush if no provision for keyIndicator per AU*/
		builder->force_flush = (builder->flags & M4HF_KeyIndicatorPerAU) ? 0 : 1;

		if (!builder->key_indicator) builder->key_indicator = malloc(sizeof(char)*builder->slMap.KI_length);
		memcpy(builder->key_indicator, key_indicator, sizeof(char)*builder->slMap.KI_length);
	}
	if (builder->IV != IV) {
		builder->IV = IV;
		if (builder->slMap.IV_delta_length && (builder->slMap.IV_delta_length < GetNumBitsForMaxValue((u32) (IV - builder->first_AU_IV) ))) {
			builder->first_AU_IV = IV;
			builder->force_flush = 1;
		}
	}
	builder->is_encrypted = is_encrypted;
}

Bool M4RTP_GetPayloadName(LPM4RTPBUILDER rtpb, char *szPayloadName, char *szMediaName)
{
	u32 flags = rtpb->flags;

	switch (rtpb->rtp_payt) {
	case RTP_PAYT_MPEG4:
		if ((rtpb->slMap.StreamType==M4ST_VISUAL) && (rtpb->slMap.ObjectTypeIndication==0x20)) {
			strcpy(szMediaName, "video");
			/*ISMACryp video*/
			if ( (flags & M4HF_SignalRAP) && rtpb->slMap.IV_length
				&& !(flags & M4HF_SignalIDX) && !(flags & M4HF_SignalSize)
				&& (flags & M4HF_SignalTS) && !(flags & M4HF_UseMulti)
				) 
			{
				strcpy(szPayloadName, "enc-mpeg4-generic");
				return 1;
			}
			/*mpeg4-generic*/
			if ( (flags & M4HF_SignalRAP) || (flags & M4HF_SignalIDX) || (flags & M4HF_SignalSize) 
				|| (flags & M4HF_SignalTS) || (flags & M4HF_UseMulti) ) {
				strcpy(szPayloadName, "mpeg4-generic");
				return 1;
			} else {
				strcpy(szPayloadName, "MP4V-ES");
				return 1;
			} 
		}
		/*for all other types*/
		if (rtpb->slMap.StreamType==M4ST_AUDIO) strcpy(szMediaName, "audio");
		else if (rtpb->slMap.StreamType==M4ST_MPEGJ) strcpy(szMediaName, "application");
		else strcpy(szMediaName, "video");
		strcpy(szPayloadName, rtpb->slMap.IV_length ? "enc-mpeg4-generic" : "mpeg4-generic");
		return 1;
	case RTP_PAYT_MPEG12:
		if (rtpb->slMap.StreamType==M4ST_VISUAL) {
			strcpy(szMediaName, "video");
			strcpy(szPayloadName, "MPV");
			return 1;
		} else if (rtpb->slMap.StreamType==M4ST_AUDIO) {
			strcpy(szMediaName, "audio");
			strcpy(szPayloadName, "MPA");
			return 1;
		} 
		return 0;
	case RTP_PAYT_H263:
		strcpy(szMediaName, "video");
		strcpy(szPayloadName, "H263-1998");
		return 1;
	case RTP_PAYT_AMR:
		strcpy(szMediaName, "audio");
		strcpy(szPayloadName, "AMR");
		return 1;
	case RTP_PAYT_AMR_WB:
		strcpy(szMediaName, "audio");
		strcpy(szPayloadName, "AMR-WB");
		return 1;
	case RTP_PAYT_3GPP_TEXT:
		strcpy(szMediaName, "text");
		strcpy(szPayloadName, "3gpp-tt");
		return 1;
	case RTP_PAYT_H264_AVC:
		strcpy(szMediaName, "video");
		strcpy(szPayloadName, "H264");
		return 1;
	case RTP_PAYT_QCELP:
		strcpy(szMediaName, "audio");
		strcpy(szPayloadName, "QCELP");
		return 1;
	case RTP_PAYT_EVRC_SMV:
		strcpy(szMediaName, "audio");
		strcpy(szPayloadName, (rtpb->slMap.ObjectTypeIndication==0xA0) ? "EVRC" : "SMV");
		/*header-free version*/
		if (rtpb->auh_size<=1) strcat(szPayloadName, "0");
		return 1;
	case RTP_PAYT_LATM: 
		strcpy(szMediaName, "audio"); 
		strcpy(szPayloadName, "MP4A-LATM"); 
		return 1; 
	default:
		strcpy(szMediaName, "");
		strcpy(szPayloadName, "");
		return 0;
	}
	return 0;
}


M4Err M4RTP_FormatMPEG4_SDP(M4RTPBuilder *builder, char *payload_name, char *sdpLine, char *dsi, u32 dsi_size)
{
	char buffer[20000], dsiString[20000];
	u32 i, k;
	Bool is_first = 1;

	if ((builder->rtp_payt!=RTP_PAYT_MPEG4) && (builder->rtp_payt!=RTP_PAYT_LATM) ) return M4BadParam;
	
#define SDP_ADD_INT(_name, _val) { if (!is_first) strcat(sdpLine, "; "); sprintf(buffer, "%s=%d", _name, _val); strcat(sdpLine, buffer); is_first = 0;}
#define SDP_ADD_STR(_name, _val) { if (!is_first) strcat(sdpLine, "; "); sprintf(buffer, "%s=%s", _name, _val); strcat(sdpLine, buffer); is_first = 0;}

	sprintf(sdpLine, "a=fmtp:%d ", builder->PayloadType);
	
	/*mandatory fields*/
	if (builder->slMap.PL_ID) SDP_ADD_INT("profile-level-id", builder->slMap.PL_ID);

	if (builder->rtp_payt == RTP_PAYT_LATM) SDP_ADD_INT("cpresent", 0); 
	
	if (dsi && dsi_size) {
		k = 0;
		for (i=0; i<dsi_size; i++) {
			if ((unsigned char) dsi[i] < 0x10) {
				dsiString[k] = '0';
				k++;
				sprintf(&dsiString[k], "%x", (unsigned char) dsi[i]);
				k++;
			} else {
				sprintf(&dsiString[k], "%x", (unsigned char) dsi[i]);
				k+=2;
			}
		}
		dsiString[k] = 0;
		SDP_ADD_STR("config", dsiString);
	}
	if (!strcmp(payload_name, "MP4V-ES") || (builder->rtp_payt == RTP_PAYT_LATM) ) return M4OK;

	SDP_ADD_INT("streamType", builder->slMap.StreamType);
	if (strcmp(builder->slMap.mode, "") && strcmp(builder->slMap.mode, "default")) {
		SDP_ADD_STR("mode", builder->slMap.mode);
	} else {
		SDP_ADD_STR("mode", "generic");
	}

	/*optional fields*/
	if (builder->slMap.ObjectTypeIndication) SDP_ADD_INT("objectType", builder->slMap.ObjectTypeIndication);
	if (builder->slMap.ConstantSize) SDP_ADD_INT("constantSize", builder->slMap.ConstantSize);
	if (builder->slMap.ConstantDuration) SDP_ADD_INT("constantDuration", builder->slMap.ConstantDuration);
	if (builder->slMap.maxDisplacement) SDP_ADD_INT("maxDisplacement", builder->slMap.maxDisplacement);
	if (builder->slMap.deinterleaveBufferSize) SDP_ADD_INT("de-interleaveBufferSize", builder->slMap.deinterleaveBufferSize);
	if (builder->slMap.SizeLength) SDP_ADD_INT("sizeLength", builder->slMap.SizeLength);
	if (builder->slMap.IndexLength) SDP_ADD_INT("indexLength", builder->slMap.IndexLength);
	if (builder->slMap.IndexDeltaLength) SDP_ADD_INT("indexDeltaLength", builder->slMap.IndexDeltaLength);
	if (builder->slMap.CTSDeltaLength) SDP_ADD_INT("CTSDeltaLength", builder->slMap.CTSDeltaLength);
	if (builder->slMap.DTSDeltaLength) SDP_ADD_INT("DTSDeltaLength", builder->slMap.DTSDeltaLength);
	if (builder->slMap.RandomAccessIndication) SDP_ADD_INT("randomAccessIndication", builder->slMap.RandomAccessIndication);
	if (builder->slMap.StreamStateIndication) SDP_ADD_INT("streamStateIndication", builder->slMap.StreamStateIndication);
	if (builder->slMap.AuxiliaryDataSizeLength) SDP_ADD_INT("auxiliaryDataSizeLength", builder->slMap.AuxiliaryDataSizeLength);

	/*ISMACryp config*/
	if (builder->slMap.IV_length) {
		/*don't write default*/
		/*SDP_ADD_STR("ISMACrypCryptoSuite", "AES_CTR_128");*/
		if (builder->flags & M4HF_SelectiveEncryption) SDP_ADD_INT("ISMACrypSelectiveEncryption", 1);
		SDP_ADD_INT("ISMACrypIVLength", builder->slMap.IV_length);
		if (builder->slMap.IV_delta_length) SDP_ADD_INT("ISMACrypDeltaIVLength", builder->slMap.IV_delta_length);
		if (builder->slMap.KI_length) SDP_ADD_INT("ISMACrypKeyIndicatorLength", builder->slMap.KI_length);
		if (builder->flags & M4HF_KeyIndicatorPerAU) SDP_ADD_INT("ISMACrypKeyIndicatorPerAU", 1);
	}
	return M4OK;
}

