【Android Audio】AAudio 架构讲解以及实现范例 (附带visio流程图)

AAudio 架构讲解以及实现范例

  • 1、Google官方说明
  • 2、AAudio内部架构图
    • 2.1、控制流
    • 2.2、数据流
      • 2.2.1、共享buffer分配顺序
      • 2.2.2、客户端和oboeservice共享buffer大小设置
      • 2.2.2、client和oboeservice 的FifoBuffer实例构造顺序
      • 2.2.3、共享和独占模式
    • 2.3、AAudio 共享ring buffer
  • 3、AAudio audio hal实现实例
    • 3.1、软件实现代码
      • 3.1.1、hippo_mmap_audio.c
      • 3.1.2、aml_mmap_audio.h
      • 3.1.3、audio_hw.c
      • 3.1.4、audio_policy_configuration.xml
      • 3.1.4、系统默认prop属性加入
    • 3.2、独占alsa输出口实现代码
  • 4、AAudio debug

1、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

“AAudio 是在 Android O 版本中引入的全新 Android C API。此 API 专为需要低延迟的高性能音频应用而设计。”相对于之前的AudioFlinger音频引擎来说,AAudio整个通路的缓存都设置很小,同时也对我们vendor的音频处理实时性要求更高,最终降低整个音频通路的延时,为对实时性要求高的音频流的第三方apk提供帮助。

特征:
低延时、仅支持PCM流、不支持自动化路由

2、AAudio内部架构图

Code路径:
libaaudio.so: frameworks/av/media/libaaudio (运行在客户端进程)
libaaudioservice.so: frameworks/av/services/oboeservice (运行在audioserver进程的服务)
原流程图下载地址:https://download.csdn.net/download/u013120422/11937875

2.1、控制流

【Android Audio】AAudio 架构讲解以及实现范例 (附带visio流程图)_第1张图片

2.2、数据流

【Android Audio】AAudio 架构讲解以及实现范例 (附带visio流程图)_第2张图片

2.2.1、共享buffer分配顺序

  1. 分配AAudioServiceStreamShared的up msg buffer;(AAudioServiceStreamBase::open)
  2. 分配AAudioServiceStreamMMAP的up msg buffer; (AAudioServiceStreamBase::open)
  3. get到alsa的pcm_open mmap的fd;(AAudioServiceEndpointMMAP::open)
  4. 分配AAudioServiceStreamShared的audioData buffer;(AAudioServiceStreamShared::open)
    upMessageQueue; // server to client
    downMessageQueue; // client to server
    DataQueue; // capture or playback

2.2.2、客户端和oboeservice共享buffer大小设置

函数:AAudioServiceStreamShared::calculateBufferCapacity
未指定BufferCapacity时,AAudioStream_getBufferCapacityInFrames值是AAudioStream_getFramesPerBurst值的16倍(DEFAULT_BURSTS_PER_BUFFER)
【Android Audio】AAudio 架构讲解以及实现范例 (附带visio流程图)_第3张图片

2.2.2、client和oboeservice 的FifoBuffer实例构造顺序

①.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);

2.2.3、共享和独占模式

一个AAudioServiceStreamShared实例为一路Track,AAudioServiceStreamShared.cpp可为多个实例;
AAudioServiceStreamMMAP.cpp为专有模式,只能拥有一个实例

2.3、AAudio 共享ring buffer

【Android Audio】AAudio 架构讲解以及实现范例 (附带visio流程图)_第4张图片

3、AAudio audio hal实现实例

  • 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);

3.1、软件实现代码

  • 无DSP、无ms12的软件AAudio实现之后的框架
    【Android Audio】AAudio 架构讲解以及实现范例 (附带visio流程图)_第5张图片

3.1.1、hippo_mmap_audio.c

/*
 * 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, &timestamp);
            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, &timestamp);
    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;
}

3.1.2、aml_mmap_audio.h

/*
 * 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

3.1.3、audio_hw.c

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);
    }

3.1.4、audio_policy_configuration.xml

<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>

【Android Audio】AAudio 架构讲解以及实现范例 (附带visio流程图)_第6张图片

3.1.4、系统默认prop属性加入

#1 = AAUDIO_POLICY_NEVER, 2 = AAUDIO_POLICY_AUTO, 3 = AAUDIO_POLICY_ALWAYS
PRODUCT_PROPERTY_OVERRIDES += \
    aaudio.mmap_policy=2

【Android Audio】AAudio 架构讲解以及实现范例 (附带visio流程图)_第7张图片

3.2、独占alsa输出口实现代码

需要使用的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, &timestamp);
    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, &timestamp);
    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;
}

4、AAudio debug

dumpsys media.aaudio

【Android Audio】AAudio 架构讲解以及实现范例 (附带visio流程图)_第8张图片

你可能感兴趣的:(Android,Audio,Audio,Hal,AAudio)