Android7.1 Offload模式下的音频数据播放流程

Android7.1 Offload模式下的音频数据抽取过程完成数据的抽取后,紧跟着会将数据交给AudioFlinger的track进行播放

对于offload的track,其在AudioFlinger中使用的播放线程并非是MixerThread(这种线程是在audioserver进程创建的时候就预先创建好了的),而是实时创建的OffloadThread线程。

AudioFlinger.cpp (frameworks\av\services\audioflinger)

AudioFlinger::openOutput_l
	thread = new OffloadThread(this, outputStream, *output, devices, mSystemReady);

OffloadThread是Android音频输出的核心部分,所有Android的硬解码音频(例如:mp3等音频文件)都需要经过OffloadThread进行混音后再输出到音频设备。

OffloadThread->DirectOutputThread->PlaybackThread->ThreadBase->Thread

在PlaybackThread中,重写了Thread的threadLoop,onFirstRef等方法,因此在调用OffloadThread这些方法时,实际上就是调用了PlaybackThread的方法。

 

1. onFirstRef

在getOutput的时候,我们创建了一个OffloadThread对象,由于这个对象继承于Thread,因此在创建对象时,会调用它的onFirstRef函数。

void AudioFlinger::PlaybackThread::onFirstRef()
{
    run(mName, ANDROID_PRIORITY_URGENT_AUDIO);
}

在该方法内部,调用了run,即开始运行threadLoop。也就是说,其实在new OffloadThread的时候就已经开始启动PlaybackThread::threadLoop。

 

2. threadLoop

在分析threadLoop之前,我们先来了解PlaybackThread中的几种Audio操作。

在Threads.cpp内有几个threadLoop_xxx方法,这些方法就分别代表不同的Audio操作:

操作方法功能
standbythreadLoop_standby待机
mixthreadLoop_mix混音
writethreadLoop_write音频输出
exitthreadLoop_exit退出
drainthreadLoop_drain只有offload用到,还不清楚作用
sleepthreadLoop_sleepTime无音频需要处理,计算睡眠时间

 

另外还有几个非常重要的变量:

变量取值含义
tracksToRemove 需要被移除的Track,一旦所有的Track都被移除,则表明没有音频数据需要处理,那么线程会进入睡眠
sleepTime 睡眠时间
standbyTime 如果持续睡眠超出standbyTime,则会进入待机
mStandby 表明当前是否为待机状态
mActiveTracks 需要进行音频处理的Track,如果该Track已经播放完成或者被停止,则会被移入tracksToRemove
mMixerStatusMIXER_IDLEMixer状态,no active tracks,表明不需要混音,而是进入睡眠
mMixerStatusMIXER_TRACKS_ENABLEDMixer状态,at least one active track, but no track has any data ready
mMixerStatusMIXER_TRACKS_READYMixer状态,at least one active track, and at least one track has data,表明可以进行混音

 

 

 

threadLoop循环

threadLoop内有一个循环,PlaybackThread是与output(输出设备)相关的(在openOutput的时候才会新建OffloadThread),基本上都不会跑出循环之外。

bool AudioFlinger::PlaybackThread::threadLoop()
{
    while (!exitPending())  
    {
        ....
    }
 
}
OffloadThread创建

在进入处理循环之前,首先会设置standbyTime、sleepTime。如果目前没有音频需要处理,进入睡眠,如果持续的睡眠时间超出了standbyTime,则会进入待机。不过由于standbyTime设置为当前时间,因此第一次肯定会执行待机动作。执行了待机操作后,OffloadThread就会进入睡眠,等待被唤醒

bool AudioFlinger::PlaybackThread::threadLoop()
{
    //设置待机时间、睡眠时间
    standbyTime = systemTime();
    sleepTime = idleSleepTime;
 
    while (!exitPending())  
    {
        //创建OffloadThread时,mActiveTracks肯定是空的,并且当前时间会超出standbyTime
        if ((!mActiveTracks.size() && systemTime() > standbyTime) || isSuspended()) {
               if (shouldStandby_l()) {                                                    //创建OffloadThread时肯定会进入待机 
                       threadLoop_standby();                        
                       mStandby = true;                                                    }
               }
               if (!mActiveTracks.size() && mConfigEvents.isEmpty()) {
                   //然后OffloadThread会在这里睡眠等待,知道AudioTrack:: start发送广播唤醒
                      mWaitWorkCV.wait(mLock);
                   standbyTime = systemTime() + standbyDelay;
                   sleepTime = idleSleepTime;
               }
    ...
    }
}
OffloadThread处理音频

如上一篇所说,AudioTrack:: start被执行后,就会唤醒OffloadThread线程,接下来就会对音频数据进行处理。处理流程如下图:

MixerThread_mix

 

 

正常的音频处理时,会在threadLoop循环内不断的进行混音与音频输出,其中分为三个步骤:

  1. 混音前的准备工作,prepareTracks_l
  2. 混音,threadLoop_mix
  3. 音频输出,threadLoop_write
