AAudio framework架構 (附帶visio流程圖)

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, &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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章