/******************************************************************************
Copyright (c) 2006 Analog Devices, Inc.  All Rights Reserved.  This software is
proprietary and confidential to Analog Devices, Inc. and its licensors.
*******************************************************************************

$Revision: 1.1 $
$Date: 2008/03/27 03:02:10 $
$LastChangedBy: cjakob $

Project:    MP4 Audio Indexer
Title:      MP4 Audio Indexer
Author:     CFJ

Description:
            MP4 Audio Indexer module

References:
            None

******************************************************************************/

#include <string.h>
#include "adi_aaci_indexer.h"
#include "adi_aaci_indexer_internal.h"

static int adts_sampling_freq_table[16] =
{
	96000, 88200, 64000, 48000,	44100, 32000, 24000, 22050,
	16000, 12000, 11025, 8000,	7350, 0, 0, 0
};


// Parses an ADTS header from a bitstream
// Returns the number of bytes in the header if a header has been parsed
// or -1 if no header is present in the bitstream,
// or 0 if not enough data is available in the bitstream
static int parse_adts_header (
						adi_aaci_t *instance,
					    int num_input_bytes,
    					adi_aaci_bitstream_data_t *input_bitstream,						
						adi_aaci_adts_header* header
)
{
	// Enough byte for the header?
	adi_aaci_bitstream_data_t full_header[ADI_AACI_ADTS_HEADER_NUM_BYTES];
	if (instance->input_buffer.num_bytes_remaining <= ADI_AACI_ADTS_HEADER_NUM_BYTES)
	{
		return 0;
	}
	
	// Read the header using circular buffering
	adi_aaci_bitstream_data_t *bitstream = input_bitstream;
	int i=0;
	for (i=0; i<ADI_AACI_ADTS_HEADER_NUM_BYTES; i++)
	{
		full_header[i] = *bitstream;
		bitstream = __builtin_circptr(
								bitstream, 
								1, 
								instance->input_buffer.base, 
								instance->input_buffer.length );		
	}
	
	// First 12 bits == 0xFFF
	unsigned int magic_number = (full_header[0]<<4) + ((full_header[1] & 0xF0) >> 4);
	if (magic_number != 0xFFF)
	{
		return -1;
	}
	
	
	// Parse the necessary fields
	// bits 18..21
	header->sampling_frequency_index = (full_header[2] & 0x3C) >> 2;
	
	// bits 30..42 	
	header->aac_frame_length =	((full_header[3] & 0x03) << 11)
							+ 	 (full_header[4] << 3)
							+	 (full_header[5] >> 5);				

	
	// bits 54..55 	
	header->number_of_raw_data_blocks_in_frame =	(full_header[6] & 0x03);
	
	return (ADI_AACI_ADTS_HEADER_NUM_BYTES);
	
}

// API Functions
// Create the instance if enough memory is available
// Returns a pointer to the instance
FAST_CODE_SECTION
adi_aaci_t* adi_aaci_create (
	adi_aaci_mem_t *memory
)
{
	adi_aaci_t* instance = 0;
	if (sizeof(adi_aaci_t) <= ADI_AACI_FASTB0_PRIO0_STATE_MEM_NUMBYTES)
	{
		instance = memory->state.fastb0.prio0;
	}
	return instance;
}


// Initialises the parser
// Returns 
FAST_CODE_SECTION
adi_aaci_return_code_t adi_aaci_init (
							adi_aaci_t *instance,
							adi_aaci_config_t *config_params)
{
	adi_aaci_return_code_t result = ADI_AACI_SUCCESS;

	// Reset the state 
	adi_aaci_t* indexer = (adi_aaci_t*) instance;
	memset (indexer, 0, sizeof(adi_aaci_t));
	
	// Save the buffer parameters
    indexer->input_buffer.is_circular = config_params->use_circular_bitstream_buffer;
	if (config_params->use_circular_bitstream_buffer == 0)
	{
		// Linear buffering
		indexer->input_buffer.length = 0;
        indexer->input_buffer.base = NULL;	
	}	
	else
	{
		// Circular buffering
        if ((config_params->circular_buffer_length==0) || (config_params->circular_buffer_base==NULL))
        {
            result = ADI_AACI_INVALID_CONFIG_PARAMS;
        }
        else
        {
		    indexer->input_buffer.length = config_params->circular_buffer_length;
            indexer->input_buffer.base = config_params->circular_buffer_base;	
        }
	}	
	
	// Initialise the instance state variables
	instance->seek_state.prev_stream_index = 0;
	instance->seek_state.prev_milliseconds = 0;
	instance->index_list.current_index = 0;

	return result;
}

