[Android] AudioTrack::start
AudioTrack的start方法用于实现Android的音频输出,start究竟做了什么?回顾一下上一小节createTrack_l的最后部分,通过binder返回了一个Track的句柄,并以被保存了下来
status_t AudioTrack::createTrack_l(...)
{
sp<IAudioTrack> track = audioFlinger->createTrack(...);
mAudioTrack = track;
}
start主要就是调用这个track的start方法实现音频输出功能的
// ------------------------------------------------------------------------- status_t AudioTrack::start()
{
AutoMutex lock(mLock);
//如果该AudioTrack已经是start状态,直接返回
if (mState == STATE_ACTIVE) {
return INVALID_OPERATION;
} mInUnderrun = true;
//保存上一次的状态
State previousState = mState;
//设置当前状态
if (previousState == STATE_PAUSED_STOPPING) {
mState = STATE_STOPPING;
} else {
mState = STATE_ACTIVE;
}
//如果上一状态是停止状态,表明需要重新把position设置为0,从头播放
if (previousState == STATE_STOPPED || previousState == STATE_FLUSHED) {
// reset current position as seen by client to 0
mProxy->setEpoch(mProxy->getEpoch() - mProxy->getPosition());
// force refresh of remaining frames by processAudioBuffer() as last
// write before stop could be partial.
mRefreshRemaining = true;
}
//当前位置
mNewPosition = mProxy->getPosition() + mUpdatePeriod;
//获取share buffer的flag,原子操作
int32_t flags = android_atomic_and(~CBLK_DISABLED, &mCblk->mFlags); //是否有回调线程,一般如果我们在apk端独立调用AudioTrack,是不会设置回调线程的,但是AudioPlayer这种系统播放器则会设置回调线程
//这样做是为了设置优先级,否则Audio可能会由于得不到时间片,而卡顿
//如果是AudioPlayer,会有自己定义的优先级,AudioTrack后面新创建的线程则会继承它的优先级
//如果是Apk调用,优先级一般都是固定的,那么我们需要在这里设置一个ANDROID_PRIORITY_AUDIO的优先级来保证Audio的流畅输出
sp<AudioTrackThread> t = mAudioTrackThread;
if (t != 0) {
if (previousState == STATE_STOPPING) {
//中断
mProxy->interrupt();
} else {
//恢复播放
t->resume();
}
} else {
//保存当前线程优先级,在后面停止的时候设置回来
mPreviousPriority = getpriority(PRIO_PROCESS, 0);
get_sched_policy(0, &mPreviousSchedulingGroup);
//设置线程优先级为ANDROID_PRIORITY_AUDIO
androidSetThreadPriority(0, ANDROID_PRIORITY_AUDIO);
} status_t status = NO_ERROR;
if (!(flags & CBLK_INVALID)) {
//如果share buffer可用,则调用track的start方法
status = mAudioTrack->start();
if (status == DEAD_OBJECT) {
flags |= CBLK_INVALID;
}
}
if (flags & CBLK_INVALID) {
status = restoreTrack_l("start");
} if (status != NO_ERROR) {
//start出错后的处理
ALOGE("start() status %d", status);
mState = previousState;
if (t != 0) {
if (previousState != STATE_STOPPING) {
t->pause();
}
} else {
setpriority(PRIO_PROCESS, 0, mPreviousPriority);
set_sched_policy(0, mPreviousSchedulingGroup);
}
} return status;
}
由于mAudioTrack是binder的proxy对象,因此start会调用到BBinder对象的start方法,即
status_t AudioFlinger::TrackHandle::start() {
return mTrack->start();
}
由于我们是在PlaybackThread下进行音频输出的,因此会进一步调用到PlaybackThread::Track:: start方法,其中最主要的是下面两个步骤:
status_t AudioFlinger::PlaybackThread::Track::start(
PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
status = playbackThread->addTrack_l(this);
}
还记得我们在getOutput的时候创建了一个MixerThread吗,而且在createTrack_l的时候把这个Thread加入了mPlaybackThreads进行管理,现在我们要把它取出来,调用它的addTrack_l方法了
audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module,...)
{ thread = new MixerThread(this, output, id, *pDevices); return id;
} AudioFlinger::PlaybackThread *AudioFlinger::checkPlaybackThread_l(audio_io_handle_t output) const
{ return mPlaybackThreads.valueFor(output).get();
}
在addTrack_l方法内,主要步骤有三个:
- 如果该track(share buffer)是新增track,则需要调用startOutput进行初始化
- 把该track加入mActiveTracks
- 发送广播,通知MixerThread开始工作
// addTrack_l() must be called with ThreadBase::mLock held
status_t AudioFlinger::PlaybackThread::addTrack_l(const sp<Track>& track)
{
if (mActiveTracks.indexOf(track) < 0) {
status = AudioSystem::startOutput(mId, track->streamType(), track->sessionId());
}
mActiveTracks.add(track); broadcast_l();
}
1. track初始化
在分析getOutput的时候,我们已经知道Audio接口的调用流程,即AudioSystem->AudioPolicyService->Audio_policy_hal->AudioPolicyManagerBase,现在我们来看一下AudioPolicyManagerBase:: startOutput做了什么
status_t AudioPolicyManagerBase::startOutput(audio_io_handle_t output,
AudioSystem::stream_type stream,
int session)
{
checkAndSetVolume(stream,
mStreams[stream].getVolumeIndex(newDevice),
output,
newDevice);
}
checkAndSetVolume其实只是设置了stream volume.
2. track加入mAudioTrack
mAudioTrack即当前MixerThread所包含的Track集合,在后面就是对这些Track集合进行混音
3. broadcast_l
void AudioFlinger::PlaybackThread::broadcast_l()
{
// Thread could be blocked waiting for async
// so signal it to handle state changes immediately
// If threadLoop is currently unlocked a signal of mWaitWorkCV will
// be lost so we also flag to prevent it blocking on mWaitWorkCV
mSignalPending = true;
mWaitWorkCV.broadcast();
}
我们已经有了MixerThread,由于MixerThread继承与PlaybackThread,因此跑的是PlaybackThread::threadLoop,在threadLoop内,如果mActiveTrack为空的话,表明没有音频数据等待输出,那么threadLoop会进入睡眠,等待唤醒,这里的broadcast就是做了这个唤醒的工作
bool AudioFlinger::PlaybackThread::threadLoop()
{
if ((!mActiveTracks.size() && systemTime() > standbyTime) ||
isSuspended()) mWaitWorkCV.wait(mLock); }
...
}
下面是start的总体流程
[Android] AudioTrack::start的更多相关文章
- [Android] AudioTrack实例
AudioTrack在Android系统中是用于PCM数据的混音.播放,并不涉及到音频的解码.因此MP3这类经过编码的音频格式文件不能直接通过AudioTrack正确地播放,AudioTrack只能播 ...
- Android Audio Play Out Channel
1: 7嘴8舌 扬声器, 耳机, 和听筒 就是通过: audiomanager.setmode(AudioManager.MODE_IN_COMMUNICATION)audiomanager.setS ...
- Android多媒体开发(3)————使用Android NKD编译havlenapetr-FFMpeg-7c27aa2
1. 使用NDK去编译官方的FFmpeg原版的话,还得自己实现JNI层与Java层,工程量比较大.所以移植FFmpeg到Android平台时,可以移植一些已经实现JNI与JAVA层的开源项目,毕竟软件 ...
- 单独编译使用WebRTC的音频处理模块
块,每块个点,(12*64=768采样)即AEC-PC仅能处理48ms的单声道16kHz延迟的数据,而 - 加载编译好的NS模块动态库 接下来只需要按照 此文 的描述在 android 的JAVA代码 ...
- Android音频开发之AudioTrack实时播放
前言: 其实在Android中录音可以用MediaRecord录音,操作比较简单.但是不能对音频进行处理.考虑到项目中做的是实时语音只能选择AudioRecord进行录音.然后实时播放也只能采用Aud ...
- Android使用AudioTrack发送红外信号
最近要做一个项目,利用手机的耳机口输出红外信号,从而把手机变成红外遥控器,信号处理的知识基本都还给老师了,刚开始真的挺头疼.找了不少资料研究了一下,总算有点心得,在这里做个备忘. 一.音频信号输出原理 ...
- Android深入浅出之 AudioTrack分析
Android深入浅出之Audio 第一部分 AudioTrack分析 一 目的 本文的目的是通过从Audio系统来分析Android的代码,包括Android自定义的那套机制和一些常见类的使用,比如 ...
- 使用AudioTrack播放PCM音频数据(android)
众所周知,Android的MediaPlayer包含了Audio和video的播放功能,在Android的界面上,Music和Video两个应用程序都是调用MediaPlayer实现的.MediaPl ...
- Android Audio System 之一:AudioTrack如何与AudioFlinger
Android Framework的音频子系统中,每一个音频流对应着一个AudioTrack类的一个实例,每个AudioTrack会在创建时注册到 AudioFlinger中,由AudioFlinge ...
随机推荐
- Windows的应用管理工具 PortableApps,Chocolatey和Ninite
以前为了让我的firefox变成portable的,我使用了PortableApps,它不只是做软件的绿色版,而且也是一个软件的管理平台,可以通过它来管理软件的更新,挺方便的. 前段时间试用Scrip ...
- TCP/UDP 、HTTP、IP 、socket 的关系。
网络有上下分为7 层.物理层,数据链路层.网络层.会话层.应用层.传输层: IP协议位于网络层,IP和端口来控制网络流向: TCP.UDP是基于传输层.TCP保证三次握手.传递数据: UDP为不考虑是 ...
- [Angular 2] Pipe Purity
Explaining how Pipes only change by default when your Pipe input parameters change and not when your ...
- Eclipse debug经常使用基本技巧
1.F5单步调试,步入,进入函数体内部 2.F6单步调试.步过.不进入函数体 3.F7返回 4.F8运行到最后 5.退出时.右键点击右上角Debug选择退出就可以 $(function () { $( ...
- Java对存储过程的调用方法 --转载
一.Java如何实现对存储过程的调用: A:不带输出参数的 create procedure getsum <--此处为参数--> as declare @sum int<--定义变 ...
- Fedora14下首次搭建Samba服务器遇到的一些问题
SMB(Server Messages Block,信息服务块)是一种在局域网上共享文件和打印机的通信协议.而Samba则是在Linux和Unix系统上实现SMB协议的一个免费软件,由服务器及客户端程 ...
- 教学项目之-通过Python实现简单的计算器
教学项目之-通过Python实现简单的计算器 计算器开发需求 实现加减乘除及拓号优先级解析 用户输入 1 - 2 * ( (60-30 +(-40/5) * (9-2*5/3 + 7 /3*99/ ...
- noip 2012 借教室 (线段树 二分)
/* 维护区间最小值 数据不超int 相反如果long long的话会有一组数据超时 无视掉 ll int */ #include<iostream> #include<cstdio ...
- codevs 1725 探险 (二分)
/* 二分答案 这个题目要求“体力和最小的那个小组的所有人的体力和尽量大” 很明显我们二分最小体力 如果合法 逐渐放大 但是这里我们二分的是最小而不是最大 所以累加的体力>=ans时 跳过当前体 ...
- 织梦DedeCMS广告管理模块增加图片上传功能插件
网站广告后台管理非常方便,但是织梦后台的广告管理模块,发布广告时图片没有上传选项,只能用URL地址,很不方便,那么下面就教大家一个方法实现广告图片后台直接上传,非常方便. 先给大家看下修改后的广告图片 ...