Android 9.0 AAudio源码分析(二)

Android AAudio源码分析(二)



前言

在之前的章节中我们分析了AAudioService的启动以及openStream这个方法
本章将继续分析AAdioService之中其他几个重要的方法


一、Start

经过前一节对AAudioService里面openStream的分析,相信已经对这个服务不那么陌生了

1.startStream分析

开始
xref: /frameworks/av/services/oboeservice/AAudioService.cpp

//它的功能就是启动数据流
//可以看出来这这个操作是异步的,完成后,服务将发送一个已启动的事件
aaudio_result_t AAudioService::startStream(aaudio_handle_t streamHandle) {
    //根据之前创建好的流的句柄,来获取到serviceStream
    sp<AAudioServiceStreamBase> serviceStream = convertHandleToServiceStream(streamHandle);
    if (serviceStream.get() == nullptr) {
        ALOGE("startStream(), illegal stream handle = 0x%0x", streamHandle);
        return AAUDIO_ERROR_INVALID_HANDLE;
    }

    //然后根据拿到的serviceStream来执行start
    aaudio_result_t result = serviceStream->start();
    return checkForPendingClose(serviceStream, result);
}

又到了我们之前看过的AAudioServiceStreamBase这个类中
每一个AAudioServiceStreamBase相当于一个client
xref: /frameworks/av/services/oboeservice/AAudioServiceStreamBase.cpp

/**
 * Start the flow of audio data.
 *
 * An AAUDIO_SERVICE_EVENT_STARTED will be sent to the client when complete.
 */
aaudio_result_t AAudioServiceStreamBase::start() {
    aaudio_result_t result = AAUDIO_OK;

    //已经运行,直接返回就可
    if (isRunning()) {
        return AAUDIO_OK;
    }

    //流启动时设置为false
    //首次从流中读取数据时设置为true
    setFlowing(false);

    // Start with fresh presentation timestamps.
    //计时器重置
    mAtomicTimestamp.clear();

    mClientHandle = AUDIO_PORT_HANDLE_NONE;
    //内部调用startDevice
    result = startDevice();
    if (result != AAUDIO_OK) goto error;

    // This should happen at the end of the start.
    //在start完之后应该发送一个AAUDIO_SERVICE_EVENT_STARTED事件给客户端
    //设置状态AAUDIO_STREAM_STATE_STARTED
    sendServiceEvent(AAUDIO_SERVICE_EVENT_STARTED);
    setState(AAUDIO_STREAM_STATE_STARTED);
    mThreadEnabled.store(true);
    result = mTimestampThread.start(this);
    if (result != AAUDIO_OK) goto error;

    return result;

error:
    disconnect();
    return result;
}

aaudio_result_t AAudioServiceStreamBase::startDevice() {
    mClientHandle = AUDIO_PORT_HANDLE_NONE;
    //获取到可以操作的AAudioServiceEndpoint对象
    sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
    if (endpoint == nullptr) {
        ALOGE("%s() has no endpoint", __func__);
        return AAUDIO_ERROR_INVALID_STATE;
    }
    return endpoint->startStream(this, &mClientHandle);
}

前面这两个部分的调用很简单,没有涉及到什么复杂的逻辑
startStream是AAudioServiceEndpoint.h定义的虚函数
AAudioServiceEndpointShared和AAudioServiceEndpointMMAP都继承了这个类
所以也就都实现了这个startStream方法
所以这里面的startStream会通过这两个类来实现
我们这里主要看AAudioServiceEndpointMMAP也就是独占模式下startStream
xref: /frameworks/av/services/oboeservice/AAudioServiceEndpointMMAP.cpp

