Android之DuplicatingThread音频数据流

本文详细解析了Android系统中DuplicatingThread的工作原理及其实现机制,包括AudioTrack与DuplicatingThread间共享环形buffer的创建过程,以及DuplicatingThread如何向管理的回放线程写音频数据。

我们知道连接蓝牙下播放手机铃声,手机铃声是双出的,即会从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层了。

<think>嗯,用户要求用中文回答,并在最后提出3-5个相关问题。问题关键词是"DuplicatingThread的作用",这应该是多线程编程中的概念。 先确认这个术语的常见含义。在Java并发编程里,DuplicatingThread可能指ThreadLocal的特殊实现,主要作用是创建线程局部变量的副本。这样每个线程都能独立修改自己的副本,避免并发冲突。 用户可能是开发者,正在处理多线程数据隔离问题。需要解释清楚三点:核心作用是什么(数据隔离)、为什么需要它(线程安全)、典型应用场景(如用户会话管理)。用数据库连接管理的例子说明会很直观。 现在组织回答结构:先定义概念,再分点说明作用,最后举例。要避免术语堆砌,比如强调"每个线程拥有独立副本"这个特点时,可以用"就像银行柜员各自操作保险箱"的比喻。 最后准备相关问题:围绕原理延伸(ThreadLocal区别)、实践应用(Spring框架使用场景)、配置要点(初始值设定)、潜在风险(内存泄漏原因)、替代方案(其他线程封闭技术)。这些问题能帮助用户深入理解。 检查所有要求:中文回答√ 相关问题3-5个√ 首行标记"§§相关问题§§"√ 不换行√ 包含代码示例的格式参考了但本次不需要代码。确认无误。</think>在并发编程中,**DuplicatingThread** 并非标准术语,但其概念通常与 **`ThreadLocal`** 类(尤其在 Java 中)的核心作用紧密相关。它的核心作用是**为每个线程创建变量的独立副本**,实现线程封闭(Thread Confinement),确保线程间数据隔离,避免并发访问冲突。 ### 核心作用详解: 1. **线程隔离数据** - 每个线程操作自己的变量副本,互不干扰(如用户会话信息、数据库连接)。 2. **避免同步开销** - 无需用 `synchronized` 或锁保护数据,提升并发性能。 3. **隐式传递上下文** - 跨方法调用时自动传递线程专属数据(如事务 ID、权限信息)。 4. **简化线程安全设计** - 将非线程安全对象(如 `SimpleDateFormat`)绑定到线程,规避共享风险。 ### 典型应用场景: ```java // Java 示例:ThreadLocal 实现线程专属数据库连接 public class ConnectionManager { private static final ThreadLocal<Connection> connectionHolder = ThreadLocal.withInitial(() -> DriverManager.getConnection(DB_URL)); public static Connection getConnection() { return connectionHolder.get(); // 每个线程获取独立连接 } public static void release() { Connection conn = connectionHolder.get(); if (conn != null) { conn.close(); connectionHolder.remove(); // 防止内存泄漏 } } } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值