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

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

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

AudioFlinger::openOutput_lthread = 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操作:

操作 方法 功能
standby threadLoop_standby 待机
mix threadLoop_mix 混音
write threadLoop_write 音频输出
exit threadLoop_exit 退出
drain threadLoop_drain 只有offload用到,还不清楚作用
sleep threadLoop_sleepTime 无音频需要处理,计算睡眠时间

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

变量 取值 含义
tracksToRemove   需要被移除的Track,一旦所有的Track都被移除,则表明没有音频数据需要处理,那么线程会进入睡眠
sleepTime   睡眠时间
standbyTime   如果持续睡眠超出standbyTime,则会进入待机
mStandby   表明当前是否为待机状态
mActiveTracks   需要进行音频处理的Track,如果该Track已经播放完成或者被停止,则会被移入tracksToRemove
mMixerStatus MIXER_IDLE Mixer状态,no active tracks,表明不需要混音,而是进入睡眠
mMixerStatus MIXER_TRACKS_ENABLED Mixer状态,at least one active track, but no track has any data ready
mMixerStatus MIXER_TRACKS_READY Mixer状态,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肯定是空的,并且当前时间会超出standbyTimeif ((!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线程,接下来就会对音频数据进行处理。处理流程如下图:

正常的音频处理时,会在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 hardwarewhile (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 audioif (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

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

  1. WearOS Offload模式下的表盘开发

    WearOS手表offload模式下的表盘渲染,是通过BG绘制的. 一.表盘进入offload的条件是手表处于微光模式且表盘是Decomposable的. 为了满足上述条件,需要表盘开发中做如下配置: ...

  2. matlab画某一经度范围的极地投影,极地投影模式下多变量时空数据的可视化方法...

    专利名称:极地投影模式下多变量时空数据的可视化方法 技术领域: 本发明涉及计算机技术领域,特别涉及一种极地投影模式下多变量时空数据的可视化方法. 背景技术: 地球系统模式是研究全球气候变化问题的重要工 ...

  3. 【技术实战】华为手机学生模式下的微信数据提取

    什么是学生模式? 学生模式是华为手机在EMUI4.1上新推出的功能,华为P9是第一个配有该功能的手机. 学生模式开启之后对取证有何影响? 1.手机无法打开USB调试模式: 2.取证电脑无法识别手机. ...

  4. QuteCom手记:phapi发送和接收音频数据的流程(ortp portaudio)

    phapi发送和接收音频数据的流程简介. 通过ortp库(完整的源代码在ortp工程中)实现. oRTP协议将在另外一篇中介绍,这里只要知道它是用来发送和接收媒体流(音频和视频)的协议. 本篇只记录使 ...

  5. iOS:制作简易的 AAC 播放器 —— 了解音频的播放流程

    ????????关注后回复 "进群" ,拉你进程序员交流群???????? 作者丨千帆直播 来源丨搜狐技术产品(ID:sohu-tech) 本文字数:1872字 预计阅读时间:8分 ...

  6. Redis主从模式下过期数据和数据不一致

    1. 目录 1. 目录 2. Redis删除策略 2.1. 惰性删除 2.2. 定时删除 2.3. 内存淘汰机制 3. 过期数据 3.1. 读取 Master 3.2. 读取 Slave 3.2.1. ...

  7. 大数据时代的隐身模式下的大数据创业公司

    "大数据之所以有趣,是因为它将是未来许多年时间里的一个重大投资领域.大数据浪潮将持续很久,而不会是18个月或24个月以后就宣告终结."风险投资公司Accel Partners普通合 ...

  8. 在开源模式下云计算大数据的现状浅析

    "开源"模式带来的好处很多,其中最吸引人的就是可以帮助企业降低成本.另外,开源模式消除了供应商的限制和壁垒,并且可让技术变得更加协作,合作者会不断更新开源软件,使技术得到持续的完善 ...

  9. 付呗聚合支付快速教程 分账篇③——多商户模式下分账提现全流程详解

    文章目录 一.前文 二.资金流转详解 2.1 各方关系 2.2 资金空间流转 2.3 资金时间流转 2.2 资金时空流转 三.付呗接口 四.软件流程 4.1 支付下单流程 4.2 支付查询流程 4.3 ...

  10. 数据自治开放模式下的隐私保护

    数据自治开放模式下的隐私保护 王智慧1,2, 周旭晨1,2, 朱云1,2 1. 复旦大学计算机科学技术学院,上海 201203 2. 上海市数据科学重点实验室,上海 201203 摘要:数据开放对于提 ...

最新文章

  1. 从SAP APO到SAP IBP:CIO如何实现最佳过渡?
  2. utf8汉字编码16进制对照(转载)
  3. node mysql 连接池 超时,关于NodeJS中mysql连接池卡死问题
  4. 统一建模语言(UML)介绍
  5. Spring实战(三)Spring中装配Bean的三种方式---XML、JavaConfig、AutoWire
  6. P1420 最长连号(python3实现)
  7. [Head First设计模式]生活中学设计模式——组合模式
  8. 如何查看系统启动时间-
  9. Please create pull requests instead of asking for help on Homebrew‘s GitHubError: macOS 10.13
  10. 手把手教你如何删除病毒木马(转)
  11. capslock键英语怎么读_capslock怎么读
  12. python画图方法_python画图的两种方法
  13. 如何快速上手强化学习?
  14. 数仓、数据湖、湖仓一体、数据网格的探索与研究
  15. matlab单双极性眼图程序,求通信大神讲讲这个matlab程序每一段的意思
  16. Jetson TX2 重装系统(刷机)+后续设置(安装Fcitx、解决拼音候选词不显示、换国内源、局域网实现VNC远程桌面)
  17. 高性能数据库连接池的内幕
  18. java研发网页数据采集
  19. 【陈工笔记】# ubuntu系统登陆密码忘记,如何修改 #
  20. h5小游戏--围住神经猫笔记

热门文章

  1. Python爬虫从入门到放弃(二十四)之 Scrapy登录知乎
  2. mysql 在update中实现子查询的方式
  3. Script error.全面解析
  4. 利用OpenSSL创建自签名的SSL证书备忘
  5. Android的ADT的安装
  6. MYSQL 无重复插入数据更新语法 sql一句话使insert时若主键重复则更新
  7. Cannot detect Web Project version. Please specify version of Web Project through Maven project ...报错
  8. python学习一:基本数据类型
  9. SQL分类,DDL,DML,DCL
  10. Gradle之全局配置