aaudio_result_t AAudioServiceEndpointMMAP::startStream(sp<AAudioServiceStreamBase> stream,
                                                   audio_port_handle_t *clientHandle __unused) {
    // Start the client on behalf of the AAudio service.
    // Use the port handle that was provided by openMmapStream().
    //利用之前打开流时得到的mPortHandle来作为参数传入startClient方法
    audio_port_handle_t tempHandle = mPortHandle;
    aaudio_result_t result = startClient(mMmapClient, &tempHandle);
    // When AudioFlinger is passed a valid port handle then it should not change it.
    //当AudioFlinger被传递一个有效的端口句柄时,它不应该改变它。
    LOG_ALWAYS_FATAL_IF(tempHandle != mPortHandle,
                        "%s() port handle not expected to change from %d to %d",
                        __func__, mPortHandle, tempHandle);
    ALOGV("%s(%p) mPortHandle = %d", __func__, stream.get(), mPortHandle);
    return result;
}

aaudio_result_t AAudioServiceEndpointMMAP::startClient(const android::AudioClient& client,
                                                       audio_port_handle_t *clientHandle) {
    if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
    ALOGD("%s(%p(uid=%d, pid=%d))", __func__, &client, client.clientUid, client.clientPid);
    audio_port_handle_t originalHandle =  *clientHandle;
    //这个mMmapStream可以当成AudioFlinger
    //在这里面根据启动流的客户端和保存的句柄为参数来调用start
    status_t status = mMmapStream->start(client, clientHandle);
    aaudio_result_t result = AAudioConvert_androidToAAudioResult(status);
    ALOGD("%s() , portHandle %d => %d, returns %d", __func__, originalHandle, *clientHandle, result);
    return result;
}

继续看AudioFlinger里面
xref: /frameworks/av/services/audioflinger/Threads.cpp

status_t AudioFlinger::MmapThread::start(const AudioClient& client,
                                         audio_port_handle_t *handle)
{
    ALOGV("%s clientUid %d mStandby %d mPortId %d *handle %d", __FUNCTION__,
          client.clientUid, mStandby, mPortId, *handle);
    if (mHalStream == 0) {
        return NO_INIT;
    }

    status_t ret;

    if (*handle == mPortId) {
        // for the first track, reuse portId and session allocated when the stream was opened
        return exitStandby();
    }

    audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;

    audio_io_handle_t io = mId;
    //判断是输出流还是输入流
    if (isOutput()) {
        audio_config_t config = AUDIO_CONFIG_INITIALIZER;
        config.sample_rate = mSampleRate;
        config.channel_mask = mChannelMask;
        config.format = mFormat;
        audio_stream_type_t stream = streamType();
        audio_output_flags_t flags =
                (audio_output_flags_t)(AUDIO_OUTPUT_FLAG_MMAP_NOIRQ | AUDIO_OUTPUT_FLAG_DIRECT);
        audio_port_handle_t deviceId = mDeviceId;
        //获取到之前创建好的句柄
        ret = AudioSystem::getOutputForAttr(&mAttr, &io,
                                            mSessionId,
                                            &stream,
                                            client.clientPid,
                                            client.clientUid,
                                            &config,
                                            flags,
                                            &deviceId,
                                            &portId);
    } else {
        audio_config_base_t config;
        config.sample_rate = mSampleRate;
        config.channel_mask = mChannelMask;
        config.format = mFormat;
        audio_port_handle_t deviceId = mDeviceId;
        ret = AudioSystem::getInputForAttr(&mAttr, &io,
                                              mSessionId,
                                              client.clientPid,
                                              client.clientUid,
                                              client.packageName,
                                              &config,
                                              AUDIO_INPUT_FLAG_MMAP_NOIRQ,
                                              &deviceId,
                                              &portId);
    }
    // APM should not chose a different input or output stream for the same set of attributes
    // and audo configuration
    if (ret != NO_ERROR || io != mId) {
        ALOGE("%s: error getting output or input from APM (error %d, io %d expected io %d)",
              __FUNCTION__, ret, io, mId);
        return BAD_VALUE;
    }

    bool silenced = false;
    //开始start了
    if (isOutput()) {
        ret = AudioSystem::startOutput(mId, streamType(), mSessionId);
    } else {
        ret = AudioSystem::startInput(portId, &silenced);
    }

    Mutex::Autolock _l(mLock);
    // abort if start is rejected by audio policy manager
    if (ret != NO_ERROR) {
        ALOGE("%s: error start rejected by AudioPolicyManager = %d", __FUNCTION__, ret);
        if (mActiveTracks.size() != 0) {
            mLock.unlock();
            if (isOutput()) {
                AudioSystem::releaseOutput(mId, streamType(), mSessionId);
            } else {
                AudioSystem::releaseInput(portId);
            }
            mLock.lock();
        } else {
            mHalStream->stop();
        }
        return PERMISSION_DENIED;
    }

    //音量设置
    if (isOutput()) {
        // force volume update when a new track is added
        mHalVolFloat = -1.0f;
    } else if (!silenced) {
        for (const sp<MmapTrack> &track : mActiveTracks) {
            if (track->isSilenced_l() && track->uid() != client.clientUid)
                track->invalidate();
        }
    }

    // Given that MmapThread::mAttr is mutable, should a MmapTrack have attributes ?
    sp<MmapTrack> track = new MmapTrack(this, mAttr, mSampleRate, mFormat, mChannelMask, mSessionId,
                                        client.clientUid, client.clientPid, portId);

    track->setSilenced_l(silenced);
    mActiveTracks.add(track);
    sp<EffectChain> chain = getEffectChain_l(mSessionId);
    if (chain != 0) {
        chain->setStrategy(AudioSystem::getStrategyForStream(streamType()));
        chain->incTrackCnt();
        chain->incActiveTrackCnt();
    }

    *handle = portId;
    broadcast_l();

    ALOGV("%s DONE handle %d stream %p", __FUNCTION__, *handle, mHalStream.get());

    return NO_ERROR;
}

