/*
 *******************************************************************************
 *  Copyright (c) 2010-2022 VATICS(KNERON) Inc. All rights reserved.
 *
 *  +-----------------------------------------------------------------+
 *  | THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED |
 *  | AND COPIED IN ACCORDANCE WITH THE TERMS AND CONDITIONS OF SUCH  |
 *  | A LICENSE AND WITH THE INCLUSION OF THE THIS COPY RIGHT NOTICE. |
 *  | THIS SOFTWARE OR ANY OTHER COPIES OF THIS SOFTWARE MAY NOT BE   |
 *  | PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY OTHER PERSON. THE   |
 *  | OWNERSHIP AND TITLE OF THIS SOFTWARE IS NOT TRANSFERRED.        |
 *  |                                                                 |
 *  | THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT   |
 *  | ANY PRIOR NOTICE AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY |
 *  | VATICS(KNERON) INC.                                             |
 *  +-----------------------------------------------------------------+
 *
 *******************************************************************************
 */
#include <string>

#include <signal.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>

#include <audiotk/audio_capture_mmap.h>
#include <audiotk/audio_vol_ctrl.h>
#include <audiotk/audio_encoder.h>
#include <MsgBroker/msg_broker.h>
#include <SyncRingBuffer/sync_ring_buffer.h>
#include <SharedCompactMemory/shared_compact_memory.h>

#define MAX_CONNECT_NUM (5)
#define DEFAULT_PID (0xffff)

#ifdef GAMR_SUPPORT
#include <opencore/interf_enc.h> //! For GAMR encoder.
#endif

// Flag for message broker (main loop).
static int is_terminate_ = 0;

typedef struct
{
	//! connect pid
	pid_t connect_pid;
	//! Flag to indicate whether we need to encode data or not.
	unsigned int do_encoding;
} connect_info_t;

connect_info_t g_atconnect_info[MAX_CONNECT_NUM];

typedef struct
{
	// Flag to indicate whether we need to send configuration about each encoder or not.
	bool send_conf;

	// The handles for SynRingBuf.
	SRB_HANDLE_T* srb_handle;
	// The buffers for SynRingBuf.
	SRB_BUFFER_T srb_buf;

	SCM_HANDLE_T* scm_handle;
	SCM_BUFFER_T scm_buf;

	unsigned int enc_type; // FourCC of encoder.
	ATK_AUDIOENC_HANDLE_T* enc_handle; // The handle of encoder.

	unsigned int seq_num;

	ATK_AUDIOENC_ONEFRAME_CONF_T oneframe_conf;

	pthread_mutex_t data_mutex;
	pthread_cond_t data_cond;
	STATUS process_status;

	ATK_AUDIOCAP_CONFIG_T	*p_audiocap_config;
	ATK_AUDIOCAP_HANDLE_T **p_cap_handle;
} user_data_t;

typedef enum
{
	SRB,
	SCM
} MEM_TYPE;

static MEM_TYPE g_eType = SRB;

//#define DUMP_TIMESTAMP
//#define DUMP_PCM_DATA
//#define DUMP_ENC_DATA
#ifdef DUMP_ENC_DATA
#define DUMP_ELD_ENC_DATA
#endif 


#ifdef DUMP_PCM_DATA
static int audio_fd_ = -1;

static int open_pcm_file(/* int is_interleaved, unsigned int channels */)
{
	const char *filename = "/tmp/vrecord/videoclips/debug_audio.pcm";
	audio_fd_ = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
	if(audio_fd_ == -1)
	{
		fprintf(stderr, "[%s, %s]: Open file error: %s\n", __FILE__, __func__, strerror(errno));
		return -1;
	}

	return 0;
}

static void close_pcm_file()
{
	if(audio_fd_ >= 0)
		close(audio_fd_);
}

static int write_pcm_data_to_file(int /*is_interleaved*/, unsigned int /*channels*/, unsigned char* const* audio_bufs, size_t data_bytes)
{
	ssize_t ret = write(audio_fd_, audio_bufs[0], data_bytes);
	if(ret < 0)
	{
		fprintf(stderr, "[%s, %s]: Write error. %s\n", __FILE__, __func__, strerror(errno));
		return -1;
	}
	else if((size_t) ret != data_bytes)
	{
		fprintf(stderr, "[%s, %s]: Data loss ..............\n", __FILE__, __func__);
		return -1;
	}
	return 0;
}

#endif // DUMP_PCM_DATA

