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
“AAudio 是在 Android O 版本中引入的全新 Android C API。此 API 专为需要低延迟的高性能音频应用而设计。”相对于之前的AudioFlinger音频引擎来说,AAudio整个通路的缓存都设置很小,同时也对我们vendor的音频处理实时性要求更高,最终降低整个音频通路的延时,为对实时性要求高的音频流的第三方apk提供帮助。
特征:
低延时、仅支持PCM流、不支持自动化路由
Code路径:
libaaudio.so: frameworks/av/media/libaaudio (运行在客户端进程)
libaaudioservice.so: frameworks/av/services/oboeservice (运行在audioserver进程的服务)
原流程图下载地址:https://download.csdn.net/download/u013120422/11937875
函数:AAudioServiceStreamShared::calculateBufferCapacity
未指定BufferCapacity时,AAudioStream_getBufferCapacityInFrames值是AAudioStream_getFramesPerBurst值的16倍(DEFAULT_BURSTS_PER_BUFFER)
①.service端 service-client msg管理buffer (AAudioServiceStreamShared父类open中分配mUpMessageQueue[SharedRingBuffer]->mFifoBuffer);
②.MMAP端MMAP-service msg管理buffer (AAudioServiceStreamMMAP父类open中分配mUpMessageQueue[SharedRingBuffer]->mFifoBuffer);③.service端service-MMAP msg管理buffer (mUpCommandQueue);
④.service端service-ION data管理buffer (mDataQueue);
⑤.service端service-client data管理 buffer (AAudioServiceStreamShared::open中分配mAudioDataQueue[SharedRingBuffer]->mFifoBuffer);
⑥.client端client-service msg管理buffer (mUpCommandQueue);
⑦.client端client-service data管理buffer (mDataQueue);
一个AAudioServiceStreamShared实例为一路Track,AAudioServiceStreamShared.cpp可为多个实例;
AAudioServiceStreamMMAP.cpp为专有模式,只能拥有一个实例
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);
/*
* Copyright (C) 2019 hippo Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "hippo_mmap_audio"
#include
#include
#include
#include
#include "audio_virtual_buf.h"
#include "audio_hw.h"
#include "hippo_android_utils.h"
#include "hippo_volume_utils.h"
#include "audio_hw_utils.h"
#include "hippo_mmap_audio.h"
#include "audio_hw_ms12.h"
#include "hippo_audio_timer.h"
#define MMAP_FRAME_SIZE_BYTE (4)
#define MMAP_SAMPLE_RATE_HZ (48000)
#define MMAP_BUFFER_SIZE_BYTE (MMAP_SAMPLE_RATE_HZ * MMAP_FRAME_SIZE_BYTE * 32 / 1000)
#define MMAP_WRITE_SIZE_BYTE (MMAP_SAMPLE_RATE_HZ * MMAP_FRAME_SIZE_BYTE * 8 / 1000) // every time to write 8ms data
#define MMAP_WRITE_SIZE_FRAME (MMAP_WRITE_SIZE_BYTE / MMAP_FRAME_SIZE_BYTE)
#define MMAP_WRITE_PERIOD_TIME_NANO (MMAP_WRITE_SIZE_FRAME * 1000000000LL / MMAP_SAMPLE_RATE_HZ)
enum {
MMAP_INIT,
MMAP_START,
MMAP_START_DONE,
MMAP_STOP,
MMAP_STOP_DONE
};
static FILE *g_pFile = NULL;
static void *outMmapThread(void *pArg) {
struct hippo_stream_out *out = (struct hippo_stream_out *) pArg;
hippo_mmap_audio_param_st *pstParam = (hippo_mmap_audio_param_st *)out->pstMmapAudioParam;
struct audio_virtual_buf *pstVirtualBuffer = NULL;
unsigned char *pu8CurReadAddr = pstParam->pu8MmapAddr;
unsigned char *pu8StartAddr = pstParam->pu8MmapAddr;
unsigned char *pu8TempBufferAddr = NULL;
hippo_mmap_thread_param_st *pstThread = &pstParam->stThreadParam;
struct timespec timestamp;
ALOGI("[%s:%d] enter threadloop bExitThread:%d, bStopPlay:%d, mmap addr:%p, out:%p", __func__, __LINE__,
pstThread->bExitThread, pstThread->bStopPlay, pu8StartAddr, out);
if (NULL == pu8StartAddr) {
ALOGE("[%s:%d] pu8MmapAddr is null", __func__, __LINE__);
return NULL;
}
prctl(PR_SET_NAME, "outMmapThread");
hippo_set_thread_priority("outMmapThread", pstThread->threadId);
pu8TempBufferAddr = (unsigned char *)malloc(MMAP_WRITE_SIZE_BYTE);
while (false == pstThread->bExitThread) {
if (false == pstThread->bStopPlay) {
if (pstThread->status == MMAP_START) {
ALOGI("MMAP status: start");
pu8CurReadAddr = pu8StartAddr;
pstParam->u32FramePosition = 0;
pstThread->status = MMAP_START_DONE;
if (pstVirtualBuffer) {
audio_virtual_buf_reset(pstVirtualBuffer);
audio_virtual_buf_process((void *)pstVirtualBuffer, MMAP_WRITE_PERIOD_TIME_NANO * 4);
}
}
if (pstVirtualBuffer == NULL) {
audio_virtual_buf_open((void **)&pstVirtualBuffer, "aaudio mmap",
MMAP_WRITE_PERIOD_TIME_NANO * 4, MMAP_WRITE_PERIOD_TIME_NANO * 4, 0);
audio_virtual_buf_process((void *)pstVirtualBuffer, MMAP_WRITE_PERIOD_TIME_NANO * 4);
}
unsigned int u32RemainSizeByte = (MMAP_BUFFER_SIZE_BYTE + pu8StartAddr) - pu8CurReadAddr;
if (u32RemainSizeByte >= MMAP_WRITE_SIZE_BYTE) {
memcpy(pu8TempBufferAddr, pu8CurReadAddr, MMAP_WRITE_SIZE_BYTE);
memset(pu8CurReadAddr, 0, MMAP_WRITE_SIZE_BYTE);
pu8CurReadAddr += MMAP_WRITE_SIZE_BYTE;
} else {
memcpy(pu8TempBufferAddr, pu8CurReadAddr, u32RemainSizeByte);
memset(pu8CurReadAddr, 0, u32RemainSizeByte);
memcpy(pu8TempBufferAddr + u32RemainSizeByte, pu8StartAddr, MMAP_WRITE_SIZE_BYTE - u32RemainSizeByte);
memset(pu8StartAddr, 0, MMAP_WRITE_SIZE_BYTE - u32RemainSizeByte);
pu8CurReadAddr = pu8StartAddr + MMAP_WRITE_SIZE_BYTE - u32RemainSizeByte;
}
apply_volume(out->volume_l, pu8TempBufferAddr, 2, MMAP_WRITE_SIZE_BYTE);
if (out->dev->useSubMix) {
out->stream.write(&out->stream, pu8TempBufferAddr, MMAP_WRITE_SIZE_BYTE);
} else {
out_write_new(&out->stream, pu8TempBufferAddr, MMAP_WRITE_SIZE_BYTE);
}
if (hippo_getprop_bool("vendor.media.audiohal.outdump")) {
if (g_pFile) {
fwrite(pu8TempBufferAddr, 1, MMAP_WRITE_SIZE_BYTE, g_pFile);
}
}
audio_virtual_buf_process((void *)pstVirtualBuffer, MMAP_WRITE_PERIOD_TIME_NANO);
if (out->dev->debug_flag >= 100) {
ALOGI("[%s:%d] CurReadAddr:%p, RemainSize:%d, FramePosition:%d offset=%d", __func__, __LINE__,
pu8CurReadAddr, u32RemainSizeByte, pstParam->u32FramePosition, pstParam->u32FramePosition%(MMAP_BUFFER_SIZE_BYTE / MMAP_FRAME_SIZE_BYTE));
}
pstParam->u32FramePosition += MMAP_WRITE_SIZE_FRAME;
// Absolutet time must be used when get timestamp.
clock_gettime(CLOCK_MONOTONIC, ×tamp);
pstParam->time_nanoseconds = (long long)timestamp.tv_sec * 1000000000 + (long long)timestamp.tv_nsec;
} else {
struct timespec tv;
clock_gettime(CLOCK_MONOTONIC, &tv);
// The suspend time set to 30 sec, reduce cpu power consumption.
// And waitting time can be awakened by out_start func.
tv.tv_sec += 30;
pthread_mutex_lock(&pstThread->mutex);
pthread_cond_timedwait(&pstThread->cond, &pstThread->mutex, &tv);
pthread_mutex_unlock(&pstThread->mutex);
}
}
if (pstVirtualBuffer != NULL) {
audio_virtual_buf_close((void **)&pstVirtualBuffer);
}
free(pu8TempBufferAddr);
pu8TempBufferAddr = NULL;
ALOGI("[%s:%d] exit threadloop, out:%p", __func__, __LINE__, out);
return NULL;
}
static int outMmapStart(const struct audio_stream_out *stream)
{
ALOGI("[%s:%d] stream:%p", __func__, __LINE__, stream);
struct hippo_stream_out *out = (struct hippo_stream_out *) stream;
hippo_mmap_audio_param_st *pstParam = (hippo_mmap_audio_param_st *)out->pstMmapAudioParam;
if (0 == pstParam->stThreadParam.threadId) {
ALOGE("[%s:%d] exit threadloop", __func__, __LINE__);
return -ENOSYS;
}
if (hippo_getprop_bool("vendor.media.audiohal.outdump")) {
g_pFile = fopen("/data/audio/pcm_mmap", "a+");
}
pstParam->u32FramePosition = 0;
pstParam->stThreadParam.bStopPlay = false;
pstParam->stThreadParam.status = MMAP_START;
pthread_mutex_lock(&pstParam->stThreadParam.mutex);
pthread_cond_signal(&pstParam->stThreadParam.cond);
pthread_mutex_unlock(&pstParam->stThreadParam.mutex);
ALOGI("--[%s:%d] stream:%p", __func__, __LINE__, stream);
return 0;
}
static int outMmapStop(const struct audio_stream_out *stream)
{
ALOGI("[%s:%d] stream:%p", __func__, __LINE__, stream);
struct hippo_stream_out *out = (struct hippo_stream_out *) stream;
hippo_mmap_audio_param_st *pstParam = (hippo_mmap_audio_param_st *)out->pstMmapAudioParam;
if (hippo_getprop_bool("vendor.media.audiohal.outdump")) {
if (g_pFile) {
fclose(g_pFile);
g_pFile = NULL;
}
}
// suspend threadloop.
pstParam->stThreadParam.status = MMAP_STOP;
/*sleep some time, to make sure the read thread read all the data*/
hippo_audio_sleep(8 * 1000);
pstParam->stThreadParam.bStopPlay = true;
pstParam->stThreadParam.status = MMAP_STOP_DONE;
memset(pstParam->pu8MmapAddr, 0, MMAP_BUFFER_SIZE_BYTE);
pstParam->u32FramePosition = 0;
ALOGI("[--%s:%d] stream:%p", __func__, __LINE__, stream);
return 0;
}
static int outMmapCreateBuffer(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);
struct hippo_stream_out *out = (struct hippo_stream_out *) stream;
hippo_mmap_audio_param_st *pstParam = (hippo_mmap_audio_param_st *)out->pstMmapAudioParam;
int ret = 0;
if (NULL == pstParam) {
ALOGE("[%s:%d] uninitialized, can't create mmap buffer", __func__, __LINE__);
return -1;
}
info->shared_memory_address = pstParam->pu8MmapAddr;
info->shared_memory_fd = pstParam->s32IonShareFd;
info->buffer_size_frames = MMAP_BUFFER_SIZE_BYTE / MMAP_FRAME_SIZE_BYTE;
info->burst_size_frames = 384;
hippo_mmap_thread_param_st *pstThread = &pstParam->stThreadParam;
if (pstThread->threadId != 0) {
ALOGW("[%s:%d] mmap thread already exist, recreate thread", __func__, __LINE__);
pstThread->bExitThread = true;
pstThread->bStopPlay = true;
pthread_mutex_lock(&pstThread->mutex);
pthread_cond_signal(&pstThread->cond);
pthread_mutex_unlock(&pstThread->mutex);
pthread_join(pstThread->threadId, NULL);
memset(pstThread, 0, sizeof(hippo_mmap_thread_param_st));
}
pthread_condattr_init(&pstThread->condAttr);
pthread_condattr_setclock(&pstThread->condAttr, CLOCK_MONOTONIC);
pthread_mutex_init (&pstThread->mutex, NULL);
pthread_cond_init(&pstThread->cond, &pstThread->condAttr);
pstThread->bExitThread = false;
pstThread->bStopPlay = true;
pstThread->status = MMAP_INIT;
pthread_create(&pstThread->threadId, NULL, &outMmapThread, out);
ALOGI("[%s:%d], mmap_fd:%d, mmap address:%p", __func__, __LINE__, info->shared_memory_fd, pstParam->pu8MmapAddr);
return 0;
}
static int outMmapGetPosition(const struct audio_stream_out *stream,
struct audio_mmap_position *position)
{
struct hippo_stream_out *out = (struct hippo_stream_out *) stream;
hippo_mmap_audio_param_st *pstParam = (hippo_mmap_audio_param_st *)out->pstMmapAudioParam;
struct timespec timestamp;
int64_t curr_nanoseconds = 0;
int64_t time_diff = 0;
int drift_frames = 0;
// Absolutet time must be used when get timestamp.
clock_gettime(CLOCK_MONOTONIC, ×tamp);
curr_nanoseconds = (long long)timestamp.tv_sec * 1000000000 + (long long)timestamp.tv_nsec;
time_diff = curr_nanoseconds - pstParam->time_nanoseconds;
if (time_diff <= 8*1000000) {
drift_frames = (time_diff / 1000000) * (48);
ALOGV("normal time diff=%lld drift_frames=%d", time_diff, drift_frames);
} else {
ALOGI("big time diff =%lld", time_diff);
time_diff = 0;
drift_frames = 0;
}
position->time_nanoseconds = pstParam->time_nanoseconds + time_diff;
position->position_frames = pstParam->u32FramePosition + drift_frames;
if (out->dev->debug_flag >= 100) {
ALOGD("[%s:%d] stream:%p, position_frames:%d, nano:%lld",__func__, __LINE__, stream,
position->position_frames, (long long)position->time_nanoseconds);
}
return 0;
}
int outMmapInit(struct hippo_stream_out *out)
{
ALOGI("[%s:%d] stream:%p", __func__, __LINE__, out);
hippo_mmap_audio_param_st *pstParam = NULL;
int ret = 0;
out->stream.start = outMmapStart;
out->stream.stop = outMmapStop;
out->stream.create_mmap_buffer = outMmapCreateBuffer;
out->stream.get_mmap_position = outMmapGetPosition;
if (out->pstMmapAudioParam) {
ALOGW("[%s:%d] already init, can't again init", __func__, __LINE__);
return 0;
}
out->pstMmapAudioParam = (hippo_mmap_audio_param_st *)malloc(sizeof(hippo_mmap_audio_param_st));
pstParam = out->pstMmapAudioParam;
if (pstParam == NULL) {
ALOGW("[%s:%d] mmap audio param memory malloc fail", __func__, __LINE__);
return -1;
}
memset(pstParam, 0, sizeof(hippo_mmap_audio_param_st));
pstParam->s32IonFd = ion_open();
if (pstParam->s32IonFd < 0) {
ALOGE("[%s:%d] ion_open fail ret:%#x", __func__, __LINE__, ret);
return -1;
}
ret = ion_alloc(pstParam->s32IonFd, MMAP_BUFFER_SIZE_BYTE, 32, ION_HEAP_SYSTEM_MASK, 0,
&pstParam->hIonHanndle);
if (ret < 0) {
ALOGE("[%s:%d] ion_alloc fail ret:%#x", __func__, __LINE__, ret);
return -1;
}
ret = ion_share(pstParam->s32IonFd, pstParam->hIonHanndle, &pstParam->s32IonShareFd);
if (ret < 0) {
ALOGE("[%s:%d] ion_share fail ret:%#x", __func__, __LINE__, ret);
return -1;
}
pstParam->pu8MmapAddr = mmap(NULL, MMAP_BUFFER_SIZE_BYTE, PROT_WRITE | PROT_READ,
MAP_SHARED, pstParam->s32IonShareFd, 0);
return 0;
}
int outMmapDeInit(struct hippo_stream_out *out)
{
ALOGI("[%s:%d] stream:%p", __func__, __LINE__, out);
hippo_mmap_audio_param_st *pstParam = (hippo_mmap_audio_param_st *)out->pstMmapAudioParam;
if (NULL == pstParam) {
ALOGW("[%s:%d] uninitialized, can't deinit", __func__, __LINE__);
return 0;
}
pstParam->stThreadParam.bExitThread = true;
pthread_mutex_lock(&pstParam->stThreadParam.mutex);
pthread_cond_signal(&pstParam->stThreadParam.cond);
pthread_mutex_unlock(&pstParam->stThreadParam.mutex);
if (pstParam->stThreadParam.threadId != 0) {
pthread_join(pstParam->stThreadParam.threadId, NULL);
}
munmap(pstParam->pu8MmapAddr, MMAP_BUFFER_SIZE_BYTE);
ion_free(pstParam->s32IonFd, pstParam->hIonHanndle);
ion_close(pstParam->s32IonFd);
free(pstParam);
out->pstMmapAudioParam = NULL;
return 0;
}
/*
* Copyright (C) 2019 hippo Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _HIPPO_MMAP_AUDIO_H_
#define _HIPPO_MMAP_AUDIO_H_
#include
typedef struct HIPPO_MMAP_THREAD_PARAM {
pthread_t threadId;
bool bExitThread;
bool bStopPlay;
int status;
pthread_condattr_t condAttr;
pthread_mutex_t mutex;
pthread_cond_t cond;
} hippo_mmap_thread_param_st;
typedef struct HIPPO_MMAP_AUDIO_PARAM {
unsigned char *pu8MmapAddr;
ion_user_handle_t hIonHanndle;
int s32IonFd;
int s32IonShareFd;
unsigned int u32FramePosition;
int64_t time_nanoseconds;
hippo_mmap_thread_param_st stThreadParam;
} hippo_mmap_audio_param_st;
int outMmapInit(struct hippo_stream_out *out);
int outMmapDeInit(struct hippo_stream_out *out);
#endif
adev_open_output_stream函数中加入
if (flags & AUDIO_OUTPUT_FLAG_MMAP_NOIRQ) {
outMmapInit(out);
}
adev_close_output_stream函数中加入
if (out->flags & AUDIO_OUTPUT_FLAG_MMAP_NOIRQ) {
outMmapDeInit(out);
}
<mixPort name="mmap_no_irq_out" role="source" flags="AUDIO_OUTPUT_FLAG_DIRECT|AUDIO_OUTPUT_FLAG_MMAP_NOIRQ">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000"
channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
mixPort>
#1 = AAUDIO_POLICY_NEVER, 2 = AAUDIO_POLICY_AUTO, 3 = AAUDIO_POLICY_ALWAYS
PRODUCT_PROPERTY_OVERRIDES += \
aaudio.mmap_policy=2
需要使用的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 HIPPO_MMAP_THREAD_PARAM {
pthread_t threadId;
bool bExitThread;
bool bStopPlay;
pthread_condattr_t condAttr;
pthread_mutex_t mutex;
pthread_cond_t cond;
} hippo_mmap_thread_param_st;
static hippo_mmap_thread_param_st g_stMmapThreadParam;
static void *out_mmap_commit_threadloop(void *pArg) {
struct hippo_stream_out *out = (struct hippo_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 hippo_stream_out *out = (struct hippo_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 hippo_stream_out *out = (struct hippo_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 hippo_stream_out *out = (struct hippo_stream_out *) stream;
struct hippo_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 hippo_stream_out *out = (struct hippo_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;
}
dumpsys media.aaudio