/* ***** BEGIN LICENSE BLOCK *****  
 * Source last modified: $Id: aacdec.c,v 1.1 2005/02/26 01:47:31 jrecker Exp $ 
 *   
 * Portions Copyright (c) 1995-2005 RealNetworks, Inc. All Rights Reserved.  
 *       
 * The contents of this file, and the files included with this file, 
 * are subject to the current version of the RealNetworks Public 
 * Source License (the "RPSL") available at 
 * http://www.helixcommunity.org/content/rpsl unless you have licensed 
 * the file under the current version of the RealNetworks Community 
 * Source License (the "RCSL") available at 
 * http://www.helixcommunity.org/content/rcsl, in which case the RCSL 
 * will apply. You may also obtain the license terms directly from 
 * RealNetworks.  You may not use this file except in compliance with 
 * the RPSL or, if you have a valid RCSL with RealNetworks applicable 
 * to this file, the RCSL.  Please see the applicable RPSL or RCSL for 
 * the rights, obligations and limitations governing use of the 
 * contents of the file. 
 *   
 * This file is part of the Helix DNA Technology. RealNetworks is the 
 * developer of the Original Code and owns the copyrights in the 
 * portions it created. 
 *   
 * This file, and the files included with this file, is distributed 
 * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY 
 * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS 
 * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES 
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET 
 * ENJOYMENT OR NON-INFRINGEMENT. 
 *  
 * Technology Compatibility Kit Test Suite(s) Location:  
 *    http://www.helixcommunity.org/content/tck  
 *  
 * Contributor(s):  
 *   
 * ***** END LICENSE BLOCK ***** */  

/**************************************************************************************
 * Fixed-point HE-AAC decoder
 * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com)
 * February 2005
 *
 * aacdec.c - platform-independent top level decoder API
 **************************************************************************************/

#include "aaccommon.h"

#include "profile.h"

/**************************************************************************************
 * Function:    AACInitDecoder
 *
 * Description: allocate memory for platform-specific data
 *              clear all the user-accessible fields
 *              initialize SBR decoder if enabled
 *
 * Inputs:      none
 *
 * Outputs:     none
 *
 * Return:      handle to AAC decoder instance, 0 if malloc fails
 **************************************************************************************/
HAACDecoder AACInitDecoder(void)
{
	AACDecInfo *aacDecInfo;

	aacDecInfo = AllocateBuffers();
	if (!aacDecInfo)
		return 0;

#ifdef AAC_ENABLE_SBR
	if (InitSBR(aacDecInfo)) {
		AACFreeDecoder(aacDecInfo);
		return 0;
	}
#endif

	return (HAACDecoder)aacDecInfo;
}

/**************************************************************************************
 * Function:    AACFreeDecoder
 *
 * Description: free platform-specific data allocated by AACInitDecoder
 *              free SBR decoder if enabled
 *
 * Inputs:      valid AAC decoder instance pointer (HAACDecoder)
 *
 * Outputs:     none
 *
 * Return:      none
 **************************************************************************************/
void AACFreeDecoder(HAACDecoder hAACDecoder)
{
	AACDecInfo *aacDecInfo = (AACDecInfo *)hAACDecoder;

	if (!aacDecInfo)
		return;

#ifdef AAC_ENABLE_SBR
	FreeSBR(aacDecInfo);
#endif
	FreeBuffers(aacDecInfo);
}

/**************************************************************************************
 * Function:    AACFindSyncWord
 *
 * Description: locate the next byte-alinged sync word in the raw AAC stream
 *
 * Inputs:      buffer to search for sync word
 *              max number of bytes to search in buffer
 *
 * Outputs:     none
 *
 * Return:      offset to first sync word (bytes from start of buf)
 *              -1 if sync not found after searching nBytes
 **************************************************************************************/
int AACFindSyncWord(unsigned char *buf, int nBytes)
{
	int i;

	/* find byte-aligned syncword (12 bits = 0xFFF) */
	for (i = 0; i < nBytes - 1; i++) {
		if ( (buf[i+0] & SYNCWORDH) == SYNCWORDH && (buf[i+1] & SYNCWORDL) == SYNCWORDL )
			return i;
	}
	
	return -1;
}