bool AudioFlinger::PlaybackThread::threadLoop()
{
    while (!exitPending())
    {
        mMixerStatus = prepareTracks_l(&tracksToRemove); 
 
        if(mMixerStatus == MIXER_TRACKS_READY)
            threadLoop_mix();
        }
 
        threadLoop_write();
    }
}
① prepareTracks_l

准备混音的过程中,主要的目的有三个:

  1. 设置混音所需要的参数,包括:音量,混音的源buffer,混音目的buffer,音频格式,是否重采样等。    
  2. 删除被加入tracksToRemove的track
  3. 返回当前状态mMixerStatus

由于在mActiveTracks中维护的track可能会有多个,因此需要对每个track都执行上述步骤,我们可以依据上述目的来对prepareTrack_l进行分析。

Threads.cpp (frameworks\av\services\audioflinger)

AudioFlinger::OffloadThread::prepareTracks_l:
...
}  else if (track->framesReady() && track->isReady() &&
                !track->isPaused() && !track->isTerminated() && !track->isStopping_2()) {
                
mixerStatus = MIXER_TRACKS_READY;

track->framesReady()要特别留意,其会检查AudioTrack(客户端)是否已经将数据准备好,如果已经Ready了则会设置mixerStatus(混音器状态)为MIXER_TRACKS_READY

②threadLoop_mix

在prepareTrack_l返回了mMixerStatus = MIXER_TRACK_READY,那么就可以进入threadLoop_mix进行混音了。有了上面prepareTrack_l设置的参数,在threadLoop_mix所需要做的主要就是调用AudioMixer的process方法进行混音了。不过还需要对某些变量进行更新。

源码如下:
void AudioFlinger::DirectOutputThread::threadLoop_mix()
{
    size_t frameCount = mFrameCount;
    int8_t *curBuf = (int8_t *)mSinkBuffer;
    // output audio to hardware
    while (frameCount) {
        AudioBufferProvider::Buffer buffer;
        buffer.frameCount = frameCount;
        //通过共享内存的方式得到客户端传递过来的数据
        status_t status = mActiveTrack->getNextBuffer(&buffer);
        if (status != NO_ERROR || buffer.raw == NULL) {
            // no need to pad with 0 for compressed audio
            if (audio_has_proportional_frames(mFormat)) {
                memset(curBuf, 0, frameCount * mFrameSize);
            }
            break;
        }
        //将待播放的音频数据拷贝到mSinkBuffer上去
        memcpy(curBuf, buffer.raw, buffer.frameCount * mFrameSize);
        frameCount -= buffer.frameCount;
        curBuf += buffer.frameCount * mFrameSize;
        mActiveTrack->releaseBuffer(&buffer);
    }
    mCurrentWriteLength = curBuf - (int8_t *)mSinkBuffer;
    mSleepTimeUs = 0;
    mStandbyTimeNs = systemTime() + mStandbyDelayNs;
    mActiveTrack.clear();
}
③threadLoop_write

threadLoop_write用于混音后的音频输出

ssize_t AudioFlinger::PlaybackThread::threadLoop_write()
{
	...
	//网Hal中写入音频数据
	bytesWritten = mOutput->write((char *)mSinkBuffer + offset, mBytesRemaining);
	...
}

参考博客:
1. 混音线程MixerThread

  • 3
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
GPU和MIC都是用于高性能计算的加速器。offload模式是将部分计算任务从CPU转移到加速器上,以提高计算性能。GPU和MIC的offload模式有以下异同: 1. 异同点:GPU和MIC的offload模式都是将计算任务从CPU转移到加速器上进行处理,以提高计算性能。但是,两者的处理方式不同。GPU通过CUDA或OpenCL等编程模型来实现offload,而MIC则通过Intel Xeon Phi架构来实现offload。 2. 异同点:GPU和MIC的offload模式都需要将数据传输到加速器的内存中,以便进行计算。但是,两者的内存结构和管理方式也不同。GPU的内存结构是分层的,包括全局内存、共享内存和寄存器等不同层次的内存。而MIC的内存结构是统一的,可以通过NUMA架构进行管理。 3. 异同点:GPU和MIC的offload模式都需要进行编程,以便将计算任务分配到加速器上进行处理。但是,两者的编程模型也不同。GPU的编程模型是基于CUDA或OpenCL等语言的,而MIC的编程模型则是基于OpenMP等标准的。 4. 异同点:GPU和MIC的offload模式都可以提高计算性能,但是对于不同的应用场景,两者的性能表现也不同。GPU适用于大规模并行的计算任务,而MIC适用于单节点的高性能计算任务。 综上所述,GPU和MIC的offload模式在实现方式、内存结构、编程模型和性能表现等方面都有所不同。在选择适合自己应用场景的加速器时,需要根据实际需求进行选择。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值