AudioTrack播放音频的流程
来源:互联网 发布:mac忘记了管理员密码 编辑:程序博客网 时间:2024/05/17 17:39
之前从事手机方案开发的时候对Audio这块只有个大概的印象,并没有去仔细地看过。
当播放音乐的时候,尤其是缓冲音频数据时,我们会用到AudioTrack类。
首先得new一个对象出来,
AudioTrack mPlayer = new AudioTrack(3, 44100, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT, mTrackBufflen, 1);
先来看下这个初始化函数中做了哪些事情。主要位于android_media_AudioTrack.cpp中的setup()函数中。
1.获取当前对应设备的frame size和sample rate
2.检查输入的参数正确性,如声道、pcm位数
3.初始化一个AudioTrack对象` sp lpTrack = new AudioTrack();
4.这里出现两个类型的源:MODE_STREAM和MODE_STATIC,
STREAM的意思是由用户在应用程序通过write方式把数据一次一次得写到audiotrack中。这个和我们在socket中发送数据一样,应用层从某个地方获取数据,例如通过编解码得到PCM数据,然后write到audiotrack。
这种方式的坏处就是总是在JAVA层和Native层交互,效率损失较大。
而STATIC的意思是一开始创建的时候,就把音频数据放到一个固定的buffer,然后直接传给audiotrack,后续就不用一次次得write了。AudioTrack会自己播放这个buffer中的数据。
`
5.调用AudioTrack.cpp中的set函数。
在set函数中,主要处理的地方有以下几点
5.1 通过aps获取当前情景模式下的输出profile。
5.2 打开对应的output,
这里先把一大堆的什么Thread之类的简单介绍下,反正我已经晕了:
*一个AudioTrack会new一个MixedThread/DirectOutputThread之类的对象,然后他打开的output拥有一个mPlaybackThreads变量,里面加入了所有的new出来的MixedThread/DirectOutputThread之类的对象,具体干活的是这些子Thread。
一个AudioTrack对象会有一个Track对象
通过下面的类图可以看到父类RecordThread和PlaybackThread是最主要的。这两个Thread会有一个mTracks数组对象包含了所有的Track对象,还有一个mActiveTrack来代表当前哪个是正在活动的Track。PlaybackThread额外有一个mActiveTracks来保存当前active的Track队列,可能是混音时需要一层层叠加吧*
output = mpClientInterface->openOutput(profile->mModule->mHandle, &outputDesc->mDevice, &outputDesc->mSamplingRate, &outputDesc->mFormat, &outputDesc->mChannelMask, &outputDesc->mLatency, outputDesc->mFlags, offloadInfo);
audio_io_handle_t output = AudioSystem::getOutput( streamType, sampleRate, format, channelMask, flags, offloadInfo);
5.2.1 调用AF中的
audio_io_handle_t AudioFlinger::openOutput(){ ****省略**** status_t status = hwDevHal->open_output_stream(hwDevHal, id, *pDevices, (audio_output_flags_t)flags, &config, &outStream);**//5.2.1.1** if (status == NO_ERROR && outStream != NULL) { ****省略**** thread = new DirectOutputThread(this, output, id, *pDevices);**//5.2.1.2** ****省略**** } else { thread = new MixerThread(this, output, id, *pDevices); ALOGV("openOutput() created mixer output: ID %d thread %p", id, thread); } mPlaybackThreads.add(id, thread);**//5.2.1.2**}
5.2.1.1 此时会找到真正对应的profile中处理的函数,具体如果是默认状态下回是audio_hw.c
out = (struct stub_stream_out *)calloc(1, sizeof(struct stub_stream_out));
实际工作就是创建了一个包含该流参数和一些控制函数的结构体。
5.2.1.2 new 一个MixerThread并加入mPlaybackThreads,这里涉及到各种不同种类的thread。附上一张图
5.3 开始创建属于自己的真正的Track。
status_t status = createTrack_l(streamType, sampleRate, format, frameCount, flags, sharedBuffer, output, 0 /*epoch*/)
实际调用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;//AudioTrack通过trackHandle来操作Track对象
AF中获取到刚才我们加入进去的MixerThread对象(注意DirectOutputThread和他的区别),并调用createTrack_l()
track = new Track(this, client, streamType, sampleRate, format, channelMask, frameCount, sharedBuffer, sessionId, uid, *flags);mTracks.add(track);
在PlaybackThread类和RecordThread类中分别都有一个变量 SortedVector< sp > mTracks; 这里的Track类实现在Tracks.cpp中。
AudioTrack对象初始化好之后,接下来是play(),等待数据写入。
这个函数最终会调用到Tracks.cpp中的AudioFlinger::PlaybackThread::Track类中的start()。
status_t AudioFlinger::PlaybackThread::Track::start(AudioSystem::sync_event_t event, int triggerSession) { PlaybackThread *playbackThread = (PlaybackThread *)thread.get(); status = playbackThread->addTrack_l(this); }
这里调用了这个track所属的mixedthread的addTrack_l函数,我们会发现在该函数里,将这个Track加入了mActiveTracks。
然后调用broadcast_l()通知正在等待的线程。(mWaitWorkCV是信号量)
这个通知的最终接收者就是刚才我们new出来的MixedThread,(但是我还没找到这个线程是从什么时候开始跑的。。。)这里有个循环,会一直接收write()捡来的数据。
具体的步骤见如下注释:
bool AudioFlinger::PlaybackThread::threadLoop(){ALOGV("%s going to sleep", myName.string()); mWaitWorkCV.wait(mLock); ALOGV("%s waking up", myName.string()); mMixerStatus = prepareTracks_l(&tracksToRemove);//当发现mix过程返回ready的话,正式开始mixif (mMixerStatus == MIXER_TRACKS_READY) { // threadLoop_mix() sets mCurrentWriteLength threadLoop_mix(); } else if ((mMixerStatus != MIXER_DRAIN_TRACK) && (mMixerStatus != MIXER_DRAIN_ALL)) { // threadLoop_sleepTime sets sleepTime to 0 if data // must be written to HAL threadLoop_sleepTime(); if (sleepTime == 0) { mCurrentWriteLength = mixBufferSize; } } //处理好了,可以写入output了 threadLoop_write();}
prepareTracks_l()是个庞大的函数。。反正我已经晕了。主要是遍历每个当前playthread中的active track,当一个track的最小buffer数据已经ready的话,就让AudioMixer干活将声音混合在一起,最后得到一个可以播放的声音吧。
for (size_t i=0 ; i<count ; i++) { const sp<Track> t = mActiveTracks[i].promote(); if ((framesReady >= minFrames) && track->isReady() && !track->isPaused() && !track->isTerminated()) { mAudioMixer->setBufferProvider(name, track); mAudioMixer->enable(name); mAudioMixer->setParameter(name, param, AudioMixer::VOLUME0, (void *)vl); mAudioMixer->setParameter(name, param, AudioMixer::VOLUME1, (void *)vr); mAudioMixer->setParameter(name, param, AudioMixer::AUXLEVEL, (void *)va); mAudioMixer->setParameter( name, AudioMixer::TRACK, AudioMixer::FORMAT, (void *)track->format()); mAudioMixer->setParameter( name, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK, (void *)track->channelMask()); }}
当数据READY的时候,调用threadLoop_mix(),这里分为两个不同的sink,没仔细看,可能是不同类型的设置吧。。。
if (mNormalSink != 0) { status = mNormalSink->getNextWriteTimestamp(&pts); } else { status = mOutputSink->getNextWriteTimestamp(&pts); } if (status != NO_ERROR) { pts = AudioBufferProvider::kInvalidPTS; } // mix buffers... mAudioMixer->process(pts);
这里的process(pts)是一个回调函数,应该是先调用我们在mix的enable()时设置的process__validate函数.
void AudioMixer::process__validate(state_t* state, int64_t pts){while (en) { const int i = 31 - __builtin_clz(en); en &= ~(1<<i); countActiveTracks++; track_t& t = state->tracks[i];//设置各个track不同类型的处理函数。if ((n & NEEDS_MUTE__MASK) == NEEDS_MUTE_ENABLED) { t.hook = track__nop; } else { if ((n & NEEDS_AUX__MASK) == NEEDS_AUX_ENABLED) { all16BitsStereoNoResample = false; } if ((n & NEEDS_RESAMPLE__MASK) == NEEDS_RESAMPLE_ENABLED) { all16BitsStereoNoResample = false; resampling = true; t.hook = track__genericResample; ALOGV_IF((n & NEEDS_CHANNEL_COUNT__MASK) > NEEDS_CHANNEL_2, "Track %d needs downmix + resample", i); } else { if ((n & NEEDS_CHANNEL_COUNT__MASK) == NEEDS_CHANNEL_1){ t.hook = track__16BitsMono; all16BitsStereoNoResample = false; } if ((n & NEEDS_CHANNEL_COUNT__MASK) >= NEEDS_CHANNEL_2){ t.hook = track__16BitsStereo; ALOGV_IF((n & NEEDS_CHANNEL_COUNT__MASK) > NEEDS_CHANNEL_2, "Track %d needs downmix", i); } }//设置接下来要调用到的处理函数state->hook = process__nop; if (countActiveTracks) { if (resampling) { if (!state->outputTemp) { state->outputTemp = new int32_t[MAX_NUM_CHANNELS * state->frameCount]; } if (!state->resampleTemp) { state->resampleTemp = new int32_t[MAX_NUM_CHANNELS * state->frameCount]; } state->hook = process__genericResampling; } else { if (state->outputTemp) { delete [] state->outputTemp; state->outputTemp = NULL; } if (state->resampleTemp) { delete [] state->resampleTemp; state->resampleTemp = NULL; } state->hook = process__genericNoResampling; if (all16BitsStereoNoResample && !volumeRamp) { if (countActiveTracks == 1) { state->hook = process__OneTrack16BitsStereoNoResampling; } } } //调用各自track的处理函数 state->hook(state, pts);}
关于怎么处理混音的部分,很多参数我看不懂,随便看一个吧
void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state, int64_t pts){while (numFrames) {//取write下来的数据并保存t.bufferProvider->getNextBuffer(&b, outputPTS);//释放原先的数据 t.bufferProvider->releaseBuffer(&b);}}
最后处理完之后,就调用 threadLoop_write(),后面就是hal层的事情了。
bytesWritten = mOutput->stream->write(mOutput->stream, mMixBuffer + offset, mBytesRemaining);
做好这些工作后,接下来就是往里写数据了,我们使用write()方法,对应writeToTrack()。如果是STREAM方式初始化AudioTrack的话,sharedBuffer() 是返回0的,反之则不同。
jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, jbyte* data, jint offsetInBytes, jint sizeInBytes) { // regular write() or copy the data to the AudioTrack's shared memory? if (track->sharedBuffer() == 0) { written = track->write(data + offsetInBytes, sizeInBytes); } else { memcpy(track->sharedBuffer()->pointer(), data + offsetInBytes, sizeInBytes); } return written;
这里的buffer就是会被上面所说的循环取到的,具体的共享未看。
- AudioTrack播放音频的流程
- Android AudioTrack 播放音频
- Android音频播放AudioTrack
- [AudioTrack]使用AudioTrack播放PCM音频数据
- 使用AudioTrack进行音频播放
- 使用AudioTrack进行音频播放
- 使用AudioTrack进行音频播放
- Android音频播放之AudioTrack
- 使用AudioTrack播放PCM音频数据
- Android 通过AudioTrack播放CAF音频
- 使用AudioTrack播放PCM音频数据
- Android 通过AudioTrack播放CAF音频
- Android音频开发之AudioTrack实时播放
- FFMPEG 之音频解码及AudioTrack播放音频
- android中AudioRecord采集音频的参数说明以及audioTrack的播放
- 8926音频播放流程
- Android中如何实现播放音频设置不同的播放速率(MediaPlayer SoundPool AudioTrack OpenSL ES)
- 使用AudioTrack播放PCM音频数据(android)
- win 7 64位系统下 opencv2.4.9+vs2012的环境配置以及x64的运行
- CPU绑定技术
- 进程间通信-信号详解
- Java IO流 字符常用流:BufferedWriter详细解释
- 深入理解JavaScript系列(19):求值策略(Evaluation strategy)
- AudioTrack播放音频的流程
- SDKD Summer Team Contest R
- 深入理解JavaScript系列(20):《你真懂JavaScript吗?》答案详解
- 控件的使用
- codeforces 490 D Chocolate
- hdu5289 Assignment --2015多校训练赛(一)
- Ubuntu 查看文件及文件夹大小
- 深入理解JavaScript系列(21):S.O.L.I.D五大原则之接口隔离原则ISP
- 点滴资源汇总