/**************************************************************************************
 * Function:    AACGetLastFrameInfo
 *
 * Description: get info about last AAC frame decoded (number of samples decoded, 
 *                sample rate, bit rate, etc.)
 *
 * Inputs:      valid AAC decoder instance pointer (HAACDecoder)
 *              pointer to AACFrameInfo struct
 *
 * Outputs:     filled-in AACFrameInfo struct
 *
 * Return:      none
 *
 * Notes:       call this right after calling AACDecode()
 **************************************************************************************/
void AACGetLastFrameInfo(HAACDecoder hAACDecoder, AACFrameInfo *aacFrameInfo)
{
	AACDecInfo *aacDecInfo = (AACDecInfo *)hAACDecoder;

	if (!aacDecInfo) {
		aacFrameInfo->bitRate =       0;
		aacFrameInfo->nChans =        0;
		aacFrameInfo->sampRateCore =  0;
		aacFrameInfo->sampRateOut =   0;
		aacFrameInfo->bitsPerSample = 0;
		aacFrameInfo->outputSamps =   0;
		aacFrameInfo->profile =       0;
		aacFrameInfo->tnsUsed =       0;
		aacFrameInfo->pnsUsed =       0;
	} else {
		aacFrameInfo->bitRate =       aacDecInfo->bitRate;
		aacFrameInfo->nChans =        aacDecInfo->nChans;
		aacFrameInfo->sampRateCore =  aacDecInfo->sampRate;
		aacFrameInfo->sampRateOut =   aacDecInfo->sampRate * (aacDecInfo->sbrEnabled ? 2 : 1);
		aacFrameInfo->bitsPerSample = 16;
		aacFrameInfo->outputSamps =   aacDecInfo->nChans * AAC_MAX_NSAMPS * (aacDecInfo->sbrEnabled ? 2 : 1);
		aacFrameInfo->profile =       aacDecInfo->profile;
		aacFrameInfo->tnsUsed =       aacDecInfo->tnsUsed;
		aacFrameInfo->pnsUsed =       aacDecInfo->pnsUsed;
	}
}

/**************************************************************************************
 * Function:    AACSetRawBlockParams
 *
 * Description: set internal state variables for decoding a stream of raw data blocks
 *
 * Inputs:      valid AAC decoder instance pointer (HAACDecoder)
 *              flag indicating source of parameters
 *              AACFrameInfo struct, with the members nChans, sampRate, and profile
 *                optionally filled-in
 *
 * Outputs:     updated codec state 
 *
 * Return:      0 if successful, error code (< 0) if error
 *
 * Notes:       if copyLast == 1, then the codec sets up its internal state (for 
 *                decoding raw blocks) based on previously-decoded ADTS header info
 *              if copyLast == 0, then the codec uses the values passed in
 *                aacFrameInfo to configure its internal state (useful when the
 *                source is MP4 format, for example)
 **************************************************************************************/
int AACSetRawBlockParams(HAACDecoder hAACDecoder, int copyLast, AACFrameInfo *aacFrameInfo)
{
	AACDecInfo *aacDecInfo = (AACDecInfo *)hAACDecoder;

	if (!aacDecInfo)
		return ERR_AAC_NULL_POINTER;

	aacDecInfo->format = AAC_FF_RAW;
	if (copyLast)
		return SetRawBlockParams(aacDecInfo, 1, 0, 0, 0);
	else
		return SetRawBlockParams(aacDecInfo, 0, aacFrameInfo->nChans, aacFrameInfo->sampRateCore, aacFrameInfo->profile);
}

/**************************************************************************************
 * Function:    AACFlushCodec
 *
 * Description: flush internal codec state (after seeking, for example)
 *
 * Inputs:      valid AAC decoder instance pointer (HAACDecoder)
 *
 * Outputs:     updated state variables in aacDecInfo
 *
 * Return:      0 if successful, error code (< 0) if error
 **************************************************************************************/
