Android7.1 Offload模式下的音频数据播放流程
在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循环内不断的进行混音与音频输出,其中分为三个步骤:
- 混音前的准备工作,prepareTracks_l
- 混音,threadLoop_mix
- 音频输出,threadLoop_write
bool AudioFlinger::PlaybackThread::threadLoop()
{while (!exitPending()){mMixerStatus = prepareTracks_l(&tracksToRemove); if(mMixerStatus == MIXER_TRACKS_READY)threadLoop_mix();}threadLoop_write();}
}
① prepareTracks_l
准备混音的过程中,主要的目的有三个:
- 设置混音所需要的参数,包括:音量,混音的源buffer,混音目的buffer,音频格式,是否重采样等。
- 删除被加入tracksToRemove的track
- 返回当前状态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模式下的音频数据播放流程相关推荐
- WearOS Offload模式下的表盘开发
WearOS手表offload模式下的表盘渲染,是通过BG绘制的. 一.表盘进入offload的条件是手表处于微光模式且表盘是Decomposable的. 为了满足上述条件,需要表盘开发中做如下配置: ...
- matlab画某一经度范围的极地投影,极地投影模式下多变量时空数据的可视化方法...
专利名称:极地投影模式下多变量时空数据的可视化方法 技术领域: 本发明涉及计算机技术领域,特别涉及一种极地投影模式下多变量时空数据的可视化方法. 背景技术: 地球系统模式是研究全球气候变化问题的重要工 ...
- 【技术实战】华为手机学生模式下的微信数据提取
什么是学生模式? 学生模式是华为手机在EMUI4.1上新推出的功能,华为P9是第一个配有该功能的手机. 学生模式开启之后对取证有何影响? 1.手机无法打开USB调试模式: 2.取证电脑无法识别手机. ...
- QuteCom手记:phapi发送和接收音频数据的流程(ortp portaudio)
phapi发送和接收音频数据的流程简介. 通过ortp库(完整的源代码在ortp工程中)实现. oRTP协议将在另外一篇中介绍,这里只要知道它是用来发送和接收媒体流(音频和视频)的协议. 本篇只记录使 ...
- iOS:制作简易的 AAC 播放器 —— 了解音频的播放流程
????????关注后回复 "进群" ,拉你进程序员交流群???????? 作者丨千帆直播 来源丨搜狐技术产品(ID:sohu-tech) 本文字数:1872字 预计阅读时间:8分 ...
- Redis主从模式下过期数据和数据不一致
1. 目录 1. 目录 2. Redis删除策略 2.1. 惰性删除 2.2. 定时删除 2.3. 内存淘汰机制 3. 过期数据 3.1. 读取 Master 3.2. 读取 Slave 3.2.1. ...
- 大数据时代的隐身模式下的大数据创业公司
"大数据之所以有趣,是因为它将是未来许多年时间里的一个重大投资领域.大数据浪潮将持续很久,而不会是18个月或24个月以后就宣告终结."风险投资公司Accel Partners普通合 ...
- 在开源模式下云计算大数据的现状浅析
"开源"模式带来的好处很多,其中最吸引人的就是可以帮助企业降低成本.另外,开源模式消除了供应商的限制和壁垒,并且可让技术变得更加协作,合作者会不断更新开源软件,使技术得到持续的完善 ...
- 付呗聚合支付快速教程 分账篇③——多商户模式下分账提现全流程详解
文章目录 一.前文 二.资金流转详解 2.1 各方关系 2.2 资金空间流转 2.3 资金时间流转 2.2 资金时空流转 三.付呗接口 四.软件流程 4.1 支付下单流程 4.2 支付查询流程 4.3 ...
- 数据自治开放模式下的隐私保护
数据自治开放模式下的隐私保护 王智慧1,2, 周旭晨1,2, 朱云1,2 1. 复旦大学计算机科学技术学院,上海 201203 2. 上海市数据科学重点实验室,上海 201203 摘要:数据开放对于提 ...
最新文章
- 从SAP APO到SAP IBP:CIO如何实现最佳过渡?
- utf8汉字编码16进制对照(转载)
- node mysql 连接池 超时,关于NodeJS中mysql连接池卡死问题
- 统一建模语言(UML)介绍
- Spring实战(三)Spring中装配Bean的三种方式---XML、JavaConfig、AutoWire
- P1420 最长连号(python3实现)
- [Head First设计模式]生活中学设计模式——组合模式
- 如何查看系统启动时间-
- Please create pull requests instead of asking for help on Homebrew‘s GitHubError: macOS 10.13
- 手把手教你如何删除病毒木马(转)
- capslock键英语怎么读_capslock怎么读
- python画图方法_python画图的两种方法
- 如何快速上手强化学习?
- 数仓、数据湖、湖仓一体、数据网格的探索与研究
- matlab单双极性眼图程序,求通信大神讲讲这个matlab程序每一段的意思
- Jetson TX2 重装系统(刷机)+后续设置(安装Fcitx、解决拼音候选词不显示、换国内源、局域网实现VNC远程桌面)
- 高性能数据库连接池的内幕
- java研发网页数据采集
- 【陈工笔记】# ubuntu系统登陆密码忘记,如何修改 #
- h5小游戏--围住神经猫笔记
热门文章
- Python爬虫从入门到放弃(二十四)之 Scrapy登录知乎
- mysql 在update中实现子查询的方式
- Script error.全面解析
- 利用OpenSSL创建自签名的SSL证书备忘
- Android的ADT的安装
- MYSQL 无重复插入数据更新语法 sql一句话使insert时若主键重复则更新
- Cannot detect Web Project version. Please specify version of Web Project through Maven project ...报错
- python学习一:基本数据类型
- SQL分类,DDL,DML,DCL
- Gradle之全局配置