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的更多相关文章

  1. [Android] AudioTrack实例

    AudioTrack在Android系统中是用于PCM数据的混音.播放,并不涉及到音频的解码.因此MP3这类经过编码的音频格式文件不能直接通过AudioTrack正确地播放,AudioTrack只能播 ...

  2. Android Audio Play Out Channel

    1: 7嘴8舌 扬声器, 耳机, 和听筒 就是通过: audiomanager.setmode(AudioManager.MODE_IN_COMMUNICATION)audiomanager.setS ...

  3. Android多媒体开发(3)————使用Android NKD编译havlenapetr-FFMpeg-7c27aa2

    1. 使用NDK去编译官方的FFmpeg原版的话,还得自己实现JNI层与Java层,工程量比较大.所以移植FFmpeg到Android平台时,可以移植一些已经实现JNI与JAVA层的开源项目,毕竟软件 ...

  4. 单独编译使用WebRTC的音频处理模块

    块,每块个点,(12*64=768采样)即AEC-PC仅能处理48ms的单声道16kHz延迟的数据,而 - 加载编译好的NS模块动态库 接下来只需要按照 此文 的描述在 android 的JAVA代码 ...

  5. Android音频开发之AudioTrack实时播放

    前言: 其实在Android中录音可以用MediaRecord录音,操作比较简单.但是不能对音频进行处理.考虑到项目中做的是实时语音只能选择AudioRecord进行录音.然后实时播放也只能采用Aud ...

  6. Android使用AudioTrack发送红外信号

    最近要做一个项目,利用手机的耳机口输出红外信号,从而把手机变成红外遥控器,信号处理的知识基本都还给老师了,刚开始真的挺头疼.找了不少资料研究了一下,总算有点心得,在这里做个备忘. 一.音频信号输出原理 ...

  7. Android深入浅出之 AudioTrack分析

    Android深入浅出之Audio 第一部分 AudioTrack分析 一 目的 本文的目的是通过从Audio系统来分析Android的代码,包括Android自定义的那套机制和一些常见类的使用,比如 ...

  8. 使用AudioTrack播放PCM音频数据(android)

    众所周知,Android的MediaPlayer包含了Audio和video的播放功能,在Android的界面上,Music和Video两个应用程序都是调用MediaPlayer实现的.MediaPl ...

  9. Android Audio System 之一:AudioTrack如何与AudioFlinger

    Android Framework的音频子系统中,每一个音频流对应着一个AudioTrack类的一个实例,每个AudioTrack会在创建时注册到 AudioFlinger中,由AudioFlinge ...

随机推荐

  1. mysql5.5 对触发器,函数,存储引擎,事件进行主从复制情况.(转)

       mysql5.5 对触发器,函数,存储引擎,事件进行主从复制情况. 转(http://blog.csdn.net/m582445672/article/details/7670802) 一.My ...

  2. 高性能MySql进化论(九):查询优化器常用的优化方式

    1        介绍 1.1     处理流程 当MYSQL 收到一条查询请求时,会首先通过关键字对SQL语句进行解析,生成一颗“解析树”,然后预处理器会校验“解析树”是否合法(主要校验数据列和表明 ...

  3. Java里的IO流里的 ObjectInputStream 的读取\写入!

    各位好!!我又来了!!今天遇见了一个小问题!! IO流里的对象读取总是出错!各种报错!!神烦啊!!百思不得其解啊!然后就上网百度!找了好久终于让我找到了!下面就让我来说一说! 当你用IO流里的对象流写 ...

  4. 几种任务调度的 Java 实现方法与比较--转载

    前言 任务调度是指基于给定时间点,给定时间间隔或者给定执行次数自动执行任务.本文由浅入深介绍四种任务调度的 Java 实现: Timer ScheduledExecutor 开源工具包 Quartz ...

  5. Undefined symbols for architecture i386

    这个错误的发生原因一般是“XXX”这个文件(类库)虽然引入了工程中,但是由于种种原因(常见于多人开发,svn同步不规范)导致“XXX”并未被添加到project.pbxproj这个文件中. 解决方法是 ...

  6. C#泛型类的简单创建与使用

    using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Cons ...

  7. HUD3336

    /* 巧妙地使用fail数组 根据fail数组的定义 fail[i] 有 长度为i的子串最长公共前后缀为fail[i] 比如样例 fail 0 0 1 2 那么我们维护一个ans[i]表示到i位置的时 ...

  8. HTML5 History对象,Javascript修改地址栏而不刷新页面

    一.History对象 History 对象包含用户(在浏览器窗口中)访问过的 URL. History 对象是 window 对象的一部分,可通过 window.history 属性对其进行访问. ...

  9. 详解Linux服务器最大tcp连接数

    网络编程在tcp应用中,server事先在某个固定端口监听,client主动发起连接,经过三路握手后建立tcp连接.那么对单机,其最大并发tcp连接数是多少? 如何标识一个TCP连接在确定最大连接数之 ...

  10. 4.HTTP入门

    什么是http协议查看http协议的工具http协议内容Http请求请求行http协议版本请求资源请求方式GET方式提交POST方式提交请求头3.3 实体内容3.4 HttpServletReques ...