Android之DuplicatingThread音频数据流

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/l460133921/article/details/81038893

我们知道连接蓝牙下播放手机铃声,手机铃声是双出的,即会从speaker和蓝牙同时输出,其使用的回话线程即为DuplicatingThread,那么在Android中DuplicatingThread中的音频数据流是什么样的呢?实际上DuplicatingThread主要起到了回放线程管理和数据拷贝的作用,如下图所示。
这里写图片描述

1. AudioTrack到DuplicatingThread音频数据的传递

连接蓝牙下预览手机铃声,当然需要创建AudioTrack,同时AudioFlinger也会创建一个Track与之对应,代码如下所示:

status_t AudioTrack::createTrack_l()
{
    ......
    //AudioFlinger创建与AudioTrack对应的Track
    sp<IAudioTrack> track = audioFlinger->createTrack(streamType,
                                                      mSampleRate,
                                                      mFormat,
                                                      mChannelMask,
                                                      &temp,
                                                      &flags,
                                                      mSharedBuffer,
                                                      output,
                                                      mClientPid,
                                                      tid,
                                                      &mSessionId,
                                                      mClientUid,
                                                      &status);
    ......
}

AudioFlinger最终调用回话线程的createTrack_l来生成Track,代码如下:

sp<IAudioTrack> AudioFlinger::createTrack(
        audio_stream_type_t streamType,
        uint32_t sampleRate,
        audio_format_t format,
        audio_channel_mask_t channelMask,
        size_t *frameCount,
        audio_output_flags_t *flags,
        const sp<IMemory>& sharedBuffer,
        audio_io_handle_t output,
        pid_t pid,
        pid_t tid,
        audio_session_t *sessionId,
        int clientUid,
        status_t *status)
{
    ......
    //该thread即为DuplicatingThread
    track = thread->createTrack_l(client, streamType, sampleRate, format,
                channelMask, frameCount, sharedBuffer, lSessionId, flags, tid, clientUid, &lStatus);
    ......
}      

跟踪到回放线程中的代码:

sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrack_l(
        const sp<AudioFlinger::Client>& client,
        audio_stream_type_t streamType,
        uint32_t sampleRate,
        audio_format_t format,
        audio_channel_mask_t channelMask,
        size_t *pFrameCount,
        const sp<IMemory>& sharedBuffer,
        audio_session_t sessionId,
        audio_output_flags_t *flags,
        pid_t tid,
        int uid,
        status_t *status)
{
    ......
    //构建Track,在Track中创建AudioTrack与Track间的共享环形buffer
    track = new Track(this, client, streamType, sampleRate, format,
                          channelMask, frameCount, NULL, sharedBuffer,
                          sessionId, uid, *flags, TrackBase::TYPE_DEFAULT);
    ......
    //将创建出的track加入到当前回放线程的集合中,这样在AudioMixer中就可以读取环形buffer中的数据
    mTracks.add(track);
    ......
}

以上过程创建国AudioTrack与DuplicatingThread之间共享的环形buffer。

2. DuplicatingThread向真正的回放线程写音频数据

首先我们要清楚DuplicatingThread的生成的过程,在连接蓝牙耳机后,AudioPolichManager会执行如下代码:

status_t AudioPolicyManager::checkOutputsForDevice(const sp<DeviceDescriptor> devDesc,
                                                   audio_policy_dev_state_t state,
                                                   SortedVector<audio_io_handle_t>& outputs,
                                                   const String8 address)
{
    ......
    //在连接蓝牙场景下,output为蓝牙输出设备handle,mPrimaryOutput->mIoHandle代表primary设备handle
    duplicatedOutput = mpClientInterface->openDuplicateOutput(output,mPrimaryOutput->mIoHandle);
    ......
}

最终会调用到AudioFlinger中,代码如下:

audio_io_handle_t AudioFlinger::openDuplicateOutput(audio_io_handle_t output1,
        audio_io_handle_t output2)
{
    Mutex::Autolock _l(mLock);
    MixerThread *thread1 = checkMixerThread_l(output1); //蓝牙输出设备的回放线程
    MixerThread *thread2 = checkMixerThread_l(output2); //primary输出设备的回放线程

    if (thread1 == NULL || thread2 == NULL) {
        ALOGW("openDuplicateOutput() wrong output mixer type for output %d or %d", output1,
                output2);
        return AUDIO_IO_HANDLE_NONE;
    }

    audio_io_handle_t id = nextUniqueId(AUDIO_UNIQUE_ID_USE_OUTPUT);
    //创建了DuplicatingThread
    DuplicatingThread *thread = new DuplicatingThread(this, thread1, id, mSystemReady);
    //DuplicatingThread中又添加了primary输出设备回放线程
    thread->addOutputTrack(thread2);
    mPlaybackThreads.add(id, thread);
    // notify client processes of the new output creation
    thread->ioConfigChanged(AUDIO_OUTPUT_OPENED);
    return id;
}

至此,DuplicatingThread创建出来,而且DuplicatingThread中管理国两个回放线程:蓝牙的回放线程和primary设备(如speaker)的回放线程,那么DuplicatingThread是如何创建和这些回放线程之间的共享的环形buffer的呢?接着看DuplicatingThread的构造和函数,代码如下:

