[Android] createTrack_l
在分析AudioTrack的时候,第一步会new AudioTrack,并调用他的set方法。在set方法的最后调用了createTrack_l创建音轨。我们现在来分析createTrack_l的流程。
在分析createTrack_l之前,我们先来了解Android音频流的从PCM到输出的路线。首先,我们的PCM音频数据一般会在用户端,而混音会在AudioFlinger端,因此需要把PCM数据传送给AudioFlinger,因此需要开辟出一块内存用于数据传送;数据到了AudioFlinger之后,可以给PCM数据调节音量,增加音效等(即混音),因此还需要一块内存用于音效处理,这块buffer在getOutput内已经开辟;混音完成后即可把PCM数据输出给音频设备进行播放。
creatTrack_l的任务主要是创建音轨,即开辟出数据传送的内存。具体实现是创建出一块share buffer,这块buffer既可以被AudioTrack写入,又可以被AudioFlinger读取进行混音。
createTrack总体可以分为三个步骤:
- 从AudioFlinger获取创建sharebuffer所需的参数,如latency,framecount,sampleRate;然后与传入的参数(framecount,sampleRate)做对比,目的是计算出正确的framecount
- 从AudioFlinger创建buffer,并创建对sharebuffer进行控制的对象AudioTrackServerProxy
- 创建可以对sharebuffer进行控制的对象AudioTrackClientProxy
1. 获取正确framecount
AudioTrack按照如下方式获取framecount
status_t AudioTrack::createTrack_l( status = AudioSystem::getLatency(output, streamType, &afLatency); status = AudioSystem::getFrameCount(output, streamType, &afFrameCount); status = AudioSystem::getSamplingRate(output, streamType, &afSampleRate); if (!audio_is_linear_pcm(format)) {
if (sharedBuffer != 0) {
// Same comment as below about ignoring frameCount parameter for set()
frameCount = sharedBuffer->size();
} else if (frameCount == 0) {
frameCount = afFrameCount;
}
if (mNotificationFramesAct != frameCount) {
mNotificationFramesAct = frameCount;
}
} else if (sharedBuffer != 0) {
// user share buffer,we donot neet to allocate
// Ensure that buffer alignment matches channel count
// 8-bit data in shared memory is not currently supported by AudioFlinger
size_t alignment = /* format == AUDIO_FORMAT_PCM_8_BIT ? 1 : */ 2;
if (mChannelCount > 1) {
alignment <<= 1;
}
if (((size_t)sharedBuffer->pointer() & (alignment - 1)) != 0) {
return BAD_VALUE;
}
frameCount = sharedBuffer->size()/mChannelCount/sizeof(int16_t); } else if (!(flags & AUDIO_OUTPUT_FLAG_FAST)) {
// non-fast
uint32_t minBufCount = 2; if (minBufCount <= nBuffering) {
minBufCount = nBuffering;
} // calculate buffer size by param from AudioFlinger
size_t minFrameCount = (afFrameCount*sampleRate*minBufCount)/afSampleRate; if (frameCount == 0) {
frameCount = minFrameCount;
} else if (frameCount < minFrameCount) {
frameCount = minFrameCount;
}
} else {
// For fast tracks, the frame count calculations and checks are done by server
}
先看一下AudioTrack计算framecount时的式子:
minFrameCount = (afFrameCount*sampleRate*minBufCount)/afSampleRate;
afFrameCount与afSampleRate都是从AudioFlinger得到的两个参数。
- afFrameCount代表MixerBuffer的大小,单位为Frame。Frame的定义为PCM音频数据的一个“采样 * 音轨个数”。
- afSampleRate代表MixerBuffer的默认采样率,即一秒内包含的Frame数目。
因此有如下公式:
$BufferSeconds = \frac{afFrameCount}{afSampleRate} = \frac{frameCount}{sampleRate}$
计算出buffer中包含多少秒音频数据。
下面是一个buffer实例,虽然sample rate一般都会是44100,但是为了方便画图,下面以5代替
AudioFlinger获取AfFrameCount的过程如下:
//AudioFlinger.cpp
size_t AudioFlinger::frameCount(audio_io_handle_t output) const
{
return thread->frameCount();
} //Thread.h
virtual size_t frameCount() const { return mNormalFrameCount; } //Thread.cpp
void AudioFlinger::PlaybackThread::readOutputParameters()
{
mFrameCount = mOutput->stream->common.get_buffer_size(&mOutput->stream->common) / mFrameSize;
mNormalFrameCount = multiplier * mFrameCount;
} //Audio_hw.c
#define SHORT_PERIOD_SIZE 512 static size_t out_get_buffer_size_low_latency(const struct audio_stream *stream)
{
struct tuna_stream_out *out = (struct tuna_stream_out *)stream; /* take resampling into account and return the closest majoring
multiple of 16 frames, as audioflinger expects audio buffers to
be a multiple of 16 frames. Note: we use the default rate here
from pcm_config_tones.rate. */
size_t size = (SHORT_PERIOD_SIZE * DEFAULT_OUT_SAMPLING_RATE) / pcm_config_tones.rate;
size = ((size + 15) / 16) * 16;
return size * audio_stream_frame_size((struct audio_stream *)stream);
}
获取与AfSampleRate的过程如下:
//AudioFlinger.cpp
uint32_t AudioFlinger::sampleRate(audio_io_handle_t output) const
{
return thread->sampleRate();
} //Thread.h
uint32_t sampleRate() const { return mSampleRate; } //Thread.cpp where sample rate be initialized
void AudioFlinger::PlaybackThread::readOutputParameters()
{
mSampleRate = mOutput->stream->common.get_sample_rate(&mOutput->stream->common);
} //Audio_hw.c
#define DEFAULT_OUT_SAMPLING_RATE 44100 // 48000 is possible but interacts poorly with HDMI static uint32_t out_get_sample_rate(const struct audio_stream *stream)
{
return DEFAULT_OUT_SAMPLING_RATE;
}
而minFrameCount则包含了minBufferCount,即share buffer有多少个Mixer Buffer的大小
// The client's AudioTrack buffer is divided into n parts for purpose of wakeup by server, where
// n = 1 fast track; nBuffering is ignored
// n = 2 normal track, no sample rate conversion
// n = 3 normal track, with sample rate conversion
// (pessimistic; some non-1:1 conversion ratios don't actually need triple-buffering)
// n > 3 very high latency or very small notification interval; nBuffering is ignored
- 如果在调用set方法的的时候,指定了flag为fast track,则表明希望Audio Buffer内的数据被尽快处理,因此Buffer会被创建得比较小,期采用单buffer
- 一般情况下,即输入PCM音频数据的采样率与输出音频数据的采样率一样的话,则不用进行采样率转换,采用双buffer
- 在需要采样率转换的情况,则采用三buffer
- 在碰到高延迟的情况,(如硬件不能及时输出PCM音频),则需要更大的buffer对数据进行缓存
2. AudioFlinger创建share buffer
AudioTrack是通过调用AudioFlinger的createTrack的方法来实现创建share buffer。createTrack的步骤如下:
- 获取输出线程PlaybackThread
- 调用获取到的PlaybackThread的createTrack_l函数来创建Track对象,在Track对象内部会创建share buffer
- 创建Track的binder对象TrackHandle,Track由于需要通过binder返回给AudioTrack,因此是个binder对象,该对象会包含share buffer的信息
sp<IAudioTrack> AudioFlinger::createTrack(...)
{
PlaybackThread *thread = checkPlaybackThread_l(output); track = thread->createTrack_l(client, streamType, sampleRate, format,
channelMask, frameCount, sharedBuffer, lSessionId, flags, tid, clientUid, &lStatus); trackHandle = new TrackHandle(track);
return trackHandle;
}
①. 获取输出线程PlaybackThread
还记得getOutput时所创建的PlaybackThread吗?PlaybackThread会在创建MixerThread时一同被创建。在getOutput内,我们把该thread放进了mPlaybackThreads进行维护。现在我们有需要把它取出来。
AudioFlinger::PlaybackThread *AudioFlinger::checkPlaybackThread_l(audio_io_handle_t output) const
{
return mPlaybackThreads.valueFor(output).get();
}
②. 调用PlaybackThread的createTrack_l
在createTrack_l内调用了new Track来实现创建share buffer
sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrack_l(...)
{
track = new Track(this, client, streamType, sampleRate, format,
channelMask, frameCount, sharedBuffer, sessionId, uid, *flags);
}
Track的父类是TrackBase,因此会先构建TrackBase对象
// TrackBase constructor must be called with AudioFlinger::mLock held
AudioFlinger::ThreadBase::TrackBase::TrackBase(...)
{
// buffer header
size_t size = sizeof(audio_track_cblk_t); // buffer content size
size_t bufferSize = (sharedBuffer == 0 ? roundup(frameCount) : frameCount) * mFrameSize;
if (sharedBuffer == 0) {
size += bufferSize;
} if (client != 0) {
//allocate share buffer
mCblkMemory = client->heap()->allocate(size);
if (mCblkMemory != 0) {
mCblk = static_cast<audio_track_cblk_t *>(mCblkMemory->pointer());
// can't assume mCblk != NULL
} else {
ALOGE("not enough memory for AudioTrack size=%u", size);
client->heap()->dump("AudioTrack");
return;
}
} else {
// this syntax avoids calling the audio_track_cblk_t constructor twice
mCblk = (audio_track_cblk_t *) new uint8_t[size];
// assume mCblk != NULL
} // construct the shared structure in-place.
if (mCblk != NULL) {
// this is header above buffer content
new(mCblk) audio_track_cblk_t();
// clear all buffers
mCblk->frameCount_ = frameCount;
if (sharedBuffer == 0) {
mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t);
memset(mBuffer, 0, bufferSize);
} else {
mBuffer = sharedBuffer->pointer();
} }
}
其中,创建出来的buffer需要包含存放Audio PCM data的share buffer,还需要包含audio_track_cblk_t这个buffer头。调用heap->allocate这个函数来创建share buffer,buffer头部调用new(mCblk) audio_track_cblk_t;这种定位new的方式来创建。buffer的结构如下:
new Track在构造函数体内,会创建AudioTrackServerProxy,这个对象会被用作AudioFlinger这边的buffer操作,由于share buffer是跨线程,甚至是跨进程的,而Proxy可以保证buffer访问的线程安全。
AudioFlinger::PlaybackThread::Track::Track(
{
mAudioTrackServerProxy = new AudioTrackServerProxy(mCblk, mBuffer, frameCount,mFrameSize);
mServerProxy = mAudioTrackServerProxy;
}
③. 创建TrackHandle
由于share buffer不止会在AudioFlinger这端被读取,还会在AudioTrack这端被写入,因此创建出来的Track需要被传送回AudioTrack。而在binder间传送对象只有binder对象,因此需要构建binder对象TrackHandle,返回给AudioTrack。
sp<IAudioTrack> AudioFlinger::createTrack(...)
{
trackHandle = new TrackHandle(track);
} // TrackHandle is a BnBinder object
class TrackHandle : public android::BnAudioTrack {
...
}
至此,createTrack_l在AudioFlinger这端的工作基本完成了。
3. 创建ClientProxy
有ServerProxy,相应地也会有ClientProxy,AudioTrackClientProxy就是在AudioTrack端可以对Track(share buffer)进行操作的类。
从AudioFlinger的createTrack返回TrackHandle后,就能通过TrackHandle的相关函数获得Track的信息,如buffer的起始地址等。用这些信息构造AudioTrackClientProxy.
status_t AudioTrack::createTrack_l(...)
{
sp<IAudioTrack> track = audioFlinger->createTrack(...);
sp<IMemory> iMem = track->getCblk();
audio_track_cblk_t* cblk = static_cast<audio_track_cblk_t*>(iMem->pointer()); mProxy = new AudioTrackClientProxy(cblk, buffers, frameCount, mFrameSizeAF);
}
4. 总结
最后,总结一下各个对象间的关系。
AudioFlinger:
- 首先会在AudioFlinger端创建Track,Track内包含buffer的创建及buffer指针的维护
- Track内部有一个AudioTrackServerProxy的成员对象,用于进行buffer的相关操作
- TrackHandle是Track对象的Binder实例,用于通过Binder返回给AudioTrack
AudioTrack:
- IAudioTrack是TrackHandle在AudioTrack端相对应的类,该类用于提供buffer的相关信息给AudioTrackClientProxy
- AudioTrackClientProxy获得buffer的信息后,即可以对buffer进行相关操作
createTrack_l的总体流程如下:
[Android] createTrack_l的更多相关文章
- [Android] 混音器AudioMixer
AudioMixer是Android的混音器,通过混音器可以把各个音轨的音频数据混合在一起,然后输出到音频设备. 创建AudioMixer AudioMixer在MixerThread的构造函数内创建 ...
- [Android] AudioTrack::start
AudioTrack的start方法用于实现Android的音频输出,start究竟做了什么?回顾一下上一小节createTrack_l的最后部分,通过binder返回了一个Track的句柄,并以被保 ...
- [Android] AudioTrack实例
AudioTrack在Android系统中是用于PCM数据的混音.播放,并不涉及到音频的解码.因此MP3这类经过编码的音频格式文件不能直接通过AudioTrack正确地播放,AudioTrack只能播 ...
- 深入剖析Android音频之AudioTrack
播放声音能够用MediaPlayer和AudioTrack,两者都提供了java API供应用开发人员使用.尽管都能够播放声音.但两者还是有非常大的差别的.当中最大的差别是MediaPlayer能够播 ...
- Android 音频播放分析笔记
AudioTrack是Android中比较偏底层的用来播放音频的接口,它主要被用来播放PCM音频数据,和MediaPlayer不同,它不涉及到文件解析和解码等复杂的流程,比较适合通过它来分析Andro ...
- [深入理解Android卷一全文-第七章]深入理解Audio系统
由于<深入理解Android 卷一>和<深入理解Android卷二>不再出版,而知识的传播不应该由于纸质媒介的问题而中断,所以我将在CSDN博客中全文转发这两本书的全部内容. ...
- 【原】Android热更新开源项目Tinker源码解析系列之三:so热更新
本系列将从以下三个方面对Tinker进行源码解析: Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Android热更新开源项目Tinker源码解析系列之二:资源文件热更新 A ...
- 配置android sdk 环境
1:下载adnroid sdk安装包 官方下载地址无法打开,没有vpn,使用下面这个地址下载,地址:http://www.android-studio.org/
- Android SwipeRefreshLayout 下拉刷新——Hi_博客 Android App 开发笔记
以前写下拉刷新 感觉好费劲,要判断ListView是否滚到顶部,还要加载头布局,还要控制 头布局的状态,等等一大堆.感觉麻烦死了.今天学习了SwipeRefreshLayout 的用法,来分享一下,有 ...
随机推荐
- js~一个列表中包含上移下移删除等功能
最近做了一个项目,包括了一个列表页,为了用户体验,操作均使用JS实现,其中包括在列表中实现上移,下移,删除等功能,前台JS,后端数据修改使用AJAX,本文主要说一下前台JS这块 先看一下页面的截图
- border、margin、padding属性的区别
可以先看下这个视频教程:http://my.tv.sohu.com/us/97014746/64226777.shtml 本文参考:http://www.cnblogs.com/chinhr/arch ...
- C\C++代码优化的27个建议
1. 记住阿姆达尔定律: funccost是函数func运行时间百分比,funcspeedup是你优化函数的运行的系数. 所以,如果你优化了函数TriangleIntersect执行40%的运行时间, ...
- Qt 学习之路 :Qt 线程相关类
希望上一章有关事件循环的内容还没有把你绕晕.本章将重新回到有关线程的相关内容上面来.在前面的章节我们了解了有关QThread类的简单使用.不过,Qt 提供的有关线程的类可不那么简单,否则的话我们也没必 ...
- migrate from weblogic to tomcat: directory mapping--reference
Question: I am trying to migrate from weblogic to tomcat. in weblogic I have <virtual-directory-m ...
- 第二篇:R语言数据可视化之数据塑形技术
前言 绘制统计图形时,半数以上的时间会花在调用绘图命令之前的数据塑型操作上.因为在把数据送进绘图函数前,还得将数据框转换为适当格式才行. 本文将给出使用R语言进行数据塑型的一些基本的技巧,更多技术细节 ...
- Python爬虫:获取糗事百科笑话
为了收集笑话也是挺拼的,我就不相信你所有的都看过了.还有,请问哪位仁兄能指点之下怎么把网上抓取到的图片写到word里面,捉摸了好久都没弄出来. 糗百不需要登录,html直接解析,只要在reques ...
- 如何解决eclipse上的Android程序“Please ensure that adb is correctly located at 'D:\eclipse\sdk\platform-tools\adb.exe' and can be executed.”小问题?
首先,把运行的Android模拟器和eclipse一块儿关了, 然后win+R,cmd, 下面输入adb kill_server 再输入adb start_server 之后重新运行项目,不出意外的话 ...
- Ubuntu上glibc CVE-2015-7547漏洞的POC验证和修复
Ubuntu上查看Glibc版本 $ldd --version ldd (Ubuntu GLIBC 2.21-0ubuntu4) 2.21 Ubuntu上查看使用Glibc的相关程序 sudo lso ...
- 重装系统时,将MBR分区转为GPT 分区
摘要 很多同学在重装系统的时候,或多或少都遇到过这样的问题:镜像文件没有问题,软碟通刻录也没有问题,但偏偏就在选择安装系统盘盘符的时候,跳出对话框,提示:Windows无法安装到这个磁盘,选中的磁盘具 ...