Google官方网站:
Audio Hal 适配: https://source.android.google.cn/devices/audio/aaudio
AAudio 上层应用使用说明:https://developer.android.google.cn/ndk/guides/audio/aaudio/aaudio
AAudio API说明文档:https://developer.android.com/ndk/reference/group/audio
原流程图下载地址:https://download.csdn.net/download/u013120422/11937875
Audio Hal 需适配一下4个接口
int (*start)(const struct audio_stream_out stream);
int (*stop)(const struct audio_stream_out stream);
int (*create_mmap_buffer)(const struct audio_stream_out *stream,
int32_t min_size_frames,
struct audio_mmap_buffer_info *info);
int (*get_mmap_position)(const struct audio_stream_out *stream,
struct audio_mmap_position *position);
需要使用的tinyalsa接口:
pcm_mmap_commit // 更新appl_ptr到alsa
pcm_mmap_avail // 获取当前buffer的Empty空闲大小
pcm_prepare // 切换pcm的状态为 PREPARED
pcm_get_htimestamp // 将driver的appl_ptr指针位置同步到alsa
pcm_mmap_begin // 获取当前mmap的内存地址
pcm_get_poll_fd // 获取当前mmap的fd
pcm_mmap_get_hw_ptr //获取当前 alsa的hw_ptr值,以及相对时间(以1970年为基准的时间戳)
注意:
get_mmap_position接口中的timestamp必须使用绝对时间戳;
适配请参考以下示例
typedef struct AML_MMAP_THREAD_PARAM {
pthread_t threadId;
bool bExitThread;
bool bStopPlay;
pthread_condattr_t condAttr;
pthread_mutex_t mutex;
pthread_cond_t cond;
} aml_mmap_thread_param_st;
static aml_mmap_thread_param_st g_stMmapThreadParam;
static void *out_mmap_commit_threadloop(void *pArg) {
struct aml_stream_out *out = (struct aml_stream_out *) pArg;
ALOGI("%s:%d enter threadloop bExitThread:%d, bStopPlay:%d", __func__, __LINE__,
g_stMmapThreadParam.bExitThread, g_stMmapThreadParam.bStopPlay);
while (false == g_stMmapThreadParam.bExitThread) {
if (false == g_stMmapThreadParam.bStopPlay) {
unsigned int u32AvailEmptySize = 0;
unsigned int u32AvailDataSize = 0;
u32AvailEmptySize = pcm_mmap_avail(out->pcm);
if ((unsigned int)u32AvailEmptySize > out->config.period_size*out->config.period_count) {
u32AvailDataSize = out->config.period_size * out->config.period_count;
} else {
u32AvailDataSize = out->config.period_size * out->config.period_count - u32AvailEmptySize;
}
// Commit appl_ptr only when the available data is less than or equal to half.
if (u32AvailDataSize <= out->config.period_size * out->config.period_count / 2) {
pcm_mmap_commit(out->pcm, 0, out->config.period_size);
}
} else {
struct timespec tv;
clock_gettime(CLOCK_MONOTONIC, &tv);
// The suspend time set to 10 sec, reduce cpu power consumption.
// And waitting time can be awakened by out_start func.
tv.tv_sec += 10;
pthread_mutex_lock(&g_stMmapThreadParam.mutex);
pthread_cond_timedwait(&g_stMmapThreadParam.cond, &g_stMmapThreadParam.mutex, &tv);
pthread_mutex_unlock(&g_stMmapThreadParam.mutex);
ALOGI("%s:%d starting threadloop", __func__, __LINE__);
}
}
ALOGI("%s:%d exit threadloop", __func__, __LINE__);
return NULL;
}
static int out_start(const struct audio_stream_out *stream)
{
struct aml_stream_out *out = (struct aml_stream_out *) stream;
int s32Ret = 0;
unsigned int u32AvailEmpty = 0;
struct timespec stTimeStamp;
// pcm_prepare can reset driver read and write pointer.
pcm_prepare(out->pcm);
// pcm_get_htimestamp func can sync driver appl_ptr and hw_ptr to alsa is 0.
pcm_get_htimestamp(out->pcm, &u32AvailEmpty, &stTimeStamp);
// It must fill data to driver before pcm_start or it will fail.
s32Ret = pcm_mmap_commit(out->pcm, 0, out->config.period_size);
g_stMmapThreadParam.bStopPlay = false;
ALOGI("%s:%d ret:%#x, AvailEmpty:%d", __func__, __LINE__, s32Ret, u32AvailEmpty);
pthread_mutex_lock(&g_stMmapThreadParam.mutex);
pthread_cond_signal(&g_stMmapThreadParam.cond);
pthread_mutex_unlock(&g_stMmapThreadParam.mutex);
s32Ret = pcm_start(out->pcm);
if (s32Ret != 0) {
ALOGE("%s:%d pcm_start fail ret:%#x, err:%s", __func__, __LINE__, s32Ret, strerror(errno));
}
return s32Ret;
}
static int out_stop(const struct audio_stream_out *stream)
{
struct aml_stream_out *out = (struct aml_stream_out *) stream;
// suspend threadloop.
g_stMmapThreadParam.bStopPlay = true;
//g_stMmapThreadParam.bExitThread = true;
//pthread_join(g_stMmapThreadParam.threadId, NULL);
int s32Ret = pcm_stop(out->pcm);
ALOGI("%s:%d stream:%p, ret:%#x", __func__, __LINE__, stream, s32Ret);
return s32Ret;
}
static int out_create_mmap_buffer(const struct audio_stream_out *stream,
int32_t min_size_frames,
struct audio_mmap_buffer_info *info)
{
ALOGI("%s:%d, stream:%p, min_size_frames:%d", __func__, __LINE__, stream, min_size_frames);
int s32Ret = 0;
struct aml_stream_out *out = (struct aml_stream_out *) stream;
struct aml_audio_device *adev = out->dev;
unsigned int u32Offset = 0;
unsigned int u32Frames = 0;
int card = alsa_device_get_card_index();
int port = alsa_device_update_pcm_index(PORT_I2S, PLAYBACK);
out->config = pcm_config_out;
out->config.avail_min = min_size_frames;
out->config.start_threshold = out->config.period_size;
if (adev->pcm_handle[I2S_DEVICE]) {
ALOGE("%s:%d, pcm:%p device already opened, card:%d, port:%d, cnt:%d", __func__, __LINE__,
adev->pcm_handle[I2S_DEVICE], card, port, adev->pcm_refs[I2S_DEVICE]);
return -1;
}
out->pcm = pcm_open(card, port, PCM_OUT | PCM_MMAP | PCM_NONEBLOCK, &out->config);
if (!out->pcm) {
ALOGW("%s:%d, pcm_open fail, card:%d, port:%d", __func__, __LINE__, card, port);
return -1;
}
adev->pcm = out->pcm;
adev->pcm_handle[I2S_DEVICE] = out->pcm;
out->device = I2S_DEVICE;
adev->pcm_refs[I2S_DEVICE]++;
pcm_mmap_begin(out->pcm, &info->shared_memory_address, &u32Offset, &u32Frames);
info->shared_memory_fd = pcm_get_poll_fd(out->pcm);
info->buffer_size_frames = pcm_config_out.period_count * pcm_config_out.period_size;
info->burst_size_frames = pcm_config_out.period_size;
pthread_condattr_init(&g_stMmapThreadParam.condAttr);
pthread_condattr_setclock(&g_stMmapThreadParam.condAttr, CLOCK_MONOTONIC);
pthread_mutex_init (&g_stMmapThreadParam.mutex, NULL);
pthread_cond_init(&g_stMmapThreadParam.cond, &g_stMmapThreadParam.condAttr);
g_stMmapThreadParam.bExitThread = false;
g_stMmapThreadParam.bStopPlay = true;
pthread_create(&g_stMmapThreadParam.threadId, NULL, &out_mmap_commit_threadloop, out);
ALOGI("%s:%d, pcm:%p, mmap_fd:%d, mmap_address:%p", __func__, __LINE__,
out->pcm, info->shared_memory_fd, info->shared_memory_address);
out->status = STREAM_HW_WRITING;
return 0;
}
static int out_get_mmap_position(const struct audio_stream_out *stream,
struct audio_mmap_position *position)
{
struct timespec timestamp;
int s32Ret = 0;
struct aml_stream_out *out = (struct aml_stream_out *) stream;
// Absolutet time must be used when get timestamp.
clock_gettime(CLOCK_MONOTONIC, ×tamp);
position->time_nanoseconds = (long long)timestamp.tv_sec * 1000000000 + (long long)timestamp.tv_nsec;
position->position_frames = 0;
// Gets currently palyed frames.
s32Ret = pcm_mmap_get_hw_ptr(out->pcm, (unsigned int *)&position->position_frames, ×tamp);
ALOGI("%s:%d stream:%p, position_frames:%d, nano:%lld, ret:%#x",__func__, __LINE__, stream,
position->position_frames, (long long)position->time_nanoseconds, s32Ret);
return s32Ret;
}