看完上一章的open函数之后,再看start就没那么困难了
其实内部和audioTrack和audioRecord的方法实现差不多
只不过AAudio这边到底层数据通信时用的是MMAP
因此可以被称为高性能音频

其他的方法就不予在这边分析了
相信读者可以再看完这两章后自己去深入研究
会更有收获呦!


总结

关于AAudioService

AAudioService这个服务属于安卓原生后来才出现的一个服务
其存在的意义就是为了满足我们安卓手机中低延迟音频的需要
在苹果手机中好像早就有这样的功能了
安卓目前占据主流,但是目前我却已经嗅到了一丝丝不安
或许有些已经坐好了准备,有些已经吹起了号角

安卓作为一个开源项目,而且其底层是linux内核
其内部还是有非常多的编码精髓值得我们去学习
未来仍旧可期


觉得看完有帮助的请一键三连吧!
您的支持将是我继续下去的动力

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android AAudio 是一种用于音频处理的新型API,旨在提高Android平台上的音频性能和稳定性。它于Android 8.0 Oreo版本中首次引入,并在后续版本中得到了改进和优化。 AAudio专注于解决传统音频API(如OpenSL ES)中存在的延迟和频率浮动等问题。它使用了一种低延迟的音频管道,能够以更高的效率和更精确的时间同步处理音频数据。AAudio的设计目标是提供可预测、一致和高质量的音频流,以满足实时音频应用(如音乐制作、游戏、语音通信等)的需求。 AAudio提供了一套简单易用的API,供开发人员进行音频流的读取和写入操作。开发者可以直接控制音频流的属性,如采样率、通道数和数据格式等,以满足不同应用的需求。此外,AAudio还提供了音频设备的状态查询和事件回调功能,方便开发者监控和调整音频流的运行状态。 相比于传统的音频API,AAudio提供了更低的音频输出延迟,可达到毫秒级别,从而大大降低了音频输入和输出之间的延迟。这使得实时音频应用能够更加精确地控制和处理音频数据,提供更流畅和逼真的音频体验。 总的来说,Android AAudio是一种用于音频处理的先进API,通过其低延迟和高质量的音频管道,提高了Android平台上实时音频应用的性能和稳定性。它为开发者提供了更多的控制权和灵活性,使得他们能够更好地满足用户的需求,提供更出色的音频体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值