893 lines
26 KiB
C++
893 lines
26 KiB
C++
/*
|
|
*******************************************************************************
|
|
* 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;
|
|
}
|