技术标签: android audio Android Audio系统
好久没写了,今天碰巧有个小伙伴问我关于音频流这一块的,久了还有点记不起来,我就顺便写一下,后面就不用又找一遍代码了,所谓好记性不如烂笔头。
所以,这里是关于如何从AudioTrack 写入数据到audioflinger,以及audioflinger如何写入到hal层的音频流处理流程,主要写一下audioflinger处理流程,和写一些细节。
获取音频流
app client 通过创建AudioTrack后,在播放的时候会不断的调用audiotrack的write方法,不断的向audioflinger写数据。
//frameworks\av\media\libaudioclient\AudioTrack.cpp
ssize_t AudioTrack::write(const void* buffer, size_t userSize, bool blocking)
{
if (mTransfer != TRANSFER_SYNC) {
return INVALID_OPERATION;
}
if (isDirect()) {
AutoMutex lock(mLock);
int32_t flags = android_atomic_and(
~(CBLK_UNDERRUN | CBLK_LOOP_CYCLE | CBLK_LOOP_FINAL | CBLK_BUFFER_END),
&mCblk->mFlags);
if (flags & CBLK_INVALID) {
return DEAD_OBJECT;
}
}
if (ssize_t(userSize) < 0 || (buffer == NULL && userSize != 0)) {
// Sanity-check: user is most-likely passing an error code, and it would
// make the return value ambiguous (actualSize vs error).
ALOGE("AudioTrack::write(buffer=%p, size=%zu (%zd)", buffer, userSize, userSize);
return BAD_VALUE;
}
size_t written = 0;
Buffer audioBuffer;
while (userSize >= mFrameSize) {
audioBuffer.frameCount = userSize / mFrameSize;
status_t err = obtainBuffer(&audioBuffer,
blocking ? &ClientProxy::kForever : &ClientProxy::kNonBlocking);
if (err < 0) {
if (written > 0) {
break;
}
if (err == TIMED_OUT || err == -EINTR) {
err = WOULD_BLOCK;
}
return ssize_t(err);
}
size_t toWrite = audioBuffer.size;
memcpy(audioBuffer.i8, buffer, toWrite);
buffer = ((const char *) buffer) + toWrite;
userSize -= toWrite;
written += toWrite;
releaseBuffer(&audioBuffer);
}
if (written > 0) {
mFramesWritten += written / mFrameSize;
}
return written;
}
作为service端,不断的接收并处理client写入的数据。
//frameworks/av/services/audioflinger/Tracks.cpp
bool AudioFlinger::PlaybackThread::OutputTrack::write(void* data, uint32_t frames)
{
Buffer *pInBuffer;
Buffer inBuffer;
bool outputBufferFull = false;
inBuffer.frameCount = frames;
inBuffer.raw = data;
//处理客户端write数据,这个就不在这里细说了,这里不打算细讲这个环形缓冲,有兴趣的可以自行阅读代码
}
主要处理流程:
track预处理
混音处理
向HAL输出数据
bool AudioFlinger::PlaybackThread::threadLoop()
{
//1、track预处理
mMixerStatus = prepareTracks_l(&tracksToRemove);
//2、混音处理
threadLoop_mix();
if (mMixerBufferValid) {
//
void *buffer = mEffectBufferValid ? mEffectBuffer : mSinkBuffer;
audio_format_t format = mEffectBufferValid ? mEffectBufferFormat : mFormat;
// mono blend occurs for mixer threads only (not direct or offloaded)
// and is handled here if we're going directly to the sink.
if (requireMonoBlend() && !mEffectBufferValid) {
mono_blend(mMixerBuffer, mMixerBufferFormat, mChannelCount, mNormalFrameCount,
true /*limit*/);
}
//将mMixerBuffer 按hal 层格式 实际最终还是拷贝到 mSinkBuffer 对象上
memcpy_by_audio_format(buffer, format, mMixerBuffer, mMixerBufferFormat,
mNormalFrameCount * mChannelCount);
}
//3、向HAL输出数据
ret = threadLoop_write();
}
向mAudioMixer更新track对象等操作
//prepareTracks_l
AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTracks_l(
Vector< sp<Track> > *tracksToRemove)
{
.....
for (size_t i=0 ; i<count ; i++) {
const sp<Track> t = mActiveTracks[i];
....
// if an active track doesn't exist in the AudioMixer, create it.
if (!mAudioMixer->exists(name)) {
status_t status = mAudioMixer->create(
name,
track->mChannelMask,
track->mFormat,
track->mSessionId);
if (status != OK) {
ALOGW("%s: cannot create track name"
" %d, mask %#x, format %#x, sessionId %d in AudioMixer",
__func__, name, track->mChannelMask, track->mFormat, track->mSessionId);
tracksToRemove->add(track);
track->invalidate(); // consider it dead.
continue;
}
}
....
}
.....
}
void AudioFlinger::MixerThread::threadLoop_mix()
{
// mix buffers...
mAudioMixer->process();
mCurrentWriteLength = mSinkBufferSize;
// increase sleep time progressively when application underrun condition clears.
// Only increase sleep time if the mixer is ready for two consecutive times to avoid
// that a steady state of alternating ready/not ready conditions keeps the sleep time
// such that we would underrun the audio HAL.
if ((mSleepTimeUs == 0) && (sleepTimeShift > 0)) {
sleepTimeShift--;
}
mSleepTimeUs = 0;
mStandbyTimeNs = systemTime() + mStandbyDelayNs;
//TODO: delay standby when effects have a tail
}
//frameworks/av/media/libaudioprocessing/include/media/AudioMixerBase.h
void process() {
preProcess();
//这里才是处理数据
(this->*mHook)();
postProcess();
}
数据获取&混音处理 (this->*mHook)();
mHook 以process__genericResampling 为例:
//frameworks/av/media/libaudioprocessing/AudioMixerBase.cpp
// generic code with resampling
void AudioMixerBase::process__genericResampling()
{
ALOGVV("process__genericResampling\n");
int32_t * const outTemp = mOutputTemp.get(); // naked ptr
size_t numFrames = mFrameCount;
for (const auto &pair : mGroups) {
const auto &group = pair.second;
const std::shared_ptr<TrackBase> &t1 = mTracks[group[0]];
// clear temp buffer
memset(outTemp, 0, sizeof(*outTemp) * t1->mMixerChannelCount * mFrameCount);
for (const int name : group) {
const std::shared_ptr<TrackBase> &t = mTracks[name];
int32_t *aux = NULL;
if (CC_UNLIKELY(t->needs & NEEDS_AUX)) {
aux = t->auxBuffer;
}
// this is a little goofy, on the resampling case we don't
// acquire/release the buffers because it's done by
// the resampler.
if (t->needs & NEEDS_RESAMPLE) {
(t.get()->*t->hook)(outTemp, numFrames, mResampleTemp.get() /* naked ptr */, aux);
} else {
size_t outFrames = 0;
while (outFrames < numFrames) {
t->buffer.frameCount = numFrames - outFrames;
//获取client写入的数据
t->bufferProvider->getNextBuffer(&t->buffer);
t->mIn = t->buffer.raw;
// t->mIn == nullptr can happen if the track was flushed just after having
// been enabled for mixing.
if (t->mIn == nullptr) break;
(t.get()->*t->hook)(
outTemp + outFrames * t->mMixerChannelCount, t->buffer.frameCount,
mResampleTemp.get() /* naked ptr */,
aux != nullptr ? aux + outFrames : nullptr);
outFrames += t->buffer.frameCount;
//通知释放 通知client 有可以写入的buffer了
t->bufferProvider->releaseBuffer(&t->buffer);
}
}
}
/**
这个mainBuffer与 mOutput->write((char *)mSinkBuffer, 0); 中的mSinkBuffer 相关联
//相关代码如下:
//frameworks\av\services\audioflinger\Threads.cpp
AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTracks_l(
Vector< sp<Track> > *tracksToRemove)
{
........
mAudioMixer->setParameter(
name,
AudioMixer::TRACK,
AudioMixer::MIXER_FORMAT, (void *)mMixerBufferFormat);
mAudioMixer->setParameter(
name,
AudioMixer::TRACK,
AudioMixer::MAIN_BUFFER, (void *)mMixerBuffer);
........
}
*/
//这里是将outTemp 数据按最终输出格式拷贝给到 mainBuffer
convertMixerFormat(t1->mainBuffer, t1->mMixerFormat,
outTemp, t1->mMixerInFormat, numFrames * t1->mMixerChannelCount);
}
}
// frameworks\av\services\audioflinger\Threads.cpp
ssize_t AudioFlinger::MixerThread::threadLoop_write()
{
// FIXME we should only do one push per cycle; confirm this is true
// Start the fast mixer if it's not already running
if (mFastMixer != 0) {
FastMixerStateQueue *sq = mFastMixer->sq();
FastMixerState *state = sq->begin();
if (state->mCommand != FastMixerState::MIX_WRITE &&
(kUseFastMixer != FastMixer_Dynamic || state->mTrackMask > 1)) {
if (state->mCommand == FastMixerState::COLD_IDLE) {
// FIXME workaround for first HAL write being CPU bound on some devices
ATRACE_BEGIN("write");
//AudioStreamOut *mOutput;
//致此,audioflinger的写入流程基本结束,剩下的就是写入到audiohal层,
//最后在audiohal处理之后就会通过tinyalsa写入到驱动完成了声音流的输出过程了
mOutput->write((char *)mSinkBuffer, 0);
ATRACE_END();
int32_t old = android_atomic_inc(&mFastMixerFutex);
if (old == -1) {
(void) syscall(__NR_futex, &mFastMixerFutex, FUTEX_WAKE_PRIVATE, 1);
}
#ifdef AUDIO_WATCHDOG
if (mAudioWatchdog != 0) {
mAudioWatchdog->resume();
}
#endif
}
state->mCommand = FastMixerState::MIX_WRITE;
#ifdef FAST_THREAD_STATISTICS
mFastMixerDumpState.increaseSamplingN(mAudioFlinger->isLowRamDevice() ?
FastThreadDumpState::kSamplingNforLowRamDevice : FastThreadDumpState::kSamplingN);
#endif
sq->end();
sq->push(FastMixerStateQueue::BLOCK_UNTIL_PUSHED);
if (kUseFastMixer == FastMixer_Dynamic) {
mNormalSink = mPipeSink;
}
} else {
sq->end(false /*didModify*/);
}
}
return PlaybackThread::threadLoop_write();
}
每个client 创建audiotrack的时候都是要通过AudioFlinger来创建的。audioflinger交给Threads.cpp这边创建,创建完毕后会保存在Threads 的mTracks 对象里面,当每次loop的时候会先调用prepareTracks_l 来准备数据,此时会及时将mTracks 同步到AudioMixer(不是将对象直接赋值给AudioMixer对象,AudioMixer会同步创建一个AudioMixer::Track来与mTracks对应,这个AudioMixer::Track然后再包含Track,最后实际获取数据的时候是通过这个Track来获取到client数据的)。
sp<IAudioTrack> AudioFlinger::createTrack(const CreateTrackInput& input,
CreateTrackOutput& output,
status_t *status)
{
......
track = thread->createTrack_l(client, streamType, input.attr, &output.sampleRate,
input.config.format, input.config.channel_mask,
&output.frameCount, &output.notificationFrameCount,
input.notificationsPerBuffer, input.speed,
input.sharedBuffer, sessionId, &output.flags,
input.clientInfo.clientTid, clientUid, &lStatus, portId);
......
}
sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrack_l(
const sp<AudioFlinger::Client>& client,
audio_stream_type_t streamType,
const audio_attributes_t& attr,
uint32_t *pSampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
size_t *pFrameCount,
size_t *pNotificationFrameCount,
uint32_t notificationsPerBuffer,
float speed,
const sp<IMemory>& sharedBuffer,
audio_session_t sessionId,
audio_output_flags_t *flags,
pid_t tid,
uid_t uid,
status_t *status,
audio_port_handle_t portId)
{
......
track = new Track(this, client, streamType, attr, sampleRate, format,
channelMask, frameCount,
nullptr /* buffer */, (size_t)0 /* bufferSize */, sharedBuffer,
sessionId, uid, *flags, TrackBase::TYPE_DEFAULT, portId);
lStatus = track != 0 ? track->initCheck() : (status_t) NO_MEMORY;
if (lStatus != NO_ERROR) {
ALOGE("createTrack_l() initCheck failed %d; no control block?", lStatus);
// track must be cleared from the caller as the caller has the AF lock
goto Exit;
}
mTracks.add(track);
sp<EffectChain> chain = getEffectChain_l(sessionId);
if (chain != 0) {
ALOGV("createTrack_l() setting main buffer %p", chain->inBuffer());
track->setMainBuffer(chain->inBuffer());
chain->setStrategy(AudioSystem::getStrategyForStream(track->streamType()));
chain->incTrackCnt();
}
......
}
sourceinside阅读有些跳转不了,有点恼火。
文章浏览阅读9.9k次,点赞2次,收藏10次。2017年随着google发布了Kotlin作为android的一级语言,与java100%互通。开发者就陆陆续续从java转到Kotlin中了,我现在有学习了Kotlin几天,的确感觉Kotlin写起来非常简洁,下面我介绍一下如何在android studio配置Kotlin环境。步骤1.在android studio中下载插件(windows)点击File->Setting->..._kotlin怎么指定jdk
文章浏览阅读108次。REM 如果不想该程序一次性查杀后就退出,可以注释掉16行的"exit",这样程序就会一直后台监控。_aceguard
文章浏览阅读1.9k次,点赞51次,收藏47次。通过这道题,我们对算法的时间复杂度和空间复杂度有了一定的理解。时间复杂度和空间复杂度并不是完全独立的两个概念,虽然它们衡量的是算法效率的不同方面,但在实际应用中,这两个因素有时会相互制约,形成一种“时间-空间”的平衡。比如:对于某些问题,使用一种时间复杂度较低的算法可能需要更多的空间来存储中间结果或额外的数据结构,而使用一种空间复杂度较低的算法可能会牺牲一些时间效率。然而,时间复杂度和空间复杂度并不是矛盾的。它们分别衡量算法在不同方面的效率,可以同时达到最优。
文章浏览阅读6.8k次,点赞3次,收藏11次。1 程序保存路径设置鼠标双击Eclipse.exe,打开Eclipse程序。首先在弹出的对话框中设置Eclipse的工作空间,即编写的安卓程序保存的路径,可以使用默认路径,也可以通过点击“Browse...”按键进行自定义设置,如图1-1所示。图1-1 设置Eclipse的工作空间2 安卓应用程序的创建2.1 新建程序在Eclipse主界面的菜单栏中,选择“File->_android中helloworld测试程序的执行过程
文章浏览阅读168次。题目判断一个数是不是为伪素数。能够通过费马测试的合数。分析数论,直接按照定义判断即可。说明学python,ヾ(◍°∇°◍)ノ゙import mathdef is_prime(x): for i in range(2, int(math.sqrt(x))+2): if x % i == 0: return False ..._uva11287
文章浏览阅读4.6k次。对python学习的总结怎么写1.Python初步Python是一种面向对象、直译式计算机程序设计语言。公认的特点是简单、易学、免费、开源等等。个人觉得特别喜欢Python的地方是对字符串操作特别的灵活、采取缩进的方式简单明了(虽然百度百科上把这个说成是局限)、以及简单的语法。Python 和c类似,是顺序进行的,不想visual c++是事件触发不同模块进行的。操作和matlab相似,有编辑窗口..._大学生python实训总结
文章浏览阅读6.7k次,点赞2次,收藏14次。数据在当今世界意味着金钱。随着向基于app的世界的过渡,数据呈指数增长。然而,大多数数据是非结构化的,因此需要一个过程和方法从数据中提取有用的信息,并将其转换为可理解的和可用的形式。数据挖掘或“数据库中的知识发现”是通过人工智能、机器学习、统计和数据库系统发现大数据集中的模式的过程。免费的数据挖掘工具包括从完整的模型开发环境如Knime和Orange,到各种用Java、c++编写的库,最常..._)好用(19)
文章浏览阅读191次。java-net-php-python-jspm米兰酒店管理系统计算机毕业设计程序。springboot基于B_S模式的后勤管理系统-在线报修系统。springcloud基于微服务架构的乐居租房网的设计与实现。springboot基于springboot的社会公益平台。ssm基于web的考试资料交易系统的设计与实现。ssm基于JEE的人才招聘系统的智能化管理。springboot中国民航酒店分销系统。_米兰酒店管理系统登录
文章浏览阅读7.4k次,点赞2次,收藏2次。成本中心是无法直接和公司代码进行配对的。但是利润中心能够绑定公司代码再通过利润中心的对应公司代码可以进行成本中心对应公司代码的对应_sap 成本中心关联公司
文章浏览阅读1.6k次。真实职场关于Web api学习指南(免费开放)一一5.Web api服务结构解析_c# .net5 web api 原理
文章浏览阅读4.8k次,点赞5次,收藏15次。文章目录二元连续型随机变量,联合概率密度联合概率密度函数概率密度的性质二元连续型随机变量,联合概率密度联合概率密度函数定义:对于二元随机变量 (X,Y)(X, Y)(X,Y) 的 分布函数 F(x,y)F(x, y)F(x,y),如果存在非负函数 f(x,y)f(x,y)f(x,y),使对于任意 x,yx,yx,y,有F(x,y)=∫−∞x∫−∞yf(u,v) dudvF(x,y) ..._二元联合密度函数的分布函数
文章浏览阅读2.2k次,点赞2次,收藏20次。【PLC毕业设计】触摸屏立体车库控制系统毕业论文_200smart做毕业设计