gf_ai_box/include/modules/apps/alacaenc/playback_alac_mmap.cpp
2026-04-12 17:47:54 +08:00

466 lines
13 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 <stdio.h>
#include <string.h>
#include <signal.h>
#include <stdlib.h>
#include <assert.h>
#include <string>
#include <list>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <memory>
#include <audiotk/audio_common.h>
#include <audiotk/audio_playback_mmap.h>
#include <MsgBroker/msg_broker.h>
#include <SyncRingBuffer/sync_ring_buffer.h>
#include <alac/ALACAudioTypes.h>
#include <alac/ALACDecoder.h>
#include <alac/ALACBitUtilities.h>
//#define DUMP_PCM_DATA
#define CMD_PLAY_FIFO_PATH "/tmp/playback/c0/command.fifo"
typedef struct {
pthread_mutex_t data_mutex;
pthread_cond_t data_cond;
//pthread_mutex_t play_mutex;
pthread_cond_t play_cond;
STATUS process_status;
ATK_AUDIOPLAY_HANDLE_T **p_playback_handle;
ATK_AUDIOPLAY_CONFIG_T *p_config;
} user_data_t;
#ifdef DUMP_PCM_DATA
static int audio_fd_ = -1;
static int open_pcm_file()
{
const char *filename = "./playback_alac_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(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
static int is_terminate_ = 0;
// The handle of ring buffer to receive audio data.
static srb_handle_t* audio_srb_handle_ = NULL;
static void exit_process()
{
is_terminate_ = 1;
if(audio_srb_handle_)
{
// Notify the reader of ring buffer to exit.
SRB_WakeupReader(audio_srb_handle_);
}
}
static void sig_kill(int signo)
{
printf("[%s,%s] Receive SIGNAL %d!!!\n", __FILE__, __func__, signo);
switch(signo)
{
case SIGTERM:
case SIGINT:
exit_process();
break;
default:
break;
}
}
static void dump_trace(int /*signo*/)
{
printf(" ===== Segmentation fault. ===== \n");
exit(EXIT_FAILURE);
}
static void print_usage(const char *ap_name)
{
fprintf(stderr, "Usage:\n"
" %s -d PCM_device_name -r sample_rate -c channels [-b bit_depth] [-R name] [-D]\n"
"Options:\n"
" -b Bit depth for audio (Default: 16 -> S16_LE).\n"
" -d PCM device name for ALSA (ex: hw:0,0).\n"
" -r Sample rate for audio.\n"
" -c The number of audio channels.\n"
" -R The name of ring buffer for receiving data.\n"
" -D Run as Daemon.\n"
" -h This help.\n"
, ap_name);
fprintf(stderr, "ex:\n"
"%s -d \"hw:0,0\" -r 8000 -c 2\n"
"%s -d \"hw:0,0\" -r 8000 -c 2 -B -R audio_backchannel_srb_1\n", ap_name, ap_name);
}
static void send_cmd(const char *cmd)
{
// Send the command to the audio encoder.
MsgContext msg_context;
pid_t connect_pid = getpid();
msg_context.bHasResponse = 0;
msg_context.pszHost = "encoder";
msg_context.dwHostLen = strlen(msg_context.pszHost) + 1;
msg_context.pszCmd = cmd;
msg_context.dwCmdLen = strlen(msg_context.pszCmd) + 1;
msg_context.dwDataSize = sizeof(pid_t);
msg_context.pbyData = (unsigned char *) &connect_pid;
MsgBroker_SendMsg(CMD_FIFO_PATH, &msg_context);
}
static void* play_interleaved_data(void* args) {
unsigned int* values = NULL;
unsigned char* temp_buf = NULL;
user_data_t *temp_data = (user_data_t*) args;
ATK_AUDIOPLAY_HANDLE_T *playback_handle = *(temp_data->p_playback_handle);
#ifdef DUMP_PCM_DATA
// Get total bytes of data in one period.
size_t period_frame_size = ATK_AudioPlay_GetPeriodFramesSize(playback_handle);
#endif
// Audio output buffer.
unsigned char *bufs[ATK_AUDIO_MAX_CHANNELS] = {NULL};
// Prepare the buffer pointer for the ring buffer.
srb_buffer_t srb_buf;
memset(&srb_buf, 0, sizeof(srb_buffer_t));
ALACDecoder* decoder = NULL;
AudioFormatDescription outputFormat;
uint8_t* theMagicCookie = NULL;
uint32_t theMagicCookieSize = 0;
// Tell the audio encoder to send the configuration data.
send_cmd("forceCI");
outputFormat.mFormatID = kALACFormatLinearPCM;
outputFormat.mSampleRate = temp_data->p_config->dwSampleRate;
outputFormat.mBitsPerChannel = 16;
switch (temp_data->p_config->eFormat)
{
case SND_PCM_FORMAT_S16_LE:
outputFormat.mBitsPerChannel = 16;
break;
case SND_PCM_FORMAT_S24_LE:
outputFormat.mBitsPerChannel = 24;
break;
case SND_PCM_FORMAT_S32_LE:
outputFormat.mBitsPerChannel = 32;
break;
default:
break;
}
outputFormat.mFramesPerPacket = 1;
outputFormat.mChannelsPerFrame = temp_data->p_config->dwChannelsCount;
outputFormat.mBytesPerPacket = outputFormat.mBytesPerFrame = outputFormat.mBitsPerChannel != 20
? temp_data->p_config->dwChannelsCount * ((outputFormat.mBitsPerChannel) >> 3) : (int32_t)(temp_data->p_config->dwChannelsCount * 2.5 + .5);
outputFormat.mFormatFlags = kALACFormatFlagsNativeEndian;
outputFormat.mReserved = 0;
while (!is_terminate_)
{
if (temp_data->process_status == STOP) {
pthread_mutex_lock(&(temp_data->data_mutex));
pthread_cond_signal(&(temp_data->data_cond));
pthread_mutex_unlock(&(temp_data->data_mutex));
pthread_mutex_lock(&(temp_data->data_mutex));
pthread_cond_wait(&(temp_data->play_cond), &(temp_data->data_mutex));
pthread_mutex_unlock(&(temp_data->data_mutex));
}
#ifdef DUMP_PCM_DATA
if(bufs[0] != NULL)
{
write_pcm_data_to_file(bufs, period_frame_size);
}
#endif
// Output the current buffer and get the next output buffer.
if(ATK_AudioPlay_PlayPeriodFrames(playback_handle, bufs) < 0)
{
fprintf(stderr, "[%s,%s] Can't get the audio output buffer..\n", __FILE__, __func__);
break;
}
// Get the buffer with audio data from the audio encoder.
if(SRB_ReturnReceiveReaderBuff(audio_srb_handle_, &srb_buf) == 0)
{
// We check whether the data is audio data or not.
values = (unsigned int*)srb_buf.buffer;
if(values[0] != FOURCC_CONF)
{
// This is audio data.
// Check whether the data is ALAC or not.
if(values[0] == FOURCC_ALAC && decoder)
{
// This is the payload.
BitBuffer input_bit_buffer;
uint32_t data_bytes = values[3];
uint32_t frames = 0;
temp_buf = srb_buf.buffer + MAX_AUDIO_DATA_HEADER_SIZE;
BitBufferInit(&input_bit_buffer, (uint8_t*)temp_buf, data_bytes);
decoder->Decode(&input_bit_buffer, bufs[0], 1, 2, &frames);
//uint32_t numBytes = frames * outputFormat.mBytesPerFrame;
//printf("ALAC decoded %u bytes, %u frames, mBytesPerFrame = %u\n", numBytes, numFrames, outputFormat.mBytesPerFrame);
}
}
else
{
// This is configuration data.
if(values[2] == FOURCC_ALAC)
{
temp_buf = srb_buf.buffer + MAX_AUDIO_DATA_HEADER_SIZE;
theMagicCookieSize = values[5];
theMagicCookie = (uint8_t *) calloc(theMagicCookieSize, 1);
memcpy(theMagicCookie, &values[6], theMagicCookieSize);
if (decoder) delete decoder;
decoder = new ALACDecoder;
decoder->Init(theMagicCookie, theMagicCookieSize);
free(theMagicCookie);
}
// Tell the audio encoder to start to encode data.
send_cmd("start");
}
}
}
// Return the last buffer to the ring buffer.
SRB_ReturnReaderBuff(audio_srb_handle_, &srb_buf);
// Tell the audio encoder to stop encoding data.
send_cmd("stop");
if (decoder) delete decoder;
return NULL;
}
static void msg_callback(MsgContext* msg, void* args)
{
user_data_t *temp_data = (user_data_t*) args;
if (!strcasecmp(msg->pszHost, SR_MODULE_NAME)) {
if (!strcasecmp(msg->pszCmd, SUSPEND_CMD)) {
pthread_mutex_lock(&(temp_data->data_mutex));
SRB_WakeupReader(audio_srb_handle_);
temp_data->process_status = STOP;
pthread_cond_wait(&(temp_data->data_cond), &(temp_data->data_mutex));
pthread_mutex_unlock(&(temp_data->data_mutex));
if(*(temp_data->p_playback_handle)) ATK_AudioPlay_Release(*(temp_data->p_playback_handle));
MsgBroker_SuspendAckMsg();
}else if(!strcasecmp(msg->pszCmd, RESUME_CMD) ) {
*(temp_data->p_playback_handle) = ATK_AudioPlay_Init(temp_data->p_config);
pthread_mutex_lock(&(temp_data->data_mutex));
temp_data->process_status = START;
pthread_cond_signal(&(temp_data->play_cond));
pthread_mutex_unlock(&(temp_data->data_mutex));
}
}
if (msg->bHasResponse) msg->dwDataSize = 0;
}
int main(int argc, char **argv)
{
int opt;
bool is_daemon = false;
int bit_depth = 16;
pthread_t playback_tid = 0;
ATK_AUDIOPLAY_HANDLE_T *playback_handle = NULL;
ATK_AUDIOPLAY_CONFIG_T config;
std::string pcm_name = "hw:0,0";
std::string ring_buf_name;
// Default value of configuration.
memset(&config, 0, sizeof(ATK_AUDIOPLAY_CONFIG_T));
config.szPcmName = pcm_name.c_str();
config.bIsInterleaved = 1;
config.eFormat = SND_PCM_FORMAT_S16_LE;
config.dwChannelsCount = 2;
config.dwSampleRate = 8000;
config.bUseSimpleConfig = 0;
config.dwPeriodsPerBuffer = 8;
config.dwPeriodSizeInFrames = PERIOD_SIZE_IN_FRAMES;
while ((opt = getopt(argc, argv, "b:c:d:Dh:r:R:")) != -1)
{
switch(opt)
{
case 'b':
bit_depth = atoi(optarg);
break;
case 'c':
config.dwChannelsCount = atoi(optarg);
break;
case 'd':
pcm_name = optarg;
config.szPcmName = pcm_name.c_str();
break;
case 'h':
print_usage(argv[0]);
exit(EXIT_FAILURE);
case 'r':
config.dwSampleRate = atoi(optarg);
break;
case 'D':
is_daemon = true;
break;
case 'R':
ring_buf_name = optarg;
break;
default:
print_usage(argv[0]);
exit(EXIT_FAILURE);
}
}
switch (bit_depth)
{
case 16:
config.eFormat = SND_PCM_FORMAT_S16_LE;
break;
case 24:
config.eFormat = SND_PCM_FORMAT_S24_LE;
break;
case 32:
config.eFormat = SND_PCM_FORMAT_S32_LE;
break;
default:
print_usage(argv[0]);
exit(EXIT_FAILURE);
}
signal(SIGTERM, sig_kill);
signal(SIGINT, sig_kill);
signal(SIGSEGV, dump_trace);
if (is_daemon)
{
daemon(1,1);
}
user_data_t user_data;
memset(&user_data, 0, sizeof(user_data_t));
pthread_mutex_init(&(user_data.data_mutex), NULL);
pthread_cond_init(&(user_data.data_cond), NULL);
pthread_cond_init(&(user_data.play_cond), NULL);
user_data.process_status = START;
user_data.p_playback_handle = &playback_handle;
user_data.p_config = &config;
// Initialize audio playback.
playback_handle = ATK_AudioPlay_Init(&config);
if(playback_handle == NULL)
{
fprintf(stderr, "[%s,%s] Can't initialize the audio playback.\n", __FILE__, __func__);
goto main_end;
}
// Initialize the reader of ring buffer.
if(ring_buf_name.empty())
{
ring_buf_name = "aenc_srb_1";
}
audio_srb_handle_ = SRB_InitReader(ring_buf_name.c_str());
if(audio_srb_handle_ == NULL)
{
fprintf(stderr, "[%s,%s] Can't initialize the reader of ring buffer.\n", __FILE__, __func__);
goto main_end;
}
#ifdef DUMP_PCM_DATA
if(open_pcm_file() < 0)
{
goto main_end;
}
#endif
pthread_create(&playback_tid, NULL, play_interleaved_data, &user_data);
MsgBroker_RegisterMsg(CMD_PLAY_FIFO_PATH);
MsgBroker_Run(CMD_PLAY_FIFO_PATH, msg_callback, (void *)&user_data, (int *)&is_terminate_);
MsgBroker_UnRegisterMsg();
main_end:
pthread_join(playback_tid, NULL);
#ifdef DUMP_PCM_DATA
close_pcm_file();
#endif
// Release audio playback.
if (playback_handle) ATK_AudioPlay_Release(playback_handle);
pthread_mutex_destroy(&(user_data.data_mutex));
pthread_cond_destroy(&(user_data.data_cond));
pthread_cond_destroy(&(user_data.play_cond));
// Release the reader of ring buffer.
if(audio_srb_handle_) SRB_Release(audio_srb_handle_);
return 0;
}