// Add the time index to the index list
// Returns	ADI_AACI_OPERATION_FAILED if index could not be added
//			ADI_AACI_SUCCESS otherwise
adi_aaci_return_code_t adi_aaci_indexer (
    adi_aaci_t 					*instance,
    adi_aaci_time_t 			milliseconds,
    int				 			stream_byte_offset
)
{
	int read_index = 0;
	int write_index = 0;
	int index_time_increment;
	
	// Add the given index to the next available position (table is ordered)
	if ( (instance->index_list.current_index == 0)
	    || (milliseconds > instance->index_list.index[instance->index_list.current_index-1].milliseconds) )
    {
    	// Cull the index list if necessary
    	if (instance->index_list.current_index >= ADI_AACI_MAX_NUM_INDICES)
    	{
    		// Remove roughly every second element, from the second until the most recently added
    		index_time_increment = milliseconds / (ADI_AACI_MAX_NUM_INDICES/2);
    
    		// Ensure the initial (0,0) index is kept
    		write_index = 0;
    		for (read_index = 1;read_index < ADI_AACI_MAX_NUM_INDICES;read_index++)
    		{
    		    // try to create a regularly spaced index table
    		    if ( instance->index_list.index[read_index].milliseconds
    		         > (instance->index_list.index[write_index].milliseconds + index_time_increment))
    		    {
        			write_index ++;
        			instance->index_list.index[write_index].stream_offset = instance->index_list.index[read_index].stream_offset;
        			instance->index_list.index[write_index].milliseconds = instance->index_list.index[read_index].milliseconds;
                }		
    		}
    		instance->index_list.current_index = write_index+1;
    	}
    	
    	// keep at least one entry free in the table
    	if (instance->index_list.current_index >= ADI_AACI_MAX_NUM_INDICES)
    	{
    		instance->index_list.current_index = ADI_AACI_MAX_NUM_INDICES-1;
    	}
	
    	instance->index_list.index[instance->index_list.current_index].stream_offset = stream_byte_offset;
    	instance->index_list.index[instance->index_list.current_index].milliseconds = milliseconds;
    	instance->index_list.current_index++;
	}

	return ADI_AACI_SUCCESS;
}

