[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 ...
随机推荐
- mongoDB的基本使用----飞天博客
Mongo的介绍:这个mongoDB官网说的好啊,MongoDB是一个开源的基于document的数据库,并且是优秀的NoSQL数据库,并且它是用C++写滴哈,非常有效率.一些什么特点呢? 全索引支持 ...
- Firemonkey的旁门左道[四]
做开发,就是发现问题,解决问题,又发现问题...周而复始的循环 下面又应该是Firemonkey下的bug. 官方文档中描述: Using the FireMonkey TMenuBar's OSMe ...
- Reso | liunx下longeneQQ和搜狗拼音
sogoupinyin_2.0.0.0078_amd64.deb: http://pan.baidu.com/s/1eSDLvEU WineQQ7.8-20151109-Longene .deb: ...
- C/C++笔试题整理
1. C的结构体和C++结构体的区别 (1)C的结构体内不允许有函数存在,C++允许有内部成员函数,且允许该函数是虚函数.所以C的结构体是没有构造函数.析构函数.和this指针的. (2)C的结构体对 ...
- Linux 基本命令(持续更新ing)
cd -> 变换路径 //文件一般存在/var/路径下,var为可修改存储盘 ls -> 列出所有隐藏文件与相关文件的属性 #ls -al ...
- Python多线程锁
[Python之旅]第六篇(四):Python多线程锁 python lock 多线程 多线程使用方法 多线程锁 摘要: 在多线程程序执行过程中,为什么需要给一些线程加锁以及如何加锁,下面就来 ...
- 三星I9308(移动版)正确Root的方法,进入正确的recovery的关键(网上很多方法是误导)
三星I9308(移动版)正确Root的方法,进入正确的recovery的关键(网上很多方法是误导) 1)首先在电脑上安装手机驱动:下载地址:点击这里下载 2)手机设置USB调试 方法1:设置- ...
- 如何在ASP.NET端获取屏幕宽度
using System.Windows.Forms; 首先引用上面的命名空间. 然后在代码中获取屏幕信息.如下代码: System.Windows.Forms.Screen screen = Sys ...
- jquery $('#btn').click与$("#btn").live("click",function()有什么区别?
live方法绑定的事件处理函数,在页面中未来添加的元素只要满足原来的选择器,仍然会导致事件触发.普通的事件绑定则没有这个效果.对于#btn这个选择器来说,如果你未来将id为btn的元素删除,然后再创建 ...
- Python 函数的使用 外加引入文件
#coding=utf-8 #!user/bin/python import sys import test2 def functionsss(name,sex,age=25): print name ...