//注意这里传的mainThread即为输出设备为蓝牙的回放线程
AudioFlinger::DuplicatingThread::DuplicatingThread(const sp<AudioFlinger>& audioFlinger,
        AudioFlinger::MixerThread* mainThread, audio_io_handle_t id, bool systemReady)
    :   MixerThread(audioFlinger, mainThread->getOutput(), id, mainThread->outDevice(),
                    systemReady, DUPLICATING),
        mWaitTimeMs(UINT_MAX)
{
    //调用国DuplicatingThread的内部函数
    addOutputTrack(mainThread);
}

接着看addOutputTrack函数,代码如下:

void AudioFlinger::DuplicatingThread::addOutputTrack(MixerThread *thread)
{
    ......
    //创建了OutputTrack对象,thread为输出设备为蓝牙的回放线程
    sp<OutputTrack> outputTrack = new OutputTrack(thread,
                                            this,
                                            mSampleRate,
                                            mFormat,
                                            mChannelMask,
                                            frameCount,
                                            IPCThreadState::self()->getCallingUid());
    ......
}

那么OutputTrack又是什么呢?看下其构造函数,代码如下:

//playbackThread为输出设备为蓝牙的回放线程
AudioFlinger::PlaybackThread::OutputTrack::OutputTrack(
            PlaybackThread *playbackThread,
            DuplicatingThread *sourceThread,
            uint32_t sampleRate,
            audio_format_t format,
            audio_channel_mask_t channelMask,
            size_t frameCount,
            int uid)
    //OutputTrack为Track的子类
    :   Track(playbackThread, NULL, AUDIO_STREAM_PATCH,
              sampleRate, format, channelMask, frameCount,
              NULL, 0, AUDIO_SESSION_NONE, uid, AUDIO_OUTPUT_FLAG_NONE,
              TYPE_OUTPUT),
    mActive(false), mSourceThread(sourceThread), mClientProxy(NULL)
{

    if (mCblk != NULL) {
        mOutBuffer.frameCount = 0;
        //playbackThread中添加了要播放的音频数据track
        playbackThread->mTracks.add(this);
        ALOGV("OutputTrack constructor mCblk %p, mBuffer %p, "
                "frameCount %zu, mChannelMask 0x%08x",
                mCblk, mBuffer,
                frameCount, mChannelMask);
        // since client and server are in the same process,
        // the buffer has the same virtual address on both sides
        //创建了访问环形buffer的proxy
        mClientProxy = new AudioTrackClientProxy(mCblk, mBuffer, mFrameCount, mFrameSize,
                true /*clientInServer*/);
        mClientProxy->setVolumeLR(GAIN_MINIFLOAT_PACKED_UNITY);
        mClientProxy->setSendLevel(0.0);
        mClientProxy->setSampleRate(sampleRate);
    } else {
        ALOGW("Error creating output track on thread %p", playbackThread);
    }
}

从以上代码可以看出,OutputTrack为Track的子类,而在Track中会创建环形buffer CBLK,而playbackThread(输出设备为蓝牙的回话线程)又将该OutputTrack加入到国其要回话的mTracks列表中,因此构造OutputTrack的过程中就相当于建立了一个DuplicatingThread和某个输出设备的回放线程间的共享的环形buffer CBLK。

同理在openDuplicateOutput函数中的thread->addOutputTrack(thread2);语句建立了DuplicatingThread与primary输出设备的回放线程间的共享的环形buffer。

接下来我们需要了解的是DuplicatingThread是如何向其管理的回放线程的环形buffer写音频数据的。为了搞清这点,首先我们要明白,DuplicatingThread继承自MixerThread,而MixerThread又继承了PlaybackThread,由于MixerThread和DuplicatingThread均没有重新实现threadLoop,因此DuplicatingThread执行的threadLoop实际上是PlaybackThread的threadLoop。在PlaybackThread的threadLoop函数中会调用threadLoop_write,由于DuplicatingThread重新实现了该函数,因此这里调用的为DuplicatingThread的threadLoop_write函数,代码如下:

ssize_t AudioFlinger::DuplicatingThread::threadLoop_write()
{
    for (size_t i = 0; i < outputTracks.size(); i++) {
        //调用国OutputTrack的write函数来写DuplicatingThread经过AudioMixer处理后的数据mSinkBuffer
        outputTracks[i]->write(mSinkBuffer, writeFrames);
    }
    mStandby = false;
    return (ssize_t)mSinkBufferSize;
}

接着看OutputTrack的write函数,代码如下:

bool AudioFlinger::PlaybackThread::OutputTrack::write(void* data, uint32_t frames)
{
    ......
    //获得playbackThread的环形buffer的可写的起始地址和大小
    status_t status = obtainBuffer(&mOutBuffer, waitTimeLeftMs);
    ......
    //将数据拷贝到环形buffer中
    memcpy(mOutBuffer.raw, pInBuffer->raw, outFrames * mFrameSize);
    ......
}

由此可以看出DuplicatingThread是如何向其管理的回放线程中写音频数据的。

接下来的播放就是各个回放线程在threadLoop中循环获得各自的环形buffer中的音频数据并写到HAL层了。

猜你喜欢

转载自blog.csdn.net/l460133921/article/details/81038893