int AACFlushCodec(HAACDecoder hAACDecoder)
{
	int ch;
	AACDecInfo *aacDecInfo = (AACDecInfo *)hAACDecoder;

	if (!aacDecInfo)
		return ERR_AAC_NULL_POINTER;

	/* reset common state variables which change per-frame
	 * don't touch state variables which are (usually) constant for entire clip 
	 *   (nChans, sampRate, profile, format, sbrEnabled)
	 */
	aacDecInfo->prevBlockID = AAC_ID_INVALID;
	aacDecInfo->currBlockID = AAC_ID_INVALID;
	aacDecInfo->currInstTag = -1;
	for (ch = 0; ch < MAX_NCHANS_ELEM; ch++)
		aacDecInfo->sbDeinterleaveReqd[ch] = 0;
	aacDecInfo->adtsBlocksLeft = 0;
	aacDecInfo->tnsUsed = 0;
	aacDecInfo->pnsUsed = 0;

	/* reset internal codec state (flush overlap buffers, etc.) */
	FlushCodec(aacDecInfo);
#ifdef AAC_ENABLE_SBR
	FlushCodecSBR(aacDecInfo);
#endif

	return ERR_AAC_NONE;
}

/**************************************************************************************
 * Function:    AACDecode
 *
 * Description: decode AAC frame
 *
 * Inputs:      valid AAC decoder instance pointer (HAACDecoder)
 *              double pointer to buffer of AAC data
 *              pointer to number of valid bytes remaining in inbuf
 *              pointer to outbuf, big enough to hold one frame of decoded PCM samples
 *                (outbuf must be double-sized if SBR enabled)
 *
 * Outputs:     PCM data in outbuf, interleaved LRLRLR... if stereo
 *                number of output samples = 1024 per channel (2048 if SBR enabled)
 *              updated inbuf pointer
 *              updated bytesLeft
 *
 * Return:      0 if successful, error code (< 0) if error
 *
 * Notes:       inbuf pointer and bytesLeft are not updated until whole frame is
 *                successfully decoded, so if ERR_AAC_INDATA_UNDERFLOW is returned
 *                just call AACDecode again with more data in inbuf
 **************************************************************************************/