// Finds the time index pair closest to the seek time and returns it in the output status 
//	structure before returning ADI_MP4AI_SUCCESS
// If the time index pair is not available, return ADI_MP4AI_NEED_MORE_DATA
// Each call, a new stream index and time index is required for the ADTS packet pointed to 
//	by the input bitstream
adi_aaci_return_code_t adi_aaci_seek (
    adi_aaci_t 					*instance,
    adi_aaci_time_t 			seek_milliseconds,
    adi_aaci_time_t 			milliseconds,     
    int				 			stream_byte_offset,       
    int 						num_input_bytes,
    adi_aaci_bitstream_data_t 	*input_bitstream,
    adi_aaci_output_status_t 	*output_status
)
{
	int index = 0;
	adi_aaci_return_code_t result = ADI_AACI_SUCCESS;
	
	// Return an index if the seek time is prior to the last time index
	output_status->stream_offset = stream_byte_offset;
	output_status->milliseconds = milliseconds;

	// Is the seek time in the index list? (REWIND - use the index table)	
	if ( (instance->index_list.current_index > 0)
	    && (seek_milliseconds <= instance->index_list.index[instance->index_list.current_index - 1].milliseconds) )
	{
	    index = 0;
	    // first check if the seek point is after the first entry in the table
	    if ( seek_milliseconds > instance->index_list.index[0].milliseconds )
	    {
		    // Find the closest time index before the seek time from the index list
    		for (index = 0; index < instance->index_list.current_index - 2; index++)
    		{
    			if (	(seek_milliseconds >= instance->index_list.index[index    ].milliseconds)
    				&&	(seek_milliseconds <  instance->index_list.index[index + 1].milliseconds)	)
    			{
    				break;
    			}
    		}
		}
		
		// Return the time index
		output_status->stream_offset = instance->index_list.index[index].stream_offset;
		output_status->milliseconds = instance->index_list.index[index].milliseconds;
					
		// Seek done
		result = ADI_AACI_SUCCESS;
	}
	else // (FAST FORWARD - need to parse the input bitstream)
	{
	    // The indexer fundamentally operates on the input bitstream in a
	    // circular fashion. In case the application does not want to implement a
	    // circular buffer (i.e. using a linear buffer), the indexer can still
	    // operate on this provided the base address is set to the start of the
	    // bitstream on this call to the indexer and the length to the number of
	    // words contained in the buffer.
	    //
	    // If the application has implemented circular buffering, the base
	    // address and length would have needed to be set in the initialization
	    // phase of the indexer. See the function "adi_aaci_init()".
	    if (!instance->input_buffer.is_circular) {
	    	instance->input_buffer.base   = input_bitstream;
	    	instance->input_buffer.length = num_input_bytes;          
	    }

		// Find the closest time index before the seek time by parsing ADTS headers
		// and returning to the application for more data as needed
		adi_aaci_adts_header adts_header;
		int is_fast_forwarding = 1;
		int prev_stream_index = 0;
		instance->input_buffer.num_bytes_remaining = num_input_bytes;
		output_status->unconsumed_input_data_ptr = input_bitstream;
		output_status->num_input_bytes_consumed = 0;
		while (is_fast_forwarding)
		{
			// Parse the next header
			int num_bytes_consumed = parse_adts_header (
										instance, 
										instance->input_buffer.num_bytes_remaining, 
										output_status->unconsumed_input_data_ptr,
										&adts_header);

			if (num_bytes_consumed == -1)
			{
				// Did not find header
				is_fast_forwarding = 0;
				result = ADI_AACI_INVALID_BITSTREAM;				
			}
			else
			if (	(num_bytes_consumed == 0) 
				||  (adts_header.aac_frame_length > instance->input_buffer.num_bytes_remaining)	)
			{
				// Not enough data available to read the header and/or frame.  Ask for more.
				is_fast_forwarding = 0;
				result = ADI_AACI_NEED_MORE_DATA;
			}
			else 
			{
				// What is the duration of this frame? 
				// = num_blocks_in_frame * samples_in_block / sampling_freq
				int adts_sampling_freq = adts_sampling_freq_table[adts_header.sampling_frequency_index];
				int delta_milliseconds = (adts_header.number_of_raw_data_blocks_in_frame + 1) *
				    (1000.0 * ADI_AACI_ADTS_NUM_SAMPLES_PER_FRAME / adts_sampling_freq);
				output_status->milliseconds += delta_milliseconds;
				
				// Determine how many bytes to skip for this frame
				output_status->stream_offset += adts_header.aac_frame_length;
				num_bytes_consumed = adts_header.aac_frame_length;
				
				// Passed the seek time?
				if (output_status->milliseconds > seek_milliseconds)
				{
					// Yes, go back to the previous seek
					output_status->stream_offset = instance->seek_state.prev_stream_index;
					output_status->milliseconds = instance->seek_state.prev_milliseconds;
					
					// Seek done
					is_fast_forwarding = 0;
					result = ADI_AACI_SUCCESS;
				}
				else
				{
					// No, continue seeking
					instance->seek_state.prev_stream_index = output_status->stream_offset;
					instance->seek_state.prev_milliseconds = output_status->milliseconds;
				}
	
				// Consume the bytes
				instance->input_buffer.num_bytes_remaining -= num_bytes_consumed;
				output_status->num_input_bytes_consumed += num_bytes_consumed;
				output_status->unconsumed_input_data_ptr = __builtin_circptr(
								output_status->unconsumed_input_data_ptr, 
								num_bytes_consumed, 
								instance->input_buffer.base, 
								instance->input_buffer.length );

				// Index the stream while seeking
				result = adi_aaci_indexer (instance, output_status->milliseconds, output_status->stream_offset);
				if (result != ADI_AACI_SUCCESS)
				{
					is_fast_forwarding = 0;
				}							
			}

		}
	}
	
	return result;	
}