#ifdef DUMP_ENC_DATA
static int audio_enc_fd_ = -1;
#ifdef OFFLINE_DEC
static int audio_txt_fd_ = -1;
#endif //
static int open_aac_file(/* int is_interleaved, unsigned int channels */)
{
	const char *filename = "/tmp/vrecord/videoclips/debug_audio.aac";
	audio_enc_fd_ = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
	
	if(audio_enc_fd_ == -1)
	{
		fprintf(stderr, "[%s, %s]: Open file error: %s\n", __FILE__, __func__, strerror(errno));
		return -1;
	}
	
	#ifdef OFFLINE_DEC
	const char *filename2 = "/tmp/vrecord/videoclips/debug_audio.txt";
	audio_txt_fd_ = open(filename2, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
	
	if(audio_txt_fd_ == -1)
	{
		fprintf(stderr, "[%s, %s]: Open file error: %s\n", __FILE__, __func__, strerror(errno));
		return -1;
	}
	#endif //
	return 0;
}

static void close_aac_file()
{
	if(audio_enc_fd_ >= 0)
		close(audio_enc_fd_);
	#ifdef OFFLINE_DEC
	if(audio_txt_fd_ >= 0)
		close(audio_txt_fd_);
	#endif	
}

static int write_aac_data_to_file(int /*is_interleaved*/, unsigned int /*channels*/, unsigned char* audio_bufs, size_t data_bytes)
{
	ssize_t ret = write(audio_enc_fd_, audio_bufs, data_bytes);
	if(ret < 0)
	{
		fprintf(stderr, "[%s, %s]: Write error. %s\n", __FILE__, __func__, strerror(errno));
		return -1;
	}
	else if((size_t) ret != data_bytes)
	{
		fprintf(stderr, "[%s, %s]: Data loss ..............\n", __FILE__, __func__);
		return -1;
	}
	
	#ifdef OFFLINE_DEC // write txt for offline decode
	char eld[32];
 	memset(eld, 0x00, sizeof(eld));
	int write_size = 0, check = 0;

	sprintf(eld, "EncSize %d\n", data_bytes);
	check = eld[0];
	while(check != 0x00)
    {
        write_size++;
        check = eld[write_size];
    }
	
	ret = write(audio_txt_fd_, eld, sizeof(char)*write_size);
	if(ret < 0)
	{
		fprintf(stderr, "[%s, %s]: Write error. %s\n", __FILE__, __func__, strerror(errno));
		return -1;
	}
	else if((size_t) ret != (sizeof(char)*write_size))
	{
		fprintf(stderr, "[%s, %s]: Data loss 2..............\n", __FILE__, __func__);
		return -1;
	}
	#endif 
	
	return 0;
}

#endif // DUMP_AAC_DATA

static int pid_default(int dwStart)
{	
	unsigned int i;
	for (i = 0; i < MAX_CONNECT_NUM; ++i)
	{
		if (dwStart) {
			//! set default pid and start encoding 
			if (!g_atconnect_info[i].connect_pid) {
				g_atconnect_info[i].connect_pid = DEFAULT_PID;
				++g_atconnect_info[i].do_encoding;
				break;
			} else if (g_atconnect_info[i].connect_pid == DEFAULT_PID){
				++g_atconnect_info[i].do_encoding;
				break;
			} else {
				printf("[%s] Error: Something wrong \n", __func__);
				break;
			}
		} else {
			//! set default pid and stop encoding
			if (g_atconnect_info[i].connect_pid == DEFAULT_PID) {
				--g_atconnect_info[i].do_encoding;
				if (!g_atconnect_info[i].do_encoding) {
					g_atconnect_info[i].connect_pid = 0;
				}
				break;
			}
		}
	}

	if(i == MAX_CONNECT_NUM){
		printf("[%s] Error: AENC Connect number to MAX\n",__func__);
		return 1;
	}
	return 0;
}	

static int pid_start (pid_t new_pid)
{
	unsigned int i = 0;
	for (i = 0; i < MAX_CONNECT_NUM; i++) 
	{
		if (g_atconnect_info[i].connect_pid == 0 && !g_atconnect_info[i].do_encoding) {	
			g_atconnect_info[i].connect_pid = new_pid;
			g_atconnect_info[i].do_encoding++;
			break;
		} else if (g_atconnect_info[i].connect_pid == new_pid) {
			g_atconnect_info[i].do_encoding++;
			break;
		}
	}

	if(i == MAX_CONNECT_NUM){
		printf("[%s] Error: AENC Connect number to MAX\n",__func__);
		return 1;
	}
		
	return 0;
}

static int pid_stop(pid_t new_pid)
{
	for (size_t i = 0; i < MAX_CONNECT_NUM; i++) 
	{
		if (g_atconnect_info[i].connect_pid != 0
			&& g_atconnect_info[i].connect_pid == new_pid
			&& g_atconnect_info[i].do_encoding) {	
			g_atconnect_info[i].do_encoding--;
			if (!g_atconnect_info[i].do_encoding)
				g_atconnect_info[i].connect_pid = 0;
			break;
		} 
	}

	return 0;
}

static int pid_do_encoding(void)
{
	for (size_t i = 0; i < MAX_CONNECT_NUM; i++) 
	{
		if (g_atconnect_info[i].connect_pid != 0 && g_atconnect_info[i].do_encoding) 
			return 1;
	}
	return 0;
}


static void audiocap_callback(const ATK_AUDIO_NOTIFY_DATA_INFO_T *audio_info, void* user_data)
{
	//Input pcm data should be interleaved
	user_data_t *temp_data = (user_data_t*) user_data;

#ifdef DUMP_TIMESTAMP
	printf("time = %lu.%lu\n", audio_info->tDataTimestamp.tv_sec, audio_info->tDataTimestamp.tv_usec);
#endif // DUMP_TIMESTAMP

#ifdef DUMP_PCM_DATA
	write_pcm_data_to_file(audio_info->bIsInterleaved, audio_info->dwChannels, audio_info->ppbyAudioBufs, audio_info->dwDataBytes);
#endif
	pthread_mutex_lock(&(temp_data->data_mutex));

	if (temp_data->send_conf)
	{
		unsigned int *values = NULL;
		unsigned char *buffer = NULL;

		if (SRB == g_eType) {
			values = (unsigned int *) temp_data->srb_buf.buffer;
			buffer = temp_data->srb_buf.buffer;
		}
		else if (SCM == g_eType) {
			values = (unsigned int *) temp_data->scm_buf.pbyVirtAddr;
			buffer = temp_data->scm_buf.pbyVirtAddr;
		}

		temp_data->send_conf = false;
		// Send configuration of encoder.
		printf("send conf .............\n");
		if(FOURCC_GPCM != temp_data->enc_type) {
			ATK_AudioEnc_GetConf(temp_data->enc_handle, buffer);
		}
		else {
			values[0] = FOURCC_CONF;
			values[1] = 4;
			values[2] = FOURCC_GPCM;	
			values[3] = temp_data->p_audiocap_config->dwSampleRate;
		}
		if (SRB == g_eType) {
			SRB_SendGetWriterBuff(temp_data->srb_handle, &temp_data->srb_buf);
		}
		else if (SCM == g_eType) {
			temp_data->scm_buf.dwOccupiedSpace = values[1];
			SCM_SendGetWriterBuff(temp_data->scm_handle, &temp_data->scm_buf);
		}
	}

	
	if (temp_data->process_status == STOP) {
		pthread_cond_signal(&(temp_data->data_cond));	
		pthread_mutex_unlock(&(temp_data->data_mutex));	
		return;
	}
	pthread_mutex_unlock(&(temp_data->data_mutex));	
	
	if (pid_do_encoding())
	{
		if(FOURCC_GPCM == temp_data->enc_type)
		{
			if(audio_info->dwDataBytes > 0)
			{
				unsigned int* buf_ptr = NULL;
				if (SRB == g_eType)
					buf_ptr = (unsigned int*) temp_data->srb_buf.buffer;
				else if (SCM == g_eType)
					buf_ptr = (unsigned int *) temp_data->scm_buf.pbyVirtAddr;
				buf_ptr[0] = temp_data->enc_type;
				buf_ptr[1] = audio_info->tDataTimestamp.tv_sec;
				buf_ptr[2] = audio_info->tDataTimestamp.tv_usec;
				buf_ptr[3] = audio_info->dwDataBytes;
				buf_ptr[4] = audio_info->bIsInterleaved;
				buf_ptr[5] = audio_info->dwChannels;
				buf_ptr[6] = temp_data->seq_num;

				if (SRB == g_eType)
				{
					memcpy(temp_data->srb_buf.buffer + MAX_AUDIO_DATA_HEADER_SIZE, audio_info->ppbyAudioBufs[0], audio_info->dwDataBytes);
					SRB_SendGetWriterBuff(temp_data->srb_handle, &temp_data->srb_buf);
				} else if (SCM == g_eType)
				{
					memcpy(temp_data->scm_buf.pbyVirtAddr + MAX_AUDIO_DATA_HEADER_SIZE, audio_info->ppbyAudioBufs[0], audio_info->dwDataBytes);
					temp_data->scm_buf.dwOccupiedSpace = MAX_AUDIO_DATA_HEADER_SIZE + audio_info->dwDataBytes;
					SCM_SendGetWriterBuff(temp_data->scm_handle, &temp_data->scm_buf);
				}
			}
			else
			{
				fprintf(stderr, "[%s, %s]: PCM error !!!\n", __FILE__, __func__);
			}
			++(temp_data->seq_num);
		}
		else
		{
			// Encode audio frames.
			temp_data->oneframe_conf.pbyInBuf = audio_info->ppbyAudioBufs[0];
			if (SRB == g_eType)
				temp_data->oneframe_conf.pbyOutBuf = temp_data->srb_buf.buffer + MAX_AUDIO_DATA_HEADER_SIZE;
			else if (SCM == g_eType)
				temp_data->oneframe_conf.pbyOutBuf = temp_data->scm_buf.pbyVirtAddr + MAX_AUDIO_DATA_HEADER_SIZE;

			//time_t nowtime = time(NULL);
			//printf("[%s] (%d) audio_info->data_bytes = %d !!!!!!!!!!!!!!!!!!!!!!!! \n", __func__, nowtime, audio_info->data_bytes);

			int encode_data_size = ATK_AudioEnc_EncodeOneFrame(temp_data->enc_handle, &temp_data->oneframe_conf);
			if(encode_data_size > 0)
			{
				unsigned int* buf_ptr = NULL;
				if (SRB == g_eType)
					buf_ptr = (unsigned int*) temp_data->srb_buf.buffer;
				else if (SCM == g_eType)
					buf_ptr = (unsigned int *) temp_data->scm_buf.pbyVirtAddr;
				buf_ptr[0] = temp_data->enc_type;
				buf_ptr[1] = audio_info->tDataTimestamp.tv_sec;
				buf_ptr[2] = audio_info->tDataTimestamp.tv_usec;
				buf_ptr[3] = encode_data_size;
				buf_ptr[4] = temp_data->seq_num;
				//printf("Encode size %d(%d)\n",buf_ptr[3],encode_data_size);
				#ifdef DUMP_ENC_DATA
					#ifdef DUMP_ELD_ENC_DATA
					if (SRB == g_eType)
						write_aac_data_to_file(audio_info->bIsInterleaved, audio_info->dwChannels, temp_data->srb_buf.buffer, encode_data_size+MAX_AUDIO_DATA_HEADER_SIZE);
					else if (SCM == g_eType)
						write_aac_data_to_file(audio_info->bIsInterleaved, audio_info->dwChannels, temp_data->scm_buf.pbyVirtAddr, encode_data_size+MAX_AUDIO_DATA_HEADER_SIZE);
					#else //DUMP_ELD_ENC_DATA
					write_aac_data_to_file(audio_info->bIsInterleaved, audio_info->dwChannels, temp_data->oneframe_conf.pbyOutBuf, encode_data_size);
					#endif //DUMP_ELD_ENC_DATA
				#endif //DUMP_ENC_DATA
				if (SRB == g_eType) {
					SRB_SendGetWriterBuff(temp_data->srb_handle, &temp_data->srb_buf);
				} else if (SCM == g_eType) {
					temp_data->scm_buf.dwOccupiedSpace = MAX_AUDIO_DATA_HEADER_SIZE + encode_data_size;
					SCM_SendGetWriterBuff(temp_data->scm_handle, &temp_data->scm_buf);
				}
			}
			else if(encode_data_size < 0)
			{
				fprintf(stderr, "[%s, %s]: Encode error !!!\n", __FILE__, __func__);
			}
			++(temp_data->seq_num);
		}
	}
}

static void msg_callback(MsgContext* msg, void* user_data)
{
	user_data_t *temp_data = (user_data_t*) user_data;

	if (!strncasecmp(msg->pszHost, "encoder", 7))
	{
		if (!strcasecmp(msg->pszCmd, "start"))
		{
			if (msg->dwDataSize) {
				pid_t *ptNew_pid =  (pid_t *)msg->pbyData;
				pid_start(*ptNew_pid);
			} else {
				pid_default(1);
			}
			
			printf("start .............. \n");
		}
		else if (!strcasecmp(msg->pszCmd, "stop"))
		{
			
			if (msg->dwDataSize) {
				pid_t *ptNew_pid =  (pid_t *)msg->pbyData;
				pid_stop(*ptNew_pid);
			} else {
				pid_default(0);
			}
			
			printf("stop .............. \n");
		}
		else if (!strcasecmp(msg->pszCmd, "forceCI"))
		{
			printf("forceCI ..............\n");
			temp_data->send_conf = true;
		}
	}
	else if( !strcasecmp(msg->pszHost, SR_MODULE_NAME) ){
		if( !strcasecmp(msg->pszCmd, SUSPEND_CMD) ) {
			pthread_mutex_lock(&(temp_data->data_mutex));
			temp_data->process_status = STOP;
			pthread_cond_wait(&(temp_data->data_cond), &(temp_data->data_mutex));
			pthread_mutex_unlock(&(temp_data->data_mutex));
			ATK_AudioCap_Release(*(temp_data->p_cap_handle));
			MsgBroker_SuspendAckMsg();
		}else if( !strcasecmp(msg->pszCmd, RESUME_CMD) ) {
			*(temp_data->p_cap_handle) = ATK_AudioCap_Init(temp_data->p_audiocap_config);
			pthread_mutex_lock(&(temp_data->data_mutex));
			temp_data->process_status = START;
			pthread_mutex_unlock(&(temp_data->data_mutex));
		}
	}
	if (msg->bHasResponse)
		msg->dwDataSize = 0;
}

static void exit_process()
{
	is_terminate_ = 1;
}

static void sig_kill(int signo)
{
	fprintf(stderr, "[%s,%s] Receive SIGNAL %d!!!\n", __FILE__, __func__, signo);
	switch(signo)
	{
		case SIGTERM:
		case SIGINT:
			exit_process();
			break;
		default:
			break;
	}
}

static void print_usage(const char *ap_name)
{
	fprintf(stderr, "Usage:\n"
			"    %s -d PCM_device_name -r sample_rate -C codec_type [-b bit_rate] [-m compression_mode] [-S aac_stereo_mode] [-f command_FIFO_path] [-i input_type] [-a aot] [-D][-h]\n"
			"Options:\n"
			"    -d                 PCM device name for ALSA (ex: hw:0,0).\n"
			"    -r                 Sample rate for audio.\n"
			"    -C                 Codec type for encoder (0: AAC4, 1: G711, 2: G726).\n"
			"    -b                 Bit rate for audio encoder (AAC4: 8000 ~ 320000, G726: 16000, 24000, 32000, 40000, G711: 64000).\n"
			"                       (GAMR:4750 ,5150 , 5900, 6700, 7400, 7950, 10200, 12200)\n"
			"    -m                 Compression mode for G711 (0: ulaw, 1: alaw).\n"
			"    -x                 Bitstream format for AAC (0: raw, 1: ADTS, 2: ADIF).\n"
			"    -S                 Stereo mode to control the desired output channel of AAC (0: stereo, 1: joint stereo, 3: mono).\n"
			"    -f                 The path of command FIFO.\n"
			"    -i                 Input type of audio (0: MicIn, 1: LineIn. Default: LineIn).\n"
			"    -D                 Run as Daemon.\n"
			"    -a                 AAC4 AOT setting (0: LC mode, 1: ELD).\n"
			"    -t                 0(SRB mode)/1(SCM mode).\n"
			"    -h                 This help.\n", ap_name);

	fprintf(stderr, "ex:\n"
			"%s -d \"hw:0,0\" -r 8000 -C 0 -b 32000 -B \"aenc_srb_\" -s 1 -o 1 -f \"/tmp/aenc/c0/command.fifo\"\n"
			"%s -d \"hw:0,0\" -r 8000 -C 0 -b 32000 -x 0 -S 0\n"
			"%s -d \"hw:0,0\" -r 8000 -C 1 -b 64000 -m 0\n"
			"%s -d \"hw:0,0\" -r 8000 -C 2 -b 40000\n", ap_name, ap_name, ap_name, ap_name);
}

int main(int argc, char **argv)
{
	int opt;
	bool is_daemon = false;
	// Default setting.
	int codec_type = -1; // 0: AAC4, 1: G711, 2: G726 3: GAMR 4: GPCM
	int bit_rate = -1; // AAC4: 8000, 16000, 24000, 32000, G726: 16000, 24000, 32000, 40000, G711: 64000
	int compression_mode = -1; // 0: ulaw, 1: alaw
	int adts = 0; // 0: raw, 1: ADTS, 2: ADIF
	unsigned int aac_stereo_mode = 0; // 0: stereo, 1: joint stereo, 3: mono
	int input_type = 1; //0: MicIn, 1: LineIn
	std::string srb_name = "aenc_srb_1";
	std::string pcm_name = "hw:0,0";
	std::string cmd_fifo_path = CMD_FIFO_PATH;
	int aot = 0; // 0: LC mode, 1:ELD mode
	user_data_t user_data;

	ATK_AUDIOCAP_CONFIG_T audiocap_config;
	ATK_AUDIOCAP_HANDLE_T *cap_handle = NULL;
	memset(&audiocap_config, 0, sizeof(ATK_AUDIOCAP_CONFIG_T));
	memset(&user_data, 0, sizeof(user_data_t));

	audiocap_config.szPcmName = pcm_name.c_str();
	audiocap_config.bIsInterleaved = 1;
	audiocap_config.eFormat = SND_PCM_FORMAT_S16_LE;
	audiocap_config.dwChannelsCount = 2;
	audiocap_config.dwSampleRate = 8000;
	audiocap_config.dwPeriodsPerBuffer = 8;
	audiocap_config.dwPeriodSizeInFrames = PERIOD_SIZE_IN_FRAMES;
	audiocap_config.bUseSimpleConfig = 0;

	while ((opt = getopt(argc, argv, "Dhd:r:C:b:m:f:x:i:S:s:T:a:t:p:M:")) != -1)
	{
		switch(opt)
		{
			case 'd':
				pcm_name = optarg;
				audiocap_config.szPcmName = pcm_name.c_str();
				break;
			case 'r':
				audiocap_config.dwSampleRate = atoi(optarg);
				break;
			case 'C':
				codec_type = atoi(optarg);
				break;
			case 'b':
				bit_rate = atoi(optarg);
				break;
			case 'm':
				compression_mode = atoi(optarg);
				break;
			case 'x':
				adts = atoi(optarg);
				break;
			case 'S':
				aac_stereo_mode = atoi(optarg);
				break;
			case 'f':
				cmd_fifo_path = optarg;
				break;
			case 'D':
				is_daemon = true;
				break;
			case 'i':
				input_type = (atoi(optarg) != 0)? 1:0;
				break;
			case 'a':
				aot = atoi(optarg);
				break;
			case 't':
				g_eType = (MEM_TYPE) atoi(optarg);
				break;
			case 'h':
			default:
				print_usage(argv[0]);
				exit(EXIT_FAILURE);
		}
	}
	
#ifdef HAS_HW_AAC_ENC
	if (aot == 1) {
		fprintf(stderr, "[%s,%s] AAC ELD only supprt in AAC SW Enc.(Currently using HW Enc)\n", __FILE__, __func__);
		return -1;		
	}
#endif 
	
	signal(SIGTERM, sig_kill);
	signal(SIGINT, sig_kill);

	if (is_daemon)
	{
		daemon(1,1);
	}

	if (input_type == 1)
	{
		ATK_Audio_InputSelection(kTKAudioLineIn);

		//set audio volume to 90
		ATK_Audio_SetCaptureVolume(90);

	}
	else
	{
		ATK_Audio_InputSelection(kTKAudioMicIn);

		//set audio volume to 90
		ATK_Audio_SetCaptureVolume(90);
	}

	// Reset the configurations for each encoder.
	ATK_AAC4ENC_CONFIG_T aac4_config;
	ATK_G711ENC_CONFIG_T g711_config;
	ATK_G726ENC_CONFIG_T g726_config;
	ATK_GAMRENC_CONFIG_T gamr_config;
	ATK_AUDIOENC_INITOPT_T audioenc_initopt;

	memset(&aac4_config, 0, sizeof(ATK_AAC4ENC_CONFIG_T));
	memset(&g711_config, 0, sizeof(ATK_G711ENC_CONFIG_T));
	memset(&g726_config, 0, sizeof(ATK_G726ENC_CONFIG_T));
	memset(&gamr_config, 0, sizeof(ATK_GAMRENC_CONFIG_T));
	memset(&audioenc_initopt, 0, sizeof(ATK_AUDIOENC_INITOPT_T));

	// Callbacks for audio capture and the private user data for callback.	
	user_data.send_conf = true;
	pthread_mutex_init(&(user_data.data_mutex), NULL); 
	pthread_cond_init(&(user_data.data_cond), NULL);
	user_data.process_status = START;
	user_data.p_audiocap_config = &audiocap_config;
	user_data.p_cap_handle = &cap_handle;

	audiocap_config.pfnCallback = audiocap_callback;
	audiocap_config.pUserData = (void*) (&user_data);


	audioenc_initopt.dwChannels = audiocap_config.dwChannelsCount;
	audioenc_initopt.bIsInterleaved = audiocap_config.bIsInterleaved;
	switch(codec_type)
	{
		case kAAC4: // AAC4
			if((bit_rate < 8000) || (bit_rate > 320000))
			{
				fprintf(stderr, "[%s,%s] AAC4 doesn't support bit rate = %d\n", __FILE__, __func__, bit_rate);
				return -1;
			}
			if( (audiocap_config.dwSampleRate < 8000) || (audiocap_config.dwSampleRate > 96000) )
			{
				fprintf(stderr, "[%s,%s] AAC doesn't support sample rate = %d\n", __FILE__, __func__, audiocap_config.dwSampleRate);
				return -1;
			}
			if (aot == 1){
				if (audiocap_config.dwSampleRate < 16000) {
					fprintf(stderr, "[%s,%s] AAC4 sample rate = %d, must be 16000 or more\n", __FILE__, __func__, bit_rate);
					fprintf(stderr, "[%s,%s] Please refer to fdk-aac guide, aacenc_lib.h\n",__FILE__,__func__);
					return -1;
				}
				if (bit_rate < 56000) {
					fprintf(stderr, "[%s,%s] AAC bit rate(%d) is too small\n", __FILE__, __func__, audiocap_config.dwSampleRate);
					fprintf(stderr, "[%s,%s] Please refer to fdk-aac guide, aacenc_lib.h\n",__FILE__,__func__);
					return -1;
				}
			}

			audioenc_initopt.eType = kAAC4;
			aac4_config.dwBitRate = bit_rate; // 8000, 16000, 24000, 32000
			aac4_config.dwSampleRate = audiocap_config.dwSampleRate;
			aac4_config.dwAdts = adts;
			aac4_config.dwStereoMode = aac_stereo_mode;
			aac4_config.dwELDMode = aot;
			if (aot == 1)
				audioenc_initopt.dwPeriodSizeInFrames = PERIOD_SIZE_IN_FRAMES_AAC/2;
			else
				audioenc_initopt.dwPeriodSizeInFrames = PERIOD_SIZE_IN_FRAMES_AAC;

			audiocap_config.dwPeriodSizeInFrames = audioenc_initopt.dwPeriodSizeInFrames;
			user_data.oneframe_conf.dwOutBufSize = MAX_ENCODE_DATA_SIZE;

			user_data.enc_type = FOURCC_AAC4;
			user_data.enc_handle = ATK_AudioEnc_Init(&audioenc_initopt, &aac4_config);
			break;
		case kG711: // G711
			if((compression_mode != 0) && (compression_mode != 1))
			{
				fprintf(stderr, "[%s,%s] G711 doesn't support compression mode = %d\n", __FILE__, __func__, compression_mode);
				return -1;
			}
			if(bit_rate != 64000)
			{
				fprintf(stderr, "[%s,%s] G711 doesn't support bit rate = %d\n", __FILE__, __func__, bit_rate);
				return -1;
			}
			if(audiocap_config.dwSampleRate != 8000)
			{
				fprintf(stderr, "[%s,%s] G711 doesn't support sample rate = %d\n", __FILE__, __func__, audiocap_config.dwSampleRate);
				return -1;
			}
			audioenc_initopt.eType = kG711;
			audioenc_initopt.dwPeriodSizeInFrames = PERIOD_SIZE_IN_FRAMES;
			g711_config.iCompressionMode = compression_mode; /* It can be 0: ulaw or 1: alaw. */

			user_data.enc_type = FOURCC_G711;
			user_data.enc_handle = ATK_AudioEnc_Init(&audioenc_initopt, &g711_config);
			break;
		case kG726: // G726
			if((bit_rate != 16000) && (bit_rate != 24000) && (bit_rate != 32000) && (bit_rate != 40000))
			{
				fprintf(stderr, "[%s,%s] G726 doesn't support bit rate = %d\n", __FILE__, __func__, bit_rate);
				return -1;
			}
			if(audiocap_config.dwSampleRate != 8000)
			{
				fprintf(stderr, "[%s,%s] G726 doesn't support sample rate = %d\n", __FILE__, __func__, audiocap_config.dwSampleRate);
				return -1;
			}
			audioenc_initopt.eType = kG726;
			audioenc_initopt.dwPeriodSizeInFrames = PERIOD_SIZE_IN_FRAMES;
			g726_config.dwBitRate = bit_rate; // 16000, 24000, 32000, 40000

			user_data.enc_type = FOURCC_G726;
			user_data.enc_handle = ATK_AudioEnc_Init(&audioenc_initopt, &g726_config);
			break;
		case kGAMR:
#ifdef GAMR_SUPPORT
			if(audiocap_config.dwSampleRate != 8000)
			{
				fprintf(stderr, "[%s,%s] GAMR doesn't support sample rate = %d\n", __FILE__, __func__, audiocap_config.dwSampleRate);
				return -1;
			}
			audiocap_config.dwPeriodSizeInFrames = PERIOD_SIZE_IN_FRAMES_GAMR;
			{
				if(-1 == bit_rate) {
					bit_rate = 4750;
					gamr_config.dwMode = 0;
				}
				else {			
					int gamr_bitrate[] = {4750 ,5150 , 5900, 6700, 7400, 7950, 10200, 12200};
					int ret = -1;
					for(unsigned int i=0;i<8;i++) {
						if(bit_rate == gamr_bitrate[i]) {
							ret = 0;
							gamr_config.dwMode = i;
							break;
						}
					}				
					if(-1 == ret) {
						fprintf(stderr, "[%s,%s] GAMR doesn't support bitrate = %d\n", __FILE__, __func__, bit_rate);
						return -1;					
					}
				}
				//bit_rate = gamr_bitrate[MR122];
			}
			printf("[%s] GAMR bitrate: %d\n", __func__, bit_rate);
			audioenc_initopt.eType = kGAMR;
			audioenc_initopt.dwPeriodSizeInFrames = PERIOD_SIZE_IN_FRAMES_GAMR;
			gamr_config.dwBitRate = bit_rate;
			gamr_config.dwSampleRate = audiocap_config.dwSampleRate;

			user_data.enc_type = FOURCC_GAMR;
			user_data.enc_handle = ATK_AudioEnc_Init(&audioenc_initopt, &gamr_config);
#else
			fprintf(stderr, "[%s,%s] GAMR doesn't be supported, please rebuild sdk with GAMR_SUPPORT definition\n", __FILE__, __func__);
#endif
			break;
		case kGPCM:
			user_data.enc_type = FOURCC_GPCM;
			user_data.enc_handle = NULL;
			break;
		default:
			printf("Unknown codec type.\n");
			return -1;
	}

	// Check encoder is initialized
	if(codec_type != kGPCM && user_data.enc_handle == NULL)
	{
		goto main_end;
	}

	if (SRB == g_eType)
	{
		//SynRingBuffer
		user_data.srb_handle = SRB_InitWriter(srb_name.c_str(), MAX_RING_BUF_SIZE, 4);
		memset(&user_data.srb_buf, 0, sizeof(srb_buffer_t));
		// Get first buffer from SyncRingBuf.
		SRB_SendGetWriterBuff(user_data.srb_handle, &user_data.srb_buf);
	} else if (SCM == g_eType)
	{
		user_data.scm_handle = SCM_InitWriter(srb_name.c_str(), MAX_RING_BUF_SIZE << 0, MAX_RING_BUF_SIZE >> 2, 1, 1);
		user_data.scm_buf = SCM_BUFFER_T_DEFAULT;
		SCM_SendGetWriterBuff(user_data.scm_handle, &user_data.scm_buf);
	}

	// Initialize the audio capture.
	cap_handle = ATK_AudioCap_Init(&audiocap_config);
	if(cap_handle == NULL)
	{
		fprintf(stderr, "[%s,%s] Can't initialize the audio capture.\n", __FILE__, __func__);
		goto main_end;
	}
#ifdef DUMP_PCM_DATA
	if(open_pcm_file() < 0)
	{
		goto main_end;
	}
#endif
#ifdef DUMP_ENC_DATA
	if(open_aac_file() < 0)
	{
		goto main_end;
	}
#endif

	MsgBroker_RegisterMsg(cmd_fifo_path.c_str());
	// Enter the main message loop.
	MsgBroker_Run(cmd_fifo_path.c_str(), msg_callback, &user_data, &is_terminate_);
	MsgBroker_UnRegisterMsg();

main_end:
	ATK_AudioCap_Release(cap_handle);
	ATK_AudioEnc_Release(user_data.enc_handle);
	if (SRB == g_eType)
		SRB_Release(user_data.srb_handle);
	else if (SCM == g_eType)
		SCM_Release(user_data.scm_handle);
	pthread_mutex_destroy(&(user_data.data_mutex));
	pthread_cond_destroy(&(user_data.data_cond));
#ifdef DUMP_PCM_DATA
	close_pcm_file();
#endif
#ifdef DUMP_ENC_DATA
	close_aac_file();
#endif

	return 0;
}