int AACDecode(HAACDecoder hAACDecoder, unsigned char **inbuf, int *bytesLeft, short *outbuf)
{
	int err, offset, bitOffset, bitsAvail;
	int ch, baseChan, baseChanSBR, elementChans;
	unsigned char *inptr;
	AACDecInfo *aacDecInfo = (AACDecInfo *)hAACDecoder;
#ifdef AAC_ENABLE_SBR
	int elementChansSBR;
#endif

	if (!aacDecInfo)
		return ERR_AAC_NULL_POINTER;

	/* make local copies (see "Notes" above) */
	inptr = *inbuf;
	bitOffset = 0;
	bitsAvail = (*bytesLeft) << 3;

	/* first time through figure out what the file format is */
	if (aacDecInfo->format == AAC_FF_Unknown) {
		if (bitsAvail < 32)
			return ERR_AAC_INDATA_UNDERFLOW;
		
		if (IS_ADIF(inptr)) {
			/* unpack ADIF header */
			aacDecInfo->format = AAC_FF_ADIF;
			err = UnpackADIFHeader(aacDecInfo, &inptr, &bitOffset, &bitsAvail);
			if (err)
				return err;
		} else {
			/* assume ADTS by default */
			aacDecInfo->format = AAC_FF_ADTS;
		}
	} 
	
	/* if ADTS, search for start of next frame */
	if (aacDecInfo->format == AAC_FF_ADTS) {
		/* can have 1-4 raw data blocks per ADTS frame (header only present for first one) */
		if (aacDecInfo->adtsBlocksLeft == 0) {
			offset = AACFindSyncWord(inptr, bitsAvail >> 3);
			if (offset < 0)
				return ERR_AAC_INDATA_UNDERFLOW;
			inptr += offset;
			bitsAvail -= (offset << 3);

			err = UnpackADTSHeader(aacDecInfo, &inptr, &bitOffset, &bitsAvail);
			if (err)
				return err;

			if (aacDecInfo->nChans == -1) {
				/* figure out implicit channel mapping if necessary */
				err = GetADTSChannelMapping(aacDecInfo, inptr, bitOffset, bitsAvail);
				if (err)
					return err;
			}
		}
		aacDecInfo->adtsBlocksLeft--;
	} else if (aacDecInfo->format == AAC_FF_RAW) {
		err = PrepareRawBlock(aacDecInfo);
		if (err)
			return err;
	}

	/* check for valid number of channels */
	if (aacDecInfo->nChans > AAC_MAX_NCHANS || aacDecInfo->nChans <= 0)
		return ERR_AAC_NCHANS_TOO_HIGH;

	/* will be set later if active in this frame */
	aacDecInfo->tnsUsed = 0;
	aacDecInfo->pnsUsed = 0;

	bitOffset = 0;
	baseChan = 0;
	baseChanSBR = 0;
	do {
		/* parse next syntactic element */
		err = DecodeNextElement(aacDecInfo, &inptr, &bitOffset, &bitsAvail);
		if (err)
			return err;
		
		elementChans = elementNumChans[aacDecInfo->currBlockID];
		if (baseChan + elementChans > AAC_MAX_NCHANS)
			return ERR_AAC_NCHANS_TOO_HIGH;

		/* noiseless decoder and dequantizer */
		for (ch = 0; ch < elementChans; ch++) {
      PROFILE_START("noiseless decoder");
			err = DecodeNoiselessData(aacDecInfo, &inptr, &bitOffset, &bitsAvail, ch);
      PROFILE_END();
      			
			if (err)
				return err;

			PROFILE_START("dequant");
			if (Dequantize(aacDecInfo, ch))
				return ERR_AAC_DEQUANT;
      PROFILE_END();
		}

    PROFILE_START("mid-side and intensity stereo");
		/* mid-side and intensity stereo */
		if (aacDecInfo->currBlockID == AAC_ID_CPE) {
			if (StereoProcess(aacDecInfo))
				return ERR_AAC_STEREO_PROCESS;
		}
    PROFILE_END();


		/* PNS, TNS, inverse transform */
		for (ch = 0; ch < elementChans; ch++) {
      PROFILE_START("PNS");
			if (PNS(aacDecInfo, ch))
				return ERR_AAC_PNS;
      PROFILE_END();

			if (aacDecInfo->sbDeinterleaveReqd[ch]) {
				/* deinterleave short blocks, if required */
				if (DeinterleaveShortBlocks(aacDecInfo, ch))
					return ERR_AAC_SHORT_BLOCK_DEINT;
				aacDecInfo->sbDeinterleaveReqd[ch] = 0;
			}
	
      PROFILE_START("TNS");
			if (TNSFilter(aacDecInfo, ch))
				return ERR_AAC_TNS;
      PROFILE_END();

      PROFILE_START("IMDCT");
			if (IMDCT(aacDecInfo, ch, baseChan + ch, outbuf))
				return ERR_AAC_IMDCT;
      PROFILE_END();
		}


#ifdef AAC_ENABLE_SBR
		if (aacDecInfo->sbrEnabled && (aacDecInfo->currBlockID == AAC_ID_FIL || aacDecInfo->currBlockID == AAC_ID_LFE)) {
			if (aacDecInfo->currBlockID == AAC_ID_LFE)
				elementChansSBR = elementNumChans[AAC_ID_LFE];
			else if (aacDecInfo->currBlockID == AAC_ID_FIL && (aacDecInfo->prevBlockID == AAC_ID_SCE || aacDecInfo->prevBlockID == AAC_ID_CPE))
				elementChansSBR = elementNumChans[aacDecInfo->prevBlockID];
			else 
				elementChansSBR = 0;
			
			if (baseChanSBR + elementChansSBR > AAC_MAX_NCHANS)
				return ERR_AAC_SBR_NCHANS_TOO_HIGH;

			/* parse SBR extension data if present (contained in a fill element) */
			if (DecodeSBRBitstream(aacDecInfo, baseChanSBR))
				return ERR_AAC_SBR_BITSTREAM;

			/* apply SBR */
			if (DecodeSBRData(aacDecInfo, baseChanSBR, outbuf))
				return ERR_AAC_SBR_DATA;

			baseChanSBR += elementChansSBR;
		}
#endif
		
		baseChan += elementChans;
	} while (aacDecInfo->currBlockID != AAC_ID_END);

	/* byte align after each raw_data_block */
	if (bitOffset) {
		inptr++;
		bitsAvail -= (8-bitOffset);
		bitOffset = 0;
		if (bitsAvail < 0)
			return ERR_AAC_INDATA_UNDERFLOW;
	}

	/* update pointers */
	aacDecInfo->frameCount++;
	*bytesLeft -= (inptr - *inbuf);
	*inbuf = inptr;

	return ERR_AAC_NONE;
}

